Refactor imir implementation to match HEIF Draft Amendment 2, which replaces 'axis' with 'mode' and inverts the behavior
diff --git a/apps/avifenc.c b/apps/avifenc.c index 162c815..3e8a3b0 100644 --- a/apps/avifenc.c +++ b/apps/avifenc.c
@@ -103,7 +103,7 @@ printf(" --crop CROPX,CROPY,CROPW,CROPH : Add clap property (clean aperture), but calculated from a crop rectangle\n"); printf(" --clap WN,WD,HN,HD,HON,HOD,VON,VOD: Add clap property (clean aperture). Width, Height, HOffset, VOffset (in num/denom pairs)\n"); printf(" --irot ANGLE : Add irot property (rotation). [0-3], makes (90 * ANGLE) degree rotation anti-clockwise\n"); - printf(" --imir AXIS : Add imir property (mirroring). 0=vertical axis (\"left-to-right\"), 1=horizontal axis (\"top-to-bottom\")\n"); + printf(" --imir MODE : Add imir property (mirroring). 0=top-to-bottom, 1=left-to-right\n"); printf("\n"); if (avifCodecName(AVIF_CODEC_CHOICE_AOM, 0)) { printf("aom-specific advanced options:\n"); @@ -439,7 +439,7 @@ uint32_t clapValues[8]; avifBool cropConversionRequired = AVIF_FALSE; uint8_t irotAngle = 0xff; // sentinel value indicating "unused" - uint8_t imirAxis = 0xff; // sentinel value indicating "unused" + uint8_t imirMode = 0xff; // sentinel value indicating "unused" avifCodecChoice codecChoice = AVIF_CODEC_CHOICE_AUTO; avifRange requestedRange = AVIF_RANGE_FULL; avifBool lossless = AVIF_FALSE; @@ -729,9 +729,9 @@ } } else if (!strcmp(arg, "--imir")) { NEXTARG(); - imirAxis = (uint8_t)atoi(arg); - if (imirAxis > 1) { - fprintf(stderr, "ERROR: Invalid imir axis: %s\n", arg); + imirMode = (uint8_t)atoi(arg); + if (imirMode > 1) { + fprintf(stderr, "ERROR: Invalid imir mode: %s\n", arg); returnCode = 1; goto cleanup; } @@ -909,9 +909,9 @@ image->transformFlags |= AVIF_TRANSFORM_IROT; image->irot.angle = irotAngle; } - if (imirAxis != 0xff) { + if (imirMode != 0xff) { image->transformFlags |= AVIF_TRANSFORM_IMIR; - image->imir.axis = imirAxis; + image->imir.mode = imirMode; } avifBool usingAOM = AVIF_FALSE;
diff --git a/apps/shared/avifutil.c b/apps/shared/avifutil.c index 810387a..77355e2 100644 --- a/apps/shared/avifutil.c +++ b/apps/shared/avifutil.c
@@ -99,9 +99,7 @@ printf(" * irot (Rotation) : %u\n", avif->irot.angle); } if (avif->transformFlags & AVIF_TRANSFORM_IMIR) { - printf(" * imir (Mirror) : %u (%s)\n", - avif->imir.axis, - (avif->imir.axis == 0) ? "Vertical axis, \"left-to-right\"" : "Horizontal axis, \"top-to-bottom\""); + printf(" * imir (Mirror) : Mode %u (%s)\n", avif->imir.mode, (avif->imir.mode == 0) ? "top-to-bottom" : "left-to-right"); } } }
diff --git a/include/avif/avif.h b/include/avif/avif.h index d731a51..040a54d 100644 --- a/include/avif/avif.h +++ b/include/avif/avif.h
@@ -360,14 +360,24 @@ typedef struct avifImageMirror { - // 'imir' from ISO/IEC 23008-12:2017 6.5.12: - // "axis specifies a vertical (axis = 0) or horizontal (axis = 1) axis for the mirroring operation." + // 'imir' from ISO/IEC 23008-12:2017 6.5.12 (Draft Amendment 2): + // + // 'mode' specifies how the mirroring is performed: + // + // 0 indicates that the top and bottom parts of the image are exchanged; + // 1 specifies that the left and right parts are exchanged. + // + // NOTE In Exif, orientation tag can be used to signal mirroring operations. Exif + // orientation tag 4 corresponds to mode = 0 of ImageMirror, and Exif orientation tag 2 + // corresponds to mode = 1 accordingly. // // Legal values: [0, 1] // - // 0: Mirror about a vertical axis ("left-to-right") - // 1: Mirror about a horizontal axis ("top-to-bottom") - uint8_t axis; + // NOTE: As of HEIF Draft Amendment 2, the name of this variable has changed from 'axis' to 'mode' as + // the logic behind it has been *inverted*. Please use the wording above describing the legal + // values for 'mode' and update any code that previously may have used `axis` to use + // the *opposite* value (0 now means top-to-bottom, where it used to mean left-to-right). + uint8_t mode; } avifImageMirror; // ---------------------------------------------------------------------------
diff --git a/src/read.c b/src/read.c index a37fb80..d363f84 100644 --- a/src/read.c +++ b/src/read.c
@@ -1553,10 +1553,10 @@ BEGIN_STREAM(s, raw, rawLen, diag, "Box[imir]"); avifImageMirror * imir = &prop->u.imir; - CHECK(avifROStreamRead(&s, &imir->axis, 1)); // unsigned int (7) reserved = 0; unsigned int (1) axis; - if ((imir->axis & 0xfe) != 0) { + CHECK(avifROStreamRead(&s, &imir->mode, 1)); // unsigned int (7) reserved = 0; unsigned int (1) mode; + if ((imir->mode & 0xfe) != 0) { // reserved bits must be 0 - avifDiagnosticsPrintf(diag, "Box[imir] contains nonzero reserved bits [%u]", imir->axis); + avifDiagnosticsPrintf(diag, "Box[imir] contains nonzero reserved bits [%u]", imir->mode); return AVIF_FALSE; } return AVIF_TRUE;
diff --git a/src/write.c b/src/write.c index 1c1d779..6a2474a 100644 --- a/src/write.c +++ b/src/write.c
@@ -275,8 +275,8 @@ } if (imageMetadata->transformFlags & AVIF_TRANSFORM_IMIR) { avifBoxMarker imir = avifRWStreamWriteBox(s, "imir", AVIF_BOX_SIZE_TBD); - uint8_t axis = imageMetadata->imir.axis & 0x1; - avifRWStreamWrite(s, &axis, 1); // unsigned int (7) reserved = 0; unsigned int (1) axis; + uint8_t mode = imageMetadata->imir.mode & 0x1; + avifRWStreamWrite(s, &mode, 1); // unsigned int (7) reserved = 0; unsigned int (1) mode; avifRWStreamFinishBox(s, imir); if (ipma && itemPropertyIndex) { ipmaPush(ipma, ++(*itemPropertyIndex), AVIF_TRUE);