diff options
Diffstat (limited to 'conversion/png.cc')
-rw-r--r-- | conversion/png.cc | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/conversion/png.cc b/conversion/png.cc new file mode 100644 index 0000000..b869a82 --- /dev/null +++ b/conversion/png.cc @@ -0,0 +1,597 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: png.cc + * + * MODULE: conversion + * Provides functions to convert data to PNG and back. + * + * CLASSES: r_Conv_PNG + * + * COMMENTS: + * - option parsing known bugs: + * * no overflow check on options string buffer + * * missing ")" is silently ignored + * * on hex input, non-hex chars are silently discarded (!) + * * too large numbers are mapped to unsigned short's max int + * * negative numbers are mapped to unsigned short's max int + * - do not use "," within an option because this is the parse_param separator + * - FIXME: define some 3xxx error codes instead of general exception + * +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <iostream> +#include <string.h> + +#include "png.h" + +#include "raslib/rminit.hh" +#include "raslib/rmdebug.hh" +#include "debug.hh" + +#include "conversion/png.hh" +#include "conversion/memfs.hh" + +#include "raslib/parseparams.hh" + + +// transparency keyword in option string (cf. PNG standard): +#define TRANSP_KEY "tRNS" + + +/* memfs interface functions in C namespace */ + +extern "C" { + +static void png_mem_read_data(png_struct *png_ptr, png_byte *data, png_size_t length) +{ + void *handle=NULL; + + handle = (void*)png_get_io_ptr(png_ptr); + memfs_chunk_read(handle, (tdata_t)data, (tsize_t)length); +} + + +static void png_mem_write_data(png_struct *png_ptr, png_byte *data, png_size_t length) +{ + void *handle=NULL; + + handle = (void*)png_get_io_ptr(png_ptr); + memfs_write(handle, (tdata_t)data, (tsize_t)length); +} + + +static void png_mem_flush_data(png_struct *png_ptr) +{ + void *handle=NULL; + + handle = (void*)png_get_io_ptr(png_ptr); +} + + +/* Customized error handling */ +static void *png_user_error_ptr = NULL; + +static void png_user_warning_fn(png_struct *png_ptr, const char *warning_msg) +{ + fprintf(stdout, "r_Conv_PNG warning: %s\n", warning_msg); fflush(stdout); +} + +static void png_user_error_fn(png_struct *png_ptr, const char *error_msg) +{ + fprintf(stderr, "r_Conv_PNG error: %s\n", error_msg); + // return from this routine, exception will be thrown in setjmp code +} + +} // end of C namespace + + + + +/* + * r_Conv_PNG class for converting MDD to PNG and back + */ + +const char *r_Conv_PNG::name_InfoKey = "Description"; +const char *r_Conv_PNG::name_InfoText = "rasdaman MDD encoded as PNG"; +const char *r_Conv_PNG::method_convertTo = "r_Conv_PNG::convertTo()"; +const char *r_Conv_PNG::method_convertFrom = "r_Conv_PNG::convertFrom()"; + +r_Conv_PNG::r_Conv_PNG(const char *src, const r_Minterval &interv, int tp) throw(r_Error) +: r_Convert_Memory(src, interv, tp) +{ +} + + +r_Conv_PNG::r_Conv_PNG(const char *src, const r_Minterval &interv, const r_Type *tp) throw(r_Error) +: r_Convert_Memory(src, interv, tp) +{ +} + + +r_Conv_PNG::~r_Conv_PNG(void) +{ +} + + +r_convDesc &r_Conv_PNG::convertTo( const char *options ) throw(r_Error) +{ + ENTER( "r_Conv_PNG::convertTo( " << (options==NULL?"(null)":options) << " )" ); + + png_struct *write_ptr=NULL; + png_info *info_ptr = NULL; + int i=0, j=0; + png_uint_32 width=0, height=0; + int colourType=0, compType=0; + int spp=0, bps=0, lineAdd=0, pixelAdd=0; + png_color_8 sig_bit; + png_text infotext[1]; + char *trans_string = NULL; // transparency string buffer + int itemsScanned = 0; // # of items scanned in options string + bool transpFound = false; // keyword for transparency found in options? + + i = 0; // error state: 0 is ok, !=0 is error + + // option analysis: create parse object -- PB 2005-jul-12 + if (params == NULL) + params = new r_Parse_Params(); + params->add(TRANSP_KEY, &trans_string, r_Parse_Params::param_type_string); + transpFound = params->process(options); + + // check for good options, if any + if (options != NULL && ! transpFound) + { + RMInit::logOut << "Error: illegal PNG option string: " << options << endl; + throw r_Error(r_Error::r_Error_General); + } + + write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, png_user_error_ptr, png_user_error_fn, png_user_warning_fn); + + if (write_ptr == NULL) + i=1; + else + { + info_ptr = png_create_info_struct(write_ptr); + if (info_ptr == NULL) + { + RMInit::logOut << "Error: unable to allocate PNG header." << endl; + i=1; + } + else if (setjmp(write_ptr->jmpbuf)) + { + png_destroy_write_struct(&write_ptr, &info_ptr); + RMInit::logOut << "Error: unable to save PNG stack" << endl; + throw r_Error(r_Error::r_Error_General); + } + } + + if (i != 0) + { + png_destroy_write_struct(&write_ptr, &info_ptr); + throw r_Error(r_Error::r_Error_General); + } + + memfs_newfile(handle); + + png_set_write_fn(write_ptr, (void*)handle, png_mem_write_data, png_mem_flush_data); + + // Compression + compType = PNG_COMPRESSION_TYPE_BASE; + + // Size + width = desc.srcInterv[0].high() - desc.srcInterv[0].low() + 1; + height = desc.srcInterv[1].high() - desc.srcInterv[1].low() + 1; + + // Depth and sample format and transparency + // added transparency -- PB 2005-jul-12 + switch (desc.baseType) + { + case ctype_bool: + spp = 1; bps = 1; pixelAdd = height; lineAdd = 1; + colourType = PNG_COLOR_TYPE_GRAY; sig_bit.gray = 1; + if (transpFound) + { + itemsScanned = sscanf( trans_string, " %hi ", &(info_ptr->trans_values.gray) ); + if (itemsScanned == 1) // all required items found? + { + info_ptr->valid |= PNG_INFO_tRNS; // activate tRNS chunk + } + else + { + RMInit::logOut << "Error: illegal syntax in transparency color specification - should be \"%i\", but is: " << trans_string << endl; + throw r_Error(r_Error::r_Error_General); + } + } + break; + + case ctype_char: + spp = 1; bps = 8; pixelAdd = height; lineAdd = 1; + colourType = PNG_COLOR_TYPE_GRAY; sig_bit.gray = 8; + if (transpFound) + { + itemsScanned = sscanf( trans_string, " %hi ", &(info_ptr->trans_values.gray) ); + if (itemsScanned == 1) // all required items found? + { + info_ptr->valid |= PNG_INFO_tRNS; // activate tRNS chunk + } + else + { + RMInit::logOut << "Error: illegal syntax in transparency color specification - should be \"%i\", but is: " << trans_string << endl; + throw r_Error(r_Error::r_Error_General); + } + } + break; + + case ctype_rgb: + spp = 3; bps = 8; pixelAdd = 3*height; lineAdd = 3; + colourType = PNG_COLOR_TYPE_RGB; + sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; + if (transpFound) + { + itemsScanned = sscanf( trans_string, " ( %hi ; %hi ; %hi ) ", &(info_ptr->trans_values.red), &(info_ptr->trans_values.green), &(info_ptr->trans_values.blue) ); + if (itemsScanned == 3) // all required items found? + { + info_ptr->valid |= PNG_INFO_tRNS; // activate tRNS chunk + } + else + { + RMInit::logOut << "Error: illegal syntax in item #" << itemsScanned << " of transparency color specification - should be \"(%i;%i;%i)\", but is: " << trans_string << endl; + throw r_Error(r_Error::r_Error_General); + } + } + break; + + default: + RMInit::logOut << "Error: " << method_convertTo << ": Unknown base type." << endl; + throw r_Error(r_Error::r_Error_General); + } // switch + + // adjust transparency color value to pixel depth (unconditionally, even if transparency is unused) + if (bps == 8) + { + info_ptr->trans_values.red &= 0xff; + info_ptr->trans_values.green &= 0xff; + info_ptr->trans_values.blue &= 0xff; + info_ptr->trans_values.gray &= 0xff; + } + + if (trans_string != NULL) + { + delete [] trans_string; + trans_string = NULL; + } + + + png_set_IHDR(write_ptr, info_ptr, width, height, bps, colourType, PNG_INTERLACE_NONE, compType, PNG_FILTER_TYPE_BASE); + png_set_sBIT(write_ptr, info_ptr, &sig_bit); + + // Info text + infotext[0].key = new char[strlen(name_InfoKey)+1]; + strcpy(infotext[0].key, name_InfoKey); + infotext[0].text = new char[strlen(name_InfoText)+1]; + strcpy(infotext[0].text, name_InfoText); + infotext[0].compression = PNG_TEXT_COMPRESSION_NONE; + infotext[0].text_length = strlen(infotext[0].text); + png_set_text(write_ptr, info_ptr, infotext, 1); + + // Write header + png_write_info(write_ptr, info_ptr); + + png_byte *row=NULL, *rowPtr=NULL; + const unsigned char *src=NULL, *srcPtr=NULL; + + row = new png_byte[((bps * spp * width + 7) >> 3)]; + src = (const unsigned char*)(desc.src); + + for (j=0; j<height; j++) + { + rowPtr = row; srcPtr = src; + + switch (desc.baseType) + { + case ctype_bool: + { + int mask=0; + png_byte val=0; + + val = 0; mask = 0x80; // png docs: leftmost pixel in high-order bits + for (i=0; i<width; i++, srcPtr += pixelAdd) + { + if (*srcPtr != 0) val |= mask; + mask >>= 1; + if (mask == 0) + { + *rowPtr++ = val; val = 0; mask = 0x80; + } + } + if (mask != 0x80) *rowPtr++ = val; + } + break; + case ctype_char: + { + for (i=0; i<width; i++, srcPtr += pixelAdd) + { + *rowPtr++ = *srcPtr; + } + } + break; + case ctype_rgb: + { + for (i=0; i<width; i++, srcPtr += pixelAdd) + { + *rowPtr++ = srcPtr[0]; + *rowPtr++ = srcPtr[1]; + *rowPtr++ = srcPtr[2]; + } + } + break; + } + + png_write_row(write_ptr, row); + + src += lineAdd; + } + + delete [] row; row=NULL; + + png_write_end(write_ptr, info_ptr); + +#ifdef RMANDEBUG + // if (RManDebug >= 4) + { + RMInit::dbgOut << "wrote PNG image: width=" << width << ", height=" << height << ", bps=" << bps << ", colour="; + switch (desc.baseType) + { + case ctype_bool: + RMInit::dbgOut << "bw"; + if (transpFound) + RMInit::dbgOut << ", transparent=" << info_ptr->trans_values.gray; + break; + case ctype_char: + RMInit::dbgOut << "grey"; + if (transpFound) + RMInit::dbgOut << ", transparent=" << info_ptr->trans_values.gray; + break; + case ctype_rgb: + RMInit::dbgOut << "rgb"; + if (transpFound) + RMInit::dbgOut << ", transparent=(" << info_ptr->trans_values.red << info_ptr->trans_values.green << info_ptr->trans_values.blue << ")"; + break; + default: + RMInit::dbgOut << "(illegal)"; + break; + } + RMInit::dbgOut << endl; + } +#endif + + // --- Cleanup ------------------------------------------------------- + png_destroy_write_struct(&write_ptr, &info_ptr); + + delete [] infotext[0].key; infotext[0].key=NULL; + delete [] infotext[0].text; infotext[0].text=NULL; + + r_Long pngSize = memfs_size(handle); + + if ((desc.dest = (char*)mystore.storage_alloc(pngSize)) == NULL) + { + RMInit::logOut << "Error: " << method_convertTo << ": out of memory." << endl; + throw r_Error(MEMMORYALLOCATIONERROR); + } + memfs_seek(handle, 0, SEEK_SET); + memfs_read(handle, desc.dest, pngSize); + + desc.destInterv = r_Minterval(1); + desc.destInterv << r_Sinterval((r_Range)0, (r_Range)pngSize - 1); + + // set result type to char string + desc.destType = r_Type::get_any_type("char"); + + LEAVE( "r_Conv_PNG::convertTo()" ); + return desc; +} + + +r_convDesc &r_Conv_PNG::convertFrom(const char *options) throw(r_Error) +{ + png_struct *read_ptr=NULL; + png_info *info_ptr = NULL; + int i=0, j=0, pass=0, numPasses=0; + png_uint_32 width=0, height=0, pitch=0; + int colourType=0, interlaceType=0, compType=0, filterType=0; + int spp=0, bps=0, lineAdd=0, pixelAdd=0; + + i = 0; + read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, png_user_error_ptr, png_user_error_fn, png_user_warning_fn); + if (read_ptr == NULL) i=1; + else + { + info_ptr = png_create_info_struct(read_ptr); + if (info_ptr == NULL) i=1; + else if (setjmp(read_ptr->jmpbuf)) + { + png_destroy_read_struct(&read_ptr, &info_ptr, NULL); + RMInit::logOut << "r_Conv_PNG::convertFrom(" << options << "): unable to save the stack" << endl; + throw r_Error(r_Error::r_Error_General); + } + } + if (i != 0) + { + png_destroy_read_struct(&read_ptr, &info_ptr, NULL); + throw r_Error(r_Error::r_Error_General); + } + + memfs_chunk_initfs(handle, (char*)desc.src, (r_Long)(desc.srcInterv[0].high() - desc.srcInterv[0].low() + 1)); + + desc.dest = NULL; + + png_set_read_fn(read_ptr, (void*)handle, png_mem_read_data); + + png_read_info(read_ptr, info_ptr); + + png_get_IHDR(read_ptr, info_ptr, &width, &height, &bps, &colourType, &interlaceType, &compType, &filterType); + + if (bps > 8) + { + RMInit::logOut << method_convertFrom << " warning: 16 bit samples quantized to 8 bit" << endl; + png_set_strip_16(read_ptr); + } + + if ((colourType & PNG_COLOR_MASK_ALPHA) != 0) + { + RMInit::logOut << method_convertFrom << " warning: image contains alpha channel which will be lost" << endl; + png_set_strip_alpha(read_ptr); + } + + if (bps < 8) + { + png_set_packing(read_ptr); // extract depths 1-4 as 1 byte per pixel + } + + switch (colourType) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_GRAY_ALPHA: + pitch = width; lineAdd = 1; pixelAdd = height; + if (bps == 1) desc.baseType = ctype_bool; else desc.baseType = ctype_char; + break; + case PNG_COLOR_TYPE_PALETTE: + png_set_expand(read_ptr); + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + pitch = 3*width; lineAdd = 3; pixelAdd = 3*height; + desc.baseType = ctype_rgb; + break; + default: + RMInit::logOut << method_convertFrom << ": don't understand image format" << endl; + throw r_Error(r_Error::r_Error_General); + } + + numPasses = png_set_interlace_handling(read_ptr); + +#ifdef RMANDEBUG + if (RManDebug >= 4) + { + RMInit::dbgOut << "PNG image: width " << width << ", height " << height << ", bps " << bps; + RMInit::dbgOut << ", colour "; + switch (desc.baseType) + { + case ctype_bool: RMInit::dbgOut << "bw"; break; + case ctype_char: RMInit::dbgOut << "grey"; break; + case ctype_rgb: RMInit::dbgOut << "rgb"; break; + } + RMInit::dbgOut << ", interlace level " << numPasses << endl; + } +#endif + + png_byte *row = new png_byte[pitch]; + + desc.dest = (char*)mystore.storage_alloc(pitch * height); + + for (pass=0; pass < numPasses; pass++) + { + unsigned char *dest, *destPtr; + + dest = (unsigned char*)(desc.dest); + for (j=0; j<height; j++, dest += lineAdd) + { + png_byte *rowPtr=NULL; + + destPtr = dest; rowPtr = row; + switch (desc.baseType) + { + case ctype_bool: + case ctype_char: + { + // In case of multiple passes set up the buffer according to the last pass + if (pass != 0) + { + for (i=0; i<width; i++, destPtr += pixelAdd) + { + *rowPtr++ = *destPtr; + } + destPtr = dest; rowPtr = row; + } + png_read_row(read_ptr, row, NULL); + for (i=0; i<width; i++, destPtr += pixelAdd) + { + *destPtr = *rowPtr++; + } + } + break; + case ctype_rgb: + { + if (pass != 0) + { + for (i=0; i<width; i++, destPtr += pixelAdd) + { + *rowPtr++ = destPtr[0]; *rowPtr++ = destPtr[1]; *rowPtr++ = destPtr[2]; + } + destPtr = dest; rowPtr = row; + } + png_read_row(read_ptr, row, NULL); + for (i=0; i<width; i++, destPtr += pixelAdd) + { + destPtr[0] = *rowPtr++; destPtr[1] = *rowPtr++; destPtr[2] = *rowPtr++; + } + } + break; + } + } + } + + png_read_end(read_ptr, info_ptr); + + delete [] row; row=NULL; + + png_destroy_read_struct(&read_ptr, &info_ptr, NULL); + + desc.destInterv = r_Minterval(2); + desc.destInterv << r_Sinterval((r_Range)0, (r_Range)width-1) + << r_Sinterval((r_Range)0, (r_Range)height-1); + + desc.destType = get_external_type(desc.baseType); + + return desc; +} + + + +const char *r_Conv_PNG::get_name( void ) const +{ + return format_name_png; +} + + +r_Data_Format r_Conv_PNG::get_data_format( void ) const +{ + return r_PNG; +} + + +r_Convertor *r_Conv_PNG::clone( void ) const +{ + return new r_Conv_PNG(desc.src, desc.srcInterv, desc.baseType); +} |