mirror of
https://github.com/aaronsgiles/JPEGView.git
synced 2024-06-14 12:29:33 +00:00
92bdb55672
These are the sources for the final official release of JPEGView for the Mac, back in 1994.
1 line
15 KiB
C
1 line
15 KiB
C
/*********************************************************/
|
|
/* This source code copyright (c) 1991-2001, Aaron Giles */
|
|
/* See the Read Me file for licensing information. */
|
|
/* Contact email: mac@aarongiles.com */
|
|
/*********************************************************/
|
|
|
|
//=====================================================================================
|
|
// Generic includes for Macintosh headers
|
|
//=====================================================================================
|
|
|
|
#if THINK_C
|
|
#include "THINK.Header"
|
|
#elif applec
|
|
#pragma load ":Headers:MPW.Header"
|
|
#elif __MWERKS__
|
|
//#include "MW.Header"
|
|
#else
|
|
#include "JPEGView.h"
|
|
#endif
|
|
|
|
//=====================================================================================
|
|
// Includes specific to this module
|
|
//=====================================================================================
|
|
|
|
#include <stdio.h>
|
|
#include <setjmp.h>
|
|
#include <string.h>
|
|
#include "jpeglib.h"
|
|
#include "jerror.h"
|
|
#include "JPEGNoQuickTime.h"
|
|
extern const char * const jpeg_message_table[];
|
|
|
|
//=====================================================================================
|
|
// Prototypes for functions local to this module
|
|
//=====================================================================================
|
|
|
|
static OSErr DecompressJPEGImage(struct jpeg_decompress_struct *cinfo, PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog);
|
|
static CTabHandle MakeGrayscaleColorTable(void);
|
|
static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow);
|
|
|
|
GLOBAL void
|
|
InitIJGSource (j_decompress_ptr cinfo, Handle theHandle);
|
|
GLOBAL struct jpeg_error_mgr *
|
|
InitIJGErrors (struct jpeg_error_mgr * err);
|
|
|
|
static jmp_buf gJPEGErrorReturn;
|
|
|
|
extern OSErr DrawJPEGNoQuickTime(Handle theHandle, JVDrawParamsHandle theParams)
|
|
{
|
|
NestedProgress theProgress = (*theParams)->progress;
|
|
struct jpeg_decompress_struct cinfo;
|
|
char hState = HGetState(theHandle);
|
|
struct jpeg_error_mgr jerr;
|
|
CTabHandle theColors = nil;
|
|
GWorldPtr theGWorld = nil;
|
|
OSErr theErr;
|
|
|
|
if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc,
|
|
codecProgressUpdatePercent, 0x00000000L, theProgress.prog.progressRefCon);
|
|
HLock(theHandle);
|
|
cinfo.err = InitIJGErrors(&jerr);
|
|
theErr = setjmp(gJPEGErrorReturn);
|
|
if (theErr == noErr) {
|
|
jpeg_create_decompress(&cinfo);
|
|
InitIJGSource(&cinfo, theHandle);
|
|
theErr = setjmp(gJPEGErrorReturn);
|
|
if (theErr == noErr) {
|
|
jpeg_read_header(&cinfo, true);
|
|
jpeg_start_decompress(&cinfo);
|
|
if (cinfo.out_color_components == 1) theColors = MakeGrayscaleColorTable();
|
|
theGWorld = NewTempGWorld(cinfo.output_width, cinfo.output_height, (cinfo.out_color_components == 1) ? 8 : 32, theColors);
|
|
if (cinfo.out_color_components == 1) DisposCTable(theColors);
|
|
if (theGWorld) {
|
|
theErr = DecompressJPEGImage(&cinfo, GetGWorldPixMap(theGWorld), (ICMProgressProcRecordPtr)&theProgress);
|
|
if ((*theParams)->progress.aborted) theErr = codecAbortErr;
|
|
} else gIntError = errNoDrawMemory, theErr = memFullErr;
|
|
}
|
|
/*if (theErr == noErr) {
|
|
theErr = setjmp(gJPEGErrorReturn);
|
|
if (theErr == noErr) jpeg_finish_decompress(&cinfo);
|
|
}*/
|
|
jpeg_destroy_decompress(&cinfo);
|
|
}
|
|
if (theGWorld) DisposeGWorld(theGWorld);
|
|
HSetState(theHandle, hState);
|
|
if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc,
|
|
codecProgressUpdatePercent, 0x00010000L, theProgress.prog.progressRefCon);
|
|
return theErr;
|
|
}
|
|
|
|
static OSErr DecompressJPEGImage(struct jpeg_decompress_struct *cinfo, PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog)
|
|
{
|
|
char pState = GetPixelsState(dstPixMap);
|
|
RgnHandle dstRgn = qd.thePort->visRgn;
|
|
ulong bandRow = 0, row, bufferRows = Height(&(*dstPixMap)->bounds), total, count = 1, nextUpdate = 32;
|
|
short lastLine = (*qd.thePort->visRgn)->rgnBBox.bottom;
|
|
JSAMPARRAY scanLines = nil;
|
|
OSErr theErr = noErr;
|
|
uchar *rowStart;
|
|
|
|
LockPixels(dstPixMap);
|
|
theErr = setjmp(gJPEGErrorReturn);
|
|
if (theErr == noErr) {
|
|
rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
|
|
if (scanLines = (JSAMPARRAY)NewPtrClear(bufferRows * sizeof(JSAMPROW))) {
|
|
for (row = 0; row < bufferRows; row++) scanLines[row] = rowStart, rowStart += (*dstPixMap)->rowBytes & 0x3fff;
|
|
for (row = 0; row < lastLine && count && theErr == noErr; row += total) {
|
|
total = 0;
|
|
while (total < bufferRows && (row + total) < lastLine && theErr == noErr) {
|
|
// note that we don't swap MMU mode here; we assume that since the GWorld was allocated
|
|
// locally, it is always addressable through the current MMU state
|
|
count = jpeg_read_scanlines(cinfo, &scanLines[total], bufferRows - total);
|
|
if (!count) break;
|
|
total += count;
|
|
if ((row + total) > nextUpdate) {
|
|
if (prog->progressProc)
|
|
theErr = (OSErr)CallICMProgressProc(prog->progressProc,
|
|
codecProgressUpdatePercent, FixRatio(row + total, lastLine), prog->progressRefCon);
|
|
nextUpdate += 32;
|
|
}
|
|
}
|
|
CopyPixMapToDestination(dstPixMap, row);
|
|
}
|
|
}
|
|
}
|
|
if (scanLines) DisposePtr((Ptr)scanLines);
|
|
SetPixelsState(dstPixMap, pState);
|
|
return theErr;
|
|
}
|
|
|
|
static CTabHandle MakeGrayscaleColorTable(void)
|
|
{
|
|
ushort color, count = 256, i;
|
|
CTabHandle theColors;
|
|
|
|
theColors = (CTabHandle)NewHandleClear(sizeof(ColorTable) + (count - 1) * sizeof(ColorSpec));
|
|
if (theColors) {
|
|
(*theColors)->ctSeed = GetCTSeed();
|
|
(*theColors)->ctFlags = 0x8000;
|
|
(*theColors)->ctSize = count - 1;
|
|
for (i = 0; i < count; i++) {
|
|
color = (ulong)i * 65535L / (long)(count - 1);
|
|
(*theColors)->ctTable[i].rgb.red = (*theColors)->ctTable[i].rgb.green =
|
|
(*theColors)->ctTable[i].rgb.blue = color;
|
|
}
|
|
}
|
|
return theColors;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
|
|
//=====================================================================================
|
|
// Copies the band contained in srcPixMap to the destination, offset from the top by
|
|
// startRow.
|
|
//=====================================================================================
|
|
|
|
static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
|
|
{
|
|
Rect srcRect = (*srcPixMap)->bounds, dstRect = (*srcPixMap)->bounds;
|
|
PixMapHandle dstPixMap = GetGWorldPixMap((CGrafPtr)qd.thePort);
|
|
char srcState = HGetState((Handle)srcPixMap);
|
|
char dstState = HGetState((Handle)dstPixMap);
|
|
|
|
OffsetRect(&dstRect, 0, startRow);
|
|
if (dstRect.bottom > (*qd.thePort->visRgn)->rgnBBox.bottom) {
|
|
srcRect.bottom -= dstRect.bottom - (*qd.thePort->visRgn)->rgnBBox.bottom;
|
|
dstRect.bottom = (*qd.thePort->visRgn)->rgnBBox.bottom;
|
|
}
|
|
HLock((Handle)srcPixMap);
|
|
HLock((Handle)dstPixMap);
|
|
CopyBits((BitMap *)*srcPixMap, (BitMap *)*dstPixMap, &srcRect, &dstRect,
|
|
srcCopy + ditherCopy, qd.thePort->visRgn);
|
|
HSetState((Handle)dstPixMap, dstState);
|
|
HSetState((Handle)srcPixMap, srcState);
|
|
}
|
|
|
|
//============================================================================================
|
|
//============================================================================================
|
|
//=========================== ==================================
|
|
//=========================== IJG Data Source Object Code ==================================
|
|
//=========================== ==================================
|
|
//============================================================================================
|
|
//============================================================================================
|
|
|
|
/* Expanded data source object for stdio input */
|
|
|
|
typedef struct {
|
|
struct jpeg_source_mgr pub; /* public fields */
|
|
} my_source_mgr;
|
|
|
|
typedef my_source_mgr * my_src_ptr;
|
|
|
|
/*
|
|
* Initialize source --- called by jpeg_read_header
|
|
* before any data is actually read.
|
|
*/
|
|
|
|
METHODDEF void
|
|
init_source (j_decompress_ptr cinfo)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* Fill the input buffer --- called whenever buffer is emptied.
|
|
*/
|
|
|
|
METHODDEF boolean
|
|
fill_input_buffer (j_decompress_ptr cinfo)
|
|
{
|
|
static JOCTET dummyBuffer[] = { (JOCTET)0xff, (JOCTET)JPEG_EOI };
|
|
my_src_ptr src = (my_src_ptr) cinfo->src;
|
|
|
|
WARNMS(cinfo, JWRN_JPEG_EOF);
|
|
src->pub.next_input_byte = (JOCTET *)&dummyBuffer;
|
|
src->pub.bytes_in_buffer = 2;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Skip data --- used to skip over a potentially large amount of
|
|
* uninteresting data (such as an APPn marker).
|
|
*/
|
|
|
|
METHODDEF void
|
|
skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
|
{
|
|
my_src_ptr src = (my_src_ptr) cinfo->src;
|
|
|
|
src->pub.next_input_byte += num_bytes;
|
|
src->pub.bytes_in_buffer -= num_bytes;
|
|
}
|
|
|
|
/*
|
|
* An additional method that can be provided by data source modules is the
|
|
* resync_to_restart method for error recovery in the presence of RST markers.
|
|
* For the moment, this source module just uses the default resync method
|
|
* provided by the JPEG library. That method assumes that no backtracking
|
|
* is possible.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Terminate source --- called by jpeg_finish_decompress
|
|
* after all data has been read. Often a no-op.
|
|
*/
|
|
|
|
METHODDEF void
|
|
term_source (j_decompress_ptr cinfo)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* Prepare for input from a memory handle.
|
|
*/
|
|
|
|
GLOBAL void
|
|
InitIJGSource (j_decompress_ptr cinfo, Handle theHandle)
|
|
{
|
|
my_src_ptr src;
|
|
|
|
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(my_source_mgr));
|
|
}
|
|
|
|
src = (my_src_ptr) cinfo->src;
|
|
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; /* use default method */
|
|
src->pub.term_source = term_source;
|
|
src->pub.bytes_in_buffer = GetHandleSize(theHandle);
|
|
src->pub.next_input_byte = (JOCTET *)StripAddress(*theHandle);
|
|
}
|
|
|
|
//============================================================================================
|
|
//============================================================================================
|
|
//=========================== ==================================
|
|
//=========================== IJG Error Object Code ==================================
|
|
//=========================== ==================================
|
|
//============================================================================================
|
|
//============================================================================================
|
|
|
|
/*
|
|
* Error exit handler: must not return to caller.
|
|
*/
|
|
|
|
METHODDEF void
|
|
error_exit (j_common_ptr cinfo)
|
|
{
|
|
(*cinfo->err->output_message) (cinfo);
|
|
if (cinfo->err->msg_code == JERR_OUT_OF_MEMORY) {
|
|
gIntError = errNoDrawMemory;
|
|
longjmp(gJPEGErrorReturn, memFullErr);
|
|
} else {
|
|
longjmp(gJPEGErrorReturn, codecBadDataErr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Actual output of an error or trace message.
|
|
*/
|
|
|
|
METHODDEF void
|
|
output_message (j_common_ptr cinfo)
|
|
{
|
|
Str255 theString;
|
|
short length;
|
|
|
|
/* Create the message */
|
|
(*cinfo->err->format_message) (cinfo, (char *)theString);
|
|
|
|
/* Display it in a dialog box */
|
|
if (!gDrawing || !Corrupt(gDrawing)) {
|
|
length = strlen((char *)theString);
|
|
BlockMove(theString, &theString[1], length);
|
|
*theString = length;
|
|
PushPort();
|
|
StringError(errJPEGError, theString);
|
|
PopPort();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Decide whether to emit a trace or warning message.
|
|
* msg_level is one of:
|
|
* -1: recoverable corrupt-data warning, may want to abort.
|
|
* 0: important advisory messages (always display to user).
|
|
* 1: first level of tracing detail.
|
|
* 2,3,...: successively more detailed tracing messages.
|
|
* An application might override this method if it wanted to abort on warnings
|
|
* or change the policy about which messages to display.
|
|
*/
|
|
|
|
METHODDEF void
|
|
emit_message (j_common_ptr cinfo, int msg_level)
|
|
{
|
|
struct jpeg_error_mgr * err = cinfo->err;
|
|
#if NOWARNINGS
|
|
if (msg_level < 0) {
|
|
/* It's a warning message. Since corrupt files may generate many warnings,
|
|
* the policy implemented here is to show only the first warning,
|
|
* unless trace_level >= 3.
|
|
*/
|
|
if (err->num_warnings == 0 || err->trace_level >= 3)
|
|
(*err->output_message) (cinfo);
|
|
/* Always count warnings in num_warnings. */
|
|
err->num_warnings++;
|
|
} else {
|
|
/* It's a trace message. Show it if trace_level >= msg_level. */
|
|
if (err->trace_level >= msg_level)
|
|
(*err->output_message) (cinfo);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Format a message string for the most recent JPEG error or message.
|
|
* The message is stored into buffer, which should be at least JMSG_LENGTH_MAX
|
|
* characters. Note that no '\n' character is added to the string.
|
|
* Few applications should need to override this method.
|
|
*/
|
|
|
|
METHODDEF void
|
|
format_message (j_common_ptr cinfo, char * buffer)
|
|
{
|
|
struct jpeg_error_mgr * err = cinfo->err;
|
|
int msg_code = err->msg_code;
|
|
const char * msgtext = NULL;
|
|
const char * msgptr;
|
|
char ch;
|
|
boolean isstring;
|
|
|
|
/* Look up message string in proper table */
|
|
if (msg_code > 0 && msg_code <= err->last_jpeg_message) {
|
|
msgtext = err->jpeg_message_table[msg_code];
|
|
} else if (err->addon_message_table != NULL &&
|
|
msg_code >= err->first_addon_message &&
|
|
msg_code <= err->last_addon_message) {
|
|
msgtext = err->addon_message_table[msg_code - err->first_addon_message];
|
|
}
|
|
|
|
/* Defend against bogus message number */
|
|
if (msgtext == NULL) {
|
|
err->msg_parm.i[0] = msg_code;
|
|
msgtext = err->jpeg_message_table[0];
|
|
}
|
|
|
|
/* Check for string parameter, as indicated by %s in the message text */
|
|
isstring = FALSE;
|
|
msgptr = msgtext;
|
|
while ((ch = *msgptr++) != '\0') {
|
|
if (ch == '%') {
|
|
if (*msgptr == 's') isstring = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Format the message into the passed buffer */
|
|
if (isstring)
|
|
sprintf(buffer, msgtext, err->msg_parm.s);
|
|
else
|
|
sprintf(buffer, msgtext,
|
|
err->msg_parm.i[0], err->msg_parm.i[1],
|
|
err->msg_parm.i[2], err->msg_parm.i[3],
|
|
err->msg_parm.i[4], err->msg_parm.i[5],
|
|
err->msg_parm.i[6], err->msg_parm.i[7]);
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset error state variables at start of a new image.
|
|
* This is called during compression startup to reset trace/error
|
|
* processing to default state, without losing any application-specific
|
|
* method pointers. An application might possibly want to override
|
|
* this method if it has additional error processing state.
|
|
*/
|
|
|
|
METHODDEF void
|
|
reset_error_mgr (j_common_ptr cinfo)
|
|
{
|
|
cinfo->err->num_warnings = 0;
|
|
/* trace_level is not reset since it is an application-supplied parameter */
|
|
cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */
|
|
}
|
|
|
|
|
|
/*
|
|
* Fill in the standard error-handling methods in a jpeg_error_mgr object.
|
|
* Typical call is:
|
|
* struct jpeg_compress_struct cinfo;
|
|
* struct jpeg_error_mgr err;
|
|
*
|
|
* cinfo.err = jpeg_std_error(&err);
|
|
* after which the application may override some of the methods.
|
|
*/
|
|
|
|
GLOBAL struct jpeg_error_mgr *
|
|
InitIJGErrors (struct jpeg_error_mgr * err)
|
|
{
|
|
err->error_exit = error_exit;
|
|
err->emit_message = emit_message;
|
|
err->output_message = output_message;
|
|
err->format_message = format_message;
|
|
err->reset_error_mgr = reset_error_mgr;
|
|
|
|
err->trace_level = 0; /* default = no tracing */
|
|
err->num_warnings = 0; /* no warnings emitted yet */
|
|
err->msg_code = 0; /* may be useful as a flag for "no error" */
|
|
|
|
/* Initialize message table pointers */
|
|
err->jpeg_message_table = jpeg_message_table;
|
|
err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1;
|
|
|
|
err->addon_message_table = NULL;
|
|
err->first_addon_message = 0; /* for safety */
|
|
err->last_addon_message = 0;
|
|
|
|
return err;
|
|
}
|