/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these librararies and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $XConsortium: JpegUtils.c /main/3 1996/10/06 19:38:48 rws $ */ /* ** JpegUtils.c ** ** This module provides utilities for converting jpeg data associated with ** a _DtGrStream into an XImage. The module is based on code from several ** of the files from the Independent JPEG library, version 6a (copyright ** attached below). */ /* * Copyright (C) 1991-1996, Thomas G. Lane. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * */ /* ** Include files */ #include #include #include #include #include #include "GraphicsP.h" #include "jpeglib.h" #include "cdjpeg.h" #include "JpegUtilsI.h" /* ** Macro definitions */ #define BYTESPERSAMPLE 1 #define MAX_COLORS 64 /* Not allowed to be higher than 256 */ #define BYTE_MAXVAL 0xFF #define XCOL_MAXVAL 0xFFFF #define INTERP_TO_XCOLORSPACE(val) ((XCOL_MAXVAL/BYTE_MAXVAL) * val) #define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ /* ** Type definitions */ /* ** Custom destination manager structure */ typedef struct { struct djpeg_dest_struct pub; /* public fields */ JSAMPROW pixrow; /* decompressor output buffer */ XImage *ximage; /* XImage to put pixel info into */ } ximg_dest_struct; typedef ximg_dest_struct * ximg_dest_ptr; /* ** Custom source manager structure */ typedef struct { struct jpeg_source_mgr pub; /* public fields */ _DtGrStream *stream; /* source stream */ unsigned long input_buf_size; /* size of input buffer */ JOCTET * buffer; /* start of buffer */ boolean start_of_file; /* have we gotten any data yet? */ } stream_source_mgr; typedef stream_source_mgr * stream_src_ptr; /* ** Custom error handler structure */ struct my_error_mgr { struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* for return to caller */ }; typedef struct my_error_mgr * my_error_ptr; /* ** Routines */ /****************************************************************************** * * Function my_error_exit * * This routine replaces the standard fatal error handling routine. Instead * of exiting the program, the routine longjmps back to jpeg_to_ximage, * which cleans up and returns a _DtGrCONVERT_FAILURE. * *****************************************************************************/ static void my_error_exit (j_common_ptr cinfo) { my_error_ptr myerr = (my_error_ptr) cinfo->err; (*cinfo->err->output_message) (cinfo); /* Return control to the setjmp point */ longjmp(myerr->setjmp_buffer, 1); } /****************************************************************************** * * Function my_output_message * * This routine replaces the standard error message outputter. Instead * of outputting JPEG library error and warning messages to stderr, it * suppresses them. * *****************************************************************************/ static void my_output_message (j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; /* ** Uncommenting the lines below will cause error and warning messages ** from the JPEG library to be displayed to stderr instead of suppressed */ /* ** Create the message ** ** (*cinfo->err->format_message) (cinfo, buffer); */ /* ** Send it to stderr, adding a newline ** ** fprintf(stderr, "%s\n", buffer); */ } /****************************************************************************** * * Function init_source * * This is the custom source manager's initialization routine, called by * jpeg_read_header before any data is actually read. It currently does * nothing. * *****************************************************************************/ static void init_source ( j_decompress_ptr cinfo) { stream_src_ptr src = (stream_src_ptr) cinfo->src; } /****************************************************************************** * * Function fill_input_buffer * * This is the custom source manager's fill input buffer routine, called by * the JPEG library whenever it has finished processing the data in the * source buffer and needs it to be refilled with the next chunk of data. * *****************************************************************************/ static boolean fill_input_buffer ( j_decompress_ptr cinfo) { stream_src_ptr src = (stream_src_ptr) cinfo->src; size_t nbytes; /* ** Since we always process buffer-based streams in a single chunk, the ** only reason read_input_buffer should be called for one is if the data ** in the buffer was truncated or otherwise bogus. If this is the case ** we set nbytes to zero, allocate a new buffer to hold a fake EOI marker ** so the stream buffer isn't overwritten, and let the error-handling ** code below take care of things. */ if (src->stream->type == _DtGrBUFFER) { nbytes = 0; src->buffer = (JOCTET *) (*cinfo->mem->alloc_small) ( (j_common_ptr) cinfo, JPOOL_IMAGE, 2 * SIZEOF(JOCTET)); } else /* _DtGrFILE, read the next chunk of data */ nbytes = _DtGrRead (src->buffer, 1, src->input_buf_size, src->stream); if (nbytes <= 0) { if (src->start_of_file) /* Treat empty input file as fatal error */ ERREXIT(cinfo, JERR_INPUT_EMPTY); WARNMS(cinfo, JWRN_JPEG_EOF); /* Insert a fake EOI marker */ src->buffer[0] = (JOCTET) 0xFF; src->buffer[1] = (JOCTET) JPEG_EOI; nbytes = 2; } src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = nbytes; src->start_of_file = FALSE; return TRUE; } /****************************************************************************** * * Function skip_input_data * * This is the custom source manager's skip input data function, called * by the JPEG library when it wants to skip over a potentially large * amount of uninteresting data (such as an APPn marker). * *****************************************************************************/ static void skip_input_data ( j_decompress_ptr cinfo, long num_bytes) { stream_src_ptr src = (stream_src_ptr) cinfo->src; /* Just a dumb implementation for now. Could use fseek() except ** it doesn't work on pipes. Not clear that being smart is worth ** any trouble anyway --- large skips are infrequent. */ if (num_bytes > 0) { while (num_bytes > (long) src->pub.bytes_in_buffer) { num_bytes -= (long) src->pub.bytes_in_buffer; (void) fill_input_buffer(cinfo); /* note we assume that fill_input_buffer will never return FALSE, ** so suspension need not be handled. */ } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } } /****************************************************************************** * * Function term_source * * This is the custom source manager's termination routine, it currently * does nothing. * *****************************************************************************/ static void term_source ( j_decompress_ptr cinfo) { /* no work necessary here */ return; } /****************************************************************************** * * Function jpeg_stream_src * * This is the custom source manager's creation routine. Most of the custom * source manager code is based on the JPEG library's stdio source manager * code in the jdatasrc.c file included with the JPEG library distribution. * *****************************************************************************/ static void jpeg_stream_src ( j_decompress_ptr cinfo, _DtGrStream *stream) { stream_src_ptr src; /* ** Create the custom source manager structure */ cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ( (j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(stream_source_mgr)); src = (stream_src_ptr) cinfo->src; src->stream = stream; /* ** If this is a file-based stream, we need to allocate a buffer to ** read data into. If this is a buffer-based stream, we just use the ** buffer already attached to the stream. Note that this implies that ** we always process buffer-based streams in a single chunk, if there ** is ever a reason to do otherwise, this routine and fill_input_buffer ** will need to be modified appropriately. */ if (stream->type == _DtGrFILE) { src->buffer = (JOCTET *) (*cinfo->mem->alloc_small) ( (j_common_ptr) cinfo, JPOOL_IMAGE, INPUT_BUF_SIZE * SIZEOF(JOCTET)); src->input_buf_size = INPUT_BUF_SIZE; src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer call */ src->pub.next_input_byte = NULL; /* on first read */ src->start_of_file = TRUE; } else /* _DtGrBUFFER */ { src->buffer = (unsigned char *) stream->source.buffer.base; src->input_buf_size = stream->source.buffer.size; src->pub.bytes_in_buffer = src->input_buf_size; src->pub.next_input_byte = src->buffer; src->start_of_file = FALSE; } /* ** Initialize the method procedures */ src->pub.init_source = init_source; src->pub.fill_input_buffer = fill_input_buffer; src->pub.skip_input_data = skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; /* default method */ src->pub.term_source = term_source; } /****************************************************************************** * * Function copy_pixels * * This routine loops through a scanline of decompressed, quantized * JPEG data and uses XPutPixel to copy the pixel values into the * XImage associated with the destination manager. * *****************************************************************************/ static void copy_pixels( j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, JDIMENSION rows_supplied) { register int pixval; register JSAMPROW ptr; register JDIMENSION col; ximg_dest_ptr dest = (ximg_dest_ptr) dinfo; ptr = dest->pub.buffer[0]; for (col=0; col < cinfo->output_width; col++) { pixval = GETJSAMPLE(*ptr++); XPutPixel(dest->ximage,col,cinfo->output_scanline-1, pixval); } } /****************************************************************************** * * Function start_output_ximg * * This is the data destination manager startup routine, it currently does * nothing. * *****************************************************************************/ static void start_output_ximg ( j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) { return; } /****************************************************************************** * * Function finish_output_ximg * * This is the data destination manager shutdown routine, it currently does * nothing. * *****************************************************************************/ static void finish_output_ximg (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) { } /****************************************************************************** * * Function init_jpeg_dest_mgr * * This routine allocates and initializes a data destination manager * that the JPEG library will use as a sink for JPEG data after the * data has been decompressed. * *****************************************************************************/ djpeg_dest_ptr init_jpeg_dest_mgr ( j_decompress_ptr cinfo) { ximg_dest_ptr dest; /* ** Create module interface object, fill in the method pointers */ dest = (ximg_dest_ptr) (*cinfo->mem->alloc_small) ( (j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF(ximg_dest_struct)); dest->pub.start_output = start_output_ximg; dest->pub.finish_output = finish_output_ximg; dest->pub.put_pixel_rows = copy_pixels; /* ** Calculate the output image dimensions so we can allocate the ** right amount of space */ jpeg_calc_output_dimensions(cinfo); /* ** Create a buffer for the JPEG library to write decompressed ** scanline data into. */ dest->pixrow = (JSAMPROW) (*cinfo->mem->alloc_small) ( (j_common_ptr) cinfo, JPOOL_IMAGE, cinfo->output_width * cinfo->out_color_components ); dest->pub.buffer = &dest->pixrow; dest->pub.buffer_height = 1; /* ** Return the initialized destination manager */ return (djpeg_dest_ptr) dest; } /****************************************************************************** * * Function jpeg_to_ximage * * This routine converts compressed jpeg data associated with a _DtGrStream * into an XImage. * * No X color allocation is done. The image is quantized down to MAX_COLORS * during decompression, and an array of XColor structures with the red, * green, and blue fields initialized to the colors used in the image is * returned to the caller. Each pixel value in the XImage data is an index * into this array. The caller must use this information to allocate X * color cells and substitute the appropriate pixel values into the XImage * data array before using the XImage. * * The routine makes use of a custom source data manager to allow the JPEG * data source to be a _DtGrStream, a custom data destination manager to * allow the decompressed and post-processed data to be written to an XImage, * and a custom error handler to allow standard _DtGr error codes to be * returned to the caller in the event of a JPEG library error. * *****************************************************************************/ enum _DtGrLoadStatus jpeg_to_ximage ( _DtGrStream *stream, Screen *screen, Visual *visual, Dimension *in_out_width, Dimension *in_out_height, XImage **ximage, XColor **xcolors, int *ncolors, int *xres) { struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; djpeg_dest_ptr dest_mgr = NULL; ximg_dest_ptr dest; int i, num_scanlines, nullCount, ximWidth; unsigned char *ximData = NULL; Display *display = DisplayOfScreen(screen); int nplanes = DisplayPlanes(display,XScreenNumberOfScreen(screen)); XColor *colors = NULL; /* ** Initialize the return values */ *ximage = NULL; *xres = *ncolors = *in_out_width = *in_out_height = 0; /* ** Initialize the jpeg library error handler with our custom routines */ cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; jerr.pub.output_message = my_output_message; /* ** Establish the setjmp return context for my_error_exit to use */ if (setjmp(jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. We need to ** free memory, clean up the JPEG object, and return a failure code. */ if (*ximage != NULL) XDestroyImage (*ximage); if (colors != NULL) free (colors); jpeg_destroy_decompress(&cinfo); return (_DtGrCONVERT_FAILURE); } /* ** Create a jpeg decompression object */ jpeg_create_decompress(&cinfo); /* ** Create a custom source data manager */ jpeg_stream_src(&cinfo, stream); /* ** Read the jpeg header */ jpeg_read_header(&cinfo, TRUE); if (cinfo.X_density > 0 && (cinfo.density_unit == 1 || cinfo.density_unit == 2)) { if (cinfo.density_unit == 1) *xres = cinfo.X_density; else *xres = cinfo.X_density * 2.54 + 0.5; } /* ** Initialize our desired post-processing attributes */ cinfo.quantize_colors = TRUE; cinfo.desired_number_of_colors = MAX_COLORS; /* ** Create a custom data destination manager to allow our processed data ** to be channeled into an XImage. */ dest_mgr = init_jpeg_dest_mgr(&cinfo); /* ** Initialize the decompression state */ jpeg_start_decompress(&cinfo); (*dest_mgr->start_output) (&cinfo, dest_mgr); /* ** Create an XImage to hold the processed data */ nullCount = (4 - (cinfo.output_width % 4)) & 0x03; ximWidth = cinfo.output_width + nullCount; if (nplanes > 8 ) ximData = (unsigned char *) malloc(ximWidth * cinfo.output_height * 4 ); else ximData = (unsigned char *) malloc(ximWidth * cinfo.output_height ); if (!ximData) { jpeg_destroy_decompress(&cinfo); return (_DtGrNO_MEMORY); } *ximage = XCreateImage(display, visual, nplanes, (nplanes == 1) ? XYPixmap : ZPixmap, 0, (char *)ximData, cinfo.output_width, cinfo.output_height, 32, 0); if (!*ximage) { free (ximData); jpeg_destroy_decompress(&cinfo); return (_DtGrCONVERT_FAILURE); } /* ** Store the XImage in the custom destination manager */ dest = (ximg_dest_ptr) dest_mgr; dest->ximage = *ximage; /* ** Process scanlines until there are none left */ while (cinfo.output_scanline < cinfo.output_height) { num_scanlines = jpeg_read_scanlines(&cinfo, (JSAMPARRAY)dest_mgr->buffer, dest_mgr->buffer_height); (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); } /* ** Return the colormap info as an array of XColors which can be ** used later for X color allocation purposes. */ if (cinfo.actual_number_of_colors) { colors = (XColor *) malloc((unsigned) sizeof(XColor) * cinfo.actual_number_of_colors); if (!colors) { XDestroyImage (*ximage); jpeg_destroy_decompress(&cinfo); return (_DtGrNO_MEMORY); } for (i=0; ifinish_output) (&cinfo, dest_mgr); jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); /* ** Return success */ return (_DtGrSUCCESS); }