Partager via


Création et dessin sur des bitmaps SkiaSharp

Vous avez vu comment une application peut charger des bitmaps à partir du web, des ressources d’application et de la bibliothèque de photos de l’utilisateur. Il est également possible de créer de nouvelles bitmaps au sein de votre application. L’approche la plus simple implique l’un des constructeurs de SKBitmap:

SKBitmap bitmap = new SKBitmap(width, height);

Les width paramètres et height les entiers sont des entiers et spécifient les dimensions de pixels de la bitmap. Ce constructeur crée une bitmap de couleur complète avec quatre octets par pixel : un octet chacun pour les composants rouge, vert, bleu et alpha (opacité).

Une fois que vous avez créé une image bitmap, vous devez obtenir quelque chose sur la surface de la bitmap. Vous effectuez généralement cette opération de deux manières :

  • Dessinez sur la bitmap à l’aide de méthodes de dessin standard Canvas .
  • Accédez directement aux bits de pixels.

Cet article illustre la première approche :

Exemple de dessin

La deuxième approche est abordée dans l’article Accès aux pixels bitmap SkiaSharp.

Dessin sur la bitmap

Le dessin sur la surface d’une bitmap est identique au dessin sur un affichage vidéo. Pour dessiner sur un affichage vidéo, vous obtenez un SKCanvas objet à partir des PaintSurface arguments d’événement. Pour dessiner sur une bitmap, vous créez un SKCanvas objet à l’aide du SKCanvas constructeur :

SKCanvas canvas = new SKCanvas(bitmap);

Lorsque vous avez terminé de dessiner sur la bitmap, vous pouvez supprimer l’objet SKCanvas . Pour cette raison, le SKCanvas constructeur est généralement appelé dans une using instruction :

using (SKCanvas canvas = new SKCanvas(bitmap))
{
    ··· // call drawing function
}

L’image bitmap peut ensuite être affichée. Ultérieurement, le programme peut créer un SKCanvas objet basé sur cette même bitmap et dessiner sur celui-ci.

La page Hello Bitmap de l’exemple d’application écrit le texte « Hello, Bitmap ! » sur une bitmap, puis affiche cette bitmap plusieurs fois.

Le constructeur du HelloBitmapPage début en créant un SKPaint objet pour l’affichage du texte. Elle détermine les dimensions d’une chaîne de texte et crée une bitmap avec ces dimensions. Il crée ensuite un SKCanvas objet basé sur cette bitmap, appelle Clear, puis appelle DrawText. Il est toujours judicieux d’appeler Clear avec une nouvelle bitmap, car une bitmap nouvellement créée peut contenir des données aléatoires.

Le constructeur conclut en créant un SKCanvasView objet pour afficher la bitmap :

public partial class HelloBitmapPage : ContentPage
{
    const string TEXT = "Hello, Bitmap!";
    SKBitmap helloBitmap;

    public HelloBitmapPage()
    {
        Title = TEXT;

        // Create bitmap and draw on it
        using (SKPaint textPaint = new SKPaint { TextSize = 48 })
        {
            SKRect bounds = new SKRect();
            textPaint.MeasureText(TEXT, ref bounds);

            helloBitmap = new SKBitmap((int)bounds.Right,
                                       (int)bounds.Height);

            using (SKCanvas bitmapCanvas = new SKCanvas(helloBitmap))
            {
                bitmapCanvas.Clear();
                bitmapCanvas.DrawText(TEXT, 0, -bounds.Top, textPaint);
            }
        }

        // Create SKCanvasView to view result
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear(SKColors.Aqua);

        for (float y = 0; y < info.Height; y += helloBitmap.Height)
            for (float x = 0; x < info.Width; x += helloBitmap.Width)
            {
                canvas.DrawBitmap(helloBitmap, x, y);
            }
    }
}

Le PaintSurface gestionnaire affiche la bitmap plusieurs fois dans les lignes et les colonnes de l’affichage. Notez que la Clear méthode du PaintSurface gestionnaire a un argument de SKColors.Aqua, qui colore l’arrière-plan de l’aire d’affichage :

Bonjour, Bitmap !

L’apparence de l’arrière-plan aqua révèle que la bitmap est transparente à l’exception du texte.

Effacement et transparence

