« 【#UWP】 CompactOverlay モード: Picture in Picture というか、「最前面に表示」するウィンドウを作る | トップページ

2017年8月23日 (水)

【#UWP】 ビットマップの表示色を変える (Win2D.uwp 経由で Direct2D を使う)

例えば、 モノクロ 2 値やグレイスケールの画像を用意しておいて、表示するときに色を変えたいとかあるわけですよ。

20170823_clockpanel01

上の画像は、 白と透明の  2 値画像ですが、 そのまま黒バックの上に表示すると、 次の画像のようになります (真ん中にカラーピッカーを追加しています)。

20170823_colormatrixeffect01

メモリー上のビットマップ内部では、 それぞれのピクセルは (R,G,B) = (0xff, 0xff, 0xff) か (0, 0, 0) のどちらかになっています。 そこで例えば、 (0xff, 0xff, 0xff) のピクセルだけを (0xff, 0, 0) に変えてやれば、 赤い画像に変わるわけです。

が、 それをピクセルごとにちまちまやってたんでは、 時間が掛かってしょうがありません。
昔、 8 x 8 = 64 ピクセルとか、 16 x 16 = 256 ピクセルとかを扱ってればよかった時代には、 当時の超非力な CPU でも、 256 回のループなんてのはまぁなんとかなりました (←いつの時代だよ!? w)。
今や、 1000 x 1000 ピクセルのビットマップとか平気で扱わなきゃならない時代。 1000 x 1000 ピクセルを全部舐めたら 100 万回ですよ!?

そんなわけで、 Direct2D の出番です♪

 

C# で書いている UWP アプリから DirectX を使うには、 SharpDX ってのもあります。
SharpDX は、 ほぼ DirectX をラップしただけの API なので、 生 DirectX プログラミングできるスキルがないと、 使うのはちょっと大変です。
例: WinRT/Metro TIPS:SharpDXを使ってDirectXで音声ファイルを再生するには?[ユニバーサルWindowsアプリ開発]

で、 それよりもっと WinRT API 寄りにガッツリとラップしてくれたのが、 Win2D  です。

横文字得意な人向け ⇒ Introducing Win2D: GPU accelerated 2D graphics programming in the Windows Runtime - Building Apps for WindowsBuilding Apps for Windows (2014/9/5)

Win2D でどんなことが出来るのかは、 サンプルアプリを見るのが早いです。
Win2D Example Gallery

20170823_win2dsamplegallery01

 

てことで、 Win2D を使って画像の色を変換するコードを作ってみましょう。
VS 2017 + C# 7 で書いています。

■ Win2D.uwp を導入する

NuGet から Win2D.uwp パッケージ をプロジェクトに導入します。

■ Win2D のキャンバスを配置する

XAML に Win2D の Canvas コントロールを置きます。
※ Windows.UI.Xaml.Controls の Canvas コントロールとは、 まったくの別物です。

まず、Microsoft.Graphics.Canvas.UI.Xaml 名前空間のエイリアスを定義して……

<Page
  ……略……
  xmlns:Win2DC="using:Microsoft.Graphics.Canvas.UI.Xaml"
  >

適当なところにWin2D の Canvas コントロールを置きます。

<Grid>
  <Win2DC:CanvasControl x:Name="win2DCanvas" />
</Grid>


■ Win2D のキャンバスに画像を表示してみる

その一連の処理は、 Win2D Canvas コントロールの CreateResources イベントと Draw イベントで行います。

まず CreateResources イベントが叩かれるので、 そこで画像ファイルを Win2D の CanvasBitmap オブジェクトに読み込ませる処理をスタートします (非同期で書いて、 そのタスクを Win2D に返します)。

そして Draw イベントで、 DrawingSession オブジェクトの DrawImage メソッドを呼び出して、
先ほどの CanvasBitmap オブジェクトを描画させます (DrawingSession オブジェクトは、 Draw イベントの引数として渡されます)。

