Вот тест, который я написал и который в настоящее время не пройдёт:
var unusableColor = Color.FromArgb(13, 19, 20, 19);
var retrievedColor = Color.Empty;
var tempFile = Path.GetTempFileName();
using (var bitmap = new Bitmap(1, 1))
{
bitmap.SetPixel(0, 0, unusableColor);
bitmap.Save(tempFile, ImageFormat.Png);
}
using (var image = Image.FromFile(tempFile))
// This will lead to the error
using (var bitmap = new Bitmap(image))
// But this will work
//using (var bitmap = (Bitmap)image)
{
retrievedColor = bitmap.GetPixel(0, 0);
}
Assert.That(retrievedColor, Is.SameAs(unusableColor));
Если вы посмотрите на retrievedColor
, вы обнаружите, что оно будет таким же, как Color.FromArgb(13, 19, 19, 19)
. Таким образом, разница будет в том, что зеленая часть изменилась с 20 на 19.
Есть идеи, почему это происходит или при каких условиях конструктор растрового изображения изменит пиксель?
Обновление
Кажется, это более глубокая вложенная проблема. Заменив конструктор Bitmap
простым преобразованием переменной изображения, проблема исчезнет. Это может решить проблему, но не объясняет. Более того, я смог воспроизвести проблему даже в Paint.Net, выполнив следующую процедуру:
- Откройте Paint.Net и создайте новое изображение (размер не имеет значения)
- Выбрать все ( Ctrl + A )
- Снять выделение ( Del )
- Открыть диалоговое окно цвета ( F8 )
- Введите вышеуказанные значения для RGB (19, 20, 19) и внизу прозрачность (13).
- Выберите инструмент заливки ( F )
- Заполните цвет пустым изображением
- Выберите инструмент выбора цвета ( K )
- Щелкните где-нибудь на вашем новом изображении и посмотрите цветовой диалог
Так что, похоже, это более серьезная проблема, вызванная не классом Bitmap или Image, а, возможно, более глубокой функциональностью, такой как GDI + или чем-то подобным.
Обновление 2
Я только что написал новый тест, чтобы выяснить все затронутые цвета:
for (int a = 0; a < 256; a++)
{
for (int r = 0; r < 256; r++)
{
for (int g = 0; g < 256; g++)
{
for (int b = 0; b < 256; b++)
{
using (var bitmap = new Bitmap(1, 1))
{
var desiredColor = Color.FromArgb(a, r, g, b);
bitmap.SetPixel(0, 0, desiredColor);
// This will fail in a lot of colors with a low alpha channel value
using (var copiedBitmap = new Bitmap(bitmap))
// This will work, cause the information is entirely copied.
//using (var copiedBitmap = (Bitmap)bitmap.Clone())
{
var retrievedColor = copiedBitmap.GetPixel(0, 0);
if (desiredColor != retrievedColor)
{
Debug.Print(desiredColor + " != " + retrievedColor);
}
}
}
}
}
}
Пожалуйста, не позволяйте ему работать полностью сам по себе, потому что потребуется много времени, чтобы закончить, и он также обнаружит множество различий. Но что вы можете видеть, если вы поиграете с прозрачностью (установив 1 или 10), то увидите, что значения RGB используют это как некоторую битовую глубину.
Таким образом, проблема возникает, если вы создаете новое растровое изображение из существующего, которое использует значения низкой прозрачности. Кажется, что истинная причина кроется в GDI, ядре или где-то еще в этой области и не может быть решена из .Net.
Просто имейте в виду, что цвет может измениться, вызвав конструктор растрового изображения, если цвет имеет низкое значение прозрачности. Если вам действительно нужны исходные цвета, чтобы остаться в живых во втором экземпляре, вместо этого используйте (Bitmap)myBitmap.Clone()
или если вы загружаете его с диска, используйте (Bitmap)Image.FromFile(filename)
, потому что Image
- это только абстрактный класс, который обычно создается через класс Bitmap
.