cdesktopenv/cde/lib/DtHelp/JpegUtils.c

652 lines
20 KiB
C

/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <setjmp.h>
#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; i<cinfo.actual_number_of_colors; i++)
{
if (cinfo.out_color_space == JCS_GRAYSCALE)
{
colors[i].red = colors[i].green = colors[i].blue =
INTERP_TO_XCOLORSPACE(cinfo.colormap[0][i]);
}
else /* JCS_RGB */
{
colors[i].red = INTERP_TO_XCOLORSPACE(cinfo.colormap[0][i]);
colors[i].green = INTERP_TO_XCOLORSPACE(cinfo.colormap[1][i]);
colors[i].blue = INTERP_TO_XCOLORSPACE(cinfo.colormap[2][i]);
}
}
*xcolors = colors;
*ncolors = cinfo.actual_number_of_colors;
}
/*
** Set the other return parameters
*/
*in_out_width = cinfo.output_width;
*in_out_height = cinfo.output_height;
/*
** Shut down the decompression engine and free the allocated memory
*/
(*dest_mgr->finish_output) (&cinfo, dest_mgr);
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
/*
** Return success
*/
return (_DtGrSUCCESS);
}