Skip to content

Instantly share code, notes, and snippets.

@zrbecker
Created July 17, 2012 06:46
Show Gist options
  • Select an option

  • Save zrbecker/3127677 to your computer and use it in GitHub Desktop.

Select an option

Save zrbecker/3127677 to your computer and use it in GitHub Desktop.
/*
* PNGImage.cpp
*
* Created on: Jul 16, 2012
* Author: zrbecker
*/
#include "PNGImage.h"
#include <png.h>
#include <iostream>
#include <algorithm>
PNGImage::PNGImage(unsigned int width, unsigned int height, bool hasAlpha, unsigned char *pixels)
: m_width(width), m_height(height), m_hasAlpha(hasAlpha), m_pixels(NULL) {
int bytes_per_pixel = (hasAlpha) ? 4 : 3;
m_pixels = new unsigned char[bytes_per_pixel * width * height];
std::copy(pixels, pixels + bytes_per_pixel * width * height, m_pixels);
}
PNGImage::PNGImage(const std::string &filename)
: m_width(0), m_height(0), m_hasAlpha(false), m_pixels(NULL) {
FILE *file;
file = fopen(filename.c_str(), "rb");
if (file == NULL) {
std::cerr << "Error: Could not open " << filename << "." << std::endl;
return;
}
png_struct *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
std::cerr << "Error: Could not create png_struct struct." << std::endl;
fclose(file);
return;
}
png_info *info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
std::cerr << "Error: Could not create png_info struct." << std::endl;
png_destroy_read_struct(&png_ptr, NULL, NULL);
fclose(file);
return;
}
if (setjmp(png_jmpbuf(png_ptr))) {
std::cerr << "Error: libpng failed to load " << filename << "." << std::endl;
png_destroy_read_struct(&png_ptr, NULL, NULL);
if (m_pixels != NULL) {
free(m_pixels);
m_pixels = NULL;
}
fclose(file);
return;
}
const int sig_bytes = 8;
unsigned char header[sig_bytes];
fread(header, 1, sig_bytes, file);
if (png_sig_cmp(header, 0, sig_bytes) != 0) {
std::cerr << "Error: " << filename << " is not a png file." << std::endl;
png_destroy_read_struct(&png_ptr, NULL, NULL);
fclose(file);
return;
}
unsigned int width, height;
bool hasAlpha;
png_init_io(png_ptr, file);
png_set_sig_bytes(png_ptr, sig_bytes);
png_read_png(png_ptr, info_ptr,
PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND,
NULL);
width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);
switch (png_get_color_type(png_ptr, info_ptr)) {
case PNG_COLOR_TYPE_RGBA: hasAlpha = true; break;
case PNG_COLOR_TYPE_RGB: hasAlpha = false; break;
default:
std::cerr << "Error: " << filename << " has unsupported color type." << std::endl;
png_destroy_read_struct(&png_ptr, NULL, NULL);
fclose(file);
return;
}
unsigned int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
m_pixels = new unsigned char[rowbytes * height];
png_byte **row_pointers = png_get_rows(png_ptr, info_ptr);
for (int i = 0; i < height; ++i)
std::copy(row_pointers[i], row_pointers[i] + rowbytes, m_pixels + i * rowbytes);
png_destroy_read_struct(&png_ptr, NULL, NULL);
fclose(file);
m_width = width;
m_height = height;
m_hasAlpha = hasAlpha;
}
PNGImage::~PNGImage() {
if (m_pixels != NULL) {
delete[] m_pixels;
m_pixels = NULL;
}
}
bool PNGImage::Good() {
return (m_pixels != NULL);
}
bool PNGImage::Save(const std::string &filename) {
FILE *file = fopen(filename.c_str(), "wb");
if (file == NULL) {
std::cerr << "Could not open " << filename << " for writing." << std::endl;
return false;
}
png_struct *png_s = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_s == NULL) {
std::cerr << "Error: Could not create png_struct struct." << std::endl;
fclose(file);
return false;
}
png_info *png_i = png_create_info_struct(png_s);
if (png_i == NULL) {
std::cerr << "Error: Could not create png_info struct." << std::endl;
png_destroy_write_struct(&png_s, &png_i);
fclose(file);
return false;
}
if (setjmp(png_jmpbuf(png_s))) {
std::cerr << "Error: libpng failed to save " << filename << "." << std::endl;
png_destroy_write_struct(&png_s, &png_i);
fclose(file);
return false;
}
png_init_io(png_s, file);
int color_type = (m_hasAlpha) ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB;
png_set_IHDR(png_s, png_i, m_width, m_height, 8, color_type, 0, 0, 0);
png_write_info(png_s, png_i);
int bytes_per_pixel = (m_hasAlpha) ? 4 : 3;
png_bytep row_pointers[m_height];
if (m_height > PNG_UINT_32_MAX / png_sizeof(png_bytep))
png_error(png_s, "Image is too tall to process in memory.");
for (png_uint_32 k = 0; k < m_height; ++k)
row_pointers[k] = m_pixels + k * m_width * bytes_per_pixel;
png_write_image(png_s, row_pointers);
png_write_end(png_s, png_i);
png_destroy_write_struct(&png_s, &png_i);
fclose(file);
return true;
}
/*
* PNGImage.h
*
* Created on: Jul 16, 2012
* Author: zrbecker
*/
#ifndef PNGIMAGE_H_
#define PNGIMAGE_H_
#include <string>
class PNGImage {
public:
PNGImage(const std::string &filename);
PNGImage(unsigned int width, unsigned int height, bool hasAlpha, unsigned char *pixels);
virtual ~PNGImage();
bool Good();
bool Save(const std::string &filename);
private:
unsigned int m_width;
unsigned int m_height;
bool m_hasAlpha;
unsigned char *m_pixels;
};
#endif /* PNGIMAGE_H_ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment