GDI 无法使用精确调色板转换为索引颜色?

发布于 2024-08-15 17:19:55 字数 1456 浏览 5 评论 0原文

总结

使用 Windows GDI 将 24 位颜色转换为索引颜色,即使提供的调色板中存在完全匹配的颜色,GDI 似乎也会选择“足够接近”的颜色。

谁能确认这是 GDI 问题还是我在某个地方犯了错误?

也许有一个我找不到的“请检查整个调色板的颜色匹配”标志?

注意:这与量化无​​关。源是 24 位的,但包含 256 种或更少的颜色,因此计算精确的调色板很简单。问题是 GDI 不使用完整的调色板。

解决方法

我已经通过自己映射颜色来解决这个问题,但我更喜欢使用 GDI,因为它应该得到更好的优化。问题是,它似乎“快但错误”。

详细说明

我的源图像是 24 位,但使用 256(或更少)颜色。我为其生成一个精确的调色板,并要求 GDI 使用该调色板将图像传输到索引位图中。对于某些像素,GDI 选择相似但不精确的颜色,即使调色板中的其他位置也有精确的颜色。这会破坏平滑的渐变。

此问题发生在:

  • SetDIBitsToDevice
  • StretchDIBits
  • BitBlt
  • StretchBlt

问题不会发生在:

  • 循环中的 SetPixel 或 SetPixelV(非常慢!)
  • 使用我自己的代码进行映射时,

我已经在以下设备上进行了测试:

  • Windows 7(NVidia 硬件/驱动程序)
  • Windows Vista(ATI 硬件/驱动程序)
  • Windows 2000(VMware 硬件/驱动程序)

在每次测试中我都得到相同的结果。 (不仅仅是错误的颜色,而且总是相同的错误颜色。)

我不认为问题是颜色管理(ICM/ICC 配置文件/等),因为大多数 API 说他们不使用它,我已经尝试过在 GDI DC 上以及通过 V5 位图标头显式关闭它,我认为它不适用于我的 vanlilla-Win2k VM。

测试项目

一个简单的 Win32/GDI/VS2008 测试项目的代码可以在这里找到:

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

Win32UI.cpp 中的 Test1 函数是实际测试。它有两个 RGBQUAD 数组,一个是源图像,另一个是它的确切调色板。它验证调色板是否确实准确,然后要求 GDI 使用上述 API 转换图像,每次都测试结果。对于每个测试,它都会告诉您第一个不正确的像素之前和之后的像素。在颜色之后,或者告诉您如果有效的话所有像素都是正确的。

谢谢!

感谢您阅读我的问题!抱歉,如果这是我做了一些非常愚蠢的事情的结果! :-)

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! :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

温馨耳语 2024-08-22 17:19:55

我遇到了同样的问题,最终联系了微软并向他们提供了一个测试用例。在测试用例中,我提供了一个 24 位 DIB 中有 128 种颜色的渐变图像,然后将其转换为使用包含 24 位图像中所有 128 种颜色的颜色表创建的 8 位 DIB。转换后,8 位图像仅使用了 128 种颜色中的 65 种。

总结一下他们的回应:
这不是一个错误,GDI 在向下转换图像的颜色深度时确实使用了足够接近的计算。这并没有在任何地方得到真正的记录,确保所有原始颜色都能准确转换的唯一方法是自己手动操作像素。

I ran into this exact same problem, eventually contacted Microsoft and provided them with a test case. In the test case I provided a gradient image that had 128 colors in a 24bit DIB, I then converted that to an 8bit DIB that was created with a color table containing all 128 colors from the 24bit image. After conversion, the 8 bit image had only used 65 of the 128 colors.

To sum up their response:
This is not a bug, GDI does use a close enough calculation when down converting the color depth of an image. This is not really documented anywhere, and the only way to insure all of the original colors will convert exactly is to manually manipulate the pixels yourself.

撩动你心 2024-08-22 17:19:55

您是否使用 SetDIBColorTable()这篇文章似乎暗示,当绘制到DIB时,调用SelectPalette是不够的() 但还需要调用 SetDIBColorTable() 来设置 DIB 的调色板:

但是,如果应用程序正在使用
一个 DIB 部分,您创建一个逻辑
DIB 颜色表中的调色板为
通常,然后也通过 DIB
颜色表到 DIB 部分
调用 SetDIBColorTable()。尽管
什么是“平台 SDK”文档
RealizePalette() 似乎暗示,
RealizePalette() 不调整
DIB部分的颜色表。

本文包含一些有关绘制可能相关的调色板 DIB 的更多信息(请参阅“调色板和 DIB 部分”部分)。

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").

思慕 2024-08-22 17:19:55

我依稀记得在DC中选择调色板后还需要调用RealizePalette(hdc)。我们很久以前就放弃了调色板代码,以至于该代码甚至不再出现在我们的源代码树中。我从你的代码中看到你已经尝试过这个,但我建议你可能想再尝试一下。

我确实记得调色板代码非常脆弱,我们尽快停止使用它。

一些较旧的 AVI 文件将具有 8 位调色板视频,并在文件中嵌入调色板,因此这些文件的播放代码需要加载实现调色板。我记得除非你是前台应用程序,否则意识到不会做任何事情,但这应该只适用于屏幕 DC 而不是内存 DC。

如果您搜索了可以播放调色板化 AVI 的示例源代码,您可能会发现一些东西,其中显示了使调色板工作的神奇公式。

抱歉我无法提供更多帮助。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文