diff --git a/Tests/images/hopper_I.png b/Tests/images/hopper_I.png new file mode 100644 index 000000000..314ab09c7 Binary files /dev/null and b/Tests/images/hopper_I.png differ diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 6a061fe6a..23fa160e7 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -295,7 +295,7 @@ class TestFilePng(PillowTestCase): for mode, num_transparent in { "1": 1994, "L": 559, - "I": 559, + "I": 4096, }.items(): in_file = "Tests/images/"+mode.lower()+"_trns.png" im = Image.open(in_file) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 970dc8ce1..36f2c9d64 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -38,6 +38,8 @@ class TestImageConvert(PillowTestCase): def _test_float_conversion(self, im): orig = im.getpixel((5, 5)) + if im.mode[0] == 'I': + orig //= 256 converted = im.convert('F').getpixel((5, 5)) self.assertEqual(orig, converted) @@ -231,3 +233,11 @@ class TestImageConvert(PillowTestCase): # Assert # No change self.assert_image_equal(converted_im, im) + + def test_i_to_l(self): + im = Image.open("Tests/images/hopper_I.png").convert("L") + self.assert_image_similar(hopper("L"), im, 0.02) + + def test_i_to_rgb(self): + im = Image.open("Tests/images/hopper_I.png").convert("RGB") + self.assert_image_similar(hopper("L").convert("RGB"), im, 0.05) diff --git a/Tests/test_image_getcolors.py b/Tests/test_image_getcolors.py index aa2a40976..1f4a9b69d 100644 --- a/Tests/test_image_getcolors.py +++ b/Tests/test_image_getcolors.py @@ -17,13 +17,13 @@ class TestImageGetColors(PillowTestCase): self.assertEqual(getcolors("1"), 2) self.assertEqual(getcolors("L"), 255) - self.assertEqual(getcolors("I"), 255) self.assertEqual(getcolors("F"), 255) self.assertEqual(getcolors("P"), 90) # fixed palette self.assertIsNone(getcolors("RGB")) self.assertIsNone(getcolors("RGBA")) self.assertIsNone(getcolors("CMYK")) self.assertIsNone(getcolors("YCbCr")) + self.assertIsNone(getcolors("I")) self.assertIsNone(getcolors("L", 128)) self.assertEqual(getcolors("L", 1024), 255) diff --git a/Tests/test_image_getdata.py b/Tests/test_image_getdata.py index fe8f9adcd..56ba39cca 100644 --- a/Tests/test_image_getdata.py +++ b/Tests/test_image_getdata.py @@ -21,7 +21,7 @@ class TestImageGetData(PillowTestCase): self.assertEqual(getdata("1"), (0, 960, 960)) self.assertEqual(getdata("L"), (16, 960, 960)) - self.assertEqual(getdata("I"), (16, 960, 960)) + self.assertEqual(getdata("I"), (4313, 960, 960)) self.assertEqual(getdata("F"), (16.0, 960, 960)) self.assertEqual(getdata("RGB"), ((11, 13, 52), 960, 960)) self.assertEqual(getdata("RGBA"), ((11, 13, 52, 255), 960, 960)) diff --git a/Tests/test_image_getextrema.py b/Tests/test_image_getextrema.py index 1689744af..985896e20 100644 --- a/Tests/test_image_getextrema.py +++ b/Tests/test_image_getextrema.py @@ -11,7 +11,7 @@ class TestImageGetExtrema(PillowTestCase): self.assertEqual(extrema("1"), (0, 255)) self.assertEqual(extrema("L"), (0, 255)) - self.assertEqual(extrema("I"), (0, 255)) + self.assertEqual(extrema("I"), (150, 65280)) self.assertEqual(extrema("F"), (0, 255)) self.assertEqual(extrema("P"), (0, 225)) # fixed palette self.assertEqual( @@ -20,7 +20,7 @@ class TestImageGetExtrema(PillowTestCase): extrema("RGBA"), ((0, 255), (0, 255), (0, 255), (255, 255))) self.assertEqual( extrema("CMYK"), ((0, 255), (0, 255), (0, 255), (0, 0))) - self.assertEqual(extrema("I;16"), (0, 255)) + self.assertEqual(extrema("I;16"), (150, 65280)) def test_true_16(self): im = Image.open("Tests/images/16_bit_noise.tif") diff --git a/Tests/test_image_histogram.py b/Tests/test_image_histogram.py index 0a023cd69..c70585874 100644 --- a/Tests/test_image_histogram.py +++ b/Tests/test_image_histogram.py @@ -11,7 +11,7 @@ class TestImageHistogram(PillowTestCase): self.assertEqual(histogram("1"), (256, 0, 10994)) self.assertEqual(histogram("L"), (256, 0, 638)) - self.assertEqual(histogram("I"), (256, 0, 638)) + self.assertEqual(histogram("I"), (256, 1, 662)) self.assertEqual(histogram("F"), (256, 0, 638)) self.assertEqual(histogram("P"), (256, 0, 1871)) self.assertEqual(histogram("RGB"), (768, 4, 675)) diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index 8273b63c1..8acc9420f 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -31,22 +31,22 @@ class TestImageMath(PillowTestCase): def test_sanity(self): self.assertEqual(ImageMath.eval("1"), 1) self.assertEqual(ImageMath.eval("1+A", A=2), 3) - self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") - self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") + self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 768") + self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 768") self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") self.assertEqual(pixel( - ImageMath.eval("int(float(A)+B)", images)), "I 3") + ImageMath.eval("int(float(A)+B)", images)), "I 768") def test_ops(self): - self.assertEqual(pixel(ImageMath.eval("-A", images)), "I -1") + self.assertEqual(pixel(ImageMath.eval("-A", images)), "I -256") self.assertEqual(pixel(ImageMath.eval("+B", images)), "L 2") - self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") - self.assertEqual(pixel(ImageMath.eval("A-B", images)), "I -1") - self.assertEqual(pixel(ImageMath.eval("A*B", images)), "I 2") + self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 768") + self.assertEqual(pixel(ImageMath.eval("A-B", images)), "I -256") + self.assertEqual(pixel(ImageMath.eval("A*B", images)), "I 131072") self.assertEqual(pixel(ImageMath.eval("A/B", images)), "I 0") - self.assertEqual(pixel(ImageMath.eval("B**2", images)), "I 4") + self.assertEqual(pixel(ImageMath.eval("B**2", images)), "I 262144") self.assertEqual(pixel( ImageMath.eval("B**33", images)), "I 2147483647") @@ -72,61 +72,61 @@ class TestImageMath(PillowTestCase): ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") def test_compare(self): - self.assertEqual(pixel(ImageMath.eval("min(A, B)", images)), "I 1") - self.assertEqual(pixel(ImageMath.eval("max(A, B)", images)), "I 2") - self.assertEqual(pixel(ImageMath.eval("A == 1", images)), "I 1") + self.assertEqual(pixel(ImageMath.eval("min(A, B)", images)), "I 256") + self.assertEqual(pixel(ImageMath.eval("max(A, B)", images)), "I 512") + self.assertEqual(pixel(ImageMath.eval("A == 256", images)), "I 1") self.assertEqual(pixel(ImageMath.eval("A == 2", images)), "I 0") def test_one_image_larger(self): - self.assertEqual(pixel(ImageMath.eval("A+B", A=A2, B=B)), "I 3") - self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B2)), "I 3") + self.assertEqual(pixel(ImageMath.eval("A+B", A=A2, B=B)), "I 768") + self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B2)), "I 768") def test_abs(self): - self.assertEqual(pixel(ImageMath.eval("abs(A)", A=A)), "I 1") - self.assertEqual(pixel(ImageMath.eval("abs(B)", B=B)), "I 2") + self.assertEqual(pixel(ImageMath.eval("abs(A)", A=A)), "I 256") + self.assertEqual(pixel(ImageMath.eval("abs(B)", B=B)), "I 512") def test_binary_mod(self): self.assertEqual(pixel(ImageMath.eval("A%A", A=A)), "I 0") self.assertEqual(pixel(ImageMath.eval("B%B", B=B)), "I 0") - self.assertEqual(pixel(ImageMath.eval("A%B", A=A, B=B)), "I 1") + self.assertEqual(pixel(ImageMath.eval("A%B", A=A, B=B)), "I 256") self.assertEqual(pixel(ImageMath.eval("B%A", A=A, B=B)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z%A", A=A, Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z%B", B=B, Z=Z)), "I 0") def test_bitwise_invert(self): self.assertEqual(pixel(ImageMath.eval("~Z", Z=Z)), "I -1") - self.assertEqual(pixel(ImageMath.eval("~A", A=A)), "I -2") - self.assertEqual(pixel(ImageMath.eval("~B", B=B)), "I -3") + self.assertEqual(pixel(ImageMath.eval("~A", A=A)), "I -257") + self.assertEqual(pixel(ImageMath.eval("~B", B=B)), "I -513") def test_bitwise_and(self): self.assertEqual(pixel(ImageMath.eval("Z&Z", A=A, Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z&A", A=A, Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("A&Z", A=A, Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("A&A", A=A, Z=Z)), "I 1") + self.assertEqual(pixel(ImageMath.eval("A&A", A=A, Z=Z)), "I 256") def test_bitwise_or(self): self.assertEqual(pixel(ImageMath.eval("Z|Z", A=A, Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("Z|A", A=A, Z=Z)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A|Z", A=A, Z=Z)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A|A", A=A, Z=Z)), "I 1") + self.assertEqual(pixel(ImageMath.eval("Z|A", A=A, Z=Z)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A|Z", A=A, Z=Z)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A|A", A=A, Z=Z)), "I 256") def test_bitwise_xor(self): self.assertEqual(pixel(ImageMath.eval("Z^Z", A=A, Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("Z^A", A=A, Z=Z)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A^Z", A=A, Z=Z)), "I 1") + self.assertEqual(pixel(ImageMath.eval("Z^A", A=A, Z=Z)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A^Z", A=A, Z=Z)), "I 256") self.assertEqual(pixel(ImageMath.eval("A^A", A=A, Z=Z)), "I 0") def test_bitwise_leftshift(self): self.assertEqual(pixel(ImageMath.eval("Z<<0", Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z<<1", Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("A<<0", A=A)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A<<1", A=A)), "I 2") + self.assertEqual(pixel(ImageMath.eval("A<<0", A=A)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A<<1", A=A)), "I 512") def test_bitwise_rightshift(self): self.assertEqual(pixel(ImageMath.eval("Z>>0", Z=Z)), "I 0") self.assertEqual(pixel(ImageMath.eval("Z>>1", Z=Z)), "I 0") - self.assertEqual(pixel(ImageMath.eval("A>>0", A=A)), "I 1") - self.assertEqual(pixel(ImageMath.eval("A>>1", A=A)), "I 0") + self.assertEqual(pixel(ImageMath.eval("A>>0", A=A)), "I 256") + self.assertEqual(pixel(ImageMath.eval("A>>1", A=A)), "I 128") def test_logical_eq(self): self.assertEqual(pixel(ImageMath.eval("A==A", A=A)), "I 1") diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 80730a312..00caed1f2 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -17,6 +17,8 @@ class TestModeI16(PillowTestCase): xy = x, y p1 = pix1[xy] p2 = pix2[xy] + if im1.mode[0] != "I" and im2.mode[0] == "I": + p2 //= 256 self.assertEqual( p1, p2, ("got %r from mode %s at %s, expected %r" % diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index 055821a15..8e4c0d6da 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -25,9 +25,9 @@ Modes ----- The ``mode`` of an image defines the type and depth of a pixel in the image. -Each pixel uses the full range of the bit depth. So a 1-bit pixel has a range -of 0-1, an 8-bit pixel has a range of 0-255 and so on. The current release -supports the following standard modes: +A 1-bit pixel has a range of 0-1, an 8-bit pixel or a 32-bit floating point +pixel has a range of 0-255, and a 32-bit signed integer has a range of 0-65535. +These modes are supported: * ``1`` (1-bit pixels, black and white, stored with one pixel per byte) * ``L`` (8-bit pixels, black and white) diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index a6fefca32..e3dd87048 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -231,7 +231,7 @@ rgb2i(UINT8* out_, const UINT8* in, int xsize) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++, in += 4) - *out++ = L24(in) >> 16; + *out++ = L24(in) >> 8; } static void @@ -575,8 +575,8 @@ l2i(UINT8* out_, const UINT8* in, int xsize) { int x; INT32* out = (INT32*) out_; - for (x = 0; x < xsize; x++) - *out++ = (INT32) *in++; + for (x = 0; x < xsize; x++, in++) + *out++ = (INT32) (*in << 8); } static void @@ -585,12 +585,7 @@ i2l(UINT8* out, const UINT8* in_, int xsize) int x; INT32* in = (INT32*) in_; for (x = 0; x < xsize; x++, in++, out++) { - if (*in <= 0) - *out = 0; - else if (*in >= 255) - *out = 255; - else - *out = (UINT8) *in; + *out = (UINT8) (*in >> 8); } } @@ -601,7 +596,7 @@ i2f(UINT8* out_, const UINT8* in_, int xsize) INT32* in = (INT32*) in_; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (FLOAT32) *in++; + *out++ = (FLOAT32) (*in++ >> 8); } static void @@ -610,12 +605,7 @@ i2rgb(UINT8* out, const UINT8* in_, int xsize) int x; INT32* in = (INT32*) in_; for (x = 0; x < xsize; x++, in++, out+=4) { - if (*in <= 0) - out[0] = out[1] = out[2] = 0; - else if (*in >= 255) - out[0] = out[1] = out[2] = 255; - else - out[0] = out[1] = out[2] = (UINT8) *in; + out[0] = out[1] = out[2] = (UINT8) (*in >> 8); out[3] = 255; } } @@ -664,7 +654,7 @@ f2i(UINT8* out_, const UINT8* in_, int xsize) FLOAT32* in = (FLOAT32*) in_; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (INT32) *in++; + *out++ = (INT32) *in++ << 8; } /* ----------------- */ @@ -722,24 +712,22 @@ ycbcr2la(UINT8* out, const UINT8* in, int xsize) static void I_I16L(UINT8* out, const UINT8* in_, int xsize) { - int x, v; + int x; INT32* in = (INT32*) in_; for (x = 0; x < xsize; x++, in++) { - v = CLIP16(*in); - *out++ = (UINT8) v; - *out++ = (UINT8) (v >> 8); + *out++ = (UINT8) *in; + *out++ = (UINT8) (*in >> 8); } } static void I_I16B(UINT8* out, const UINT8* in_, int xsize) { - int x, v; + int x; INT32* in = (INT32*) in_; for (x = 0; x < xsize; x++, in++) { - v = CLIP16(*in); - *out++ = (UINT8) (v >> 8); - *out++ = (UINT8) v; + *out++ = (UINT8) (*in >> 8); + *out++ = (UINT8) *in; } } @@ -769,7 +757,7 @@ I16L_F(UINT8* out_, const UINT8* in, int xsize) int x; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++, in += 2) - *out++ = (FLOAT32) (in[0] + ((int) in[1] << 8)); + *out++ = (FLOAT32) in[1]; } @@ -779,7 +767,7 @@ I16B_F(UINT8* out_, const UINT8* in, int xsize) int x; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++, in += 2) - *out++ = (FLOAT32) (in[1] + ((int) in[0] << 8)); + *out++ = (FLOAT32) in[0]; } static void @@ -787,8 +775,8 @@ L_I16L(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in++) { + *out++ = *in << 8; *out++ = *in; - *out++ = 0; } } @@ -797,8 +785,8 @@ L_I16B(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in++) { - *out++ = 0; *out++ = *in; + *out++ = *in << 8; } } @@ -807,10 +795,7 @@ I16L_L(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 2) - if (in[1] != 0) - *out++ = 255; - else - *out++ = in[0]; + *out++ = in[1] + (in[0] << 8); } static void @@ -818,10 +803,7 @@ I16B_L(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 2) - if (in[0] != 0) - *out++ = 255; - else - *out++ = in[1]; + *out++ = in[0] + (in[1] << 8); } static struct { @@ -1020,7 +1002,7 @@ p2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++) - *out++ = L(&palette[in[x]*4]) / 1000; + *out++ = (L(&palette[in[x]*4]) / 1000) << 8; } static void @@ -1029,7 +1011,7 @@ pa2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++, in += 4) - *out++ = L(&palette[in[0]*4]) / 1000; + *out++ = (L(&palette[in[0]*4]) / 1000) << 8; } static void diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c index b7c1a9834..b330f88f8 100644 --- a/src/libImaging/Histo.c +++ b/src/libImaging/Histo.c @@ -135,11 +135,10 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) if (imin >= imax) break; ImagingSectionEnter(&cookie); - scale = 255.0F / (imax - imin); for (y = 0; y < im->ysize; y++) { INT32* in = im->image32[y]; for (x = 0; x < im->xsize; x++) { - i = (int) (((*in++)-imin)*scale); + i = (int) (((*in++)-imin) * 255.0F / (imax - imin)); if (i >= 0 && i < 256) h->histogram[i]++; }