936 lines
27 KiB
C
936 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;
|
|
}
|
|
|