L’affichage de la page Hello Bitmap montre que la bitmap créée par le programme est transparente, à l’exception du texte noir. C’est pourquoi la couleur aqua de la surface d’affichage s’affiche.

La documentation des Clear méthodes de SKCanvas les décrit avec l’instruction : « Remplace tous les pixels dans le clip actuel du canevas ». L’utilisation du mot « replaces » révèle une caractéristique importante de ces méthodes : toutes les méthodes de dessin d’ajouter SKCanvas quelque chose à l’aire d’affichage existante. Les Clear méthodes remplacent ce qui est déjà là.

Clear existe dans deux versions différentes :

  • La Clear méthode par un SKColor paramètre remplace les pixels de l’aire d’affichage par des pixels de cette couleur.

  • La Clear méthode sans paramètre remplace les pixels par la SKColors.Empty couleur, qui est une couleur dans laquelle tous les composants (rouge, vert, bleu et alpha) sont définis sur zéro. Cette couleur est parfois appelée « noir transparent ».

L’appel Clear sans argument sur une nouvelle bitmap initialise l’intégralité de la bitmap pour être entièrement transparente. Tout élément dessiné par la suite sur la bitmap sera généralement opaque ou partiellement opaque.

Voici un élément à essayer : dans la page Hello Bitmap , remplacez la Clear méthode appliquée à celle-ci bitmapCanvas :

bitmapCanvas.Clear(new SKColor(255, 0, 0, 128));

L’ordre des paramètres du SKColor constructeur est rouge, vert, bleu et alpha, où chaque valeur peut aller de 0 à 255. N’oubliez pas qu’une valeur alpha de 0 est transparente, tandis qu’une valeur alpha de 255 est opaque.

La valeur (255, 0, 0, 128) efface les pixels bitmap en pixels rouges avec une opacité de 50 %. Cela signifie que l’arrière-plan bitmap est semi-transparent. L’arrière-plan rouge semi-transparent de l’image bitmap se combine avec l’arrière-plan aqua de l’aire d’affichage pour créer un arrière-plan gris.

Essayez de définir la couleur du texte en noir transparent en plaçant l’affectation suivante dans l’initialiseur SKPaint :

Color = new SKColor(0, 0, 0, 0)

Vous pensez peut-être que ce texte transparent créerait des zones entièrement transparentes de la bitmap à travers laquelle vous voyiez l’arrière-plan aqua de l’aire d’affichage. Mais ce n’est pas le cas. Le texte est dessiné en haut de ce qui se trouve déjà sur la bitmap. Le texte transparent ne sera pas visible du tout.

Aucune méthode ne Draw rend une bitmap plus transparente. Je ne peux le faire que Clear .

Types de couleurs bitmap

Le constructeur le plus simple SKBitmap vous permet de spécifier une largeur et une hauteur de pixels entiers pour l’image bitmap. D’autres SKBitmap constructeurs sont plus complexes. Ces constructeurs nécessitent des arguments de deux types d’énumération : SKColorType et SKAlphaType. D’autres constructeurs utilisent la SKImageInfo structure, qui consolide ces informations.

L’énumération SKColorType comporte 9 membres. Chacun de ces membres décrit un moyen particulier de stocker les pixels bitmap :

  • Unknown
  • Alpha8 — chaque pixel est de 8 bits, représentant une valeur alpha de totalement transparente à entièrement opaque
  • Rgb565 — chaque pixel est de 16 bits, 5 bits pour le rouge et le bleu, et 6 pour le vert
  • Argb4444 — chaque pixel est de 16 bits, 4 chacun pour alpha, rouge, vert et bleu
  • Rgba8888 — chaque pixel est de 32 bits, 8 chacun pour rouge, vert, bleu et alpha
  • Bgra8888 — chaque pixel est de 32 bits, 8 chacun pour le bleu, le vert, le rouge et l’alpha
  • Index8 — chaque pixel est de 8 bits et représente un index dans un SKColorTable
  • Gray8 — chaque pixel est de 8 bits représentant une nuance grise de noir à blanc
  • RgbaF16 — chaque pixel est de 64 bits, avec rouge, vert, bleu et alpha dans un format à virgule flottante 16 bits

Les deux formats où chaque pixel est de 32 pixels (4 octets) sont souvent appelés formats de couleur complète. La plupart des autres formats datent d’une heure où la vidéo s’affiche elle-même n’était pas capable de couleur entière. Les bitmaps de couleur limitée étaient adéquates pour ces affichages et permettaient aux bitmaps d’occuper moins d’espace en mémoire.

