/* * 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: jpeg.cc * * MODULE: conversion * * CLASSES: r_Conv_JPEG * * COMMENTS: * * Provides functions to convert data to JPEG and back * */ #include #include #include #include #include extern "C" { #include "jpeglib.h" } #include "raslib/rminit.hh" #include "raslib/parseparams.hh" #include "conversion/jpeg.hh" #include "conversion/memfs.hh" #define JPEG_IO_BUFFER_SIZE 4096 // JPEG-interface in C-namespace (like JPEG lib) extern "C" { typedef struct my_compress_struct { jpeg_compress_struct pub; thandle_t handle; JOCTET *buffer; int bufferSize; } my_compress_struct; typedef struct my_decompress_struct { jpeg_decompress_struct pub; thandle_t handle; JOCTET *buffer; int bufferSize; } my_decompress_struct; typedef struct my_error_mgr { jpeg_error_mgr pub; jmp_buf setjmp_buffer; } my_error_mgr; // See jpeg library example static void my_error_exit(jpeg_common_struct *cptr) { my_error_mgr *myerr = (my_error_mgr*)(cptr->err); (*cptr->err->output_message)(cptr); longjmp(myerr->setjmp_buffer, 1); } /* * Memory IO wrapper functions, rely on memFS. */ // Destination manager methods // cptr is actually a pointer to my_compress_struct static void dm_init_destination(jpeg_compress_struct *cptr) { my_compress_struct *mptr = (my_compress_struct*)cptr; if ((mptr->buffer = new JOCTET[JPEG_IO_BUFFER_SIZE]) == NULL) { RMInit::logOut << "r_Conv_JPEG@dm_init_destination(): out of memory" << endl; throw r_Error(MEMMORYALLOCATIONERROR); } mptr->bufferSize = JPEG_IO_BUFFER_SIZE; cptr->dest->next_output_byte = mptr->buffer; cptr->dest->free_in_buffer = mptr->bufferSize; } static boolean dm_empty_output_buffer(jpeg_compress_struct *cptr) { my_compress_struct *mptr = (my_compress_struct*)cptr; boolean retval=FALSE; if (memfs_write(mptr->handle, mptr->buffer, mptr->bufferSize) == mptr->bufferSize) { cptr->dest->next_output_byte = mptr->buffer; cptr->dest->free_in_buffer = mptr->bufferSize; retval=TRUE; } return retval; } static void dm_term_destination(jpeg_compress_struct *cptr) { my_compress_struct *mptr = (my_compress_struct*)cptr; if (cptr->dest->next_output_byte != mptr->buffer) { memfs_write(mptr->handle, mptr->buffer, (cptr->dest->next_output_byte - mptr->buffer)); } delete [] mptr->buffer; mptr->buffer = NULL; } // Source manager methods // dptr is actually a pointer to my_decompress_struct static void sm_init_source(jpeg_decompress_struct *dptr) { my_decompress_struct *mptr = (my_decompress_struct*)dptr; if ((mptr->buffer = new JOCTET[JPEG_IO_BUFFER_SIZE]) == NULL) { RMInit::logOut << "r_Conv_JPEG@sm_init_source(): out of memory" << endl; throw r_Error(MEMMORYALLOCATIONERROR); } mptr->bufferSize = JPEG_IO_BUFFER_SIZE; dptr->src->next_input_byte = mptr->buffer; dptr->src->bytes_in_buffer = 0; } // See jdatasrc.c static boolean sm_fill_input_buffer(jpeg_decompress_struct *dptr) { my_decompress_struct *mptr = (my_decompress_struct*)dptr; int read_bytes=0; if ((read_bytes = memfs_chunk_read(mptr->handle, mptr->buffer, mptr->bufferSize)) != 0) { dptr->src->bytes_in_buffer = read_bytes; } else { mptr->buffer[0] = (JOCTET)0xff; mptr->buffer[1] = (JOCTET)JPEG_EOI; dptr->src->bytes_in_buffer = 2; } dptr->src->next_input_byte = mptr->buffer; return TRUE; } // See jdatasrc.c static void sm_skip_input_data(jpeg_decompress_struct *dptr, long num_bytes) { my_decompress_struct *mptr = (my_decompress_struct*)dptr; if (num_bytes < dptr->src->bytes_in_buffer) { dptr->src->next_input_byte += num_bytes; dptr->src->bytes_in_buffer -= num_bytes; } else { int read_bytes=0; num_bytes -= dptr->src->bytes_in_buffer; dptr->src->next_input_byte = mptr->buffer; while (num_bytes >= mptr->bufferSize) { memfs_chunk_seek(mptr->handle, mptr->bufferSize, SEEK_CUR); num_bytes -= mptr->bufferSize; } read_bytes = memfs_chunk_read(mptr->handle, mptr->buffer, mptr->bufferSize); if (read_bytes <= num_bytes) { mptr->buffer[0] = (JOCTET)0xff; mptr->buffer[1] = (JOCTET)JPEG_EOI; dptr->src->bytes_in_buffer = 2; } else { dptr->src->next_input_byte = mptr->buffer + num_bytes; dptr->src->bytes_in_buffer = read_bytes - num_bytes; } } } /*static boolean sm_resync_to_restart(jpeg_decompress_struct *dptr, int desired) { return FALSE; }*/ static void sm_term_source(jpeg_decompress_struct *dptr) { my_decompress_struct *mptr = (my_decompress_struct*)dptr; delete [] mptr->buffer; mptr->buffer = NULL; } } // end of C namespace /* * r_Conv_JPEG class for converting MDD to JPEG and back */ void r_Conv_JPEG::initJPEG( void ) { quality = 80; if (params == NULL) params = new r_Parse_Params; params->add("quality", &quality, r_Parse_Params::param_type_int); } r_Conv_JPEG::r_Conv_JPEG(const char *src, const r_Minterval &interv, int tp) throw(r_Error) : r_Convert_Memory(src, interv, tp) { initJPEG(); } r_Conv_JPEG::r_Conv_JPEG(const char *src, const r_Minterval &interv, const r_Type *tp) throw(r_Error) : r_Convert_Memory(src, interv, tp) { initJPEG(); } r_Conv_JPEG::~r_Conv_JPEG(void) { } r_convDesc &r_Conv_JPEG::convertTo( const char *options) throw(r_Error) { struct jpeg_destination_mgr destMgr; struct jpeg_compress_struct *cptr=NULL; struct jpeg_error_mgr *jptr=NULL; my_error_mgr jerr; my_compress_struct cinfo; int width=0, height=0, lineAdd=0, pixelAdd=0; int i=0, j=0, spp=0; J_COLOR_SPACE jcs; JSAMPROW row_pointers[1]; JSAMPLE *row=NULL, *rowPtr=NULL; r_Long jpegSize=0; row = NULL; memset(&cinfo, 0, sizeof(my_compress_struct)); cinfo.handle = (thandle_t)handle; cptr = (struct jpeg_compress_struct*)&cinfo.pub; jptr = (struct jpeg_error_mgr*)&jerr.pub; cptr->err = jpeg_std_error(jptr); jptr->error_exit = my_error_exit; params->process(options); if (setjmp(jerr.setjmp_buffer)) { if (row != NULL) {delete [] row; row = NULL;} jpeg_abort_compress(cptr); jpeg_destroy_compress(cptr); // destination manager destructor might not be called on an abort if (cinfo.buffer != NULL){delete [] cinfo.buffer; cinfo.buffer=NULL;} RMInit::logOut << "r_Conv_JPEG::convertTo(" << options << "): unable to save the stack" << endl; throw r_Error(r_Error::r_Error_General); } jpeg_create_compress(cptr); jpeg_set_quality(cptr, quality, 0); memfs_newfile(handle); destMgr.init_destination = dm_init_destination; destMgr.empty_output_buffer = dm_empty_output_buffer; destMgr.term_destination = dm_term_destination; cptr->dest = &destMgr; width = (int)(desc.srcInterv[0].high() - desc.srcInterv[0].low() + 1); height = (int)(desc.srcInterv[1].high() - desc.srcInterv[1].low() + 1); cptr->image_width = (JDIMENSION)width; cptr->image_height = (JDIMENSION)height; switch (desc.baseType) { case ctype_bool: case ctype_char: jcs = JCS_GRAYSCALE; spp = 1; lineAdd = 1; pixelAdd = height; break; case ctype_rgb: jcs = JCS_RGB; spp = 3; lineAdd = 3; pixelAdd = 3*height; break; default: RMInit::logOut << "r_Conv_JPEG::convertTo(" << options << "): unsupported base type" << endl; throw r_Error(r_Error::r_Error_General); } cptr->input_components = spp; cptr->in_color_space = jcs; jpeg_set_defaults(cptr); jpeg_set_quality(cptr, quality, TRUE); jpeg_start_compress(cptr, TRUE); if ((row = new JSAMPLE[width * spp]) == NULL) { RMInit::logOut << "r_Conv_JPEG::convertTo(" << options << "): out of memory" << endl; throw r_Error(MEMMORYALLOCATIONERROR); } row_pointers[0] = row; const unsigned char *src=NULL, *srcPtr=NULL; src = (const unsigned char*)desc.src; for (j=0; jerr = jpeg_std_error(jptr); jptr->error_exit = my_error_exit; if (setjmp(jerr.setjmp_buffer)) { if (row != NULL) {delete [] row; row = NULL;} if (desc.dest != NULL) {mystore.storage_free(desc.dest); desc.dest = NULL;} jpeg_abort_decompress(dptr); jpeg_destroy_decompress(dptr); // Source manager destructor might not be called on an abort if (cinfo.buffer != NULL) {delete [] cinfo.buffer; cinfo.buffer=NULL;} RMInit::logOut << "r_Conv_JPEG::convertFrom(" << options << "): unable to save the stack" << endl; throw r_Error(r_Error::r_Error_General); } jpeg_create_decompress(dptr); memfs_chunk_initfs(handle, (char*)desc.src, (r_Long)(desc.srcInterv[0].high() - desc.srcInterv[0].low() + 1)); srcMgr.init_source = sm_init_source; srcMgr.fill_input_buffer = sm_fill_input_buffer; srcMgr.skip_input_data = sm_skip_input_data; srcMgr.resync_to_restart = NULL; //sm_resync_to_restart; srcMgr.term_source = sm_term_source; dptr->src = &srcMgr; jpeg_read_header(dptr, TRUE); width = (int)(dptr->image_width); height = (int)(dptr->image_height); if (dptr->num_components == 1) { desc.baseType = ctype_char; lineAdd = 1; pixelAdd = height; } else { desc.baseType = ctype_rgb; lineAdd = 3; pixelAdd = 3*height; dptr->out_color_space = JCS_RGB; } jpeg_start_decompress(dptr); spp = (int)(dptr->output_components); row = new JSAMPLE[width * spp]; desc.dest = (char*)mystore.storage_alloc(width * height * spp); if ((row == NULL) || (desc.dest == NULL)) { if (row != NULL) {delete [] row; row = NULL;} RMInit::logOut << "r_Conv_JPEG::convertFrom(): out of memory" << endl; throw r_Error(MEMMORYALLOCATIONERROR); } row_pointers[0] = row; dest = desc.dest; for (j=0; j