Skip to content

Apply rotation and mirroring when decoding to PNG or JPEG.#3059

Merged
maryla-uc merged 3 commits intoAOMediaCodec:mainfrom
maryla-uc:irot_imir
Feb 26, 2026
Merged

Apply rotation and mirroring when decoding to PNG or JPEG.#3059
maryla-uc merged 3 commits intoAOMediaCodec:mainfrom
maryla-uc:irot_imir

Conversation

@maryla-uc
Copy link
Copy Markdown
Contributor

Copy link
Copy Markdown
Contributor

@y-guyon y-guyon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Comment thread apps/shared/avifjpeg.c Outdated
avifRWDataFree(&exif);
goto cleanup;
}
// Ignore errors: if the exif is invalid, we can consider it as equivalent to not having an orientation.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe assert(result == AVIF_RESULT_INVALID_EXIF_PAYLOAD || result == AVIF_RESULT_NOT_IMPLEMENTED); to be safe

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I added a check for those errors, and I return in other cases.

Comment thread apps/shared/avifutil.c

if (rotation->angle == 0) {
const size_t bytesPerRow = (size_t)bytesPerPixel * srcImage->width;
// 0 degrees. Just copy the rows as is.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: In this case the function could return right after *dstImage = *srcImage; (note that this branch is never reached when called from avifApplyTransforms() anyway)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would make the contract more complicated. The current contract says that dstImage owns the returned pixels. The RGBImage struct itself doesn't know whether it owns its pixels, so this would have to be communicated in some way. And in any case this path is indeed not used but just there for completeness.

Comment thread apps/shared/avifutil.c Outdated
Comment thread apps/shared/avifutil.c
// May be less than image->rowBytes e.g. if image is a cropped view.
const size_t bytesPerRowToMove = (size_t)bytesPerPixel * image->width;
// Top-to-bottom
uint8_t * tempRow = (uint8_t *)avifAlloc(bytesPerRowToMove);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Not strictly necessary, is it faster and/or more convenient to use a temporary buffer? mirror=1 do it pixel by pixel anyway.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it should be faster although I haven't benchmarked it.

Comment thread apps/shared/avifutil.c Outdated
Comment thread apps/shared/avifutil.c
Comment thread apps/shared/avifpng.c
Comment thread apps/shared/avifpng.c Outdated
@maryla-uc maryla-uc merged commit d01ba22 into AOMediaCodec:main Feb 26, 2026
40 of 48 checks passed
Comment thread apps/shared/avifjpeg.c
const uint8_t orientation = avifImageGetExifOrientationFromIrotImir(avif);
result = avifSetExifOrientation(&exif, orientation);
// We already rotated the pixels if necessary in avifApplyTransforms(), so we set the orientation to 1 (no rotation, no mirror).
result = avifSetExifOrientation(&exif, 1);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maryla: This avifSetExifOrientation(&exif, 1) call was added to avifjpeg.c but was not added to avifpng.c. I can observe a difference when decoding the attached happy_dog.avif file to a JPEG output and a PNG output. The following patch (to illustrate the bug, not for check-in) fixes the difference:

diff --git a/apps/shared/avifpng.c b/apps/shared/avifpng.c
index 3e6b034f..826b4f2b 100644
--- a/apps/shared/avifpng.c
+++ b/apps/shared/avifpng.c
@@ -783,6 +783,7 @@ avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint3
             fprintf(stderr, "Error writing PNG: Exif metadata is too big\n");
             goto cleanup;
         }
+        avifResult result = avifSetExifOrientation((avifRWData *)&avif->exif, 1);
         png_set_eXIf_1(png, info, (png_uint_32)avif->exif.size, avif->exif.data);
     }
     if (avif->xmp.data && (avif->xmp.size > 0)) {

happy_dog.avif.zip

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maryla: I filed issue #3070. Let's continue the discussion there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants