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
40 KiB
C
1 line
40 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"
|
|
#include <stddef.h>
|
|
#elif applec
|
|
#pragma load ":Headers:MPW.Header"
|
|
#elif __MWERKS__
|
|
//#include "MW.Header"
|
|
#else
|
|
#include "JPEGView.h"
|
|
#endif
|
|
|
|
//=====================================================================================
|
|
// Includes specific to this module
|
|
//=====================================================================================
|
|
|
|
#include "TIFF.h"
|
|
#include "TIFFTables.c"
|
|
|
|
//=====================================================================================
|
|
// TIFFPrivates: private data structure for TIFF images, maintained for each image.
|
|
//=====================================================================================
|
|
|
|
typedef struct TIFFPrivates TIFFPrivates, *TIFFPrivatesPtr, **TIFFPrivatesHandle;
|
|
typedef (*ExtractProcPtr)(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
|
|
|
|
struct TIFFPrivates
|
|
{
|
|
Boolean offsetsAreShort; // flag: true if the strip offsets are shorts
|
|
ushort photoInt; // photometric interpretation
|
|
ushort compression; // compression method used
|
|
ushort bitsPerSample; // number of bits per sample (must be same for all)
|
|
ushort samplesPerPixel; // number of samples per pixel (only first 3 count)
|
|
ushort depth; // calculated depth of the image
|
|
ulong height, width; // size of the image
|
|
ulong rowsPerStrip; // number of rows per strip
|
|
ulong stripOffsetsList; // offset in data of list of strip offsets
|
|
ulong colorTableOffset; // offset of the color table in the file
|
|
CTabHandle decompColors; // color table to be used in decompression
|
|
Boolean invert; // flag: true to invert all outgoing bytes
|
|
Boolean prediction; // flag: true if we are decoding with prediction
|
|
ExtractProcPtr extract; // pointer to extract function
|
|
};
|
|
|
|
//=====================================================================================
|
|
// Constants specific to this module
|
|
//=====================================================================================
|
|
|
|
enum {
|
|
tagBitsPerSample = 0x102, // [short], default = 1
|
|
tagColorMap = 0x140, // [short], required for palette images
|
|
tagCompression = 0x103, // short, default = 1
|
|
compNone = 1,
|
|
compModCCITT3 = 2,
|
|
compCCITT3 = 3,
|
|
compLZW = 5,
|
|
compPackBits = 32773,
|
|
tagFillOrder = 0x10a, // fill order, must be 1
|
|
tagImageDescription = 0x13c, // [ASCII], optional
|
|
tagImageLength = 0x101, // short or long, required
|
|
tagImageWidth = 0x100, // short or long, required
|
|
tagPhotometricInterpretation = 0x106,// short, required
|
|
photoWhiteIsZero = 0,
|
|
photoBlackIsZero = 1,
|
|
photoRGB = 2,
|
|
photoPalette = 3,
|
|
tagOrientation = 0x112, // orientation, must be 1
|
|
tagPlanarConfiguration = 0x11c, // planar configuration, must be 1
|
|
tagPredictor = 0x13d, // predictor, must be 1
|
|
tagRowsPerStrip = 0x116, // short or long, default = 16777215
|
|
tagSamplesPerPixel = 0x115, // short, default = 1
|
|
tagStripOffsets = 0x111 // [short or long], required
|
|
};
|
|
|
|
enum {
|
|
typeByte = 1,
|
|
typeASCII = 2,
|
|
typeShort = 3,
|
|
typeLong = 4,
|
|
typeRational = 5
|
|
};
|
|
|
|
enum {
|
|
errAllDone = 1
|
|
};
|
|
|
|
//=====================================================================================
|
|
// Global variables local to this module
|
|
//=====================================================================================
|
|
|
|
static TIFFPrivates gTIFFDefaults = {
|
|
false, // offsets are long
|
|
0, // no photometric interpretation default
|
|
compNone, // no compression
|
|
1, // 1 bit per sample
|
|
1, // 1 sample per pixel
|
|
1, // depth is 1 bit
|
|
0, 0, // height and width are 0
|
|
0, // 0 rows per strip (fixed at runtime for def.)
|
|
0, // a nil strip offsets list
|
|
0, // and a nil color table offset
|
|
nil, // nil handle for the image color table
|
|
false, // don't invert
|
|
false, // no prediction
|
|
nil // no default decoding function
|
|
};
|
|
|
|
static long gTotalLines, gCurrentLines;
|
|
static uchar *gStack, *gTree;
|
|
|
|
//=====================================================================================
|
|
// Prototypes for functions local to this module
|
|
//=====================================================================================
|
|
|
|
static OSErr ProcessTag(uchar *dataStart, ulong offset, ImageHandle theImage);
|
|
static CTabHandle MakeBlackAndWhiteCTable(ulong pi, short depth);
|
|
static CTabHandle ExtractColorTable(uchar *dataStart, ulong offset, short depth);
|
|
static OSErr SetUpCompression(TIFFPrivatesHandle privates, ImageHandle theImage);
|
|
static OSErr DecompressTIFFImage(uchar *dataStart, TIFFPrivatesHandle privates,
|
|
PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length);
|
|
static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow);
|
|
static OSErr ExtractUncompressed(uchar **srcData, uchar *dstData,
|
|
TIFFPrivatesHandle privates);
|
|
static OSErr ExtractCCITT3(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
|
|
static OSErr ExtractPackBits(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
|
|
static OSErr ExtractLZW(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
|
|
static OSErr ExtractUncompressedRGB(uchar **srcData, uchar *dstData,
|
|
TIFFPrivatesHandle privates);
|
|
static OSErr ExtractPackBitsRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
|
|
static OSErr ExtractLZWRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
|
|
static ulong GetOneUnsigned(uchar *data, long offset);
|
|
static ulong GetByte(uchar *data, long offset);
|
|
static ulong GetShort(uchar *data, long offset);
|
|
static ulong GetLong(uchar *data, long offset);
|
|
static Fixed GetRational(uchar *data, long offset);
|
|
|
|
//=====================================================================================
|
|
// Boolean idTIFF(uchar *theData, long theSize)
|
|
//=====================================================================================
|
|
// Examines the given data and attempts to identify it as a TIFF image.
|
|
//=====================================================================================
|
|
|
|
extern Boolean idTIFF(uchar *theData, long theSize, short refNum, FSSpec *theSpec)
|
|
{
|
|
#if applec
|
|
#pragma unused(refNum, theSpec)
|
|
#endif
|
|
return (theSize > 4 &&
|
|
(theData[0] == 'I' && theData[1] == 'I' && theData[2] == 42 && theData[3] == 0) ||
|
|
(theData[0] == 'M' && theData[1] == 'M' && theData[2] == 0 && theData[3] == 42));
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr OpenTIFF(ImageHandle theImage)
|
|
//=====================================================================================
|
|
// Initializes the image record for a TIFF image; note that since we are a simple
|
|
// baseline TIFF reader, we only read the first set of tags.
|
|
//=====================================================================================
|
|
|
|
extern OSErr OpenTIFF(ImageHandle theImage)
|
|
{
|
|
long theLen = GetHandleSize((*theImage)->data);
|
|
char hState = HGetState((*theImage)->data);
|
|
TIFFPrivatesHandle privates;
|
|
OSErr theErr = noErr;
|
|
ulong offset, count;
|
|
uchar *dataStart;
|
|
|
|
theErr = PtrToHand(&gTIFFDefaults, (Handle *)&privates, sizeof(TIFFPrivates));
|
|
if (theErr != noErr) return theErr;
|
|
(*theImage)->privateData = (Handle)privates;
|
|
HLock((*theImage)->data);
|
|
dataStart = (uchar *)*(*theImage)->data;
|
|
offset = GetLong(dataStart, 4);
|
|
count = GetShort(dataStart, offset), offset += 2;
|
|
while (count-- && theErr == noErr) {
|
|
theErr = ProcessTag(dataStart, offset, theImage);
|
|
offset += 12;
|
|
if (offset > theLen) theErr = codecBadDataErr;
|
|
}
|
|
if (theErr == noErr) {
|
|
if ((*privates)->photoInt == photoWhiteIsZero || (*privates)->photoInt == photoBlackIsZero) {
|
|
(*privates)->depth = (*theImage)->depth = (*privates)->bitsPerSample;
|
|
if ((*theImage)->depth != 1 && (*theImage)->depth != 4 && (*theImage)->depth != 8)
|
|
gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
|
|
else if ((*theImage)->depth == 1)
|
|
(*privates)->invert = ((*privates)->photoInt == photoBlackIsZero);
|
|
else {
|
|
(*privates)->decompColors = MakeBlackAndWhiteCTable((*privates)->photoInt,
|
|
(*theImage)->depth);
|
|
if (!(*privates)->decompColors) theErr = memFullErr;
|
|
}
|
|
} else if ((*privates)->photoInt == photoRGB) {
|
|
if ((*privates)->bitsPerSample != 8)
|
|
gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
|
|
(*privates)->depth = (*theImage)->depth = 32;
|
|
} else if ((*privates)->photoInt == photoPalette) {
|
|
(*privates)->depth = (*theImage)->depth = (*privates)->bitsPerSample;
|
|
if ((*theImage)->depth != 4 && (*theImage)->depth != 8)
|
|
gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
|
|
if (!(*privates)->colorTableOffset) theErr = codecBadDataErr;
|
|
(*privates)->decompColors = ExtractColorTable(dataStart,
|
|
(*privates)->colorTableOffset, (*theImage)->depth);
|
|
if (!(*privates)->decompColors) theErr = memFullErr;
|
|
} else gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
|
|
if (!(*theImage)->depth) theErr = codecBadDataErr;
|
|
if (!(*privates)->stripOffsetsList)
|
|
gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
|
|
if ((*privates)->compression == compModCCITT3 && (*theImage)->depth != 1)
|
|
theErr = codecBadDataErr;
|
|
if (!Width(&(*theImage)->grect) || !Height(&(*theImage)->grect))
|
|
theErr = codecBadDataErr;
|
|
if (theErr == noErr) theErr = SetUpCompression(privates, theImage);
|
|
if (!(*privates)->rowsPerStrip || (*privates)->rowsPerStrip > (*privates)->height)
|
|
(*privates)->rowsPerStrip = (*privates)->height;
|
|
if ((*privates)->decompColors)
|
|
(*theImage)->ipalette = NewPalette(256, (*privates)->decompColors, pmTolerant, 0);
|
|
(*theImage)->qrect = (*theImage)->crect = (*theImage)->grect;
|
|
}
|
|
HSetState((*theImage)->data, hState);
|
|
return theErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr DrawTIFF(Handle theHandle, JVDrawParamsHandle theParams)
|
|
//=====================================================================================
|
|
// Draws a TIFF image into the specified GWorld, from the srcRect into the dstRect,
|
|
// clipping as dstRgn specifies.
|
|
//=====================================================================================
|
|
|
|
extern OSErr DrawTIFF(Handle theHandle, JVDrawParamsHandle theParams)
|
|
{
|
|
TIFFPrivatesHandle privates = (TIFFPrivatesHandle)(*theParams)->privateData;
|
|
NestedProgress theProgress = (*theParams)->progress;
|
|
char hState = HGetState(theHandle);
|
|
Handle lzwDataHandle = nil;
|
|
OSErr theErr = noErr;
|
|
GWorldPtr theGWorld;
|
|
uchar *dataStart;
|
|
|
|
if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc,
|
|
codecProgressUpdatePercent, 0x00000000L, theProgress.prog.progressRefCon);
|
|
HLockHi(theHandle);
|
|
if ((*privates)->compression == compLZW) {
|
|
lzwDataHandle = NewHandleClear(4096 + 4096 * 4);
|
|
if (!lzwDataHandle) lzwDataHandle = TempNewHandle(4096 + 4096 * 4, &theErr);
|
|
if (lzwDataHandle && theErr == noErr) {
|
|
HLockHi(lzwDataHandle);
|
|
gStack = (uchar *)StripAddress(*lzwDataHandle);
|
|
gTree = gStack + 4096;
|
|
}
|
|
}
|
|
if (theErr == noErr) {
|
|
dataStart = (uchar *)StripAddress(*theHandle);
|
|
theGWorld = NewTempGWorld((*privates)->width, (*privates)->height, (*privates)->depth,
|
|
(*privates)->decompColors);
|
|
if (theGWorld) {
|
|
theErr = DecompressTIFFImage(dataStart, privates, GetGWorldPixMap(theGWorld),
|
|
(ICMProgressProcRecordPtr)&theProgress, GetHandleSize(theHandle));
|
|
if ((*theParams)->progress.aborted) theErr = codecAbortErr;
|
|
DisposeGWorld(theGWorld);
|
|
} else gIntError = errNoDrawMemory, theErr = memFullErr;
|
|
}
|
|
if (lzwDataHandle) DisposeHandle(lzwDataHandle);
|
|
HSetState(theHandle, hState);
|
|
if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc,
|
|
codecProgressUpdatePercent, 0x00010000L, theProgress.prog.progressRefCon);
|
|
return theErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr CloneTIFF(ImageHandle origImage, ImageHandle newImage)
|
|
//=====================================================================================
|
|
// Called when an image is cloned, to give us a chance to duplicate any secondary
|
|
// storage.
|
|
//=====================================================================================
|
|
|
|
extern OSErr CloneTIFF(ImageHandle origImage, ImageHandle newImage)
|
|
{
|
|
TIFFPrivatesHandle origPrivates = (TIFFPrivatesHandle)(*origImage)->privateData;
|
|
TIFFPrivatesHandle newPrivates = (TIFFPrivatesHandle)(*newImage)->privateData;
|
|
CTabHandle theColors;
|
|
OSErr theErr;
|
|
|
|
if (origPrivates && newPrivates) {
|
|
if (theColors = (*origPrivates)->decompColors) {
|
|
theErr = HandToHand((Handle *)&theColors);
|
|
(*newPrivates)->decompColors = theColors;
|
|
}
|
|
}
|
|
return theErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr CloseTIFF(ImageHandle theImage)
|
|
//=====================================================================================
|
|
// Called before the image is disposed, to get rid of any secondary storage we
|
|
// allocated on open.
|
|
//=====================================================================================
|
|
|
|
extern OSErr CloseTIFF(ImageHandle theImage)
|
|
{
|
|
TIFFPrivatesHandle privates = (TIFFPrivatesHandle)(*theImage)->privateData;
|
|
|
|
if (privates)
|
|
if ((*privates)->decompColors) DisposeHandle((Handle)(*privates)->decompColors);
|
|
return noErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr ProcessTag(uchar *dataStart, ulong offset, ImageHandle theImage)
|
|
//=====================================================================================
|
|
// Process the TIFF tag pointed to by offset. If it's something we recognize, we
|
|
// attempt to fill in the appropriate data structures in the image record, or in the
|
|
// attached TIFF private data.
|
|
//=====================================================================================
|
|
|
|
static OSErr ProcessTag(uchar *dataStart, ulong offset, ImageHandle theImage)
|
|
{
|
|
TIFFPrivatesHandle privates = (TIFFPrivatesHandle)(*theImage)->privateData;
|
|
ushort tag = GetShort(dataStart, offset);
|
|
OSErr theErr = noErr;
|
|
ulong data;
|
|
|
|
switch (tag) {
|
|
case tagImageWidth:
|
|
(*privates)->width = (*theImage)->grect.right =
|
|
GetOneUnsigned(dataStart, offset + 2);
|
|
break;
|
|
|
|
case tagImageLength:
|
|
(*privates)->height = (*theImage)->grect.bottom =
|
|
GetOneUnsigned(dataStart, offset + 2);
|
|
break;
|
|
|
|
case tagBitsPerSample:
|
|
(*privates)->bitsPerSample = GetOneUnsigned(dataStart, offset + 2);
|
|
break;
|
|
|
|
case tagCompression:
|
|
(*privates)->compression = GetOneUnsigned(dataStart, offset + 2);
|
|
break;
|
|
|
|
case tagPhotometricInterpretation:
|
|
(*privates)->photoInt = GetOneUnsigned(dataStart, offset + 2);
|
|
break;
|
|
|
|
case tagStripOffsets:
|
|
data = GetShort(dataStart, offset + 2);
|
|
if (data == typeShort) (*privates)->offsetsAreShort = true;
|
|
else if (data == typeLong) (*privates)->offsetsAreShort = false;
|
|
else return codecBadDataErr;
|
|
data = GetLong(dataStart, offset + 4);
|
|
if (data == 1 || (data == 2 && (*privates)->offsetsAreShort))
|
|
(*privates)->stripOffsetsList = offset + 8;
|
|
else (*privates)->stripOffsetsList = GetLong(dataStart, offset + 8);
|
|
break;
|
|
|
|
case tagSamplesPerPixel:
|
|
(*privates)->samplesPerPixel = GetOneUnsigned(dataStart, offset + 2);
|
|
break;
|
|
|
|
case tagRowsPerStrip:
|
|
(*privates)->rowsPerStrip = GetOneUnsigned(dataStart, offset + 2);
|
|
break;
|
|
|
|
case tagImageDescription:
|
|
if (GetShort(dataStart, offset + 2) != typeASCII) return codecBadDataErr;
|
|
data = GetLong(dataStart, offset + 4);
|
|
if (data <= 4) offset += 8;
|
|
else offset = GetLong(dataStart, offset + 8);
|
|
if (!(*theImage)->comments)
|
|
theErr = PtrToHand(dataStart + offset, &(*theImage)->comments, data - 1);
|
|
break;
|
|
|
|
case tagColorMap:
|
|
if (GetShort(dataStart, offset + 2) != typeShort) return codecBadDataErr;
|
|
(*privates)->colorTableOffset = offset + 4;
|
|
break;
|
|
|
|
case tagPredictor:
|
|
data = GetOneUnsigned(dataStart, offset + 2);
|
|
if (data == 1) (*privates)->prediction = false;
|
|
else if (data == 2) (*privates)->prediction = true;
|
|
else gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
|
|
break;
|
|
|
|
case tagFillOrder:
|
|
case tagOrientation:
|
|
case tagPlanarConfiguration:
|
|
if (GetOneUnsigned(dataStart, offset + 2) != 1)
|
|
gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
|
|
}
|
|
return theErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// CTabHandle MakeBlackAndWhiteCTable(ulong pi, short depth)
|
|
//=====================================================================================
|
|
// Creates a black & white/grayscale color table, according to the given
|
|
// photometric interpretation. If pi says that white is zero, we go from brightest to
|
|
// darkest in even steps for the entire color table size; otherwise, we go from
|
|
// darkest to brightest.
|
|
//=====================================================================================
|
|
|
|
static CTabHandle MakeBlackAndWhiteCTable(ulong pi, short depth)
|
|
{
|
|
ushort color, count = (1L << depth), 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);
|
|
if (pi == photoWhiteIsZero) color = 65535L - color;
|
|
(*theColors)->ctTable[i].rgb.red = (*theColors)->ctTable[i].rgb.green =
|
|
(*theColors)->ctTable[i].rgb.blue = color;
|
|
}
|
|
}
|
|
return theColors;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// CTabHandle ExtractColorTable(uchar *dataStart, ulong offset, short depth)
|
|
//=====================================================================================
|
|
// Extracts the included TIFF color table from the data at the given offset.
|
|
//=====================================================================================
|
|
|
|
static CTabHandle ExtractColorTable(uchar *dataStart, ulong offset, short depth)
|
|
{
|
|
ulong count = GetLong(dataStart, offset) / 3, size = (1L << depth), i;
|
|
CTabHandle theColors;
|
|
|
|
if ((depth != 4 && depth != 8) || count > size) return nil;
|
|
theColors = (CTabHandle)NewHandleClear(sizeof(ColorTable) + (size - 1) * sizeof(ColorSpec));
|
|
offset = GetLong(dataStart, offset + 4);
|
|
if (theColors) {
|
|
(*theColors)->ctSeed = GetCTSeed();
|
|
(*theColors)->ctFlags = 0x8000;
|
|
(*theColors)->ctSize = size;
|
|
for (i = 0; i < count; i++)
|
|
(*theColors)->ctTable[i].rgb.red = GetShort(dataStart, offset), offset += 2;
|
|
for (i = 0; i < count; i++)
|
|
(*theColors)->ctTable[i].rgb.green = GetShort(dataStart, offset), offset += 2;
|
|
for (i = 0; i < count; i++)
|
|
(*theColors)->ctTable[i].rgb.blue = GetShort(dataStart, offset), offset += 2;
|
|
}
|
|
return theColors;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr SetUpCompression(TIFFPrivatesHandle privates, ImageHandle theImage)
|
|
//=====================================================================================
|
|
// Sets up the compression parameters for the TIFF image.
|
|
//=====================================================================================
|
|
|
|
static OSErr SetUpCompression(TIFFPrivatesHandle privates, ImageHandle theImage)
|
|
{
|
|
OSErr theErr = noErr;
|
|
|
|
switch ((*privates)->compression) {
|
|
case compNone:
|
|
(*theImage)->compression = 0;
|
|
BlockMove(gString[strUncompressed], (*theImage)->compressionDesc, *gString[strUncompressed] + 1);
|
|
(*privates)->extract = ((*privates)->samplesPerPixel == 1) ?
|
|
(ExtractProcPtr)ExtractUncompressed : (ExtractProcPtr)ExtractUncompressedRGB;
|
|
break;
|
|
|
|
// case compCCITT3:
|
|
case compModCCITT3:
|
|
(*theImage)->compression = kCCITTCompression;
|
|
BlockMove(gString[strCCITT3], (*theImage)->compressionDesc, *gString[strCCITT3] + 1);
|
|
(*privates)->extract = (ExtractProcPtr)ExtractCCITT3;
|
|
break;
|
|
|
|
case compPackBits:
|
|
(*theImage)->compression = kPackBitsCompression;
|
|
BlockMove(gString[strPackBits], (*theImage)->compressionDesc, *gString[strPackBits] + 1);
|
|
(*privates)->extract = ((*privates)->samplesPerPixel == 1) ?
|
|
(ExtractProcPtr)ExtractPackBits : (ExtractProcPtr)ExtractPackBitsRGB;
|
|
break;
|
|
|
|
case compLZW:
|
|
(*theImage)->compression = kLZWCompression;
|
|
if ((*privates)->prediction)
|
|
BlockMove(gString[strLZWPredict], (*theImage)->compressionDesc, *gString[strLZWPredict] + 1);
|
|
else BlockMove(gString[strLZW], (*theImage)->compressionDesc, *gString[strLZW] + 1);
|
|
(*privates)->extract = ((*privates)->samplesPerPixel == 1) ?
|
|
(ExtractProcPtr)ExtractLZW : (ExtractProcPtr)ExtractLZWRGB;
|
|
break;
|
|
|
|
default:
|
|
gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
|
|
break;
|
|
}
|
|
return theErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr DecompressTIFFImage(uchar *dataStart, TIFFPrivatesHandle privates,
|
|
// PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length)
|
|
//=====================================================================================
|
|
// Oversees the decompression of a TIFF image, handling strip decoding and banding, and
|
|
// calling the appropriate function to perform the actual decompression.
|
|
//=====================================================================================
|
|
|
|
static OSErr DecompressTIFFImage(uchar *dataStart, TIFFPrivatesHandle privates,
|
|
PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length)
|
|
{
|
|
ulong numStrips = (((*privates)->height + (*privates)->rowsPerStrip - 1) /
|
|
(*privates)->rowsPerStrip);
|
|
ulong row, strip, offset, offsetOffset = (*privates)->stripOffsetsList;
|
|
uchar *ptr, *rowStart, *dataEnd = dataStart + length;
|
|
char pState = GetPixelsState(dstPixMap);
|
|
RgnHandle dstRgn = qd.thePort->visRgn;
|
|
ulong totalRows = 0, bandRow = 0;
|
|
char mmuMode = true32b;
|
|
OSErr theErr = noErr;
|
|
|
|
LockPixels(dstPixMap);
|
|
rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
|
|
for (strip = 0; strip < numStrips && totalRows < (*privates)->height; strip++) {
|
|
if ((*privates)->offsetsAreShort)
|
|
offset = GetShort(dataStart, offsetOffset), offsetOffset += 2;
|
|
else offset = GetLong(dataStart, offsetOffset), offsetOffset += 4;
|
|
ptr = dataStart + offset;
|
|
for (row = 0; row < (*privates)->rowsPerStrip && totalRows < (*privates)->height; row++) {
|
|
if (ptr < dataStart || ptr > dataEnd) {
|
|
theErr = codecBadDataErr;
|
|
break;
|
|
}
|
|
SwapMMUMode(&mmuMode);
|
|
if (row == 0) (*privates)->extract(nil, nil, privates);
|
|
theErr = (*privates)->extract(&ptr, rowStart, privates);
|
|
SwapMMUMode(&mmuMode);
|
|
rowStart += ((*dstPixMap)->rowBytes & 0x3fff);
|
|
if (theErr == noErr && !(++totalRows & 63) && prog->progressProc)
|
|
theErr = (OSErr)CallICMProgressProc(prog->progressProc,
|
|
codecProgressUpdatePercent,
|
|
FixRatio(totalRows, (*privates)->height), prog->progressRefCon);
|
|
if (++bandRow == Height(&(*dstPixMap)->bounds)) {
|
|
CopyPixMapToDestination(dstPixMap, totalRows - bandRow);
|
|
bandRow = 0;
|
|
rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
|
|
}
|
|
if (theErr != noErr) break;
|
|
}
|
|
if (theErr != noErr) break;
|
|
}
|
|
if (theErr == errAllDone) theErr = noErr;
|
|
if (theErr == noErr && bandRow)
|
|
CopyPixMapToDestination(dstPixMap, totalRows - bandRow);
|
|
SetPixelsState(dstPixMap, pState);
|
|
return theErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 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);
|
|
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);
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr ExtractUncompressed(uchar **srcData, uchar *dstData,
|
|
// TIFFPrivatesHandle privates)
|
|
//=====================================================================================
|
|
// Pulls out a row of uncompressed 1, 4, or 8 bit data.
|
|
//=====================================================================================
|
|
|
|
static OSErr ExtractUncompressed(uchar **srcData, uchar *dstData,
|
|
TIFFPrivatesHandle privates)
|
|
{
|
|
ulong bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
|
|
uchar *src = *srcData;
|
|
|
|
if (srcData == nil && dstData == nil) return noErr;
|
|
if ((*privates)->invert) while (bytes--) *dstData++ = ~(*src++);
|
|
else while (bytes--) *dstData++ = *src++;
|
|
*srcData = src;
|
|
return noErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr ExtractUncompressedRGB(uchar **srcData, uchar *dstData,
|
|
// TIFFPrivatesHandle privates)
|
|
//=====================================================================================
|
|
// Pulls out a row of uncompressed RGB data, skipping the alpha channel in the
|
|
// destination and ignoring anything other than the first 3 samples for each pixel.
|
|
//=====================================================================================
|
|
|
|
static OSErr ExtractUncompressedRGB(uchar **srcData, uchar *dstData,
|
|
TIFFPrivatesHandle privates)
|
|
{
|
|
ushort extra = (*privates)->samplesPerPixel - 3;
|
|
ulong col = (*privates)->width;
|
|
uchar *src = *srcData;
|
|
|
|
if (srcData == nil && dstData == nil) return noErr;
|
|
while (col--) {
|
|
dstData++, *dstData++ = *src++, *dstData++ = *src++, *dstData++ = *src++;
|
|
src += extra;
|
|
}
|
|
*srcData = src;
|
|
return noErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr ExtractCCITT3(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
//=====================================================================================
|
|
// Pulls out a row of modified Huffman CCITT-compressed 1-bit data.
|
|
//=====================================================================================
|
|
|
|
static OSErr ExtractCCITT3(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
{
|
|
static ushort gBitBuffer, gBitsInBuffer;
|
|
Boolean white = true, nextWhite = true, invert = ((*privates)->invert) ? true : false;
|
|
Boolean wasTerminating = true;
|
|
uchar *src = *srcData, dstMask = 0xff;
|
|
long width = (*privates)->width;
|
|
short count, dstBits = 8;
|
|
ExtractTable *code;
|
|
|
|
if (srcData == nil && dstData == nil) {
|
|
gBitBuffer = gBitsInBuffer = 0;
|
|
return noErr;
|
|
}
|
|
while (width || !wasTerminating) {
|
|
if (gBitsInBuffer < 8) {
|
|
gBitBuffer |= (ushort)*src++ << (8 - gBitsInBuffer);
|
|
gBitsInBuffer += 8;
|
|
}
|
|
code = (white) ? &whiteExtract[gBitBuffer >> 8] : &blackExtract[gBitBuffer >> 8];
|
|
if (code->leftover < 0) {
|
|
gBitBuffer <<= 8;
|
|
gBitsInBuffer -= 8;
|
|
if (gBitsInBuffer < 8) {
|
|
gBitBuffer |= (ushort)*src++ << (8 - gBitsInBuffer);
|
|
gBitsInBuffer += 8;
|
|
}
|
|
count = -code->leftover;
|
|
code = (white) ? &whiteExtract[code->count + (gBitBuffer >> (16 - count))] :
|
|
&blackExtract[code->count + (gBitBuffer >> (16 - count))];
|
|
gBitBuffer <<= (count - code->leftover);
|
|
gBitsInBuffer -= (count - code->leftover);
|
|
} else {
|
|
gBitBuffer <<= (8 - code->leftover);
|
|
gBitsInBuffer -= (8 - code->leftover);
|
|
}
|
|
count = code->count;
|
|
if (count == -1) {
|
|
return codecBadDataErr;
|
|
}
|
|
else if (count == -2) continue;
|
|
if ((width -= count) < 0) {
|
|
return codecBadDataErr;
|
|
}
|
|
if (wasTerminating = count < 64) nextWhite = !white;
|
|
if (count >= dstBits) {
|
|
if (white ^ invert) *dstData++ &= ~dstMask;
|
|
else *dstData++ |= dstMask;
|
|
count -= dstBits;
|
|
while (count >= 8) {
|
|
*dstData++ = (white ^ invert) ? 0 : 0xff;
|
|
count -= 8;
|
|
}
|
|
dstBits = 8;
|
|
dstMask = 0xff;
|
|
}
|
|
if (count) {
|
|
if (white ^ invert) *dstData &= ~(dstMask ^ (dstMask >> count));
|
|
else *dstData |= (dstMask ^ (dstMask >> count));
|
|
dstMask >>= count;
|
|
dstBits -= count;
|
|
}
|
|
white = nextWhite;
|
|
}
|
|
if ((*privates)->compression == compModCCITT3) {
|
|
while (gBitsInBuffer >= 8) src--, gBitsInBuffer -= 8;
|
|
gBitsInBuffer = gBitBuffer = 0;
|
|
}
|
|
*srcData = src;
|
|
return noErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr ExtractPackBits(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
//=====================================================================================
|
|
// Pulls out a row of PackBits-compressed 1, 4, or 8 bit data.
|
|
//=====================================================================================
|
|
|
|
static OSErr ExtractPackBits(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
{
|
|
long bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
|
|
Boolean invert = (*privates)->invert;
|
|
uchar *src = *srcData, value;
|
|
signed short count;
|
|
|
|
if (srcData == nil && dstData == nil) return noErr;
|
|
while (bytes) {
|
|
count = (signed char)*src++;
|
|
if (count < 0) {
|
|
value = (invert) ? ~(*src++) : *src++;
|
|
count = -count + 1;
|
|
if ((bytes -= count) < 0) return codecBadDataErr;
|
|
while (count--) *dstData++ = value;
|
|
} else {
|
|
count++;
|
|
if ((bytes -= count) < 0) return codecBadDataErr;
|
|
if (invert) while (count--) *dstData++ = ~(*src++);
|
|
else while (count--) *dstData++ = *src++;
|
|
}
|
|
}
|
|
*srcData = src;
|
|
return noErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr ExtractPackBitsRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
//=====================================================================================
|
|
// Pulls out a row of PackBits-compressed RGB data, skipping the alpha channel in the
|
|
// destination and ignoring anything other than the first 3 samples for each pixel.
|
|
//=====================================================================================
|
|
|
|
static OSErr ExtractPackBitsRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
{
|
|
long bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
|
|
ushort extra = (*privates)->samplesPerPixel - 3, sample = 0;
|
|
Boolean invert = (*privates)->invert;
|
|
uchar *src = *srcData, value;
|
|
signed short count;
|
|
|
|
if (srcData == nil && dstData == nil) return noErr;
|
|
bytes *= (*privates)->samplesPerPixel;
|
|
while (bytes) {
|
|
count = (signed char)*src++;
|
|
if (count < 0) {
|
|
value = *src++;
|
|
count = -count + 1;
|
|
if ((bytes -= count) < 0) return codecBadDataErr;
|
|
while (count--) {
|
|
if (!sample) dstData++;
|
|
if (sample < 3) *dstData++ = value;
|
|
if (++sample == (*privates)->samplesPerPixel) sample = 0;
|
|
}
|
|
} else {
|
|
count++;
|
|
if ((bytes -= count) < 0) return codecBadDataErr;
|
|
while (count--) {
|
|
if (!sample) dstData++;
|
|
if (sample < 3) *dstData++ = *src++;
|
|
if (++sample == (*privates)->samplesPerPixel) sample = 0;
|
|
}
|
|
}
|
|
}
|
|
*srcData = src;
|
|
return noErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr ExtractLZW(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
//=====================================================================================
|
|
// Pulls out a row of LZW-compressed 1, 4, or 8 bit data.
|
|
//=====================================================================================
|
|
|
|
static OSErr ExtractLZW(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
{
|
|
#define gClearCode 256
|
|
#define gEndCode 257
|
|
static ushort gTreeSize, gCodeSize, gFirstCode, gLastCode;
|
|
static ulong gBitBuffer, gBitsInBuffer;
|
|
static uchar *gStackPtr;
|
|
ushort *parent = (ushort *)gTree;
|
|
uchar *code = (uchar *)(parent + 4096);
|
|
short i, j, currentCode, oldCode;
|
|
ulong bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
|
|
uchar *src;
|
|
|
|
if (srcData == nil && dstData == nil) {
|
|
gStackPtr = gStack;
|
|
gCodeSize = 9;
|
|
gBitsInBuffer = gBitBuffer = 0;
|
|
gTreeSize = gEndCode + 1;
|
|
return noErr;
|
|
}
|
|
src = *srcData;
|
|
for (i = 0; i < bytes; i++) {
|
|
if (gStackPtr != gStack) {
|
|
if ((*privates)->prediction && i)
|
|
*dstData = dstData[-1] + *(--gStackPtr), dstData++;
|
|
else *dstData++ = *(--gStackPtr);
|
|
continue;
|
|
}
|
|
while (gCodeSize > gBitsInBuffer) {
|
|
gBitBuffer |= (ulong)*src++ << (24 - gBitsInBuffer);
|
|
gBitsInBuffer += 8;
|
|
}
|
|
currentCode = gBitBuffer >> (32 - gCodeSize);
|
|
gBitBuffer <<= gCodeSize;
|
|
gBitsInBuffer -= gCodeSize;
|
|
if (currentCode == gEndCode) return errAllDone;
|
|
else if (currentCode == gClearCode) {
|
|
gCodeSize = 9;
|
|
gTreeSize = gEndCode + 1;
|
|
do {
|
|
while (gCodeSize > gBitsInBuffer) {
|
|
gBitBuffer |= (ulong)*src++ << (24 - gBitsInBuffer);
|
|
gBitsInBuffer += 8;
|
|
}
|
|
currentCode = gBitBuffer >> (32 - gCodeSize);
|
|
gBitBuffer <<= gCodeSize;
|
|
gBitsInBuffer -= gCodeSize;
|
|
} while (currentCode == gClearCode);
|
|
if ((*privates)->prediction && i)
|
|
*dstData = dstData[-1] + currentCode, dstData++;
|
|
else *dstData++ = currentCode;
|
|
gFirstCode = gLastCode = currentCode;
|
|
continue;
|
|
}
|
|
oldCode = currentCode;
|
|
if (currentCode > gTreeSize) return codecBadDataErr;
|
|
else if (currentCode == gTreeSize) {
|
|
*gStackPtr++ = gFirstCode;
|
|
currentCode = gLastCode;
|
|
}
|
|
for (j = currentCode; j >= gClearCode; j = parent[j]) *gStackPtr++ = code[j];
|
|
if ((*privates)->prediction && i)
|
|
*dstData = dstData[-1] + j, dstData++;
|
|
else *dstData++ = j;
|
|
gFirstCode = j;
|
|
if (gTreeSize != 4096) {
|
|
code[gTreeSize] = gFirstCode;
|
|
parent[gTreeSize] = gLastCode;
|
|
if ((++gTreeSize >= ((1 << gCodeSize) - 1)) && (gCodeSize != 12)) gCodeSize++;
|
|
}
|
|
gLastCode = oldCode;
|
|
}
|
|
*srcData = src;
|
|
return noErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// OSErr ExtractLZWRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
//=====================================================================================
|
|
// Pulls out a row of LZW-compressed RGB data, skipping the alpha channel in the
|
|
// destination and ignoring anything other than the first 3 samples for each pixel.
|
|
//=====================================================================================
|
|
|
|
static OSErr ExtractLZWRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
|
|
{
|
|
#define gClearCode 256
|
|
#define gEndCode 257
|
|
static ushort gTreeSize, gCodeSize, gFirstCode, gLastCode;
|
|
static ulong gBitBuffer, gBitsInBuffer;
|
|
static uchar *gStackPtr;
|
|
ulong bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
|
|
ushort extra = 0, storeCount = 0, extraCount = 0, total;
|
|
ushort *parent = (ushort *)gTree;
|
|
uchar *code = (uchar *)(parent + 4096);
|
|
short i, j, currentCode, oldCode;
|
|
uchar *src;
|
|
|
|
if (srcData == nil && dstData == nil) {
|
|
gStackPtr = gStack;
|
|
gCodeSize = 9;
|
|
gBitsInBuffer = gBitBuffer = 0;
|
|
gTreeSize = gEndCode + 1;
|
|
return noErr;
|
|
}
|
|
bytes *= total = (*privates)->samplesPerPixel;
|
|
extraCount = extra = total - 3;
|
|
storeCount = 3;
|
|
src = *srcData;
|
|
for (i = 0; i < bytes; i++) {
|
|
if (storeCount == 3) dstData++;
|
|
if (gStackPtr != gStack) {
|
|
if (storeCount) {
|
|
if ((*privates)->prediction && i >= total)
|
|
*dstData = dstData[-4] + *(--gStackPtr), dstData++;
|
|
else *dstData++ = *(--gStackPtr);
|
|
if (!--storeCount && !extraCount) storeCount = 3;
|
|
} else {
|
|
--gStackPtr;
|
|
if (!--extraCount) storeCount = 3, extraCount = extra;
|
|
}
|
|
continue;
|
|
}
|
|
while (gCodeSize > gBitsInBuffer) {
|
|
gBitBuffer |= (ulong)*src++ << (24 - gBitsInBuffer);
|
|
gBitsInBuffer += 8;
|
|
}
|
|
currentCode = gBitBuffer >> (32 - gCodeSize);
|
|
gBitBuffer <<= gCodeSize;
|
|
gBitsInBuffer -= gCodeSize;
|
|
if (currentCode == gEndCode) return errAllDone;
|
|
else if (currentCode == gClearCode) {
|
|
gCodeSize = 9;
|
|
gTreeSize = gEndCode + 1;
|
|
do {
|
|
while (gCodeSize > gBitsInBuffer) {
|
|
gBitBuffer |= (ulong)*src++ << (24 - gBitsInBuffer);
|
|
gBitsInBuffer += 8;
|
|
}
|
|
currentCode = gBitBuffer >> (32 - gCodeSize);
|
|
gBitBuffer <<= gCodeSize;
|
|
gBitsInBuffer -= gCodeSize;
|
|
} while (currentCode == gClearCode);
|
|
if (storeCount) {
|
|
if ((*privates)->prediction && i >= total)
|
|
*dstData = dstData[-4] + currentCode, dstData++;
|
|
else *dstData++ = currentCode;
|
|
if (!--storeCount && !extraCount) storeCount = 3;
|
|
} else if (!--extraCount) storeCount = 3, extraCount = extra;
|
|
gFirstCode = gLastCode = currentCode;
|
|
continue;
|
|
}
|
|
oldCode = currentCode;
|
|
if (currentCode > gTreeSize) return codecBadDataErr;
|
|
else if (currentCode == gTreeSize) {
|
|
*gStackPtr++ = gFirstCode;
|
|
currentCode = gLastCode;
|
|
}
|
|
for (j = currentCode; j >= gClearCode; j = parent[j]) *gStackPtr++ = code[j];
|
|
if (storeCount) {
|
|
if ((*privates)->prediction && i >= total)
|
|
*dstData = dstData[-4] + j, dstData++;
|
|
else *dstData++ = j;
|
|
if (!--storeCount && !extraCount) storeCount = 3;
|
|
} else if (!--extraCount) storeCount = 3, extraCount = extra;
|
|
gFirstCode = j;
|
|
if (gTreeSize != 4096) {
|
|
code[gTreeSize] = gFirstCode;
|
|
parent[gTreeSize] = gLastCode;
|
|
if ((++gTreeSize >= ((1 << gCodeSize) - 1)) && (gCodeSize != 12)) gCodeSize++;
|
|
}
|
|
gLastCode = oldCode;
|
|
}
|
|
*srcData = src;
|
|
return noErr;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// ulong GetOneUnsigned(uchar *data, long offset)
|
|
//=====================================================================================
|
|
// Extracts an unsigned value from the data at the given offset.
|
|
//=====================================================================================
|
|
|
|
static ulong GetOneUnsigned(uchar *data, long offset)
|
|
{
|
|
ushort type = GetShort(data, offset);
|
|
ulong count = GetLong(data, offset + 2);
|
|
|
|
if (type == typeByte) {
|
|
if (count <= 4) return GetByte(data, offset + 6);
|
|
else return GetByte(data, GetLong(data, offset + 6));
|
|
} else if (type == typeShort) {
|
|
if (count <= 2) return GetShort(data, offset + 6);
|
|
else return GetShort(data, GetLong(data, offset + 6));
|
|
} else if (type == typeLong) {
|
|
if (count == 1) return GetLong(data, offset + 6);
|
|
else return GetLong(data, GetLong(data, offset + 6));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// ulong GetByte(uchar *data, long offset)
|
|
//=====================================================================================
|
|
// Extracts an unsigned byte from the data at the given offset.
|
|
//=====================================================================================
|
|
|
|
static ulong GetByte(uchar *data, long offset)
|
|
{
|
|
return data[offset];
|
|
}
|
|
|
|
//=====================================================================================
|
|
// ulong GetShort(uchar *data, long offset)
|
|
//=====================================================================================
|
|
// Extracts an unsigned byte from the data at the given offset.
|
|
//=====================================================================================
|
|
|
|
static ulong GetShort(uchar *data, long offset)
|
|
{
|
|
if (*data == 'I') return (ulong)data[offset] + ((ulong)data[offset + 1] << 8);
|
|
else return ((ulong)data[offset] << 8) + (ulong)data[offset + 1];
|
|
}
|
|
|
|
//=====================================================================================
|
|
// ulong GetLong(uchar *data, long offset)
|
|
//=====================================================================================
|
|
// Extracts an unsigned byte from the data at the given offset.
|
|
//=====================================================================================
|
|
|
|
static ulong GetLong(uchar *data, long offset)
|
|
{
|
|
if (*data == 'I') return (ulong)data[offset] + ((ulong)data[offset + 1] << 8) +
|
|
((ulong)data[offset + 2] << 16) + ((ulong)data[offset + 3] << 24);
|
|
else return ((ulong)data[offset] << 24) + ((ulong)data[offset + 1] << 16) +
|
|
((ulong)data[offset + 2] << 8) + (ulong)data[offset + 3];
|
|
}
|
|
|
|
//=====================================================================================
|
|
// Fixed GetRational(uchar *data, long offset)
|
|
//=====================================================================================
|
|
// Extracts a rational value from the data at the given offset, return it as a fixed-
|
|
// point value.
|
|
//=====================================================================================
|
|
|
|
static Fixed GetRational(uchar *data, long offset)
|
|
{
|
|
ulong num = GetLong(data, offset);
|
|
ulong den = GetLong(data, offset + 4);
|
|
return FixRatio(num, den);
|
|
}
|