/***** * readJPEGplc.c : JPEG progressive loading interfaces * * This file Version $Revision: 1.6 $ * * Creation date: Thu Jun 19 14:56:52 GMT+0100 1997 * Last modification: $Date: 1999/07/29 01:26:29 $ * By: $Author: sopwith $ * Current State: $State: Exp $ * * Author: newt * * Copyright (C) 1994-1997 by Ripley Software Development * All Rights Reserved * * This file is part of the XmHTML Widget Library. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****/ /***** * ChangeLog * $Log: readJPEGplc.c,v $ * Revision 1.6 1999/07/29 01:26:29 sopwith * * * Fix all warnings. * * Revision 1.5 1998/02/12 03:09:47 unammx * Merge to Koen's XmHTML 1.1.2 + following fixes: * * Wed Feb 11 20:27:19 1998 Miguel de Icaza * * * gtk-forms.c (freeForm): gtk_destroy_widget is no longer needed * with the refcounting changes. * * * gtk-xmhtml.c (gtk_xmhtml_remove): Only god knows why I was * adding the just removed widget. * * Revision 1.4 1998/01/07 01:45:41 unammx * Gtk/XmHTML is ready to be used by the Gnome hackers now! * Weeeeeee! * * This afternoon: * * - Changes to integrate gtk-xmhtml into an autoconf setup. * * - Changes to make gtk-xmhtml a library to be used by Gnome * (simply include #endif #include #include /* prevent Byte re-declaration */ #if defined(HAVE_LIBPNG) || defined(HAVE_LIBZ) #include #endif #include "XmHTMLP.h" #include "XmHTMLfuncs.h" #ifdef WITH_MOTIF #include "XCC.h" #endif #include "plc.h" #ifdef HAVE_LIBJPEG /***** * All routines in this file are fully re-entrant. *****/ /*** External Function Prototype Declarations ***/ /*** Public Variable Declarations ***/ /*** Private Datatype Declarations ****/ /* our own memory manager */ typedef struct _plc_jpeg_source_mgr{ struct jpeg_source_mgr pub; /* public fields */ PLC *plc; /* ptr to current PLC */ Byte *buffer; /* jpeg input buffer */ int buf_size; /* size of jpeg input buffer */ int nskip; /* bytes to be skipped in the input buf. */ }plc_jpeg_source_mgr; /*** Private Function Prototype Declarations ****/ static void _PLC_JPEG_ErrorExit(j_common_ptr cinfo); static void _PLC_JPEG_InitSource(j_decompress_ptr cinfo); static void _PLC_JPEG_TermSource(j_decompress_ptr cinfo); static void _PLC_JPEG_SetSource(j_decompress_ptr cinfo, PLC *plc); static void _PLC_JPEG_SkipInputData(j_decompress_ptr cinfo, long nskip); static boolean _PLC_JPEG_FillInputBuffer(j_decompress_ptr cinfo); /***** * Additional PLC object function. Performs two-pass color quantization * using FS dithering. *****/ static void _PLC_JPEG_FinalPass(PLC *plc); /*** Private Variable Declarations ***/ /***** * Name: _PLC_JPEG_ErrorExit * Return Type: void * Description: JPEG error override. Called when libjpeg signals an error. * In: * cinfo: current decompressor. * Returns: * nothing. *****/ static void _PLC_JPEG_ErrorExit(j_common_ptr cerr) { char err_msg[JMSG_LENGTH_MAX]; j_decompress_ptr cinfo = (j_decompress_ptr)cerr; plc_jpeg_source_mgr *src = (plc_jpeg_source_mgr*)cinfo->src; plc_jpeg_err_mgr *jerr = (plc_jpeg_err_mgr*)cinfo->err; PLC *plc = src->plc; /* create the error message */ (*jerr->pub.format_message)(cerr, err_msg); /* show it */ _XmHTMLWarning(__WFUNC__(plc->object->plc_any_image.owner, "_PLC_JPEG_ErrorExit"), "libjpeg error on image %s:\n" " %s", plc->url, err_msg); /* jump to saved restart point */ longjmp(jerr->setjmp_buffer, 1); } /***** * Name: _PLC_JPEG_InitSource * Return Type: void * Description: jpeg init_buffer method. Allocates the input buffer. * In: * cinfo: JPEG decompresser info. * Returns: * nothing. * Note: * This routine is only called once by the decompressor. *****/ static void _PLC_JPEG_InitSource(j_decompress_ptr cinfo) { plc_jpeg_source_mgr *src = (plc_jpeg_source_mgr*)cinfo->src; PLC *plc = src->plc; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_InitSource for %s\n", plc->url)); if(src->buf_size == 0) { /***** * Private fields. * We use our own private jpeg input buffer. *****/ src->buf_size = plc->input_size; src->buffer = (Byte*)malloc(src->buf_size * sizeof(Byte)); /* public fields */ src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = 0; } } /* What's 42? */ #define TATTUQOLTUAE do { \ /* end of data, insert a fake EOI marker and return True */ \ if(plc->plc_data_status == STREAM_END) \ { \ _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FillInputBuffer, end of " \ "input, inserting fake EOI marker.\n")); \ src->buffer[0] = (JOCTET)0xFF; \ src->buffer[1] = (JOCTET)JPEG_EOI; \ src->pub.next_input_byte = src->buffer; \ src->pub.bytes_in_buffer = 2; \ return(TRUE); \ } \ }while(0) /***** * Name: _PLC_JPEG_FillInputBuffer * Return Type: boolean (JPEG boolean is int, XmHTML Boolean is a char) * Description: jpeg fill_input_buffer method. We use the Suspended I/O * facility of the jpeg library. * In: * cinfo: JPEG decompresser info. * Returns: * False when decompression of the current decompressor should be suspended * (not enough data or data is being skipped), True when the decompresser * should continue. * Note: * This is the core of the JPEG PLC object. It updates the JPEG input buffer * by making appropriate data requests from the current PLC, inserting a * fake jpeg EOI marker when all data has been read or by skipping bogus * input data if the jpeg skip_input_data method has been called. * The hardest part to figure out for progressive JPEG support was that, if * input is to be suspended, the pub.next_input_byte and pub.bytes_in_buffer * fields *MUST* be set to NULL and 0 respectively. Havoc occurs otherwise. * (this took me two days to figure out...) *****/ static boolean _PLC_JPEG_FillInputBuffer(j_decompress_ptr cinfo) { plc_jpeg_source_mgr *src = (plc_jpeg_source_mgr*)cinfo->src; PLC *plc = src->plc; Byte *dest; int len; size_t status; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FillInputBuffer for %s\n", plc->url)); /***** * If the current input buffer is empty, we need to suspend the current * JPEG stream. If there is any data left in the jpeg stream, it's saved * so it can be restored upon the next invocation of this routine. * * buffer is of type JOCTET, which is an unsigned char (Byte) on all Unix * systems. * next_input_byte points to the next byte that is to be read from * the buffer. Data before this byte can be discarded safely, while * data after this byte must be saved. * bytes_in_buffer indicates how many bytes are left in the current * buffer. *****/ if(plc->left == 0) { _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FillInputBuffer, suspending " "input, left = %i, bytes_in_buffer = %i\n", plc->left, src->pub.bytes_in_buffer)); /***** * Save current state by backtracking in the main PLC input buffer. * This ensures we have things right when a STREAM_RESIZE request * was made and that we stay aligned with the incoming data. *****/ if(src->pub.bytes_in_buffer) { plc->left = src->pub.bytes_in_buffer; plc->next_in = plc->buffer + (plc->size - plc->left); } /* issue a new data request */ plc->min_in = 0; plc->max_in = plc->input_size; (void)_PLCDataRequest(plc); /* check end of data */ TATTUQOLTUAE; /* need to resize jpeg buffer if PLC input buffer size changed */ if(plc->input_size != src->buf_size) { _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FillInputBuffer, resizing " "jpeg input buffer from %i to %i bytes\n", src->buf_size, plc->input_size)); src->buf_size = plc->input_size; src->buffer = (Byte*)realloc(src->buffer, src->buf_size * sizeof(Byte)); } /* suspend decoder */ src->pub.next_input_byte = NULL; src->pub.bytes_in_buffer = 0; return(FALSE); } /* move data left to the beginning of the current jpeg buffer */ if(src->pub.bytes_in_buffer) { Byte *next_inpb; int bib; next_inpb = (Byte*)src->pub.next_input_byte; bib = src->pub.bytes_in_buffer; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FillInputBuffer, backtracking " "%i bytes (current).\n", bib)); /* move data left to the beginning of the buffer */ src->buffer = memmove(src->buffer, src->pub.next_input_byte, src->pub.bytes_in_buffer); src->pub.next_input_byte = src->buffer; } /***** * We have data available. We can do two things here: * 1. skip input data if the jpeg skip_input_data method has been told to * skip more data than was available (in which nskip contains the no of * bytes to be skipped). * 2. copy data from the PLC input buffer to the JPEG input buffer. *****/ /* do we still have to skip input data? */ if(src->nskip) { /* sanity */ src->pub.bytes_in_buffer = 0; src->pub.next_input_byte = NULL; /* maximum no of bytes we may consume */ len = (src->buf_size > plc->left ? plc->left : src->buf_size); /* don't consume more bytes than we may */ if(len > src->nskip) len = src->nskip; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FillInputBuffer, skipping " "%i bytes (out of %i total).\n", len, src->nskip)); dest = src->buffer; /* make a data request. This should *never* fail. */ if((status = _PLCReadOK(plc, dest, len)) == 0) { TATTUQOLTUAE; /* this should *never* happen */ _XmHTMLWarning(__WFUNC__(plc->object->plc_any_image.owner, "_PLC_JPEG_FillInputBuffer"), "Read error " "while skipping jpeg input data: _PLCReadOK failed for %i\n" " bytes. Skipping this image.", len); plc->plc_status = PLC_ABORT; return(FALSE); } /* this amount of data to be skipped upon next call */ src->nskip -= status; /* still skipping data or no more data */ if(src->nskip || plc->left == 0) { _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FillInputBuffer, suspending " "input, nskip: %i, left: %i\n", src->nskip, plc->left)); return(FALSE); } /* no more data to be skipped and input available, fall through */ } /* maximum no of bytes we can consume */ len = src->buf_size - src->pub.bytes_in_buffer; /***** * Maximum no of bytes available from input (prevents ReadOK from issueing * a data request by itself). *****/ if(len > plc->left) len = plc->left; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FillInputBuffer, requesting " "%i bytes.\n", len)); dest = src->buffer + src->pub.bytes_in_buffer; /***** * This one should *never* fail, so it's an error if it does. * Check for STREAM_END anyway. *****/ if((status = _PLCReadOK(plc, dest, len)) == 0) { TATTUQOLTUAE; /* we should *never* get here */ _XmHTMLWarning(__WFUNC__(plc->object->plc_any_image.owner, "_PLC_JPEG_FillInputBuffer"), "Read error while " "filling jpeg input buffer: _PLCReadOK failed for %i\n bytes. " "Skipping this image.", len); /* suspend decoder */ src->pub.next_input_byte = NULL; src->pub.bytes_in_buffer = 0; /* abort this PLC */ plc->plc_status = PLC_ABORT; return(FALSE); } /* update source manager fields */ src->pub.next_input_byte = (JOCTET*)src->buffer; src->pub.bytes_in_buffer += (size_t)status; /* continue processing */ return(TRUE); } /***** * Name: _PLC_JPEG_SkipInputData * Return Type: void * Description: jpeg skip_input_data method. * In: * cinfo: jpeg decompressor info; * nskip: no of bytes to be skipped in the input; * Returns: * nothing. * Note: * Due to the fact that this routine may *not* be suspended, we must save * the amount of data to skip to a public field in the source manager * which will then skip past the amount requested. *****/ static void _PLC_JPEG_SkipInputData(j_decompress_ptr cinfo, long nskip) { plc_jpeg_source_mgr *src = (plc_jpeg_source_mgr*)cinfo->src; #ifdef DEBUG PLC *plc = src->plc; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_SkipInputData for %s, nskip = %li\n", plc->url, nskip)); #endif if(nskip != 0) { if(nskip > src->pub.bytes_in_buffer) { /* this much data needs to be skipped by FillInputBuffer */ src->nskip = (nskip - src->pub.bytes_in_buffer); /* skip input data */ src->pub.bytes_in_buffer = 0; src->pub.next_input_byte = 0; } else { /* skip input data */ src->pub.next_input_byte += (size_t)nskip; src->pub.bytes_in_buffer -= (size_t)nskip; } } } /***** * Name: _PLC_JPEG_TermSource * Return Type: void * Description: jpeg term_source method. * In: * cinfo: JPEG decompressor info; * Returns: * nothing. *****/ static void _PLC_JPEG_TermSource(j_decompress_ptr cinfo) { plc_jpeg_source_mgr *src = (plc_jpeg_source_mgr*)cinfo->src; #ifdef DEBUG PLC *plc = src->plc; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_TermSource for %s\n", plc->url)); #endif if(src->buf_size) free(src->buffer); src->buffer = NULL; src->buf_size = 0; } /***** * Name: _PLC_JPEG_SetSource * Return Type: void * Description: jpeg_stdio_src variant for JPEG PLC. * In: * cinfo: JPEG decompressor info; * plc: current PLC * Returns: * nothing. *****/ static void _PLC_JPEG_SetSource(j_decompress_ptr cinfo, PLC *plc) { plc_jpeg_source_mgr *src; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_SetSource for %s\n", plc->url)); if(cinfo->src == NULL) { /* first time for this JPEG object */ cinfo->src=(struct jpeg_source_mgr*) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(plc_jpeg_source_mgr)); } src = (plc_jpeg_source_mgr*)cinfo->src; src->plc = plc; src->buffer = NULL; src->buf_size = 0; src->nskip = 0; src->pub.init_source = _PLC_JPEG_InitSource; src->pub.fill_input_buffer = _PLC_JPEG_FillInputBuffer; src->pub.skip_input_data = _PLC_JPEG_SkipInputData; src->pub.resync_to_restart = jpeg_resync_to_restart; /* default */ src->pub.term_source = _PLC_JPEG_TermSource; src->pub.bytes_in_buffer = 0; src->pub.next_input_byte = NULL; } /***** * Name: ReadJPEGColormap * Return Type: void * Description: reads the colormap allocated for a JPEG image. * In: * jpeg: PLC image object data; * cinfo: current decompressor data. * Returns: * Nothing, but the cmap and cmapsize fields of the PLC object are updated. * An existing colormap is replaced. *****/ static void ReadJPEGColormap(PLCImageJPEG *jpeg, struct jpeg_decompress_struct *cinfo) { register int i; _XmHTMLDebug(15, ("plc.c: ReadJPEGColormap\n")); /* free previous colormap */ if(jpeg->cmap) { free(jpeg->cmap); jpeg->cmap = NULL; } /* allocate new colormap */ jpeg->cmapsize = cinfo->actual_number_of_colors; jpeg->cmap = (TColor*)calloc(jpeg->cmapsize, sizeof(TColor)); /* fill colormap. Upscale RGB to 16bits precision */ if(cinfo->out_color_components == 3) { int cshift = 16 - cinfo->data_precision; jpeg->colorclass = XmIMAGE_COLORSPACE_RGB; for (i=0; i < jpeg->cmapsize; i++) { jpeg->cmap[i].red = cinfo->colormap[0][i] << cshift; jpeg->cmap[i].green = cinfo->colormap[1][i] << cshift; jpeg->cmap[i].blue = cinfo->colormap[2][i] << cshift; jpeg->cmap[i].pixel = (Pixel)i; #ifdef WITH_MOTIF jpeg->cmap[i].flags = DoRed|DoGreen|DoBlue; #endif } } else { int cshift = 16 - cinfo->data_precision; jpeg->colorclass = XmIMAGE_COLORSPACE_GRAYSCALE; for(i = 0; i < jpeg->cmapsize; i++) { jpeg->cmap[i].red = jpeg->cmap[i].green = jpeg->cmap[i].blue = cinfo->colormap[0][i] << cshift; jpeg->cmap[i].pixel = (Pixel)i; #ifdef WITH_MOTIF jpeg->cmap[i].flags = DoRed|DoGreen|DoBlue; #endif } } /***** * as we are now sure every color values lies within the range 0-2^16, * we can downscale to the range 0-255 *****/ for(i = 0; i < jpeg->cmapsize; i++) { jpeg->cmap[i].red >>= 8; jpeg->cmap[i].green >>= 8; jpeg->cmap[i].blue >>= 8; } /* get image depth */ jpeg->depth = 1; while(jpeg->cmapsize > (1 << jpeg->depth)) jpeg->depth++; } /***** * Name: _PLC_JPEG_Init * Return Type: void * Description: JPEG PLC initialization routine. Allocates data structures, * reads initial JPEG info. * In: * plc: current PLC * Returns: * nothing. *****/ void _PLC_JPEG_Init(PLC *plc) { struct jpeg_decompress_struct *cinfo; plc_jpeg_err_mgr *jerr; PLCImageJPEG *jpeg; XmHTMLWidget html; int i = 0; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_Init for %s\n", plc->url)); /* this plc is active */ plc->plc_status = PLC_ACTIVE; plc->max_in = PLC_MAX_BUFFER_SIZE; /* ptrs to various objects and substructures */ jpeg = &(plc->object->plc_jpeg_image); cinfo = &(jpeg->cinfo); jerr = &(jpeg->jerr); html = jpeg->owner; /***** * If the jpeg decompressor hasn't been allocated yet, do it now. * This test may fail if we have been suspended while reading the JPEG * header. * Note that we only rewind the input buffer only *once* as the jpeg * decompressor keeps its own internal state when I/O suspension occurs * in any of libjpeg's internal routines. Rewinding the input buffer * every time would disrupt the decompressor. *****/ if(jpeg->init == False) { /* we might have already processed some incoming data, reset */ _PLCRewindInputBuffer(plc); cinfo->err = (struct jpeg_error_mgr *)jpeg_std_error(&(jerr->pub)); jerr->pub.error_exit = _PLC_JPEG_ErrorExit; jpeg_create_decompress(cinfo); _PLC_JPEG_SetSource(cinfo, plc); jpeg->init = True; } /***** * exit override must be done for each invocation of this routine, * set_jmp saves the current environment, which differs each time this * routine is called. *****/ if(setjmp(jerr->setjmp_buffer)) { /***** * JPEG signalled an error, abort this PLC. * Necessary cleanup will be done by _PLC_JPEG_Destructor. *****/ _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_Init: end, libjpeg internal " "error.\n")); /* abort this PLC */ plc->plc_status = PLC_ABORT; return; } /***** * Read the header. We only want to have images (e.i. the TRUE). * libjpeg will backtrack to a suitable place in the input stream * when I/O suspension occurs in this call. *****/ if((i = jpeg_read_header(cinfo, TRUE)) != JPEG_HEADER_OK) { /* an error of some kind, abort this PLC */ if(i != JPEG_SUSPENDED) plc->plc_status = PLC_ABORT; /* no more data, aborted or suspended */ return; } /* we know this is a JPEG image */ jpeg->info->type = IMAGE_JPEG; /* jpeg images are always fully opaque */ jpeg->transparency = XmHTML_NONE; jpeg->bg_pixel = -1; /***** * Set jpeg options *****/ /***** * We want to have buffered output if we want to be able to display * the data read so far. *****/ cinfo->buffered_image = TRUE; /***** * We only want colormapped output, and we only do single-pass color * quantization for all but the last passes (two pass quantization requires * the entire image data to be present, making progressive loading of JPEG * images useless). A final color optimization pass can be performed when * the XmNprogressivePerfectColor resource has been set to True, so if we * want to be able to allow this we must make preparations for this (hence * the enable_ settings). *****/ cinfo->quantize_colors = TRUE; cinfo->enable_1pass_quant = TRUE; cinfo->enable_2pass_quant = TRUE; cinfo->two_pass_quantize = FALSE; cinfo->dither_mode = JDITHER_ORDERED; cinfo->colormap = NULL; cinfo->output_gamma = html->html.screen_gamma; cinfo->desired_number_of_colors = html->html.max_image_colors; /***** * Now initialize the decompressor using the options set above. * This routine will never suspend in buffered image mode. *****/ jpeg_start_decompress(cinfo); /* check colorspace. We only support RGB and GRAYSCALE */ if(cinfo->out_color_space != JCS_RGB && cinfo->out_color_space != JCS_GRAYSCALE) { J_COLOR_SPACE j_cs = cinfo->out_color_space; _XmHTMLWarning(__WFUNC__(html, "_PLC_JPEG_Init"), "Unsupported JPEG colorspace %s on image\n %s. Skipping.", (j_cs == JCS_UNKNOWN ? "unspecified" : (j_cs == JCS_YCbCr ? "YCbCr (also known as YUV)" : (j_cs == JCS_CMYK ? "CMYK" : "YCCK"))), plc->url); /* to bad, abort it */ plc->plc_status = PLC_ABORT; return; } /***** * Image colorclass and colormap. As the RGB image data will be colormapped * (pixelized that is), XmHTML classifies this image as being indexed. *****/ jpeg->ncolors = cinfo->desired_number_of_colors; /* image dimensions */ jpeg->width = cinfo->output_width; jpeg->height = cinfo->output_height; /***** * Decoded data buffer and counters. *****/ /* input buffer data stride; output_comp should always be 1 */ jpeg->stride = cinfo->output_width * cinfo->output_components; jpeg->data_pos = 0; /* current pos in data received so far */ jpeg->prev_pos = 0; /* size of data received so far */ jpeg->data_size = jpeg->stride*jpeg->height; jpeg->data = (Byte*)calloc(jpeg->data_size, sizeof(Byte)); /* object has been initialized */ plc->obj_funcs[1] = (PLCProc)_PLC_JPEG_FinalPass; plc->initialized = True; plc->curr_obj_func = 0; /* move to scanline reader */ } /***** * Name: _PLC_JPEG_ScanlineProc * Return Type: void * Description: JPEG scanline processor * In: * plc: current PLC * Returns: * nothing. *****/ void _PLC_JPEG_ScanlineProc(PLC *plc) { struct jpeg_decompress_struct *cinfo; plc_jpeg_err_mgr *jerr; PLCImageJPEG *jpeg = &(plc->object->plc_jpeg_image); Byte *r; JSAMPROW buffer[1]; /* row pointer array for read_scanlines */ #ifdef DEBUG int lines_processed; #endif _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_ScanlineProc for %s\n", plc->url)); cinfo = &(jpeg->cinfo); jerr = &(jpeg->jerr); /***** * exit override must be done for each invocation of this routine, * set_jmp saves the current environment, which differs each time this * routine is called. *****/ if(setjmp(jerr->setjmp_buffer)) { /***** * JPEG signalled an error, abort this PLC. * Necessary cleanup will be done by _PLC_JPEG_Destructor. *****/ _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_ScanlineProc: end, libjpeg " "internal error.\n")); /* abort this PLC */ plc->plc_status = PLC_ABORT; return; } /* a new pass */ if(cinfo->input_scan_number != cinfo->output_scan_number) { _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_ScanlineProc, initializing scan " "%i\n", cinfo->input_scan_number)); cinfo->do_block_smoothing = TRUE; /* rewind output buffer */ jpeg->prev_pos = 0; jpeg->data_pos = 0; /* will never suspend as we are only using one-pass quantization */ jpeg_start_output(cinfo, cinfo->input_scan_number); /***** * read colormap only if this is the first pass. As we are using * dithered ordering, it will be the same for each pass. The final * colormap will be read by _PLC_JPEG_FinalPass. *****/ if(cinfo->input_scan_number == 1) ReadJPEGColormap(jpeg, cinfo); } #ifdef DEBUG else _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_ScanlineProc, doing scan %i.\n", cinfo->output_scan_number)); lines_processed = cinfo->output_scanline; #endif r = jpeg->data + jpeg->data_pos; /* keep processing scanlines as long as we have data available */ while(cinfo->output_scanline < cinfo->output_height) { buffer[0] = r; /***** * Read a new scanline and break if jpeg_read_scanlines couldn't read * anymore scanlines (input suspended) or when the PLC gets * de-activated. *****/ if((jpeg_read_scanlines(cinfo, buffer, 1)) == 0) { _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_ScanlineProc, input " "suspended.\n")); break; } /* next data slot */ r += jpeg->stride; } _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_ScanlineProc, processed scanline %i " "to %i\n", lines_processed, cinfo->output_scanline)); /***** * Note: since we have set the buffered_image flag, data_pos will probably * become equal to data_size after the first scan or so. ****/ jpeg->data_pos = cinfo->output_scanline*jpeg->stride; /* pass completed */ if(cinfo->output_scanline == cinfo->output_height) { _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_ScanlineProc, scan %i done.\n", cinfo->output_scan_number)); /* skip to next scan */ if((jpeg_finish_output(cinfo)) == FALSE) return; /* no more data, suspended or aborted, return */ } /* and this PLC has finished when no more input is available */ if(jpeg_input_complete(cinfo) && cinfo->input_scan_number == cinfo->output_scan_number) { XmHTMLWidget html = jpeg->owner; /* only move to final dithering pass if requested */ switch(html->html.perfect_colors) { case XmAUTOMATIC: /***** * We decide by ourselves: if the image has less than 1/3 of * the requested image colors, we dither, else we don't. *****/ if(3*jpeg->nused-1 < jpeg->cmapsize) { plc->curr_obj_func = 1; break; } plc->plc_status = PLC_COMPLETE; plc->obj_funcs_complete = True; break; case XmALWAYS: /* always dither */ plc->curr_obj_func = 1; break; case XmNEVER: /* no dithering wanted at all */ default: plc->plc_status = PLC_COMPLETE; plc->obj_funcs_complete = True; break; } _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_ScanlineProc, image %s read.\n", plc->url)); } } /***** * Name: _PLC_JPEG_FinalPass * Return Type: void * Description: performs last pass on the decoded image data to do proper * color quantization. * In: * plc: current PLC * Returns: * nothing. * Note: * This routine is only used when the XmNprogressivePerfectColor resource * has been set to True (it's a very time consuming operation and causes * a colorflash while the results aren't always good. It is False by default). *****/ static void _PLC_JPEG_FinalPass(PLC *plc) { struct jpeg_decompress_struct *cinfo; plc_jpeg_err_mgr *jerr; PLCImageJPEG *jpeg = &(plc->object->plc_jpeg_image); Byte *r; JSAMPROW buffer[1]; /* row pointer array for read_scanlines */ XmHTMLWidget html; XmHTMLImage *image; XmImageInfo *info; int i; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FinalPass for %s\n", plc->url)); cinfo = &(jpeg->cinfo); jerr = &(jpeg->jerr); html = jpeg->owner; image = jpeg->image; info = jpeg->info; /***** * exit override must be done for each invocation of this routine, * set_jmp saves the current environment, which differs each time this * routine is called. *****/ if(setjmp(jerr->setjmp_buffer)) { /***** * JPEG signalled an error, abort this PLC. * Necessary cleanup will be done by _PLC_JPEG_Destructor. *****/ _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FinalPass : end, libjpeg " "internal error.\n")); /* abort this PLC */ plc->plc_status = PLC_ABORT; return; } /***** * The decoded image is by now completely decoded and has been buffered * in the current decompressor. Set proper color quantization params * and initialize the final pass. *****/ cinfo->quantize_colors = TRUE; cinfo->two_pass_quantize = TRUE; cinfo->dither_mode = JDITHER_FS; cinfo->colormap = NULL; cinfo->desired_number_of_colors = html->html.max_image_colors; /* sanity */ if(image->npixels == 0) { _XmHTMLWarning(__WFUNC__(html, "_PLC_JPEG_FinalPass"), "PLC Internal Error: no colors found in final JPEG decoding pass."); plc->plc_status = PLC_ABORT; return; } /***** * Will never suspend as the image is already totally decoded, so it's * an error if it *does* suspend! *****/ if((jpeg_start_output(cinfo, cinfo->input_scan_number)) == FALSE) { _XmHTMLWarning(__WFUNC__(html, "_PLC_JPEG_FinalPass"), "JPEG Error: I/O suspension while initializing final decoding " "pass.\n Final pass not performed."); /* PLC completed. */ plc->plc_status = PLC_COMPLETE; plc->obj_funcs_complete = True; return; } /* rewind output buffer */ jpeg->prev_pos = 0; jpeg->data_pos = 0; /* keep processing scanlines as long as we have data available */ r = jpeg->data; while(cinfo->output_scanline < cinfo->output_height) { buffer[0] = r; if((jpeg_read_scanlines(cinfo, buffer, 1)) == 0) { _XmHTMLWarning(__WFUNC__(html, "_PLC_JPEG_FinalPass"), "Aaaaie! JPEG I/O suspension in final decoding pass!"); plc->plc_status = PLC_ABORT; return; } r += jpeg->stride; } /* finish this final pass */ if((jpeg_finish_output(cinfo)) == FALSE) { /* this is *very* unlikely to happen */ _XmHTMLWarning(__WFUNC__(html, "_PLC_JPEG_FinalPass"), "Aaaaie! JPEG I/O suspension in final output pass! Aborting " "image load..."); plc->plc_status = PLC_COMPLETE; return; } /* This will equal jpeg->data_size; it's the final image data. */ jpeg->data_pos = cinfo->output_scanline*jpeg->stride; /* get rid of current XmImageInfo RGB arrays */ if(info->reds) free(info->reds); info->reds = info->greens = info->blues = (Dimension*)NULL; /* now reset used colors array */ for(i = 0; i < MAX_IMAGE_COLORS; i++) { jpeg->used[i] = 0; jpeg->xcolors[i] = 0L; } jpeg->nused = 1; /* read new colormap */ ReadJPEGColormap(jpeg, cinfo); /* allocate new RGB arrays */ info->reds = (Dimension*)calloc(3*jpeg->cmapsize, sizeof(Dimension)); info->greens = info->reds + jpeg->cmapsize; info->blues = info->greens + jpeg->cmapsize; /* pass completed */ _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_FinalPass, done reading image %s.\n", plc->url)); plc->obj_funcs_complete = True; } /***** * Name: _PLC_JPEG_Destructor * Return Type: void * Description: JPEG PLC virtual destructor method. * In: * plc: current PLC * Returns: * nothing. *****/ void _PLC_JPEG_Destructor(PLC *plc) { struct jpeg_decompress_struct *cinfo; plc_jpeg_source_mgr *src; PLCImageJPEG *jpeg; XmHTMLImage *image; _XmHTMLDebug(15, ("plc.c: _PLC_JPEG_Destructor for %s\n", plc->url)); jpeg = &(plc->object->plc_jpeg_image); cinfo = &(jpeg->cinfo); src = (plc_jpeg_source_mgr*)cinfo->src; image = jpeg->image; /* wipe out source manager buffers if not already done */ if(src->buf_size) free(src->buffer); src->buffer = NULL; src->buf_size = 0; /* and destroy the decompressor */ jpeg_destroy_decompress(cinfo); plc->plc_status = PLC_COMPLETE; } #else /* !HAVE_LIBJPEG */ /***** * Dummy JPEG PLC support functions. Only _PLC_JPEG_Init will be called by the * main PLC cycler. The other two are here to prevent linker errors. *****/ void _PLC_JPEG_Init(PLC *plc) { plc->plc_status = PLC_ABORT; } void _PLC_JPEG_ScanlineProc(PLC *plc) { plc->plc_status = PLC_ABORT; } void _PLC_JPEG_Destructor(PLC *plc) { plc->plc_status = PLC_ABORT; } #endif /* HAVE_LIBJPEG */