Ошибка выполнения Windows при попытке загрузить изображение с помощью libpng

Я использую pHash и эта библиотека использует libpng. У меня возникают проблемы с запуском моей программы, потому что libpng не загружает файл PNG.

  • Версия libpng: 1.4.19
  • Платформа: Windows 10
  • Окружающая среда: Visual Studio 2015

Trivial

Просто, если вы столкнулись со следующими вопросами...

  • Правильно ли путь к изображению? Да
  • Является ли образ действительным PNG файлом? Да

Информация о кодах

Библиотека pHash использует CImg, версия CImg, которую они используют, немного устарела, я думаю:

#define cimg_version 148 // In CImg.h

Я отлаживал библиотеку, и проблемы возникают в CImg.h (содержится в проекте pHash VС++):

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), которая вызывается библиотекой CImg, используемой pHash.

Ошибка выполнения

Код компилируется отлично, но я получаю эту ошибку во время выполнения, которую я вижу в отладчике:

CImgIOException: не удалось инициализировать 'png_ptr'...

В пункте, указанном в коде. Я не знаю, почему, он не загружает изображение. Ошибка возникает при вызове png_create_read_struct в CImg.h. Этот код немного неясен, как определено в директивах препроцессора. Непонятно, почему он терпит неудачу.

Любые идеи?

Ответ 1

Если вы включаете libpng самостоятельно или если в библиотеку включена и используется libpng, вам нужно знать несколько вещей.

  • Какую версию Visual Studio вы используете, файлы libpng (dll или lib) должны быть созданы из той же версии Visual Studio, с которой связано ваше решение.
  • Вы занимаетесь платформой, использующей 32-битную или 64-разрядную версию.
  • Параметры проекта при создании библиотеки png должны соответствовать типам сборки вашего текущего проекта. (Генерация кода → Библиотека времени выполнения) должна совпадать. Ваш набор символов должен совпадать.

Немного сложно сказать, что именно вызывает проблему, но это несколько вещей, на которые нужно обратить внимание.

Одна вещь, которую я хотел бы предложить, - перейти на веб-сайт, который предоставляет новейшую версию libpng и загрузить ее. Установите папку на своем компьютере и создайте "переменную системной среды через окна", чтобы указать на вашу библиотеку. Откройте решение этой библиотеки в текущей версии VS, которую вы используете, создайте ее как для статической lib, так и для динамической библиотеки (два разных решения) и создайте их для 32-битного и 64-битного сохранения сгенерированных файлов в отдельные папки, Затем перейдите в другую библиотеку, которая зависит от этого, и попытайтесь переключить dlls или libs и связать их с новыми, если это возможно. Также в третьей сторонней библиотеке вы должны попытаться открыть свое решение в той же версии VS и попытаться сделать чистую сборку оттуда. Затем убедитесь, что вы все правильно свяжете. Возможно, вам придется изменить файл реквизита.

ИЗМЕНИТЬ

Я не знаком с 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

Ответ 2

Просматривая исходный код для png_create_read_struct_2(), существует только 2 режима отказа: невозможность выделения памяти, что вряд ли будет проблемой, и конфликт версии библиотеки.

Если вы используете предварительно скомпилированную сборку библиотеки pHash, вы должны убедиться, что копия DLL библиотеки libpng, которая динамически подключается во время выполнения, представляет собой ту же версию библиотеки, с которой скомпилирован файл pHash. Последние версии Windows, основанные на pHash.org, поставляются с libpng12.dll в подкаталоге "Release", что, вероятно, несовместимо с версией, о которой вы говорили в этом вопросе, а именно 1.4.19.

Если вы создаете pHash из источника, убедитесь, что файлы libpng include, которые используются в вашем процессе сборки, соответствуют версии, загружаемой во время выполнения.

Если вы точно не знаете, какие DLL загружаются во время выполнения, самым надежным способом, который я знаю, чтобы определить это, было бы использовать Process Monitor.