尝试使用 libpng 加载图像时 Windows 上的运行时错误

Posted

技术标签:

【中文标题】尝试使用 libpng 加载图像时 Windows 上的运行时错误【英文标题】:Runtime error on Windows when trying to load image with libpng 【发布时间】:2016-11-08 18:58:17 【问题描述】:

我正在使用pHash,而该库使用libpng。我在运行程序时遇到问题,因为 libpng 无法加载 PNG 文件。

libpng 版本:1.4.19 平台:Windows 10 环境:Visual Studio 2015

琐碎

只要你提出以下问题......

    图像的路径是否正确?是的 图像是有效的 PNG 文件吗?是的

代码详情

图书馆pHash使用CImg,我认为他们使用的CImg版本有点旧:

#define cimg_version 148 // In CImg.h

我已经调试了库,问题出现在CImg.h(包含在pHash VC++项目中):

CImg<T>& _load_png(std::FILE *const file, const char *const filename) 
      if (!file && !filename)
        throw CImgArgumentException(_cimg_instance
                                    "load_png() : Specified filename is (null).",
                                    cimg_instance);
      // Open file and check for PNG validity
      if (Buffer) strcat(Buffer, "Checking PNG availability\r\n");
      const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
      std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");

      unsigned char pngCheck[8] =  0 ;
      cimg::fread(pngCheck,8,(std::FILE*)nfile);
      if (png_sig_cmp(pngCheck,0,8)) 
        if (!file) cimg::fclose(nfile);
        throw CImgIOException(_cimg_instance
                              "load_png() : Invalid PNG file '%s'.",
                              cimg_instance,
                              nfilename?nfilename:"(FILE*)");
      

      // Setup PNG structures for read
      png_voidp user_error_ptr = 0;
      png_error_ptr user_error_fn = 0, user_warning_fn = 0;
      png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);

      if (!png_ptr)  // <-- PROBLEM HERE
        if (!file) cimg::fclose(nfile);
        throw CImgIOException(_cimg_instance
                              "load_png() : Failed to initialize 'png_ptr' structure for file '%s'.",
                              cimg_instance,
                              nfilename?nfilename:"(FILE*)");
...

sn-p 显示CImg&lt;T&gt;&amp; _load_png(std::FILE *const file, const char *const filename) 的第一部分,由pHash 使用的CImg 库调用。

运行时问题

代码编译得很好,但我在运行时得到了这个错误,我可以在调试器中看到:

CImgIOException: 无法初始化 'png_ptr'...

在代码中指出的点。我不知道为什么,它无法加载图像。在CImg.h 中调用png_create_read_struct 时发生故障。正如通过预处理器指令定义的那样,该代码有点晦涩难懂。目前尚不清楚为什么会失败。

有什么想法吗?

【问题讨论】:

也许它希望在传递给 png_create_read_struct() 的值中使用 NULL 而不是 0。 我可以试试,但是#define NULL 0不行吗? 另外,我更改了 user_error_fnuser_warning_fn 以指向我创建的一些函数,用于查看我遇到的错误并仍然遇到同样的问题 在某些平台上 NULL 为 0,在其他平台上 0L 或 (void*)0 应该都是等效的。为了清楚起见,最好使用 NULL 而不是其中之一,即使问题可能是其他问题。 另外,请确保您在构建 libpng 和您的应用程序时使用了相同的设置(32 位与 64 位、发布与调试等)。 【参考方案1】:

无论是您自己包含 libpng,还是其他库包含并使用 libpng,都需要注意一些事项。

无论您使用的是哪个版本的 Visual Studio,libpng(dll 或 lib)文件必须从您的解决方案链接的同一版本的 Visual Studio 构建。 您使用的平台是 32 位还是 64 位值得关注。 构建 png 库时的项目设置必须与当前项目的构建类型相匹配。 (代码生成 -> 运行时库)必须匹配。您的字符集也应该匹配。

很难说到底是什么导致了这个问题,但这些是值得一看的几件事。

我建议的一件事是访问提供最新版本 libpng 的网站并下载它。在您的计算机上设置一个文件夹并创建“通过 windows 的系统环境变量”以指向您的库。在您正在使用的当前版本的 VS 中打开此库的解决方案,为静态库和动态库(两种不同的解决方案)构建它,并为 32 位和 64 位构建它们,将生成的文件保存到单独的文件夹中.然后进入依赖于此的另一个库,并尝试切换 dll 或 lib,并尽可能链接到新的。此外,您应该尝试在相同版本的 VS 中打开其解决方案并尝试从那里进行干净构建的其他 3rd 方库。然后确保正确链接所有内容。您可能还需要修改 props 文件。

编辑

我不熟悉pHash或CImg,但我熟悉libpng。

这是我的一个项目中的一个函数,用于将 png 加载到纹理结构中。现在这是依赖于许多其他类的类对象的一部分,但是您应该能够从这个 sn-p 中看到我正在成功使用 libpng。

