tags:

views:

292

answers:

2

Summary

Using Windows GDI to convert 24-bit color to indexed color, it seems GDI chooses colors which are "close enough" even though there are exact matches in the supplied palette.

Can anyone confirm this as a GDI issue or am I making a mistake somewhere?

Maybe there's a "please check the whole palette for color matches" flag which I've failed to find?

Note: This is not about quantizing. The source is 24-bit but contains 256 or fewer colors so an exact palette is trivial to calculate. The problem is GDI doesn't use the full palette.

Workaround

I've worked around the problem by mapping the colors myself but I'd prefer to use GDI as it should be better optimized. Problem is, it seems to be "fast but wrong."

Detailed description

My source image is 24-bit but uses 256 (or fewer) colors. I generate an exact palette for it and ask GDI to transfer the image into an indexed bitmap using that palette. For some pixels GDI chooses similar, but not exact, colors even though there are exact colors elsewhere in the palette. This ruins smooth gradients.

This problem happens with:

  • SetDIBitsToDevice
  • StretchDIBits
  • BitBlt
  • StretchBlt

The problem does not happen with:

  • SetPixel or SetPixelV in a loop (incredibly slow!)
  • Using my own code to do the mapping

I've tested this on:

  • Windows 7 (NVidia hardware/drivers)
  • Windows Vista (ATI hardware/drivers)
  • Windows 2000 (VMware hardware/drivers)

In every test I get the same results. (Not just the wrong colours but always the same wrong colors.)

I don't think the issue is color management (ICM/ICC profiles/etc.) as most of the APIs say they don't use it, I've tried explicitly turning it off on the GDI DC as well as via the V5 bitmap header, and I don't think it would apply within my vanlilla-Win2k VM.

Test Project

Code for a simple Win32/GDI/VS2008 test project can be found here:

http://www.pretentiousname.com/data/GdiIndexColor.zip

The Test1 function within Win32UI.cpp is the actual test. It has two arrays of RGBQUADs, one the source image and the other the exact palette for it. It verifies that the palette really is exact and then asks GDI to convert the image using the APIs mentioned above, testing the result each time. For each test it'll tell you the first incorrect pixel's before & after colors, or tell you that all pixels are correct if it worked.

Thanks!

Thanks for reading my question! Sorry if it's the result of me doing something really dumb! :-)

A: 

Are you using SetDIBColorTable()? This article seems to imply that, when drawing to a DIB, it is not sufficient to call SelectPalette() but that SetDIBColorTable() also needs to be called to set the palette for the DIB:

However, if the application is using a DIB section, you create a logical palette from the DIB colour table as usual and then also pass the DIB colour table to the DIB section with a call to SetDIBColorTable(). Despite what the "Platform SDK" documentation of RealizePalette() appears to imply, RealizePalette() does not adjust the colour table of the DIB section.

The article contains some more information on drawing into palettized DIBs that may be relevant (see the section "Palettes and DIB sections").

Martin B
I have tried SetDIBColorTable on the destination DC, after selecting the DIB into it, but the results are the same.Also tried moving the SelectPalette call so it was after the DIB selection, as well as adding back the RealizePalette calls after SelectPalette in both places, but no luck.I've updated the sample code to include the SetDIBColorTable call, FWIW. (Previous version of the code moved to GdiIndexColor_old.zip)Thanks for your reply, though! I'll try any idea.
Leo Davidson
Sorry to hear that didn't do the trick...
Martin B
A: 

I vaguely remember that you also need to call RealizePalette(hdc) after a palette is selected into a DC. We ditched our palette code so long ago that the code isn't even in our source tree anymore. I see from your code that you alrady tried this, but I suggest that you might want to play with that some more.

I do remember that the palette code was pretty fragile, and we stopped using it as soon as we could.

Some older AVI files would have 8 bit palettized video with a palette imbedded in the file, so playback code for those files would need to load an realize a palette. I remember that realize didn't do anything unless you were the foreground app, but that SHOULD only apply to screen DC's and not memory DC's.

If you searched around for sample source code that could play palettized AVI's you might find something that shows the magic formula for getting palettes to work.

Sorry I can't be more help.

John Knoeller