C# 为什么图像后的颜色.Clear(x)与颜色x不完全相等?

C# 为什么图像后的颜色.Clear(x)与颜色x不完全相等?,c#,graphics,gdi+,xunit.net,system.drawing.color,C#,Graphics,Gdi+,Xunit.net,System.drawing.color,此问题的示例代码基本上是自解释的,因此: [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))

此问题的示例代码基本上是自解释的,因此:

[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);
}
在本例中,我希望背景的颜色与我清除背景的颜色相同。相反,我得到的是:

Assert.Equal() Failure 

Expected: Color [A=30, R=60, G=90, B=120]
Actual:   Color [A=30, R=59, G=93, B=119]
这怎么可能呢

一些通行证:

Color.FromArgb(0, 0, 0, 0);
Color.FromArgb(255, 255, 255, 255);
还有一些失败的例子:

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]

@雷德怀尔的评论是正确的(但我自己没有足够的声誉来评论)。因此,我将向您介绍Vincent Povirk的评论:

前景图像的格式并不重要(假设它有alpha),因为您将其设置为Gdiplus::Color。颜色值定义为非预乘,因此gdiplus在清除前景图像时将分量乘以alpha值。另一种选择是颜色值根据渲染目标的格式具有不同的含义,而这种方式就是疯狂

该文章中的示例直接使用Gdiplus,但是System.Drawing.Graphics也使用Gdiplus,正如您在.NET源代码中看到的那样


您看到的不同值与使用8位算术从颜色通道值到预乘值的往返直接相关。(例如,从上一个示例中,alpha=32和B=90:90*32/255=11.2+截断为11,然后返回11*255/32=87.6+截断为87。)

PS:我已经搜索过,不认为这是重复的,我能找到的最接近的匹配是,我尝试了PixelFormat32bppPArgb和PixelFormat32bppArgb,它们产生了相同的结果(正如64位PArgb等)我不希望前后计算的alpha都产生完全相同的舍入误差,否则对alpha通道进行预乘有什么意义?同样使用上面的主要示例(使用
[A=30,R=60,G=90,B=120]
)G怎么可能最终变成93呢?对于你突出显示的90->87示例,它必须向零舍入11.2。对于90->93示例,它只有在离零舍入10.5时才有效(即使这样,也应该是94而不是93)。正是结果的不一致性让我认为“bug”是一种可能性。@nathanchere-90->93的例子也很有效,因为结果是四舍五入的。我在上面说了截断,因为那个例子就是这样,但算术实际上是四舍五入的。94 vs 93的问题是计算机算术的标准问题,并不总是四舍五入我们在学校学到的方法(例如,0.5四舍五入)。事实上,计算机算术经常向0四舍五入。同样,同一体系结构上的不同指令可能会有不同的四舍五入,例如,通用寄存器中的常规指令与SIMD寄存器中的SIMD指令(如SSE)@nathanchere-W.r.t.你关于xPArgb和xArgb使用的问题-网络上关于GDI+内部结构的信息非常少。我已经看到很多提示,它使用的本机内部格式总是预乘alpha,尽管它会以其他本机格式操作位图(例如,如果您创建一个具有特定像素格式的GDI位图,然后将其句柄传递给GDI+)。考虑到这一点,您指定的像素格式似乎有时不是内部表示,而仅仅是它将使用的I/O表示,如果您(例如)递给它一个字节数组。