C ++ абстракция libpng вызывает сбой в malloc - при удалении «зубчатого» 2D массива - PullRequest
0 голосов
/ 31 марта 2012

Класс, соответствующий этому сбою:

#ifndef IMAGE_DATA_
#define IMAGE_DATA_

 #include <stdexcept>

template <typename data_type>
class ImageData 
{
public:
  ImageData(unsigned long width, unsigned long height);
  ~ImageData();
  data_type **&get_data();
  unsigned long int get_width() const
  {
    return _m_Width;
  }
  unsigned long int get_height() const
  {
    return _m_Height;
  }
protected:
  ImageData(ImageData &copy);
  ImageData& operator= (ImageData &copy);
private:
  data_type **_m_rData;
  unsigned long _m_Width;
  unsigned long _m_Height;
};

template <typename data_type>
ImageData<data_type>::ImageData(unsigned long width, unsigned long height) :
  _m_rData(NULL),
  _m_Width(width),
  _m_Height(height)
{
  if (width == 0 || height == 0)
    throw std::runtime_error("Invalid width or height");

  try {
    _m_rData = new data_type*[_m_Height]();
    for (unsigned long int i = 0; i < _m_Height; ++i) {
      _m_rData[i] = NULL;
    }
    for (unsigned long int i = 0; i < _m_Height; ++i) {
      _m_rData[i] = new data_type[_m_Width];
    }
  }
  catch (std::bad_alloc e) {
    throw std::runtime_error("Failure to create space for Image");
  }
}

template <typename data_type>
ImageData<data_type>::~ImageData()
{
  for (unsigned long i = 0; i < _m_Height; ++i) {
      delete [] _m_rData[i];
     _m_rData[i] = NULL;
  }
  delete [] _m_rData;
  _m_rData = NULL;
}

template <typename data_type>
data_type **&ImageData<data_type>::get_data()
{
  return _m_rData;
}

#endif

И он используется следующим образом:

PNGFileReader::PNGFileReader(const std::string &path) :
  _m_Image(NULL),
  _m_pPNG(NULL),
  _m_pPNGInfo(NULL)
{
   ...
  /*
   * Read Image in all at once into users data
   */  
  _m_Image = new ImageData<unsigned char>(width, height);
  png_read_image(_m_pPNG, _m_Image->get_data());
  png_read_end(_m_pPNG, NULL);
  fclose(_m_CFilePointer);
  _m_CFilePointer = NULL;
}

PNGFileReader::~PNGFileReader()
{
  if (_m_CFilePointer) {
    fclose(_m_CFilePointer);
  }
  png_destroy_read_struct(&_m_pPNG, &_m_pPNGInfo, NULL);
  delete _m_Image;
}

При прохождении с отладчиком _m_rData в классе ImageDataтот же указатель, что и при использовании нового на нем.Я даже пытался обернуть оператор удаления внутри деструктора ImageData с if == NULL statments.Тем не менее, я все еще получаю sigabrt во время выполнения моего кода.Трассировка стека из GDB:

0   __GI_raise  raise.c 64  0x3512a36285    
1   __GI_abort  abort.c 91  0x3512a37b9b    
2   __libc_message  libc_fatal.c    198 0x3512a77a7e    
3   malloc_printerr malloc.c    5021    0x3512a7dda6    
4   _int_free   malloc.c    3942    0x3512a7f08e    
5   ImageData<unsigned char>::~ImageData    imagedata.h 57  0x40236d    
6   PNGFileReader::~PNGFileReader   pngfilereader.cpp   59  0x401ed3    
7   main    main.cpp    8   0x40246a    

ОБНОВЛЕНИЕ

Для всех, кто интересуется, теперь работает следующее.Очевидно, это проблема с тем, как png_alligns свои данные.Это заставляет вас, я думаю, использовать вызовы методов libpng, которые внутренне используют free и malloc, а не new.По сути, это то же самое, что вызов free (data), когда данные были созданы с data = new type [N].Код ниже показывает, как правильно использовать libpng.

#ifndef PNG_FILE_READER_H_
#define PNG_FILE_READER_H_

#include "imagedata.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#include <iostream>
#include <vector>
#include <string>

template <typename data_type>
class ImageData;

class PNGFileReader
{
  public:
    // Ctor and Dtor
    PNGFileReader(const std::string &path);
    ~PNGFileReader();

    // For testing purposes
    friend std::ostream &operator<< (std::ostream &out, 
      PNGFileReader *object)
    {
      for (unsigned long i = 0; i < object->get_image_height(); ++i) {
        for (unsigned long j = 0; j < object->get_image_width(); ++j) {
          png_byte c = object->_m_ImageData[i][j];
          out << c;
        }   
      }
      return out;
    }

    // Getters
    long unsigned int get_image_width() const;
    long unsigned int get_image_height() const;

  private:
    // Helper functions:
    bool _create_png_structs();

    // Member variables:
    FILE *_m_CFilePointer;
    unsigned long int _m_ImageWidth;
    unsigned long int _m_ImageHeight;
    png_bytepp _m_ImageData;
    png_structp _m_pPNG;
    png_infop _m_pPNGInfo;

