10

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?

Andry
  • 16,172
  • 27
  • 138
  • 246
  • Perhaps it wants NULL instead of 0 in the values passed to png_create_read_struct(). – Glenn Randers-Pehrson Jul 06 '16 at 21:48
  • I can try, but isn't `#define NULL 0`? – Andry Jul 07 '16 at 05:57
  • Also, I changed `user_error_fn` and `user_warning_fn` to point to some functions I created for seeing what error I get and still having same issue – Andry Jul 07 '16 at 05:58
  • On some platforms NULL is 0, on others 0L or (void*)0 which should all be equivalent. It's best to use NULL rather than one of those, just for clarity, even though the problem may be something else. – Glenn Randers-Pehrson Jul 08 '16 at 13:56
  • Also, make sure you have used the same settings (32-bit vs 64-bit, Release vs Debug, etc.) when building libpng and your application. – Glenn Randers-Pehrson Jul 08 '16 at 14:06
  • @Andry In the past I've had similar problems with libpng where the library files (dll or lib) files were built using VS2010, 12 or 13 and when I ported over to 2015 I would get linking or runtime errors as well. Once I built out newer versions of the library files using the same compiler version everything worked correctly. I have provided an answer that may help you find out the cause of your problem. – Francis Cugler Jul 10 '16 at 14:11
  • Have you checked your functors you're passing to the function? Here is a similar issue - http://stackoverflow.com/questions/10011925/can-not-read-png-file-with-libpng that will allow you to see the errors during initialization. Also may be a zlib issue as per http://stackoverflow.com/questions/11516870/crash-in-png-set-read-fn-on-windows-7 – Daniel Protopopov Jul 10 '16 at 18:42
  • @DanielProtopopov: I am using them, the code I have inside those functions is not executed :( – Andry Jul 10 '16 at 19:08
  • That is because you have those variables set to 0 just one line above, Andry. – Daniel Protopopov Jul 10 '16 at 19:12
  • @DanielProtopopov: Daniel, in that version of the code I did not report the functions I used for custom error and warning. In another version of that code I of course replaced those 0s with the pointer to 2 functions I have written and that display the error... And during that attempt, those functions were not executed – Andry Jul 10 '16 at 19:32
  • Any chance to check and show us the stderr output? – Daniel Protopopov Jul 10 '16 at 19:50
  • have you tried any other PNG? I got few problems in the past with libpng based loader and PNG's created by MsPaint. After re-encoding in different program (like faststone image viewer) the same images work fine. You should add the tested PNG here so we see if the problem is not there .... – Spektre Jul 15 '16 at 06:39
  • Are you using precompiled binaries of phash, or are you building it from source? – nephtes Jul 15 '16 at 17:57

2 Answers2

2

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
Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
1

Looking through the source code for png_create_read_struct_2(), there are only 2 failure modes: inability to allocate memory, which is unlikely to be the problem, and a library version conflict.

If you are using a precompiled build of the pHash library, you must ensure that the copy of the libpng DLL that gets linked dynamically at runtime is the same version of the library that pHash was compiled against. The latest Windows build on pHash.org ships with libpng12.dll in the "Release" subdirectory, which is probably incompatible the version that you mentioned in the question, namely 1.4.19.

If you are building pHash from source, make sure that the libpng include files that are being used in your build process match the version being loaded at runtime.

If you're unsure exactly which DLLs are being loaded at runtime, the surest way I know to determine it would be to use Process Monitor.

nephtes
  • 1,319
  • 9
  • 19