Merge pull request #5364 from wiredfool/4641_merge

Add support for reading TIFFs with PlanarConfiguration=2
This commit is contained in:
wiredfool 2021-03-28 14:33:42 +01:00 committed by GitHub
commit d0612030a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 493 additions and 185 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -17,7 +17,6 @@ from .helper import (
assert_image_similar, assert_image_similar,
assert_image_similar_tofile, assert_image_similar_tofile,
hopper, hopper,
is_big_endian,
skip_unless_feature, skip_unless_feature,
) )
@ -824,14 +823,12 @@ class TestFileLibTiff(LibTiffTestCase):
assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5) assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
@pytest.mark.valgrind_known_error(reason="Known Failing") @pytest.mark.valgrind_known_error(reason="Known Failing")
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
def test_strip_ycbcr_jpeg_2x2_sampling(self): def test_strip_ycbcr_jpeg_2x2_sampling(self):
infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif" infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif"
with Image.open(infile) as im: with Image.open(infile) as im:
assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5) assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
@pytest.mark.valgrind_known_error(reason="Known Failing") @pytest.mark.valgrind_known_error(reason="Known Failing")
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
def test_strip_ycbcr_jpeg_1x1_sampling(self): def test_strip_ycbcr_jpeg_1x1_sampling(self):
infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif" infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif"
with Image.open(infile) as im: with Image.open(infile) as im:
@ -843,20 +840,57 @@ class TestFileLibTiff(LibTiffTestCase):
assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5) assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
@pytest.mark.valgrind_known_error(reason="Known Failing") @pytest.mark.valgrind_known_error(reason="Known Failing")
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
def test_tiled_ycbcr_jpeg_1x1_sampling(self): def test_tiled_ycbcr_jpeg_1x1_sampling(self):
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif" infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif"
with Image.open(infile) as im: with Image.open(infile) as im:
assert_image_equal_tofile(im, "Tests/images/flower2.jpg") assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
@pytest.mark.valgrind_known_error(reason="Known Failing") @pytest.mark.valgrind_known_error(reason="Known Failing")
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
def test_tiled_ycbcr_jpeg_2x2_sampling(self): def test_tiled_ycbcr_jpeg_2x2_sampling(self):
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif" infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif"
with Image.open(infile) as im: with Image.open(infile) as im:
assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5) assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian") def test_strip_planar_rgb(self):
# gdal_translate -co TILED=no -co INTERLEAVE=BAND -co COMPRESS=LZW \
# tiff_strip_raw.tif tiff_strip_planar_lzw.tiff
infile = "Tests/images/tiff_strip_planar_lzw.tiff"
with Image.open(infile) as im:
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
def test_tiled_planar_rgb(self):
# gdal_translate -co TILED=yes -co INTERLEAVE=BAND -co COMPRESS=LZW \
# tiff_tiled_raw.tif tiff_tiled_planar_lzw.tiff
infile = "Tests/images/tiff_tiled_planar_lzw.tiff"
with Image.open(infile) as im:
assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
def test_tiled_planar_16bit_RGB(self):
# gdal_translate -co TILED=yes -co INTERLEAVE=BAND -co COMPRESS=LZW \
# tiff_16bit_RGB.tiff tiff_tiled_planar_16bit_RGB.tiff
with Image.open("Tests/images/tiff_tiled_planar_16bit_RGB.tiff") as im:
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png")
def test_strip_planar_16bit_RGB(self):
# gdal_translate -co TILED=no -co INTERLEAVE=BAND -co COMPRESS=LZW \
# tiff_16bit_RGB.tiff tiff_strip_planar_16bit_RGB.tiff
with Image.open("Tests/images/tiff_strip_planar_16bit_RGB.tiff") as im:
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png")
def test_tiled_planar_16bit_RGBa(self):
# gdal_translate -co TILED=yes \
# -co INTERLEAVE=BAND -co COMPRESS=LZW -co ALPHA=PREMULTIPLIED \
# tiff_16bit_RGBa.tiff tiff_tiled_planar_16bit_RGBa.tiff
with Image.open("Tests/images/tiff_tiled_planar_16bit_RGBa.tiff") as im:
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png")
def test_strip_planar_16bit_RGBa(self):
# gdal_translate -co TILED=no \
# -co INTERLEAVE=BAND -co COMPRESS=LZW -co ALPHA=PREMULTIPLIED \
# tiff_16bit_RGBa.tiff tiff_strip_planar_16bit_RGBa.tiff
with Image.open("Tests/images/tiff_strip_planar_16bit_RGBa.tiff") as im:
assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png")
def test_old_style_jpeg(self): def test_old_style_jpeg(self):
infile = "Tests/images/old-style-jpeg-compression.tif" infile = "Tests/images/old-style-jpeg-compression.tif"
with Image.open(infile) as im: with Image.open(infile) as im:

