JPEGView/Source/C/TIFF.c
Aaron Giles 92bdb55672 JPEGView 3.3 for Macintosh
These are the sources for the final official release of JPEGView for the
Mac, back in 1994.
2015-02-05 00:18:10 -08:00

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);
}