Share via


在 SkiaSharp 位圖上建立和繪製

您已瞭解應用程式如何從 Web、應用程式資源,以及從使用者相片庫載入點陣圖。 您也可以在應用程式中建立新的點陣圖。 最簡單的方法牽涉到 的其中一個建構函式 SKBitmap

SKBitmap bitmap = new SKBitmap(width, height);

widthheight 參數是整數,並指定位圖的像素維度。 此建構函式會為每個圖元建立四個字節的完整色彩點陣圖:紅色、綠色、藍色和Alpha(不透明度)元件各有一個字節。

建立新的點陣圖之後,您必須取得位圖表面的內容。 您通常會以下列兩種方式之一來執行此動作:

  • 使用標準 Canvas 繪圖方法繪製位圖。
  • 直接存取圖元位。

本文示範第一種方法:

繪圖範例

第二種方法會在存取 SkiaSharp 位圖圖。

在點陣圖上繪製

在點陣圖介面上繪製與在視訊顯示上繪製相同。 若要在視訊顯示器上繪製,您可以從事件自變數取得 SKCanvas 物件 PaintSurface 。 若要在點陣圖上繪製,您可以使用 建SKCanvas構函式建立 SKCanvas 物件:

SKCanvas canvas = new SKCanvas(bitmap);

完成在點陣圖上繪製時,您可以處置 SKCanvas 物件。 基於這個理由, SKCanvas 建構函式通常會在 語句中 using 呼叫:

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

然後可以顯示點陣圖。 稍後,程式可以根據相同的點陣圖建立新的 SKCanvas 物件,並再繪製一些。

範例應用程式中的 Hello Bitmap 頁面會在點陣圖上寫入文字 “Hello, Bitmap!” ,然後多次顯示該位圖。

的建構函式會 HelloBitmapPage 從建立 SKPaint 對象來顯示文字開始。 它會決定文字字串的維度,並建立具有這些維度的點陣圖。 然後 SKCanvas ,它會根據該點陣圖建立 物件、呼叫 Clear,然後呼叫 DrawText。 使用新位圖呼叫 Clear 永遠是個好主意,因為新建立的位圖可能包含隨機數據。

建構函式會建立 SKCanvasView 物件以顯示點陣圖來結束:

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);
            }
    }
}

處理程式 PaintSurface 會在顯示的數據列和數據行中轉譯點圖多次。 請注意,ClearPaintSurface處理程式中的 方法具有的SKColors.Aqua自變數,其會色彩顯示介面的背景:

你好,位圖!

水背景的外觀會顯示點陣圖是透明的,但文字除外。

清除和透明度

[Hello Bitmap] 頁面的顯示會示範程式建立的 點陣圖 是透明的,但黑色文字除外。 這就是為什麼顯示表面的青色顯示通過的原因。

Clear方法SKCanvas的檔會使用語句來描述這些方法:「取代畫布目前剪輯中的所有圖元」。使用 「replaces」 一詞會顯示這些方法的重要特性:所有將某些專案新增至現有顯示介面的繪圖方法SKCanvas。 方法 Clear取代 已經存在的內容。

Clear 存在於兩個不同的版本中:

  • 方法 ClearSKColor 參數會以該色彩的圖元取代顯示介面的圖元。

  • Clear沒有參數的方法會將圖元取代為SKColors.Empty色彩,也就是所有元件(紅色、綠色、藍色和 Alpha)都設定為零的色彩。 這種色彩有時稱為「透明黑色」。

在沒有自變數的新位陣圖上呼叫 Clear ,會將整個點陣圖初始化為完全透明。 後續在點陣圖上繪製的任何專案通常不透明或部分不透明。

以下是要嘗試的動作:在 Hello Bitmap 頁面中,以下列方法取代 Clear 套用至 bitmapCanvas 的方法:

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

建構函式參數的順序 SKColor 是紅色、綠色、藍色和Alpha,其中每個值的範圍可以從0到255。 請記住,Alpha 值為 0 是透明的,而 Alpha 值為 255 則不透明。

值 (255, 0, 0, 128) 會將點陣圖圖圖元清除為 50% 不透明度的紅色圖元。 這表示位圖背景為半透明。 位圖的半透明紅色背景會結合顯示表面的青色背景,以建立灰色背景。

請嘗試將下列指派放在初始化運算式中 SKPaint ,將文字的色彩設定為透明黑色:

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

您可能會認為此透明文字會建立位圖的完全透明區域,讓您看到顯示表面的青色背景。 但這不是這樣。 文字會繪製在點陣圖上已經有的內容之上。 完全看不到透明文字。

任何 Draw 方法都不會讓位圖更加透明。 只能 Clear 這樣做。

點陣圖色彩類型

最簡單的 SKBitmap 建構函式可讓您指定位圖的整數像素寬度和高度。 其他 SKBitmap 建構函式更為複雜。 這些建構函式需要兩個列舉型別的自變數: SKColorTypeSKAlphaType。 其他建構函式會使用 SKImageInfo 結構,其會合併這項資訊。

