Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1619 lines
48 KiB

/*
Enhanced NCSA Mosaic from Spyglass
"Guitar"
Copyright 1994 Spyglass, Inc.
All Rights Reserved
Author(s):
Eric W. Sink [email protected]
Scott Piette [email protected]
Jim Seidman [email protected]
Paul Rohr [email protected]
*/
#include "all.h"
/* ****************************************************************** */
/* External Prototypes */
int HT_CreateDeviceImageMap(struct Mwin *tw, struct ImageInfo *pImg);
BOOL W3Doc_CheckForImageLoadElement (struct _www *w3doc, int element);
void GTR_DrawProgessiveImage (struct Mwin* tw, int ndx);
/* ****************************************************************** */
#ifdef FEATURE_JPEG
/* Stream Object
** ------------
*/
#define BLOCK_SIZE 32768
struct _HTStream
{
CONST HTStreamClass *isa;
HTRequest *request;
struct Mwin *tw;
/* state info for "push"-model decompressor */
struct buf_in *src;
struct _JPEGinfo *jpg;
int state;
/* platform-specific stuff */
#ifdef UNIX
XColor *colors;
#endif
};
/* Image streams
*/
PRIVATE void HTJPEG_free(HTStream * me);
PRIVATE void HTJPEG_abort(HTStream * me, HTError e);
PRIVATE BOOL HTJPEG_put_character(HTStream * me, char c);
PRIVATE BOOL HTJPEG_put_string(HTStream * me, CONST char *s);
PRIVATE BOOL HTJPEG_write(HTStream * me, CONST char *s, int l);
struct ImageInfo *JPG_DoSetImage (HTStream *me);
#ifdef MAC
PRIVATE CONST HTStreamClass HTJPEGClass =
{
"JPEG",
NULL,
NULL,
NULL,
HTJPEG_free,
HTJPEG_abort,
HTJPEG_put_character, HTJPEG_put_string,
HTJPEG_write
};
void HTJPEG_InitStaticStrings(void);
void HTJPEG_InitStaticStrings(void)
{
HTJPEGClass.szStatusNoLength = GTR_GetString(HTJPEG_RECEIVING_INLINE_S);
HTJPEGClass.szStatusWithLength = GTR_GetString(HTJPEG_RECEIVING_INLINE_S_S);
}
#else
PRIVATE CONST HTStreamClass HTJPEGClass =
{
"JPEG",
SID_HTJPEG_RECEIVING_INLINE_S,
SID_HTJPEG_RECEIVING_INLINE_S_S,
NULL,
HTJPEG_free,
HTJPEG_abort,
HTJPEG_put_character, HTJPEG_put_string,
HTJPEG_write
};
#endif
/***************************************************************************
"push"-model JPEG decoder (used to be JPEG.C)
***************************************************************************/
#ifdef UNIX
#include "gui/x_dither.h"
#endif /* UNIX */
#include <setjmp.h>
/******************** JPEG DECOMPRESSION SAMPLE INTERFACE *******************/
/* This half of the example shows how to read data from the JPEG decompressor.
* It's a bit more refined than the above, in that we show:
* (a) how to modify the JPEG library's standard error-reporting behavior;
* (b) how to allocate workspace using the library's memory manager.
*
* Just to make this example a little different from the first one, we'll
* assume that we do not intend to put the whole image into an in-memory
* buffer, but to send it line-by-line someplace else. We need a one-
* scanline-high JSAMPLE array as a work buffer, and we will let the JPEG
* memory manager allocate it for us. This approach is actually quite useful
* because we don't need to remember to deallocate the buffer separately: it
* will go away automatically when the JPEG object is cleaned up.
*/
/*
We only define the following symbol so we can get the definitions of
RGB_PIXELSIZE, RGB_RED, RGB_GREEN, and RGB_BLUE
*/
#define JPEG_INTERNALS
#include "jpeglib.h"
/*
* ERROR HANDLING:
*
* The JPEG library's standard error handler (jerror.c) is divided into
* several "methods" which you can override individually. This lets you
* adjust the behavior without duplicating a lot of code, which you might
* have to update with each future release.
*
* Our example here shows how to override the "error_exit" method so that
* control is returned to the library's caller when a fatal error occurs,
* rather than calling exit() as the standard error_exit method does.
*
* We use C's setjmp/longjmp facility to return control. This means that the
* routine which calls the JPEG library must first execute a setjmp() call to
* establish the return point. We want the replacement error_exit to do a
* longjmp(). But we need to make the setjmp buffer accessible to the
* error_exit routine. To do this, we make a private extension of the
* standard JPEG error handler object. (If we were using C++, we'd say we
* were making a subclass of the regular error handler.)
*
* Here's the extended error handler struct:
*/
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;
#ifdef WIN32
int x_MapGraysToGlobalPalette[6] = {
0*GREEN_COLOR_LEVELS*BLUE_COLOR_LEVELS + 0*BLUE_COLOR_LEVELS + 0,
1*GREEN_COLOR_LEVELS*BLUE_COLOR_LEVELS + 1*BLUE_COLOR_LEVELS + 1,
2*GREEN_COLOR_LEVELS*BLUE_COLOR_LEVELS + 2*BLUE_COLOR_LEVELS + 2,
3*GREEN_COLOR_LEVELS*BLUE_COLOR_LEVELS + 3*BLUE_COLOR_LEVELS + 3,
4*GREEN_COLOR_LEVELS*BLUE_COLOR_LEVELS + 4*BLUE_COLOR_LEVELS + 4,
5*GREEN_COLOR_LEVELS*BLUE_COLOR_LEVELS + 5*BLUE_COLOR_LEVELS + 5
};
int x_MapGraysToVGAPalette[3] = {
0,
7,
15
};
#endif /* WIN32 */
/*
* Here's the routine that will replace the standard error_exit method:
*/
METHODDEF void
my_error_exit (j_common_ptr cinfo)
{
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
my_error_ptr myerr = (my_error_ptr) cinfo->err;
/* Always display the message. */
/* We could postpone this until after returning, if we chose. */
(*cinfo->err->output_message) (cinfo);
/* Return control to the setjmp point */
longjmp(myerr->setjmp_buffer, 1);
}
/*************************************************************************
structs
*************************************************************************/
/* state info for push-model JPEG decoder */
struct _JPEGinfo
{
/* libjpeg stuff */
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr; /* private extension JPEG error handler */
JSAMPARRAY buffer; /* output row work buffer */
BOOL bOldBufferLogic;
BOOL bForceFlush; /* forces source manager to be flushed */
struct ImageInfo *pIInfo;
BOOL bGotHeader; /* true, AFTER JPG_write has extracted header */
/* image characteristics */
int dither_type;
long width, height; /* of image, in pixels */
long rowbytes; /* width of image row (in bytes) */
unsigned char *image; /* the decompressed image */
long decoded_pass; /* image decoded as of this pass */
long decoded_ypos; /* image filled through this row */
BOOL bFirstPass; /* is this the first pass? */
/* async state info */
int state;
#ifdef FEATURE_MULTISCAN_JPEG
int state_multi; /* extra state info for JPEG_MultiScanImage */
#endif /* FEATURE_MULTISCAN_JPEG */
long xpos, ypos; /* for current pixel */
/* applies iff multi-scan file */
long pass; /* in JPEG lingo, a "scan" */
/* platform-specific stuff */
#ifdef UNIX
XColor * colrs; /* colormap */
int ncolors;
BOOL bGray;
#endif
#ifdef MAC
GWorldPtr gw;
#endif /* MAC */
};
/*************************************************************************
start of single JPEG reader
*************************************************************************/
#ifdef WIN32
extern DWORD vga_colors[16]; /* bitmaps.c */
#endif /* WIN32 */
extern enum {DITHER_CUBE, DITHER_VGA, DITHER_NONE} dither_enum;
/* TODO: (John) is this difference still necessary? */
#ifndef MAC
#define JPEG_OUTPUT_ROWS 8 /* max rows output by jpeg_read_scanlines */
#else
#define JPEG_OUTPUT_ROWS 1 /* max rows output by jpeg_read_scanlines */
#endif /* !MAC */
static int DecodeJPEG (unsigned char *data, long len, struct _JPEGinfo *jpg);
static BOOL JPEG_ScanImage(struct _JPEGinfo *jpg);
#ifdef FEATURE_MULTISCAN_JPEG
static BOOL JPEG_MultiScanImage(struct _JPEGinfo *jpg);
#endif /* FEATURE_MULTISCAN_JPEG */
static BOOL
JPEG_InitDithering (
j_decompress_ptr cinfo,
int dither_type
#ifdef UNIX
, XColor *colrs,
int ncolors
#endif
);
#ifndef MAC
static BOOL JPEG_AllocImage(unsigned char **image, long w, long h, long *rowbytes, int dither_type);
#else
static BOOL JPEG_AllocImage(GWorldPtr *gw, long w, long h, long *rowbytes, BOOL bIsRGB);
#endif /* !MAC */
/*
* Sample routine for JPEG decompression. We assume that the JPEG file image
* is passed in. We want to return a pointer on success, NULL on error.
*/
/*
ReadJPEG: convert entire JPEG into properly-oriented image, colors, & info
*/
#ifdef WIN32
unsigned char *
ReadJPEG ( unsigned char *data, long len, long *width, long *height,
int dither_type )
#endif /* WIN32 */
#ifdef UNIX
unsigned char *
ReadJPEG ( unsigned char *data, long len, long *width, long *height,
int dither_type, XColor *colrs, unsigned char *gray )
#endif /* UNIX */
#ifdef MAC
GWorldPtr
ReadJPEG ( Handle hData, long len, long *width, long *height,
int dither_type )
#endif /* MAC */
{
struct _JPEGinfo *jpg;
int state;
#ifdef MAC
unsigned char *data;
#endif
/*
IDEA: for the moment, hang onto this API &
process the entire image as currently.
*/
/* alloc, initialize state variables */
jpg = (struct _JPEGinfo *)GTR_CALLOC(1, sizeof(struct _JPEGinfo));
if (!jpg)
{
ERR_ReportError(NULL, SID_ERR_COULD_NOT_LOAD_IMAGE, NULL, NULL);
GTR_FREE(jpg);
#ifndef MAC
return (NULL);
#else
return 0;
#endif /* !MAC */
}
#ifdef MAC
HLock(hData);
data = *hData;
#endif /* MAC */
jpg->dither_type = dither_type;
#ifdef UNIX
jpg->colrs = dither_info.colors;
jpg->ncolors = dither_info.ncolors;
#endif /* UNIX */
jpg->bOldBufferLogic = TRUE; /* HACK: consume entire file at once */
jpg->bForceFlush = TRUE; /* NOTE: actually not used in this mode */
jpg->state = STATE_INIT;
/*
TODO: do we care about the suspension state (if any) here?
HYP: probably not. depends on whether it's all handled already.
*/
state = DecodeJPEG(data, len, jpg);
*width = jpg->width;
*height = jpg->height;
#ifdef UNIX
*colrs = *jpg->colrs;
*gray = (jpg->cinfo.out_color_space == JCS_GRAYSCALE);
jpg->bGray = *gray;
#endif /* UNIX */
#ifndef MAC
return jpg->image;
#else
HUnlock(hData);
return jpg->gw;
#endif /* !MAC */
}
#define STATE_INIT_LIBJPEG (STATE_OTHER)
#define STATE_READ_HEADER (STATE_OTHER + 1)
#define STATE_START_DECOMPRESS (STATE_OTHER + 2)
#define STATE_IMAGE_CREATE (STATE_OTHER + 3)
#define STATE_IMAGE_DATA (STATE_OTHER + 4)
#define STATE_IMAGE_DONE (STATE_OTHER + 5)
/*
DecodeJPEG: "push"-model version of ReadJPEG
-------------------------------------------------------------------
consumes ENTIRE input buffer, processes that buffer as far as it
can, and then suspends.
each state has the following logic:
1. try to consume a given amount of input
2. if not enough, buffer it and suspend (restart w/same state)
3. otherwise, process entire buffer and go to next state
on restart, we jump to the same state we left and keep adding to
whatever we already have buffered.
*/
static int DecodeJPEG (unsigned char *data, long len, struct _JPEGinfo *jpg)
{
int row_stride; /* physical row width in LIBJPEG's output buffer */
/*
NOTE: this state has to come BEFORE the libjpeg setjmp
*/
if (jpg->state == STATE_INIT)
{
jpg->image = NULL;
#ifdef MAC
jpg->gw = NULL;
#endif /* MAC */
/* Step 1: allocate and initialize JPEG decompression object */
/* We set up the normal JPEG error routines, then override error_exit. */
jpg->cinfo.err = jpeg_std_error(&jpg->jerr.pub);
jpg->jerr.pub.error_exit = my_error_exit;
jpg->state = STATE_INIT_LIBJPEG;
/* special case: simple initialization */
if (len == 0)
return jpg->state; /* suspend and try again */
/* fall through */
}
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp(jpg->jerr.setjmp_buffer))
{
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, close the input file, and return.
*/
jpeg_destroy_decompress(&jpg->cinfo);
#ifndef MAC
if (jpg->image)
{
GTR_FREE(jpg->image);
jpg->image = NULL;
}
#else
if (jpg->gw)
{
GTR_FREEGWORLD(jpg->gw);
jpg->gw = 0;
}
#endif /* !MAC */
return STATE_ABORT;
}
/*
NOTE: this state has to come BETWEEN the libjpeg setjmp and the pushbytes call
*/
if (jpg->state == STATE_INIT_LIBJPEG)
{
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(&jpg->cinfo);
/* Step 2: specify data source (eg, a file, or a memory buffer) */
if (jpg->bOldBufferLogic)
jpeg_memory_src(&jpg->cinfo, data, len);
else
jpeg_network_src(&jpg->cinfo, INPUT_BUFFER_SIZE); /* from reformat.h */
jpg->state = STATE_READ_HEADER;
/* fall through */
}
if (!jpg->bOldBufferLogic)
{
/* push bytes into source manager */
jpeg_network_pushbytes(&jpg->cinfo, data, len, jpg->bForceFlush);
}
/*
NOTE: entire post-INIT state machine starts here (ie, after setjmp)
so that context is set appropriately for each invocation.
*/
switch (jpg->state)
{
case STATE_READ_HEADER:
/* Step 3: read file parameters with jpeg_read_header() */
if (jpeg_read_header(&jpg->cinfo, TRUE) == JPEG_SUSPENDED)
break; /* suspend and try again */
/* Step 4: set parameters for decompression */
jpg->cinfo.dct_method = JDCT_IFAST;
{
short bOK;
#ifdef UNIX
bOK = JPEG_InitDithering(&jpg->cinfo, jpg->dither_type,
jpg->colrs, jpg->ncolors);
#else
bOK = JPEG_InitDithering(&jpg->cinfo, jpg->dither_type);
#endif /* UNIX */
if (!bOK)
{
jpg->state = STATE_ABORT;
break;
}
}
/* select buffered-image mode for multi-scan files */
#ifdef FEATURE_MULTISCAN_JPEG
if (jpeg_has_multiple_scans(&jpg->cinfo))
{
jpg->cinfo.buffered_image = TRUE;
jpg->state_multi = STATE_INIT;
}
#endif /* FEATURE_MULTISCAN_JPEG */
jpg->state = STATE_START_DECOMPRESS;
/* fall through */
case STATE_START_DECOMPRESS:
/* Step 5: Start decompressor */
if (!jpeg_start_decompress(&jpg->cinfo))
break; /* suspend and try again */
/* We may need to do some setup of our own at this point before reading
* the data. After jpeg_start_decompress() we have the correct scaled
* output image dimensions available, as well as the output colormap
* if we asked for color quantization.
*/
jpg->width = jpg->cinfo.output_width;
jpg->height = jpg->cinfo.output_height;
#ifdef UNIX
jpg->bGray = (jpg->cinfo.out_color_space == JCS_GRAYSCALE);
#endif
jpg->state = STATE_IMAGE_CREATE;
/* fall through */
case STATE_IMAGE_CREATE:
/*
* In this example, we need to make an output work buffer of the right size.
*/
#ifndef MAC
if (!JPEG_AllocImage(&jpg->image, jpg->width, jpg->height, &jpg->rowbytes,
jpg->dither_type))
#else
if (!JPEG_AllocImage(&jpg->gw, jpg->width, jpg->height, &jpg->rowbytes,
(jpg->cinfo.out_color_space == JCS_RGB)))
#endif /* !MAC */
{
jpg->state = STATE_ABORT;
break;
}
/* JSAMPLEs per row in output buffer */
row_stride = jpg->cinfo.output_width * jpg->cinfo.output_components;
/* Make a sample array that will go away when done with image */
jpg->buffer = (*jpg->cinfo.mem->alloc_sarray)
((j_common_ptr) &jpg->cinfo, JPOOL_IMAGE, row_stride, JPEG_OUTPUT_ROWS);
/* initialize loops */
jpg->pass = 0;
jpg->xpos = jpg->ypos = 0;
jpg->decoded_pass = 0;
jpg->decoded_ypos = 0;
jpg->bFirstPass = TRUE;
jpg->state = STATE_IMAGE_DATA;
/* fall through */
case STATE_IMAGE_DATA:
/* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
#ifdef FEATURE_MULTISCAN_JPEG
if (jpeg_has_multiple_scans(&jpg->cinfo))
{
if (!JPEG_MultiScanImage(jpg))
break; /* suspend and try again */
}
else
{
#endif /* FEATURE_MULTISCAN_JPEG */
if (!JPEG_ScanImage(jpg))
break; /* suspend and try again */
#ifdef FEATURE_MULTISCAN_JPEG
}
#endif /* FEATURE_MULTISCAN_JPEG */
/* HACK: make sure that last line gets progressively painted */
//jpg->decoded_ypos = jpg->height;
//jpg->state = STATE_IMAGE_DONE;
break;
/* fall through */
case STATE_IMAGE_DONE:
/* Step 7: Finish decompression */
if (!jpeg_finish_decompress(&jpg->cinfo))
break; /* suspend and try again */
jpg->state = STATE_DONE;
break;
case STATE_DONE:
case STATE_ABORT:
default:
/* TODO: error, should not have gotten here??
avoid repeated attempts to clean up decompression objects */
break;
}
if ((jpg->state == STATE_DONE) ||
(jpg->state == STATE_ABORT))
{
#ifdef _DEBUG
{
char sz[256];
ThreadID tid = Async_GetCurrentThread();
wsprintf(sz, "tid = %lx, ptr = %lx\n", tid, jpg->cinfo);
OutputDebugString(sz);
}
#endif // _DEBUG
/* Step 8: Release JPEG decompression object */
/* This is an important step since it will release a good deal of memory. */
jpeg_destroy_decompress(&jpg->cinfo);
/* After finish_decompress, we can close the input file.
* Here we postpone it until after no more JPEG errors are possible,
* so as to simplify the setjmp error logic above. (Actually, I don't
* think that jpeg_destroy can do an error exit, but why assume anything...)
*/
/* At this point you may want to check to see whether any corrupt-data
* warnings occurred (test whether jpg->jerr.pub.num_warnings is nonzero).
*/
}
#ifdef XX_DEBUG
else
{
XX_Assert((jpg->bForceFlush==FALSE),("DecodeJPEG: Caller wants bForceFlush, but state %s doesn't clean up.", jpg->state));
}
#endif
return jpg->state;
}
/*
JPEG_ScanImage: process a single image scan out of a JPEG file
*/
static BOOL JPEG_ScanImage(struct _JPEGinfo *jpg)
{
int num_rows_read; /* # of such rows output by jpeg_read_scanlines */
int irow; /* iterator over these rows */
long yRow;
unsigned char *pCurRow;
#ifdef MAC
PixMapHandle thePix = GetGWorldPixMap(jpg->gw);
LockPixels(thePix);
jpg->image = (*thePix)->baseAddr;
#endif /* MAC */
while (jpg->ypos < jpg->height)
{
/* have the library decode several full rows into jpg->buffer */
num_rows_read = jpeg_read_scanlines(&jpg->cinfo, jpg->buffer, JPEG_OUTPUT_ROWS);
if (num_rows_read == 0)
return FALSE; /* suspend and try again */
/* add those rows to platform-specific image */
if (jpg->cinfo.out_color_space == JCS_RGB)
{
for (irow = 0; irow < num_rows_read; irow++)
{
#ifdef WIN32
yRow = jpg->height - jpg->ypos - 1; /* the DIB is stored upside down */
#else
yRow = jpg->ypos; /* our DIB is right side up */
#endif /* WIN32 */
pCurRow = jpg->image + jpg->rowbytes*yRow;
#ifndef MAC
if (jpg->dither_type == DITHER_NONE)
{
for (jpg->xpos=0; jpg->xpos<jpg->width; jpg->xpos++)
{
/*
DIB's are stored blue-green-red (backwards)
*/
*pCurRow++ = jpg->buffer[irow][jpg->xpos*3+RGB_BLUE];
*pCurRow++ = jpg->buffer[irow][jpg->xpos*3+RGB_GREEN];
*pCurRow++ = jpg->buffer[irow][jpg->xpos*3+RGB_RED];
}
}
else
{
for (jpg->xpos=0; jpg->xpos<jpg->width; jpg->xpos++)
{
*pCurRow++ = jpg->buffer[irow][jpg->xpos];
}
}
#else
for (jpg->xpos=0; jpg->xpos<jpg->width; jpg->xpos++)
{
pCurRow++;
*pCurRow++ = jpg->buffer[irow][jpg->xpos*3+RGB_RED];
*pCurRow++ = jpg->buffer[irow][jpg->xpos*3+RGB_GREEN];
*pCurRow++ = jpg->buffer[irow][jpg->xpos*3+RGB_BLUE];
}
#endif /* !MAC */
jpg->ypos++;
}
}
else
{
XX_Assert((jpg->cinfo.out_color_space == JCS_GRAYSCALE), ("Illegal color space"));
for (irow = 0; irow < num_rows_read; irow++)
{
/* Beginning each new row, the horizontal errors are 0 */
#ifdef WIN32
yRow = jpg->height - jpg->ypos - 1; /* the DIB is stored upside down */
#else
yRow = jpg->ypos; /* our DIB is right side up */
#endif /* WIN32 */
pCurRow = jpg->image + jpg->rowbytes*yRow;
#ifndef MAC
switch (jpg->dither_type)
{
case DITHER_NONE:
for (jpg->xpos=0; jpg->xpos<jpg->width; jpg->xpos++)
{
*pCurRow++ = jpg->buffer[irow][jpg->xpos];
*pCurRow++ = jpg->buffer[irow][jpg->xpos];
*pCurRow++ = jpg->buffer[irow][jpg->xpos];
}
break;
#ifdef WIN32
case DITHER_CUBE:
for (jpg->xpos=0; jpg->xpos<jpg->width; jpg->xpos++)
{
*pCurRow++ = x_MapGraysToGlobalPalette[(jpg->buffer[irow][jpg->xpos])];
}
break;
case DITHER_VGA:
for (jpg->xpos=0; jpg->xpos<jpg->width; jpg->xpos++)
{
*pCurRow++ = x_MapGraysToVGAPalette[(jpg->buffer[irow][jpg->xpos])];
}
break;
#endif /* WIN32 */
default:
for (jpg->xpos=0; jpg->xpos<jpg->width; jpg->xpos++)
{
*pCurRow++ = jpg->buffer[irow][jpg->xpos];
}
break;
}
#else
for (jpg->xpos=0; jpg->xpos<jpg->width; jpg->xpos++)
{
*pCurRow++ = 255 - jpg->buffer[irow][jpg->xpos];
}
#endif /* !MAC */
jpg->ypos++;
}
}
/* how much of image is now decoded? */
jpg->decoded_pass = jpg->pass; /* this pass */
jpg->decoded_ypos = jpg->ypos - 1; /* at least one more line */
}
#ifdef MAC
UnlockPixels(jpg->gw->portPixMap);
#endif /* MAC */
/* made it all the way through this scan of image */
return TRUE;
}
#ifdef FEATURE_MULTISCAN_JPEG
#define STATE_PROCESS_SCAN (STATE_OTHER)
#define STATE_FINISH_OUTPUT (STATE_OTHER + 1)
/*
JPEG_MultiScanImage: process a multi-scan image from a JPEG file
*/
static BOOL JPEG_MultiScanImage(struct _JPEGinfo *jpg)
{
int retcode;
BOOL final_pass;
/* consume as much of input buffer as possible */
do
{
retcode = jpeg_consume_input(&jpg->cinfo);
}
while ((retcode != JPEG_SUSPENDED) &&
(retcode != JPEG_REACHED_EOI));
/* are we all done? */
final_pass = jpeg_input_complete(&jpg->cinfo);
do
{
switch (jpg->state_multi)
{
case STATE_INIT:
/* start a new output pass */
jpg->pass = jpg->cinfo.input_scan_number;
jpg->xpos = jpg->ypos = 0;
/* TODO: adjust output decompression param.s IFF req.d (ie, for final_pass) */
if (!jpeg_start_output(&jpg->cinfo, jpg->cinfo.input_scan_number))
return FALSE; /* suspends IFF 2-pass quant. & full scan !avail */
/* fall through */
jpg->state_multi = STATE_PROCESS_SCAN;
case STATE_PROCESS_SCAN:
/* continue processing current scan */
if (!JPEG_ScanImage(jpg))
return FALSE; /* suspend and try again */
/* fall through */
jpg->state_multi = STATE_FINISH_OUTPUT;
case STATE_FINISH_OUTPUT:
/* terminate output pass */
jpg->bFirstPass = FALSE;
if (!jpeg_finish_output(&jpg->cinfo))
return FALSE; /* suspend and try again */
/* fall through (& loop again) */
jpg->state_multi = STATE_INIT;
}
}
while (!final_pass);
/* made it all the way through the image */
return TRUE;
}
#endif /* FEATURE_MULTISCAN_JPEG */
/*************************************************************
platform-specific stuff
**************************************************************/
/*
JPEG_InitDithering: initialize as needed for dithering of a given type
*/
#ifdef WIN32
static BOOL JPEG_InitDithering(j_decompress_ptr cinfo, int dither_type)
{
switch (dither_type)
{
case DITHER_CUBE:
{ /* Use the IJG dithering code to dither into our 6x6x6 cube */
switch (cinfo->jpeg_color_space)
{
case JCS_GRAYSCALE:
XX_Assert((GREEN_COLOR_LEVELS == RED_COLOR_LEVELS), ("Green and red guns aren't the same"));
XX_Assert((GREEN_COLOR_LEVELS == BLUE_COLOR_LEVELS), ("Green and blue guns aren't the same"));
cinfo->out_color_space = JCS_GRAYSCALE;
cinfo->quantize_colors = TRUE;
cinfo->desired_number_of_colors = GREEN_COLOR_LEVELS;
cinfo->two_pass_quantize = FALSE;
cinfo->dither_mode = JDITHER_FS;
break;
default:
cinfo->out_color_space = JCS_RGB;
/*
We are making the assumption here that by setting the
following parameters, we are causing the IJG quant/
dithering code to dither to a palette which happens to
be exactly like our global palette.
*/
cinfo->quantize_colors = TRUE;
cinfo->desired_number_of_colors = NUM_MAIN_PALETTE_COLORS;
cinfo->two_pass_quantize = FALSE;
cinfo->dither_mode = JDITHER_FS;
break;
}
break;
} /* DITHER_CUBE */
case DITHER_VGA:
{ /* Use the IJG dithering code to dither into the VGA palette */
switch (cinfo->jpeg_color_space)
{
case JCS_GRAYSCALE:
cinfo->out_color_space = JCS_GRAYSCALE;
cinfo->quantize_colors = TRUE;
cinfo->desired_number_of_colors = 3;
cinfo->two_pass_quantize = FALSE;
cinfo->dither_mode = JDITHER_FS;
break;
default:
cinfo->out_color_space = JCS_RGB;
cinfo->quantize_colors = TRUE;
cinfo->desired_number_of_colors = 16;
cinfo->two_pass_quantize = FALSE;
cinfo->dither_mode = JDITHER_FS;
cinfo->colormap = (*cinfo->mem->alloc_sarray)
((j_common_ptr) cinfo, JPOOL_IMAGE, 16, 3);
{
int i;
for (i=0; i<16; i++)
{
cinfo->colormap[RGB_RED][i] = GetRValue(vga_colors[i]);
cinfo->colormap[RGB_GREEN][i] = GetGValue(vga_colors[i]);
cinfo->colormap[RGB_BLUE][i] = GetBValue(vga_colors[i]);
}
}
cinfo->actual_number_of_colors = 16;
break;
}
break;
} /* DITHER_VGA */
case DITHER_NONE:
{ /* We want the actual RGB data here */
cinfo->quantize_colors = FALSE;
switch (cinfo->jpeg_color_space)
{
case JCS_GRAYSCALE:
cinfo->out_color_space = JCS_GRAYSCALE;
break;
default:
cinfo->out_color_space = JCS_RGB;
break;
}
break;
} /* DITHER_NONE*/
default:
/* TODO: invalid case -- emit warning/error here */
return FALSE;
}
return TRUE;
}
#endif /* WIN32 */
#ifdef MAC
static BOOL JPEG_InitDithering(j_decompress_ptr cinfo, int dither_type)
{
switch (dither_type)
{
case DITHER_NONE:
/* no-op: defaults work fine */
break;
default:
/* TODO: invalid case -- emit warning/error here */
return FALSE;
}
return TRUE;
}
#endif /* MAC */
#ifdef UNIX
static BOOL JPEG_InitDithering(j_decompress_ptr cinfo, int dither_type, XColor *colrs, int ncolors)
{
switch (dither_type)
{
case DITHER_CUBE:
{ /* dither to colorcube we have allocated from X (colrs) */
switch (cinfo->jpeg_color_space)
{
case JCS_GRAYSCALE:
cinfo->out_color_space = JCS_GRAYSCALE;
cinfo->quantize_colors = FALSE;
cinfo->desired_number_of_colors = 256;
cinfo->two_pass_quantize = FALSE;
cinfo->dither_mode = JDITHER_NONE;
cinfo->colormap = (*cinfo->mem->alloc_sarray)
((j_common_ptr) cinfo, JPOOL_IMAGE, 256, 3);
{
int i;
for (i=0; i < 256; i++)
{
colrs[i].red = i << 8;
colrs[i].green = i << 8;
colrs[i].blue = i << 8;
cinfo->colormap[RGB_RED][i] = i;
cinfo->colormap[RGB_GREEN][i] = i;
cinfo->colormap[RGB_BLUE][i] = i;
}
}
break;
default:
cinfo->out_color_space = JCS_RGB;
cinfo->quantize_colors = TRUE;
cinfo->desired_number_of_colors = ncolors;
cinfo->two_pass_quantize = FALSE;
cinfo->dither_mode = JDITHER_FS;
cinfo->colormap = (*cinfo->mem->alloc_sarray)
((j_common_ptr) cinfo, JPOOL_IMAGE, ncolors, 3);
{
int i;
for (i=0; i < ncolors; i++)
{
cinfo->colormap[RGB_RED][i] = (colrs[i].red >> 8) & 0xff;
cinfo->colormap[RGB_GREEN][i] = (colrs[i].green >> 8) & 0xff;
cinfo->colormap[RGB_BLUE][i] = (colrs[i].blue >> 8) & 0xff;
}
}
cinfo->actual_number_of_colors = ncolors;
break;
}
break;
} /* DITHER_CUBE */
case DITHER_NONE:
{ /* We want the actual RGB data here */
cinfo->quantize_colors = FALSE;
switch (cinfo->jpeg_color_space)
{
case JCS_GRAYSCALE:
cinfo->out_color_space = JCS_GRAYSCALE;
break;
default:
cinfo->out_color_space = JCS_RGB;
break;
}
break;
} /* DITHER_NONE*/
default:
/* TODO: invalid case -- emit warning/error here */
return FALSE;
}
return TRUE;
}
#endif /* UNIX */
/*
AllocImage: calculate row width (in bytes) & allocate image
*/
#ifdef WIN32
static BOOL JPEG_AllocImage(unsigned char **image, long w, long h, long *rowbytes, int dither_type)
{
if (dither_type == DITHER_NONE)
*rowbytes = w*3; /* each pixel takes 3 components */
else
*rowbytes = w; /* each pixel is one color entry */
if (*rowbytes%4)
{
*rowbytes = *rowbytes + 4 - (*rowbytes%4);
}
*image = GTR_CALLOC(*rowbytes * h, 1);
return (*image != NULL);
}
#endif /* WIN32 */
#ifdef UNIX
static BOOL JPEG_AllocImage(unsigned char **image, long w, long h, long *rowbytes, int dither_type)
{
if (dither_type == DITHER_NONE)
*rowbytes = w*3; /* each pixel takes 3 components */
else
*rowbytes = w; /* each pixel is one color entry */
#ifdef NEED_TO_PADD /* TODO: is this **ever** needed on Unix? if not, kill it */
if (*rowbytes%4)
{
*rowbytes = *rowbytes + 4 - (*rowbytes%4);
}
#endif
*image = GTR_CALLOC(*rowbytes * h, 1);
return (*image != NULL);
}
#endif /* UNIX */
#ifdef MAC
static BOOL JPEG_AllocImage(GWorldPtr *gw, long w, long h, long *rowbytes, BOOL bIsRGB)
{
Rect rBounds;
PixMapHandle thePix;
rBounds.left = 0;
rBounds.top = 0;
rBounds.right = w;
rBounds.bottom = h;
if (bIsRGB)
*gw = GTR_ALLOCGWORLD(24, &rBounds, NULL);
else
*gw = GTR_ALLOCGWORLD(8, &rBounds,
(CTabHandle) GetResource('clut', 1000)); /* FIXME handle this better */
if (!*gw)
{
return FALSE;
}
else
{
thePix = GetGWorldPixMap(*gw);
*rowbytes = (*thePix)->rowBytes & 0x7fff;
return TRUE;
}
}
#endif /* !MAC */
/*************************************************************************
temp. version of old APIs as wrapper functions
*************************************************************************/
#ifdef WIN32
/* This version of the routine uses the IJG dithering code to dither into our 6x6x6 cube */
unsigned char *
ReadJPEG_Dithered(unsigned char *data, long len, long *width, long *height)
{
return ReadJPEG(data, len, width, height, DITHER_CUBE);
}
/* This version of the routine uses the IJG dithering code to dither into the VGA palette */
unsigned char *
ReadJPEG_Dithered_VGA(unsigned char *data, long len, long *width, long *height)
{
return ReadJPEG(data, len, width, height, DITHER_VGA);
}
unsigned char *
ReadJPEG_RGB(unsigned char *data, long len, long *width, long *height)
{
return ReadJPEG(data, len, width, height, DITHER_NONE);
}
#endif /* WIN32 */
#ifdef UNIX
/*
** for the unix version, we pass in a colortable that it will
** dither into. this will match the colorcube we have allocated from X
*/
/** Scott... Added gray field so we can pass back information to the
call on if we need to dither this data again (GRAYSCALE) **/
unsigned char *
ReadJPEG_Dithered (unsigned char *data, long len, XColor *colrs, long *width, long *height, unsigned char *gray)
{
return ReadJPEG(data, len, width, height, DITHER_CUBE, colrs, gray);
}
unsigned char *
ReadJPEG_RGB(unsigned char *data, long len, long *width, long *height)
{
return ReadJPEG(data, len, width, height, DITHER_NONE, NULL, NULL);
}
#endif /* UNIX */
#ifdef MAC
GWorldPtr MacReadJPEG(Handle hData, long len, long *width, long *height)
{
return ReadJPEG(hData, len, width, height, DITHER_NONE);
}
#endif /* MAC */
/*
* SOME FINE POINTS:
*
* We cheated a bit by calling alloc_sarray() after jpeg_start_decompress();
* we should have done it beforehand to ensure that the space would be
* counted against the JPEG max_memory setting. In some systems the above
* code would risk an out-of-memory error. However, in general we don't
* know the output image dimensions before jpeg_start_decompress(), unless we
* call jpeg_calc_output_dimensions(). See libjpeg.doc for more about this.
*
* Scanlines are returned in the same order as they appear in the JPEG file,
* which is standardly top-to-bottom. If you must emit data bottom-to-top,
* you can use one of the virtual arrays provided by the JPEG memory manager
* to invert the data. See wrbmp.c for an example.
*
* As with compression, some operating modes may require temporary files.
* On some systems you may need to set up a signal handler to ensure that
* temporary files are deleted if the program is interrupted. See libjpeg.doc.
*/
/***************************************************************************
HTJPEG stream stuff
***************************************************************************/
PRIVATE BOOL HTJPEG_put_character(HTStream * me, char c)
{
return HTJPEG_write(me, &c, 1);
}
PRIVATE BOOL HTJPEG_put_string(HTStream * me, CONST char *s)
{
/* This never gets called */
return FALSE;
}
static void JPEG_DoProgressiveStuff (HTStream * me)
{
/* check if 1st time AND has header AND has colormap
** AND had allocated the image buffer
*/
/* Note this depends on all states being increasing numerically */
if (!me->jpg->bGotHeader && me->state > STATE_IMAGE_CREATE)
{
me->jpg->bGotHeader = 1;
me->jpg->pIInfo = JPG_DoSetImage(me);/* set up image header stuff */
}
/* fprintf (stderr, "GIF->LINE %d\n", me->jpg->decoded_ypos-1); */
if (me->jpg->pIInfo &&
(me->jpg->decoded_ypos-1) >= 0) /* Got some data, lets get to it */
{
HTList *cur;
wImageEleP p;
struct Mwin *mw;
BOOL bDrawMe;
me->jpg->pIInfo->nPass = me->jpg->decoded_pass;
me->jpg->pIInfo->nLastRow = me->jpg->decoded_ypos;
me->jpg->pIInfo->bFirstPass = me->jpg->bFirstPass;
/* And finally request that it be updated on the display */
/* printf ("Calling HT_CreateDeviceImageMap\n"); */
HT_CreateDeviceImageMap (me->tw, me->jpg->pIInfo);
/*
** Walk through list of elements which reference this
** image and update them.
*/
for (cur = me->jpg->pIInfo->llElements ;
p = (wImageEleP) HTList_nextObject(cur) ; )
{
for (mw = Mlist; mw; mw = mw->next)
if (p->w3doc == mw->w3doc)
{
bDrawMe = TRUE;
if ( gPrefs.ReformatHandling >= 2 &&
W3Doc_CheckForImageLoadElement (p->w3doc, p->element))
{
/* We're in "high-flicker" mode - reformat the document */
TW_Reformat (mw);
}
if ( gPrefs.ReformatHandling < 2 )
{
RECT rUpdate;
struct _element *pel;
/* This is "no-flicker" mode - don't update the display unless the placeholder
is already the right size (because we had image hints) */
pel = &p->w3doc->aElements[p->element];
rUpdate = pel->r;
if (pel->iBorder > 0)
{
GTR_InsetRect(&rUpdate, pel->iBorder, pel->iBorder);
}
if (!pel->portion.img.height || !pel->portion.img.width)
{
bDrawMe = FALSE;
}
}
/* TODO don't call if not resized or off screen */
if (bDrawMe)
GTR_DrawProgessiveImage (mw, p->element);
}
}
me->jpg->pIInfo->nPreviousLastRow = me->jpg->pIInfo->nLastRow;
me->jpg->pIInfo->nPreviousPass = me->jpg->pIInfo->nPass;
}
}
PRIVATE BOOL HTJPEG_write(HTStream * me, CONST char *s, int l)
{
me->jpg->bForceFlush = FALSE;
/* let state machine consume input */
me->state = DecodeJPEG((unsigned char *) s, (long) l, me->jpg);
#ifdef FEATURE_PROGRESSIVE_IMAGE
if (gPrefs.bProgressiveImageDisplay)
JPEG_DoProgressiveStuff (me);
#endif
/* else just return state. */
return (me->state != STATE_ABORT);
/*
//
// BUGBUG: This causes background or button jpegs to fail to load
// since the STATE_DONE gets sent too early???
//
if ((me->state == STATE_ABORT) || (me->state == STATE_DONE))
{
return FALSE;
}
else
{
return TRUE;
}
*/
}
PRIVATE void HTJPEG_free(HTStream * me)
{
me->jpg->bForceFlush = TRUE;
/* force state machine to consume rest of its buffers */
if (me->state != STATE_DONE)
me->state = DecodeJPEG(NULL, 0, me->jpg);
/* see what happened */
if ( (me->state == STATE_IMAGE_DATA) ||
(me->state == STATE_IMAGE_DONE) ||
(me->state == STATE_DONE) )
{
#ifdef FEATURE_PROGRESSIVE_IMAGE
if (gPrefs.bProgressiveImageDisplay)
{
JPEG_DoProgressiveStuff (me);
#ifdef UNIX
/* now call it one last time so it can fix up pixmaps */
if (me->jpg->pIInfo)
{
me->jpg->pIInfo->bComplete = 1;
HT_CreateDeviceImageMap (me->tw, me->jpg->pIInfo);
}
else
XX_DMsg(DBG_IMAGE, ("JPEG ImageSetInfo failed\n"));
#endif
}
else /* display at end (i.e. the old way) */
{
#endif
/* hack. don't let image get destroyed on
** inlined image viewers.
*/
me->jpg->pIInfo = JPG_DoSetImage (me);/* set up image header stuff*/
#ifdef FEATURE_INLINED_IMAGES
if (me->tw->w3doc->bIsImage)
HT_CreateDeviceImageMap (me->tw, me->jpg->pIInfo);
#endif
#ifdef UNIX
me->jpg->pIInfo->bComplete = 1;
HT_CreateDeviceImageMap (me->tw, me->jpg->pIInfo);
#endif
#ifdef FEATURE_PROGRESSIVE_IMAGE
}
#endif
/* display the image */
}
else
{ /* image incomplete */
#ifdef WIN32
Image_SetImageData (me->request, NULL, 0, IMG_ERROR, NULL, -1, 0);
#endif
#ifdef MAC
Image_SetImageData (me->request, NULL, NULL, 0, IMG_ERROR);
#endif
#ifdef UNIX
Image_SetImageData (me->request, NULL, NULL, 0, IMG_ERROR, NULL, -1, 0, 0);
GTR_FREE (me->jpg->colrs);
#endif
}
/* clean up */
if (me->jpg)
GTR_FREE(me->jpg);
GTR_FREE(me);
}
PRIVATE void HTJPEG_abort(HTStream * me, HTError e)
{
XX_DMsg(DBG_IMAGE, ("Aborting transfer of %s, e = %d", me->request->destination->szActualURL, e));
me->jpg->bForceFlush = TRUE;
me->jpg->state = STATE_ABORT;
/* force state machine to clean up after itself */
if (me->state != STATE_ABORT)
me->state = DecodeJPEG(NULL, 0, me->jpg);
#ifdef FEATURE_PROGRESSIVE_IMAGE
if ((gPrefs.bProgressiveImageDisplay) &&
(e == HTERROR_CANCELLED) &&
((me->jpg->decoded_pass > 0) || (me->jpg->decoded_ypos > 0)))
{
/* HACK: user cancelled during LOADING, so set PARTIAL, rather than NOTLOADED */
me->jpg->pIInfo->flags |= IMG_PARTIAL;
}
else
#endif /* FEATURE_PROGRESSIVE_IMAGE */
{
#ifdef WIN32
Image_SetImageData(me->request, NULL, 0, (e != HTERROR_CANCELLED) ? IMG_ERROR : IMG_NOTLOADED, NULL, -1, 0);
#endif
#ifdef MAC
Image_SetImageData(me->request, NULL, NULL, 0, (e != HTERROR_CANCELLED) ? IMG_ERROR : IMG_NOTLOADED);
#endif
#ifdef UNIX
Image_SetImageData(me->request, NULL, NULL, 0, (e != HTERROR_CANCELLED) ? IMG_ERROR : IMG_NOTLOADED, NULL, -1, 0, 0);
#endif
}
if (me->jpg)
GTR_FREE(me->jpg);
GTR_FREE(me);
}
/* Image creation
*/
PUBLIC HTStream *Image_JPEG(struct Mwin *tw, HTRequest * request, void *param, HTFormat input_format, HTFormat output_format, HTStream * output_stream)
{
BOOL bOK;
HTStream *me = (HTStream *) GTR_CALLOC(sizeof(*me), 1);
XX_DMsg(DBG_IMAGE, ("Creating new JPEG stream for %s\n", request->destination->szActualURL));
/* for convenience, assume failure */
bOK = FALSE;
if (!me)
goto fini;
me->isa = &HTJPEGClass;
me->request = request;
/* alloc, initialize platform-specific state */
#ifdef UNIX
if (!(me->colors = GTR_MALLOC (sizeof (XColor) * 256)))
goto fini;
#endif
/* alloc, initialize decoder state variables */
me->jpg = (struct _JPEGinfo *)GTR_CALLOC(1, sizeof(struct _JPEGinfo));
if (!me->jpg)
goto fini;
me->jpg->bForceFlush = FALSE;
me->jpg->state = STATE_INIT;
/* select platform-specific dithering mix */
#ifdef WIN32
if (wg.eColorMode == 8)
me->jpg->dither_type = DITHER_CUBE;
else if (wg.eColorMode == 4)
me->jpg->dither_type = DITHER_VGA; /* 16 color screen */
else
me->jpg->dither_type = DITHER_NONE; /* true color screen */
#endif /* WIN32 */
#ifdef MAC
me->jpg->dither_type = DITHER_NONE;
#endif /* MAC */
#ifdef UNIX
if (display_depth == 8)
{
/* if 8 bit display, just give them an 8 bit buffer and color table */
memcpy (me->colors, dither_info.colors, sizeof (XColor) * 256);
me->jpg->ncolors = dither_info.ncolors;
me->jpg->colrs = me->colors;
me->jpg->dither_type = DITHER_CUBE;
}
else
{
/* if 24 bit display, create a 24 bit buffer */
me->jpg->dither_type = DITHER_NONE;
}
#endif /* UNIX */
/* start state machine */
me->state = DecodeJPEG(NULL, 0, me->jpg);
/* see what happened */
if (me->state == STATE_ABORT)
goto fini;
if (me->state != STATE_INIT_LIBJPEG)
{
XX_DMsg(DBG_IMAGE, ("JPEG: unexpected state %s after initialization\n", me->state));
goto fini;
}
/* made it through, so everything worked */
bOK = TRUE;
fini:
if (!bOK)
{
/* couldn't initialize: clean up */
#ifdef WIN32
Image_SetImageData(request, NULL, 0, IMG_NOTLOADED, NULL, -1, 0);
#endif
#ifdef MAC
Image_SetImageData(request, NULL, NULL, 0, IMG_NOTLOADED);
#endif
#ifdef UNIX
Image_SetImageData(request, NULL, NULL, 0, IMG_NOTLOADED, NULL, -1, 0, 0);
#endif
if (me)
{
if (me->jpg)
GTR_FREE(me->jpg);
}
return (NULL);
}
me->tw = tw;
return me;
}
struct ImageInfo *JPG_DoSetImage (HTStream *me)
{
int flags = 0;
#ifdef WIN32
return Image_SetImageData(me->request, me->jpg->image, me->jpg->width, me->jpg->height, NULL, -1, IMG_JPEG);
#endif /* WIN32 */
#ifdef MAC
return Image_SetImageData(me->request, me->jpg->gw, NULL, me->jpg->width, me->jpg->height);
#endif
#ifdef UNIX
#ifdef FEATURE_INLINED_IMAGES
if (me->tw->w3doc->bIsImage)
flags |= IMG_ISIMAGE;
#endif /* FEATURE_INLINED_IMAGES */
if (me->jpg->dither_type == DITHER_CUBE)
{
flags |= (me->jpg->bGray ? IMG_GRAY : IMG_JPEG);
return Image_SetImageData(me->request, me->jpg->image, NULL,
me->jpg->width, me->jpg->height, me->jpg->colrs, -1, 8, flags);
}
else
return Image_SetImageData(me->request, me->jpg->image, NULL,
me->jpg->width, me->jpg->height, NULL, -1, 24, IMG_24 | flags);
#endif /* UNIX */
}
#endif /* FEATURE_JPEG */