/* * 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 . * * Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / rasdaman GmbH. * * For more information please see * or contact Peter Baumann via . / /** * 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 #include #include #include #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>= 1; if (mask == 0) { *rowPtr++ = val; val = 0; mask = 0x80; } } if (mask != 0x80) *rowPtr++ = val; } break; case ctype_char: { for (i=0; i= 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