This is SurfJPEGDecoder.Internal.m in view mode; [Download] [Up]
/*
** Copyright (c) 1995 Netsurfer Inc. All Rights Reserved.
**
** Author: <bbum@friday.com>
*/
/* This object is included in the MiscKit by permission from the author
** and its use is governed by the MiscKit license, found in the file
** "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
** for a list of all applicable permissions and restrictions.
*/
#import <appkit/appkit.h>
#import "SurfJPEGSupport.h"
#import "SurfJPEGDecoder.Internal.h"
#import <jpeglib.h>
#import <jerror.h>
#import <jconfig.h>
#import <mach/mach.h>
#ifdef VERBOSE
#undef VERBOSE
#undef METHOD
#undef METHODnl
#endif
#define VERBOSE if(_SDFlags.verboseMode)
#define METHOD fprintf(stderr, "[%s %s%s] ", \
object_getClassName(self), \
(self == [self class]) ? "+" : "-", \
SELNAME(_cmd))
#define METHODnl fprintf(stderr, "[%s %s%s]\n", \
object_getClassName(self), \
(self == [self class]) ? "+" : "-", \
SELNAME(_cmd))
#define INPUT_BUF_SIZE vm_page_size
typedef struct {
struct jpeg_source_mgr pub; /* public fields */
NXStream *infile; /* source stream */
JOCTET *buffer; /* start of buffer */
boolean start_of_file; /* have we gotten any data yet? */
} my_source_mgr;
typedef my_source_mgr * my_src_ptr;
@implementation SurfJPEGDecoder (InternalAPI)
/*"
* This category implements the internals of the JPEG/JFIF decoder. It
* basically provides an interface between the #{SurfDecoder} API and the
* IJG libjpeg API.
"*/
void _init_source(j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
src->start_of_file = TRUE;
}
boolean _fill_input_buffer (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
size_t nbytes;
nbytes = NXRead(src->infile, src->buffer, INPUT_BUF_SIZE);
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;
}
static void _skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
my_src_ptr src = (my_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;
}
}
void _term_source (j_decompress_ptr cinfo)
{
/* no work necessary here */
}
void _nxstream_stdio_src (j_decompress_ptr cinfo, NXStream *infile)
{
my_src_ptr src;
/* The source object and input buffer are made permanent so that a series
* of JPEG images can be read from the same file by calling jpeg_stdio_src
* only before the first one. (If we discarded the buffer at the end of
* one image, we'd likely lose the start of the next one.)
* This makes it unsafe to use this manager and a different source
* manager serially with the same JPEG object. Caveat programmer.
*/
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->buffer = (JOCTET *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
INPUT_BUF_SIZE * sizeof(JOCTET));
}
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->infile = infile;
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
src->pub.next_input_byte = NULL; /* until buffer loaded */
}
void _update_row_ptr_buf(unsigned char **rowPtrBuf,
unsigned totalRows,
unsigned rowsRead,
unsigned readCount,
unsigned bytesPerRow,
unsigned char *data)
{
unsigned row;
unsigned bufIndex;
unsigned maxRow = (rowsRead + readCount);
maxRow = (maxRow < totalRows) ? maxRow : totalRows;
for(row=rowsRead, bufIndex = 0; row < maxRow; row++, bufIndex++)
rowPtrBuf[bufIndex] = &(data[row * bytesPerRow]);
}
- _decodeImage
{
NXBitmapImageRep *imageRep;
void *data;
int bytesPerRow;
VERBOSE {
METHODnl;
}
totalRows = jpegDecompInfo.pub.output_height;
imageRep = [[NXBitmapImageRep allocFromZone:[self zone]]
initDataPlanes:NULL // instance will allocate
pixelsWide: jpegDecompInfo.pub.output_width
pixelsHigh: totalRows
bitsPerSample:8
samplesPerPixel:jpegDecompInfo.pub.output_components
hasAlpha:NO
isPlanar:NO
colorSpace:NX_RGBColorSpace
bytesPerRow:0 // instance will figure it out
bitsPerPixel:0];
if(!imageRep) {
VERBOSE {
METHOD;
fprintf(stderr,
"Failed to allocate imageRep.\n");
}
return nil;
}
data = [imageRep data];
bytesPerRow = [imageRep bytesPerRow];
rowsRead = 0;
while (jpegDecompInfo.pub.output_scanline <
jpegDecompInfo.pub.output_height) {
_update_row_ptr_buf(rowPtrBuf,
totalRows,
rowsRead,
readCount,
bytesPerRow,
data);
rowsRead += jpeg_read_scanlines(&jpegDecompInfo.pub,
(JSAMPARRAY) rowPtrBuf,
1);
}
[returnImage useRepresentation:imageRep];
return returnImage;
}
void surfjpeg_error_exit (j_common_ptr cinfo)
{
surfjpeg_error_ptr theErr = (surfjpeg_error_ptr) cinfo->err;
/* Always display the message. */
// (*cinfo->err->output_message) (cinfo);
longjmp(theErr->setjmp_buffer, 1); // bounce to setjmp() point
}
void surfjpeg_error_msg (j_common_ptr cinfo)
{
char buffer[JMSG_LENGTH_MAX];
surfjpeg_decompress_ptr decompInfo = (surfjpeg_decompress_ptr) cinfo;
/* Create the message */
(*cinfo->err->format_message) (cinfo, buffer);
[decompInfo->jpegDecoder setLastImageCorrupt: YES];
if(decompInfo->jpegDecoder)
[decompInfo->jpegDecoder
spewMessage:buffer
withSeverity:SEV_Failure];
}
- _decodeFromStream:(NXStream *) theStream
{
id theImage;
// initialize decompressor
jpegDecompInfo.pub.err = jpeg_std_error(&jpegErrMgr.pub);
jpegDecompInfo.jpegDecoder = self;
jpeg_create_decompress(&jpegDecompInfo.pub);
// set source to theStream
_nxstream_stdio_src(&jpegDecompInfo.pub, theStream);
// set up jpeg error handlers
jpegErrMgr.pub.output_message = surfjpeg_error_msg;
jpegErrMgr.pub.error_exit = surfjpeg_error_exit;
if(setjmp(jpegErrMgr.setjmp_buffer)) {
[self setLastImageCorrupt: YES];
jpeg_destroy_decompress(&jpegDecompInfo.pub);
return (rowsRead < (totalRows / 2)) ? nil : theImage;
}
// read header and start decompression
jpeg_read_header(&jpegDecompInfo.pub, TRUE);
jpeg_start_decompress(&jpegDecompInfo.pub);
// dump verbosity
VERBOSE {
METHOD;
fprintf(stderr,
"(%d x %d); %d components\n",
jpegDecompInfo.pub.image_width,
jpegDecompInfo.pub.image_height,
jpegDecompInfo.pub.num_components);
}
// decode image
theImage = [self _decodeImage];
// finish decompression
jpeg_finish_decompress(&jpegDecompInfo.pub);
// destroy decompression context
jpeg_destroy_decompress(&jpegDecompInfo.pub);
// return theImage
return theImage; // will either by returnImage or nil
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.