Почему цвет после Image.Clear(x) не совсем совпадает с цветом x?

Пример кода для этой проблемы не требует пояснений, поэтому:

[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]

1 ответ

Решение

Комментарий @redwyre правильный (но у меня недостаточно репутации, чтобы комментировать). Поэтому я отсылаю вас к комментарию Винсента Повирка на Рисование изображений PixelFormat32bppPARGB с GDI+ использует обычную формулу вместо предварительно умноженной:

Формат изображения переднего плана не имеет значения (учитывая, что оно имеет альфа-канал), потому что вы устанавливаете его в Gdiplus::Color. Цветовые значения определены как неумноженные, поэтому gdiplus умножает компоненты на альфа-значение, когда очищает изображение переднего плана. Альтернативой было бы, чтобы значения Color имели различное значение в зависимости от формата цели рендеринга, и в этом заключается безумие.

Пример в этом посте использует Gdiplus напрямую, но также и System.Drawing.Graphics, как вы можете видеть здесь в источниках.NET.

Различные значения, которые вы видите, напрямую связаны с циклическим переключением от значения цветового канала к предварительно умноженному значению и обратно с использованием 8-битной арифметики. (Например, из вашего последнего примера альфа =32 и B=90: 90*32/255 = 11,2+ усекает до 11, затем обратно 11*255/32 = 87,6+ усекает до 87).

Другие вопросы по тегам