这是我写的测试,目前将失败:
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的构造函数会改变一个像素?
似乎是一个更深层次的嵌套问题。通过图像变量的简单转换替换Bitmap
构造函数,问题就消失了。这可能解决了这个问题,但并没有解释它。通过以下过程,我甚至可以在Paint.Net中重现问题:
所以看起来它可能是一个更深层次的问题,不是由Bitmap或Image类引起的,而是可能是由GDI +等类似的更深层次的功能引起的。
我刚刚写了一个新的测试来找出所有受影响的颜色:
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
班。
答案 0 :(得分:8)
我使用Paint.NET检查了使用您的代码保存的PNG文件,像素颜色正好是unusableColor
如果您使用以下内容更改阅读代码:
using (Bitmap bitmap = (Bitmap)Image.FromFile(tempFile))
{
retrievedColor = bitmap.GetPixel(0, 0);
}
一切正常
答案 1 :(得分:2)
你可以使用克隆方法:
using (var image = Image.FromFile(tempFile))
{
using (var bitmap = image.Clone() as Bitmap)
{
retrievedColor = bitmap.GetPixel(0, 0);
}
}
问题在于“新位图(图像)”,因为它会创建新实例。如果查看位图的构造函数,它会创建新的透明图像并绘制源图像。图形对象具有平滑模式属性,用于绘制质量。默认是没有抗锯齿。
这是Bitmap的构造函数:
Graphics graphics = null;
try
{
graphics = Graphics.FromImage(this);
graphics.Clear(Color.Transparent);
graphics.DrawImage(original, 0, 0, width, height);
}
finally
{
if (graphics != null)
{
graphics.Dispose();
}
}
因此,如果您只是从文件或克隆加载图像,则位图数据是相同的。