Ces jours-ci, les programmeurs utilisent presque toujours des bitmaps de couleur complète et ne se dérangent pas avec d’autres formats. L’exception est le RgbaF16 format, qui permet une résolution de couleur supérieure à celle des formats de couleur complète. Toutefois, ce format est utilisé à des fins spécialisées, comme l’imagerie médicale, et n’a pas beaucoup de sens lorsqu’il est utilisé avec des affichages de couleur complète standard.

Cette série d’articles se limite aux SKBitmap formats de couleur utilisés par défaut lorsqu’aucun membre n’est SKColorType spécifié. Ce format par défaut est basé sur la plateforme sous-jacente. Pour les plateformes prises en charge par Xamarin.Forms, le type de couleur par défaut est :

  • Rgba8888 pour iOS et Android
  • Bgra8888 pour UWP

La seule différence est l’ordre des 4 octets en mémoire, et cela devient un problème uniquement lorsque vous accédez directement aux bits de pixels. Cela ne sera pas important tant que vous n’aurez pas accès à l’article Accès aux pixels bitmap SkiaSharp.

L’énumération SKAlphaType comporte quatre membres :

  • Unknown
  • Opaque — la bitmap n’a pas de transparence
  • Premul — Les composants de couleur sont pré-multipliés par le composant alpha
  • Unpremul — Les composants de couleur ne sont pas pré-multipliés par le composant alpha

Voici un pixel bitmap rouge de 4 octets avec une transparence de 50 %, avec les octets affichés dans l’ordre rouge, vert, bleu, alpha :

0xFF 0x00 0x00 0x80

Lorsqu’une bitmap contenant des pixels semi-transparents est rendue sur une surface d’affichage, les composants de couleur de chaque pixel bitmap doivent être multipliés par la valeur alpha de ce pixel, et les composants de couleur du pixel correspondant de l’aire d’affichage doivent être multipliés par 255 moins la valeur alpha. Les deux pixels peuvent ensuite être combinés. La bitmap peut être rendue plus rapidement si les composants de couleur des pixels bitmap ont déjà été pré-mulitpliés par la valeur alpha. Ce même pixel rouge serait stocké comme suit dans un format prédéfinis :

0x80 0x00 0x00 0x80

Cette amélioration des performances est la raison pour laquelle SkiaSharp les bitmaps par défaut sont créées avec un Premul format. Mais encore une fois, il devient nécessaire de le savoir uniquement lorsque vous accédez et manipulez des bits de pixels.

Dessin sur des bitmaps existantes

Il n’est pas nécessaire de créer une image bitmap pour dessiner dessus. Vous pouvez également dessiner sur une bitmap existante.

La page Monkey Moustache utilise son constructeur pour charger l’image MonkeyFace.png . Il crée ensuite un SKCanvas objet basé sur cette bitmap et utilise et SKPath les SKPaint objets pour dessiner une moustache sur celle-ci :

public partial class MonkeyMoustachePage : ContentPage
{
    SKBitmap monkeyBitmap;

    public MonkeyMoustachePage()
    {
        Title = "Monkey Moustache";

        monkeyBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
            "SkiaSharpFormsDemos.Media.MonkeyFace.png");

        // Create canvas based on bitmap
        using (SKCanvas canvas = new SKCanvas(monkeyBitmap))
        {
            using (SKPaint paint = new SKPaint())
            {
                paint.Style = SKPaintStyle.Stroke;
                paint.Color = SKColors.Black;
                paint.StrokeWidth = 24;
                paint.StrokeCap = SKStrokeCap.Round;

                using (SKPath path = new SKPath())
                {
                    path.MoveTo(380, 390);
                    path.CubicTo(560, 390, 560, 280, 500, 280);

                    path.MoveTo(320, 390);
                    path.CubicTo(140, 390, 140, 280, 200, 280);

                    canvas.DrawPath(path, paint);
                }
            }
        }

        // Create SKCanvasView to view result
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(monkeyBitmap, info.Rect, BitmapStretch.Uniform);
    }
}

Le constructeur conclut en créant un SKCanvasView gestionnaire dont PaintSurface le gestionnaire affiche simplement le résultat :

