From a32dd832530a176b5b45f06c3a97f52383227480 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Thu, 26 Feb 2015 11:35:20 +1100 Subject: [PATCH] image/jpeg: support 4:1:1 and 4:1:0 chroma subsampling. The test data was generated by: convert video-001.png tmp.tga cjpeg -quality 50 -sample 4x2,1x1,1x1 tmp.tga > video-001.q50.410.jpeg cjpeg -quality 50 -sample 4x1,1x1,1x1 tmp.tga > video-001.q50.411.jpeg cjpeg -quality 50 -sample 4x2,1x1,1x1 -progressive tmp.tga > video-001.q50.410.progressive.jpeg cjpeg -quality 50 -sample 4x1,1x1,1x1 -progressive tmp.tga > video-001.q50.411.progressive.jpeg rm tmp.tga Change-Id: I5570389c462360f98c3160f3c6963d9466d511de Reviewed-on: https://go-review.googlesource.com/6041 Reviewed-by: Rob Pike --- src/image/jpeg/reader.go | 17 ++++++----------- src/image/jpeg/reader_test.go | 2 ++ src/image/jpeg/scan.go | 13 ++++++------- src/image/testdata/video-001.q50.410.jpeg | Bin 0 -> 3259 bytes .../video-001.q50.410.progressive.jpeg | Bin 0 -> 3125 bytes src/image/testdata/video-001.q50.411.jpeg | Bin 0 -> 3362 bytes .../video-001.q50.411.progressive.jpeg | Bin 0 -> 3251 bytes 7 files changed, 14 insertions(+), 18 deletions(-) create mode 100644 src/image/testdata/video-001.q50.410.jpeg create mode 100644 src/image/testdata/video-001.q50.410.progressive.jpeg create mode 100644 src/image/testdata/video-001.q50.411.jpeg create mode 100644 src/image/testdata/video-001.q50.411.progressive.jpeg diff --git a/src/image/jpeg/reader.go b/src/image/jpeg/reader.go index 5d7fefc6cd..8fcb2e06c1 100644 --- a/src/image/jpeg/reader.go +++ b/src/image/jpeg/reader.go @@ -42,12 +42,6 @@ const ( maxTq = 3 maxComponents = 4 - - // We only support 4:4:4, 4:4:0, 4:2:2 and 4:2:0 downsampling, and therefore the - // number of luma samples per chroma sample is at most 2 in the horizontal - // and 2 in the vertical direction. - maxH = 2 - maxV = 2 ) const ( @@ -346,12 +340,13 @@ func (d *decoder) processSOF(n int) error { d.comp[i].v = int(hv & 0x0f) switch d.nComp { case 3: - // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2 or 4:2:0 chroma - // downsampling ratios. This implies that the (h, v) values for the Y - // component are either (1, 1), (1, 2), (2, 1) or (2, 2), and the (h, v) - // values for the Cr and Cb components must be (1, 1). + // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0, + // 4:1:1 or 4:1:0 chroma downsampling ratios. This implies that the + // (h, v) values for the Y component are either (1, 1), (1, 2), + // (2, 1), (2, 2), (4, 1) or (4, 2), and the (h, v) values for the Cr + // and Cb components must be (1, 1). if i == 0 { - if hv != 0x11 && hv != 0x21 && hv != 0x22 && hv != 0x12 { + if hv != 0x11 && hv != 0x21 && hv != 0x22 && hv != 0x12 && hv != 0x41 && hv != 0x42 { return UnsupportedError("luma/chroma downsample ratio") } } else if hv != 0x11 { diff --git a/src/image/jpeg/reader_test.go b/src/image/jpeg/reader_test.go index 4de2e8ee73..c5a36cba21 100644 --- a/src/image/jpeg/reader_test.go +++ b/src/image/jpeg/reader_test.go @@ -23,6 +23,8 @@ import ( func TestDecodeProgressive(t *testing.T) { testCases := []string{ "../testdata/video-001", + "../testdata/video-001.q50.410", + "../testdata/video-001.q50.411", "../testdata/video-001.q50.420", "../testdata/video-001.q50.422", "../testdata/video-001.q50.440", diff --git a/src/image/jpeg/scan.go b/src/image/jpeg/scan.go index 4168936312..8fcf401dd8 100644 --- a/src/image/jpeg/scan.go +++ b/src/image/jpeg/scan.go @@ -26,6 +26,10 @@ func (d *decoder) makeImg(h0, v0, mxx, myy int) { subsampleRatio = image.YCbCrSubsampleRatio422 case h0 == 2 && v0 == 2: subsampleRatio = image.YCbCrSubsampleRatio420 + case h0 == 4 && v0 == 1: + subsampleRatio = image.YCbCrSubsampleRatio411 + case h0 == 4 && v0 == 2: + subsampleRatio = image.YCbCrSubsampleRatio410 default: panic("unreachable") } @@ -175,13 +179,8 @@ func (d *decoder) processSOS(n int) error { // 0 1 2 // 3 4 5 if nComp != 1 { - bx, by = d.comp[compIndex].h*mx, d.comp[compIndex].v*my - if h0 == 1 { - by += j - } else { - bx += j % 2 - by += j / 2 - } + bx = d.comp[compIndex].h*mx + j%h0 + by = d.comp[compIndex].v*my + j/h0 } else { q := mxx * d.comp[compIndex].h bx = blockCount % q diff --git a/src/image/testdata/video-001.q50.410.jpeg b/src/image/testdata/video-001.q50.410.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..4cebd1eb25c9e4ace1f0ec27302b205bcdebfefa GIT binary patch literal 3259 zcmbW0c{tSXyT?CcFbrWBGK>Z@WGNc5lYJS=G$JbBB+Ce68DtA(t7PA@#5b}GW2-Ea zQc;a1ePxN5$da*?qA^04qu=jb=UmtM>zwnxujl#azMuE?yzb|II3GFFfQYRX&I$m5 z006Wf0EYpX10n)~LV{2cp#wsqFcC3HB`HY>2}vwkK}Jbm)5uUyQ&-2t#?{`$!r4+s z_cV#%>~@*xLp*URI5NmH!qtoD@y7%N6BU&dmsFRMQui>^HS_pCi}MnI@c~329}F@B zxL_bK48(a2p!V;{1NxVM{}hM|%+13K;p2x2>=(3%09+t2n2Q_C!^6$JUmdfb2e@H8 zqH8!KMx$_>LUfx8XE50G2VOPT=BBK%#lag=Vx}B1plbe@+x1g}7{6R%! zRW+ri_R-_!mew}fllHDxuiwzSdwTnZhDSb(j(r@Tn3agY@*q?Kb9_c&?GM_&$o_X=@&7Nf{{s7Ot_eU84BCG@FbpsT=3hPl52apO zvuoN;Arukp=z4VJYJiQhoqe)Wr8uSRcsaeK<|^|p(qV2YHN;XiiGE|n<{+d9x~rmh z9hULyCadQ9_VB|WxT$g)2RN&{C%3bRC)j!0SS!ZsR>*8UfEX!v|By1%#5?LDQ3cb% zQ;p_Tk^j^_7I7ge=U26d!P=tnt_5ao4~6Lv&w$|W5kQ)Kd?^84K~6qK!U8n5zK zX@gqITPz?<_IozCen(Z;v9kS?#_pD@=K|xs6QgJ4d(K;0l2_`gsW@}91|6s7n3)bs zfURpyGWtD!){k&U>hV0CYebaKRA1J%P0<$>S=bs*?_a1b_L&PS(Onu_5&iP|Rj4N$ zJ-o^Rpp|V!+P|UD(Tyhpkt)NV#4w~*EwsT6dgP+;dYxI-U6SI&yN**i@4Y`)Do%|* z5Et*+9WdW-1l7QYVWlGQ@OQ%A>R#w!O66k=ItQ0gAA0DZU1Y^QJklSXW}?Qcfg5nE z^f{5?tDf)E=)e@1+;k`I@f7o&TDpQbb42t68MA#NT)c zum`3s$puv_ht^O3QbR!%!4tU&f8{4~0KdT*A_q9fk4>iIW%PevuEQ%QV`<=T<+Pxx z(wgBT@I#4Xgc<5r@z&p9oxEUL(^SM=qfYh)we4h9WcDQ1#U0_lUS1Ty6w7Y6l4uJF z)Otj(n+ghI@EHoiHpA)+9i|7{isB7uZ{6S1>+FSHL+HMvARu1yP@^Yt0BeU#>NA#PMv~hJFlR|U9=_WeFYk#HZ7>%CM|+pedX6LDep3wWe{4? z)GP>rCzO8P^<4^h<{mgO9=cRy2JjL=*7MnZQI7LRPpXU(-rsq1bwFmvK~q_j4gjpI zduvPYE@gGwY7gRR?WNmG4QdsiH50ORp$32fG#L>WEGcr_tbw1^cvJy2^O%*MxINDE*by;T+hr6PZDX6! zRquA`wc`c?A|u@4)K)UmhSEG#xHME>u~xBmCv!kzro(YMi`}rHeqae}ftI>PrjpN} zeA3%(xNB^?lt_Z7BmVj6&k7RHyMctIIA+ov)gt!7$`f%?wC|dBZQ{{%DN<{`e_Bgj z(2?5Ceq!1y2x_2cz9Pquio$>KJKNc2-bTY@lxg{qLS`R$77ixo*TYtJd{kGgR9f$+ zZRlL8Nb>6K2A8tK;RVM%QWzu0QO5QuLlf>?$BRqmA!;!(dwFm1=P+ToMe_MOOoF7W zN9l96Xq(AvUL#2Th34VLz=GZEQCxD64NB?6mo?|lUFZACUyRT8%8HA{o4nkpH=s)E zg|4&?_5>WA*RWc4JYHAu$?nqcjIIX8SBZo9S;aL+pVc@U91cOp@X)BUVqpJgaiUncG&X?!|0qwld(>ZLm3f_K+m zp0Zm9En9;Z_jPk@f3CvEL@eNMn(pr|nlBoNjc|bNHJ7yr2kEzx71`fIvu$39lwFOt z3QmnXZ)13%`sFO{hZ;t8Z#iS$d4<$W!k$|p&GiX34-2{1;a8lLIW$;$zb z98+Ty+{NS!Zxe#O0>cC>^#ju$=3zGUP<{8Mw5|_)zfc-Cs!MQ3T}Bs#twIw zy?SHt03Y~?Nm{9Hv(x=9&ycs2l`ZL3*flvZd4Q~~amPCOoO`;H@KethVNC`cp!7qX zp<8q^i{M56@_nig9z}vwa{%g8%QL-MFnI-@T0Tc{U7cfmiVYh-@i!425baag9-yK* zW)$0!QMVZ7ReC4Js3jHK*QDz#&@9F{@U>O>O_i_ zp2&67$ES#fy8I}x)?dXeHvae)XZ!U?V}2;8Ks!pRd+lgn$H>`bbH2Mr*~Yv6v7yd} z%eD}UD>dKjh_iS2np>=L5tp~L*D*pji+bDFJTyuEV2fWjMSdXEd0u5 zQ`X;7(^R$sNaKRVwwW(8O_2+Kvr;}udF30=jjH!G<#NHTXjhJbx-mlQOL{rJyB}uD zju?&(_)g?i!UqQk&0F}Bt1;`_$!QC&nV#p)Y4BO{PQh9dPlc3B;!^6(5$hb1ymZw?OC+R z``deCESww!lP`B2oz%?%F1A)%mt16?q4uqKJgwsZBKLd_E(}TRIn7%-(OtkcS=PNT z|M43Q4G5(MP9csJ2u1d=8{FuzhaJVPXeX$};=Z7-3$JEG&791bytVDXUxF8k6B}*H zdHk?8?L)6(3pZ0UA^m2@g`tK<|8x2Ltq)NunUdgjBV*{J?Po;hJ-xi=L_;$E;<^)e z&O28M^QNehkW-9iPSMB5IBwK)%OfuLQjzIO4gTiwxL#@Zt>P=1=5nUqi##FA+|k97 z6WWbUl(Fl$uTBE7?B}@zOYw-f8F|%f{_FUgaX;02Ie^=KgToxX6NZbF%2PhY>z3JE zXXGSJ@PC;b%4SZfQp4lJCZ$u4{iBkqFH{n!VF$ODu|fZ;v%aW1U^rZC-sGX)t0ekR zjjBn(u&0p(OZb^HxmP`#i$5Ir5P%*GMO+k~y!C)_5CLp7qD!5Nyz{*$(b@em_$Y-G zOPk3t0oguey04+&{=u{4PooCH$tDe&J>6OX%KVumZguY;9ALD**y>$f>~+!~cYhHmK_VJdBnD%$Wh`R}Q6xht%gB~3ONCx*gtBDGn#5FO-+3%y z2xDz**|LO+7DBefJJqv&-v8cn-Tz&m>wBN&ch1=x*_#Fs`Z{_#00;sA$T+~>IG_n2 z*g4=FYzR0f9EC)n`NaA8czO9`MUM!IE6Wj7l;ji@)O9bNQP(olR!}r@H8!&%J2*P1 z8hZF%yW(@n&cXI41Vo}xd^~*8{QT0kL`960@qqyJXCVI=;|gHf8v{6?AOJx?5sb%OptkOf?2zCxsAcY(hnVc% z6V`{V4`SDL#;Xlg9Em@8w7oUTpz@xM<}`c(GSiUrKTl+R%a00F9;N9-;Ec|_x{@V? zmHaSEt#y|-+9&%?n%p9JZmIv^=#@tEb6*Q*PXZ8cLAHuSGsP3!{LX(g2Jnzt>a~#auy=|!*kcw z^SIhrL=9_5z0}3w`KoZzrQ0+mG0FL)`^4ww;YohgY5u41e)+YW2-KpV1U~DPqcf6} zHt+YSu%?5B=?Y1ZJA>_gPM)7%PEI}nk6S#hZ{J@?BCFazZJ_08m|Sg6GGEsp&GQ#X zKlXz^Hrtw5_RKrOSlOv*>Bmmss~hjWcH7uux1G0QahdrSo~Nd!cCRuVzqY%3a%V@q z)s7?k?%*2gA~lVbhFNBY&IhP5k}Ze?7=mJa{}>2CAfd7-VdGyJW)wjU%<+7tdzmwG zFelW363{Czo7b$O(!=eY-09f@Cy2et7R+Rz4lz*w!vY~lC_)%5i@|C9f%A(rXF8w|29-1Crx1hr4RrJg}@*%DC|$~7?MW{ zGjxE#8Ovyh*m%TcLD9iRo*|_(f2x654f2C+5uQ=zYcwN#%I%FP#^@MV*g?lcfzI0e zJgI&u@pF^T(*B}x2BMAn8Po_|@SGFPAofc03}W$eyv^_OsTk2`DeQi^9@Q*CtTBlDrqmjX zkO@SWQZ&p28fN`ici#{x4?b_a?|yaW6&74D7DtTV;F)p!c3jkjsh8WxrEsNcr!b%V zFog8#(ZwbXTdu74z$DM&{_8Cy`!}bH3H!HgwTb!HD3p0% zr?XmKu;w2TDq$wd9OJfqkX{suNlpX#Dxsmla;ccwR@ z=4T+Mx6XeLHT6BhOYZIr+XDhW1qzCN*U4NV&&yOW8KG&ktMQ{01)_89;JT!XrC=W= zE=mrIDao6NW~ES2@N2PD?&`?KNz|adFKfb!mpf%2JKEAFgsiqWzi)BPxa=}_b1V@>d#8D1>8BsEMg8HeX(AhkRAmyhuRrV(h$wp*N^237rf+$Qm3qsOo^4tP zeJps9SQ8ZkdvfVc@fLS#!5<-B0xmx3Qtb7qrO# zZ#)x~9a+I`WVGQ$XGsgy5hC_fhaWn}Q5IYEGD%`Zz?+ts!QqBc`dpZVNiA^dH<)&= z*#mwy{I9hF{1gm=dwvA-rNeyla_y}mg_M?7sYZEri|y-YBwKw`qqu^06>fFWC51P3 zg8+d&fFU>mB>2;QJ^tOVAb^w=MrhdlJxCA&ZUUYn&$*I0Uv^5dplP%FBiiH_8saT2 zBsAkT!khw@u=l+V+mDxv>WqmiScUHIfBQL*f#&~>{?{gCpc#YUH~HsOL5$mcZdAdo z3%xm8a=D6-?AF?F0n#M!_%uMPemko!C>FJ)#8lQ&C+Vq$^x08~UR-WuaK#Xei2^b) znuM`Uf7vgthOv!DaOpB4E~^e72STsC5IlX;_*jHBmsS?WACYL@R_F}NL1 zk`IqG;;R5YliLz+xfik8u(QN1>6tG+^b0gOzbq;gNuAehrZ1r-i)FNeSS5xU{zJS< z=n0V8ouzQ{cIn-;PhL|&JuR?~n~BtQfG@t@k2+pqJXK$Ev3wL>>?>Qi8kY}l zLiZA@EB1?bM+0le%c6}t_b*4P|J;P*~Mg>1f_Cn0{dP+wsvwE+ajvY-Zv`fZyaFOrhsz>(Dw^b>WV`4D z!g}XQIgyk)4r)YMaE_bMaZ^$j%&+RB>s3m(9u6&Ugrij`( z)sMl=Evt)DjPD(cay@8?Mh+p#HiGHFEiCl$fz8mSs;KMW&4gXENoKPmBYL++yt+CY z&dN%qO|KNxGMEfFUq-QGD`5+K;D1?7s8!S2-b;G|R%z(Cgbh6aaD|`M{Uj|HRa8f9 z8~{S@f(2RiU5~Zw$ISa8wAl4eVf*h#(aiCc^11kX$w3Wui65$Y-8y|oV-2|-sE2Yv zSgqf{iI1{2Ma)C$Xzo40-ghu})j{Ufc&`k+ceXYbK{}LQ(8EE6PS?4y;}h)U$>xGU zP*a`{Yfw??_hZW{hfc?&hNYe$&KDgqiDu75e>!S=iM*PUq)0>6PG2PpJ{dWA`Yn&r d%?C;BH|&j#m{&-)B9Y}cx3JS}V^PKbh>-YP;uXA1hT%U8D>vcXK<`{DZ;5X36=>s4T0Dz7b zz?=kh0sdp$yxd&;yga;u0{q9}$dhm(A-EDkMhvNbLF2O81+?lFtj+Z+S1t5Z(KkHs z7IqFUt}dEJKEd9OK{ie<#J?W_2?z?pVes=OPo5|0pmm7<=P=&@0vvz~kO>B911tg{ zumFhp4v;wdCoAZm0sb8j3mC%6#?HaXb?m61i63AAfx#>gFe@tra#Vfq=pKLwunI~k zX|o;2+q0kYfhs>pE#N@t)OEm2K7NzF;6RGx$e*OW0LBSzW52IsZAH~I|r9aNde3F%&Q&?17^1QUH zyrRCLv8kE*vgK9h+jm{vJ-xK|^r26~Bco&E6SJS^<`)*1mRDB4|JdBx-ub!vYwvFt z2mt?!b#(p<_CH(#M=ll!1Po#S+XZ3?JlbFZ2&XTu=|X3pFENw4#j+K$rBi!58Co`T43ZiElkyRY=|@CV!Ubk8V*OPs@9K zhvS3BjT!H7;)9-$Hs0(d^@=`i``pKKzZTO2*uKP51JcI(9I|-creLIE%gU5`+}P>y zPLf?3L>GU8u(Kp+-6$|eGu6q3>oFDg+^$`LoVy>w)fz0;Mh_0;ud1VPoT`vjabHgD zbXWHos+o|X#lT#Qk%}LBli|q)FV?4t5*eQP2wLyizr=z* zoZz`6P;Z7GOo`R%ui(=s;%$p4qf%Zn)}fJ(iOt=`F5c(?4bWl6BQyHf;z=vos#`Np zqel^pb?kQs69|e>$E^yEr`OIero^v=sfji9XRExve}dZ(@E+4V%qZY#%H6ywD9y%i z=8@vErQ&@$6na4LXR%Ju>rHL0y@3YATMjs0m_ivv)A1soUfzLTV{ejUYdy<(I9k*> za9A%$+Z}xFLq`z{W?y;LK)SPjmxfdGk@lc$N<3N2xT+Z8d^21e1 zNp+Oypx$YDk&Z)h#I)U#&Xi1ptOBiA&N^IVTEz+59oCx#+wQr1h(1^=Oq#h@_NLZo z@w}E|XY#j-s888#+;*_Z%YomqeZ_`w0Nk(d+f%9%i4j9i>hbB@$OIK$c}^)GIs+Ad zIEtUGT!S_5gHtv;3x=6#dgKeegf^J|*m z6%&!FV~of&*b`Y%-q2B0AjWho79aK=0pa!?oh~$AhTreW->wYE@&CFhQEyFC?B@h8 zuuoCs_je7g%q;dVTS>DNdSl_@!z)9#RxiJg+q@t%T#y}gjw`IhcS<}W!_2t7iF86u zrQ|LXI18PROE~rg9#45IIalzrqIvUG=G-E2NJE{_NI_AQNW!0|R4Z}?HJhfpX4_69 z9}0BNslw*BJP$mR)nam0g)J9tvr=Kai>SkHS>B$ zs-rw(y}WS}I&LEqOIQ!ZV%Ua*FxcNtk&6j6=iCYLbThkYZ z=Ehi|)GB-nBN{Hc$2*K7=Q*YzWimv(Na$sSkH#6frbb0HDo?y}AaX)`m9I^iMR;tE zJVHkNaS3moyHh*a&><|5;amytupH?B5dB-;q&?I}+L1eb^n18#kPt@~&^mGSPPqd!G}3EYc_ zmL0cf>(ko(xoc|8;6QxE`yT!o5p6rX6p78h>7T+d5JJYKyV7=49wJGt&3@`})g0Ad zOR0wF^;46l^du#p^~n6bqRHSPT~toM#8}xU5%^ji+x=@VF#*bO)n&WTm`%JBX?}hB zJv79Fy_5-5PB*<)`wS+1g^~;B%51*PO-_aTj}3pM1r)nauUI=bb5hQq^X*8}%4nCc z>uxyp%?z)^xF=;idkoJ0{R3g7(d&!5>tcMsGuxfJP937hDVmSX-HNcK#-;}6xpaex z&@|irG#uOkdTtQguavxoV}H=ND~i)tco!YPz#mlnu5M0zuAoukDt2D4|T z9vC-D7@mI50L7pubY<1TZr|qXe)cO4zv?G$U@UBYh7BuqT~4wtlIPI!wk=iZ98R`$ ztMK#t=~bb4zrRF?^0+S~t&A%ucTnCG^)eHv^7>v4CB!WeMYM-RHg38cPN?qEBRaK8 zA_nrAz#o$bu1w%zr~wnmQ#$Gab$2jKt3=q`)JZtir>UN#G}J~&X<3+R+`E^X*z&c| zdF5rdi(zY7Dc6gLpxAUzHBL&qoptiH zS1;)`nTw1RT4OmkCq$q>mkpczv@KUDm@G9VE)UPQEZp#E*;C@YRa&g^a5mD#l)mum=isAI9 zYdSRtdhui1h0aU~dP^b-6!s45_@lXAOCyliXP2(G-_yv$k@qzMmBvrPIlx)aHWAxv z4ZLlmtG)ye+|9aC*Qpirzg{cbD~fKWuGzGc53ZM;rhBm29HjSR0Et=g?apk8R)MYJ zu+@Lv-Qj~YfA}wGodZdcgf49HAvNM(oYdSz zDMN)k?^|f*V_NDGi`@3P5`!R=rW@3an=96{M{`JC3%k2z92@cDJdFw1@f}3_n>j}f zxJXxLhnU}I5^tu=p@v{nZ3q5do=&5ck z@jMMFSa+`SLYegTj3}YID!=NHcS0c1|K_7DTQ-ZRpAN{KuI?)umAy+Di`gaFRP20% zgz0pgL#m0Ye`l|9EFvcv3?pxZKE>F7>xFd(!xyW&q?CE`qrWhiz}|3!>v&W0AWu=e QLOQhVlZP(uk_2=7AHhd6lK=n! literal 0 HcmV?d00001 diff --git a/src/image/testdata/video-001.q50.411.progressive.jpeg b/src/image/testdata/video-001.q50.411.progressive.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1ddb22b8b0e0b7f815ab793617fe53b252c107fa GIT binary patch literal 3251 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