    // Enums
    enum PNGBOOL {NOT_PNG, PNG};
    enum PNGERRORS {ERROR, SUCCESS};
};

#endif /* PNG_FILE_READER_H_ */

#include "pngfilereader.h"
#include "filereader.h"
#include <stdexcept>


PNGFileReader::PNGFileReader(const std::string &path) :
  _m_ImageData(NULL),
  _m_pPNG(NULL),
  _m_pPNGInfo(NULL)
{
  /*
   * Check if first 8 bytes are the correct PNG header
   */ 
  enum {BYTES_TO_READ = 8};
  unsigned char sig[BYTES_TO_READ];
  FileReader(path, sig, BYTES_TO_READ);
  bool not_png = png_sig_cmp(sig, 0, BYTES_TO_READ);
  if (not_png) {
    throw std::runtime_error("Your file is not of PNG format");
  }

  /*
   * Create the png structs using a FILE *. libpng requires
   * this type and will not take a C++ stream
   */ 
  _m_CFilePointer = fopen(path.c_str(), "rb");
  if (!_m_CFilePointer) {
    throw std::runtime_error("Failure to open PNG file");
  }
  if (!_create_png_structs()) {
    throw std::runtime_error("Failure to create PNG structs");   
  }

  /*
   * Initialize PNG io and read data into PNG structs
   */ 
  png_init_io(_m_pPNG, _m_CFilePointer);
  png_read_info(_m_pPNG, _m_pPNGInfo);
  _m_ImageHeight = png_get_image_height(_m_pPNG, _m_pPNGInfo);
  _m_ImageWidth = png_get_rowbytes(_m_pPNG, _m_pPNGInfo);

  /*
   * Create sufficient PNG Space and Read Image in all at
   * once into users data. Note that you have to use png's
   * types to prevent sigabrt (6) while freeing memory.
   */
  _m_ImageData = (png_bytepp)png_malloc(_m_pPNG,
    sizeof(png_bytep)*_m_ImageHeight);
  if (_m_ImageData == NULL) {
    throw std::runtime_error("Memory allocation failure");
  }
  for (unsigned long int i = 0; i < _m_ImageHeight; ++i) {
     _m_ImageData[i] = NULL;
  }
  for (unsigned long int i = 0; i < _m_ImageHeight; ++i) {
    _m_ImageData[i] = (png_bytep)png_malloc(_m_pPNG,
      sizeof(png_byte)*_m_ImageWidth);
    if (_m_ImageData[i] == NULL) {
      throw std::runtime_error("Memory allocation failure.");
    }
  }
  png_read_image(_m_pPNG, _m_ImageData);
  png_read_end(_m_pPNG, NULL);
  fclose(_m_CFilePointer);
  _m_CFilePointer = NULL;
}

PNGFileReader::~PNGFileReader()
{
  if (_m_CFilePointer) {
    fclose(_m_CFilePointer);
  }
  /*
   * Free all resources (-1)
   */
  png_free_data(_m_pPNG, _m_pPNGInfo, PNG_FREE_ALL, -1);
  for (unsigned long int i = 0; i < _m_ImageHeight; ++i) {
    png_free(_m_pPNG, _m_ImageData[i]);
  }
  free(_m_ImageData);
  png_destroy_read_struct(&_m_pPNG, &_m_pPNGInfo, NULL);
}
// Getters
long unsigned int PNGFileReader::get_image_width() const
{
  return _m_ImageWidth;
}

long unsigned int PNGFileReader::get_image_height() const
{
  return _m_ImageHeight;
}

// Private helper functions
bool PNGFileReader::_create_png_structs()
{
  /* 
   * Create the pointer to main libpng struct, as well as
   * two info structs to maintain information after, and
   * prior to all operations on png m_Data. Only necessary
   * to release resource after function succeeds.
   */
  _m_pPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
    NULL, NULL);
  if (!_m_pPNG){
    return PNGFileReader::ERROR;
  }

  _m_pPNGInfo = png_create_info_struct(_m_pPNG);
  if (!_m_pPNGInfo) {
    return PNGFileReader::ERROR;
  }

  return PNGFileReader::SUCCESS;
}

1 Ответ

1 голос
/ 31 марта 2012

Если вам нужен действительно двумерный массив для передачи в библиотеку, но вы хотите иметь гибкость зубчатого массива, то вы делаете:

  1. Выделите блок указателя первого уровня как обычно
  2. Вместо выделения m отдельных строк из n ячеек (по одной на каждый указатель в блоке первого уровня) вы выделяете один набор n*m ячеек, а затем устанавливаете указатели первого уровня, чтобы они указывали на каждый n й локация.Таким образом, основное выделение измеряется и размещается в памяти как двумерный массив, но вы все равно можете использовать синтаксис разыменования с двумя указателями [][], чтобы добраться до ячеек.
  3. Передать началоРаспределение второго уровня по библиотеке.

Это работает, потому что существуют строгие требования к тому, кто размещает многомерные массивы в памяти (т. е. они должны быть смежными на каждом уровне интерпретации).

...