Singe Moustache

Copie et modification des bitmaps

Les méthodes de SKCanvas ce que vous pouvez utiliser pour dessiner sur une bitmap incluent DrawBitmap. Cela signifie que vous pouvez dessiner une bitmap sur une autre, généralement la modifier d’une certaine manière.

La façon la plus polyvalente de modifier une bitmap consiste à accéder aux bits de pixels réels, un sujet abordé dans l’article Accès aux pixels bitmap SkiaSharp. Toutefois, il existe de nombreuses autres techniques pour modifier des bitmaps qui ne nécessitent pas d’accès aux bits de pixels.

La bitmap suivante incluse dans l’exemple d’application est de 360 pixels de large et de 480 pixels en hauteur :

Alpinistes

Supposons que vous n’avez pas reçu l’autorisation du singe sur la gauche pour publier cette photo. Une solution consiste à masquer le visage du singe à l’aide d’une technique appelée pixelisation. Les pixels du visage sont remplacés par des blocs de couleur afin que vous ne puissiez pas faire ressortir les caractéristiques. Les blocs de couleur sont généralement dérivés de l’image d’origine en moyenne les couleurs des pixels correspondant à ces blocs. Mais vous n’avez pas besoin d’effectuer cette moyenne vous-même. Cela se produit automatiquement lorsque vous copiez une bitmap dans une dimension de pixel plus petite.

Le visage du singe gauche occupe environ une zone carrée de 72 pixels avec un coin supérieur gauche au point (112, 238). Remplacez cette zone carrée de 72 pixels par un tableau de blocs colorés de 9 par 9, chacun étant de 8 à 8 pixels carrés.

La page Pixelize Image se charge dans cette bitmap et crée d’abord une image bitmap carrée de 9 pixels minuscule appelée faceBitmap. C’est une destination pour copier juste le visage du singe. Le rectangle de destination n’est qu’à 9 pixels carrés, mais le rectangle source est carré de 72 pixels. Chaque bloc de pixels source de 8 par 8 est consolidé à un seul pixel en moyenne les couleurs.

L’étape suivante consiste à copier la bitmap d’origine dans une nouvelle bitmap de la même taille appelée pixelizedBitmap. Le minuscule faceBitmap est ensuite copié sur celui-ci avec un rectangle de destination carré de 72 pixels afin que chaque pixel de faceBitmap soit étendu à 8 fois sa taille :

public class PixelizedImagePage : ContentPage
{
    SKBitmap pixelizedBitmap;

    public PixelizedImagePage ()
    {
        Title = "Pixelize Image";

        SKBitmap originalBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
            "SkiaSharpFormsDemos.Media.MountainClimbers.jpg");

        // Create tiny bitmap for pixelized face
        SKBitmap faceBitmap = new SKBitmap(9, 9);

        // Copy subset of original bitmap to that
        using (SKCanvas canvas = new SKCanvas(faceBitmap))
        {
            canvas.Clear();
            canvas.DrawBitmap(originalBitmap,
                              new SKRect(112, 238, 184, 310),   // source
                              new SKRect(0, 0, 9, 9));          // destination

        }

        // Create full-sized bitmap for copy
        pixelizedBitmap = new SKBitmap(originalBitmap.Width, originalBitmap.Height);

        using (SKCanvas canvas = new SKCanvas(pixelizedBitmap))
        {
            canvas.Clear();

            // Draw original in full size
            canvas.DrawBitmap(originalBitmap, new SKPoint());

            // Draw tiny bitmap to cover face
            canvas.DrawBitmap(faceBitmap,
                              new SKRect(112, 238, 184, 310));  // destination
        }

        // Create SKCanvasView to view result
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(pixelizedBitmap, info.Rect, BitmapStretch.Uniform);
    }
}

Le constructeur conclut en créant un SKCanvasView pour afficher le résultat :

Pixeliser l’image

Rotation des bitmaps

Une autre tâche courante consiste à faire pivoter des bitmaps. Cela est particulièrement utile lors de la récupération de bitmaps à partir d’une bibliothèque de photos i Téléphone ou iPad. Sauf si l’appareil a été maintenu dans une orientation particulière lorsque la photo a été prise, l’image est susceptible d’être à l’envers ou à côté.

