home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 3
/
Info_Mac_1994-01.iso
/
HyperCard
/
TIFFWindow1.1
/
tiffinfo.c
< prev
next >
Wrap
Text File
|
1993-06-12
|
27KB
|
1,160 lines
/*
* This software is copyright 1992 by Robert Morris.
* You may freely redistribute this software as shareware
* if you do so in the same form as you got it. If you find
* this software useful, please send $12 to:
* Robert Morris
* P.O. Box 1044
* Harvard Square Station
* Cambridge, MA 02238
* ecognome@aol.com
* If you incorporate any of this software in any kind of
* commercial product, please send $2 per copy distributed
* to the above address.
*/
/*
* manipulate TIFF files.
*/
#include "tiffinfo.h"
#include "string.h"
#ifdef TIFF_PRINTFS
#include <stdio.h>
#endif
void TIFFError(TIFFPtr, OSErr, StringPtr);
long ttohs(TIFFPtr, short);
long ttohl(TIFFPtr, long);
OSErr tiff_read(TIFFPtr, unsigned long, void *, long);
OSErr ScanTIFFDirectory(TIFFPtr);
OSErr ReadTIFFEntry(TIFFPtr, long offset, struct TIFFEntry *);
long ParseScalar(TIFFPtr, struct TIFFEntry *);
long *ParseArray(TIFFPtr, struct TIFFEntry *);
CTabHandle TranslateColorMap(long *colorMap, int ncolors);
CTabHandle MakeGrayTable(int depth, int zeroIsWhite);
Ptr ThreeToFour(Ptr in, long nPixels);
long RowBytes(TIFFPtr);
void CopyPMStrip(PixMapHandle pm, Rect sr, Rect dr, int dither);
void CopyBMStrip(BitMap *, Rect sr, Rect dr, int inv);
void UnDifference(Ptr p, long rowbytes, long nrows, long span);
/*
* Scan a TIFF file's fields to ferret out interesting information.
* Returns a TIFFPtr whether it succeeded or failed; the caller must
* call GetTIFFError() to distinguish.
*/
TIFFPtr
ScanTIFF(int ref)
{
struct TIFFHeader header;
TIFFPtr ti;
ti = (TIFFPtr) NewPtr(sizeof(*ti));
if(ti == 0)
return(0);
memset(ti, 0, sizeof(*ti)); /* clear the record */
ti->ref = ref;
if(tiff_read(ti, 0L, &header, sizeof(header)) != 0)
return(ti);
ti->byteOrder = header.byteOrder;
if((ti->byteOrder != BigEndian && ti->byteOrder != LittleEndian) ||
ttohs(ti, header.versionNumber) != 42){
TIFFError(ti, -1, "\pNot a TIFF file.");
return(ti);
}
ti->offset = ttohl(ti, header.offset);
ScanTIFFDirectory(ti);
return(ti);
}
void
DisposeTIFF(TIFFPtr ti)
{
if(ti){
if(ti->bitsPerSample)
DisposPtr(ti->bitsPerSample);
ti->bitsPerSample = 0;
if(ti->colorMap)
DisposCTable(ti->colorMap);
ti->colorMap = 0;
if(ti->stripOffsets)
DisposPtr(ti->stripOffsets);
ti->stripOffsets = 0;
if(ti->stripByteCounts)
DisposPtr(ti->stripByteCounts);
ti->stripByteCounts = 0;
DisposPtr(ti);
}
}
OSErr
ScanTIFFDirectory(TIFFPtr ti)
{
unsigned short nentries, entryno;
struct TIFFEntry entry;
long nextoffset, n, i, offset, *colorMap = 0;
OSErr err;
offset = ti->offset;
ti->err = 0;
/* assign default values */
ti->bitsPerSample = (long *) NewPtr(sizeof(long));
if(ti->bitsPerSample == 0){
TIFFError(ti, err = MemError(), "\pOut of memory");
return(err);
}
ti->bitsPerSample[0] = 1;
ti->colorMap = 0;
ti->compression = NoCompression;
ti->predictor = NoPredictor;
ti->imageLength = -1;
ti->imageWidth = -1;
ti->photometricInterpretation = -1;
ti->planarConfiguration = PCContiguous;
ti->rowsPerStrip = 0x7fffffff;
ti->samplesPerPixel = 1;
ti->stripByteCounts = 0;
ti->stripOffsets = 0;
ti->t6Options = 0;
ti->fillOrder = 1;
if((err = tiff_read(ti, ti->offset, &nentries, sizeof(nentries))) != 0)
return(err);
offset += sizeof(nentries);
nentries = ttohs(ti, nentries);
#define ZAP(x) {if(x){DisposPtr(x);} x = 0;}
for(entryno = 0; entryno < nentries; entryno++){
if((err = ReadTIFFEntry(ti, offset, &entry)) != 0)
return(err);
offset += sizeof(entry);
switch(entry.tag){
case ImageWidth:
ti->imageWidth = ParseScalar(ti, &entry);
break;
case ImageLength:
ti->imageLength = ParseScalar(ti, &entry);
break;
case BitsPerSample:
ZAP(ti->bitsPerSample);
ti->bitsPerSample = ParseArray(ti, &entry);
break;
case ColorMap:
if(ti->colorMap)
DisposCTable(ti->colorMap);
ti->colorMap = 0;
colorMap = ParseArray(ti, &entry);
if(colorMap){
ti->colorMap = TranslateColorMap(colorMap, entry.length / 3);
DisposPtr(colorMap);
colorMap = 0;
if(ti->colorMap == 0)
TIFFError(ti, -1, "\pCould not make color map.");
}
break;
case Compression:
ti->compression = ParseScalar(ti, &entry);
break;
case Predictor:
ti->predictor = ParseScalar(ti, &entry);
break;
case PhotometricInterpretation:
ti->photometricInterpretation = ParseScalar(ti, &entry);
break;
case StripByteCounts:
ZAP(ti->stripByteCounts);
ti->stripByteCounts = ParseArray(ti, &entry);
break;
case StripOffsets:
ZAP(ti->stripOffsets);
ti->stripOffsets = ParseArray(ti, &entry);
break;
case SamplesPerPixel:
ti->samplesPerPixel = ParseScalar(ti, &entry);
break;
case RowsPerStrip:
ti->rowsPerStrip = ParseScalar(ti, &entry);
break;
case PlanarConfiguration:
ti->planarConfiguration = ParseScalar(ti, &entry);
break;
case FillOrder:
ti->fillOrder = ParseScalar(ti, &entry);
break;
case T6Options:
ti->t6Options = ParseScalar(ti, &entry);
break;
#ifdef TIFF_PRINTFS
default:
printf("tag %d, type %d, length %ld, value %ld/0x%lx\n",
entry.tag, entry.type, entry.length, entry.offset, entry.offset);
#endif
}
}
/* Some NeXT program generates bogus PlanarConfigurations of 2. */
if(ti->samplesPerPixel == 1 && ti->planarConfiguration == 2)
ti->planarConfiguration = 1;
if(ti->rowsPerStrip > ti->imageLength)
ti->rowsPerStrip = ti->imageLength;
ti->stripsPerImage = (ti->imageLength + ti->rowsPerStrip - 1) / ti->rowsPerStrip;
/*
* We need a color map for gray-scale images deeper than one bit.
*/
if(ti->colorMap == 0 &&
ti->samplesPerPixel == 1 &&
ti->bitsPerSample[0] <= 8 &&
ti->bitsPerSample[0] > 1 &&
(ti->photometricInterpretation == PIZeroIsWhite ||
ti->photometricInterpretation == PIZeroIsBlack)){
ti->colorMap = MakeGrayTable(ti->bitsPerSample[0],
ti->photometricInterpretation == PIZeroIsWhite);
if(ti->colorMap == 0)
TIFFError(ti, -1, "\pCould not make gray-scale color map.");
}
tiff_read(ti, offset, &nextoffset, sizeof(nextoffset));
ti->nextOffset = ttohl(ti, nextoffset);
return(0);
}
/*
* returns 0 if OK, or a negative error #.
*/
OSErr
ReadTIFFEntry(TIFFPtr ti, long offset, struct TIFFEntry *ep)
{
OSErr err;
if((err = tiff_read(ti, offset, ep, sizeof(struct TIFFEntry))) != 0)
return(err);
ep->tag = ttohs(ti, ep->tag);
ep->type = ttohs(ti, ep->type);
ep->length = ttohl(ti, ep->length);
return(0);
}
/*
* tries to interpret an entry as a single 32-bit integer value.
* can handle either TIFF_SHORT or TIFF_LONG.
* assumes ReadTIFFEntry() has already adjusted byte order of tag, type, and length.
* returns 0 if OK, or a negative error #.
* On the Mac, values appear in the highest bits of offset.
*/
long
ParseScalar(TIFFPtr ti, struct TIFFEntry *ep)
{
if(ep->length != 1)
return(-1);
if(ep->type == TIFF_BYTE){
return((ep->offset >> 24) & 0xff);
} else if(ep->type == TIFF_SHORT){
return(ttohs(ti, (ep->offset >> 16) & 0xffff));
} else if(ep->type == TIFF_LONG){
return(ttohl(ti, ep->offset));
} else {
TIFFError(ti, -1, "\pUnknown value type.");
return(-1);
}
}
/*
* Returns an array of longs, no matter what the size of the array element.
* Note that if the array fits in the offset field, it will be there, in the
* high-order bits, with element zero highest.
*/
long *
ParseArray(TIFFPtr ti, struct TIFFEntry *ep)
{
long nbytes, i;
unsigned char *cp = 0;
unsigned short *sp = 0;
long *lp = 0;
if(ep->length < 0)
goto bad;
lp = (long *) NewPtr(ep->length * sizeof(long));
if(lp == 0)
goto bad;
if(ep->type == TIFF_BYTE){
nbytes = ep->length;
if(nbytes <= 4){
for(i = 0; i < ep->length; i++)
lp[i] = (ep->offset >> (24 - (i * 8))) & 0xff;
} else {
cp = (unsigned char *) NewPtr(nbytes);
if(cp == 0)
goto bad;
if(tiff_read(ti, ttohl(ti, ep->offset), (char *)cp, nbytes) != 0)
goto bad;
for(i = 0; i < ep->length; i++)
lp[i] = cp[i];
}
} else if(ep->type == TIFF_SHORT){
nbytes = ep->length * 2;
if(nbytes <= 4){
for(i = 0; i < ep->length; i++)
lp[i] = ttohs(ti, ep->offset >> (16 - (i * 16)));
} else {
sp = (unsigned short *) NewPtr(nbytes);
if(sp == 0)
goto bad;
if(tiff_read(ti, ttohl(ti, ep->offset), (char *)sp, nbytes) != 0)
goto bad;
for(i = 0; i < ep->length; i++)
lp[i] = ttohs(ti, sp[i]);
}
} else if(ep->type == TIFF_LONG){
nbytes = ep->length * 4;
if(nbytes <= 4){
lp[0] = ttohl(ti, ep->offset);
} else {
if(tiff_read(ti, ttohl(ti, ep->offset), (char *)lp, nbytes) != 0)
goto bad;
for(i = 0; i < ep->length; i++)
lp[i] = ttohl(ti, lp[i]);
}
} else {
goto bad;
}
if(cp)
DisposPtr(cp);
if(sp)
DisposPtr(sp);
return(lp);
bad:
TIFFError(ti, -1, "\pInvalid array field.");
if(cp)
DisposPtr(cp);
if(sp)
DisposPtr(sp);
if(lp)
DisposPtr(lp);
return(0);
}
/* convert depth (<= 8) to # of colors */
int ColorsOfDepth[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256 };
/*
* return a color map with ngrays shades of gray in it.
*/
CTabHandle
MakeGrayTable(int depth, int zeroIsWhite)
{
int i, ncolors;
CTabHandle cth;
RGBColor rgb;
if(HasColorQD() == 0)
return(0);
if(depth >= 1 && depth <= 8)
ncolors = ColorsOfDepth[depth];
else
return(0);
cth = (CTabHandle) NewHandle((ncolors * sizeof(ColorSpec)) + 10);
if(cth == 0)
return(0);
(*cth)->ctSeed = GetCTSeed();
(*cth)->ctFlags = 0;
(*cth)->ctSize = ncolors - 1;
if(zeroIsWhite){
for(i = 0; i < ncolors; i++){
rgb.red = rgb.green = rgb.blue = (65535 / (ncolors - 1)) * (ncolors - i - 1);
(*cth)->ctTable[i].value = i; /* this must be filled in... */
(*cth)->ctTable[i].rgb = rgb;
}
} else {
/* this doesn't work so hot on the Mac */
for(i = 0; i < ncolors; i++){
rgb.red = rgb.green = rgb.blue = (65535 / (ncolors - 1)) * i;
(*cth)->ctTable[i].value = i; /* this must be filled in... */
(*cth)->ctTable[i].rgb = rgb;
}
}
return(cth);
}
/*
* Turn a TIFF color table, which has red values, then green values, then blue values,
* into a QuickDraw ColorTable.
*/
CTabHandle
TranslateColorMap(long *colorMap, int ncolors)
{
int i;
CTabHandle cth;
RGBColor rgb;
if(HasColorQD() == 0)
return(0);
if(colorMap == 0 || ncolors <= 0 || ncolors > 256)
return(0);
cth = (CTabHandle) NewHandle((ncolors * sizeof(ColorSpec)) + 10);
if(cth == 0)
return(0);
(*cth)->ctSeed = GetCTSeed();
(*cth)->ctFlags = 0;
(*cth)->ctSize = ncolors - 1;
for(i = 0; i < ncolors; i++){
rgb.red = colorMap[i];
rgb.green = colorMap[i + ncolors];
rgb.blue = colorMap[i + ncolors + ncolors];
(*cth)->ctTable[i].value = i; /* this must be filled in... */
(*cth)->ctTable[i].rgb = rgb;
}
return(cth);
}
long
ttohs(TIFFPtr ti, short s)
{
long s1;
if(ti->byteOrder == BigEndian){
/* Mac-order */
return(s & 0xffff);
} else {
s1 = (s >> 8) & 0xff;
s1 |= (s & 0xff) << 8;
return(s1);
}
}
long
ttohl(TIFFPtr ti, long l)
{
long l1;
int i;
if(ti->byteOrder == BigEndian){
/* Mac-order */
return(l);
} else {
l1 = 0;
for(i = 0; i < 4; i++){
l1 <<= 8;
l1 |= (l & 0xff);
l >>= 8;
}
return(l1);
}
}
OSErr
tiff_read(TIFFPtr ti, unsigned long offset, void *buf, long n)
{
int err;
long count;
err = SetFPos(ti->ref, fsFromStart, offset);
if(err < 0){
TIFFError(ti, err, "\pSeek failed.");
return(err);
}
count = n;
err = FSRead(ti->ref, &count, buf);
if(err < 0 && err != eofErr){
TIFFError(ti, err, "\pRead failed.");
return(err);
}
if(count != n){
TIFFError(ti, -1, "\pRead returned too little data.");
return(eofErr);
}
return(0);
}
PixMapHandle
PalettePixMap(short depth, CTabHandle cth, long x, long y, long width, long height,
Ptr data, long row)
{
PixMapHandle pm;
if(HasColorQD() == 0)
return(0);
/* Enforce restrictions. */
if((row & 1) != 0 ||
(((long) data) & 1) != 0 ||
(depth != 1 && depth != 2 && depth != 4 && depth != 8) ||
cth == 0){
return(0);
}
pm = NewPixMap();
if(pm == 0)
return(0);
(*pm)->pmVersion = 0; /* this is crucial */
(*pm)->pmReserved = 0;
(*pm)->packType = 0;
(*pm)->packSize = 0;
(*pm)->pixelType = 0; /* chunky */
(*pm)->pixelSize = depth; /* bits per pixel */
(*pm)->cmpCount = 1; /* 1 component per pixel */
(*pm)->cmpSize = depth; /* bits per component */
(*pm)->planeBytes = 0;
(*pm)->bounds.left = x;
(*pm)->bounds.right = x + width;
(*pm)->bounds.top = y;
(*pm)->bounds.bottom = y + height;
(*pm)->baseAddr = data;
(*pm)->rowBytes = row | 0x8000; /* the 0x8000 marks this as a Pixmap, not Bitmap */
if((*pm)->pmTable == 0){
DisposPixMap(pm);
return(0);
}
/*
* Give the PixMap its own color table, which DisposPixMap() frees.
*/
MoveHHi(cth);
HLock(cth);
PtrToXHand(*cth, (*pm)->pmTable, GetHandleSize(cth));
HUnlock(cth);
return(pm);
}
PixMapHandle
RGBPixMap(short depth, long x, long y, long width, long height,
Ptr data, long rowbytes)
{
PixMapHandle pm;
if(HasQD32() == 0)
return(0);
/* Enforce restrictions. */
if((rowbytes & 1) != 0 ||
(((long) data) & 1) != 0 ||
(depth != 16 && depth != 32)){
return(0);
}
pm = NewPixMap();
if(pm == 0)
return(0);
(*pm)->pmVersion = 0; /* this is crucial */
(*pm)->pmReserved = 0;
(*pm)->packType = 0;
(*pm)->packSize = 0;
(*pm)->pixelType = 16; /* RGBDirect */
(*pm)->pixelSize = depth; /* bits per pixel */
(*pm)->cmpCount = 3; /* 1 component per pixel */
(*pm)->cmpSize = (depth == 32 ? 8 : 5); /* bits per component */
(*pm)->planeBytes = 0;
(*pm)->bounds.left = x;
(*pm)->bounds.right = x + width;
(*pm)->bounds.top = y;
(*pm)->bounds.bottom = y + height;
(*pm)->baseAddr = data;
(*pm)->rowBytes = rowbytes | 0x8000; /* the 0x8000 marks this as a Pixmap, not Bitmap */
return(pm);
}
/*
* Return (a*b)/c. Try to minimize the error.
*/
#define MulDiv(a, b, c) (((a) * (b)) / (c))
/*
* Convert a from coordinate world aref to world bref. Result in b.
* Optimized for aref and bref the same size.
*/
void
ScaleRect(Rect *aref, Rect *a, Rect *bref, Rect *b)
{
long hmul, hdiv, hoff;
long vmul, vdiv, voff;
hmul = bref->right - bref->left;
hdiv = aref->right - aref->left;
hoff = bref->left - MulDiv(aref->left, hmul, hdiv);
vmul = bref->bottom - bref->top;
vdiv = aref->bottom - aref->top;
voff = bref->top - MulDiv(aref->top, vmul, vdiv);
b->left = MulDiv(a->left, hmul, hdiv) + hoff;
b->right = MulDiv(a->right, hmul, hdiv) + hoff;
b->top = MulDiv(a->top, vmul, vdiv) + voff;
b->bottom = MulDiv(a->bottom, vmul, vdiv) + voff;
}
/*
* Calculate how many (uncompressed) bytes in a scanline.
* Doesn't work for separated planes.
* Returns -1 on error.
*/
long
RowBytes(TIFFPtr ti)
{
long rowbytes;
if(ti->planarConfiguration != PCContiguous){
TIFFError(ti, -1, "\pCannot understand planar data.");
return(-1);
}
rowbytes = (ti->bitsPerSample[0] * ti->samplesPerPixel * ti->imageWidth + 7) / 8;
return(rowbytes);
}
/*
* Read a reasonable number of scan lines from an image, uncompress them if
* necessary, and return a new pointer to the data. For compressed images,
* this routine always reads exactly one strip. Since some applications write
* large uncompressed images as a single strip, this routine tries to read
* only about 64K at a time. ReadStrip() returns the number of lines actually
* read in *linesread, and the first line actually read in *firstread. The
* latter will always be <= startline.
*/
Ptr
ReadStrip(TIFFPtr ti, long startline, long *firstread, long *linesread)
{
long count, rowbytes, offset, uncount, i, nrows, strip;
long skip, suboffset;
Ptr p = 0, p1 = 0;
Ptr srcPtr, dstPtr;
/*
* Decide which strip to read.
*/
for(strip = 0; strip < ti->stripsPerImage; strip++)
if(startline >= strip*ti->rowsPerStrip && startline < (strip+1)*ti->rowsPerStrip)
break;
if(strip >= ti->stripsPerImage){
TIFFError(ti, -1, "\pBad arg to ReadStrip()");
return(0);
}
/*
* Decide how many rows in this strip; the last strip might have
* fewer than the others.
*/
nrows = ti->rowsPerStrip;
if((strip * ti->rowsPerStrip) + nrows > ti->imageLength)
nrows = ti->imageLength - (strip * ti->rowsPerStrip);
if(ti->stripOffsets == 0){
TIFFError(ti, -1, "\pNo strip offsets.");
return(0);
}
offset = ti->stripOffsets[strip];
if(ti->compression == NoCompression){
/*
* Just read part of a strip.
*/
if((rowbytes = RowBytes(ti)) == -1)
return(0);
skip = startline - (strip * ti->rowsPerStrip);
suboffset = offset + (skip * rowbytes);
nrows -= skip;
#define BUFSIZE 64000L
if(rowbytes >= BUFSIZE){
nrows = 1;
} else if((nrows * rowbytes) > BUFSIZE){
nrows = BUFSIZE / rowbytes;
}
count = rowbytes * nrows;
p = NewPtr(count);
if(p == 0){
TIFFError(ti, MemError(), "\pOut of memory.");
return(0);
}
if(tiff_read(ti, suboffset, p, count) != 0)
goto bad;
*firstread = startline;
*linesread = nrows;
return(p);
}
if(ti->stripByteCounts){
count = ti->stripByteCounts[strip];
} else {
/* cannot guess strip length */
return(0);
}
p = NewPtr(count);
if(p == 0){
TIFFError(ti, MemError(), "\pOut of memory.");
return(0);
}
if(tiff_read(ti, offset, p, count) != 0)
goto bad;
if(ti->compression == T6Compression){
rowbytes = RowBytes(ti);
if(rowbytes == -1)
goto bad;
uncount = rowbytes * ti->rowsPerStrip;
p1 = NewPtr(uncount);
if(p1 == 0){
TIFFError(ti, MemError(), "\pOut of memory.");
goto bad;
}
if(DecodeT6(ti, p, count, p1, uncount) != 0)
goto bad;
DisposPtr(p);
p = p1;
p1 = 0;
} else if(ti->compression == LZWCompression){
rowbytes = RowBytes(ti);
if(rowbytes == -1)
goto bad;
uncount = rowbytes * ti->rowsPerStrip;
p1 = NewPtr(uncount);
if(p1 == 0){
TIFFError(ti, MemError(), "\pOut of memory.");
goto bad;
}
if(UnLZW(p, count, p1, uncount) != 0){
TIFFError(ti, -1, "\pLZW decompression failed.");
goto bad;
}
DisposPtr(p);
p = p1;
p1 = 0;
if(ti->predictor == HDPredictor &&
ti->bitsPerSample[0] == 8 &&
ti->planarConfiguration == PCContiguous){
/* horizontal differencing */
for(i = 0; i < ti->samplesPerPixel; i++)
UnDifference(p+i, rowbytes, ti->rowsPerStrip, ti->samplesPerPixel);
} else if(ti->predictor != NoPredictor){
TIFFError(ti, -1, "\pUnimplemented LZW prediction type.");
goto bad;
}
} else if(ti->compression == PackCompression){
rowbytes = RowBytes(ti);
if(rowbytes == -1)
goto bad;
if(rowbytes > 32767){
TIFFError(ti, -1, "\pRow too long for UnpackBits.");
goto bad;
}
uncount = rowbytes * ti->rowsPerStrip;
p1 = NewPtr(uncount);
if(p1 == 0){
TIFFError(ti, MemError(), "\pOut of memory.");
goto bad;
}
srcPtr = p;
for(i = 0; i < nrows; i++){
dstPtr = p1 + (i * rowbytes);
/* does UnpackBits restrict the size of rowbytes? */
UnpackBits(&srcPtr, &dstPtr, (int)rowbytes);
}
DisposPtr(p);
p = p1;
p1 = 0;
} else {
TIFFError(ti, -1, "\pUnimplemented compression type.");
goto bad;
}
*firstread = strip * ti->rowsPerStrip;
*linesread = nrows;
return(p);
bad:
if(p)
DisposPtr(p);
if(p1)
DisposPtr(p1);
return(0);
}
/*
* Draw a tiff file image in the current QuickDraw port.
* The idea is to read each strip, convert into a form
* that's usable as a QuickDraw PixMap, and copy it to the
* current port. As an optimization, don't bother reading
* strips that won't be visible.
*/
OSErr
DrawTIFF(int ref, TIFFPtr ti, Rect sr, Rect dr, int dither)
{
long y, stripHeight, dstStripHeight, i, rowbytes;
Ptr p = 0, p1;
PixMapHandle pm = 0;
Rect visr, portr;
GrafPtr port;
int ppb, inv;
BitMap bm;
GetPort(&port);
/*
* Make sure we know how many bits there are per sample, and that
* the Red, Green, and Blue channels are the same size for RGB images.
*/
if(ti->bitsPerSample == 0){
TIFFError(ti, -1, "\pNo bits-per-sample given.");
return(-1);
}
for(i = 1; i < ti->samplesPerPixel; i++){
if(ti->bitsPerSample[i] != ti->bitsPerSample[i-1]){
TIFFError(ti, -1, "\pBits-per-sample are different.");
return(-1);
}
}
ti->ref = ref;
/*
* The destination rectangle may not be totally visible. As an optimization,
* translate the bounding box of the visible area into the source image
* space, and only copy the relevant strips.
*/
portr = (*(port->visRgn))->rgnBBox;
ScaleRect(&dr, &portr, &sr, &visr);
y = visr.top;
stripHeight = 0;
for( ; y < visr.bottom && y < ti->imageLength; y += stripHeight){
p = ReadStrip(ti, y, &y, &stripHeight);
if(p == 0)
goto bad;
/*
* Find a way to draw the strip.
*/
if(ti->samplesPerPixel == 1 &&
ti->bitsPerSample[0] == 1 &&
(ti->photometricInterpretation == PIZeroIsBlack ||
ti->photometricInterpretation == PIZeroIsWhite)){
rowbytes = (ti->bitsPerSample[0] * ti->imageWidth + 7) / 8;
inv = ti->photometricInterpretation == PIZeroIsBlack;
if(rowbytes & 1){
bm.bounds.right = ti->imageWidth;
bm.rowBytes = rowbytes + 1;
for(i = 0; i < stripHeight; i++){
p1 = p + (i * rowbytes);
bm.bounds.top = y + i;
bm.bounds.bottom = y + i + 1;
if((long)p1 & 1){
bm.bounds.left = -8;
bm.baseAddr = p1 - 1;
} else {
bm.bounds.left = 0;
bm.baseAddr = p1;
}
CopyBMStrip(&bm, sr, dr, inv);
}
} else {
bm.baseAddr = p;
bm.rowBytes = rowbytes;
bm.bounds.left = 0;
bm.bounds.right = ti->imageWidth;
bm.bounds.top = y;
bm.bounds.bottom = y + stripHeight;
CopyBMStrip(&bm, sr, dr, inv);
}
} else if(ti->samplesPerPixel == 3 &&
ti->bitsPerSample[0] == 8 &&
ti->planarConfiguration == PCContiguous &&
ti->photometricInterpretation == PIRGB){
/* convert from 3 to 4 bytes per pixel */
p1 = ThreeToFour(p, ti->imageWidth * stripHeight);
if(p1 == 0){
TIFFError(ti, -1, "\pOut of memory.");
goto bad;
}
rowbytes = 4 * ti->imageWidth;
pm = RGBPixMap(32, 0, y, ti->imageWidth, stripHeight, p1, rowbytes);
if(pm == 0){
DisposPtr(p1);
TIFFError(ti, -1, "\pCould not allocate PixMap.");
goto bad;
}
CopyPMStrip(pm, sr, dr, dither);
DisposPixMap(pm);
pm = 0;
DisposPtr(p1);
} else if(ti->samplesPerPixel == 1 &&
(ti->bitsPerSample[0] == 1 || ti->bitsPerSample[0] == 2 ||
ti->bitsPerSample[0] == 4 || ti->bitsPerSample[0] == 8) &&
(ti->photometricInterpretation == PIPalette ||
ti->photometricInterpretation == PIZeroIsBlack ||
ti->photometricInterpretation == PIZeroIsWhite)){
rowbytes = (ti->bitsPerSample[0] * ti->imageWidth + 7) / 8;
if(rowbytes & 1){
/* must copy odd widths a line at a time */
ppb = 8 / ti->bitsPerSample[0];
pm = PalettePixMap(ti->bitsPerSample[0], ti->colorMap,
0, 0,
ti->imageWidth, 1,
0, rowbytes + 1);
if(pm == 0){
TIFFError(ti, -1, "\pCould not allocate PixMap.");
goto bad;
}
for(i = 0; i < stripHeight; i++){
p1 = p + (i * rowbytes);
(*pm)->bounds.top = y + i;
(*pm)->bounds.bottom = y + i + 1;
if((long)p1 & 1){
(*pm)->bounds.left = -ppb;
(*pm)->baseAddr = p1 - 1;
} else {
(*pm)->bounds.left = 0;
(*pm)->baseAddr = p1;
}
CopyPMStrip(pm, sr, dr, dither);
}
DisposPixMap(pm);
pm = 0;
} else {
pm = PalettePixMap(ti->bitsPerSample[0], ti->colorMap,
0, y,
ti->imageWidth, stripHeight,
p, rowbytes);
if(pm == 0){
TIFFError(ti, -1, "\pCould not allocate PixMap.");
goto bad;
}
CopyPMStrip(pm, sr, dr, dither);
DisposPixMap(pm);
pm = 0;
}
} else {
TIFFError(ti, -1, "\pUnimplemented image representation.");
goto bad;
}
DisposPtr(p);
p = 0;
}
return(0);
bad:
if(p)
DisposPtr(p);
if(pm)
DisposPixMap(pm);
return(-1);
}
/*
* Copy a pixmap of a strip to the right place.
*/
void
CopyPMStrip(PixMapHandle pm, Rect sr, Rect dr, int dither)
{
Rect sr1, dr1;
GrafPtr port;
sr1 = (*pm)->bounds;
sr1.left = sr.left;
sr1.right = sr.right;
if(sr1.top < sr.top)
sr1.top = sr.top;
if(sr1.bottom > sr.bottom)
sr1.bottom = sr.bottom;
ScaleRect(&sr, &sr1, &dr, &dr1);
GetPort(&port);
MoveHHi(pm);
HLock(pm);
CopyBits(*pm, &(port->portBits), &sr1, &dr1, dither ? 64 : srcCopy, 0L);
HUnlock(pm);
}
/*
* Copy a bitmap of a strip to the right place.
* Optionally invert black and white.
*/
void
CopyBMStrip(BitMap *bm, Rect sr, Rect dr, int inv)
{
Rect sr1, dr1;
GrafPtr port;
sr1 = bm->bounds;
sr1.left = sr.left;
sr1.right = sr.right;
if(sr1.top < sr.top)
sr1.top = sr.top;
if(sr1.bottom > sr.bottom)
sr1.bottom = sr.bottom;
ScaleRect(&sr, &sr1, &dr, &dr1);
GetPort(&port);
CopyBits(bm, &(port->portBits), &sr1, &dr1, inv ? notSrcCopy : srcCopy, 0L);
}
/*
* Convert 3-bytes-per-pixel RGB data to 4-bytes-per-pixel, as required
* for QuickDraw 32-bit Pix Maps. inLen is the number of bytes.
*/
Ptr
ThreeToFour(Ptr in, long nPixels)
{
Ptr out;
register unsigned long *inp, *outp;
register unsigned long in1, in2, in3;
char *ip, *op;
out = NewPtr(4 * nPixels);
if(out == 0)
return(0);
inp = (unsigned long *) in;
outp = (unsigned long *) out;
for( ; nPixels >= 4; nPixels -= 4){
/* read 4 pixels worth of data in 4 longs: RGBR GBRG BRGB */
in1 = *inp++;
in2 = *inp++;
in3 = *inp++;
/* write the 4 pixels as 4 longs */
*outp++ = in1 >> 8;
*outp++ = ((in1 & 0xff) << 16) | ((in2 >> 16) & 0xffff);
*outp++ = ((in2 & 0xffff) << 8) | (in3 >> 24);
*outp++ = in3 & 0xffffff;
}
/* take care of last 1, 2 or 3 pixels one byte at a time */
ip = (char *) inp;
op = (char *) outp;
for( ; nPixels > 0; --nPixels){
*op++ = 0;
*op++ = *ip++;
*op++ = *ip++;
*op++ = *ip++;
}
return(out);
}
/*
* Un-difference a strip of image. Must be 8-bit samples.
* Span indicates how many bytes to skip (for interleaved RGB).
* Modifies the data in place.
*/
void
UnDifference(Ptr p, long rowbytes, long nrows, long span)
{
long row, col, off;
for(row = 0; row < nrows; row++){
off = row * rowbytes;
for(col = span; col < rowbytes; col += span){
p[off + col] += p[off + col - span];
}
}
}
/*
* Register an error with a TIFF record, for later retrieval with GetTIFFError().
*/
void
TIFFError(TIFFPtr ti, OSErr err, StringPtr p)
{
int i;
if(ti && ti->err == 0){
if(err == 0)
err = -1;
ti->err = err;
for(i = 0; i < p[0] && i < 255; i++)
ti->errStr[i+1] = p[i+1];
ti->errStr[0] = p[0];
}
}
/*
* Return and clear the error associated with a TIFF record.
*/
OSErr
GetTIFFError(TIFFPtr ti, StringPtr p)
{
int i;
OSErr err;
p[0] = 0;
if(ti == 0)
return(-1);
p[0] = ti->errStr[0];
for(i = 0; i < p[0] && i < 255; i++)
p[i+1] = ti->errStr[i+1];
err = ti->err;
ti->err = 0;
ti->errStr[0] = 0;
return(err);
}
/*
* Can a TIFF image be drawn with this system's version of QuickDraw?
*/
Boolean
TIFFDrawable(TIFFPtr ti)
{
if(ti->samplesPerPixel > 1)
return(HasQD32());
if(ti->bitsPerSample[0] > 1)
return(HasColorQD());
return(TRUE);
}