LoginSignup
0
0

More than 3 years have passed since last update.

【C#】 GDI+でカラー オーバーレイ(風)

Last updated at Posted at 2020-08-19

概要

@Kyome さんのRunCat の記事

・Windowsのタスクバーでもネコ走らせてみた🐈 - Qiita
https://qiita.com/Kyome/items/47aac4979933dac12263

を見ていて、個人的に猫に自由に任意の色を付けたくなったので、WinForm (GDI+) で カラー オーバーレイ が出来ないか試してみた。

カラー オーバーレイ とは?

Photoshopの レイヤー効果 でおなじみ(?)のやつ。
レイヤーの描画ピクセルのに色情報を任意の色に上書きするやつ。
私は良くレイヤーの描画モードを スクリーン にした単色レイヤーを重ね合わせえて色味調整を行うことがよくあるのだけど。カラー オーバーレイ だとカラーピッカーで選択中の色がリアルタイムで反映されるので、 スクリーン レイヤーの色味調整でよく使ってる。

SS.png

開発環境

プログラムは @Kyome22 さんの RunCat for windows を拝借させていただきました。ありがとうございます!

概略

  • Graphics.DrawImage のオプションで使える System.Drawing.Imaging.ImageAttributes で描画時に色を置換させる。
  • 置換情報は ImageAttributes.SetRemapTable()System.Drawing.Imaging.ColorMap をセットして行う。
  • 画像ソースは 白1色 + α の32bit画像
  • 白に対して、置換色表はα = 0~255の 256パターン 用意する。
  • 指定色のα値も反映させるため、置換後の色のα値にもかけ合わせる。
ColorMapはαの深度分.cs
ColorMap[] map = new ColorMap[256];
for (int j = 0; j < map.Length; j++)
{
    colorMap[j] = new ColorMap()
    {
        OldColor = Color.FromArgb(j, 255, 255, 255),
        NewColor = Color.FromArgb(j * newColor.A / 255, newColor.R, newColor.G, newColor.B),
    };
}

ss_00.png
ss_01.png
ss_02.png

こんな塩梅。
Color.Transparent もちゃんと効くのがありがたい。ただ、今回 カラー オーバーレイ(風) としているのは、オーバーレイ対象が描画ピクセルでなく白だけだから。白(+α)以外の色があるとダメなので、実は オーバーレイ としては若干用足らず(苦笑)。

サンプル ソース

カラー オーバーレイを試すにあたり、 @Kyome22 さんの RunCat for windows を勝手に利用させていただきました。ありがとうございます!

バック バッファImageオブジェクト で白猫の画像の枚数分予め作っておき、下記メソッドが呼ばれたら バックバッファ の内容を更新し、Iconオブジェクト を再生成する。 (バック バッファ はタスクトレイなので一応16x16。)

カラー オーバーレイ 抜粋.cs
private void SetIconColor(Color catColor, Color bgColor)
{
    Bitmap[] images = new Bitmap[]
    {
        Resources.cat16_0,
        Resources.cat16_1,
        Resources.cat16_2,
        Resources.cat16_3,
        Resources.cat16_4,
    };
    ImageAttributes attr = new ImageAttributes();

    // remap color table
    {
        ColorMap[] colorMap = new ColorMap[256];
        for (int j = 0; j < colorMap.Length; j++)
        {
            colorMap[j] = new ColorMap()
            {
                OldColor = Color.FromArgb(j, 255, 255, 255),
                NewColor = Color.FromArgb(j * catColor.A / 255, catColor.R, catColor.G, catColor.B),
            };
        }
        attr.SetRemapTable(colorMap);
    }

    //  recreate icons
    for (int i  = 0; i < imgIconBufs.Length; i++)
    {
        Bitmap imgBuf = imgIconBufs[i];
        Bitmap imgCat = images[i];
        Icon ico = icons[i];
        // bg
        {
            Graphics g = Graphics.FromImage(imgBuf);
            g.Clear(bgColor);
            g.DrawImage(imgCat, new Rectangle(new Point(0, 0), imgCat.Size), 0, 0, imgCat.Width, imgCat.Height, GraphicsUnit.Pixel, attr);
        }
        if(icons[i] != null)
        {
            // 再作成時、古いHICONは必ず廃棄する!
            DestroyIcon(icons[i].Handle);
            icons[i] = null;
        }
        icons[i] = Icon.FromHandle(imgBuf.GetHicon());
    }
}

感想

一応それっぽいのは出来たけど、Bitmapクラスから 生成した Iconクラス を明示的に破棄するためにWin32APIの DestroyIcon を呼ぶ必要があって プラットフォーム 呼び出し ( P/Invoke) を使ってしまったのが若干つらい。とは言っても、64bit モジュールがちゃんとがある普通のWin32API だからそんなに気にしなくてもだけど。でもやっぱり.NETは可能な限りマネージド コードだけで行きたい。。。(びば!AnyCPU!)

ただ、最初はピクセル操作になると思っていたので Marshal.Copy するくらいならと Unsafe も覚悟していたけど、一応カラー オーバーレイ(?) 部分だけはマネージド コードだけなので、そこは助かった。今度は ColorMatrix も試してみたい。ColorMapで愚直に対応表を作るとすると。2^32 - 14,294,967,295 パターンにもなり、とても現実的ではないので。。。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0