La désactivation d’une bitmap à l’envers nécessite la création d’une autre image bitmap de la même taille que la première, puis la définition d’une transformation pour faire pivoter de 180 degrés tout en copiant la première vers la seconde. Dans tous les exemples de cette section, bitmap est l’objet SKBitmap que vous devez faire pivoter :

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.RotateDegrees(180, bitmap.Width / 2, bitmap.Height / 2);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Lors de la rotation de 90 degrés, vous devez créer une bitmap qui est une taille différente de celle de l’origine en permuter la hauteur et la largeur. Par exemple, si la bitmap d’origine est de 1200 pixels de large et de 800 pixels de haut, la bitmap pivotée est de 800 pixels de large et de 1200 pixels. Définissez la traduction et la rotation afin que la bitmap soit pivotée autour de son angle supérieur gauche, puis décalée en mode affichage. (Gardez à l’esprit que les méthodes et RotateDegrees les Translate méthodes sont appelées dans l’ordre opposé de la façon dont elles sont appliquées.) Voici le code de rotation de 90 degrés dans le sens des aiguilles d’une montre :

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.Translate(bitmap.Height, 0);
    canvas.RotateDegrees(90);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Et voici une fonction similaire pour la rotation de 90 degrés dans le sens inverse des aiguilles d’une montre :

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.Translate(0, bitmap.Width);
    canvas.RotateDegrees(-90);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Ces deux méthodes sont utilisées dans les pages Photo Puzzle décrites dans l’article Cropping SkiaSharp Bitmaps.

Un programme qui permet à l’utilisateur de faire pivoter une bitmap par incréments de 90 degrés n’a besoin d’implémenter qu’une seule fonction pour la rotation de 90 degrés. L’utilisateur peut ensuite faire pivoter n’importe quel incrément de 90 degrés par exécution répétée de cette fonction.

Un programme peut également faire pivoter une bitmap en fonction de n’importe quelle quantité. Une approche simple consiste à modifier la fonction qui fait pivoter de 180 degrés en remplaçant 180 par une variable généralisée angle :

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.RotateDegrees(angle, bitmap.Width / 2, bitmap.Height / 2);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Toutefois, dans le cas général, cette logique rognera les coins de la bitmap pivotée. Une meilleure approche consiste à calculer la taille de la bitmap pivotée à l’aide de trigonométrie pour inclure ces angles.

Cette trigonométrie s’affiche dans la page Pivotor bitmap. Le fichier XAML instancie un SKCanvasView et un Slider qui peut aller de 0 à 360 degrés avec une Label valeur montrant la valeur actuelle :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Bitmaps.BitmapRotatorPage"
             Title="Bitmap Rotator">
    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="slider"
                Maximum="360"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference slider},
                              Path=Value,
                              StringFormat='Rotate by {0:F0}&#x00B0;'}"
               HorizontalTextAlignment="Center" />

    </StackLayout>
</ContentPage>

Le fichier code-behind charge une ressource bitmap et l’enregistre sous la forme d’un champ statique en lecture seule nommé originalBitmap. La bitmap affichée dans le PaintSurface gestionnaire est rotatedBitmap, qui est initialement définie sur originalBitmap:

public partial class BitmapRotatorPage : ContentPage
{
    static readonly SKBitmap originalBitmap =
        BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
            "SkiaSharpFormsDemos.Media.Banana.jpg");

    SKBitmap rotatedBitmap = originalBitmap;

    public BitmapRotatorPage ()
    {
        InitializeComponent ();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(rotatedBitmap, info.Rect, BitmapStretch.Uniform);
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        double angle = args.NewValue;
        double radians = Math.PI * angle / 180;
        float sine = (float)Math.Abs(Math.Sin(radians));
        float cosine = (float)Math.Abs(Math.Cos(radians));
        int originalWidth = originalBitmap.Width;
        int originalHeight = originalBitmap.Height;
        int rotatedWidth = (int)(cosine * originalWidth + sine * originalHeight);
        int rotatedHeight = (int)(cosine * originalHeight + sine * originalWidth);

        rotatedBitmap = new SKBitmap(rotatedWidth, rotatedHeight);

        using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
        {
            canvas.Clear(SKColors.LightPink);
            canvas.Translate(rotatedWidth / 2, rotatedHeight / 2);
            canvas.RotateDegrees((float)angle);
            canvas.Translate(-originalWidth / 2, -originalHeight / 2);
            canvas.DrawBitmap(originalBitmap, new SKPoint());
        }

        canvasView.InvalidateSurface();
    }
}

