#include "Image.h" #include "Base.h" namespace gameplay { Image::Image() : _data(NULL), _format(RGBA), _width(0), _height(0), _bpp(0) { } Image::~Image() { delete[] _data; } Image* Image::create(const char* path) { // Open the file. FILE* fp = fopen(path, "rb"); if (fp == NULL) { LOG(1, "Failed to open image file '%s'.\n", path); return NULL; } // Verify PNG signature. unsigned char sig[8]; if (fread(sig, 1, 8, fp) != 8 || png_sig_cmp(sig, 0, 8) != 0) { LOG(1, "Failed to load file '%s'; not a valid PNG.\n", path); if (fclose(fp) != 0) { LOG(1, "Failed to close image file '%s'.\n", path); } return NULL; } // Initialize png read struct (last three parameters use stderr+longjump if NULL). png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png == NULL) { LOG(1, "Failed to create PNG structure for reading PNG file '%s'.\n", path); if (fclose(fp) != 0) { LOG(1, "Failed to close image file '%s'.\n", path); } return NULL; } // Initialize info struct. png_infop info = png_create_info_struct(png); if (info == NULL) { LOG(1, "Failed to create PNG info structure for PNG file '%s'.\n", path); if (fclose(fp) != 0) { LOG(1, "Failed to close image file '%s'.\n", path); } png_destroy_read_struct(&png, NULL, NULL); return NULL; } // Initialize file io. png_init_io(png, fp); // Indicate that we already read the first 8 bytes (signature). png_set_sig_bytes(png, 8); // Read the entire image into memory. png_read_png(png, info, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL); Image* image = new Image(); image->_width = png_get_image_width(png, info); image->_height = png_get_image_height(png, info); png_byte colorType = png_get_color_type(png, info); switch (colorType) { case PNG_COLOR_TYPE_GRAY: image->_bpp = 1; image->_format = Image::LUMINANCE; break; case PNG_COLOR_TYPE_RGBA: image->_bpp = 4; image->_format = Image::RGBA; break; case PNG_COLOR_TYPE_RGB: image->_bpp = 3; image->_format = Image::RGB; break; default: LOG(1, "Unsupported PNG color type (%d) for image file '%s'.\n", (int)colorType, path); if (fclose(fp) != 0) { LOG(1, "Failed to close image file '%s'.\n", path); } png_destroy_read_struct(&png, &info, NULL); return NULL; } size_t stride = png_get_rowbytes(png, info); // Allocate image data. image->_data = new unsigned char[stride * image->_height]; // Read rows into image data. png_bytepp rows = png_get_rows(png, info); for (unsigned int i = 0; i < image->_height; ++i) { memcpy(image->_data+(stride * i), rows[i], stride); } // Clean up. png_destroy_read_struct(&png, &info, NULL); if (fclose(fp) != 0) { LOG(1, "Failed to close image file '%s'.\n", path); } return image; } Image* Image::create(Format format, unsigned int width, unsigned int height) { unsigned int bpp; switch (format) { case LUMINANCE: bpp = 1; break; case RGB: bpp = 3; break; case RGBA: bpp = 4; break; default: LOG(1, "Invalid image format passed to create.\n"); return NULL; } Image* image = new Image(); image->_format = format; image->_width = width; image->_height = height; image->_bpp = bpp; image->_data = new unsigned char[width * height * bpp]; memset(image->_data, 0, width * height * bpp); return image; } void* Image::getData() const { return _data; } void Image::setData(void* data) { memcpy(_data, data, _width * _height * _bpp); } Image::Format Image::getFormat() const { return _format; } unsigned int Image::getHeight() const { return _height; } unsigned int Image::getWidth() const { return _width; } unsigned int Image::getBpp() const { return _bpp; } int getPNGColorType(Image::Format format) { switch (format) { case Image::LUMINANCE: return PNG_COLOR_TYPE_GRAY; case Image::RGB: return PNG_COLOR_TYPE_RGB; case Image::RGBA: return PNG_COLOR_TYPE_RGBA; } return PNG_COLOR_TYPE_RGBA; } void Image::save(const char* path) { png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_bytep row = NULL; unsigned int stride; int index; FILE* fp = fopen(path, "wb"); if (fp == NULL) { LOG(1, "Error: Failed to open image for writing: %s\n", path); goto error; } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { LOG(1, "Error: Write struct creation failed: %s\n", path); goto error; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { LOG(1, "Error: Info struct creation failed: %s\n", path); goto error; } png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, _width, _height, 8, getPNGColorType(_format), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); // Allocate memory for a single row of image data stride = _bpp * _width * sizeof(png_byte); row = (png_bytep)malloc(stride); index = 0; for (unsigned int y = 0; y < _height; ++y) { for (unsigned int x = 0; x < stride; ++x) { // Write data row[x] = (png_byte)_data[index++]; } png_write_row(png_ptr, row); } png_write_end(png_ptr, NULL); error: if (fp) fclose(fp); if (row) free(row); if (info_ptr) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); if (png_ptr) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); } }