SkiaSharp でのビットマップの基本

Download Sampleサンプルのダウンロード

さまざまなソースからビットマップを読み込んで表示します。

SkiaSharp でのビットマップのサポートは非常に広範囲に渡ります。 この記事では、ビットマップを読み込む方法とそれらの表示方法に関する基本のみを紹介します。

The display of two bitmaps

ビットマップのより詳しい説明については、「SkiaSharp ビットマップ」セクションを参照してください。

SkiaSharp ビットマップは、型 SKBitmap のオブジェクトです。 ビットマップを作成する方法は多数ありますが、この記事は、.NET SKBitmap.Decode オブジェクトからビットマップを読み込む Stream メソッドに限定されます。

SkiaSharpFormsDemos プログラムの [Basic Bitmaps] ページでは、3 つの異なるソースからビットマップを読み込む方法を示します。

  • インターネット経由
  • 実行可能ファイルに埋め込まれているリソースから
  • ユーザーの写真ライブラリから

これら 3 つのソースの 3 つの SKBitmap オブジェクトは、BasicBitmapsPage クラスのフィールドとして定義されます。

public class BasicBitmapsPage : ContentPage
{
    SKCanvasView canvasView;
    SKBitmap webBitmap;
    SKBitmap resourceBitmap;
    SKBitmap libraryBitmap;

    public BasicBitmapsPage()
    {
        Title = "Basic Bitmaps";

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
        ...
    }
    ...
}

Web からのビットマップの読み込み

URL に基づいてビットマップを読み込むには、HttpClient クラスを使用できます。 HttpClient のインスタンスを 1 つだけインスタンス化して再利用する必要があるため、それをフィールドとして格納します。

HttpClient httpClient = new HttpClient();

iOS および Android アプリケーションで HttpClient を使用する場合は、トランスポート層セキュリティ (TLS) 1.2 のドキュメントの説明に従って、プロジェクトのプロパティを設定する必要があります。

await 演算子を HttpClient と共に使用するのが最も便利なので、BasicBitmapsPage コンストラクターではコードは実行できません。 代わりに、これは OnAppearing オーバーライドの一部になっています。 ここでの URL は、いくつかのサンプル ビットマップを含む Xamarin Web サイト上の領域を指します。 Web サイト上のパッケージを使用すると、ビットマップのサイズを特定の幅に変更するための仕様を追加できます。

protected override async void OnAppearing()
{
    base.OnAppearing();

    // Load web bitmap.
    string url = "https://developer.xamarin.com/demo/IMG_3256.JPG?width=480";

    try
    {
        using (Stream stream = await httpClient.GetStreamAsync(url))
        using (MemoryStream memStream = new MemoryStream())
        {
            await stream.CopyToAsync(memStream);
            memStream.Seek(0, SeekOrigin.Begin);

            webBitmap = SKBitmap.Decode(memStream);
            canvasView.InvalidateSurface();
        };
    }
    catch
    {
    }
}

Android オペレーティング システムでは、メイン スレッドで時間のかかる操作を実行しているため、SKBitmap.Decode メソッドで GetStreamAsync から返される Stream の使用時に例外が発生します。 このため、ビットマップ ファイルの内容は、CopyToAsync を使用して MemoryStream オブジェクトにコピーされます。

静的 SKBitmap.Decode メソッドは、ビットマップ ファイルのデコードを行います。 これは、JPEG、PNG、GIF ビットマップ形式で動作し、結果を内部 SkiaSharp 形式で格納します。 この時点で、PaintSurfaceハンドラーが表示を更新できるようにするために SKCanvasView を無効にする必要があります。

ビットマップ リソースの読み込み

コードの観点から見ると、ビットマップを読み込む最も簡単な方法は、ビットマップ リソースをアプリケーションに直接含める方法です。 SkiaSharpFormsDemos プログラムには、Media という名前のフォルダーが含まれています。これには、monkey.png という名前の 1 つのビットマップ ファイルを含む複数のビットマップ ファイルが含まれています。 プログラム リソースとして格納されているビットマップの場合は、[プロパティ] ダイアログを使用して、ファイルに埋め込みリソースビルド アクションを指定する必要があります。

各埋め込みリソースには、プロジェクト名、フォルダー、ファイル名で構成されるリソース ID があり、すべてピリオドで連結されます: SkiaSharpFormsDemos.Media.monkey.png。 このリソースにアクセスするには、そのリソース ID を Assembly クラスの GetManifestResourceStream メソッドの引数として指定します。

string resourceID = "SkiaSharpFormsDemos.Media.monkey.png";
Assembly assembly = GetType().GetTypeInfo().Assembly;

using (Stream stream = assembly.GetManifestResourceStream(resourceID))
{
    resourceBitmap = SKBitmap.Decode(stream);
}

この Stream オブジェクトは、SKBitmap.Decode メソッドに直接渡すことができます。

写真ライブラリからビットマップを読み込む

また、ユーザーはデバイスの画像ライブラリから写真を読み込むこともできます。 この機能は、Xamarin.Forms 自体によって提供されません。 この作業には、「画像ライブラリから写真を選択する」の記事で説明されているような依存関係サービスが必要です。