View File

@ -320,6 +320,23 @@ class TestLibUnpack:
self.assert_unpack("RGB", "G", 1, (0, 1, 0), (0, 2, 0), (0, 3, 0)) self.assert_unpack("RGB", "G", 1, (0, 1, 0), (0, 2, 0), (0, 3, 0))
self.assert_unpack("RGB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3)) self.assert_unpack("RGB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3))
self.assert_unpack("RGB", "R;16B", 2, (1, 0, 0), (3, 0, 0), (5, 0, 0))
self.assert_unpack("RGB", "G;16B", 2, (0, 1, 0), (0, 3, 0), (0, 5, 0))
self.assert_unpack("RGB", "B;16B", 2, (0, 0, 1), (0, 0, 3), (0, 0, 5))
self.assert_unpack("RGB", "R;16L", 2, (2, 0, 0), (4, 0, 0), (6, 0, 0))
self.assert_unpack("RGB", "G;16L", 2, (0, 2, 0), (0, 4, 0), (0, 6, 0))
self.assert_unpack("RGB", "B;16L", 2, (0, 0, 2), (0, 0, 4), (0, 0, 6))
if sys.byteorder == "little":
self.assert_unpack("RGB", "R;16N", 2, (2, 0, 0), (4, 0, 0), (6, 0, 0))
self.assert_unpack("RGB", "G;16N", 2, (0, 2, 0), (0, 4, 0), (0, 6, 0))
self.assert_unpack("RGB", "B;16N", 2, (0, 0, 2), (0, 0, 4), (0, 0, 6))
else:
self.assert_unpack("RGB", "R;16N", 2, (1, 0, 0), (3, 0, 0), (5, 0, 0))
self.assert_unpack("RGB", "G;16N", 2, (0, 1, 0), (0, 3, 0), (0, 5, 0))
self.assert_unpack("RGB", "B;16N", 2, (0, 0, 1), (0, 0, 3), (0, 0, 5))
def test_RGBA(self): def test_RGBA(self):
self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6)) self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6))
self.assert_unpack( self.assert_unpack(
@ -450,6 +467,43 @@ class TestLibUnpack:
self.assert_unpack("RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) self.assert_unpack("RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0))
self.assert_unpack("RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) self.assert_unpack("RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3))
self.assert_unpack("RGBA", "R;16B", 2, (1, 0, 0, 0), (3, 0, 0, 0), (5, 0, 0, 0))
self.assert_unpack("RGBA", "G;16B", 2, (0, 1, 0, 0), (0, 3, 0, 0), (0, 5, 0, 0))
self.assert_unpack("RGBA", "B;16B", 2, (0, 0, 1, 0), (0, 0, 3, 0), (0, 0, 5, 0))
self.assert_unpack("RGBA", "A;16B", 2, (0, 0, 0, 1), (0, 0, 0, 3), (0, 0, 0, 5))
self.assert_unpack("RGBA", "R;16L", 2, (2, 0, 0, 0), (4, 0, 0, 0), (6, 0, 0, 0))
self.assert_unpack("RGBA", "G;16L", 2, (0, 2, 0, 0), (0, 4, 0, 0), (0, 6, 0, 0))
self.assert_unpack("RGBA", "B;16L", 2, (0, 0, 2, 0), (0, 0, 4, 0), (0, 0, 6, 0))
self.assert_unpack("RGBA", "A;16L", 2, (0, 0, 0, 2), (0, 0, 0, 4), (0, 0, 0, 6))
if sys.byteorder == "little":
self.assert_unpack(
"RGBA", "R;16N", 2, (2, 0, 0, 0), (4, 0, 0, 0), (6, 0, 0, 0)
)
self.assert_unpack(
"RGBA", "G;16N", 2, (0, 2, 0, 0), (0, 4, 0, 0), (0, 6, 0, 0)
)
self.assert_unpack(
"RGBA", "B;16N", 2, (0, 0, 2, 0), (0, 0, 4, 0), (0, 0, 6, 0)
)
self.assert_unpack(
"RGBA", "A;16N", 2, (0, 0, 0, 2), (0, 0, 0, 4), (0, 0, 0, 6)
)
else:
self.assert_unpack(
"RGBA", "R;16N", 2, (1, 0, 0, 0), (3, 0, 0, 0), (5, 0, 0, 0)
)
self.assert_unpack(
"RGBA", "G;16N", 2, (0, 1, 0, 0), (0, 3, 0, 0), (0, 5, 0, 0)
)
self.assert_unpack(
"RGBA", "B;16N", 2, (0, 0, 1, 0), (0, 0, 3, 0), (0, 0, 5, 0)
)
self.assert_unpack(
"RGBA", "A;16N", 2, (0, 0, 0, 1), (0, 0, 0, 3), (0, 0, 0, 5)
)
def test_RGBa(self): def test_RGBa(self):
self.assert_unpack( self.assert_unpack(
"RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12) "RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)

View File

@ -1324,6 +1324,15 @@ class TiffImageFile(ImageFile.ImageFile):
if ";16L" in rawmode: if ";16L" in rawmode:
rawmode = rawmode.replace(";16L", ";16N") rawmode = rawmode.replace(";16L", ";16N")
# YCbCr images with new jpeg compression with pixels in one plane
# unpacked straight into RGB values
if (
photo == 6
and self._compression == "jpeg"
and self._planar_configuration == 1
):
rawmode = "RGB"
# Offset in the tile tuple is 0, we go from 0,0 to # Offset in the tile tuple is 0, we go from 0,0 to
# w,h, and we only do this once -- eds # w,h, and we only do this once -- eds
a = (rawmode, self._compression, False, self.tag_v2.offset) a = (rawmode, self._compression, False, self.tag_v2.offset)

View File

@ -213,24 +213,63 @@ ImagingLibTiffInit(ImagingCodecState state, int fp, uint32 offset) {
} }
int int
_decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) { _pickUnpackers(Imaging im, ImagingCodecState state, TIFF *tiff, uint16 planarconfig, ImagingShuffler *unpackers) {
// To avoid dealing with YCbCr subsampling, let libtiff handle it // if number of bands is 1, there is no difference with contig case
if (planarconfig == PLANARCONFIG_SEPARATE && im->bands > 1) {
uint16 bits_per_sample = 8;
TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
if (bits_per_sample != 8 && bits_per_sample != 16) {
TRACE(("Invalid value for bits per sample: %d\n", bits_per_sample));
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
// We'll pick appropriate set of unpackers depending on planar_configuration
// It does not matter if data is RGB(A), CMYK or LUV really,
// we just copy it plane by plane
unpackers[0] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "R;16N" : "R", NULL);
unpackers[1] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "G;16N" : "G", NULL);
unpackers[2] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "B;16N" : "B", NULL);
unpackers[3] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "A;16N" : "A", NULL);
return im->bands;
} else {
unpackers[0] = state->shuffle;
return 1;
}
}
int
_decodeAsRGBA(Imaging im, ImagingCodecState state, TIFF *tiff) {
// To avoid dealing with YCbCr subsampling and other complications, let libtiff handle it
// Use a TIFFRGBAImage wrapping the tiff image, and let libtiff handle // Use a TIFFRGBAImage wrapping the tiff image, and let libtiff handle
// all of the conversion. Metadata read from the TIFFRGBAImage could // all of the conversion. Metadata read from the TIFFRGBAImage could
// be different from the metadata that the base tiff returns. // be different from the metadata that the base tiff returns.
INT32 strip_row; INT32 current_row;
UINT8 *new_data; UINT8 *new_data;
UINT32 rows_per_strip, row_byte_size, rows_to_read; UINT32 rows_per_block, row_byte_size, rows_to_read;
int ret; int ret;
TIFFRGBAImage img; TIFFRGBAImage img;
char emsg[1024] = ""; char emsg[1024] = "";
ret = TIFFGetFieldDefaulted(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip); // Since using TIFFRGBAImage* functions, we can read whole tiff into rastrr in one call
if (ret != 1) { // Let's select smaller block size. Multiplying image width by (tile length OR rows per strip)
rows_per_strip = state->ysize; // gives us manageable block size in pixels
if (TIFFIsTiled(tiff)) {
ret = TIFFGetFieldDefaulted(tiff, TIFFTAG_TILELENGTH, &rows_per_block);
} }
TRACE(("RowsPerStrip: %u \n", rows_per_strip)); else {
ret = TIFFGetFieldDefaulted(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_block);
}
if (ret != 1) {
rows_per_block = state->ysize;
}
TRACE(("RowsPerBlock: %u \n", rows_per_block));
if (!(TIFFRGBAImageOK(tiff, emsg) && TIFFRGBAImageBegin(&img, tiff, 0, emsg))) { if (!(TIFFRGBAImageOK(tiff, emsg) && TIFFRGBAImageBegin(&img, tiff, 0, emsg))) {
TRACE(("Decode error, msg: %s", emsg)); TRACE(("Decode error, msg: %s", emsg));
@ -250,69 +289,73 @@ _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) {
state->ysize, state->ysize,
img.height)); img.height));
state->errcode = IMAGING_CODEC_BROKEN; state->errcode = IMAGING_CODEC_BROKEN;
goto decodeycbcr_err; goto decodergba_err;
} }
/* overflow check for row byte size */ /* overflow check for row byte size */
if (INT_MAX / 4 < img.width) { if (INT_MAX / 4 < img.width) {
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
goto decodeycbcr_err; goto decodergba_err;
} }
// TiffRGBAImages are 32bits/pixel. // TiffRGBAImages are 32bits/pixel.
row_byte_size = img.width * 4; row_byte_size = img.width * 4;
/* overflow check for realloc */ /* overflow check for realloc */
if (INT_MAX / row_byte_size < rows_per_strip) { if (INT_MAX / row_byte_size < rows_per_block) {
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
goto decodeycbcr_err; goto decodergba_err;
} }
state->bytes = rows_per_strip * row_byte_size; state->bytes = rows_per_block * row_byte_size;
TRACE(("StripSize: %d \n", state->bytes)); TRACE(("BlockSize: %d \n", state->bytes));
/* realloc to fit whole strip */ /* realloc to fit whole strip */
/* malloc check above */ /* malloc check above */
new_data = realloc(state->buffer, state->bytes); new_data = realloc(state->buffer, state->bytes);
if (!new_data) { if (!new_data) {
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
goto decodeycbcr_err; goto decodergba_err;
} }
state->buffer = new_data; state->buffer = new_data;
for (; state->y < state->ysize; state->y += rows_per_strip) { for (; state->y < state->ysize; state->y += rows_per_block) {
img.row_offset = state->y; img.row_offset = state->y;
rows_to_read = min(rows_per_strip, img.height - state->y); rows_to_read = min(rows_per_block, img.height - state->y);
if (!TIFFRGBAImageGet(&img, (UINT32 *)state->buffer, img.width, rows_to_read)) { if (!TIFFRGBAImageGet(&img, (UINT32 *)state->buffer, img.width, rows_to_read)) {
TRACE(("Decode Error, y: %d\n", state->y)); TRACE(("Decode Error, y: %d\n", state->y));
state->errcode = IMAGING_CODEC_BROKEN; state->errcode = IMAGING_CODEC_BROKEN;
goto decodeycbcr_err; goto decodergba_err;
} }
#if WORDS_BIGENDIAN
TIFFSwabArrayOfLong((UINT32 *)state->buffer, img.width * rows_to_read);
#endif
TRACE(("Decoded strip for row %d \n", state->y)); TRACE(("Decoded strip for row %d \n", state->y));
// iterate over each row in the strip and stuff data into image // iterate over each row in the strip and stuff data into image
for (strip_row = 0; for (current_row = 0;
strip_row < min((INT32)rows_per_strip, state->ysize - state->y); current_row < min((INT32)rows_per_block, state->ysize - state->y);
strip_row++) { current_row++) {
TRACE(("Writing data into line %d ; \n", state->y + strip_row)); TRACE(("Writing data into line %d ; \n", state->y + current_row));
// UINT8 * bbb = state->buffer + strip_row * (state->bytes / // UINT8 * bbb = state->buffer + current_row * (state->bytes /
// rows_per_strip); TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], // rows_per_block); TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0],
// ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); // ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
state->shuffle( state->shuffle(
(UINT8 *)im->image[state->y + state->yoff + strip_row] + (UINT8 *)im->image[state->y + state->yoff + current_row] +
state->xoff * im->pixelsize, state->xoff * im->pixelsize,
state->buffer + strip_row * row_byte_size, state->buffer + current_row * row_byte_size,
state->xsize); state->xsize);
} }
} }
decodeycbcr_err: decodergba_err:
TIFFRGBAImageEnd(&img); TIFFRGBAImageEnd(&img);
if (state->errcode != 0) { if (state->errcode != 0) {
return -1; return -1;
@ -321,41 +364,154 @@ decodeycbcr_err:
} }
int int
_decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff) { _decodeTile(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, ImagingShuffler *unpackers) {
INT32 strip_row; INT32 x, y, tile_y, current_tile_length, current_tile_width;
UINT32 tile_width, tile_length;
tsize_t tile_bytes_size, row_byte_size;
UINT8 *new_data; UINT8 *new_data;
UINT32 rows_per_strip, row_byte_size;
int ret;
ret = TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip); tile_bytes_size = TIFFTileSize(tiff);
if (ret != 1) {
rows_per_strip = state->ysize; if (tile_bytes_size == 0) {
TRACE(("Decode Error, Can not calculate TileSize\n"));
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
} }
TRACE(("RowsPerStrip: %u \n", rows_per_strip));
// We could use TIFFStripSize, but for YCbCr data it returns subsampled data size row_byte_size = TIFFTileRowSize(tiff);
row_byte_size = (state->xsize * state->bits + 7) / 8;
if (row_byte_size == 0 || row_byte_size > tile_bytes_size) {
TRACE(("Decode Error, Can not calculate TileRowSize\n"));
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
/* overflow check for realloc */ /* overflow check for realloc */
if (INT_MAX / row_byte_size < rows_per_strip) { if (tile_bytes_size > INT_MAX - 1) {
state->errcode = IMAGING_CODEC_MEMORY; state->errcode = IMAGING_CODEC_MEMORY;
return -1; return -1;
} }
state->bytes = rows_per_strip * row_byte_size; TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tile_width);
TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tile_length);
if (tile_width > INT_MAX || tile_length > INT_MAX) {
// state->x and state->y are ints
state->errcode = IMAGING_CODEC_MEMORY;
return -1;
}
if (tile_bytes_size > ((tile_length * state->bits / planes + 7) / 8) * tile_width) {
// If the tile size as expected by LibTiff isn't what we're expecting, abort.
// man: TIFFTileSize returns the equivalent size for a tile of data as it would be returned in a
// call to TIFFReadTile ...
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
state->bytes = tile_bytes_size;
TRACE(("TIFFTileSize: %d\n", state->bytes));
/* realloc to fit whole tile */
/* malloc check above */
new_data = realloc(state->buffer, state->bytes);
if (!new_data) {
state->errcode = IMAGING_CODEC_MEMORY;
return -1;
}
state->buffer = new_data;
for (y = state->yoff; y < state->ysize; y += tile_length) {
int plane;
for (plane = 0; plane < planes; plane++) {
ImagingShuffler shuffler = unpackers[plane];
for (x = state->xoff; x < state->xsize; x += tile_width) {
/* Sanity Check. Apparently in some cases, the TiffReadRGBA* functions
have a different view of the size of the tiff than we're getting from
other functions. So, we need to check here.
*/
if (!TIFFCheckTile(tiff, x, y, 0, plane)) {
TRACE(("Check Tile Error, Tile at %dx%d\n", x, y));
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
if (TIFFReadTile(tiff, (tdata_t)state->buffer, x, y, 0, plane) == -1) {
TRACE(("Decode Error, Tile at %dx%d\n", x, y));
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
TRACE(("Read tile at %dx%d; \n\n", x, y));
current_tile_width = min((INT32) tile_width, state->xsize - x);
current_tile_length = min((INT32) tile_length, state->ysize - y);
// iterate over each line in the tile and stuff data into image
for (tile_y = 0; tile_y < current_tile_length; tile_y++) {
TRACE(("Writing tile data at %dx%d using tile_width: %d; \n", tile_y + y, x, current_tile_width));
// UINT8 * bbb = state->buffer + tile_y * row_byte_size;
// TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
shuffler((UINT8*) im->image[tile_y + y] + x * im->pixelsize,
state->buffer + tile_y * row_byte_size,
current_tile_width
);
}
}
}
}
return 0;
}
int
_decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, ImagingShuffler *unpackers) {
INT32 strip_row = 0;
UINT8 *new_data;
UINT32 rows_per_strip;
int ret;
tsize_t strip_size, row_byte_size;
ret = TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
if (ret != 1 || rows_per_strip==(UINT32)(-1)) {
rows_per_strip = state->ysize;
}
if (rows_per_strip > INT_MAX) {
state->errcode = IMAGING_CODEC_MEMORY;
return -1;
}
TRACE(("RowsPerStrip: %u\n", rows_per_strip));
strip_size = TIFFStripSize(tiff);
if (strip_size > INT_MAX - 1) {
state->errcode = IMAGING_CODEC_MEMORY;
return -1;
}
if (strip_size > ((state->xsize * state->bits / planes + 7) / 8) * rows_per_strip) {
// If the strip size as expected by LibTiff isn't what we're expecting, abort.
// man: TIFFStripSize returns the equivalent size for a strip of data as it would be returned in a
// call to TIFFReadEncodedStrip ...
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
state->bytes = strip_size;
TRACE(("StripSize: %d \n", state->bytes)); TRACE(("StripSize: %d \n", state->bytes));
if (TIFFStripSize(tiff) > state->bytes) { row_byte_size = TIFFScanlineSize(tiff);
// If the strip size as expected by LibTiff isn't what we're expecting, abort.
// man: TIFFStripSize returns the equivalent size for a strip of data as it
// would be returned in a
// call to TIFFReadEncodedStrip ...
state->errcode = IMAGING_CODEC_MEMORY; if (row_byte_size == 0 || row_byte_size > strip_size) {
state->errcode = IMAGING_CODEC_BROKEN;
return -1; return -1;
} }
TRACE(("RowsByteSize: %u \n", row_byte_size));
/* realloc to fit whole strip */ /* realloc to fit whole strip */
/* malloc check above */ /* malloc check above */
new_data = realloc(state->buffer, state->bytes); new_data = realloc(state->buffer, state->bytes);
@ -367,35 +523,35 @@ _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff) {
state->buffer = new_data; state->buffer = new_data;
for (; state->y < state->ysize; state->y += rows_per_strip) { for (; state->y < state->ysize; state->y += rows_per_strip) {
if (TIFFReadEncodedStrip( int plane;
tiff, for (plane = 0; plane < planes; plane++) {
TIFFComputeStrip(tiff, state->y, 0), ImagingShuffler shuffler = unpackers[plane];
(tdata_t)state->buffer, if (TIFFReadEncodedStrip(tiff, TIFFComputeStrip(tiff, state->y, plane), (tdata_t)state->buffer, strip_size) == -1) {
-1) == -1) { TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0)));
TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))); state->errcode = IMAGING_CODEC_BROKEN;
state->errcode = IMAGING_CODEC_BROKEN; return -1;
return -1; }
}
TRACE(("Decoded strip for row %d \n", state->y)); TRACE(("Decoded strip for row %d \n", state->y));
// iterate over each row in the strip and stuff data into image // iterate over each row in the strip and stuff data into image
for (strip_row = 0; for (strip_row = 0;
strip_row < min((INT32)rows_per_strip, state->ysize - state->y); strip_row < min((INT32) rows_per_strip, state->ysize - state->y);
strip_row++) { strip_row++) {
TRACE(("Writing data into line %d ; \n", state->y + strip_row)); TRACE(("Writing data into line %d ; \n", state->y + strip_row));
// UINT8 * bbb = state->buffer + strip_row * (state->bytes / // UINT8 * bbb = state->buffer + strip_row * (state->bytes / rows_per_strip);
// rows_per_strip); TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], // TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
// ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
state->shuffle( shuffler(
(UINT8 *)im->image[state->y + state->yoff + strip_row] + (UINT8*) im->image[state->y + state->yoff + strip_row] +
state->xoff * im->pixelsize, state->xoff * im->pixelsize,
state->buffer + strip_row * row_byte_size, state->buffer + strip_row * row_byte_size,
state->xsize); state->xsize);
}
} }
} }
return 0; return 0;
} }
@ -407,7 +563,13 @@ ImagingLibTiffDecode(
char *mode = "r"; char *mode = "r";
TIFF *tiff; TIFF *tiff;
uint16 photometric = 0; // init to not PHOTOMETRIC_YCBCR uint16 photometric = 0; // init to not PHOTOMETRIC_YCBCR
int isYCbCr = 0; uint16 compression;
int readAsRGBA = 0;
uint16 planarconfig = 0;
int planes = 1;
ImagingShuffler unpackers[4];
memset(unpackers, 0, sizeof(ImagingShuffler) * 4);
/* buffer is the encoded file, bytes is the length of the encoded file */ /* buffer is the encoded file, bytes is the length of the encoded file */
/* it all ends up in state->buffer, which is a uint8* from Imaging.h */ /* it all ends up in state->buffer, which is a uint8* from Imaging.h */
@ -502,134 +664,64 @@ ImagingLibTiffDecode(
} }
} }
TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric); TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
isYCbCr = photometric == PHOTOMETRIC_YCBCR; TIFFGetField(tiff, TIFFTAG_COMPRESSION, &compression);
TIFFGetFieldDefaulted(tiff, TIFFTAG_PLANARCONFIG, &planarconfig);
if (TIFFIsTiled(tiff)) { // Dealing with YCbCr images is complicated in case if subsampling
INT32 x, y, tile_y; // Let LibTiff read them as RGBA
UINT32 tile_width, tile_length, current_tile_length, current_line, readAsRGBA = photometric == PHOTOMETRIC_YCBCR;
current_tile_width, row_byte_size;
UINT8 *new_data;
TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tile_width); if (readAsRGBA && compression == COMPRESSION_JPEG && planarconfig == PLANARCONFIG_CONTIG) {
TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tile_length); // If using new JPEG compression, let libjpeg do RGB convertion for performance reasons
TIFFSetField(tiff, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
readAsRGBA = 0;
}
/* overflow check for row_byte_size calculation */ if (readAsRGBA) {
if ((UINT32)INT_MAX / state->bits < tile_width) { _decodeAsRGBA(im, state, tiff);
state->errcode = IMAGING_CODEC_MEMORY; }
else {
planes = _pickUnpackers(im, state, tiff, planarconfig, unpackers);
if (planes <= 0) {
goto decode_err; goto decode_err;
} }
if (isYCbCr) { if (TIFFIsTiled(tiff)) {
row_byte_size = tile_width * 4; _decodeTile(im, state, tiff, planes, unpackers);
/* sanity check, we use this value in shuffle below */ }
if (im->pixelsize != 4) { else {
state->errcode = IMAGING_CODEC_BROKEN; _decodeStrip(im, state, tiff, planes, unpackers);
goto decode_err;
}
} else {
// We could use TIFFTileSize, but for YCbCr data it returns subsampled data
// size
row_byte_size = (tile_width * state->bits + 7) / 8;
} }
/* overflow check for realloc */ if (!state->errcode) {
if (INT_MAX / row_byte_size < tile_length) { // Check if raw mode was RGBa and it was stored on separate planes
state->errcode = IMAGING_CODEC_MEMORY; // so we have to convert it to RGBA
goto decode_err; if (planes > 3 && strcmp(im->mode, "RGBA") == 0) {
} uint16 extrasamples;
uint16* sampleinfo;
ImagingShuffler shuffle;
INT32 y;
state->bytes = row_byte_size * tile_length; TIFFGetFieldDefaulted(tiff, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampleinfo);
if (TIFFTileSize(tiff) > state->bytes) { if (extrasamples >= 1 &&
// If the strip size as expected by LibTiff isn't what we're expecting, (sampleinfo[0] == EXTRASAMPLE_UNSPECIFIED || sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA)
// abort. ) {
state->errcode = IMAGING_CODEC_MEMORY; shuffle = ImagingFindUnpacker("RGBA", "RGBa", NULL);
goto decode_err;
}
/* realloc to fit whole tile */ for (y = state->yoff; y < state->ysize; y++) {
/* malloc check above */ UINT8* ptr = (UINT8*) im->image[y + state->yoff] +
new_data = realloc(state->buffer, state->bytes); state->xoff * im->pixelsize;
if (!new_data) { shuffle(ptr, ptr, state->xsize);
state->errcode = IMAGING_CODEC_MEMORY;
goto decode_err;
}
state->buffer = new_data;
TRACE(("TIFFTileSize: %d\n", state->bytes));
for (y = state->yoff; y < state->ysize; y += tile_length) {
for (x = state->xoff; x < state->xsize; x += tile_width) {
/* Sanity Check. Apparently in some cases, the TiffReadRGBA* functions
have a different view of the size of the tiff than we're getting from
other functions. So, we need to check here.
*/
if (!TIFFCheckTile(tiff, x, y, 0, 0)) {
TRACE(("Check Tile Error, Tile at %dx%d\n", x, y));
state->errcode = IMAGING_CODEC_BROKEN;
goto decode_err;
}
if (isYCbCr) {
/* To avoid dealing with YCbCr subsampling, let libtiff handle it */
if (!TIFFReadRGBATile(tiff, x, y, (UINT32 *)state->buffer)) {
TRACE(("Decode Error, Tile at %dx%d\n", x, y));
state->errcode = IMAGING_CODEC_BROKEN;
goto decode_err;
} }
} else {
if (TIFFReadTile(tiff, (tdata_t)state->buffer, x, y, 0, 0) == -1) {
TRACE(("Decode Error, Tile at %dx%d\n", x, y));
state->errcode = IMAGING_CODEC_BROKEN;
goto decode_err;
}
}
TRACE(("Read tile at %dx%d; \n\n", x, y));
current_tile_width = min((INT32)tile_width, state->xsize - x);
current_tile_length = min((INT32)tile_length, state->ysize - y);
// iterate over each line in the tile and stuff data into image
for (tile_y = 0; tile_y < current_tile_length; tile_y++) {
TRACE(
("Writing tile data at %dx%d using tile_width: %d; \n",
tile_y + y,
x,
current_tile_width));
// UINT8 * bbb = state->buffer + tile_y * row_byte_size;
// TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1],
// ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3]));
/*
* For some reason the TIFFReadRGBATile() function
* chooses the lower left corner as the origin.
* Vertically mirror by shuffling the scanlines
* backwards
*/
if (isYCbCr) {
current_line = tile_length - tile_y - 1;
} else {
current_line = tile_y;
}
state->shuffle(
(UINT8 *)im->image[tile_y + y] + x * im->pixelsize,
state->buffer + current_line * row_byte_size,
current_tile_width);
} }
} }
} }
} else {
if (!isYCbCr) {
_decodeStrip(im, state, tiff);
} else {
_decodeStripYCbCr(im, state, tiff);
}
} }
decode_err: decode_err:
TIFFClose(tiff); TIFFClose(tiff);
TRACE(("Done Decoding, Returning \n")); TRACE(("Done Decoding, Returning \n"));
// Returning -1 here to force ImageFile.load to break, rather than // Returning -1 here to force ImageFile.load to break, rather than

