pillow-avif-plugin 1.4.0__tar.gz → 1.4.2__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.

Potentially problematic release.


This version of pillow-avif-plugin might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pillow-avif-plugin
3
- Version: 1.4.0
3
+ Version: 1.4.2
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
@@ -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
@@ -126,23 +129,11 @@ def _save(im, fp, filename, save_all=False):
126
129
 
127
130
  is_single_frame = total == 1
128
131
 
129
- qmin = info.get("qmin")
130
- qmax = info.get("qmax")
131
-
132
- if qmin is None and qmax is None:
133
- # The min and max quantizer settings in libavif range from 0 (best quality)
134
- # to 63 (worst quality). If neither are explicitly specified, we use a 0-100
135
- # quality scale (default 75) and calculate the qmin and qmax from that.
136
- #
137
- # - qmin is 0 for quality >= 64. Below that, qmin has an inverse linear
138
- # relation to quality (i.e., quality 63 = qmin 1, quality 0 => qmin 63)
139
- # - qmax is 0 for quality=100, then qmax increases linearly relative to
140
- # quality decreasing, until it flattens out at quality=37.
141
- quality = info.get("quality", 75)
142
- if not isinstance(quality, int) or quality < 0 or quality > 100:
143
- raise ValueError("Invalid quality setting")
144
- qmin = max(0, min(64 - quality, 63))
145
- qmax = max(0, min(100 - quality, 63))
132
+ qmin = info.get("qmin", -1)
133
+ qmax = info.get("qmax", -1)
134
+ quality = info.get("quality", 75)
135
+ if not isinstance(quality, int) or quality < 0 or quality > 100:
136
+ raise ValueError("Invalid quality setting")
146
137
 
147
138
  duration = info.get("duration", 0)
148
139
  subsampling = info.get("subsampling", "4:2:0")
@@ -158,6 +149,20 @@ def _save(im, fp, filename, save_all=False):
158
149
  exif = info.get("exif", im.info.get("exif"))
159
150
  if isinstance(exif, Image.Exif):
160
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
+
161
166
  xmp = info.get("xmp", im.info.get("xmp") or im.info.get("XML:com.adobe.xmp"))
162
167
 
163
168
  if isinstance(xmp, text_type):
@@ -199,6 +204,7 @@ def _save(im, fp, filename, save_all=False):
199
204
  autotiling,
200
205
  icc_profile or b"",
201
206
  exif or b"",
207
+ exif_orientation,
202
208
  xmp or b"",
203
209
  advanced,
204
210
  )
@@ -2,4 +2,4 @@ from . import AvifImagePlugin
2
2
 
3
3
 
4
4
  __all__ = ["AvifImagePlugin"]
5
- __version__ = "1.4.0"
5
+ __version__ = "1.4.2"
@@ -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;
@@ -257,8 +371,18 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
257
371
  return NULL;
258
372
  }
259
373
 
260
- enc_options.qmin = normalize_quantize_value(qmin);
261
- enc_options.qmax = normalize_quantize_value(qmax);
374
+ if (qmin == -1 || qmax == -1) {
375
+ #if AVIF_VERSION >= 1000000
376
+ enc_options.qmin = -1;
377
+ enc_options.qmax = -1;
378
+ #else
379
+ enc_options.qmin = normalize_quantize_value(64 - quality);
380
+ enc_options.qmax = normalize_quantize_value(100 - quality);
381
+ #endif
382
+ } else {
383
+ enc_options.qmin = normalize_quantize_value(qmin);
384
+ enc_options.qmax = normalize_quantize_value(qmax);
385
+ }
262
386
  enc_options.quality = quality;
263
387
 
264
388
  if (speed < AVIF_SPEED_SLOWEST) {
@@ -326,7 +450,12 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
326
450
 
327
451
  encoder->maxThreads = max_threads;
328
452
  #if AVIF_VERSION >= 1000000
329
- encoder->quality = enc_options.quality;
453
+ if (enc_options.qmin != -1 && enc_options.qmax != -1) {
454
+ encoder->minQuantizer = enc_options.qmin;
455
+ encoder->maxQuantizer = enc_options.qmax;
456
+ } else {
457
+ encoder->quality = enc_options.quality;
458
+ }
330
459
  #else
331
460
  encoder->minQuantizer = enc_options.qmin;
332
461
  encoder->maxQuantizer = enc_options.qmax;
@@ -389,6 +518,7 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
389
518
  (uint8_t *)PyBytes_AS_STRING(xmp_bytes),
390
519
  PyBytes_GET_SIZE(xmp_bytes));
391
520
  }
521
+ exif_orientation_to_irot_imir(image, exif_orientation);
392
522
 
393
523
  self->image = image;
394
524
  self->frame_index = -1;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pillow-avif-plugin
3
- Version: 1.4.0
3
+ Version: 1.4.2
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