public MainPage()
{
  this.InitializeComponent();

  // 1. Win2D のビットマップ (ここに画像を読み込ませる)
  CanvasBitmap bitmap = null;

  // 2. Win2D Canvas の CreateResources イベントで画像を読み込む
  win2DCanvas.CreateResources += (s, e) =>
  {
    // s is CanvasControl
    // e is CanvasCreateResourcesEventArgs
    e.TrackAsyncAction(LoadImageAsync(s).AsAsyncAction());

    // 実際に画像を読み込むメソッド
    async Task LoadImageAsync(CanvasControl canvas)
    {
      bitmap = await CanvasBitmap.LoadAsync(canvas, "Assets/ClockPanel.png");
      // ここでは 1 個だけだが、必要なだけ何個でも!
    }
  };

  // 3. Win2D Canvas の Draw イベントで bitmap を表示する
  win2DCanvas.Draw += (s, e) =>
  {
    // s is CanvasControl
    // e is CanvasDrawEventArgs
    e.DrawingSession.DrawImage(bitmap, 0, 0);
  };
}


■ 表示する画像に Direct2D のエフェクトを掛ける

さきほどは、 Win2D の CanvasBitmap オブジェクトをそのまま表示しました。

エフェクトを掛けるには、 CanvasBitmap オブジェクトにエフェクトを掛けた結果を、 先ほどと同様に DrawingSession.DrawImage メソッドに渡せば OK です。

例えば、 白色をカラーピッカーで選んだ色に変えるには、 Win2D の ColorMatrixEffect を使います。

// 上のコードの win2DCanvas.Draw イベントハンドラーだけを修正

// 3. Win2D Canvas の Draw イベントで bitmap を表示する
win2DCanvas.Draw += (s, e) =>
{
  // s is CanvasControl
  // e is CanvasDrawEventArgs

  // 単にビットマップをそのまま表示するなら、↓これだけ
  //e.DrawingSession.DrawImage(bitmap, 0, 0);

  // ビットマップにエフェクトを掛ける
  Color c = colorPicker.Color;
  float r = c.R / 255.0f;
  float g = c.G / 255.0f;
  float b = c.B / 255.0f;
  var colorMatrixEffect = new ColorMatrixEffect
  {
    Source = bitmap,
    ColorMatrix = new Matrix5x4
    {
      M11 = r, M12 = 0, M13 = 0, M14 = 0,
      M21 = 0, M22 = g, M23 = 0, M24 = 0,
      M31 = 0, M32 = 0, M33 = b, M34 = 0,
      M41 = 0, M42 = 0, M43 = 0, M44 = 1,
      M51 = 0, M52 = 0, M53 = 0, M54 = 0
    },
  };

  // エフェクトを掛けた結果を表示する
  e.DrawingSession.DrawImage(colorMatrixEffect, 0, 0);
};


■ Win2D のキャンバスに再描画させる

再描画させるには、 Win2D Canvas オブジェクトの Invalidate メソッドを呼び出します (すると、 Draw イベントが叩かれます)。

例えば、 カラーピッカーで選択している色が変わったときに再描画させるには、

// 4. 必要に応じて Win2D Canvas を再描画
colorPicker.ColorChanged += (s, e) =>
{
  win2DCanvas.Invalidate();
};


■ 完成♪

サンプルコードの全体は、 GitHub の Win2DColorMatrixEffect に置いてあります。

20170823_colormatrixeffect02

20170823_colormatrixeffect03

|

« 【#UWP】 CompactOverlay モード: Picture in Picture というか、「最前面に表示」するウィンドウを作る | トップページ

* プログラミング ( Metro スタイル )」カテゴリの記事

プログラミング」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/209349/65700202

この記事へのトラックバック一覧です: 【#UWP】 ビットマップの表示色を変える (Win2D.uwp 経由で Direct2D を使う):

« 【#UWP】 CompactOverlay モード: Picture in Picture というか、「最前面に表示」するウィンドウを作る | トップページ