列舉 SKColorType 有9個成員。 每個成員都會描述儲存點陣圖圖像素的特定方式:

  • Unknown
  • Alpha8 — 每個圖元都是 8 位,代表從完全透明到完全不透明的 Alpha 值
  • Rgb565 — 每個圖元都是 16 位、5 位代表紅色和藍色,6 個代表綠色
  • Argb4444 — 每個圖元都是 16 位,4 個用於 Alpha、紅色、綠色和藍色
  • Rgba8888 — 每個圖元都是 32 位,紅色、綠色、藍色和 Alpha 各有 8 個
  • Bgra8888 — 每個圖元都是 32 位,8 個是藍色、綠色、紅色和 Alpha
  • Index8 — 每個圖元都是8位,並代表索引到 SKColorTable
  • Gray8 — 每個圖元都是8位,代表從黑色到白色的灰色陰影
  • RgbaF16 — 每個圖元都是 64 位,採用 16 位浮點格式的紅色、綠色、藍色和 Alpha

每個像素為32圖元(4個字節)的兩種格式通常稱為 全色 格式。 許多其他格式從視訊顯示本身無法全色的時間日期。 這些顯示器的色彩有限位圖已足夠,而且允許點陣圖佔用記憶體中的空間較少。

這些天,程式設計人員幾乎一律使用全色位圖,而且不會與其他格式困擾。 例外狀況 RgbaF16 是格式,其允許的色彩解析度甚至比全色格式還要高。 不過,此格式用於特殊用途,例如醫學影像,在搭配標準全色顯示器使用時並無太大意義。

當未SKColorType指定任何成員時,SKBitmap這一系列文章將本身限制為預設所使用的色彩格式。 此預設格式是以基礎平台為基礎。 針對 支援 Xamarin.Forms的平台,預設色彩類型為:

  • Rgba8888 適用於 iOS 和 Android
  • Bgra8888 適用於UWP

唯一的差異在於記憶體中 4 個字節的順序,這隻會在您直接存取圖元位時變成問題。 在您進入存取 SkiaSharp 位圖圖圖元一文之前,這不會變得很重要。

列舉 SKAlphaType 有四個成員:

  • Unknown
  • Opaque — 點陣圖沒有透明度
  • Premul — 色彩元件會預先乘以 Alpha 元件
  • Unpremul — 色彩元件不會預先乘以 Alpha 元件

以下是具有 50% 透明度的 4 位元組紅色點陣圖圖元,以紅色、綠色、藍色、Alpha 的順序顯示位元元:

0xFF 0x00 0x00 0x80

當顯示介面上呈現包含半透明圖元的點陣圖時,每個點陣圖圖元的色彩元件必須乘以該圖元的 Alpha 值,而顯示表面對應圖元的色彩元件必須乘以 255 減去 Alpha 值。 然後可以合併兩個圖元。 如果點圖圖圖中的色彩元件已經由Alpha值預先準備,則點陣圖可以更快轉譯。 相同的紅色圖元會以預乘格式儲存如下:

0x80 0x00 0x00 0x80

此效能改善是預設會以Premul格式建立位圖的原因SkiaSharp。 但同樣地,只有在您存取及操作圖元位時,才需要知道這一點。

在現有的點陣圖上繪製

不需要建立新的點陣圖來繪製它。 您也可以在現有的點陣圖上繪製。

猴子鬍子頁面會使用其建構函式來載入MonkeyFace.png影像。 然後,它會根據該位圖建立 SKCanvas 物件,並使用 SKPaintSKPath 對象來繪製其上的鬍子:

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);
    }
}

建構函式會建立 SKCanvasView ,其 PaintSurface 處理程式只會顯示結果:

猴子鬍子

複製和修改點陣圖

SKCanvas可用來繪製點陣圖的方法包括 DrawBitmap。 這表示您可以在另一個點陣圖上繪製一個點陣圖,通常以某種方式修改它。

修改點陣圖的最多功能方式是透過存取實際圖元,這是存取SkiaSharp位陣陣圖圖元一文中涵蓋的主題。 但有許多其他技術可修改不需要存取圖元的點陣圖。

範例應用程式隨附的下列位圖寬度為360圖元,高度為480像素:

登山者

假設您尚未收到左側猴子的許可,無法發佈此照片。 其中一個解決方案是使用稱為 圖元化的技術遮蔽猴子的臉部。 臉部的圖元會以色彩區塊取代,因此您無法製作特徵。 色彩區塊通常衍生自原始影像,方法是平均對應至這些區塊的圖元色彩。 但您不需要自行執行這項平均。 當您將點陣圖複製到較小的像素維度時,它會自動發生。

左猴子的臉大約佔據一個 72 像素的方形區域,左上角在點 (112, 238) 。 讓我們將 72 像素的方形區域取代為 9 位元組 9 的彩色區塊陣列,每個陣列都是 8 x 8 像素的平方。

