pillow-avif-plugin 1.4.1__tar.gz → 1.4.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pillow-avif-plugin
3
- Version: 1.4.1
3
+ Version: 1.4.3
4
4
  Summary: A pillow plugin that adds avif support via libavif
5
5
  Home-page: https://github.com/fdintino/pillow-avif-plugin/
6
6
  Author: Frankie Dintino
@@ -28,6 +28,19 @@ def readme():
28
28
  IS_DEBUG = hasattr(sys, "gettotalrefcount")
29
29
  PLATFORM_MINGW = os.name == "nt" and "GCC" in sys.version
30
30
 
31
+ libraries = ["avif"]
32
+ if sys.platform == "win32":
33
+ libraries.extend(
34
+ [
35
+ "advapi32",
36
+ "bcrypt",
37
+ "ntdll",
38
+ "userenv",
39
+ "ws2_32",
40
+ "kernel32",
41
+ ]
42
+ )
43
+
31
44
  setup(
32
45
  name="pillow-avif-plugin",
33
46
  description="A pillow plugin that adds avif support via libavif",
@@ -39,7 +52,7 @@ setup(
39
52
  "pillow_avif._avif",
40
53
  ["src/pillow_avif/_avif.c"],
41
54
  depends=["avif/avif.h"],
42
- libraries=["avif"],
55
+ libraries=libraries,
43
56
  ),
44
57
  ],
45
58
  package_data={"": ["README.rst"]},
@@ -3,7 +3,7 @@ from __future__ import division
3
3
  from io import BytesIO
4
4
  import sys
5
5
 
6
- from PIL import Image, ImageFile
6
+ from PIL import ExifTags, Image, ImageFile
7
7
 
8
8
  try:
9
9
  from pillow_avif import _avif
@@ -56,6 +56,9 @@ class AvifImageFile(ImageFile.ImageFile):
56
56
  __loaded = -1
57
57
  __frame = 0
58
58
 
59
+ def load_seek(self, pos):
60
+ pass
61
+
59
62
  def _open(self):
60
63
  self._decoder = _avif.AvifDecoder(
61
64
  self.fp.read(), DECODE_CODEC_CHOICE, CHROMA_UPSAMPLING
@@ -146,6 +149,20 @@ def _save(im, fp, filename, save_all=False):
146
149
  exif = info.get("exif", im.info.get("exif"))
147
150
  if isinstance(exif, Image.Exif):
148
151
  exif = exif.tobytes()
152
+
153
+ exif_orientation = 0
154
+ if exif:
155
+ exif_data = Image.Exif()
156
+ try:
157
+ exif_data.load(exif)
158
+ except SyntaxError:
159
+ pass
160
+ else:
161
+ orientation_tag = next(
162
+ k for k, v in ExifTags.TAGS.items() if v == "Orientation"
163
+ )
164
+ exif_orientation = exif_data.get(orientation_tag) or 0
165
+
149
166
  xmp = info.get("xmp", im.info.get("xmp") or im.info.get("XML:com.adobe.xmp"))
150
167
 
151
168
  if isinstance(xmp, text_type):
@@ -187,6 +204,7 @@ def _save(im, fp, filename, save_all=False):
187
204
  autotiling,
188
205
  icc_profile or b"",
189
206
  exif or b"",
207
+ exif_orientation,
190
208
  xmp or b"",
191
209
  advanced,
192
210
  )
@@ -2,4 +2,4 @@ from . import AvifImagePlugin
2
2
 
3
3
 
4
4
  __all__ = ["AvifImagePlugin"]
5
- __version__ = "1.4.1"
5
+ __version__ = "1.4.3"
@@ -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;
@@ -334,7 +448,11 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
334
448
  init_max_threads();
335
449
  }
336
450
 
337
- encoder->maxThreads = max_threads;
451
+ int is_aom_encode = strcmp(codec, "aom") == 0 ||
452
+ (strcmp(codec, "auto") == 0 &&
453
+ _codec_available("aom", AVIF_CODEC_FLAG_CAN_ENCODE));
454
+
455
+ encoder->maxThreads = is_aom_encode && max_threads > 64 ? 64 : max_threads;
338
456
  #if AVIF_VERSION >= 1000000
339
457
  if (enc_options.qmin != -1 && enc_options.qmax != -1) {
340
458
  encoder->minQuantizer = enc_options.qmin;
@@ -404,6 +522,7 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
404
522
  (uint8_t *)PyBytes_AS_STRING(xmp_bytes),
405
523
  PyBytes_GET_SIZE(xmp_bytes));
406
524
  }
525
+ exif_orientation_to_irot_imir(image, exif_orientation);
407
526
 
408
527
  self->image = image;
409
528
  self->frame_index = -1;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pillow-avif-plugin
3
- Version: 1.4.1
3
+ Version: 1.4.3
4
4
  Summary: A pillow plugin that adds avif support via libavif
5
5
  Home-page: https://github.com/fdintino/pillow-avif-plugin/
6
6
  Author: Frankie Dintino
@@ -11,5 +11,5 @@ src/pillow_avif/_avif.c
11
11
  src/pillow_avif_plugin.egg-info/PKG-INFO
12
12
  src/pillow_avif_plugin.egg-info/SOURCES.txt
13
13
  src/pillow_avif_plugin.egg-info/dependency_links.txt
14
- src/pillow_avif_plugin.egg-info/top_level.txt
15
- src/pillow_avif_plugin.egg-info/zip-safe
14
+ src/pillow_avif_plugin.egg-info/not-zip-safe
15
+ src/pillow_avif_plugin.egg-info/top_level.txt