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.
This commit is contained in:
Roland Busa
2026-10-14 09:59:31 +00:00
committed by Daniel Novomeský
parent b94001fe46
commit dc714be934
25 changed files with 61 additions and 16 deletions
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

+61 -16
View File
@@ -608,22 +608,67 @@ bool HEIFHandler::read_orientation_helper(void *heif_handle, const void *heif_ct
if (rotation_ccw == -1 && mirror == heif_transform_mirror_direction::heif_transform_mirror_direction_invalid) {
m_orientation = 0;
} else if (rotation_ccw == 0 && mirror == heif_transform_mirror_direction::heif_transform_mirror_direction_invalid) {
m_orientation = 1;
} else if (rotation_ccw <= 0 && mirror == heif_transform_mirror_direction::heif_transform_mirror_direction_horizontal) {
m_orientation = 2;
} else if (rotation_ccw == 180 && mirror == heif_transform_mirror_direction::heif_transform_mirror_direction_invalid) {
m_orientation = 3;
} else if (rotation_ccw <= 0 && mirror == heif_transform_mirror_direction::heif_transform_mirror_direction_vertical) {
m_orientation = 4;
} else if (rotation_ccw == 270 && mirror == heif_transform_mirror_direction::heif_transform_mirror_direction_horizontal) {
m_orientation = 5;
} else if (rotation_ccw == 270 && mirror == heif_transform_mirror_direction::heif_transform_mirror_direction_invalid) {
m_orientation = 6;
} else if (rotation_ccw == 270 && mirror == heif_transform_mirror_direction::heif_transform_mirror_direction_vertical) {
m_orientation = 7;
} else if (rotation_ccw == 90 && mirror == heif_transform_mirror_direction::heif_transform_mirror_direction_invalid) {
m_orientation = 8;
return true;
}
// The rotation is applied first, then the mirroring (the order required by MIAF)
switch (rotation_ccw) {
case -1:
case 0:
switch (mirror) {
case heif_transform_mirror_direction::heif_transform_mirror_direction_horizontal:
m_orientation = 2;
break;
case heif_transform_mirror_direction::heif_transform_mirror_direction_vertical:
m_orientation = 4;
break;
default:
m_orientation = 1;
break;
}
break;
case 90:
switch (mirror) {
case heif_transform_mirror_direction::heif_transform_mirror_direction_horizontal:
m_orientation = 7;
break;
case heif_transform_mirror_direction::heif_transform_mirror_direction_vertical:
m_orientation = 5;
break;
default:
m_orientation = 8;
break;
}
break;
case 180:
switch (mirror) {
case heif_transform_mirror_direction::heif_transform_mirror_direction_horizontal:
m_orientation = 4;
break;
case heif_transform_mirror_direction::heif_transform_mirror_direction_vertical:
m_orientation = 2;
break;
default:
m_orientation = 3;
break;
}
break;
case 270:
switch (mirror) {
case heif_transform_mirror_direction::heif_transform_mirror_direction_horizontal:
m_orientation = 5;
break;
case heif_transform_mirror_direction::heif_transform_mirror_direction_vertical:
m_orientation = 7;
break;
default:
m_orientation = 6;
break;
}
break;
default:
// let libheif apply the transformations
return false;
}
return true;