// ----------------------------------------------------------------------------
// loadPng()
bool TextureFileReader::loadPng( Texture* pTexture ) 
    struct PngFile 
        FILE*       fp;
        png_struct* pStruct;
        png_info*   pInfo;

        // --------------------------------------------------------------------
        PngFile() :
            fp( NULL ),
            pStruct( NULL ),
            pInfo( NULL )
         // PngFile

        // --------------------------------------------------------------------
        ~PngFile() 
            if ( NULL != fp ) 
                fclose( fp );
            

            if ( NULL != pStruct ) 
                if ( NULL != pInfo ) 
                    png_destroy_read_struct( &pStruct, &pInfo, NULL );
                 else 
                    png_destroy_read_struct( &pStruct, NULL, NULL );
                
            
         // ~PngFile
     png;

    // Error Message Handling
    std::ostringstream strStream;
    strStream << __FUNCTION__ << " ";

    if ( fopen_s( &png.fp, m_strFilenameWithPath.c_str(), "rb" ) != 0 ) 
        strStream << "can not open file for reading";
        throwError( strStream );
    

    // Test If File Is Actually A PNG Image
    const int NUM_HEADER_BYTES = 8;
    png_byte headerBytes[NUM_HEADER_BYTES];

    // Read The File Header
    if ( fread( headerBytes, 1, NUM_HEADER_BYTES, png.fp ) != NUM_HEADER_BYTES ) 
        strStream << "error reading header";
        return false;
    

    // Test Header
    if ( png_sig_cmp( headerBytes, 0, NUM_HEADER_BYTES ) != 0 ) 
        return false; // Not A PNG FILE
    

    // Init PNG Read Structure - Test PNG Version Compatibility
    png.pStruct = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
    if ( NULL == png.pStruct ) 
        strStream << "can not create struct for PNG file";
        throwError( strStream );
    

    // Init PNG Info Structure - Allocate Memory For Image Info
    png.pInfo = png_create_info_struct( png.pStruct );
    if ( NULL == png.pInfo ) 
        strStream << "can not create info for PNG file";
        throwError( strStream );
    

    // Prepare For Error Handling
    if ( setjmp( png_jmpbuf( png.pStruct ) ) ) 
        strStream << "can not init error handling for PNG file";
        throwError( strStream );
    

    // Tell libPng Where The File Data Is
    png_init_io( png.pStruct, png.fp );

    // Tell libPng That You Have Already Read The Header Bytes
    png_set_sig_bytes( png.pStruct, NUM_HEADER_BYTES );

    // Read Image Data From The File
    png_read_png( png.pStruct, png.pInfo, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_GRAY_TO_RGB, NULL );

    // Show Image Attributes
    png_byte colorType = png_get_color_type( png.pStruct, png.pInfo );
    switch( colorType ) 
        case PNG_COLOR_TYPE_RGB: 
        case PNG_COLOR_TYPE_RGBA: 
            break;
        
        default: 
            strStream << "PNG is saved in an unsupported color type (" << colorType << ")";
            throwError( strStream );
        
    

    unsigned uHeight = png_get_image_height( png.pStruct, png.pInfo );
    unsigned uBytesPerRow = png_get_rowbytes( png.pStruct, png.pInfo );
    if ( 0 == uHeight || 0 == uBytesPerRow ) 
        strStream << "invalid image size. Height(" << uHeight << "), Bytes per row(" << uBytesPerRow << ")";
        throwError( strStream );
    

    // Make Room For All Pixel Data
    unsigned uTotalNumBytes = uHeight * uBytesPerRow;
    pTexture->vPixelData.resize( uTotalNumBytes );

    // Get All Pixel Data From PNG Image
    png_bytepp ppPixelRow = png_get_rows( png.pStruct, png.pInfo );

    for ( unsigned int r = 0; r < uHeight; ++r ) 
        memcpy( &pTexture->vPixelData[ uBytesPerRow * ( uHeight - 1 - r ) ], ppPixelRow[r], uBytesPerRow );
    

    // Store Other Values In Texture
    pTexture->uWidth            = png_get_image_width( png.pStruct, png.pInfo );
    pTexture->uHeight           = uHeight;
    pTexture->hasAlphaChannel   = ( colorType == PNG_COLOR_TYPE_RGBA );

    return true;
 // loadPng

【讨论】:

为什么投反对票?如果您要对我的答案投反对票,请发表评论解释原因,以便我可以改进未来的答案。 我不是那个投反对票的人,老实说,我也不明白投反对票:( 您的代码帮助我做得更好,我根据您的工作方式对其进行了调整,这很好!谢谢 @Andry 我完全理解 Andry;我指的是实际投反对票的人。 我明白了,但我感到同情:当人们投反对票并且不发表评论时,这令人沮丧:(【参考方案2】:

查看png_create_read_struct_2()的源代码,只有2种故障模式:无法分配内存,这不太可能是问题,以及库版本冲突。

如果您使用的是 pHash 库的预编译版本,则必须确保在运行时动态链接的 libpng DLL 副本与编译 pHash 时所针对的库版本相同。 pHash.org 上的最新 Windows 版本在“Release”子目录中附带 libpng12.dll,这可能与您在问题中提到的版本不兼容,即 1.4.19。

如果您从源代码构建 pHash,请确保构建过程中使用的 libpng 包含文件与运行时加载的版本匹配。

如果您不确定在运行时加载了哪些 DLL,我知道确定它的最可靠方法是使用 Process Monitor。

【讨论】:

以上是关于尝试使用 libpng 加载图像时 Windows 上的运行时错误的主要内容,如果未能解决你的问题,请参考以下文章

libpng 警告:iCCP:已知不正确的 sRGB 配置文件

libpng 从内存缓冲区加载文件

如何在 Windows 上安装 libpng-dev?

使用 Libpng 创建 2 位颜色深度的 PNG 图像

libpng 错误:读取错误 (Visual Studio 2010)

libpng 警告:在 Python/PyGame 中使用 png_read_image 时应打开隔行处理