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

时间:2016-07-06 16:17:42

标签: c++ visual-c++ libpng cimg

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

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

琐碎

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

  1. 图像路径是否正确?是
  2. 图片是有效的PNG文件吗?是
  3. 代码详情

    图书馆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:无法初始化'png_ptr'...

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

    有什么想法吗?

2 个答案:

答案 0 :(得分:2)

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

  • 您正在使用的Visual Studio版本中,libpng(dll或lib)文件必须使用与您的解决方案相关联的相同版本的Visual Studio构建。
  • 使用32位或64位的平台值得关注。
  • 构建png库时的项目设置必须与当前项目的构建类型匹配。 (代码生成 - &gt;运行时库)必须匹配。你的角色集也应该匹配。

要确定究竟是什么导致了这个问题有点难以理解,但这些是一些值得关注的事情。

我建议的一件事是去提供最新版libpng并下载它的网站。在您的计算机上设置一个文件夹,并通过Windows&#34;创建&#34;系统环境变量。指向你的图书馆。在当前版本的VS中打开此库的解决方案,为静态库和动态库(两种不同的解决方案)构建它,并将它们构建为32位和64位,将生成的文件保存到单独的文件夹中。然后进入依赖于此的另一个库,并尝试切换dll或libs并尽可能链接新的。另外第三方库你应该尝试在同一版本的VS中打开它的解决方案并尝试从那里做一个干净的构建。然后确保正确链接所有内容。您可能还必须修改props文件。

修改

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

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

答案 1 :(得分:1)

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

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

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

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