This is MiscJPEGDecoder.m in view mode; [Download] [Up]
/* MiscJPEGDecoder.m Copyright 1996 Netsurfer Inc. This notice may not be removed from this source code. The use and distribution of this software is governed by the terms of the MiscKit license agreement. Refer to the license document included with the MiscKit distribution for the terms. Author: <bbum@friday.com> Converted to OpenStep, September 1996, Uwe Hoffmann. */ #import <AppKit/AppKit.h> #import "MiscJPEGDecoder.h" #import <mach/mach.h> #ifdef VERBOSE #undef VERBOSE #undef METHOD #undef METHODnl #endif #define VERBOSE if(_SDFlags.verboseMode) #define METHOD NSLog(@"[%@ %s%@] ", \ [[self class] description], \ (self == (id)[self class]) ? "+" : "-", \ NSStringFromSelector(_cmd)) #define METHODnl NSLog(@"[%@ %s%@]\n", \ [[self class] description], \ (self == (id)[self class]) ? "+" : "-", \ NSStringFromSelector(_cmd)) #define INPUT_BUF_SIZE vm_page_size typedef struct { struct jpeg_source_mgr pub; /* public fields */ NSData *infile; /* source stream */ JOCTET *buffer; /* start of buffer */ boolean start_of_file; /* have we gotten any data yet? */ unsigned int bytesRead; /* bytes already read */ unsigned int infileLength; /* length of infile */ } misc_source_mgr; typedef misc_source_mgr * misc_src_ptr; @implementation MiscJPEGDecoder /*" This class implements the internals of the JPEG/JFIF decoder. It basically provides an interface between the #{MiscDecoder} API and the IJG libjpeg API. "*/ + sharedInstance { static id sharedInstance = nil; if(!sharedInstance) sharedInstance = [[self alloc] init]; return sharedInstance; } + (NSArray *)imageUnfilteredFileTypes; { return [NSArray arrayWithObjects: @"jpeg", @"JPEG", @"jpg", @"JPG", @"jfif", @"JFIF", nil]; } + (BOOL)canInitWithData:(NSData *)data { unsigned index; unsigned char header[11]; unsigned short matchbuf[] = { 0xff, // marker 0xd8, // start-of-input mark 0xff, // marker 0xe0, // app0 block follows 0xfff,// block length (ignored) 0xfff,// block length (ignored) 0x4a, // 'J' 0x46, // 'F' 0x49, // 'I' 0x46, // 'F' 0x00 // NULL 'JFIF' terminator }; NSParameterAssert(data); if([data length] < 11) return NO; [data getBytes:header length:11]; for(index = 0; index < 11;index++) if((matchbuf[index] != 0xfff) && (header[index] != matchbuf[index])) return NO; return YES; } - init { [super init]; [self setReadCount:5]; return self; } - (void)dealloc { free(rowPtrBuf); return [super dealloc]; } - (void) setReadCount:(unsigned)aNum /*"Sets the readCount to aNum and reallocates %rowPtrBuf, as necessary."*/ { if(aNum) readCount = aNum; else readCount = 5; if(rowPtrBuf) rowPtrBuf = (unsigned char **)NSZoneRealloc([self zone], rowPtrBuf,sizeof(unsigned char *) * readCount); else rowPtrBuf = (unsigned char **) NSZoneMalloc([self zone], sizeof(unsigned char *) * readCount); } - (unsigned) readCount /*"Returns the number of rows that will be read during each decoding pass."*/ { return readCount; } void _init_source(j_decompress_ptr cinfo) { misc_src_ptr src = (misc_src_ptr)cinfo->src; src->start_of_file = TRUE; src->bytesRead = 0; } boolean _fill_input_buffer(j_decompress_ptr cinfo) { NSRange range; misc_src_ptr src = (misc_src_ptr) cinfo->src; size_t nbytes; if(src->bytesRead < src->infileLength){ nbytes = MIN(INPUT_BUF_SIZE, src->infileLength - src->bytesRead); range = NSMakeRange(src->bytesRead, nbytes); [src->infile getBytes:src->buffer range:range]; } else nbytes = 0; 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; src->bytesRead += nbytes; return TRUE; } static void _skip_input_data(j_decompress_ptr cinfo, long num_bytes) { misc_src_ptr src = (misc_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 */ /* no work necessary here */ } void _nsdata_stdio_src(j_decompress_ptr cinfo, NSData *infile) { misc_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(misc_source_mgr)); src = (misc_src_ptr) cinfo->src; src->buffer = (JOCTET *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof(JOCTET)); } src = (misc_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->infileLength = [infile length]; 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]); } - (NSBitmapImageRep *)_internalDecodeImage { NSBitmapImageRep *imageRep; void *data; int bytesPerRow; VERBOSE { METHODnl; } totalRows = jpegDecompInfo.pub.output_height; imageRep = [[NSBitmapImageRep allocWithZone:[self zone]] initWithBitmapDataPlanes:NULL pixelsWide:jpegDecompInfo.pub.output_width pixelsHigh:totalRows bitsPerSample:8 samplesPerPixel:jpegDecompInfo.pub.output_components hasAlpha:NO isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:0]; if(!imageRep) { VERBOSE { METHOD; NSLog(@"Failed to allocate imageRep.\n"); } return nil; } data = [imageRep bitmapData]; 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); } return [imageRep autorelease]; } void miscjpeg_error_exit(j_common_ptr cinfo) { miscjpeg_error_ptr theErr = (miscjpeg_error_ptr) cinfo->err; /* Always display the message. */ longjmp(theErr->setjmp_buffer, 1); // bounce to setjmp() point } void miscjpeg_error_msg(j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; miscjpeg_decompress_ptr decompInfo = (miscjpeg_decompress_ptr) cinfo; /* Create the message */ (*cinfo->err->format_message) (cinfo, buffer); [decompInfo->jpegDecoder setLastImageCorrupt:YES]; if(decompInfo->jpegDecoder) [decompInfo->jpegDecoder spewMessage:[NSString stringWithCString:buffer] withSeverity:MiscDecoderSeverityFailure]; } - (NSBitmapImageRep *)decodeFromData:(NSData *)data { NSBitmapImageRep *jpegRep; _SDFlags.lastCorrupt = NO; if(![[self class] canInitWithData:data]) { VERBOSE { METHOD; NSLog(@"+canInitWithData: returned NO"); } _SDFlags.lastCorrupt = YES; return nil; } else VERBOSE { METHOD; NSLog(@"Data validated"); } // initialize decompressor jpegDecompInfo.pub.err = jpeg_std_error(&jpegErrMgr.pub); jpegDecompInfo.jpegDecoder = self; jpeg_create_decompress(&jpegDecompInfo.pub); // set source to theStream _nsdata_stdio_src(&jpegDecompInfo.pub, data); // set up jpeg error handlers jpegErrMgr.pub.output_message = miscjpeg_error_msg; jpegErrMgr.pub.error_exit = miscjpeg_error_exit; if(setjmp(jpegErrMgr.setjmp_buffer)){ _SDFlags.lastCorrupt = YES; jpeg_destroy_decompress(&jpegDecompInfo.pub); return nil; } // read header and start decompression jpeg_read_header(&jpegDecompInfo.pub, TRUE); jpeg_start_decompress(&jpegDecompInfo.pub); VERBOSE { METHOD; NSLog(@"(%d x %d); %d components\n", jpegDecompInfo.pub.image_width, jpegDecompInfo.pub.image_height, jpegDecompInfo.pub.num_components); } // decode image jpegRep = [self _internalDecodeImage]; // finish decompression jpeg_finish_decompress(&jpegDecompInfo.pub); // destroy decompression context jpeg_destroy_decompress(&jpegDecompInfo.pub); VERBOSE { METHOD; NSLog(@"Image was %s corrupted.", _SDFlags.lastCorrupt ? "" : "not"); } return jpegRep; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.