【问题标题】:CImage implicitly converting grayscale PNG images to 32bpp ARGBCImage 将灰度 PNG 图像隐式转换为 32bpp ARGB
【发布时间】:2015-08-10 20:39:10
【问题描述】:

我正在使用ATL:CImage 类来解码 png 图像。 但是图像在加载时会转换为RGBA(每像素 4 字节)图像。

ATL:CImage img;
img.Load((LPCTSTR)("test.png")); // 8bit grayscale

成功加载后,m_nBPP 成员为 32(又名 4 字节)并且 m_bHasAlphaChannel 为真。但它们应该是 8 和 false。

由于隐式转换,我们需要手动将 RGBA 转换回 8BPP。我正在处理 400 多张图像。所以这对应用程序来说是一个重大的减速。

在 Visual Studio 论坛上,我了解到这是 GDI+ 中的一个问题,因为它会将所有灰度 PNG 图像隐式转换为 32bpp ARGB

有解决办法吗?

【问题讨论】:

  • (LPCTSTR)-cast 是一个错误。请改用_TTEXT 宏。

标签: mfc gdi+


【解决方案1】:

如果您想要控制,请使用Windows Imaging Component。您需要创建一个Decoder 并检索您感兴趣的image frame(s)PNG Format Overview 概述了 PNG 支持。

以下示例代码打开一个灰度 PNG 图像,并显示有关媒体的信息:

#include <windows.h>
#include <wincodec.h>
#pragma comment(lib, "Windowscodecs.lib")
#include <iostream>

int main() {
    ::CoInitialize( NULL );

创建工厂 COM 服务器:

    IWICImagingFactory* pFactory = NULL;
    HRESULT hr = ::CoCreateInstance( CLSID_WICImagingFactory,
                                     NULL,
                                     CLSCTX_INPROC_SERVER,
                                     IID_IWICImagingFactory,
                                     (void**)&pFactory );

根据图片源创建解码器:

    IWICBitmapDecoder* pDecoder = NULL;
    if ( SUCCEEDED( hr ) ) {
        hr = pFactory->CreateDecoderFromFilename( L"test.png",
                                                  NULL,
                                                  GENERIC_READ,
                                                  WICDecodeMetadataCacheOnDemand,
                                                  &pDecoder );
    }

虽然 PNG 文件总是包含一个图像,但也有一些图像格式允许将多个图像存储在一个文件中。一般来说,您必须查询帧数并遍历所有帧,一个接一个地解码:

    UINT frameCount = 0;
    if ( SUCCEEDED( hr ) ) {
        hr = pDecoder->GetFrameCount( &frameCount );
    }

    if ( SUCCEEDED( hr ) ) {
        std::wcout << L"Framecount: " << frameCount << std::endl;

        for ( UINT frameIndex = 0; frameIndex < frameCount; ++frameIndex ) {
            std::wcout << std::endl << L"Frame " << frameIndex << L":" << std::endl;
            IWICBitmapFrameDecode* pFrame = NULL;
            hr = pDecoder->GetFrame( frameIndex, &pFrame );

出于说明目的转储图像尺寸:

            if ( SUCCEEDED( hr ) ) {
                UINT width = 0, height = 0;
                hr = pFrame->GetSize( &width, &height );
                if ( SUCCEEDED( hr ) ) {
                    std::wcout << L"  width: " << width <<
                                  L", height: " << height << std::endl;
                }
            }

要验证图像数据是否未被更改,请转储 bpp 和通道计数信息:

            if ( SUCCEEDED( hr ) ) {
                WICPixelFormatGUID pixelFormat = { 0 };
                pFrame->GetPixelFormat( &pixelFormat );
                if ( SUCCEEDED( hr ) ) {
                    // Translate pixelformat to bpp
                    IWICComponentInfo* pComponentInfo = NULL;
                    hr = pFactory->CreateComponentInfo( pixelFormat, &pComponentInfo );
                    IWICPixelFormatInfo* pPixelFormatInfo = NULL;
                    if ( SUCCEEDED( hr ) ) {
                        hr = pComponentInfo->QueryInterface( &pPixelFormatInfo );
                    }

                    UINT bpp = 0;
                    if ( SUCCEEDED( hr ) ) {
                        hr = pPixelFormatInfo->GetBitsPerPixel( &bpp );
                    }
                    if ( SUCCEEDED( hr ) ) {
                        std::wcout << L"  bpp: " << bpp << std::endl;
                    }

                    UINT channelCount = 0;
                    if ( SUCCEEDED( hr ) ) {
                        hr = pPixelFormatInfo->GetChannelCount( &channelCount );
                    }
                    if ( SUCCEEDED( hr ) ) {
                        std::wcout << L"  Channel Count: " << channelCount << std::endl;
                    }

                    // Cleanup
                    if ( pPixelFormatInfo != NULL ) { pPixelFormatInfo->Release(); }
                    if ( pComponentInfo != NULL ) { pComponentInfo->Release(); }
                }
            }

剩下的就是资源清理:

            // Cleanup
            if ( pFrame != NULL ) { pFrame->Release(); }
        }
    }

    // Cleanup
    if ( pDecoder != NULL ) { pDecoder->Release(); }
    if ( pFactory != NULL ) { pFactory->Release(); }

    return 0;
}

针对这张图片运行这段代码

产生以下输出:

Framecount: 1

Frame 0:
  width: 50, height: 50
  bpp: 8
  Channel Count: 1

【讨论】:

  • 不知道你说的是哪种控制方式。我只需要将灰度 PNG 作为灰度数据加载。我的问题是为什么当我加载灰度 PNG 时,它会被转换为 32 位 RGB,我怎样才能简单地使用 CImage 加载灰度 PNG,或者我可以吗?
  • @user1116700:GDI+ 解码器将所有支持的 PNG 图像转换为 32bpp RGBA(请参阅ATL::CImage::Load converts 8 bit mono implicitly to RGBA)。如果要控制解码后数据在内存中的存储方式,则不能使用GDI+或ATL::CImage
  • @user1116700:当然你可以使用ATL::CImage来读取灰度PNG。您只是无法控制它是如何存储在内存中的。 32bpp RGBA 图像仍然是相同的灰度图像,只是在内存中的表示不同。
猜你喜欢
  • 1970-01-01
  • 2023-03-14
  • 2011-01-25
  • 1970-01-01
  • 2011-09-15
  • 2017-12-08
  • 1970-01-01
  • 2014-06-20
  • 2011-12-15
相关资源
最近更新 更多