summaryrefslogtreecommitdiffstats
path: root/conversion/jpeg.cc
diff options
context:
space:
mode:
Diffstat (limited to 'conversion/jpeg.cc')
-rw-r--r--conversion/jpeg.cc530
1 files changed, 530 insertions, 0 deletions
diff --git a/conversion/jpeg.cc b/conversion/jpeg.cc
new file mode 100644
index 0000000..7638d82
--- /dev/null
+++ b/conversion/jpeg.cc
@@ -0,0 +1,530 @@
+/*
+* 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: jpeg.cc
+ *
+ * MODULE: conversion
+ *
+ * CLASSES: r_Conv_JPEG
+ *
+ * COMMENTS:
+ *
+ * Provides functions to convert data to JPEG and back
+ *
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <iostream>
+#include <string.h>
+#include <setjmp.h>
+
+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; j<height; j++, src += lineAdd)
+ {
+ srcPtr = src; rowPtr = row;
+
+ switch (desc.baseType)
+ {
+ case ctype_bool:
+ for (i=0; i<width; i++, srcPtr += pixelAdd)
+ {
+ *rowPtr++ = (*srcPtr == 0) ? 0 : 0xff;
+ }
+ 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;
+ }
+ jpeg_write_scanlines(cptr, row_pointers, 1);
+ }
+
+ delete [] row; row = NULL;
+
+ jpeg_finish_compress(cptr);
+
+ jpeg_destroy_compress(cptr);
+
+ jpegSize = memfs_size(handle);
+
+ if ((desc.dest = (char*)mystore.storage_alloc(jpegSize)) == NULL)
+ {
+ RMInit::logOut << "r_Conv_JPEG::convertTo(" << options << "): out of memory" << endl;
+ throw r_Error(MEMMORYALLOCATIONERROR);
+ }
+ memfs_seek(handle, 0, SEEK_SET);
+ memfs_read(handle, desc.dest, jpegSize);
+
+ desc.destInterv = r_Minterval(1);
+ desc.destInterv << r_Sinterval((r_Range)0, (r_Range)jpegSize - 1);
+
+ desc.destType = r_Type::get_any_type("char");
+
+ return desc;
+}
+
+
+r_convDesc &r_Conv_JPEG::convertFrom(const char *options) throw(r_Error)
+{
+ struct jpeg_source_mgr srcMgr;
+ struct jpeg_decompress_struct *dptr=NULL;
+ struct jpeg_error_mgr *jptr=NULL;
+ my_error_mgr jerr;
+ my_decompress_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;
+ char *dest=NULL, *destPtr=NULL;
+
+ row = NULL; desc.dest = NULL;
+ memset(&cinfo, 0, sizeof(my_decompress_struct));
+ cinfo.handle = (thandle_t)handle;
+ dptr = (struct jpeg_decompress_struct*)&cinfo.pub;
+ jptr = (struct jpeg_error_mgr*)&jerr.pub;
+
+ dptr->err = 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<height; j++, dest += lineAdd)
+ {
+ jpeg_read_scanlines(dptr, row_pointers, 1);
+ destPtr = dest; rowPtr = row;
+ switch (spp)
+ {
+ case 1:
+ for (i=0; i<width; i++, destPtr += pixelAdd)
+ {
+ *destPtr = *rowPtr++;
+ }
+ break;
+ case 3:
+ for (i=0; i<width; i++, destPtr += pixelAdd)
+ {
+ destPtr[0] = *rowPtr++; destPtr[1] = *rowPtr++; destPtr[2] = *rowPtr++;
+ }
+ break;
+ }
+ }
+
+ delete [] row; row = NULL;
+
+ jpeg_finish_decompress(dptr);
+
+ jpeg_destroy_decompress(dptr);
+
+ 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_JPEG::get_name( void ) const
+{
+ return format_name_jpeg;
+}
+
+
+r_Data_Format r_Conv_JPEG::get_data_format( void ) const
+{
+ return r_JPEG;
+}
+
+
+r_Convertor *r_Conv_JPEG::clone( void ) const
+{
+ return new r_Conv_JPEG(desc.src, desc.srcInterv, desc.baseType);
+}