Skip to content

Commit

Permalink
Add libpng library support for PNG files
Browse files Browse the repository at this point in the history
Currently works only on Unix (Linux and MacOS) systems with preinstalled libpng library

#7
  • Loading branch information
ihhub committed Jun 30, 2017
1 parent 453d6bb commit 0724834
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 0 deletions.
168 changes: 168 additions & 0 deletions PenguinV/Library/FileOperation/png_file.cpp
@@ -0,0 +1,168 @@
#include <stdlib.h>
#include <png.h>
#include <vector>
#include "png_file.h"
#include "../image_exception.h"

namespace Png_Operation
{
Bitmap_Image::Image Load( const std::string & path )
{
if( path.empty() )
throw imageException( "Incorrect file path for image file loading" );

FILE * file = fopen( path.data(), "rb" );
if( !file )
throw imageException( "Cannot create file for reading" );

png_structp png = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
if( !png )
return Bitmap_Image::Image();

png_infop info = png_create_info_struct( png );
if( !info )
return Bitmap_Image::Image();

png_init_io( png, file );
png_read_info( png, info );

const uint32_t width = png_get_image_width ( png, info );
const uint32_t height = png_get_image_height( png, info );
const uint8_t colorType = png_get_color_type ( png, info );
const uint8_t bitDepth = png_get_bit_depth ( png, info );

if( bitDepth == 16u )
png_set_strip_16( png );

if( colorType == PNG_COLOR_TYPE_PALETTE )
png_set_palette_to_rgb( png );

// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth
if( colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8u )
png_set_expand_gray_1_2_4_to_8( png );

if( png_get_valid( png, info, PNG_INFO_tRNS ) )
png_set_tRNS_to_alpha( png );

// These color_type don't have an alpha channel then fill it with 0xff
if( colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_PALETTE )
png_set_filler( png, 0xFF, PNG_FILLER_AFTER );

if( colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA )
png_set_gray_to_rgb( png );

png_read_update_info( png, info );

uint8_t ** row_pointers = reinterpret_cast<uint8_t**>( malloc( sizeof( uint8_t* ) * height ) );

const size_t rowByteCount = png_get_rowbytes( png, info );

for( uint32_t y = 0; y < height; ++y )
row_pointers[y] = reinterpret_cast<uint8_t*>( malloc( rowByteCount ) );

png_read_image( png, row_pointers );

Bitmap_Image::Image image( width, height, Bitmap_Image::RGB );

uint8_t * outY = image.data();
for( uint32_t y = 0; y < height; ++y, outY += image.rowSize() ) {
const uint8_t * column = row_pointers[y];
uint8_t * outX = outY;
for( uint32_t x = 0; x < width; ++x, column += 4 ) {
*(outX++) = column[2];
*(outX++) = column[1];
*(outX++) = column[0];
}
}

fclose( file );

for( uint32_t y = 0; y < height; ++y )
free( row_pointers[y] );
free( row_pointers );

png_destroy_read_struct( &png, &info, NULL );

return image;
}

void Load( const std::string & path, Bitmap_Image::Image & raw )
{
raw = Load( path );
}

void Save( const std::string & path, const Bitmap_Image::Image & image )
{
Save( path, image, 0, 0, image.width(), image.height() );
}

void Save( const std::string & path, const Bitmap_Image::Image & image, uint32_t startX, uint32_t startY,
uint32_t width, uint32_t height )
{
Image_Function::ParameterValidation( image, startX, startY, width, height );

FILE * file = fopen( path.data(), "wb" );
if( !file )
throw imageException( "Cannot create file for saving" );

png_structp png = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
if( !png )
throw imageException( "Cannot create file for saving" );

png_infop info = png_create_info_struct( png );
if( !info )
throw imageException( "Cannot create file for saving" );

png_init_io( png, file );

// Output is 8bit depth, RGBA format
png_set_IHDR( png, info, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT );

png_write_info( png, info );

uint8_t ** row_pointers = reinterpret_cast<uint8_t**>( malloc( sizeof( uint8_t* ) * height ) );

const size_t rowByteCount = png_get_rowbytes( png, info );

for( uint32_t y = 0; y < height; ++y )
row_pointers[y] = reinterpret_cast<uint8_t*>( malloc( rowByteCount ) );

const bool grayScaleImage = image.colorCount() == Bitmap_Image::GRAY_SCALE;

const uint8_t * outY = image.data() + startY * image.rowSize() + startX * image.colorCount();
for( uint32_t y = 0; y < height; ++y, outY += image.rowSize() ) {

uint8_t * column = row_pointers[y];
const uint8_t * outX = outY;

if( grayScaleImage )
{
for( uint32_t x = 0; x < width; ++x, column += 4 ) {
column[0] = column[1] = column[2] = *(outX++);
column[3] = 255;
}
}
else
{
for( uint32_t x = 0; x < width; ++x, column += 4 ) {
column[2] = *(outX++);
column[1] = *(outX++);
column[0] = *(outX++);
column[3] = 255;
}
}
}

png_write_image( png, row_pointers );
png_write_end( png, NULL );

fclose( file );

for( uint32_t y = 0; y < height; ++y )
free( row_pointers[y] );
free( row_pointers );

png_destroy_write_struct( &png, &info );
}
}
12 changes: 12 additions & 0 deletions PenguinV/Library/FileOperation/png_file.h
@@ -0,0 +1,12 @@
#include "../image_buffer.h"

namespace Png_Operation
{
// Below functions support only Bitmap_Image::Image and Bitmap_Image::ColorImage classes
Bitmap_Image::Image Load( const std::string & path );
void Load( const std::string & path, Bitmap_Image::Image & image );

void Save( const std::string & path, const Bitmap_Image::Image & image );
void Save( const std::string & path, const Bitmap_Image::Image & image, uint32_t startX, uint32_t startY,
uint32_t width, uint32_t height );
}

0 comments on commit 0724834

Please sign in to comment.