[ 圖元化影像 ] 頁面會在該點陣圖中載入,然後先建立名為 faceBitmap的小型 9 圖元方形點陣圖。 這是只複製猴子臉的目的地。 目的矩形只有 9 像素方形,但來源矩形是 72 像素方形。 每8位元組8個來源圖元區塊都會平均合併成一個圖元。

下一個步驟是將原始點陣圖複製到相同大小的新點陣圖中,稱為 pixelizedBitmap。 然後,在上面複製一 faceBitmap 個 72 像素的方形目的地矩形,讓 的每個圖元 faceBitmap 都擴充到其大小的 8 倍:

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);
    }
}

建構函式會藉由建立 SKCanvasView 來顯示結果來結束:

圖元化影像

旋轉點陣圖

另一個常見的工作是旋轉點陣圖。 從 i 電話 或 iPad 相片庫擷取位圖時,這特別有用。 除非拍攝相片時裝置以特定方向保留,否則圖片可能會顛倒或側向。

反轉點陣圖需要建立與第一個相同的大小的另一個點陣圖,然後在將第一個點陣圖複製到第二個時,將轉換設定為旋轉 180 度。 在本節的所有範例中, bitmapSKBitmap 您需要旋轉的物件:

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());
}

旋轉 90 度時,您需要藉由交換高度和寬度來建立與原始大小不同的點陣圖。 例如,如果原始位圖寬 1200 像素且高度為 800 像素,則旋轉的點陣圖寬度為 800 像素,寬 1200 像素。 設定轉譯和旋轉,讓位圖繞著左上角旋轉,然後移入檢視。 (請記住,和 TranslateRotateDegrees 方法會以套用方式的相反順序呼叫。以下是順時針旋轉 90 度的程式代碼:

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());
}

以下是旋轉 90 度逆時針旋轉的類似函式:

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());
}

這兩種方法會用在裁剪 SkiaSharp 位圖一文所述的相片謎題頁面中。

允許使用者以90度增量旋轉點陣圖的程式,只需要實作一個函式來旋轉90度。 然後,用戶可以重複執行此一個函式,以90度的任何增量旋轉。

程式也可以以任何數量旋轉點陣圖。 一個簡單的方法是修改 180 度旋轉的函式,方法是以一般化 angle 變數取代 180:

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());
}

不過,在一般情況下,此邏輯會從旋轉點陣圖的角落裁剪。 更好的方法是使用三角計算旋轉點陣圖的大小,以包含這些角落。

此三角測量會顯示在 [點陣圖旋轉器 ] 頁面中。 XAML 檔案會具現化 , SKCanvasViewSlider 範圍從 0 到 360 度,並 Label 顯示目前值的 :

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

程序代碼後置檔案會載入點陣圖資源,並將其儲存為名為 originalBitmap的靜態只讀字段。 處理程式中顯示的 PaintSurface 點陣圖是 rotatedBitmap,一開始設定為 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();
    }
}

ValueChangedSlider處理程式會執行作業,以根據旋轉角度建立新的 rotatedBitmap 。 新的寬度和高度是根據原始寬度和高度的 sines 和餘弦值的絕對值。 用來在旋轉點陣圖上繪製原始點陣圖的轉換會將原始點中心移至原點,然後依指定的度數旋轉它,然後將該中心轉譯為旋轉點陣圖的中心。 (和 TranslateRotateDegrees 方法的呼叫順序與套用方法的順序相反。

請注意,使用 Clear 方法使淺粉色的背景 rotatedBitmap 。 這只能說明顯示器上的 大小 rotatedBitmap

點陣圖旋轉器

旋轉的位圖剛好夠大,足以包含整個原始點陣圖,但沒有較大的點陣圖。

翻轉點圖

通常會在點陣圖上執行的另一項作業稱為 翻轉。 就概念上講,位圖會在垂直軸或水平軸的三個維度中旋轉,並透過點陣圖的中心旋轉。 垂直翻轉會建立鏡像影像。

範例應用程式中的 [點陣圖翻轉器] 頁面會示範這些程式。 XAML 檔案包含和兩個 SKCanvasView 按鈕,可垂直和水平翻轉:

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

程式代碼後置檔案會在按鈕的處理程式中 Clicked 實作這兩項作業:

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();
    }
}

垂直翻轉是透過水平縮放比例為 –1 的縮放轉換來完成。 縮放中心是位圖的垂直中心。 水平翻轉是具有 -1 垂直縮放比例的縮放轉換。

正如你從猴子襯衫上反字母所見,翻轉與旋轉不同。 但正如右側 UWP 螢幕快照所示,水平和垂直翻轉與旋轉 180 度相同:

點陣圖翻轉器

另一個可以使用類似技術處理的常見工作是將點陣圖裁剪為矩形子集。 下一篇文章會說明裁剪 SkiaSharp 位陣陣圖