8

コードを介して (またはテンプレートを使用して) WPF のイメージの色を変更することは可能ですか?

タイルに適用する必要がある画像があるとします。デフォルトでは前景色が白で、背景が透明です。次の PNG のようなもの (ここのどこかにあります!):

新しい PNG を追加

さまざまな画像を追加する代わりに、さまざまな色で、白を操作したいだけで、黒に変更すると言います。

それが可能であれば、私が何をする/調べる必要があるかについて、誰かが私にいくつかの指針を与えることができますか.

4

1 に答える 1

10

これを行う 1 つの方法は、BitmapDecoderクラスを使用して生のピクセル データを取得することです。WriteableBitmapその後、ピクセルを変更し、その変更されたピクセル データから新しいものを作成できます。

// Copy pixel colour values from existing image.
// (This loads them from an embedded resource. BitmapDecoder can work with any Stream, though.)
StreamResourceInfo x = Application.GetResourceStream(new Uri(BaseUriHelper.GetBaseUri(this), "Image.png"));
BitmapDecoder dec = BitmapDecoder.Create(x.Stream, BitmapCreateOptions.None, BitmapCacheOption.Default);
BitmapFrame image = dec.Frames[0];
byte[] pixels = new byte[image.PixelWidth * image.PixelHeight * 4];
image.CopyPixels(pixels, image.PixelWidth*4, 0);

// Modify the white pixels
for (int i = 0; i < pixels.Length/4; ++i)
{
    byte b = pixels[i * 4];
    byte g = pixels[i * 4 + 1];
    byte r = pixels[i * 4 + 2];
    byte a = pixels[i * 4 + 3];

    if (r == 255 &&
        g == 255 &&
        b == 255 &&
        a == 255)
    {
        // Change it to red.
        g = 0;
        b = 0;

        pixels[i * 4 + 1] = g;
        pixels[i * 4] = b;
    }


}

// Write the modified pixels into a new bitmap and use that as the source of an Image
var bmp = new WriteableBitmap(image.PixelWidth, image.PixelHeight, image.DpiX, image.DpiY, PixelFormats.Pbgra32, null);
bmp.WritePixels(new Int32Rect(0, 0, image.PixelWidth, image.PixelHeight), pixels, image.PixelWidth*4, 0);
img.Source = bmp;

これは一応機能しますが、問題があります。暗い背景で表示すると、結果は次のようになります。

黒の背景に赤い十字

ご覧のとおり、白い枠のようなものがあります。ここで起こったことは、白い十字にアンチエイリアス処理されたエッジがあったということです。つまり、エッジの周りのピクセルは実際には半透明のグレーの色合いです。

ピクセル変更ループでもう少し洗練された手法を使用して、これに対処できます。

if ((r == 255 &&
        g == 255 &&
        b == 255 &&
        a == 255) ||
    (a != 0 && a != 255 &&
        r == g && g == b && r != 0))
{
    // Change it to red.
    g = 0;
    b = 0;

    pixels[i * 4 + 1] = g;
    pixels[i * 4] = b;
}

黒の背景にすると、次のようになります。

白枠のない黒地に赤

ご覧のとおり、それは正しいように見えます。(OK、赤ではなく黒が必要でしたが、基本的なアプローチはどのターゲット カラーでも同じです。)

EDIT 2015/1/21 ar_j がコメントで指摘したように、Prgba 形式には事前乗算が必要です。私が示した例では、実際には無視しても安全ですが、カラー チャネルを 0 に設定する以外の方法で変更する場合は、各値を で乗算する必要があります(a/255)。たとえば、G チャネルの aj_j が示すようにpixels[i * 4 + 1] = (byte)(g * a / 255);g私のコードではゼロであるため、これは違いはありませんが、非原色の場合はそのようにする必要があります。

ここでは、透明度が機能していることを示すために、グラデーションの塗りつぶしの背景を使用しています。

グラデーションの背景に赤い十字

変更されたバージョンを書き出すこともできます。

var enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bmp));
using (Stream pngStream = File.OpenWrite(@"c:\temp\modified.png"))
{
    enc.Save(pngStream);
}

結果は次のとおりです。

修正されたPNG

赤い十字が表示され、StackOverflow が使用している背景色の上に表示されます。(私がこれを書いているときは白ですが、いつか再設計されるかもしれません。)

これが使用したい画像で機能するかどうかは、「白」の定義に依存するため、特定するのが難しいです.画像がどのように生成されたかによっては、物事が非常にわずかにオフホワイトであることがわかる場合があります. (特に端の周り)、さらに微調整が必​​要になる場合があります。しかし、基本的なアプローチは問題ないはずです。

于 2013-12-31T14:40:51.643 に答える