Proton/jxrlib/jxrtestlib/JXRTestTif.c
2020-09-29 14:29:06 -05:00

937 lines
27 KiB
C

//*@@@+++@@@@******************************************************************
//
// Copyright © Microsoft Corp.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// • Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// • Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//*@@@---@@@@******************************************************************
#include <stdlib.h>
#include <string.h>
#include <strcodec.h>
#include <JXRTest.h>
//================================================================
#define TIF_tagNull 0
#define TIF_tagSubfileType 0xff
#define TIF_tagNewSubfileType 0xfe
#define TIF_tagImageWidth 0x100
#define TIF_tagImageLength 0x101
#define TIF_tagBitsPerSample 0x102
#define TIF_tagCompression 0x103
#define TIF_tagPhotometricInterpretation 0x106
#define TIF_tagStripOffsets 0x111
#define TIF_tagOrientation 0x112
#define TIF_tagSamplesPerPixel 0x115
#define TIF_tagRowsPerStrip 0x116
#define TIF_tagStripByteCounts 0x117
#define TIF_tagXResolution 0x11a
#define TIF_tagYResolution 0x11b
#define TIF_tagPlanarConfiguration 0x11c
#define TIF_tagResolutionUnit 0x128
#define TIF_tagSoftware 0x131
#define TIF_tagColorMap 0x140
#define TIF_tagPredictor 0x13d
#define TIF_tagInkSet 0x14c
#define TIF_tagExtraSamples 0x152
#define TIF_tagSampleFormat 0x153
#define TIF_typBYTE 1
#define TIF_typASCII 2
#define TIF_typSHORT 3
#define TIF_typLONG 4
#define TIF_typRATIOAL 5
#define TIF_typSBYTE 6
#define TIF_typUNDEFINED 7
#define TIF_typSSHORT 8
#define TIF_typSLONG 9
#define TIF_typSRATIONAL 10
#define TIF_typFLOAT 11
#define TIF_typDOUBLE 12
//================================================================
typedef float FLOAT;
typedef double DOUBLE;
//================================================================
// PKImageEncode_TIF helpers
//================================================================
typedef struct tagTifDE
{
U16 uTag;
U16 uType;
U32 uCount;
U32 uValueOrOffset;
} TifDE;
typedef struct tagTifDEMisc
{
U32 offBitsPerSample;
U32 offSampleFormat;
U32 bps, spp, sf;
U32 iPhotometricInterpretation;
U32 offXResolution;
U32 resXF, resXD;
U32 offYResolution;
U32 resYF, resYD;
} TifDEMisc;
ERR PutTifUShort(
struct WMPStream* pS,
size_t offPos,
U16 uValue)
{
ERR err = WMP_errSuccess;
Call(pS->SetPos(pS, offPos));
Call(pS->Write(pS, &uValue, sizeof(uValue)));
Cleanup:
return err;
}
ERR PutTifULong(
struct WMPStream* pS,
size_t offPos,
U32 uValue)
{
ERR err = WMP_errSuccess;
Call(pS->SetPos(pS, offPos));
Call(pS->Write(pS, &uValue, sizeof(uValue)));
Cleanup:
return err;
}
ERR WriteTifDE(
struct WMPStream* pS,
size_t offPos,
TifDE* pDE)
{
ERR err = WMP_errSuccess;
assert(-1 != pDE->uCount);
assert(-1 != pDE->uValueOrOffset);
Call(PutTifUShort(pS, offPos, pDE->uTag)); offPos += 2;
Call(PutTifUShort(pS, offPos, pDE->uType)); offPos += 2;
Call(PutTifULong(pS, offPos, pDE->uCount)); offPos += 4;
switch (pDE->uType)
{
case TIF_typSHORT:
if (1 == pDE->uCount)
{
Call(PutTifUShort(pS, offPos, (U16)pDE->uValueOrOffset)); offPos += 2;
Call(PutTifUShort(pS, offPos, 0)); offPos += 2;
break;
}
case TIF_typLONG:
case TIF_typRATIOAL:
Call(PutTifULong(pS, offPos, pDE->uValueOrOffset)); offPos += 4;
break;
default:
Call(WMP_errInvalidParameter);
break;
}
Cleanup:
return err;
}
ERR WriteTifHeader(
PKImageEncode* pIE)
{
ERR err = WMP_errSuccess;
struct WMPStream* pS = pIE->pStream;
size_t offPos = 0;
#ifdef _BIG__ENDIAN_
U8 IIMM[3] = "MM";
#else // _BIG__ENDIAN_
U8 IIMM[3] = "II";
#endif // _BIG__ENDIAN_
TifDEMisc tifDEMisc = {
(U32) -1, (U32) -1, (U32) -1, (U32) -1, (U32) -1,
2, // photometric interpretation
(U32) -1, 10000, 10000,
(U32) -1, 10000, 10000,
};
// const U32 cbTifDEMisc = sizeof(U16) * 10 + sizeof(U32) * 2 * 2;
const static TifDE tifDEs[] =
{
{0x100, 4, 1, (U32) -1}, // TIF_tagImageWidth
{0x101, 4, 1, (U32) -1}, // TIF_tagImageLength
{0x102, 3, (U32) -1, (U32) -1}, // TIF_tagBitsPerSample
{0x103, 3, 1, 1}, // TIF_tagCompression
{0x106, 3, 1, (U32) -1}, // TIF_tagPhotometricInterpretation
{0x111, 4, 1, (U32) -1}, // TIF_tagStripOffsets
{0x112, 3, 1, 1}, // TIF_tagOrientation
{0x115, 3, 1, (U32) -1}, // TIF_tagSamplesPerPixel
{0x116, 4, 1, (U32) -1}, // TIF_tagRowsPerStrip
{0x117, 4, 1, (U32) -1}, // TIF_tagStripByteCounts
{0x11a, 5, 1, (U32) -1}, // TIF_tagXResolution
{0x11b, 5, 1, (U32) -1}, // TIF_tagYResolution
{0x11c, 3, 1, 1}, // TIF_tagPlanarConfiguration
{0x128, 3, 1, 2}, // TIF_tagResolutionUnit
{0x153, 3, (U32) -1, (U32) -1}, // TIF_tagSampleFormat
// {0x131, 2, -1, -1}, // TIF_tagSoftware
// {0x140, 3, -1, -1}, // TIF_tagColorMap
};
U16 cTifDEs = sizeof2(tifDEs);
TifDE tifDE = {0};
PKPixelInfo PI;
size_t cbLine = 0;
size_t i = 0;
size_t j;
tifDEMisc.resXF = (U32)(pIE->fResX * 10000);
tifDEMisc.resYF = (U32)(pIE->fResY * 10000);
Call(pS->GetPos(pS, &offPos));
FailIf(0 != offPos, WMP_errUnsupportedFormat);
//================
// TifHeader
Call(pS->Write(pS, IIMM, 2)); offPos += 2;
Call(PutTifUShort(pS, offPos, 42)); offPos += 2;
Call(PutTifULong(pS, offPos, (U32)(offPos + 4))); offPos += 4;
//================
// TifDEMisc
PI.pGUIDPixFmt = &pIE->guidPixFormat;
PixelFormatLookup(&PI, LOOKUP_FORWARD);
tifDEMisc.iPhotometricInterpretation =
//the N channel TIF by PS has PhotometricInterpretation of PK_PI_RGB
PI.uInterpretation == PK_PI_NCH || PI.uInterpretation == PK_PI_RGBE ? PK_PI_RGB :
(PI.uInterpretation == PK_PI_B0 && pIE->WMP.wmiSCP.bBlackWhite ? PK_PI_W0 : PI.uInterpretation);
tifDEMisc.spp = PI.uSamplePerPixel;
tifDEMisc.bps = PI.uBitsPerSample;
tifDEMisc.sf = PI.uSampleFormat;
if (tifDEMisc.iPhotometricInterpretation == PK_PI_CMYK)
cTifDEs++;
if (PI.grBit & PK_pixfmtHasAlpha)
cTifDEs++;
tifDEMisc.offBitsPerSample = (U32)offPos + sizeof(U16) + 12 * cTifDEs + sizeof(U32);
tifDEMisc.offSampleFormat = tifDEMisc.offBitsPerSample + (tifDEMisc.spp == 1 ? 0 : tifDEMisc.spp * 2);
tifDEMisc.offXResolution = tifDEMisc.offSampleFormat + (tifDEMisc.spp == 1 ? 0 : tifDEMisc.spp * 2);
tifDEMisc.offYResolution = tifDEMisc.offXResolution + 8;
//================
// TifIFD
pIE->offPixel = tifDEMisc.offYResolution + 8;
Call(PutTifUShort(pS, offPos, cTifDEs)); offPos += 2;
//================
tifDE = tifDEs[i++];
assert(TIF_tagImageWidth == tifDE.uTag);
tifDE.uValueOrOffset = pIE->uWidth;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagImageLength == tifDE.uTag);
tifDE.uValueOrOffset = pIE->uHeight;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagBitsPerSample == tifDE.uTag);
tifDE.uCount = tifDEMisc.spp;
tifDE.uValueOrOffset = 1 == tifDE.uCount ? tifDEMisc.bps : tifDEMisc.offBitsPerSample;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagCompression == tifDE.uTag);
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagPhotometricInterpretation == tifDE.uTag);
tifDE.uValueOrOffset = tifDEMisc.iPhotometricInterpretation;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagStripOffsets == tifDE.uTag);
tifDE.uValueOrOffset = (U32)pIE->offPixel;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagOrientation == tifDE.uTag);
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagSamplesPerPixel == tifDE.uTag);
tifDE.uValueOrOffset = tifDEMisc.spp;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagRowsPerStrip == tifDE.uTag);
tifDE.uValueOrOffset = pIE->uHeight;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagStripByteCounts == tifDE.uTag);
cbLine = (BD_1 == PI.bdBitDepth ? ((PI.cbitUnit * pIE->uWidth + 7) >> 3) : (((PI.cbitUnit + 7) >> 3) * pIE->uWidth));
tifDE.uValueOrOffset = (U32)(cbLine * pIE->uHeight);
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagXResolution == tifDE.uTag);
tifDE.uValueOrOffset = tifDEMisc.offXResolution;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagYResolution == tifDE.uTag);
tifDE.uValueOrOffset = tifDEMisc.offYResolution;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagPlanarConfiguration == tifDE.uTag);
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
tifDE = tifDEs[i++];
assert(TIF_tagResolutionUnit == tifDE.uTag);
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
if (tifDEMisc.iPhotometricInterpretation == PK_PI_CMYK)
{
TifDE tifDE = {TIF_tagInkSet, 3, 1, 1};
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
}
if (PI.grBit & PK_pixfmtHasAlpha)
{
TifDE tifDE = {TIF_tagExtraSamples, 3, 1, 1};
if (!(PI.grBit & PK_pixfmtPreMul))
tifDE.uValueOrOffset++;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
}
tifDE = tifDEs[i++];
assert(TIF_tagSampleFormat == tifDE.uTag);
tifDE.uCount = tifDEMisc.spp;
tifDE.uValueOrOffset = 1 == tifDE.uCount ? tifDEMisc.sf : tifDEMisc.offSampleFormat;
Call(WriteTifDE(pS, offPos, &tifDE)); offPos += 12;
//================
Call(PutTifULong(pS, offPos, 0)); offPos += 4;
//================
// TifDEMisc
if (tifDE.uCount > 1)
{
assert(tifDEMisc.offBitsPerSample == offPos);
if (PI.bdBitDepth == BD_565)
{
Call(PutTifUShort(pS, offPos, 5)); offPos += 2;
Call(PutTifUShort(pS, offPos, 6)); offPos += 2;
Call(PutTifUShort(pS, offPos, 5)); offPos += 2;
}
else
{
for (j = 0; j < tifDE.uCount; j++)
{
Call(PutTifUShort(pS, offPos, (U16)tifDEMisc.bps)); offPos += 2;
}
}
assert(tifDEMisc.offSampleFormat == offPos);
for (j = 0; j < tifDE.uCount; j++)
{
Call(PutTifUShort(pS, offPos, (U16)tifDEMisc.sf)); offPos += 2;
}
}
assert(tifDEMisc.offXResolution == offPos);
Call(PutTifULong(pS, offPos, tifDEMisc.resXF)); offPos += 4;
Call(PutTifULong(pS, offPos, tifDEMisc.resXD)); offPos += 4;
assert(tifDEMisc.offYResolution == offPos);
Call(PutTifULong(pS, offPos, tifDEMisc.resYF)); offPos += 4;
Call(PutTifULong(pS, offPos, tifDEMisc.resYD)); offPos += 4;
assert(pIE->offPixel == offPos);
pIE->fHeaderDone = !FALSE;
Cleanup:
return err;
}
//================================================================
// PKImageEncode_TIF
//================================================================
ERR PKImageEncode_WritePixels_TIF(
PKImageEncode* pIE,
U32 cLine,
U8* pbPixel,
U32 cbStride)
{
ERR err = WMP_errSuccess;
struct WMPStream* pS = pIE->pStream;
PKPixelInfo PI;
size_t cbLine = 0;
size_t offPos = 0;
size_t i = 0;
// header
if (!pIE->fHeaderDone)
{
Call(WriteTifHeader(pIE));
}
// body
PI.pGUIDPixFmt = &pIE->guidPixFormat;
PixelFormatLookup(&PI, LOOKUP_FORWARD);
cbLine = (BD_1 == PI.bdBitDepth ? ((PI.cbitUnit * pIE->uWidth + 7) >> 3) : (((PI.cbitUnit + 7) >> 3) * pIE->uWidth));
FailIf(cbStride < cbLine, WMP_errInvalidParameter);
offPos = pIE->offPixel + cbLine * pIE->idxCurrentLine;
Call(pS->SetPos(pS, offPos));
for (i = 0; i < cLine; ++i)
{
Call(pS->Write(pS, pbPixel + cbStride * i, cbLine));
}
pIE->idxCurrentLine += cLine;
Cleanup:
return err;
}
ERR PKImageEncode_Create_TIF(PKImageEncode** ppIE)
{
ERR err = WMP_errSuccess;
PKImageEncode* pIE = NULL;
Call(PKImageEncode_Create(ppIE));
pIE = *ppIE;
pIE->WritePixels = PKImageEncode_WritePixels_TIF;
Cleanup:
return err;
}
//================================================================
// PKImageDecode_TIF helpers
//================================================================
ERR GetTifUShort(
struct WMPStream* pWS,
size_t offPos,
Bool fLittleEndian,
U16* puValue)
{
ERR err = WMP_errSuccess;
U8 buf[2];
Call(pWS->SetPos(pWS, offPos));
Call(pWS->Read(pWS, buf, sizeof2(buf)));
if (fLittleEndian)
{
*puValue = buf[0] + ((U16)buf[1] << 8);
}
else
{
*puValue = ((U16)buf[0] << 8) + buf[1];
}
Cleanup:
return err;
}
ERR GetTifULong(
struct WMPStream* pWS,
size_t offPos,
Bool fLittleEndian,
U32* puValue)
{
ERR err = WMP_errSuccess;
U8 buf[4];
Call(pWS->SetPos(pWS, offPos));
Call(pWS->Read(pWS, buf, sizeof2(buf)));
if (fLittleEndian)
{
*puValue = buf[0] + ((U32)buf[1] << 8) + ((U32)buf[2] << 16) + ((U32)buf[3] << 24);
}
else
{
*puValue = ((U32)buf[0] << 24) + ((U32)buf[1] << 16) + ((U32)buf[2] << 8) + buf[3];
}
Cleanup:
return err;
}
ERR GetTifULongArray(
struct WMPStream* pWS,
size_t offPos,
size_t cElements,
Bool fLittleEndian,
U32* puValue)
{
ERR err = WMP_errSuccess;
if (1 == cElements)
{
puValue[0] = (U32)offPos;
}
else
{
size_t i = 0;
for (i = 0; i < cElements; ++i)
{
Call(GetTifULong(pWS, offPos, fLittleEndian, &puValue[i]));
offPos += sizeof(*puValue);
}
}
Cleanup:
return err;
}
ERR ParseTifDEValue(
PKTestDecode* pID,
U16 uTag,
U16 uType,
U32 uCount)
{
ERR err = WMP_errSuccess;
struct WMPStream* pWS = pID->pStream;
U16 bpc[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
U16 sf[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
U32 uPos = 0;
U16 usValue = 0;
U32 uValue0 = 0;
U32 uValue1 = 0;
size_t i, offPos = 0;
//================================
Call(pWS->GetPos(pWS, &offPos));
//================================
switch (uType)
{
case TIF_typSHORT:
Call(GetTifUShort(pWS, offPos, pID->EXT.TIF.fLittleEndian, &usValue));
uValue0 = usValue;
break;
case TIF_typLONG:
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uValue0));
break;
}
//================================
switch (uTag)
{
case TIF_tagNewSubfileType:
FailIf(0 != uValue0, WMP_errUnsupportedFormat);
break;
case TIF_tagSubfileType:
case TIF_tagPredictor:
FailIf(1 != uValue0, WMP_errUnsupportedFormat);
break;
case TIF_tagImageWidth:
pID->uWidth = uValue0;
break;
case TIF_tagImageLength:
pID->uHeight = uValue0;
break;
case TIF_tagBitsPerSample:
if (1 == uCount)
{
pID->EXT.TIF.uBitsPerSample = uValue0;
}
else
{
Bool bpcAnd = 1;
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uPos));
offPos = uPos;
Call(GetTifUShort(pWS, offPos, pID->EXT.TIF.fLittleEndian, &bpc[0]));
for (i = 1; i < uCount; i++)
{
Call(GetTifUShort(pWS, offPos + (i << 1) , pID->EXT.TIF.fLittleEndian, &bpc[i]));
bpcAnd = (bpcAnd && (bpc[0] == bpc[i]));
}
if (bpcAnd)
pID->EXT.TIF.uBitsPerSample = bpc[0];
else
Call(WMP_errUnsupportedFormat);
}
break;
case TIF_tagExtraSamples:
FailIf(0 != uValue0 && 1 != uValue0 && 2 != uValue0, WMP_errUnsupportedFormat);
pID->EXT.TIF.uExtraSamples = uValue0;
break;
case TIF_tagSampleFormat:
if (1 == uCount)
{
pID->EXT.TIF.uSampleFormat = uValue0;
}
else
{
Bool sfAnd = 1;
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uPos));
offPos = uPos;
Call(GetTifUShort(pWS, offPos, pID->EXT.TIF.fLittleEndian, &sf[0]));
for (i = 1; i < uCount; i++)
{
Call(GetTifUShort(pWS, offPos + (i << 1) , pID->EXT.TIF.fLittleEndian, &sf[i]));
sfAnd = (sfAnd && (sf[0] == sf[i]));
}
if (sfAnd)
pID->EXT.TIF.uSampleFormat = sf[0];
else
Call(WMP_errUnsupportedFormat);
}
break;
case TIF_tagCompression:
FailIf(1 != uValue0, WMP_errUnsupportedFormat);
break;
case TIF_tagPhotometricInterpretation:
Test(PK_PI_W0 == uValue0 || PK_PI_B0 == uValue0 || PK_PI_RGB == uValue0
|| PK_PI_RGBPalette == uValue0 || PK_PI_TransparencyMask == uValue0
|| PK_PI_CMYK == uValue0 || PK_PI_YCbCr == uValue0
|| PK_PI_CIELab == uValue0, WMP_errUnsupportedFormat);
pID->EXT.TIF.uInterpretation = uValue0;
break;
case TIF_tagStripOffsets:
Call(WMPAlloc((void **) &pID->EXT.TIF.uStripOffsets, sizeof(*pID->EXT.TIF.uStripOffsets) * uCount));
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uValue0));
Call(GetTifULongArray(pWS, uValue0, uCount, pID->EXT.TIF.fLittleEndian, pID->EXT.TIF.uStripOffsets));
break;
case TIF_tagOrientation:
case TIF_tagSamplesPerPixel:
pID->EXT.TIF.uSamplePerPixel = uValue0;
break;
case TIF_tagRowsPerStrip:
pID->EXT.TIF.uRowsPerStrip = uValue0;
break;
case TIF_tagStripByteCounts:
Call(WMPAlloc((void **) &pID->EXT.TIF.uStripByteCounts, sizeof(*pID->EXT.TIF.uStripByteCounts) * uCount));
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uValue0));
Call(GetTifULongArray(pWS, uValue0, uCount, pID->EXT.TIF.fLittleEndian, pID->EXT.TIF.uStripByteCounts));
break;
case TIF_tagXResolution:
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uPos));
offPos = uPos;
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uValue0));//numerator
Call(GetTifULong(pWS, offPos + 4, pID->EXT.TIF.fLittleEndian, &uValue1));//denominator
pID->EXT.TIF.fResX = (Float)uValue0/(Float)uValue1;
break;
case TIF_tagYResolution:
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uPos));
offPos = uPos;
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uValue0));//numerator
Call(GetTifULong(pWS, offPos + 4, pID->EXT.TIF.fLittleEndian, &uValue1));//denominator
pID->EXT.TIF.fResY = (Float)uValue0/(Float)uValue1;
break;
case TIF_tagResolutionUnit:
pID->EXT.TIF.uResolutionUnit = usValue;
break;
case TIF_tagPlanarConfiguration:
case TIF_tagSoftware:
case TIF_tagColorMap:
break;
default:
printf("Unrecognized TIFTag: %d(%#x), %d, %d" CRLF, (int)uTag, (int)uTag, (int)uType, (int)uCount);
break;
}
Cleanup:
return err;
}
ERR ParseTifDEArray(
PKTestDecode* pID,
size_t offPos)
{
ERR err = WMP_errSuccess;
struct WMPStream* pWS = pID->pStream;
U16 uTag = 0;
U16 uType = 0;
U32 uCount = 0;
Call(GetTifUShort(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uTag));
offPos += 2;
Call(GetTifUShort(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uType));
offPos += 2;
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uCount));
offPos += 4;
Call(ParseTifDEValue(pID, uTag, uType, uCount));
Cleanup:
return err;
}
ERR ParseTifHeader(
PKTestDecode* pID,
struct WMPStream* pWS)
{
ERR err = WMP_errSuccess;
PKPixelInfo PI;
size_t offPosBase = 0;
size_t offPos = 0;
U8 szSig[3] = {0, 0, '\0'};
U16 uTiffId = 0;
U32 uOffNextIFD = 0;
U16 uCountDE = 0, i = 0;
//default
pID->EXT.TIF.uRowsPerStrip = (U32) -1;
pID->EXT.TIF.uInterpretation = (U32) -1;
pID->EXT.TIF.uSamplePerPixel = (U32) -1;
pID->EXT.TIF.uBitsPerSample = (U32) -1;
pID->EXT.TIF.uSampleFormat = 1;
pID->EXT.TIF.uResolutionUnit = 2;
pID->EXT.TIF.fResX = 96;
pID->EXT.TIF.fResY = 96;
//================================
Call(pWS->GetPos(pWS, &offPosBase));
FailIf(0 != offPosBase, WMP_errUnsupportedFormat);
//================================
// Header
Call(pWS->Read(pWS, szSig, 2));
offPos += 2;
if (szSig == (U8 *) strstr((char *) szSig, "II"))
{
pID->EXT.TIF.fLittleEndian = !FALSE;
}
else if (szSig == (U8 *) strstr((char *) szSig, "MM"))
{
pID->EXT.TIF.fLittleEndian = FALSE;
}
else
{
Call(WMP_errUnsupportedFormat);
}
Call(GetTifUShort(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uTiffId));
offPos += 2;
FailIf(42 != uTiffId, WMP_errUnsupportedFormat);
Call(GetTifULong(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uOffNextIFD));
offPos += 4;
//================================
// IFD
offPos = (size_t)uOffNextIFD;
Call(GetTifUShort(pWS, offPos, pID->EXT.TIF.fLittleEndian, &uCountDE));
offPos += 2;
for (i = 0; i < uCountDE; ++i)
{
Call(ParseTifDEArray(pID, offPos));
offPos += 12;
}
if(pID->EXT.TIF.uRowsPerStrip == -1)
pID->EXT.TIF.uRowsPerStrip = pID->uHeight;//default
FailIf((-1 == pID->EXT.TIF.uInterpretation
|| -1 == pID->EXT.TIF.uSamplePerPixel
|| -1 == pID->EXT.TIF.uBitsPerSample), WMP_errUnsupportedFormat);
PI.uInterpretation = pID->EXT.TIF.uInterpretation;
PI.uSamplePerPixel = pID->EXT.TIF.uSamplePerPixel;
PI.uBitsPerSample = pID->EXT.TIF.uBitsPerSample;
PI.uSampleFormat = pID->EXT.TIF.uSampleFormat;
PI.grBit = pID->EXT.TIF.uExtraSamples == 1 || pID->EXT.TIF.uExtraSamples == 2 ||
/* Workaround for some images without correct info about alpha channel */
(pID->EXT.TIF.uExtraSamples == 0 && pID->EXT.TIF.uSamplePerPixel > 3) ? PK_pixfmtHasAlpha : 0x0;
PI.grBit |= pID->EXT.TIF.uExtraSamples == 1 ? PK_pixfmtPreMul : 0x0;
pID->fResX = (3 == pID->EXT.TIF.uResolutionUnit ? (Float)(pID->EXT.TIF.fResX * 2.54) : pID->EXT.TIF.fResX);//cm -> inch
pID->fResY = (3 == pID->EXT.TIF.uResolutionUnit ? (Float)(pID->EXT.TIF.fResY * 2.54) : pID->EXT.TIF.fResY);//cm -> inch
Call(PixelFormatLookup(&PI, LOOKUP_BACKWARD_TIF));
pID->guidPixFormat = *(PI.pGUIDPixFmt);
Cleanup:
return err;
}
//================================================================
// PKImageDecode_TIF
//================================================================
ERR PKImageDecode_Initialize_TIF(
PKTestDecode* pID,
struct WMPStream* pWS)
{
ERR err = WMP_errSuccess;
Call(PKTestDecode_Initialize(pID, pWS));
Call(ParseTifHeader(pID, pWS));
Cleanup:
return err;
}
ERR GetScanLineOffset(
PKTestDecode* pID,
I32 iLine,
U32 cbLine,
U32 *offLine)
{
*offLine = pID->EXT.TIF.uRowsPerStrip ?
(pID->EXT.TIF.uStripOffsets[iLine / pID->EXT.TIF.uRowsPerStrip] +
cbLine * (iLine % pID->EXT.TIF.uRowsPerStrip)) :
0;
return WMP_errSuccess;
}
ERR PKImageDecode_Copy_TIF(
PKTestDecode* pID,
const PKRect* pRect,
U8* pb,
U32 cbStride)
{
ERR err = WMP_errSuccess;
struct WMPStream* pS = pID->pStream;
PKPixelInfo PI;
U32 cbLine = 0;
I32 i = 0;
PI.pGUIDPixFmt = &pID->guidPixFormat;
PixelFormatLookup(&PI, LOOKUP_FORWARD);
cbLine = (BD_1 == PI.bdBitDepth ? ((PI.cbitUnit * pRect->Width + 7) >> 3) : (((PI.cbitUnit + 7) >> 3) * pRect->Width));
assert(0 == pRect->X && pID->uWidth == (U32)pRect->Width);
assert(cbLine <= cbStride);
for (i = 0; i < pRect->Height; ++i)
{
U32 offPixels = 0;
Call(GetScanLineOffset(pID, pRect->Y + i, cbLine, &offPixels));
Call(pS->SetPos(pS, offPixels));
Call(pS->Read(pS, pb + cbStride * i, cbLine));
if (PK_PI_W0 == pID->EXT.TIF.uInterpretation)
{
U32 j, begin = cbStride * (U32)i, end = begin + cbLine;
for (j = begin; j < end; ++j)
{
pb[j] = ~pb[j];
}
}
}
Cleanup:
return err;
}
ERR PKImageDecode_Release_TIF(PKTestDecode** ppID)
{
ERR err = WMP_errSuccess;
PKTestDecode *pID = *ppID;
Call(WMPFree(&pID->EXT.TIF.uStripOffsets));
Call(WMPFree(&pID->EXT.TIF.uStripByteCounts));
Call(PKTestDecode_Release(ppID));
Cleanup:
return err;
}
ERR PKImageDecode_Create_TIF(PKTestDecode** ppID)
{
ERR err = WMP_errSuccess;
PKTestDecode* pID = NULL;
Call(PKTestDecode_Create(ppID));
pID = *ppID;
pID->Initialize = PKImageDecode_Initialize_TIF;
pID->Copy = PKImageDecode_Copy_TIF;
pID->Release = PKImageDecode_Release_TIF;
Cleanup:
return err;
}