If I have a pointer to TIFF data, but no indication of the size, is there any way to accurately calculate it?
I've gone through several different ideas, all of which work most of the time, but not always, since there's just so many different ways to format a TIFF, and I figured there has to be an easier way to do this. Right now, the closest I've gotten is:
ULONG readImageHeader(char* image)
{
TIF_HDR *xTIFHdr;
TIF_IFD *xTIFIFD;
TIF_IFD_ENTRY *pxTIFIFDEntry;
UCHAR *pHdrPtr;
USHORT i;
ULONG length = 0;
ULONG imgLength = 0;
ULONG count = 0;
// check to see if it is a TIFF header
xTIFHdr = (TIF_HDR *)image;
// Little Endian
if (xTIFHdr->usTIFID == TIF_HEAD_LITTLE)
{
pHdrPtr = (UCHAR*)image;
pHdrPtr += xTIFHdr->ulFirstIFDOffset;
// read TIF IFD
xTIFIFD = (TIF_IFD *)pHdrPtr;
// Look at all the IFD entries and set internal image hdr
pHdrPtr += TIF_IFD_LEN;
pxTIFIFDEntry = (TIF_IFD_ENTRY *)pHdrPtr;
// iterate through each IFD entry
for (i=0; i<xTIFIFD->usNumIFDEntries; i++)
{
if(length <= (ULONG)pxTIFIFDEntry->ulTIFValueOffset)
{
length = (ULONG)pxTIFIFDEntry->ulTIFValueOffset;
// the TIF length is in units of the TIF type
switch(pxTIFIFDEntry->usTIFType)
{
case TIF_BYTE:
length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_BYTE_SIZE;
break;
case TIF_ASCII:
length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_ASCII_SIZE;
break;
case TIF_SHORT:
length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_SHORT_SIZE;
break;
case TIF_LONG:
length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_LONG_SIZE;
break;
case TIF_RATIONAL:
length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_RATIONAL_SIZE;
break;
default:
length += (ULONG)pxTIFIFDEntry->ulTIFLength;
break;
}
}
switch (pxTIFIFDEntry->usTIFTag)
{
case TIF_STRIP_BYTE_COUNTS:
case TIF_STRIP_OFFSETS:
{
ULONG valueOffset = (ULONG)pxTIFIFDEntry->ulTIFValueOffset;
count = (ULONG)pxTIFIFDEntry->ulTIFLength;
// if the count > 1, then the valueOffset actually represents an offset
if(count > 1)
{
ULONG countsize = (count - 1) * sizeof(ULONG);
imgLength += *(ULONG*) ((UCHAR*)image + valueOffset + countsize);
}
else
{
// if count is 1, then the valueOffset is really just the value of that item
imgLength += valueOffset;
}
break;
}
default:
break;
}
pxTIFIFDEntry++;
}
// the length is the largest offset, plus the length of that item
// the imgLength is the offset of the image, plus the size of the image, which is stored as two separate tags
// return the largest of them
return(length > imgLength ? length : imgLength);
}
// Big Endian
else if(xTIFHdr->usTIFID == TIF_HEAD_BIG)
{
// I don't care about this
printf("Big Endian TIFF image\n");
}
printf("Invalid TIFF image\n");
return(0);
}
Essentially what I'm doing here is I'm iterating through the TIFF header, and calculating two running sums: (largest offset + data length) and (strip offset + strip byte count). Then I just use the larger of the two values.
This mostly works, except that sometimes the ulTIFValueOffset is not an offset at all, but the actual value. In (some of) those cases, I'm getting a file size that is too big. So far, all my failed examples have been when it's grabbing the Width or Length tag, although I can't rule out the possibility that other tags could have the same problem.
Is there either
- A way to calculate the file size given the headers? or
- A way to know if the headers are a value or an offset?
Thanks!