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.
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/PKG-INFO +1 -1
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/src/pillow_avif/AvifImagePlugin.py +24 -18
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/src/pillow_avif/__init__.py +1 -1
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/src/pillow_avif/_avif.c +134 -4
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/src/pillow_avif_plugin.egg-info/PKG-INFO +1 -1
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/src/pillow_avif_plugin.egg-info/SOURCES.txt +2 -2
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/LICENSE +0 -0
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/MANIFEST.in +0 -0
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/README.md +0 -0
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/pyproject.toml +0 -0
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/setup.cfg +0 -0
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/setup.py +0 -0
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/src/pillow_avif_plugin.egg-info/dependency_links.txt +0 -0
- /pillow-avif-plugin-1.4.0/src/pillow_avif_plugin.egg-info/zip-safe → /pillow-avif-plugin-1.4.2/src/pillow_avif_plugin.egg-info/not-zip-safe +0 -0
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/src/pillow_avif_plugin.egg-info/top_level.txt +0 -0
- {pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/tox.ini +0 -0
|
@@ -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
|
|
133
|
-
|
|
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
|
)
|
|
@@ -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
|
-
"
|
|
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
|
-
|
|
261
|
-
|
|
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
|
-
|
|
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;
|
{pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/src/pillow_avif_plugin.egg-info/SOURCES.txt
RENAMED
|
@@ -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/
|
|
15
|
-
src/pillow_avif_plugin.egg-info/
|
|
14
|
+
src/pillow_avif_plugin.egg-info/not-zip-safe
|
|
15
|
+
src/pillow_avif_plugin.egg-info/top_level.txt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pillow-avif-plugin-1.4.0 → pillow-avif-plugin-1.4.2}/src/pillow_avif_plugin.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|