尝试在运行时更改 CroppedBitmap 的 SourceRect

发布于 2024-07-25 08:37:53 字数 1279 浏览 2 评论 0原文

当我尝试在运行时更改 CroppedBitmap 的 SourceRect 属性时,没有任何反应。 没有错误,并且属性值实际上没有改变。

我正在尝试制作精灵动画。 我有一个包含精灵表的 BitmapSource,它是一个包含精灵不同姿势网格的单个位图。 然后我有一个 CroppedBitmap,它以 spritesheet 作为其源,还有一个 SourceRect,它从 spritesheet 中提取其中一个姿势。 在运行时,当我想要制作动画时,我尝试更改 CroppedBitmap 的 SourceRect 属性,以从较大的位图中拉出不同的姿势; 但是,如上所述,新的财产价值根本不存在。 这是最奇怪的事情。

下面是一些示例 XAML:

<UserControl.Resources>
    <BitmapImage x:Key="spritesheet" UriSource="Sprites/elf.png"/>
</UserControl.Resources>
<Image>
    <Image.Source>
        <CroppedBitmap x:Name="image" Source="{StaticResource spritesheet}"
                       SourceRect="240 640 240 320"/>
    </Image.Source>
</Image>

代码隐藏尝试执行此操作:

var newRect = new Int32Rect(...);
Debug.WriteLine("             Before: " + image.SourceRect);
Debug.WriteLine("Assigning new value: " + newRect);
image.SourceRect = newRect;
Debug.WriteLine("              After: " + image.SourceRect);

这给了我这个调试输出:

             Before: 240,640,240,320
Assigning new value: 240,0,240,320
              After: 240,640,240,320

所以它实际上将新矩形(Y=0)分配到属性中; 没有例外; 但之后,属性值根本没有改变(Y 仍然是 640)。

关于为什么会发生这种情况以及如何解决它有什么想法吗?

When I try to change a CroppedBitmap's SourceRect property at runtime, nothing happens. There's no error, and the property value doesn't actually get changed.

I'm trying to do sprite animation. I have a BitmapSource that contains a spritesheet, which is a single bitmap containing a grid of different poses for the sprite. Then I have a CroppedBitmap that has the spritesheet as its Source, and a SourceRect that pulls one of the poses out of the spritesheet. At runtime, when I want to animate, I'm trying to change the CroppedBitmap's SourceRect property, to pull a different pose out of the larger bitmap; but, as noted above, the new property value simply doesn't stick. It's the weirdest thing.

Here's some sample XAML:

<UserControl.Resources>
    <BitmapImage x:Key="spritesheet" UriSource="Sprites/elf.png"/>
</UserControl.Resources>
<Image>
    <Image.Source>
        <CroppedBitmap x:Name="image" Source="{StaticResource spritesheet}"
                       SourceRect="240 640 240 320"/>
    </Image.Source>
</Image>

And the codebehind tries to do this:

var newRect = new Int32Rect(...);
Debug.WriteLine("             Before: " + image.SourceRect);
Debug.WriteLine("Assigning new value: " + newRect);
image.SourceRect = newRect;
Debug.WriteLine("              After: " + image.SourceRect);

That gives me this debug output:

             Before: 240,640,240,320
Assigning new value: 240,0,240,320
              After: 240,640,240,320

So it's actually assigning the new rectangle (with Y=0) into the property; there's no exception; but afterward, the property value simply didn't change (Y is still 640).

Any ideas about why this happens, and how to fix it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

荒人说梦 2024-08-01 08:37:54

我最终找到了答案。 从 CroppedBitmap 的文档中:

CroppedBitmap 实现 ISupportInitialize 接口来优化多个属性的初始化。 属性更改只能在对象初始化期间发生。 调用 BeginInit 表示初始化已开始,调用 EndInit 表示初始化已完成。 初始化后,属性更改将被忽略。 (强调我的)

只是为了好玩,我尝试在我的方法中添加 BeginInit()..EndInit() 调用,看看是否可以会使其可修改。 毫不奇怪,我得到了 InvalidOperationException(“无法多次设置初始化状态”)。

所以 CroppedBitmap 实际上是不可变的。(但是他们忽略了自己的 Freezable 系统,该系统会抛出异常来告诉我我做错了什么,并实现了更多令人惊讶。)

这意味着,不能继续更改 SourceRect 属性。 我需要为 spritesheet 中的每个子图像创建一个单独的 CroppedBitmap 实例。

I eventually found the answer. From the documentation for CroppedBitmap:

CroppedBitmap implements the ISupportInitialize interface to optimize initialization on multiple properties. Property changes can occur only during object initialization. Call BeginInit to signal that initialization has begun and EndInit to signal that initialization has completed. After initialization, property changes are ignored. (emphasis mine)

Just for fun, I tried adding BeginInit()..EndInit() calls in my method, to see if that would make it modifiable. Not surprisingly, I got an InvalidOperationException ("Cannot set the initializing state more than once").

So CroppedBitmap is effectively immutable. (But they ignored their own Freezable system, which would have thrown an exception to tell me I was doing something wrong, and implemented something more surprising instead.)

Which means, no-go on changing the SourceRect property. I'll need to create a separate CroppedBitmap instance for each sub-image within the spritesheet.

提笔书几行 2024-08-01 08:37:54

这是处理此问题的另一种方法:
不要使用 CroppedBitmap,而是使用完整的源图像,但是:

  1. 设置 image.RenderTransform 来调整可视区域。
  2. 如有必要,请设置Image.Clip,以避免显示不需要的图像部分。

这意味着您不需要不断制作新的 CroppedBitmaps,只需调整变换即可。
在我的测试中,我发现无论哪种方式,速度都没有差异。

为了完整起见,我将如何调整您的代码以执行我的建议:

<Image RenderTransform="1, 0, 0, 1, -240, -640">
  <!-- Still include your Image.Source here, just not as a CroppedBitmap -->
  <Image.Clip>
    <RectangleGeometry Rect="0, 0, 240, 320" />
  </Image.Clip>
</Image>

然后,稍后执行与调整 SourceRect 等效的调用是:

image.RenderTransform = new MatrixTransform(1d, 0d, 0d, 1d, -240d, 0d);

Here is an alternate way to deal with this:
Instead of using a CroppedBitmap, use the full source image, but:

  1. Set the image.RenderTransform to adjust the viewable area.
  2. Set an Image.Clip if necessary, to avoid showing portions of the image that are unwanted.

This means that you don't need to keep making new CroppedBitmaps, you can just adjust the transform.
In my testing, I saw no difference in speed doing it either way.

For completeness, here's how I'd adjust your code to do what I'm suggesting:

<Image RenderTransform="1, 0, 0, 1, -240, -640">
  <!-- Still include your Image.Source here, just not as a CroppedBitmap -->
  <Image.Clip>
    <RectangleGeometry Rect="0, 0, 240, 320" />
  </Image.Clip>
</Image>

Then the later call to do the equivalent of adjusting the SourceRect is:

image.RenderTransform = new MatrixTransform(1d, 0d, 0d, 1d, -240d, 0d);
深海夜未眠 2024-08-01 08:37:54

以下是使用 IMultiValueConverter 的方法:

<Image>
    <Image.Source>
        <MultiBinding Converter="{x:Static local:SourceAndRectToCroppedBitmapConverter.Default}">
            <Binding Path="FileName" />
            <Binding Path="CropRect" />
        </MultiBinding>
    </Image.Source>

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public class SourceAndRectToCroppedBitmapConverter : IMultiValueConverter
{
    public static readonly SourceAndRectToCroppedBitmapConverter Default = new SourceAndRectToCroppedBitmapConverter();

    private static readonly ImageSourceConverter Converter = new ImageSourceConverter();

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] is string text)
        {
            return new CroppedBitmap((BitmapSource)Converter.ConvertFrom(values[0]), (Int32Rect)values[1]);
        }

        return new CroppedBitmap((BitmapSource)values[0], (Int32Rect)values[1]);
    }

    object[] IMultiValueConverter.ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

性能可能较差。

Here is a way using IMultiValueConverter:

<Image>
    <Image.Source>
        <MultiBinding Converter="{x:Static local:SourceAndRectToCroppedBitmapConverter.Default}">
            <Binding Path="FileName" />
            <Binding Path="CropRect" />
        </MultiBinding>
    </Image.Source>

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public class SourceAndRectToCroppedBitmapConverter : IMultiValueConverter
{
    public static readonly SourceAndRectToCroppedBitmapConverter Default = new SourceAndRectToCroppedBitmapConverter();

    private static readonly ImageSourceConverter Converter = new ImageSourceConverter();

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] is string text)
        {
            return new CroppedBitmap((BitmapSource)Converter.ConvertFrom(values[0]), (Int32Rect)values[1]);
        }

        return new CroppedBitmap((BitmapSource)values[0], (Int32Rect)values[1]);
    }

    object[] IMultiValueConverter.ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Potentially poor perf.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文