fixed issue with PHOTO_WHITEISZERO inverted grayscale images, added support for 1-D Group 3 Fax compressed images, added support for fill order 2 for monochrome images
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3176 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -31,15 +31,9 @@
|
||||
|
||||
#include "BitReader.h"
|
||||
|
||||
BitReader::BitReader(StreamBuffer *pstreambuf)
|
||||
BitReader::BitReader(uint16 fillOrder, StreamBuffer *pstreambuf, bool binitialRead)
|
||||
{
|
||||
finitStatus = B_BAD_VALUE;
|
||||
fnbytesRead = 0;
|
||||
fbitbuf = 0;
|
||||
fcurrentbit = 0;
|
||||
fpstreambuf = pstreambuf;
|
||||
if (fpstreambuf)
|
||||
finitStatus = ReadByte();
|
||||
SetTo(fillOrder, pstreambuf, binitialRead);
|
||||
}
|
||||
|
||||
BitReader::~BitReader()
|
||||
@@ -50,6 +44,25 @@ BitReader::~BitReader()
|
||||
fcurrentbit = 0;
|
||||
}
|
||||
|
||||
status_t
|
||||
BitReader::SetTo(uint16 fillOrder, StreamBuffer *pstreambuf, bool binitialRead)
|
||||
{
|
||||
finitStatus = B_OK;
|
||||
fnbytesRead = 0;
|
||||
fbitbuf = 0;
|
||||
fcurrentbit = 0;
|
||||
fpstreambuf = pstreambuf;
|
||||
ffillOrder = fillOrder;
|
||||
if (ffillOrder != 1 && ffillOrder != 2)
|
||||
finitStatus = B_BAD_VALUE;
|
||||
else if (!fpstreambuf)
|
||||
finitStatus = B_BAD_VALUE;
|
||||
else if (binitialRead)
|
||||
finitStatus = ReadByte();
|
||||
|
||||
return finitStatus;
|
||||
}
|
||||
|
||||
status_t
|
||||
BitReader::NextBit()
|
||||
{
|
||||
@@ -68,7 +81,10 @@ BitReader::PeekBit()
|
||||
if (!fcurrentbit && ReadByte() != B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
return (fbitbuf >> (fcurrentbit - 1)) & 1;
|
||||
if (ffillOrder == 1)
|
||||
return (fbitbuf >> (fcurrentbit - 1)) & 1;
|
||||
else
|
||||
return (fbitbuf >> (8 - fcurrentbit)) & 1;
|
||||
}
|
||||
|
||||
status_t
|
||||
|
||||
@@ -37,9 +37,13 @@
|
||||
|
||||
class BitReader {
|
||||
public:
|
||||
BitReader(StreamBuffer *pstreambuf);
|
||||
// Setup the BitReader to read from the given stream with the given fill order
|
||||
BitReader(uint16 fillOrder, StreamBuffer *pstreambuf, bool binitialRead = true);
|
||||
~BitReader();
|
||||
|
||||
// Change the BitReader to read from given stream with the given fill order
|
||||
status_t SetTo(uint16 fillOrder, StreamBuffer *pstreambuf, bool binitialRead = true);
|
||||
|
||||
status_t InitCheck() const { return finitStatus; };
|
||||
|
||||
uint32 BytesRead() const { return fnbytesRead; };
|
||||
@@ -59,6 +63,7 @@ private:
|
||||
StreamBuffer *fpstreambuf;
|
||||
status_t finitStatus;
|
||||
uint32 fnbytesRead;
|
||||
uint16 ffillOrder;
|
||||
uint8 fbitbuf;
|
||||
uint8 fcurrentbit;
|
||||
};
|
||||
|
||||
@@ -395,10 +395,7 @@ check_tiff_fields(TiffIfd &ifd, TiffDetails *pdetails, bool bAllocArrays)
|
||||
// will not be thrown when GetUint is called.
|
||||
// (If they are not present, that is fine, but if
|
||||
// they are present and have a non-default value,
|
||||
// that is a problem)
|
||||
if (ifd.HasField(TAG_FILL_ORDER) &&
|
||||
ifd.GetUint(TAG_FILL_ORDER) != 1)
|
||||
return B_NO_TRANSLATOR;
|
||||
// that is a problem)
|
||||
if (ifd.HasField(TAG_ORIENTATION) &&
|
||||
ifd.GetUint(TAG_ORIENTATION) != 1)
|
||||
return B_NO_TRANSLATOR;
|
||||
@@ -420,7 +417,7 @@ check_tiff_fields(TiffIfd &ifd, TiffDetails *pdetails, bool bAllocArrays)
|
||||
else
|
||||
compression = ifd.GetUint(TAG_COMPRESSION);
|
||||
|
||||
uint16 imageType = 0, bitsPerPixel = 0;
|
||||
uint16 imageType = 0, bitsPerPixel = 0, fillOrder = 1;
|
||||
switch (interpretation) {
|
||||
// black and white or grayscale images
|
||||
case PHOTO_WHITEZERO:
|
||||
@@ -429,6 +426,12 @@ check_tiff_fields(TiffIfd &ifd, TiffDetails *pdetails, bool bAllocArrays)
|
||||
if (ifd.HasField(TAG_SAMPLES_PER_PIXEL) &&
|
||||
ifd.GetUint(TAG_SAMPLES_PER_PIXEL) != 1)
|
||||
return B_NO_TRANSLATOR;
|
||||
|
||||
// Only the default value for T4 Options
|
||||
// is supported
|
||||
if (ifd.HasField(TAG_T4_OPTIONS) &&
|
||||
ifd.GetUint(TAG_T4_OPTIONS) != 0)
|
||||
return B_NO_TRANSLATOR;
|
||||
|
||||
// default value for bits per sample is 1
|
||||
if (!ifd.HasField(TAG_BITS_PER_SAMPLE) ||
|
||||
@@ -437,13 +440,23 @@ check_tiff_fields(TiffIfd &ifd, TiffDetails *pdetails, bool bAllocArrays)
|
||||
|
||||
if (compression != COMPRESSION_NONE &&
|
||||
compression != COMPRESSION_HUFFMAN &&
|
||||
compression != COMPRESSION_T4 &&
|
||||
compression != COMPRESSION_PACKBITS)
|
||||
return B_NO_TRANSLATOR;
|
||||
|
||||
if (ifd.HasField(TAG_FILL_ORDER)) {
|
||||
fillOrder = ifd.GetUint(TAG_FILL_ORDER);
|
||||
if (fillOrder != 1 && fillOrder != 2)
|
||||
return B_NO_TRANSLATOR;
|
||||
}
|
||||
|
||||
} else if (ifd.GetUint(TAG_BITS_PER_SAMPLE) == 4 ||
|
||||
ifd.GetUint(TAG_BITS_PER_SAMPLE) == 8) {
|
||||
bitsPerPixel = ifd.GetUint(TAG_BITS_PER_SAMPLE);
|
||||
|
||||
if (ifd.HasField(TAG_FILL_ORDER) &&
|
||||
ifd.GetUint(TAG_FILL_ORDER) != 1)
|
||||
return B_NO_TRANSLATOR;
|
||||
if (compression != COMPRESSION_NONE &&
|
||||
compression != COMPRESSION_PACKBITS)
|
||||
return B_NO_TRANSLATOR;
|
||||
@@ -456,6 +469,9 @@ check_tiff_fields(TiffIfd &ifd, TiffDetails *pdetails, bool bAllocArrays)
|
||||
|
||||
// Palette color images
|
||||
case PHOTO_PALETTE:
|
||||
if (ifd.HasField(TAG_FILL_ORDER) &&
|
||||
ifd.GetUint(TAG_FILL_ORDER) != 1)
|
||||
return B_NO_TRANSLATOR;
|
||||
if (compression != COMPRESSION_NONE &&
|
||||
compression != COMPRESSION_PACKBITS)
|
||||
return B_NO_TRANSLATOR;
|
||||
@@ -478,6 +494,9 @@ check_tiff_fields(TiffIfd &ifd, TiffDetails *pdetails, bool bAllocArrays)
|
||||
break;
|
||||
|
||||
case PHOTO_RGB:
|
||||
if (ifd.HasField(TAG_FILL_ORDER) &&
|
||||
ifd.GetUint(TAG_FILL_ORDER) != 1)
|
||||
return B_NO_TRANSLATOR;
|
||||
if (compression != COMPRESSION_NONE &&
|
||||
compression != COMPRESSION_PACKBITS)
|
||||
return B_NO_TRANSLATOR;
|
||||
@@ -494,6 +513,9 @@ check_tiff_fields(TiffIfd &ifd, TiffDetails *pdetails, bool bAllocArrays)
|
||||
break;
|
||||
|
||||
case PHOTO_SEPARATED:
|
||||
if (ifd.HasField(TAG_FILL_ORDER) &&
|
||||
ifd.GetUint(TAG_FILL_ORDER) != 1)
|
||||
return B_NO_TRANSLATOR;
|
||||
if (compression != COMPRESSION_NONE &&
|
||||
compression != COMPRESSION_PACKBITS)
|
||||
return B_NO_TRANSLATOR;
|
||||
@@ -525,13 +547,16 @@ check_tiff_fields(TiffIfd &ifd, TiffDetails *pdetails, bool bAllocArrays)
|
||||
rowsPerStrip = DEFAULT_ROWS_PER_STRIP;
|
||||
else
|
||||
rowsPerStrip = ifd.GetUint(TAG_ROWS_PER_STRIP);
|
||||
stripsPerImage = (height + rowsPerStrip - 1) / rowsPerStrip;
|
||||
if (rowsPerStrip == DEFAULT_ROWS_PER_STRIP)
|
||||
stripsPerImage = 1;
|
||||
else
|
||||
stripsPerImage = (height + rowsPerStrip - 1) / rowsPerStrip;
|
||||
|
||||
if (ifd.GetCount(TAG_STRIP_OFFSETS) != stripsPerImage ||
|
||||
ifd.GetCount(TAG_STRIP_BYTE_COUNTS) != stripsPerImage)
|
||||
return B_NO_TRANSLATOR;
|
||||
|
||||
// return read in details if output pointer is supplied
|
||||
// return read in details
|
||||
pdetails->width = width;
|
||||
pdetails->height = height;
|
||||
pdetails->compression = compression;
|
||||
@@ -540,6 +565,7 @@ check_tiff_fields(TiffIfd &ifd, TiffDetails *pdetails, bool bAllocArrays)
|
||||
pdetails->interpretation = interpretation;
|
||||
pdetails->bitsPerPixel = bitsPerPixel;
|
||||
pdetails->imageType = imageType;
|
||||
pdetails->fillOrder = fillOrder;
|
||||
|
||||
if (bAllocArrays) {
|
||||
ifd.GetUint32Array(TAG_STRIP_OFFSETS,
|
||||
@@ -823,22 +849,34 @@ tiff_to_bits(uint8 *ptiff, uint32 tifflen, uint8 *pbits, TiffDetails &details)
|
||||
}
|
||||
} else {
|
||||
// grayscale
|
||||
uint8 invert = 0;
|
||||
if (details.interpretation == PHOTO_WHITEZERO)
|
||||
invert = 0xff;
|
||||
|
||||
if (details.bitsPerPixel == 8) {
|
||||
for (uint32 i = 0; i < details.width; i++) {
|
||||
memset(pbits, invert + ptiff[i], 3);
|
||||
pbits += 4;
|
||||
if (details.interpretation == PHOTO_WHITEZERO) {
|
||||
for (uint32 i = 0; i < details.width; i++) {
|
||||
memset(pbits, 255 - ptiff[i], 3);
|
||||
pbits += 4;
|
||||
}
|
||||
} else {
|
||||
for (uint32 i = 0; i < details.width; i++) {
|
||||
memset(pbits, ptiff[i], 3);
|
||||
pbits += 4;
|
||||
}
|
||||
}
|
||||
} else if (details.bitsPerPixel == 4) {
|
||||
const uint8 mask = 0x0f;
|
||||
for (uint32 i = 0; i < details.width; i++) {
|
||||
uint8 values = (ptiff + (i / 2))[0];
|
||||
uint8 value = (values >> (4 * (1 - (i % 2)))) & mask;
|
||||
memset(pbits, invert + (value * 16), 3);
|
||||
pbits += 4;
|
||||
const uint8 mask = 0x0f;
|
||||
if (details.interpretation == PHOTO_WHITEZERO) {
|
||||
for (uint32 i = 0; i < details.width; i++) {
|
||||
uint8 values = (ptiff + (i / 2))[0];
|
||||
uint8 value = (values >> (4 * (1 - (i % 2)))) & mask;
|
||||
memset(pbits, 255 - (value * 16), 3);
|
||||
pbits += 4;
|
||||
}
|
||||
} else {
|
||||
for (uint32 i = 0; i < details.width; i++) {
|
||||
uint8 values = (ptiff + (i / 2))[0];
|
||||
uint8 value = (values >> (4 * (1 - (i % 2)))) & mask;
|
||||
memset(pbits, (value * 16), 3);
|
||||
pbits += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -958,11 +996,81 @@ unpack(StreamBuffer *pstreambuf, uint8 *tiffbuffer, uint32 tiffbufferlen)
|
||||
return read;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
TIFFTranslator::decode_t4(BitReader &stream, TiffDetails &details,
|
||||
uint8 *pbits, bool bfirstLine)
|
||||
{
|
||||
if (bfirstLine) {
|
||||
// read in and verify end-of-line sequence
|
||||
uint32 nzeros = 11;
|
||||
while (nzeros--)
|
||||
{
|
||||
if (stream.NextBit() != 0) {
|
||||
debugger("xxx");
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
if (stream.NextBit() != 1) {
|
||||
debugger("xxx");
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8 kblack = 0x00, kwhite = 0xff;
|
||||
uint8 colors[2];
|
||||
if (details.interpretation == PHOTO_WHITEZERO) {
|
||||
colors[0] = kwhite;
|
||||
colors[1] = kblack;
|
||||
} else {
|
||||
colors[0] = kblack;
|
||||
colors[1] = kwhite;
|
||||
}
|
||||
|
||||
uint32 pixelswrit = 0;
|
||||
DecodeTree *ptrees[2] = {fpwhiteTree, fpblackTree};
|
||||
uint8 currentcolor = 0;
|
||||
uint8 *pcurrentpixel = pbits;
|
||||
status_t value = 0;
|
||||
while (pixelswrit < details.width || value >= 64) {
|
||||
value = ptrees[currentcolor]->GetValue(stream);
|
||||
if (value < 0) {
|
||||
debugger("value < 0");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (pixelswrit + value > details.width) {
|
||||
debugger("gone past end of line");
|
||||
return B_ERROR;
|
||||
}
|
||||
pixelswrit += value;
|
||||
|
||||
// covert run length to B_RGB32 pixels
|
||||
uint32 pixelsleft = value;
|
||||
while (pixelsleft--) {
|
||||
memset(pcurrentpixel, colors[currentcolor], 3);
|
||||
pcurrentpixel += 4;
|
||||
}
|
||||
|
||||
if (value < 64)
|
||||
currentcolor = 1 - currentcolor;
|
||||
}
|
||||
|
||||
// read in end-of-line sequence
|
||||
uint32 nzeros = 11;
|
||||
while (nzeros--)
|
||||
{
|
||||
stream.NextBit();
|
||||
}
|
||||
stream.NextBit();
|
||||
|
||||
return stream.BytesRead();
|
||||
}
|
||||
|
||||
ssize_t
|
||||
TIFFTranslator::decode_huffman(StreamBuffer *pstreambuf, TiffDetails &details,
|
||||
uint8 *pbits)
|
||||
{
|
||||
BitReader stream(pstreambuf);
|
||||
BitReader stream(details.fillOrder, pstreambuf);
|
||||
if (stream.InitCheck() != B_OK) {
|
||||
debugger("stream init error");
|
||||
return B_ERROR;
|
||||
@@ -1028,7 +1136,8 @@ TIFFTranslator::translate_from_tiff(BPositionIO *inSource, ssize_t amtread,
|
||||
|
||||
// If TIFF uses Huffman compression, load
|
||||
// trees for decoding Huffman compression
|
||||
if (details.compression == COMPRESSION_HUFFMAN) {
|
||||
if (details.compression == COMPRESSION_HUFFMAN ||
|
||||
details.compression == COMPRESSION_T4) {
|
||||
result = LoadHuffmanTrees();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
@@ -1085,12 +1194,16 @@ TIFFTranslator::translate_from_tiff(BPositionIO *inSource, ssize_t amtread,
|
||||
// buffer for making reading compressed data
|
||||
// fast and convenient
|
||||
StreamBuffer *pstreambuf = NULL;
|
||||
BitReader bitreader(details.fillOrder, pstreambuf, false);
|
||||
// this creates an invalid bitreader, but it is
|
||||
// initialized properly with the SetTo() call
|
||||
if (details.compression != COMPRESSION_NONE) {
|
||||
pstreambuf = new StreamBuffer(inSource, 2048, false);
|
||||
if (pstreambuf->InitCheck() != B_OK)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
bool bfirstLine = true;
|
||||
for (uint32 i = 0; i < details.stripsPerImage; i++) {
|
||||
uint32 read = 0;
|
||||
|
||||
@@ -1100,6 +1213,10 @@ TIFFTranslator::translate_from_tiff(BPositionIO *inSource, ssize_t amtread,
|
||||
!pstreambuf->Seek(details.pstripOffsets[i]))
|
||||
return B_NO_TRANSLATOR;
|
||||
|
||||
if (details.compression == COMPRESSION_T4 &&
|
||||
bitreader.SetTo(details.fillOrder, pstreambuf) != B_OK)
|
||||
return B_NO_TRANSLATOR;
|
||||
|
||||
// Read / Write one line at a time for each
|
||||
// line in the strip
|
||||
while (read < details.pstripByteCounts[i]) {
|
||||
@@ -1121,6 +1238,19 @@ TIFFTranslator::translate_from_tiff(BPositionIO *inSource, ssize_t amtread,
|
||||
ret = -1;
|
||||
break;
|
||||
|
||||
case COMPRESSION_T4:
|
||||
ret = decode_t4(bitreader, details, outbuffer,
|
||||
bfirstLine);
|
||||
if (ret < 1)
|
||||
// break out of while loop
|
||||
ret = -1;
|
||||
else
|
||||
ret -= read;
|
||||
// unlike other decoders, decode_t4 retruns
|
||||
// the total bytes read from the current
|
||||
// strip
|
||||
break;
|
||||
|
||||
case COMPRESSION_PACKBITS:
|
||||
ret = unpack(pstreambuf, inbuffer, inbufferlen);
|
||||
if (ret < 1)
|
||||
@@ -1132,9 +1262,11 @@ TIFFTranslator::translate_from_tiff(BPositionIO *inSource, ssize_t amtread,
|
||||
break;
|
||||
|
||||
read += ret;
|
||||
if (details.compression != COMPRESSION_HUFFMAN)
|
||||
if (details.compression != COMPRESSION_HUFFMAN &&
|
||||
details.compression != COMPRESSION_T4)
|
||||
tiff_to_bits(inbuffer, inbufferlen, outbuffer, details);
|
||||
outDestination->Write(outbuffer, outbufferlen);
|
||||
bfirstLine = false;
|
||||
}
|
||||
// If while loop was broken...
|
||||
if (read < details.pstripByteCounts[i]) {
|
||||
|
||||
@@ -83,6 +83,7 @@ public:
|
||||
uint16 interpretation;
|
||||
uint16 bitsPerPixel;
|
||||
uint16 imageType;
|
||||
uint16 fillOrder;
|
||||
};
|
||||
|
||||
class TIFFTranslator : public BTranslator {
|
||||
@@ -137,6 +138,8 @@ private:
|
||||
|
||||
ssize_t decode_huffman(StreamBuffer *pstreambuf, TiffDetails &details,
|
||||
uint8 *pbits);
|
||||
ssize_t decode_t4(BitReader &stream, TiffDetails &details,
|
||||
uint8 *pbits, bool bfirstLine);
|
||||
|
||||
status_t translate_from_tiff(BPositionIO *inSource, ssize_t amtread,
|
||||
uint8 *read, swap_action swp, uint32 outType,
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#define COMPRESSION_T4 3
|
||||
#define COMPRESSION_T6 4
|
||||
#define COMPRESSION_LZW 5
|
||||
#define COMPRESSION_JPEG 6
|
||||
#define COMPRESSION_PACKBITS 32773
|
||||
#define TAG_PHOTO_INTERPRETATION 262
|
||||
#define PHOTO_WHITEZERO 0
|
||||
@@ -69,6 +70,13 @@
|
||||
#define DEFAULT_ROWS_PER_STRIP 4294967295UL
|
||||
#define TAG_STRIP_BYTE_COUNTS 279
|
||||
#define TAG_PLANAR_CONFIGURATION 284
|
||||
#define TAG_T4_OPTIONS 292
|
||||
// (Also called Group3Options)
|
||||
#define T4_2D 1
|
||||
#define T4_UNCOMPRESSED 2
|
||||
#define T4_FILL_BYTE_BOUNDARY 4
|
||||
#define TAG_T6_OPTIONS 293
|
||||
// (Also called Group4Options)
|
||||
#define TAG_RESOLUTION_UNIT 296
|
||||
#define TAG_COLOR_MAP 320
|
||||
#define TAG_INK_SET 332
|
||||
|
||||
Reference in New Issue
Block a user