View File

@ -1363,6 +1363,94 @@ band3I(UINT8 *out, const UINT8 *in, int pixels) {
} }
} }
static void
band016B(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* band 0 only, big endian */
for (i = 0; i < pixels; i++) {
out[0] = in[0];
out += 4; in += 2;
}
}
static void
band116B(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* band 1 only, big endian */
for (i = 0; i < pixels; i++) {
out[1] = in[0];
out += 4; in += 2;
}
}
static void
band216B(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* band 2 only, big endian */
for (i = 0; i < pixels; i++) {
out[2] = in[0];
out += 4; in += 2;
}
}
static void
band316B(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* band 3 only, big endian */
for (i = 0; i < pixels; i++) {
out[3] = in[0];
out += 4; in += 2;
}
}
static void
band016L(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* band 0 only, little endian */
for (i = 0; i < pixels; i++) {
out[0] = in[1];
out += 4; in += 2;
}
}
static void
band116L(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* band 1 only, little endian */
for (i = 0; i < pixels; i++) {
out[1] = in[1];
out += 4; in += 2;
}
}
static void
band216L(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* band 2 only, little endian */
for (i = 0; i < pixels; i++) {
out[2] = in[1];
out += 4; in += 2;
}
}
static void
band316L(UINT8* out, const UINT8* in, int pixels)
{
int i;
/* band 3 only, little endian */
for (i = 0; i < pixels; i++) {
out[3] = in[1];
out += 4; in += 2;
}
}
static struct { static struct {
const char *mode; const char *mode;
const char *rawmode; const char *rawmode;
@ -1448,6 +1536,12 @@ static struct {
{"RGB", "R", 8, band0}, {"RGB", "R", 8, band0},
{"RGB", "G", 8, band1}, {"RGB", "G", 8, band1},
{"RGB", "B", 8, band2}, {"RGB", "B", 8, band2},
{"RGB", "R;16L", 16, band016L},
{"RGB", "G;16L", 16, band116L},
{"RGB", "B;16L", 16, band216L},
{"RGB", "R;16B", 16, band016B},
{"RGB", "G;16B", 16, band116B},
{"RGB", "B;16B", 16, band216B},
/* true colour w. alpha */ /* true colour w. alpha */
{"RGBA", "LA", 16, unpackRGBALA}, {"RGBA", "LA", 16, unpackRGBALA},
@ -1476,17 +1570,42 @@ static struct {
{"RGBA", "G", 8, band1}, {"RGBA", "G", 8, band1},
{"RGBA", "B", 8, band2}, {"RGBA", "B", 8, band2},
{"RGBA", "A", 8, band3}, {"RGBA", "A", 8, band3},
{"RGBA", "R;16L", 16, band016L},
{"RGBA", "G;16L", 16, band116L},
{"RGBA", "B;16L", 16, band216L},
{"RGBA", "A;16L", 16, band316L},
{"RGBA", "R;16B", 16, band016B},
{"RGBA", "G;16B", 16, band116B},
{"RGBA", "B;16B", 16, band216B},
{"RGBA", "A;16B", 16, band316B},
#ifdef WORDS_BIGENDIAN #ifdef WORDS_BIGENDIAN
{"RGB", "RGB;16N", 48, unpackRGB16B}, {"RGB", "RGB;16N", 48, unpackRGB16B},
{"RGBA", "RGBa;16N", 64, unpackRGBa16B}, {"RGBA", "RGBa;16N", 64, unpackRGBa16B},
{"RGBA", "RGBA;16N", 64, unpackRGBA16B}, {"RGBA", "RGBA;16N", 64, unpackRGBA16B},
{"RGBX", "RGBX;16N", 64, unpackRGBA16B}, {"RGBX", "RGBX;16N", 64, unpackRGBA16B},
{"RGB", "R;16N", 16, band016B},
{"RGB", "G;16N", 16, band116B},
{"RGB", "B;16N", 16, band216B},
{"RGBA", "R;16N", 16, band016B},
{"RGBA", "G;16N", 16, band116B},
{"RGBA", "B;16N", 16, band216B},
{"RGBA", "A;16N", 16, band316B},
#else #else
{"RGB", "RGB;16N", 48, unpackRGB16L}, {"RGB", "RGB;16N", 48, unpackRGB16L},
{"RGBA", "RGBa;16N", 64, unpackRGBa16L}, {"RGBA", "RGBa;16N", 64, unpackRGBa16L},
{"RGBA", "RGBA;16N", 64, unpackRGBA16L}, {"RGBA", "RGBA;16N", 64, unpackRGBA16L},
{"RGBX", "RGBX;16N", 64, unpackRGBA16B}, {"RGBX", "RGBX;16N", 64, unpackRGBA16L},
{"RGB", "R;16N", 16, band016L},
{"RGB", "G;16N", 16, band116L},
{"RGB", "B;16N", 16, band216L},
{"RGBA", "R;16N", 16, band016L},
{"RGBA", "G;16N", 16, band116L},
{"RGBA", "B;16N", 16, band216L},
{"RGBA", "A;16N", 16, band316L},
#endif #endif
/* true colour w. alpha premultiplied */ /* true colour w. alpha premultiplied */