Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
JPEG.c
/************************************************************************************* |
# |
# JPEG.c |
# |
# This segment handles JPEG. |
# |
# Author(s): Michael Marinkovich & Guillermo Ortiz |
# marink@apple.com |
# |
# Modification History: |
# |
# 4/3/96 MWM Initial coding |
# |
# Copyright © 1992-96 Apple Computer, Inc., All Rights Reserved |
# |
# |
# You may incorporate this sample code into your applications without |
# restriction, though the sample code has been provided "AS IS" and the |
# responsibility for its operation is 100% yours. However, what you are |
# not permitted to do is to redistribute the source as "DSC Sample Code" |
# after having made changes. If you're going to re-distribute the source, |
# we require that you make it clear in the source that the code was |
# descended from Apple Sample Code, but that you've made changes. |
# |
*************************************************************************************/ |
#include <Events.h> |
#include <ToolUtils.h> |
#include <Gestalt.h> |
#include <OSUtils.h> |
#include <Palettes.h> |
#include "App.h" |
#include "Proto.h" |
// data unload buffer size |
#define kBufferSize codecMinimumDataSize |
//---------------------------------------------------------------------- |
// |
// ReadJPEG - open a JPEG file with supplied FSSpec. |
// |
// |
//---------------------------------------------------------------------- |
OSErr ReadJPEG(FSSpec spec, CGrafPtr *newWorld) |
{ |
OSErr err = noErr; |
ImageDescriptionHandle desc; |
GWorldPtr theWorld = nil; |
GWorldPtr oldWorld; |
GDHandle oldGD; |
PixMapHandle thePix; |
ICMDataProcRecord loadProc; |
Handle image; |
Rect bounds; |
DLDataRec dataRec; |
long fileLength; |
long dataSize; |
short refNum; |
Ptr stripImage; |
GetGWorld(&oldWorld, &oldGD); |
if (newWorld != nil) |
{ |
SetCursor(*GetCursor(watchCursor)); // set the cursor to a watch while busy |
err = FSpOpenDF(&spec, fsRdWrShPerm, &refNum); |
if ( err == noErr ) |
{ |
desc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription)); |
if (desc != nil) |
{ |
HLock((Handle)desc); |
err = ReadJPEGHeader(refNum, desc, &bounds); |
if (err == noErr) |
{ |
image = NewHandleClear(kBufferSize); |
err = MemError(); |
if (image != nil && err == noErr) |
{ |
err = NewJPEGWorld(&theWorld, (*desc)->depth, bounds); |
if (theWorld != nil && err == noErr) |
{ |
err = SetFPos(refNum, fsFromStart , 0); |
if (err == noErr) |
{ |
thePix = GetGWorldPixMap(theWorld); |
LockPixels(thePix); |
SetGWorld(theWorld, nil); |
HLock(image); |
dataSize = kBufferSize; |
// make sure the filesize is greater than the buffersize |
(void)GetEOF(refNum, &fileLength); |
if (kBufferSize > fileLength) |
dataSize = fileLength; |
err = FSRead(refNum, &dataSize, *image); |
if (err == noErr) |
{ |
fileLength = (*desc)->dataSize - kBufferSize; |
if (fileLength < 0) |
fileLength = 0; |
dataRec.refNum = refNum; |
dataRec.fileLength = fileLength; |
dataRec.origPtr = *image; |
dataRec.endPtr = *image + kBufferSize; |
loadProc.dataProc = NewICMDataProc(DataLoadingProc); |
loadProc.dataRefCon = (long)&dataRec; |
stripImage = StripAddress(*image); |
err = FDecompressImage(stripImage, desc, thePix, &bounds, nil, srcCopy, nil, nil, nil, |
codecHighQuality, anyCodec, kBufferSize, &loadProc, nil); |
DisposeRoutineDescriptor(loadProc.dataProc); |
HUnlock(image); |
UnlockPixels(thePix); |
SetGWorld(oldWorld, oldGD); |
if (err == noErr) |
*newWorld = theWorld; |
} |
} |
if (err != noErr) |
DisposeGWorld(theWorld); |
} |
DisposeHandle(image); |
} |
} |
HUnlock((Handle)desc); |
DisposeHandle((Handle)desc); |
} |
FSClose(refNum); // close the file |
} |
SetCursor(&qd.arrow ); // set cursor back to arrow |
} |
else |
err = paramErr; |
return err; |
} |
//---------------------------------------------------------------------- |
// |
// ReadJPEGHeader - fill out the ImageDescription with needed info. |
// |
// |
//---------------------------------------------------------------------- |
OSErr ReadJPEGHeader(short refNum, ImageDescriptionHandle desc, Rect *bounds) |
{ |
OSErr err = noErr; |
long imageSize; |
short w = 0, h = 0; |
short skip; |
UInt8 marker; |
Boolean isJFIF = false; |
Boolean readingExtension = false; |
// set file position to beginning of file |
err = SetFPos(refNum, fsFromStart , 0); |
if (err != noErr) |
return err; |
// get file length so we don't overflow |
err = GetEOF(refNum, &imageSize); |
if (err != noErr) |
return err; |
(*desc)->dataSize = imageSize; |
while (true) // loop forever |
{ |
marker = FindNextMarker(refNum); |
switch (marker) |
{ |
case kSOIMarker: |
isJFIF = true; |
break; |
case kAPPOMarker + 0: |
case kAPPOMarker + 1: |
case kAPPOMarker + 2: |
case kAPPOMarker + 3: |
case kAPPOMarker + 4: |
case kAPPOMarker + 5: |
case kAPPOMarker + 6: |
case kAPPOMarker + 7: |
case kAPPOMarker + 8: |
case kAPPOMarker + 9: |
case kAPPOMarker + 10: |
case kAPPOMarker + 11: |
case kAPPOMarker + 12: |
case kAPPOMarker + 13: |
case kAPPOMarker + 14: |
case kAPPOMarker + 15: |
err = HandleAPPOMarker(marker, refNum, desc, &readingExtension); |
if (err != noErr) |
return err; |
break; |
case kCommentMarker: |
SkipLength(refNum); |
break; |
case kSOFMarker + 0: // start of frame header marker |
case kSOFMarker + 1: |
case kSOFMarker + 2: |
case kSOFMarker + 3: |
case kSOFMarker + 5: |
case kSOFMarker + 6: |
case kSOFMarker + 7: |
case kSOFMarker + 9: |
case kSOFMarker + 10: |
case kSOFMarker + 11: |
case kSOFMarker + 13: |
case kSOFMarker + 14: |
case kSOFMarker + 15: |
err = HandleSOFMarker(refNum, desc, readingExtension); |
if (err != noErr) |
return err; |
if (!readingExtension) |
{ |
SetRect(bounds, 0, 0, (*desc)->width, (*desc)->height); |
return noErr; |
} |
break; |
case kDACMarker: |
skip = ReadWord(refNum) - 2; |
skip *= ReadWord(refNum); |
err = SetFPos(refNum, fsFromMark, skip); |
break; |
case kSOSMarker: |
HandleSOSMarker(refNum); |
break; |
case kDHTMarker: |
case kDQTMarker: |
case kRSTOMarker: |
SkipLength(refNum); |
break; |
case kEOIMarker: // we reached the end of image |
// we are reading an extension so keep going |
if (readingExtension == true) |
readingExtension = false; |
break; |
} |
} |
return err; |
} |
//---------------------------------------------------------------------- |
// |
// FindNextMarker - |
// |
// |
//---------------------------------------------------------------------- |
UInt8 FindNextMarker(long refNum) |
{ |
UInt8 marker; |
marker = ReadByte(refNum); |
while (marker == kStartMarker) |
marker = ReadByte(refNum); |
while (marker == 0x00) |
{ |
marker = ReadByte(refNum); |
while (marker != kStartMarker) |
marker = ReadByte(refNum); |
marker = ReadByte(refNum); |
} |
return marker; |
} |
//---------------------------------------------------------------------- |
// |
// HandleAPPOMarker - |
// |
// |
//---------------------------------------------------------------------- |
OSErr HandleAPPOMarker(UInt8 marker, long refNum, ImageDescriptionHandle desc, Boolean *readingExtension) |
{ |
OSErr err = noErr; |
Fixed xRes,yRes; |
long length; |
short w = 0, h = 0; |
short units; |
short version; |
UInt8 extension; |
UInt8 JIFF[5]; |
// read skip bytes - header length - skip count |
length = ReadWord(refNum) - 2; |
if (marker == kAPPOMarker && length >= 14) |
{ |
JIFF[0] = ReadByte(refNum); |
JIFF[1] = ReadByte(refNum); |
JIFF[2] = ReadByte(refNum); |
JIFF[3] = ReadByte(refNum); |
JIFF[4] = ReadByte(refNum); |
// check if we really have the JFIF header |
if ( JIFF[0] == 'J' && JIFF[1] == 'F' && JIFF[2] == 'I' && JIFF[3] == 'F' ) |
{ |
version = ReadWord(refNum); |
if ( version < 0x100 ) |
{ |
err = paramErr; |
return err; // don't know this |
} |
else |
(*desc)->version = version; |
units = ReadByte(refNum); |
xRes = ReadWord(refNum); |
yRes = ReadWord(refNum); |
switch ( units ) |
{ |
case 0: // no res, just aspect ratio |
xRes = FixMul(72L << 16, xRes << 16); |
yRes = FixMul(72L << 16, yRes << 16); |
break; |
case 1: // dots per inch |
xRes = xRes << 16; |
yRes = yRes << 16; |
break; |
case 2: // dots per centimeter (we convert to dpi ) |
xRes = FixMul(0x28a3d, xRes << 16); |
yRes = FixMul(0x28a3d, xRes << 16); |
break; |
default: |
break; |
} |
(*desc)->hRes = xRes; |
(*desc)->vRes = yRes; |
length -= 12; |
err = SetFPos(refNum, fsFromMark, length); |
} |
else |
{ |
if ( JIFF[0] == 'J' && JIFF[1] == 'F' && JIFF[2] == 'X' && JIFF[3] == 'X' ) |
{ |
*readingExtension = true; // next markers are extensions (ignore) |
extension = ReadByte(refNum); |
switch (extension) |
{ |
case 0x10: |
case 0x11: |
case 0x13: |
break; |
default: |
err = paramErr; |
return err; |
} |
} |
} |
} |
else |
err = SetFPos(refNum, fsFromMark, length); |
return err; |
} |
//---------------------------------------------------------------------- |
// |
// HandleSOFMarker - |
// |
// |
//---------------------------------------------------------------------- |
OSErr HandleSOFMarker(long refNum, ImageDescriptionHandle desc, Boolean readingExtension) |
{ |
OSErr err = noErr; |
short w = 0; |
short h = 0; |
short components; |
short length; |
if (readingExtension == false) |
{ |
length = ReadWord(refNum); |
ReadByte(refNum); |
h = ReadWord(refNum); |
w = ReadWord(refNum); |
if (w && h) // make sure we do have something to display |
{ |
/* now make up the image description */ |
(*desc)->idSize = sizeof(ImageDescription); |
(*desc)->cType = 'jpeg'; |
(*desc)->dataRefIndex = 0; |
(*desc)->revisionLevel = 0; |
(*desc)->vendor = 0; |
(*desc)->temporalQuality = 0; |
(*desc)->spatialQuality = codecNormalQuality; |
(*desc)->width = w; |
(*desc)->height = h; |
(*desc)->frameCount = 1; |
BlockMove("\pPhoto - JPEG",(*desc)->name,13); |
(*desc)->clutID = -1; |
components = ReadByte(refNum); |
switch (components) |
{ |
case 1: |
(*desc)->depth = 40; |
break; |
case 3: |
(*desc)->depth = 32; |
break; |
case 4: |
(*desc)->depth = 32; |
break; |
default: |
err = paramErr; |
return err; |
break; |
} |
err = SetFPos(refNum, fsFromMark, length - 8); |
return noErr; |
} |
} |
else |
{ |
length = ReadWord(refNum) - 2; |
err = SetFPos(refNum, fsFromMark, length); |
if (err != noErr) |
return err; |
} |
return err; |
} |
//---------------------------------------------------------------------- |
// |
// HandleSOSMarker - |
// |
// |
//---------------------------------------------------------------------- |
void HandleSOSMarker(long refNum) |
{ |
short components; |
short c; |
ReadWord(refNum); |
components = ReadByte(refNum); |
for (c = 0; c < components; c++) |
ReadWord(refNum); |
} |
//---------------------------------------------------------------------- |
// |
// ReadByte - |
// |
// |
//---------------------------------------------------------------------- |
UInt8 ReadByte(short refNum) |
{ |
UInt8 data; |
long bytesNeeded = sizeof(char); |
(void)FSRead(refNum, &bytesNeeded, &data); |
return data; |
} |
//---------------------------------------------------------------------- |
// |
// ReadWord - |
// |
// |
//---------------------------------------------------------------------- |
UInt16 ReadWord(short refNum) |
{ |
UInt16 data; |
long bytesNeeded = sizeof(UInt16); |
(void)FSRead(refNum, &bytesNeeded, &data); |
return data; |
} |
//---------------------------------------------------------------------- |
// |
// SkipLength - |
// |
// |
//---------------------------------------------------------------------- |
void SkipLength(long refNum) |
{ |
UInt16 skip; |
skip = (ReadWord(refNum) - 2); |
SetFPos(refNum, fsFromMark, skip); |
} |
//---------------------------------------------------------------------- |
// |
// NewJPEGWorld - |
// |
// |
//---------------------------------------------------------------------- |
OSErr NewJPEGWorld(GWorldPtr *theWorld, short depth, Rect theRect) |
{ |
OSErr err = noErr; |
GWorldPtr oldPort; |
PixMapHandle thePix; |
CTabHandle cTab = nil; |
GDHandle oldGD; |
if (theWorld != nil) |
{ |
GetGWorld(&oldPort,&oldGD); |
// if depth is greater than 32 then the |
// image is grayscale |
if (depth > 32) |
{ |
cTab = GetCTable(depth); |
depth = depth - 32; |
} |
err = NewGWorld(theWorld, depth, &theRect, cTab, nil, 0L); // try our heap |
if (err != noErr) |
err = NewGWorld(theWorld, depth, &theRect, cTab, nil, useTempMem); // else try temp |
if (err == noErr && theWorld != nil) |
{ |
thePix = GetGWorldPixMap(*theWorld); |
if (LockPixels(thePix)) |
{ |
SetGWorld(*theWorld, nil); |
EraseRect(&theRect); |
UnlockPixels(thePix); |
} |
} |
SetGWorld(oldPort, oldGD); |
} |
else |
err = paramErr; |
return err; |
} |
//---------------------------------------------------------------------- |
// |
// DoSaveJPEG - |
// |
// |
//---------------------------------------------------------------------- |
OSErr SaveJPEG(WindowRef window) |
{ |
OSErr err = noErr; |
StandardFileReply reply; |
ImageDescriptionHandle desc; |
Handle data; |
Rect srcRect; |
GWorldPtr theWorld; |
PixMapHandle thePix; |
CTabHandle cTab = nil; |
ICMFlushProcRecord flushProc; |
short refNum; |
short depth; |
Str255 title; |
DocHnd doc; |
GetWTitle(window, title); |
StandardPutFile("\pSave document as:", title, &reply) ; |
doc = (DocHnd)GetWRefCon(window); |
if (reply.sfGood && doc != nil) |
{ |
desc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription)); |
theWorld = (**doc).world; |
if (theWorld != nil && desc != nil) |
{ |
srcRect = theWorld->portRect; |
thePix = GetGWorldPixMap(theWorld); |
if (LockPixels(thePix)) |
{ |
// if less than 16-bit then get the color table |
// of our GWorld |
depth = (**thePix).pixelSize; |
if (depth < 16) |
cTab = (**thePix).pmTable; |
data = NewHandle(kBufferSize); |
err = MemError(); |
if (data != nil && err == noErr) |
{ |
HLock(data); |
if (reply.sfReplacing ) |
err = FSpDelete(&reply.sfFile); |
err = FSpCreate(&reply.sfFile, 'JVWR', 'JPEG', reply.sfScript); |
if (err == noErr) |
err = FSpOpenDF(&reply.sfFile,fsRdWrPerm, &refNum); |
if (err == noErr) |
err = SetFPos(refNum, fsFromStart , 0); |
if (err == noErr) |
{ |
flushProc.flushProc = NewICMFlushProc(DataUnloadProc); |
flushProc.flushRefCon = refNum; |
err = FCompressImage(thePix, &srcRect, depth, |
codecNormalQuality, 'jpeg', |
bestCompressionCodec, cTab, |
codecFlagWasCompressed, kBufferSize, |
&flushProc, nil, desc, *data); |
} |
if (err == noErr) |
err = SetFPos(refNum, fsFromStart, (**desc).dataSize); |
if (err == noErr) |
err = SetEOF(refNum, (**desc).dataSize); |
if (err == noErr) |
err = FSClose( refNum ); |
HUnlock(data); |
DisposeHandle(data); |
DisposeRoutineDescriptor(flushProc.flushProc); |
} |
} |
UnlockPixels(thePix); |
} |
if (nil != desc) |
DisposeHandle((Handle)desc); |
} |
else |
err = memFullErr; |
return err; |
} |
//---------------------------------------------------------------------- |
// |
// DataUnloadProc - |
// |
// |
//---------------------------------------------------------------------- |
pascal OSErr DataUnloadProc(Ptr data, long bytesNeeded, long refCon) |
{ |
OSErr err = noErr; |
if (data == nil) |
{ |
// if data is nil QuickTime requests a new position in the |
// file from the current mark, offset by bytesNeeded. |
err = SetFPos(refCon, fsFromMark, bytesNeeded); |
} |
else |
{ |
err = FSWrite(refCon, &bytesNeeded, data); |
} |
return err; |
} |
//---------------------------------------------------------------------- |
// |
// DataLoadingProc - |
// |
// |
//---------------------------------------------------------------------- |
pascal OSErr DataLoadingProc(Ptr *data, long bytesNeeded, long refCon) |
{ |
OSErr err = noErr; |
long unusedDataLen; |
long newDataLen; |
DLDataPtr dataRec; |
Ptr newDataPtr; |
dataRec = (DLDataPtr)refCon; |
if (data == nil) |
{ |
err = SetFPos(dataRec->refNum, fsFromMark, bytesNeeded); |
} |
else |
{ |
newDataPtr = *data; |
// if QT requests more data than is in the buffer |
// we will have to load more. |
if ((newDataPtr + bytesNeeded) >= dataRec->endPtr) |
{ |
// move whats left up to the front of the buffer |
unusedDataLen = dataRec->endPtr - newDataPtr; |
BlockMove(newDataPtr,dataRec->origPtr, unusedDataLen); |
// now fill the buffer with new data...after the |
// data we moved to the front of the buffer of course. |
newDataLen = kBufferSize - unusedDataLen; |
if (newDataLen > dataRec->fileLength) |
newDataLen = dataRec->fileLength; |
newDataPtr = dataRec->origPtr + unusedDataLen; |
err = FSRead(dataRec->refNum, &newDataLen, newDataPtr); |
dataRec->fileLength -= newDataLen; |
*data = dataRec->origPtr; |
} |
} |
return err; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14