SkiaSharpFormsDemos プロジェクトの IPhotoLibrary.cs ファイルと、プラットフォーム プロジェクトの 3 つの PhotoLibrary.cs ファイルは、この記事から採用されています。 さらに、Android MainActivity.cs ファイルは記事内の説明に従って変更されており、iOS プロジェクトには、info.plist ファイルの下部の 2 つの行により写真ライブラリにアクセスする権限が付与されています。

BasicBitmapsPage コンストラクターは、TapGestureRecognizerSKCanvasView に追加して、タップの通知を受け取ります。 タップを受け取ると、Tapped ハンドラーが写真選択の依存関係サービスにアクセスし、PickPhotoAsync を呼び出します。 返される Stream オブジェクトは、SKBitmap.Decode メソッドに渡されます。

// Add tap gesture recognizer
TapGestureRecognizer tapRecognizer = new TapGestureRecognizer();
tapRecognizer.Tapped += async (sender, args) =>
{
    // Load bitmap from photo library
    IPhotoLibrary photoLibrary = DependencyService.Get<IPhotoLibrary>();

    using (Stream stream = await photoLibrary.PickPhotoAsync())
    {
        if (stream != null)
        {
            libraryBitmap = SKBitmap.Decode(stream);
            canvasView.InvalidateSurface();
        }
    }
};
canvasView.GestureRecognizers.Add(tapRecognizer);

Tapped ハンドラーは、SKCanvasView オブジェクトの InvalidateSurface メソッドも呼び出すことに注意してください。 これにより、PaintSurface ハンドラーへの新しい呼び出しが生成されます。

ビットマップを表示する

PaintSurface ハンドラーは、3 つのビットマップを表示する必要があります。 ハンドラーは、電話が縦モードであることを前提とし、キャンバスを垂直方向に均等に 3 分割します。

最初のビットマップは、最も単純な DrawBitmap メソッドで表示されます。 指定する必要があるのは X 座標と Y 座標です。ここで、ビットマップの左上隅を位置合わせする必要があります。

public void DrawBitmap (SKBitmap bitmap, Single x, Single y, SKPaint paint = null)

SKPaint パラメーターは定義されていますが、既定値が null に設定されているため、無視してかまいません。 ビットマップのピクセルは、単に 1 対 1 のマッピングを使用して表示画面のピクセルに転送されます。 この SKPaint 引数のアプリケーションについては、SkiaSharp の透明度に関する次のセクションを参照してください。

プログラムは、ビットマップのピクセル サイズを Width および Height プロパティで取得できます。 これらのプロパティを使用すると、プログラムは、キャンバスの上 3 分の 1 の中央にビットマップを配置する座標を計算できます。

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

    canvas.Clear();

    if (webBitmap != null)
    {
        float x = (info.Width - webBitmap.Width) / 2;
        float y = (info.Height / 3 - webBitmap.Height) / 2;
        canvas.DrawBitmap(webBitmap, x, y);
    }
    ...
}

他の 2 つのビットマップは、SKRect パラメーターを持つ DrawBitmap の 1 つのバージョンで表示されます。

public void DrawBitmap (SKBitmap bitmap, SKRect dest, SKPaint paint = null)

DrawBitmap の 3 つ目のバージョンには、表示するビットマップの四角形のサブセットを指定するための 2 つの SKRect 引数がありますが、このバージョンはこの記事では使用しません。

埋め込みリソース ビットマップから読み込まれたビットマップを表示するコードを次に示します。

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    ...
    if (resourceBitmap != null)
    {
        canvas.DrawBitmap(resourceBitmap,
            new SKRect(0, info.Height / 3, info.Width, 2 * info.Height / 3));
    }
    ...
}

ビットマップは四角形の寸法に引き伸ばされるため、サルは次のスクリーンショットで水平方向に引き伸ばされています。

A triple screenshot of the Basic Bitmaps page

3 番目の画像も (プログラムを実行して自分の画像ライブラリから写真を読み込んだ場合にのみ表示される) は、四角形内に表示されますが、四角形の位置とサイズはビットマップの縦横比を維持するように調整されます。 この計算は、ビットマップのサイズとターゲットの四角形に基づいて倍率を計算し、その領域内で四角形を中央に配置する必要があるため、もう少し複雑です。

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    ...
    if (libraryBitmap != null)
    {
        float scale = Math.Min((float)info.Width / libraryBitmap.Width,
                               info.Height / 3f / libraryBitmap.Height);

        float left = (info.Width - scale * libraryBitmap.Width) / 2;
        float top = (info.Height / 3 - scale * libraryBitmap.Height) / 2;
        float right = left + scale * libraryBitmap.Width;
        float bottom = top + scale * libraryBitmap.Height;
        SKRect rect = new SKRect(left, top, right, bottom);
        rect.Offset(0, 2 * info.Height / 3);

        canvas.DrawBitmap(libraryBitmap, rect);
    }
    else
    {
        using (SKPaint paint = new SKPaint())
        {
            paint.Color = SKColors.Blue;
            paint.TextAlign = SKTextAlign.Center;
            paint.TextSize = 48;

            canvas.DrawText("Tap to load bitmap",
                info.Width / 2, 5 * info.Height / 6, paint);
        }
    }
}

画像ライブラリからビットマップがまだ読み込まれていない場合は、画面をタップするようにユーザーに求めるテキストが else ブロックに表示されます。

ビットマップはさまざまな透明度で表示できます。方法については、「SkiaSharp の透明度」を参照してください。