GED 2006 (C++)
File detail
Source code
/*
* File: pngimage.cc - Module loading/saving PNG images
* Project: GED - bitmap editor (ICP)
* Author: Jakbub Filak, xfilak01
* Team: xdudka00, xfilak01, xhefka00, xhradi08
* Created: 2006-03-10
*/
#include <string>
#include <errno.h>
#include <png.h>
#include <iostream>
#include <stdio.h>
#include "pngimage.h"
#ifndef NDEBUG
#define PRINT(x) std::cout << x << std::endl;
#else
#define PRINT(x)
#endif
/*
* create an empty PNG image of size "size"
*/
PNGImage::PNGImage (const BoxSize &size)
: Image (size)
{
createPNGInfo();
initPNGInfo();
}
/**
* create structures
*/
void PNGImage::createPNGInfo()
{
_png_read = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
_info = png_create_info_struct( _png_read);
}
/**
* Initialize for default new png image
*/
void PNGImage::initPNGInfo()
{
BoxSize box = size();
_info->height = box.height;
_info->width = box.width;
_info->channels = 4;
_info->rowbytes = _info->channels*box.width;
_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
_info->bit_depth = 8;
_info->interlace_type = PNG_INTERLACE_NONE;
_info->filter_type = PNG_FILTER_TYPE_BASE;
_info->compression_type = PNG_COMPRESSION_TYPE_BASE;
_info->pixel_depth = 8;
_info->srgb_intent = 0;
_n_of_passes = 1;
}
/*
* open "fileName" PNG image and read image
*/
PNGImage::PNGImage (const std::string &fileName) :Image(fileName) {
FILE* png_file;
png_bytep png_sig_buff;
if ( (png_file = fopen(fileName.c_str(), "rb")) == NULL ) {
throw ErrLoad();
}
png_sig_buff = new png_byte[_png_sig_num_bytes];
fread(png_sig_buff, 1, _png_sig_num_bytes, png_file);
if ( png_sig_cmp( png_sig_buff, 0, _png_sig_num_bytes) ) {
fclose( png_file );
throw ErrLoad();
}
delete [] png_sig_buff;
PRINT("Creating structures ... ")
createPNGInfo();
PRINT("[ OK ]")
PRINT("Setting long jump ... ")
if ( setjmp(_png_read->jmpbuf)) {
png_destroy_read_struct( &_png_read, &_info, (png_infopp)NULL );
throw ErrLoad();
}
PRINT("[ OK ]")
PRINT("png init io ...")
png_init_io( _png_read, png_file );
PRINT("[ OK ]")
PRINT("png set read sig bytes ...")
png_set_sig_bytes( _png_read, _png_sig_num_bytes );
PRINT("[ OK ]")
PRINT("png read info")
png_read_info( _png_read, _info );
PRINT("[ OK ]")
PRINT("striping bit depth")
if (_info->bit_depth == 16) {
png_set_strip_16(_png_read);
}
PRINT("[ OK ]")
PRINT("expadn")
if (_info->color_type == PNG_COLOR_TYPE_GRAY && _info->bit_depth < 8) {
png_set_expand(_png_read);
}
PRINT("[ OK ]")
PRINT("packing")
if (_info->color_type == PNG_COLOR_TYPE_PALETTE && _info->bit_depth < 8 ) {
png_set_packing(_png_read);
}
PRINT("[ OK ]")
PRINT("expand if color type is PALETTE")
if (_info->color_type == PNG_COLOR_TYPE_PALETTE ) {
png_set_expand(_png_read);
}
PRINT("[ OK ]")
PRINT("expand")
if (_info->color_type != PNG_COLOR_TYPE_PALETTE && (_info->valid & PNG_INFO_tRNS)) {
png_set_expand(_png_read);
}
PRINT("[ OK ]")
PRINT("packing if bit depth is less than 8")
if (_info->bit_depth < 8) {
png_set_packing(_png_read);
}
PRINT("[ OK ]")
PRINT("change type to RGB")
if (_info->color_type == PNG_COLOR_TYPE_GRAY || _info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_set_gray_to_rgb(_png_read);
}
PRINT("[ OK ]")
PRINT("setting interlance handling")
_n_of_passes = png_set_interlace_handling(_png_read);
PRINT("[ OK ]")
_n_of_passes = 1;
_color_type = static_cast<int>( _info->color_type );
PRINT("read update info")
png_read_update_info(_png_read, _info);
PRINT("[ OK ]")
PRINT("setting size")
this->size ( BoxSize( _info->width, _info->height) );
PRINT("[ OK ]")
PRINT("getting row bytes")
png_bytepp row_pointers = new png_bytep[_info->height];
png_uint_32 row_bytes = png_get_rowbytes( _png_read, _info );
for( unsigned int row = 0; row < _info->height; row++ ) {
row_pointers[row] = new png_byte[row_bytes];
}
PRINT("[ OK ]")
PRINT("reading image")
for (int pass = 1; pass <= _n_of_passes; pass++ ) {
png_read_rows(_png_read, row_pointers, NULL, _info->height);
}
PRINT("[ OK ]")
PRINT("copying data to buffer")
for ( unsigned int row = 0, pixel = 0; row < _info->height; row++) {
for ( unsigned int byte_in_row = 0; byte_in_row + (_info->channels-1) < row_bytes;
byte_in_row += _info->channels) {
_pBuff[pixel].red = row_pointers[row][byte_in_row];
_pBuff[pixel].green = row_pointers[row][byte_in_row + 1];
_pBuff[pixel].blue = row_pointers[row][byte_in_row + 2];
if ( _info->channels == 4 ) {
_pBuff[pixel].alpha = row_pointers[row][byte_in_row + 3];
}
else {
_pBuff[pixel].alpha = 0xff; // we are using alpha channel, 0xff is full
// openaguu
}
pixel += 1; // next pixel in framebuffer
}
}
PRINT("[ OK ]")
PRINT("read end")
png_read_end (_png_read, _info );
PRINT("[ OK ]")
for( unsigned int row = 0; row < _info->height; row++ ) {
delete [] row_pointers[row];
}
delete [] row_pointers;
fclose(png_file);
// set image name
this->name (fileName);
}
/*
* create PNG image from "img"
*/
PNGImage::PNGImage (const Image &img)
: Image (img)
{
createPNGInfo();
initPNGInfo();
}
/*
* Destroy PNG image structures
*/
PNGImage::~PNGImage () {
png_destroy_read_struct(&_png_read, &_info, (png_infopp)NULL );
}
/**
* save PNG image
*/
void PNGImage::save (const std::string &fileName) {
FILE* png_file;
if ( (png_file = fopen( fileName.c_str(), "wb")) == NULL){
throw ErrSave();
}
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
if ( png_ptr == NULL ) {
fclose(png_file);
throw ErrSave();
}
if ( setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, NULL);
fclose( png_file);
}
png_init_io(png_ptr, png_file);
BoxSize box= size();
_info->height = box.height; // set height
_info->width = box.width; // set width
_info->channels = _info->channels >= 3 ? _info->channels : 3; // we need minimal 3 channels
if ( _color_type == PNG_COLOR_TYPE_PALETTE ) {
_info->valid &= 0xffef; // if original is PALETTE we must delete tRNS chunk
}
png_write_info( png_ptr, _info); //write info
if ( _n_of_passes > 1 ) {
_n_of_passes = png_set_interlace_handling(png_ptr); // set interlace handling
}
if ( _info->color_type == PNG_COLOR_TYPE_PALETTE && _info->bit_depth < 8 ) {
png_set_packing(png_ptr);
}
png_bytepp row_pointers = new png_bytep[_info->height]; // create memory for image in png format
int channels = _info->channels; // get channels
png_uint_32 row_bytes = channels*_info->width; // how many bytes we need for image in png
for( unsigned int row = 0; row < _info->height; row++ ) {
row_pointers[row] = new png_byte[row_bytes]; // create memory for pixels on rows
}
for ( unsigned int row = 0, pixel = 0; row < _info->height; row++) { // copy image to png format
for ( unsigned int byte_in_row = 0; byte_in_row + (channels-1) < row_bytes;
byte_in_row += channels) {
row_pointers[row][byte_in_row] = _pBuff[pixel].red;
row_pointers[row][byte_in_row + 1] = _pBuff[pixel].green;
row_pointers[row][byte_in_row + 2] = _pBuff[pixel].blue;
if ( channels == 4 ) {
row_pointers[row][byte_in_row + 3] = _pBuff[pixel].alpha;
}
pixel += 1; // next pixel in framebuffer
}
}
for ( int pass = 1; pass <= _n_of_passes; pass++ ) { // write image
png_write_rows(png_ptr, row_pointers, _info->height);
}
png_write_end (png_ptr, _info );
png_destroy_write_struct( &png_ptr, NULL );
for( unsigned int row = 0; row < _info->height; row++ ) {
delete [] row_pointers[row];
}
delete [] row_pointers;
fclose(png_file);
// set image name
this->name (fileName);
}