@@ -139,6 +139,118 @@ exc_type_for_avif_result(avifResult result) {
139
139
}
140
140
}
141
141
142
+ static void
143
+ exif_orientation_to_irot_imir (avifImage * image , int orientation ) {
144
+ const avifTransformFlags otherFlags =
145
+ image -> transformFlags & ~(AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR );
146
+
147
+ //
148
+ // Mapping from Exif orientation as defined in JEITA CP-3451C section 4.6.4.A
149
+ // Orientation to irot and imir boxes as defined in HEIF ISO/IEC 28002-12:2021
150
+ // sections 6.5.10 and 6.5.12.
151
+ switch (orientation ) {
152
+ case 1 : // The 0th row is at the visual top of the image, and the 0th column is
153
+ // the visual left-hand side.
154
+ image -> transformFlags = otherFlags ;
155
+ image -> irot .angle = 0 ; // ignored
156
+ #if AVIF_VERSION_MAJOR >= 1
157
+ image -> imir .axis = 0 ; // ignored
158
+ #else
159
+ image -> imir .mode = 0 ; // ignored
160
+ #endif
161
+ return ;
162
+ case 2 : // The 0th row is at the visual top of the image, and the 0th column is
163
+ // the visual right-hand side.
164
+ image -> transformFlags = otherFlags | AVIF_TRANSFORM_IMIR ;
165
+ image -> irot .angle = 0 ; // ignored
166
+ #if AVIF_VERSION_MAJOR >= 1
167
+ image -> imir .axis = 1 ;
168
+ #else
169
+ image -> imir .mode = 1 ;
170
+ #endif
171
+ return ;
172
+ case 3 : // The 0th row is at the visual bottom of the image, and the 0th column
173
+ // is the visual right-hand side.
174
+ image -> transformFlags = otherFlags | AVIF_TRANSFORM_IROT ;
175
+ image -> irot .angle = 2 ;
176
+ #if AVIF_VERSION_MAJOR >= 1
177
+ image -> imir .axis = 0 ; // ignored
178
+ #else
179
+ image -> imir .mode = 0 ; // ignored
180
+ #endif
181
+ return ;
182
+ case 4 : // The 0th row is at the visual bottom of the image, and the 0th column
183
+ // is the visual left-hand side.
184
+ image -> transformFlags = otherFlags | AVIF_TRANSFORM_IMIR ;
185
+ image -> irot .angle = 0 ; // ignored
186
+ #if AVIF_VERSION_MAJOR >= 1
187
+ image -> imir .axis = 0 ;
188
+ #else
189
+ image -> imir .mode = 0 ;
190
+ #endif
191
+ return ;
192
+ case 5 : // The 0th row is the visual left-hand side of the image, and the 0th
193
+ // column is the visual top.
194
+ image -> transformFlags =
195
+ otherFlags | AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR ;
196
+ image -> irot .angle = 1 ; // applied before imir according to MIAF spec
197
+ // ISO/IEC 28002-12:2021 - section 7.3.6.7
198
+ #if AVIF_VERSION_MAJOR >= 1
199
+ image -> imir .axis = 0 ;
200
+ #else
201
+ image -> imir .mode = 0 ;
202
+ #endif
203
+ return ;
204
+ case 6 : // The 0th row is the visual right-hand side of the image, and the 0th
205
+ // column is the visual top.
206
+ image -> transformFlags = otherFlags | AVIF_TRANSFORM_IROT ;
207
+ image -> irot .angle = 3 ;
208
+ #if AVIF_VERSION_MAJOR >= 1
209
+ image -> imir .axis = 0 ; // ignored
210
+ #else
211
+ image -> imir .mode = 0 ; // ignored
212
+ #endif
213
+ return ;
214
+ case 7 : // The 0th row is the visual right-hand side of the image, and the 0th
215
+ // column is the visual bottom.
216
+ image -> transformFlags =
217
+ otherFlags | AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR ;
218
+ image -> irot .angle = 3 ; // applied before imir according to MIAF spec
219
+ // ISO/IEC 28002-12:2021 - section 7.3.6.7
220
+ #if AVIF_VERSION_MAJOR >= 1
221
+ image -> imir .axis = 0 ;
222
+ #else
223
+ image -> imir .mode = 0 ;
224
+ #endif
225
+ return ;
226
+ case 8 : // The 0th row is the visual left-hand side of the image, and the 0th
227
+ // column is the visual bottom.
228
+ image -> transformFlags = otherFlags | AVIF_TRANSFORM_IROT ;
229
+ image -> irot .angle = 1 ;
230
+ #if AVIF_VERSION_MAJOR >= 1
231
+ image -> imir .axis = 0 ; // ignored
232
+ #else
233
+ image -> imir .mode = 0 ; // ignored
234
+ #endif
235
+ return ;
236
+ default : // reserved
237
+ break ;
238
+ }
239
+
240
+ // The orientation tag is not mandatory (only recommended) according to JEITA
241
+ // CP-3451C section 4.6.8.A. The default value is 1 if the orientation tag is
242
+ // missing, meaning:
243
+ // The 0th row is at the visual top of the image, and the 0th column is the visual
244
+ // left-hand side.
245
+ image -> transformFlags = otherFlags ;
246
+ image -> irot .angle = 0 ; // ignored
247
+ #if AVIF_VERSION_MAJOR >= 1
248
+ image -> imir .axis = 0 ; // ignored
249
+ #else
250
+ image -> imir .mode = 0 ; // ignored
251
+ #endif
252
+ }
253
+
142
254
static int
143
255
_codec_available (const char * name , uint32_t flags ) {
144
256
avifCodecChoice codec = avifCodecChoiceFromName (name );
@@ -208,6 +320,7 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
208
320
int qmax = 10 ; // "High Quality", but not lossless
209
321
int quality = 75 ;
210
322
int speed = 8 ;
323
+ int exif_orientation = 0 ;
211
324
PyObject * icc_bytes ;
212
325
PyObject * exif_bytes ;
213
326
PyObject * xmp_bytes ;
@@ -223,7 +336,7 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
223
336
224
337
if (!PyArg_ParseTuple (
225
338
args ,
226
- "IIsiiiissiiOOSSSO " ,
339
+ "IIsiiiissiiOOSSiSO " ,
227
340
& width ,
228
341
& height ,
229
342
& subsampling ,
@@ -239,6 +352,7 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
239
352
& autotiling ,
240
353
& icc_bytes ,
241
354
& exif_bytes ,
355
+ & exif_orientation ,
242
356
& xmp_bytes ,
243
357
& advanced )) {
244
358
return NULL ;
@@ -404,6 +518,7 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
404
518
(uint8_t * )PyBytes_AS_STRING (xmp_bytes ),
405
519
PyBytes_GET_SIZE (xmp_bytes ));
406
520
}
521
+ exif_orientation_to_irot_imir (image , exif_orientation );
407
522
408
523
self -> image = image ;
409
524
self -> frame_index = -1 ;
0 commit comments