尝试使用 libpng 加载图像时 Windows 出现运行时错误

Runtime error on Windows when trying to load image with libpng

我正在使用 pHash and that library uses libpng。我的程序有问题 运行,因为 libpng 无法加载 PNG 文件。

琐碎

如果您提出以下问题...

  1. 图片路径是否正确?是
  2. 图像是有效的 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*)");
...
}

该代码片段显示了 CImg<T>& _load_png(std::FILE *const file, const char *const filename) 的第一部分,该部分由 pHash 使用的 CImg 库调用。

运行时问题

代码编译正常,但我在运行时收到此错误,我可以在调试器中看到它:

CImgIOException: Failed to initialize 'png_ptr'...

在代码中指出的点。我不知道为什么,它无法加载图像。在 CImg.h 中调用 png_create_read_struct 时发生故障。通过预处理器指令定义的代码有点模糊。目前尚不清楚失败的原因。

有什么想法吗?

如果您自己包含 libpng,或者如果另一个库包含并使用 libpng,则需要注意一些事项。

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

很难说出到底是什么导致了问题,但有几件事值得一看。

我建议的一件事是访问提供最新版本 libpng 的网站并下载它。在您的计算机上设置一个文件夹并创建 "system environment variable through windows" 以指向您的图书馆。在您正在使用的当前版本的 VS 中打开该库的解决方案,为静态库和动态库(两种不同的解决方案)构建它,并为 32 位和 64 位构建它们,将生成的文件保存到单独的文件夹中.然后进入依赖于此的其他库,并尝试将 dll 或 lib 和 link 切换为新的。另外,您应该尝试在同一版本的 VS 中打开其他第 3 方库的解决方案,并尝试从那里进行干净的构建。然后确保 link 一切正常。您可能还必须修改 props 文件。

编辑

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

这是我的一个项目中的一个函数,用于将 png 加载到纹理结构中。现在这是依赖于许多其他 classes 的 class 对象的一部分,但是您应该能够从这个片段中看到我成功地使用了 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

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

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

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

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