The class corresponding to this crash is:
#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 ©);
ImageData& operator= (ImageData ©);
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
And it is used in the following manner:
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;
}
When stepping through with the debugger the _m_rData in the ImageData class is the same pointer as when I used new on it. I have even tried to wrap the delete statement inside ImageData destructor with if == NULL statments. However, I still get a sigabrt while running my code. The stack trace from gdb is:
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
UPDATE
For anyone that is curios the following now works. Apparently it is an issue with how png_alligns its data. This forces you I guess to use libpng’s method calls which internally use free and malloc, not new. This is essentially the same things as calling free(data) where data was created with data = new type[N]. The code below depicts how to correctly use 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;
}
If you need a really 2D array to pass to a library, but want to have the flexibility of a jagged array, what you do is
mseparate rows ofncells (one for each pointer in the first level block) you allocate a single set ofn*mcells and then set the first level pointers to point at everynth location. This way the main allocation is sized and laid out in memory just as a 2D array, but you can still use the two-pointer-dereference[][]syntax to get to the cells.This works because there are strict requirements on who multidimensional arrays are laid out in memory (i.e. the must be contiguous at every level of interpretation).