Trying to change CroppedBitmap SourceRect at runtime

When I try to change the CroppedBitmap SourceRect property at runtime, nothing happens. There is no error, and the value of the property does not actually change.

I am trying to make sprite animations. 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 CroppedBitmap, which has a spritesheet as its Source, and SourceRect, which pulls one of the poses from the sprite. At run time, when I want to animate, I try to change the CroppedBitmap SourceRect property to pull another pose from a larger bitmap; but, as noted above, the new property value simply does not stick. This is the strangest thing.

Here is an XAML example:

<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 codebehind is trying 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); 

This gives me this debug output:

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

Therefore, it actually assigns a new rectangle (with Y = 0) to the property; no exception; but then the property value just didn't change (Y is still 640).

Any ideas on why this is happening and how to fix it?

+3
source share
3 answers

In the end, I found the answer. From the documentation for CroppedBitmap :

CroppedBitmap implements the ISupportInitialize interface to optimize initialization across multiple properties. Property changes can only occur during object initialization. Call BeginInit to indicate that initialization has begun, and EndInit to signal that initialization is complete. After initialization, property changes are ignored . (highlighted by me)

Just for fun, I tried adding BeginInit () .. EndInit () calls in my method to make sure this makes it modifiable. Unsurprisingly, I got an InvalidOperationException ("I cannot set the initialization state more than once").

So, CroppedBitmap is effectively unchanged. (But they ignored their own Freezable system, which would throw an exception to tell me that I was doing something wrong, and implemented something more amazing .

This means that you are not changing the SourceRect property. I need to create a separate CroppedBitmap instance for each sub-image inside the sprite.

+15
source

Here is an alternative way to handle this:
Instead of using CroppedBitmap use the full source image, but:

  • Set image.RenderTransform to adjust the visible area.
  • If necessary, set Image.Clip to not display parts of unwanted images.

This means that you do not need to create new CroppedBitmaps , you can just customize the conversion.
In my testing, I did not see a difference in speed doing this anyway.

For completeness, here is how I would adjust your code to do what I suggest:

 <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 a later call to do the equivalent of setting SourceRect :

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

The following is an example of 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 bad perfection.

0
source

Source: https://habr.com/ru/post/1286325/


All Articles