From dc714be93496cb3fd80805f4414b7e0cf2e88ddd Mon Sep 17 00:00:00 2001 From: Roland Busa Date: Mon, 12 Oct 2026 09:51:43 +0200 Subject: [PATCH] heif: apply all rotation and mirror combinations read_orientation_helper() mapped the irot/imir properties to an EXIF orientation with a list of conditions that misses several combinations, e.g. a 90 degrees counterclockwise rotation followed by a mirroring, which is how iPhone front-camera photos are stored. For those it still returned true, so libheif was told to ignore the transformations while Qt got no orientation either, and the image was shown sideways. The orientation is now chosen by a switch on the rotation and, in each case, on the mirroring, which is applied after the rotation as MIAF requires. Other rotation values are left to libheif. The new read tests cover the twelve rotation and mirroring combinations, named like the AVIF ones. All of them display the same picture (colored corners and an "F"): the stored pixels are that picture with the transformations undone, written as uncompressed HEIF with libheif. Four of them fail without this change. --- autotests/read/heif/rotated090.heif | Bin 0 -> 4868 bytes autotests/read/heif/rotated090.png | Bin 0 -> 167 bytes .../read/heif/rotated090_left-to-right.heif | Bin 0 -> 4878 bytes .../read/heif/rotated090_left-to-right.png | Bin 0 -> 167 bytes .../read/heif/rotated090_top-to-bottom.heif | Bin 0 -> 4878 bytes .../read/heif/rotated090_top-to-bottom.png | Bin 0 -> 167 bytes autotests/read/heif/rotated180.heif | Bin 0 -> 4868 bytes autotests/read/heif/rotated180.png | Bin 0 -> 167 bytes .../read/heif/rotated180_left-to-right.heif | Bin 0 -> 4878 bytes .../read/heif/rotated180_left-to-right.png | Bin 0 -> 167 bytes .../read/heif/rotated180_top-to-bottom.heif | Bin 0 -> 4878 bytes .../read/heif/rotated180_top-to-bottom.png | Bin 0 -> 167 bytes autotests/read/heif/rotated270.heif | Bin 0 -> 4868 bytes autotests/read/heif/rotated270.png | Bin 0 -> 167 bytes .../read/heif/rotated270_left-to-right.heif | Bin 0 -> 4878 bytes .../read/heif/rotated270_left-to-right.png | Bin 0 -> 167 bytes .../read/heif/rotated270_top-to-bottom.heif | Bin 0 -> 4878 bytes .../read/heif/rotated270_top-to-bottom.png | Bin 0 -> 167 bytes autotests/read/heif/unrotated.heif | Bin 0 -> 4858 bytes autotests/read/heif/unrotated.png | Bin 0 -> 167 bytes .../read/heif/unrotated_left-to-right.heif | Bin 0 -> 4868 bytes .../read/heif/unrotated_left-to-right.png | Bin 0 -> 167 bytes .../read/heif/unrotated_top-to-bottom.heif | Bin 0 -> 4868 bytes .../read/heif/unrotated_top-to-bottom.png | Bin 0 -> 167 bytes src/imageformats/heif.cpp | 77 ++++++++++++++---- 25 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 autotests/read/heif/rotated090.heif create mode 100644 autotests/read/heif/rotated090.png create mode 100644 autotests/read/heif/rotated090_left-to-right.heif create mode 100644 autotests/read/heif/rotated090_left-to-right.png create mode 100644 autotests/read/heif/rotated090_top-to-bottom.heif create mode 100644 autotests/read/heif/rotated090_top-to-bottom.png create mode 100644 autotests/read/heif/rotated180.heif create mode 100644 autotests/read/heif/rotated180.png create mode 100644 autotests/read/heif/rotated180_left-to-right.heif create mode 100644 autotests/read/heif/rotated180_left-to-right.png create mode 100644 autotests/read/heif/rotated180_top-to-bottom.heif create mode 100644 autotests/read/heif/rotated180_top-to-bottom.png create mode 100644 autotests/read/heif/rotated270.heif create mode 100644 autotests/read/heif/rotated270.png create mode 100644 autotests/read/heif/rotated270_left-to-right.heif create mode 100644 autotests/read/heif/rotated270_left-to-right.png create mode 100644 autotests/read/heif/rotated270_top-to-bottom.heif create mode 100644 autotests/read/heif/rotated270_top-to-bottom.png create mode 100644 autotests/read/heif/unrotated.heif create mode 100644 autotests/read/heif/unrotated.png create mode 100644 autotests/read/heif/unrotated_left-to-right.heif create mode 100644 autotests/read/heif/unrotated_left-to-right.png create mode 100644 autotests/read/heif/unrotated_top-to-bottom.heif create mode 100644 autotests/read/heif/unrotated_top-to-bottom.png diff --git a/autotests/read/heif/rotated090.heif b/autotests/read/heif/rotated090.heif new file mode 100644 index 0000000000000000000000000000000000000000..428c95628ecd3ec685c2865a9063dd7c2c64d4b8 GIT binary patch literal 4868 zcmeHHOHRWu5S`L4!ltSa2$n!E0OA5jz(GPjWu&^Xf{|be;vgK3dxSR$t-Qz}_p?B% zC${Fz%gjss6orV+O7~_Bm)C@lY&b0ezfIv%;Sd*3`MmBRk>$)S@iJfXyvn4wzoh{# z#9-8Aq9ZA%%nOml4eS#X#ldg&vdCEA*s?PM+LoMcZGj)yW)<=TZFe$T&mM1(j``UZ z$P(w+SJ)HFudUQDNfOkDTvtwJPT86iSpx4jdjA-H;>8k^r#MT~yP9>QS4Ex}^;;_y zBNTdF_nUr~qyH--cV_fTF@oqobl~4PApg8gBjhlfYRlX{=xb;6ee-)X-tP&)nvTYY zX!<>xj>h}5Az0JV_z+FMN7K=Ge>MbbIvO9M>Gx0;gQNa7?#0>Yz&X4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5+O>LMX+w>mmx{iJPv|Su@3fojA{h&8 zpzfSDJ%5sXO@4%yh{lC`v4)Eaf{8br76`vg?oz42&K_p-x(kUcXKooU{W;I8Op==` z>fwU*hMgxml61mciYz+NPLSjWi|nb)Si+%YXC!Eclx%B@_?B%}!H+QPTISaGcb9O- z{AhD{u~W1u+Q{;AE7ebu1o?pL%E_Do8?Gf7vo$Ha7#4c?`qqE{z!DQPOI@=iqfheQ zTas_Q;wY+qM?m?kKMA|$v;OVcs|5Zlf%d<$cpE&QLv;RY)gPSN)E_zVTVz zZ*08DnTge09lsCyo9t1?C;=t#8we0J-aC42UKRQFsea2c03uM1#_y-B{C_|OKz{?D C2FO|f literal 0 HcmV?d00001 diff --git a/autotests/read/heif/rotated090_left-to-right.png b/autotests/read/heif/rotated090_left-to-right.png new file mode 100644 index 0000000000000000000000000000000000000000..be62095b39e3883a93ff369e4e41e716a63a1c5e GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!2~34Z_DupQhA;(jv*Dd-cFC@I-tPQJp19_ z_Zudsy-!N~%q~+`c|}Pv*J-M9X4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5R?DYPZ#aO*_;G`l#qVJfT-u|I@h8heqsD zQFNwB=Of9#$&WyZXkPdSaa^1bOugf@K=|fzpK1+uaI;v}AtVyc{5oFyLta*yCKqQk z!UG#kgD2Y2^oY4sSqz}>A<4Hsvb!>44X481Y0yk5S!zLi$&yv@GfX?zwe|JYDcmmK zN)E5KkA93k6TWYx#z~SOpKx7yT{B_F%>m{tE=55N8{K|<8b7~ai-}vLenr&XR{bn0 z6CE&p1>X%{2l`nQb9BJ;8~*(;*lZ(U1b#Jv@T1%8{gs1lrW{uNH~sc`+uYjqzHv+^ wRyX}X!1xTyA$G5Z;`~$kvF}>l2>d+)^h%v=sFQYzqiFhpfbseKCVV%acgY>dSpWb4 literal 0 HcmV?d00001 diff --git a/autotests/read/heif/rotated090_top-to-bottom.png b/autotests/read/heif/rotated090_top-to-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..be62095b39e3883a93ff369e4e41e716a63a1c5e GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!2~34Z_DupQhA;(jv*Dd-cFC@I-tPQJp19_ z_Zudsy-!N~%q~+`c|}Pv*J-M9X4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5&$`+Tv}#!bMZ?<^C2% z^r9F|x(sm4*BRoH*HVMdDP5|A&FytrAj2bxN-$WDlqg$C_yJ|nP@dA*9e=icdc2`@ zf-kb7%rU2PL1!xQZM|xoWf|QMv27I7oS_ssWe(Ce`S=`vk+4jZxmpbr04wV=kQ}=h zQaDst%}ILn*YQ97>3+lAySBm#;`sA-^;k^{UhMBraBAzqq{nKDE+&7hR+zSEtS93D z&FcbjWM~$b3|-fgae(G^fjBb!ET%U9WSnl&_(f4V${-5295J68qBV1Ls&gKSo*gE`O}|p$7IsuHz@hDmp{L~O2zU3 literal 0 HcmV?d00001 diff --git a/autotests/read/heif/rotated180.png b/autotests/read/heif/rotated180.png new file mode 100644 index 0000000000000000000000000000000000000000..be62095b39e3883a93ff369e4e41e716a63a1c5e GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!2~34Z_DupQhA;(jv*Dd-cFC@I-tPQJp19_ z_Zudsy-!N~%q~+`c|}Pv*J-M9X4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5$nIRgPlDtS9uvKWhg4P@z$TyDvLO| zy;dWq=Vc0SSFaKOX4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ550hy-HvudbZfP&|DNdV$otrD@ z(M8f5&NaXRyJv_sx1|KFBXY{Uh4)k!$mY&T;LMGSs0`b52+$mqX-3Fe literal 0 HcmV?d00001 diff --git a/autotests/read/heif/rotated180_top-to-bottom.png b/autotests/read/heif/rotated180_top-to-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..be62095b39e3883a93ff369e4e41e716a63a1c5e GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!2~34Z_DupQhA;(jv*Dd-cFC@I-tPQJp19_ z_Zudsy-!N~%q~+`c|}Pv*J-M9X4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5QXiX^FT%+l_o%E$^=o4*1dL zh+-%33;4qFb3+xyag2PItI|aPM{G@kD2DmwuW!*i28&IWIQk&(swXlpIf@g}*UI0o zr0UbJkM61hsz6tP<~OlX4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5ld&V)>zwO42k%zQ?ZiNfQ`$vUtkYWbk6x=b`6;XFv^|Po< zbU^h5d^da<=x0&P(E-(O`1ix0vz34n_|pW!k8ZQ~cMi6ha#*!(`t9?!xwY$k|PDU`M30A-_^Pj_<@5DT_-Q^L|B}dD literal 0 HcmV?d00001 diff --git a/autotests/read/heif/rotated270_left-to-right.png b/autotests/read/heif/rotated270_left-to-right.png new file mode 100644 index 0000000000000000000000000000000000000000..be62095b39e3883a93ff369e4e41e716a63a1c5e GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!2~34Z_DupQhA;(jv*Dd-cFC@I-tPQJp19_ z_Zudsy-!N~%q~+`c|}Pv*J-M9X4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5mmx{iJPv|Su|4BF5MKV^{ zK;1b_dOni=oBRkZ5sgdtVhxv<1QTyKD-nK~!evr}olYmSrVEKI=WY=%{29;cT$0;s z8sLHrhMgxml=Pgr5?KtOA0sLD=Gjw~vxGy-&Pd>=RBRiI_>OH}!;diSMxJdR?yumE z_|X>dVkhVq=p)O|UMfk`6!`%+wM%?p*l%FW)?_G%VWIo4Z^`=ymYAAJ=GqMzeNwP( zNWKkbL8t68NtK{C{PB+r}RMihgXJv3g%tF`Zal^*aK)#%FoI zedBe`Okd6A`MaRM&K`A+5>NuafdEk(Y`xbORFQ9=>U)+E5Rr0k{9($<{|96Q^f$3N B$Xfsa literal 0 HcmV?d00001 diff --git a/autotests/read/heif/rotated270_top-to-bottom.png b/autotests/read/heif/rotated270_top-to-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..be62095b39e3883a93ff369e4e41e716a63a1c5e GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!2~34Z_DupQhA;(jv*Dd-cFC@I-tPQJp19_ z_Zudsy-!N~%q~+`c|}Pv*J-M9X4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5E`BNNEO9^hT zVTT{FuZ4%9L}!Q-DGP1(fP-px5j{^z6u3v|oq%@A9a2M#uaL?q`vFh8k;=`({Uy77 ze4>iI*g5xO?gPS?wp5nqImd@ct~&$M%HG~HfMM=Ng`WZFZPu^B2dGUa!z6n)l?*2n zpSkc!ANvK2s}Fy(YuYMS|Ax=11DN?W#o=jYnP`K5nNPGsZc!-IXNxk?mL@*7l%YN< zwwFgARXK#o=sVg4!>v_pFONQ|atM>rceD%s;?^p$#7C87nN0dDrBFeC`fT?5mH4RF U^aIJw?yEmyiI1&q(#O{48$%_;djJ3c literal 0 HcmV?d00001 diff --git a/autotests/read/heif/unrotated.png b/autotests/read/heif/unrotated.png new file mode 100644 index 0000000000000000000000000000000000000000..be62095b39e3883a93ff369e4e41e716a63a1c5e GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!2~34Z_DupQhA;(jv*Dd-cFC@I-tPQJp19_ z_Zudsy-!N~%q~+`c|}Pv*J-M9X4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ54pZX2HV53m`2BA)gP*p>0TR^#UfV64UD literal 0 HcmV?d00001 diff --git a/autotests/read/heif/unrotated_left-to-right.png b/autotests/read/heif/unrotated_left-to-right.png new file mode 100644 index 0000000000000000000000000000000000000000..be62095b39e3883a93ff369e4e41e716a63a1c5e GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!2~34Z_DupQhA;(jv*Dd-cFC@I-tPQJp19_ z_Zudsy-!N~%q~+`c|}Pv*J-M9X4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5y)Omcfo z6TD!Pne{~bCOv0fnzkrl9U{qgezNCf!Y16)Tq+Y(TT<4AMtsLQS;5b-?Z%uf9`3K; z4){rD@Ww8%PO#>hU#eEqD2kBpvf_&O5o;9#Fb00JxA*A>h(#)maW8A}T90HR8GvJ1 z;Nw?5=re>q_Pc7|{r%fkyFIVr__XD3_Xj?H-GV+rAHM?rYW?#g#{T2IS49qWu=k1n zhV{{6>_6UnRpd|yd!OiUIO1rLSl}bdvIYOLK6P%*czX+j2FX4&yZ|U2-&R@og-F%x^9c1n?$Pu%fhJ@3=dyQ-8r4| RQ5