Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Runtime error on Windows when trying to load image with libpng

I am using pHash and that library uses libpng. I am having issues running my program because libpng fails loading a PNG file.

  • Version of libpng: 1.4.19
  • Platform: Windows 10
  • Environment: Visual Studio 2015

Trivial

Just if you came up with the following questions...

  1. Is the path to image correct? Yes
  2. Is the image a valid PNG file? Yes

Code details

Library pHash uses CImg, the version of CImg they are using is a bit old I think:

#define cimg_version 148 // In CImg.h

I have debugged the library and the problems occurs in CImg.h (contained in the pHash VC++ project):

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*)");
...
}

The snippet shows the first part of CImg<T>& _load_png(std::FILE *const file, const char *const filename) which is called by the CImg library used by pHash.

Runtime issue

The code compiles fine but I get this error at runtime which I can see in the debugger:

CImgIOException: Failed to initialize 'png_ptr'...

In the point indicated in the code. I don't know why, it fails loading the image. The failure occurs when calling png_create_read_struct in CImg.h. That code is a bit obscure as defined through preprocessor directives. It is not clear why it is failing.

Any ideas?

like image 371
Andry Avatar asked Jul 06 '16 16:07

Andry


1 Answers

Either if you are including libpng yourself or if another library is including and using libpng there are a few things to be aware of.

  • Which ever version of Visual Studio you are using, the libpng (dll or lib) files must be built from the same version of Visual Studio that your solution is linking against.
  • The platform you are using 32bit or 64bit is of concern.
  • Project settings when building the png library must match the build types of your current project. (Code Generation -> Runtime Library) must match. Your character set should match as well.

It is a little to difficult to tell what exactly is causing the problem but these are a few things to have a look at.

One thing I would suggest is to go to the website that provides the newest version of libpng and download it. Set a folder on your computer and create "system environment variable through windows" to point to your library. Open the solution to this library in the current version of VS you are using, build it out for both a static lib and dynamic lib (two different solutions) and build them both out for 32 bit and 64 bit saving the generated files into separated folders. Then go into the other library that depends on this and try to switch the dlls or libs and link against the new ones if possible. Also the other 3rd party library you should try to open its solution in the same version of VS and try to do a clean build from there. Then make sure you link everything properly. You may have to also modify the props file.

EDIT

I am not familiar with pHash or CImg, but I am familiar with libpng.

Here is a function in one of my projects to load in a png into a texture structure. Now this is a part of a class object that relies on many other classes, but you should be able to see from this snippet that I am successfully using 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
like image 177
Francis Cugler Avatar answered Sep 20 '22 05:09

Francis Cugler