Why is the color after Image.Clear (x) not exactly the same as the color x?

The sample code for this problem is pretty much self-explanatory, so:

[Fact]
private void Color_in_should_equal_color_out()
{
    var bitmap = new Bitmap(128,128,PixelFormat.Format32bppArgb);   
    var color = Color.FromArgb(30,60,90,120);         
    using (var g = Graphics.FromImage(bitmap))
    {
        g.Clear(color);
    }

    var result = bitmap.GetPixel(0,0);

    Assert.Equal(color, result);
}

      

In this case, I would expect the background color to be identical to the color I cleared it to. Instead, I get this:

Assert.Equal() Failure 

Expected: Color [A=30, R=60, G=90, B=120]
Actual:   Color [A=30, R=59, G=93, B=119]

      

How is this possible?

Some pass:

Color.FromArgb(0, 0, 0, 0);
Color.FromArgb(255, 255, 255, 255);

      

A few more examples that fail:

Expected: Color [A=32, R=64, G=96, B=128]
Actual:   Color [A=32, R=63, G=95, B=127]

Expected: Color [A=128, R=192, G=32, B=16]
Actual:   Color [A=128, R=191, G=31, B=15]

Expected: Color [A=32, R=192, G=127, B=90]
Actual:   Color [A=32, R=191, G=127, B=87]

      

+1


source to share


1 answer


@Redwyre's comment is correct (but I don't have enough reputation to comment). So I'll give you Vincent Povirk's comment on Drawing PixelFormat32bppPARGB images with GDI + uses the regular formula instead of the pre-multiplied one :

The format of your foreground image doesn't matter (given that it has alpha) because you are setting it to Gdiplus :: Color. Color values ​​are defined as non-pre-multiplex, so gdiplus multiplies the components by the alpha value when it clears the foreground image. An alternative would be for the color values ​​to have a different meaning depending on the format of the render target, and therein lies the madness.



The example in this post uses Gdiplus directly, but then also does System.Drawing.Graphics, as you can see here in .NET Sources.

The various values ​​you see are directly related to rounding from color channel value to pre-multiplying and back using 8-bit arithmetic. (For example, from your last example, alpha = 32 and B = 90: 90 * 32/255 = 11.2+ truncates to 11, and then 11 * 255/32 = 87.6+ truncates to 87.)

+1


source







All Articles