diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 035d6f8d4..99beb0f97 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -141,34 +141,56 @@ OPEN_INFO = { (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"), (II, 0, (1,), 2, (1,), ()): ("1", "1;IR"), (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"), + (II, 1, (1,), 1, (1,), ()): ("1", "1"), + (MM, 1, (1,), 1, (1,), ()): ("1", "1"), + (II, 1, (1,), 2, (1,), ()): ("1", "1;R"), + (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), + + (II, 0, (1,), 1, (2,), ()): ("L", "L;2I"), + (MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"), + (II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), + (MM, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), + (II, 1, (1,), 1, (2,), ()): ("L", "L;2"), + (MM, 1, (1,), 1, (2,), ()): ("L", "L;2"), + (II, 1, (1,), 2, (2,), ()): ("L", "L;2R"), + (MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"), + + (II, 0, (1,), 1, (4,), ()): ("L", "L;4I"), + (MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"), + (II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), + (MM, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), + (II, 1, (1,), 1, (4,), ()): ("L", "L;4"), + (MM, 1, (1,), 1, (4,), ()): ("L", "L;4"), + (II, 1, (1,), 2, (4,), ()): ("L", "L;4R"), + (MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"), + (II, 0, (1,), 1, (8,), ()): ("L", "L;I"), (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"), (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), - (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"), - (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"), - (II, 1, (1,), 1, (1,), ()): ("1", "1"), - (MM, 1, (1,), 1, (1,), ()): ("1", "1"), - (II, 1, (1,), 1, (4,), ()): ("L", "L;4"), - # ? - (II, 1, (1,), 2, (1,), ()): ("1", "1;R"), - (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), (II, 1, (1,), 1, (8,), ()): ("L", "L"), (MM, 1, (1,), 1, (8,), ()): ("L", "L"), - (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), - (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), (II, 1, (1,), 2, (8,), ()): ("L", "L;R"), (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), + (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"), + (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"), (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), (II, 1, (2,), 1, (16,), ()): ("I;16S", "I;16S"), (MM, 1, (2,), 1, (16,), ()): ("I;16BS", "I;16BS"), + + (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"), + (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"), (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"), (II, 1, (2,), 1, (32,), ()): ("I", "I;32S"), (MM, 1, (2,), 1, (32,), ()): ("I;32BS", "I;32BS"), (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"), (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"), + + (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), + (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), @@ -183,6 +205,7 @@ OPEN_INFO = { (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 + (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), @@ -201,10 +224,13 @@ OPEN_INFO = { (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"), + (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), + (II, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), (MM, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), + (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), } diff --git a/Tests/images/tiff_gray_2_4_bpp/hopper2.tif b/Tests/images/tiff_gray_2_4_bpp/hopper2.tif new file mode 100644 index 000000000..70057219a Binary files /dev/null and b/Tests/images/tiff_gray_2_4_bpp/hopper2.tif differ diff --git a/Tests/images/tiff_gray_2_4_bpp/hopper2I.tif b/Tests/images/tiff_gray_2_4_bpp/hopper2I.tif new file mode 100644 index 000000000..9feaa08f0 Binary files /dev/null and b/Tests/images/tiff_gray_2_4_bpp/hopper2I.tif differ diff --git a/Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif b/Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif new file mode 100644 index 000000000..462416955 Binary files /dev/null and b/Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif differ diff --git a/Tests/images/tiff_gray_2_4_bpp/hopper2R.tif b/Tests/images/tiff_gray_2_4_bpp/hopper2R.tif new file mode 100644 index 000000000..65e0f623e Binary files /dev/null and b/Tests/images/tiff_gray_2_4_bpp/hopper2R.tif differ diff --git a/Tests/images/tiff_gray_2_4_bpp/hopper4.tif b/Tests/images/tiff_gray_2_4_bpp/hopper4.tif new file mode 100644 index 000000000..7a38271ea Binary files /dev/null and b/Tests/images/tiff_gray_2_4_bpp/hopper4.tif differ diff --git a/Tests/images/tiff_gray_2_4_bpp/hopper4I.tif b/Tests/images/tiff_gray_2_4_bpp/hopper4I.tif new file mode 100644 index 000000000..5c3a17f52 Binary files /dev/null and b/Tests/images/tiff_gray_2_4_bpp/hopper4I.tif differ diff --git a/Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif b/Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif new file mode 100644 index 000000000..7652f5e6d Binary files /dev/null and b/Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif differ diff --git a/Tests/images/tiff_gray_2_4_bpp/hopper4R.tif b/Tests/images/tiff_gray_2_4_bpp/hopper4R.tif new file mode 100644 index 000000000..0cdf4d26e Binary files /dev/null and b/Tests/images/tiff_gray_2_4_bpp/hopper4R.tif differ diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index bca638e22..83d347239 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -419,6 +419,39 @@ class TestFileLibTiff(LibTiffTestCase): self.assertEqual(im.mode, "L") self.assert_image_similar(im, original, 7.3) + def test_gray_semibyte_per_pixel(self): + test_files = ( + ( + 24.8,#epsilon + (#group + "Tests/images/tiff_gray_2_4_bpp/hopper2.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper2I.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper2R.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif", + ) + ), + ( + 7.3,#epsilon + (#group + "Tests/images/tiff_gray_2_4_bpp/hopper4.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper4I.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper4R.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif", + ) + ), + ) + original = hopper("L") + for epsilon, group in test_files: + im = Image.open(group[0]) + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.mode, "L") + self.assert_image_similar(im, original, epsilon) + for file in group[1:]: + im2 = Image.open(file) + self.assertEqual(im2.size, (128, 128)) + self.assertEqual(im2.mode, "L") + self.assert_image_equal(im, im2) + def test_save_bytesio(self): # PR 1011 # Test TIFF saving to io.BytesIO() object. diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 5af7b55aa..a0e34439d 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -337,6 +337,39 @@ class TestFileTiff(PillowTestCase): self.assertEqual(im.mode, "L") self.assert_image_similar(im, original, 7.3) + def test_gray_semibyte_per_pixel(self): + test_files = ( + ( + 24.8,#epsilon + (#group + "Tests/images/tiff_gray_2_4_bpp/hopper2.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper2I.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper2R.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif", + ) + ), + ( + 7.3,#epsilon + (#group + "Tests/images/tiff_gray_2_4_bpp/hopper4.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper4I.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper4R.tif", + "Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif", + ) + ), + ) + original = hopper("L") + for epsilon, group in test_files: + im = Image.open(group[0]) + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.mode, "L") + self.assert_image_similar(im, original, epsilon) + for file in group[1:]: + im2 = Image.open(file) + self.assertEqual(im2.size, (128, 128)) + self.assertEqual(im2.mode, "L") + self.assert_image_equal(im, im2) + def test_page_number_x_0(self): # Issue 973 # Test TIFF with tag 297 (Page Number) having value of 0 0. diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index 3fab73cec..702bc9f1f 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -191,14 +191,64 @@ unpack1IR(UINT8* out, const UINT8* in, int pixels) static void unpackL2(UINT8* out, const UINT8* in, int pixels) { - /* nibbles */ + /* nibbles (msb first, white is non-zero) */ while (pixels > 0) { UINT8 byte = *in++; switch (pixels) { - default: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; - case 3: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; - case 2: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; - case 1: *out++ = ((byte >> 6) & 3) * 255 / 3; + default: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2; + case 3: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2; + case 2: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2; + case 1: *out++ = ((byte >> 6) & 0x03U) * 0x55U; + } + pixels -= 4; + } +} + +static void +unpackL2I(UINT8* out, const UINT8* in, int pixels) +{ + /* nibbles (msb first, white is zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2; + case 3: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2; + case 2: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2; + case 1: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); + } + pixels -= 4; + } +} + +static void +unpackL2R(UINT8* out, const UINT8* in, int pixels) +{ + /* nibbles (bit order reversed, white is non-zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + byte = BITFLIP[byte]; + switch (pixels) { + default: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2; + case 3: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2; + case 2: *out++ = ((byte >> 6) & 0x03U) * 0x55U; byte <<= 2; + case 1: *out++ = ((byte >> 6) & 0x03U) * 0x55U; + } + pixels -= 4; + } +} + +static void +unpackL2IR(UINT8* out, const UINT8* in, int pixels) +{ + /* nibbles (bit order reversed, white is zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + byte = BITFLIP[byte]; + switch (pixels) { + default: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2; + case 3: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2; + case 2: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); byte <<= 2; + case 1: *out++ = 0xFFU - (UINT8)(((byte >> 6) & 0x03U) * 0x55U); } pixels -= 4; } @@ -207,12 +257,56 @@ unpackL2(UINT8* out, const UINT8* in, int pixels) static void unpackL4(UINT8* out, const UINT8* in, int pixels) { - /* nibbles */ + /* nibbles (msb first, white is non-zero) */ while (pixels > 0) { UINT8 byte = *in++; switch (pixels) { - default: *out++ = ((byte >> 4) & 15) * 255 / 15; byte <<= 4; - case 1: *out++ = ((byte >> 4) & 15) * 255 / 15; + default: *out++ = ((byte >> 4) & 0x0FU) * 0x11U; byte <<= 4; + case 1: *out++ = ((byte >> 4) & 0x0FU) * 0x11U; + } + pixels -= 2; + } +} + +static void +unpackL4I(UINT8* out, const UINT8* in, int pixels) +{ + /* nibbles (msb first, white is zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U); byte <<= 4; + case 1: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U); + } + pixels -= 2; + } +} + +static void +unpackL4R(UINT8* out, const UINT8* in, int pixels) +{ + /* nibbles (bit order reversed, white is non-zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + byte = BITFLIP[byte]; + switch (pixels) { + default: *out++ = ((byte >> 4) & 0x0FU) * 0x11U; byte <<= 4; + case 1: *out++ = ((byte >> 4) & 0x0FU) * 0x11U; + } + pixels -= 2; + } +} + +static void +unpackL4IR(UINT8* out, const UINT8* in, int pixels) +{ + /* nibbles (bit order reversed, white is zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + byte = BITFLIP[byte]; + switch (pixels) { + default: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U); byte <<= 4; + case 1: *out++ = 0xFFU - (UINT8)(((byte >> 4) & 0x0FU) * 0x11U); } pixels -= 2; } @@ -1053,7 +1147,15 @@ static struct { /* greyscale */ {"L", "L;2", 2, unpackL2}, + {"L", "L;2I", 2, unpackL2I}, + {"L", "L;2R", 2, unpackL2R}, + {"L", "L;2IR", 2, unpackL2IR}, + {"L", "L;4", 4, unpackL4}, + {"L", "L;4I", 4, unpackL4I}, + {"L", "L;4R", 4, unpackL4R}, + {"L", "L;4IR", 4, unpackL4IR}, + {"L", "L", 8, copy1}, {"L", "L;I", 8, unpackLI}, {"L", "L;R", 8, unpackLR},