From b147dea535cdb05b4692f4d58ec1ca585ad09d8e Mon Sep 17 00:00:00 2001 From: David Joy Date: Wed, 25 Jun 2014 14:06:56 -0400 Subject: [PATCH] Add tests and fix a 16bit vs 32bit integer bug Yay unit tests! --- PIL/Jpeg2KImagePlugin.py | 10 ++++----- Tests/images/16bit.cropped.j2k | Bin 0 -> 3886 bytes Tests/images/16bit.cropped.jp2 | Bin 0 -> 3932 bytes Tests/test_file_jpeg2k.py | 36 +++++++++++++++++++++++++++++++++ libImaging/Jpeg2KDecode.c | 9 +++++---- 5 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 Tests/images/16bit.cropped.j2k create mode 100644 Tests/images/16bit.cropped.jp2 diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index ff6ca4b35..66069802e 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -40,8 +40,8 @@ def _parse_codestream(fp): size = (xsiz - xosiz, ysiz - yosiz) if csiz == 1: - if (ssiz[0] & 0x7f) > 8: - mode = 'I' + if (yrsiz[0] & 0x7f) > 8: + mode = 'I;16' else: mode = 'L' elif csiz == 2: @@ -100,7 +100,7 @@ def _parse_jp2_header(fp): size = (width, height) if unkc: if nc == 1 and (bpc & 0x7f) > 8: - mode = 'I' + mode = 'I;16' elif nc == 1: mode = 'L' elif nc == 2: @@ -116,7 +116,7 @@ def _parse_jp2_header(fp): cs = struct.unpack('>I', content[3:7])[0] if cs == 16: # sRGB if nc == 1 and (bpc & 0x7f) > 8: - mode = 'I' + mode = 'I;16' elif nc == 1: mode = 'L' elif nc == 3: @@ -126,7 +126,7 @@ def _parse_jp2_header(fp): break elif cs == 17: # grayscale if nc == 1 and (bpc & 0x7f) > 8: - mode = 'I' + mode = 'I;16' elif nc == 1: mode = 'L' elif nc == 2: diff --git a/Tests/images/16bit.cropped.j2k b/Tests/images/16bit.cropped.j2k new file mode 100644 index 0000000000000000000000000000000000000000..c12df0cd7712f493ff5e49b16ee3cabd4fbea889 GIT binary patch literal 3886 zcmV+}57F@dPybN>DF6Tf002M$002M$0000000000002M$002M$00000000000S^HI z|55-90000100jgD00IA8024rfh=`Dgh>(bgkcfzoh=`E?WB?@q0Yh?SVRU6=AYyqS zPjF>!N>D{dAa-SPb7^mGATlm6E-?R)015yA000iP00IA#&-^GL2!eVy;#fdGzys+E z01u=r03S#|4l;{9uLsQxrJoQy_`|>h=^X$Mq|6fL4Y z_?hYHHB+s)I`GjNhH%}k`qLl@|7B(lV8$=N2iR)>A7RV@KEt3#hu`C4V&TdK18uF9 z(d-MFdc{vNj<9B$$}zN1!zDjVfH#?!L6Qk)gd05o?a2zrN;7YtLhT8nTx>?l-jo^x zo(`~QndSf{1$dXB$~9L%!nbA5Jct1r3jb$yADnLO05Xz;zEUGPiyGNQt3=Sb5~1T= zzUF}wE7Q}?b8pf-3aqJuA+uPICxoz2RKa71l6sz&V0PxK;Njz~G>0@Rs=O=*lEP2G z2ia~w55DDq2ibUlqSk+m1dPG!Y&6OzWi#35Q)*|ygwA68jBd54ysCK`?+1WLKhD*Xq49=T1FwatpUH9Q!d zzktA1+K0oUeO^2*I2v_D%B>+D;BN#By&Gu8J2n@GfC_9E2pZN*VUi@*G#thOX#t#b z#3_x9;eNrWyJYOP0I+$waO(=%z+jbsp1_Oa>sC-SrFoL}(adZ@IY>igj{jx@hIc}& z#YVSccfAd7>FZ?5>0ZUT?xV7oricoxw{X8@C&(|RV`uF3)e*3n&3?yquW?JgZg&OG zEl+FSunjB42_1;8%L2Qwa>-{+($T_mud7-b6f2pU9S}~>vbIgY9z^~b$}m}#0mG;u z4JFaIXPM)o%);`>%o=8)a4w(IPs%Re5<>O9XZ?nojtGcw3{2;(OE6Fc{JppMTrlM~ z0!#J@O*i;x^|{-XJRrFkffj_Gw^^t7YIe9Frj~A^{7!YYF$D%>!X~k;R~j}bVvVh^ zDdBa?uLS8(`R1%Ty%&WugcxsVshOtH_);) z-J1zue9b9YZ2Q*{LPH%ya3oA*>yXnzU|mm3-w1(Hjgc&34#uTi9z$Ft9_h`tc(PB-#5+r^OI=! zM95egr8YrS1ZLTTnrLV?CF9^kYmp7>Va`NNR&=__<7%*Q&|t zS6H$rkR_xE$L0vdc&>YOjH&Z!`Fe*$5F}x?O`lp`4z+xpmzsIyhaWN17qDZl*rg`1 z1Y}24|0hGz;(5dS{T!x8+D*~7YDJyavcow}pB#2S&D6=P+~-)@PoZ^-M4#gBNkqm> zI`w>bL5MJYx{IYp+UF^-m8X-Q4B#ucG&Q3TN{0n>%haL9;#Zw9%PFDsK`Qvq`8S2SKJ)tOewcqVfl zdVw@AwnKVjwcDSvXk)o4Yc?Zz8GopOl`V(Ls^zE0l=W%kv3576kXGv)c z9X9-j(jF`Mv1CjWw$FA~KdxLMPpb$m=nUE^i{N(1z=1IO+6fl4 zz)|!~*s{#IzX0e9Xf94+Emk72+aR(EW>!0YKm{0v-x;QEY&)W}31L9xH9qOlElBV( z_0;|bk7~g%SlSPQ#hQnRf2SyRW);Aw=v z3eJ|qzt=zxgG`l#Y0(giqTh7du7?V~#8QnjN(_V9_c!bOHxy){Z|NvPG|e2`_%ro( zMiGL4=7l>>N&sc)plOU|W9%m;dq{Fuv(2rIq&I&qZ2)^`0;csbi~zL98YR)x}x z25+gNOQ7WNwg^sg!SiDR#%dT?L?4_&2JA@oB~S0WD7lNSZxr1~VY)}-SG_$(>VlZS zE9@vqRLQaZV=1c-ZhK>Fy?jA^FVk5HeTqEN6+`_}aq6%V`dDK+8zK@A`Ry>+tV1i% zkx3LyjSy=V0(R7#z(0dYYW10T^LiaxZ%)mKm^f;&ax5{0MJB%w;&=-~j``0Hc}1V{ z*PhNl6bv&Y8YB%% zTu7s#t8f#0WpwdzD{fvIzUwY(Uu5x_==rXQ%ljkjsH)Hv@81IHV@6o$WS#Fx}Q=^EDS&ZDGOg=Oxf`9Kh-AdVKOt#I99m>Qkr%Fl0qqG>!-|T{qFsmOKma(bnm@0 zs>HC2-MI;6DVjUG$P7MdCA1)`D|$R4JKmh_LDd8GUrQLI7P%u$Hs-G+7u!WR;!}Gmi z@>oE8rFW#av0rY0#9lOsd%X0M-dU)(cIBZb?3vDp4XZ#8S$JU^YfqY%`Cs#?^fy@t z&7ceN-z-}(j&#Gp6hX!kdsZKV=vhQwnb(b!HNQ_jfK)~6{p-+~GuA+^nVbC=Hu$rB zpw^<@^6|)?2IvU4;S-&LJ4?q$D3YJp(t}}BtIkA9JDu$dHUrsq)>e6d%O3hcDkK)7 zeY^wh1?Zn5gnfhh!#2}GDpE82$L#s0bYZg9qb=9 zoHqx`HI@(rA`kAHLEz*dL`TkF~uiY$P#9;kBK)1!t#LqwDl^u5{WQ? zY>ekVp5S>y((nmNQnFAjUnjA0wyj$H``b!#p-84?DJP&7em>gAkC^Tx`)LD zQ*se~7A4GEl0}mjIlQlOYfmpz3IiK|Z9N5C;0qR>gPp~)bLMp{Qiw{T$vu>lB;q&g zaO|eZj(_tlWJ%Rx$-_CVk-pWSaxl0f5#;2@AoMFuZ`OWK92i|rC#1S=k2ZL`ZdIym z>`6Y^%QTVnf)?19ay`myg$y*Cpt2UPsw}d_duathy&B1gLmH3Q`IqB%dL|(F-?@M{ z3@8fH#pv$r2kA)z2?%Rfj(zuEowCN}DJ*9eE86CO$$xjkD%2XR@0dz0s!nw8ov2Qo z73uiy0qVCWi`n!5twnxX$5ZY+hf<0jj73LFv%{(0u*V##M_%_VI&_%HsW#R1FlnCT}i3d&*9lJzqMu#gt&A z)^jrCipO2W{9yJ*=Of_}9iK^t| zVFM!$r(+@ti0&<3!zrh>%+8;yAFkH2pRd601PMhWW2q4u&Rjs=F-*%SX*W3sj zQsmE@+6&m8iKrop4=zFmY^#3`RS*|^?N}(WYj7eU7?s-AnPN`RTd~zkOe^nuHY9;{ wPDcwS(s{EPCAJ{f#}b^oPT0b|?CjRuDmg`9He^Cv`gDOO^8O3fGe3X-+0#E*XaE2J literal 0 HcmV?d00001 diff --git a/Tests/images/16bit.cropped.jp2 b/Tests/images/16bit.cropped.jp2 new file mode 100644 index 0000000000000000000000000000000000000000..0f6685a462f5f1f5abfaf2ce4d141baba4c0da54 GIT binary patch literal 3932 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index b662124b4..b763687f7 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -120,6 +120,42 @@ class TestFileJpeg2k(PillowTestCase): self.assertEqual(j2k.mode, 'RGBA') self.assertEqual(jp2.mode, 'RGBA') + def test_16bit_monochrome_has_correct_mode(self): + + j2k = Image.open('Tests/images/16bit.cropped.j2k') + jp2 = Image.open('Tests/images/16bit.cropped.jp2') + + j2k.load() + jp2.load() + + self.assertEqual(j2k.mode, 'I;16') + self.assertEqual(jp2.mode, 'I;16') + + def test_16bit_monchrome_jp2_like_tiff(self): + + tiff_16bit = Image.open('Tests/images/16bit.cropped.tif') + jp2 = Image.open('Tests/images/16bit.cropped.jp2') + self.assert_image_similar(jp2, tiff_16bit, 1e-3) + + def test_16bit_monchrome_j2k_like_tiff(self): + + tiff_16bit = Image.open('Tests/images/16bit.cropped.tif') + j2k = Image.open('Tests/images/16bit.cropped.j2k') + self.assert_image_similar(j2k, tiff_16bit, 1e-3) + + def test_16bit_j2k_roundtrips(self): + + j2k = Image.open('Tests/images/16bit.cropped.j2k') + im = self.roundtrip(j2k) + self.assert_image_equal(im, j2k) + + def test_16bit_jp2_roundtrips(self): + + jp2 = Image.open('Tests/images/16bit.cropped.jp2') + im = self.roundtrip(jp2) + self.assert_image_equal(im, jp2) + + if __name__ == '__main__': unittest.main() diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 76c1d169b..97ec81003 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -160,7 +160,7 @@ j2ku_gray_i(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, case 1: for (y = 0; y < h; ++y) { const UINT8 *data = &tiledata[y * w]; - UINT32 *row = (UINT32 *)im->image[y0 + y] + x0; + UINT16 *row = (UINT16 *)im->image[y0 + y] + x0; for (x = 0; x < w; ++x) *row++ = j2ku_shift(offset + *data++, shift); } @@ -168,7 +168,7 @@ j2ku_gray_i(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, case 2: for (y = 0; y < h; ++y) { const UINT16 *data = (const UINT16 *)&tiledata[2 * y * w]; - UINT32 *row = (UINT32 *)im->image[y0 + y] + x0; + UINT16 *row = (UINT16 *)im->image[y0 + y] + x0; for (x = 0; x < w; ++x) *row++ = j2ku_shift(offset + *data++, shift); } @@ -176,7 +176,7 @@ j2ku_gray_i(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, case 4: for (y = 0; y < h; ++y) { const UINT32 *data = (const UINT32 *)&tiledata[4 * y * w]; - UINT32 *row = (UINT32 *)im->image[y0 + y] + x0; + UINT16 *row = (UINT16 *)im->image[y0 + y] + x0; for (x = 0; x < w; ++x) *row++ = j2ku_shift(offset + *data++, shift); } @@ -516,7 +516,8 @@ j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, static const struct j2k_decode_unpacker j2k_unpackers[] = { { "L", OPJ_CLRSPC_GRAY, 1, j2ku_gray_l }, - { "I", OPJ_CLRSPC_GRAY, 1, j2ku_gray_i }, + { "I;16", OPJ_CLRSPC_GRAY, 1, j2ku_gray_i }, + { "I;16B", OPJ_CLRSPC_GRAY, 1, j2ku_gray_i }, { "LA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la }, { "RGB", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb }, { "RGB", OPJ_CLRSPC_GRAY, 2, j2ku_gray_rgb },