Search code examples
wpfwriteablebitmapex

WriteableBitmapEx alpha blend color issue


I'm trying to use WriteableBitmap with the WriteableBitmapEx extension methods. The code sample is below along with two screenshots. The first screenshot is what my code generates and the second what I'm expecting (hand drawn via Paint.Net).

Am I using WriteableBitmapEx incorrectly, misunderstanding how it works or is it a bug?

This is on .Net 4.6.1 and WriteableBitmapEx 1.6.2.

    public MainWindow()
    {
        InitializeComponent();

        _bitmap = BitmapFactory.New(1000, 1000);

        _bitmap.FillRectangle(0,0,1000,1000, ColorToInt(Colors.White), true);

        _bitmap.FillRectangle(100, 100, 500, 500, ColorToInt(_color), true);
        _bitmap.FillRectangle(150, 150, 550, 550, ColorToInt(_color), true);
        _bitmap.FillRectangle(200, 200, 600, 600, ColorToInt(_color), true);
        _bitmap.FillRectangle(250, 250, 650, 650, ColorToInt(_color), true);
        _bitmap.FillRectangle(300, 300, 700, 700, ColorToInt(_color), true);
        _bitmap.FillRectangle(350, 350, 750, 750, ColorToInt(_color), true);
        _bitmap.FillRectangle(400, 400, 800, 800, ColorToInt(_color), true);

        Image1.Source = _bitmap;
    }

    private readonly Color _color = Color.FromArgb(25, 0, 0, 255);
    private WriteableBitmap _bitmap;

    private int ColorToInt(Color c)
    {
        return c.A << 24 | c.R << 16 | c.G << 8 | c.B;
    }

Image my code generates. Blue channel is gradually lost?

Paint.Net generating what I'd expect


Solution

  • The WriteableBitmap uses pre-multiplied alpha internally, so when you convert a color you have to take that into account and multiply your color channels with alpha.

    You can simply use the ConvertColor method that comes with WBX or roll your own similar to this:

        public static int ConvertColor(Color color)
        {
            var col = 0;
    
            if (color.A != 0)
            {
                var a = color.A + 1;
                col = (color.A << 24)
                  | ((byte)((color.R * a) >> 8) << 16)
                  | ((byte)((color.G * a) >> 8) << 8)
                  | ((byte)((color.B * a) >> 8));
            }
    
            return col;
         }