Le ValueChanged gestionnaire des Slider opérations effectue les opérations qui créent une nouvelle rotatedBitmap en fonction de l’angle de rotation. La nouvelle largeur et la hauteur sont basées sur des valeurs absolues de sines et de cosines des largeurs et hauteurs d’origine. Les transformations utilisées pour dessiner la bitmap d’origine sur la bitmap pivotée déplacent le centre bitmap d’origine vers l’origine, puis la font pivoter par le nombre spécifié de degrés, puis traduisent ce centre au centre de la bitmap pivotée. (Les Translate méthodes et RotateDegrees les méthodes sont appelées dans l’ordre opposé par rapport à la façon dont elles sont appliquées.)

Notez l’utilisation de la Clear méthode pour rendre l’arrière-plan d’un rotatedBitmap rose clair. Il s’agit uniquement d’illustrer la taille de rotatedBitmap l’affichage :

Pivoteur bitmap

La bitmap pivotée est juste assez grande pour inclure l’intégralité de la bitmap d’origine, mais pas plus grande.

Capture d’images bitmap

Une autre opération couramment effectuée sur des bitmaps est appelée capture d’écran. Conceptuellement, la bitmap est pivotée en trois dimensions autour d’un axe vertical ou d’un axe horizontal au centre de la bitmap. Le découpage vertical crée une image miroir.

La page Flipper bitmap de l’exemple d’application illustre ces processus. Le fichier XAML contient un SKCanvasView bouton et deux boutons pour basculer verticalement et horizontalement :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Bitmaps.BitmapFlipperPage"
             Title="Bitmap Flipper">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <skia:SKCanvasView x:Name="canvasView"
                           Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Button Text="Flip Vertical"
                Grid.Row="1" Grid.Column="0"
                Margin="0, 10"
                Clicked="OnFlipVerticalClicked" />

        <Button Text="Flip Horizontal"
                Grid.Row="1" Grid.Column="1"
                Margin="0, 10"
                Clicked="OnFlipHorizontalClicked" />
    </Grid>
</ContentPage>

Le fichier code-behind implémente ces deux opérations dans les Clicked gestionnaires pour les boutons :

public partial class BitmapFlipperPage : ContentPage
{
    SKBitmap bitmap =
        BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
            "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");

    public BitmapFlipperPage()
    {
        InitializeComponent();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(bitmap, info.Rect, BitmapStretch.Uniform);
    }

    void OnFlipVerticalClicked(object sender, ValueChangedEventArgs args)
    {
        SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

        using (SKCanvas canvas = new SKCanvas(flippedBitmap))
        {
            canvas.Clear();
            canvas.Scale(-1, 1, bitmap.Width / 2, 0);
            canvas.DrawBitmap(bitmap, new SKPoint());
        }

        bitmap = flippedBitmap;
        canvasView.InvalidateSurface();
    }

    void OnFlipHorizontalClicked(object sender, ValueChangedEventArgs args)
    {
        SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

        using (SKCanvas canvas = new SKCanvas(flippedBitmap))
        {
            canvas.Clear();
            canvas.Scale(1, -1, 0, bitmap.Height / 2);
            canvas.DrawBitmap(bitmap, new SKPoint());
        }

        bitmap = flippedBitmap;
        canvasView.InvalidateSurface();
    }
}

Le retournement vertical est effectué par une transformation de mise à l’échelle avec un facteur de mise à l’échelle horizontal de –1. Le centre de mise à l’échelle est le centre vertical de la bitmap. Le retournement horizontal est une transformation de mise à l’échelle avec un facteur de mise à l’échelle vertical de –1.

Comme vous pouvez le voir à partir de la lettre inversée sur la chemise du singe, le détourage n’est pas le même que la rotation. Mais comme le montre la capture d’écran UWP à droite, le fait de glisser horizontalement et verticalement est identique à la rotation de 180 degrés :

Bitmap Flipper

Une autre tâche courante qui peut être gérée à l’aide de techniques similaires consiste à rogner une bitmap vers un sous-ensemble rectangulaire. Cet article décrit dans l’article suivant Rogner les bitmaps SkiaSharp.