画像の背景に対する透明化

Last Updated 2012/04/27


ツールバー上に配置するツールボタンなどに設定する画像は背景に対して透明化しなければなりません。しかし、WPF にはこれを実現する簡単な機能がありません。このページではこの問題について考えます。


背景に対して透明化することができる画像の形式は、ICON、GIF、PNG しかありません。GIF はもはや使う人はいないでしょうから事実上 PNG が主流になると思います。そこで、このページでは PNG を背景に対して透明化する手順について説明します。

さて、System.Drawing.Bitmap クラスには MakeTransparent メソッドがあって透明化が比較的簡単に実現できます。一方、WPF にはこのような便利な機能がありません。WPF も .NET Framework の一部ですからこだわる必要はないのかもしれませんが、ここは WPF にこだわることにします。メソッドの形式にしておきましたので、再利用に便利だと思います。

// 背景に対して透明化した PNG 画像を取得する
// source : 元の画像
// transparentColor : 透明化する色
private BitmapSource GetPngWithTransparentBackground(BitmapSource source, Color transparentColor)
{
  BitmapFrame frame = BitmapFrame.Create(source);
  var writeableBitmap = new WriteableBitmap(frame);

  if (writeableBitmap.Format.BitsPerPixel != 32)
  {
    MessageBox.Show("32 bpp の画像を指定してください", "警告");
    return null;
  }

  var rect = new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight);
  var bytesPerPixel = (writeableBitmap.Format.BitsPerPixel + 7) / 8; // 1 ピクセル当たりのバイト数(4 になるはず)
  var stride = writeableBitmap.PixelWidth * bytesPerPixel; // 幅方向のバイト数
  var arraySize = stride * writeableBitmap.PixelHeight;

  var sourcePixels = new byte[arraySize];
  var newPixels = new byte[arraySize];

  writeableBitmap.CopyPixels(sourcePixels, stride, 0);
  writeableBitmap.CopyPixels(newPixels, stride, 0);

  byte a, r, g, b;
  int index; // 左上隅からのバイトのインデックス

  for (int y = 0; y < writeableBitmap.PixelHeight; ++y)
  {
    for (int x = 0; x < writeableBitmap.PixelWidth; ++x)
    {
      index = x * bytesPerPixel + y * stride;
      b = sourcePixels[index];
      g = sourcePixels[index + 1];
      r = sourcePixels[index + 2];
      a = sourcePixels[index + 3];

      Color color = Color.FromArgb(a, r, g, b);

      if (color.Equals(transparentColor))
      {
        newPixels[index + 3] = 0; // アルファ値を持つバイトを 0 に設定する
      }
    }
  }

  writeableBitmap.WritePixels(rect, newPixels, stride, 0);

  return writeableBitmap;
}

上記のメソッドを利用するコードは以下のとおりです。ここでは image1 にすでに PNG 画像が表示されているとし、Colors.White のピクセルを透明化します。

private void button1_Click(object sender, RoutedEventArgs e)
{
  image1.Source = this.GetPngWithTransparentBackground((BitmapSource)image1.Source, Colors.White);
}

−以上−