This is a bit puzzling here. The following code is part of a little testing application to verify that code changes didn't introduce a regression. To make it fast we used memcmp
which appears to be the fastest way of comparing two images of equal size (unsurprisingly).
However, we have a few test images that exhibit a rather surprising problem: memcmp
on the bitmap data tells us that they are not equal, however, a pixel-by-pixel comparison doesn't find any difference at all. I was under the impression that when using LockBits
on a Bitmap
you get the actual raw bytes of the image. For a 24 bpp bitmap it's a bit hard to imagine a condition where the pixels are the same but the underlying pixel data isn't.
A few surprising things:
- The differences are always single bytes that are
00
in one image andFF
in the other. - If one changes the
PixelFormat
forLockBits
toFormat32bppRgb
orFormat32bppArgb
, the comparison succeeds. - If one passes the
BitmapData
returned by the firstLockBits
call as 4th argument to the second one, the comparison succeeds. - As noted above, the pixel-by-pixel comparison succeeds as well.
I'm a bit stumped here because frankly I cannot imagine why this happens.
(Reduced) Code below. Just compile with csc /unsafe
and pass a 24bpp PNG image as first argument.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
class Program
{
public static void Main(string[] args)
{
Bitmap title = new Bitmap(args[0]);
Console.WriteLine(CompareImageResult(title, new Bitmap(title)));
}
private static string CompareImageResult(Bitmap bmp, Bitmap expected)
{
string retval = "";
unsafe
{
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var resultData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
var expectedData = expected.LockBits(rect, ImageLockMode.ReadOnly, expected.PixelFormat);
try
{
if (memcmp(resultData.Scan0, expectedData.Scan0, resultData.Stride * resultData.Height) != 0)
retval += "Bitmap data did not match\n";
}
finally
{
bmp.UnlockBits(resultData);
expected.UnlockBits(expectedData);
}
}
for (var x = 0; x < bmp.Width; x++)
for (var y = 0; y < bmp.Height; y++)
if (bmp.GetPixel(x, y) != expected.GetPixel(x, y))
{
Console.WriteLine("Pixel diff at {0}, {1}: {2} - {3}", x, y, bmp.GetPixel(x, y), expected.GetPixel(x, y));
retval += "pixel fail";
}
return retval != "" ? retval : "success";
}
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int memcmp(IntPtr b1, IntPtr b2, long count);
}