Domanda

Quando provo a cambiare la proprietà SourceRect di CroppedBitmap in fase di esecuzione, non succede nulla. Non ci sono errori e il valore della proprietà non viene effettivamente modificato.

Sto provando a realizzare l'animazione sprite. Ho una BitmapSource che contiene un foglio di calcolo, che è una singola bitmap contenente una griglia di diverse pose per lo sprite. Quindi ho una CroppedBitmap che ha lo spritesheet come sorgente e un SourceRect che estrae una delle pose dallo spritesheet. In fase di esecuzione, quando voglio animare, sto cercando di cambiare la proprietà SourceRect di CroppedBitmap, per estrarre una posa diversa dalla bitmap più grande; ma, come notato sopra, il nuovo valore della proprietà semplicemente non si attacca. È la cosa più strana.

Ecco alcuni esempi di 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>

E il codebehind prova a fare questo:

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

Questo mi dà questo output di debug:

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

Quindi in realtà sta assegnando il nuovo rettangolo (con Y = 0) alla proprietà; non c'è eccezione; ma in seguito, il valore della proprietà semplicemente non è cambiato (Y è ancora 640).

Qualche idea sul perché ciò accada e su come risolverlo?

È stato utile?

Soluzione

Alla fine ho trovato la risposta. Dalla documentazione per CroppedBitmap :

  

CroppedBitmap implementa l'interfaccia ISupportInitialize per ottimizzare l'inizializzazione su più proprietà. Le modifiche alle proprietà possono verificarsi solo durante l'inizializzazione degli oggetti. Chiamare BeginInit per segnalare che l'inizializzazione è iniziata e EndInit per segnalare che l'inizializzazione è stata completata. Dopo l'inizializzazione, le modifiche alle proprietà vengono ignorate. (sottolineatura mia)

Solo per divertimento, ho provato ad aggiungere le chiamate BeginInit () .. EndInit () nel mio metodo, per vedere se ciò lo avrebbe reso modificabile. Non sorprende, ho ottenuto InvalidOperationException (" Impossibile impostare lo stato di inizializzazione più di una volta ").

Quindi CroppedBitmap è effettivamente immutabile. (Ma hanno ignorato il loro sistema Freezable, che avrebbe gettato un'eccezione per dirmi che stavo facendo qualcosa di sbagliato e implementato qualcosa di più invece sorprendente.)

Il che significa che non è necessario cambiare la proprietà SourceRect. Dovrò creare un'istanza CroppedBitmap separata per ogni immagine secondaria all'interno dello spritesheet.

Altri suggerimenti

Ecco un modo alternativo per affrontare questo:
Invece di usare un CroppedBitmap , usa l'immagine sorgente completa, ma:

  1. Imposta image.RenderTransform per regolare l'area visualizzabile.
  2. Imposta un Image.Clip se necessario, per evitare di mostrare parti dell'immagine indesiderate.

Ciò significa che non è necessario continuare a creare nuovi CroppedBitmaps , puoi semplicemente regolare la trasformazione.
Nei miei test, non ho visto alcuna differenza di velocità in entrambi i casi.

Per completezza, ecco come modificherei il codice per fare ciò che sto suggerendo:

<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>

Quindi la chiamata successiva per fare l'equivalente della regolazione del SourceRect è:

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

Ecco un modo usando 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();
    }
}

Perf potenzialmente povero.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top