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

Files changed (18) hide show
  1. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/PKG-INFO +1 -1
  2. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/src/pillow_avif/__init__.py +1 -1
  3. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/src/pillow_avif_plugin.egg-info/PKG-INFO +1 -1
  4. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/src/pillow_avif_plugin.egg-info/SOURCES.txt +0 -1
  5. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/tests/test_file_avif.py +12 -0
  6. pillow-avif-plugin-1.4.4/src/pillow_avif/AvifImagePlugin.py.orig +0 -282
  7. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/LICENSE +0 -0
  8. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/MANIFEST.in +0 -0
  9. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/README.md +0 -0
  10. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/pyproject.toml +0 -0
  11. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/setup.cfg +0 -0
  12. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/setup.py +0 -0
  13. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/src/pillow_avif/AvifImagePlugin.py +0 -0
  14. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/src/pillow_avif/_avif.c +0 -0
  15. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/src/pillow_avif_plugin.egg-info/dependency_links.txt +0 -0
  16. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/src/pillow_avif_plugin.egg-info/top_level.txt +0 -0
  17. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/src/pillow_avif_plugin.egg-info/zip-safe +0 -0
  18. {pillow-avif-plugin-1.4.4 → pillow-avif-plugin-1.4.6}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pillow-avif-plugin
3
- Version: 1.4.4
3
+ Version: 1.4.6
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
  Download-URL: https://github.com/fdintino/pillow-avif-plugin/releases
@@ -2,4 +2,4 @@ from . import AvifImagePlugin
2
2
 
3
3
 
4
4
  __all__ = ["AvifImagePlugin"]
5
- __version__ = "1.4.4"
5
+ __version__ = "1.4.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pillow-avif-plugin
3
- Version: 1.4.4
3
+ Version: 1.4.6
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
  Download-URL: https://github.com/fdintino/pillow-avif-plugin/releases
@@ -6,7 +6,6 @@ setup.cfg
6
6
  setup.py
7
7
  tox.ini
8
8
  src/pillow_avif/AvifImagePlugin.py
9
- src/pillow_avif/AvifImagePlugin.py.orig
10
9
  src/pillow_avif/__init__.py
11
10
  src/pillow_avif/_avif.c
12
11
  src/pillow_avif_plugin.egg-info/PKG-INFO
@@ -584,6 +584,18 @@ class TestFileAvif:
584
584
  with Image.open("%s/tests/images/chimera-missing-pixi.avif" % CURR_DIR) as im:
585
585
  assert im.size == (480, 270)
586
586
 
587
+ @skip_unless_avif_encoder("aom")
588
+ def test_aom_optimizations(self):
589
+ im = hopper("RGB")
590
+ buf = BytesIO()
591
+ im.save(buf, format="AVIF", codec="aom", speed=1)
592
+
593
+ @skip_unless_avif_encoder("svt")
594
+ def test_svt_optimizations(self):
595
+ im = hopper("RGB")
596
+ buf = BytesIO()
597
+ im.save(buf, format="AVIF", codec="svt", speed=1)
598
+
587
599
 
588
600
  class TestAvifAnimation:
589
601
  @contextmanager
@@ -1,282 +0,0 @@
1
- from __future__ import division
2
-
3
- from io import BytesIO
4
- import sys
5
-
6
- from PIL import ExifTags, Image, ImageFile
7
-
8
- try:
9
- from pillow_avif import _avif
10
-
11
- SUPPORTED = True
12
- except ImportError:
13
- SUPPORTED = False
14
-
15
- # Decoder options as module globals, until there is a way to pass parameters
16
- # to Image.open (see https://github.com/python-pillow/Pillow/issues/569)
17
- DECODE_CODEC_CHOICE = "auto"
18
- CHROMA_UPSAMPLING = "auto"
19
- <<<<<<< HEAD
20
- DEFAULT_MAX_THREADS = 0
21
- =======
22
- DECODE_MAX_THREADS = 0
23
- >>>>>>> 1e2fa65 (Let users pass max_threads manually as an argument)
24
-
25
- _VALID_AVIF_MODES = {"RGB", "RGBA"}
26
-
27
-
28
- if sys.version_info[0] == 2:
29
- text_type = unicode # noqa
30
- else:
31
- text_type = str
32
-
33
-
34
- def _accept(prefix):
35
- if prefix[4:8] != b"ftyp":
36
- return
37
- coding_brands = (b"avif", b"avis")
38
- container_brands = (b"mif1", b"msf1")
39
- major_brand = prefix[8:12]
40
- if major_brand in coding_brands:
41
- if not SUPPORTED:
42
- return (
43
- "image file could not be identified because AVIF "
44
- "support not installed"
45
- )
46
- return True
47
- if major_brand in container_brands:
48
- # We accept files with AVIF container brands; we can't yet know if
49
- # the ftyp box has the correct compatible brands, but if it doesn't
50
- # then the plugin will raise a SyntaxError which Pillow will catch
51
- # before moving on to the next plugin that accepts the file.
52
- #
53
- # Also, because this file might not actually be an AVIF file, we
54
- # don't raise an error if AVIF support isn't properly compiled.
55
- return True
56
-
57
-
58
- class AvifImageFile(ImageFile.ImageFile):
59
- format = "AVIF"
60
- format_description = "AVIF image"
61
- __loaded = -1
62
- __frame = 0
63
-
64
- def load_seek(self, pos):
65
- pass
66
-
67
- def _open(self):
68
- self._decoder = _avif.AvifDecoder(
69
- self.fp.read(), DECODE_CODEC_CHOICE, CHROMA_UPSAMPLING, DECODE_MAX_THREADS
70
- )
71
-
72
- # Get info from decoder
73
- width, height, n_frames, mode, icc, exif, xmp = self._decoder.get_info()
74
- self._size = width, height
75
- self.n_frames = n_frames
76
- self.is_animated = self.n_frames > 1
77
- try:
78
- self.mode = self.rawmode = mode
79
- except AttributeError:
80
- self._mode = self.rawmode = mode
81
- self.tile = []
82
-
83
- if icc:
84
- self.info["icc_profile"] = icc
85
- if exif:
86
- self.info["exif"] = exif
87
- if xmp:
88
- self.info["xmp"] = xmp
89
-
90
- def seek(self, frame):
91
- if not self._seek_check(frame):
92
- return
93
-
94
- self.__frame = frame
95
-
96
- def load(self):
97
- if self.__loaded != self.__frame:
98
- # We need to load the image data for this frame
99
- data, timescale, tsp_in_ts, dur_in_ts = self._decoder.get_frame(
100
- self.__frame
101
- )
102
- timestamp = round(1000 * (tsp_in_ts / timescale))
103
- duration = round(1000 * (dur_in_ts / timescale))
104
- self.info["timestamp"] = timestamp
105
- self.info["duration"] = duration
106
- self.__loaded = self.__frame
107
-
108
- # Set tile
109
- if self.fp and self._exclusive_fp:
110
- self.fp.close()
111
- self.fp = BytesIO(data)
112
- self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)]
113
-
114
- return super(AvifImageFile, self).load()
115
-
116
- def tell(self):
117
- return self.__frame
118
-
119
-
120
- def _save_all(im, fp, filename):
121
- _save(im, fp, filename, save_all=True)
122
-
123
-
124
- def _save(im, fp, filename, save_all=False):
125
- info = im.encoderinfo.copy()
126
- if save_all:
127
- append_images = list(info.get("append_images", []))
128
- else:
129
- append_images = []
130
-
131
- total = 0
132
- for ims in [im] + append_images:
133
- total += getattr(ims, "n_frames", 1)
134
-
135
- is_single_frame = total == 1
136
-
137
- qmin = info.get("qmin", -1)
138
- qmax = info.get("qmax", -1)
139
- quality = info.get("quality", 75)
140
- if not isinstance(quality, int) or quality < 0 or quality > 100:
141
- raise ValueError("Invalid quality setting")
142
-
143
- duration = info.get("duration", 0)
144
- subsampling = info.get("subsampling", "4:2:0")
145
- speed = info.get("speed", 6)
146
- max_threads = info.get("max_threads", DEFAULT_MAX_THREADS)
147
- codec = info.get("codec", "auto")
148
- range_ = info.get("range", "full")
149
- tile_rows_log2 = info.get("tile_rows", 0)
150
- tile_cols_log2 = info.get("tile_cols", 0)
151
- alpha_premultiplied = bool(info.get("alpha_premultiplied", False))
152
- autotiling = bool(info.get("autotiling", tile_rows_log2 == tile_cols_log2 == 0))
153
-
154
- icc_profile = info.get("icc_profile", im.info.get("icc_profile"))
155
- exif = info.get("exif", im.info.get("exif"))
156
- if isinstance(exif, Image.Exif):
157
- exif = exif.tobytes()
158
-
159
- exif_orientation = 0
160
- if exif:
161
- exif_data = Image.Exif()
162
- try:
163
- exif_data.load(exif)
164
- except SyntaxError:
165
- pass
166
- else:
167
- orientation_tag = next(
168
- k for k, v in ExifTags.TAGS.items() if v == "Orientation"
169
- )
170
- exif_orientation = exif_data.get(orientation_tag) or 0
171
-
172
- xmp = info.get("xmp", im.info.get("xmp") or im.info.get("XML:com.adobe.xmp"))
173
-
174
- if isinstance(xmp, text_type):
175
- xmp = xmp.encode("utf-8")
176
-
177
- advanced = info.get("advanced")
178
- if isinstance(advanced, dict):
179
- advanced = tuple([k, v] for (k, v) in advanced.items())
180
- if advanced is not None:
181
- try:
182
- advanced = tuple(advanced)
183
- except TypeError:
184
- invalid = True
185
- else:
186
- invalid = all(isinstance(v, tuple) and len(v) == 2 for v in advanced)
187
- if invalid:
188
- raise ValueError(
189
- "advanced codec options must be a dict of key-value string "
190
- "pairs or a series of key-value two-tuples"
191
- )
192
- advanced = tuple(
193
- [(str(k).encode("utf-8"), str(v).encode("utf-8")) for k, v in advanced]
194
- )
195
-
196
- # Setup the AVIF encoder
197
- enc = _avif.AvifEncoder(
198
- im.size[0],
199
- im.size[1],
200
- subsampling,
201
- qmin,
202
- qmax,
203
- quality,
204
- speed,
205
- max_threads,
206
- codec,
207
- range_,
208
- tile_rows_log2,
209
- tile_cols_log2,
210
- alpha_premultiplied,
211
- autotiling,
212
- icc_profile or b"",
213
- exif or b"",
214
- exif_orientation,
215
- xmp or b"",
216
- advanced,
217
- )
218
-
219
- # Add each frame
220
- frame_idx = 0
221
- frame_dur = 0
222
- cur_idx = im.tell()
223
- try:
224
- for ims in [im] + append_images:
225
- # Get # of frames in this image
226
- nfr = getattr(ims, "n_frames", 1)
227
-
228
- for idx in range(nfr):
229
- ims.seek(idx)
230
- ims.load()
231
-
232
- # Make sure image mode is supported
233
- frame = ims
234
- rawmode = ims.mode
235
- if ims.mode not in _VALID_AVIF_MODES:
236
- alpha = (
237
- "A" in ims.mode
238
- or "a" in ims.mode
239
- or (ims.mode == "P" and "A" in ims.im.getpalettemode())
240
- )
241
- rawmode = "RGBA" if alpha else "RGB"
242
- frame = ims.convert(rawmode)
243
-
244
- # Update frame duration
245
- if isinstance(duration, (list, tuple)):
246
- frame_dur = duration[frame_idx]
247
- else:
248
- frame_dur = duration
249
-
250
- # Append the frame to the animation encoder
251
- enc.add(
252
- frame.tobytes("raw", rawmode),
253
- frame_dur,
254
- frame.size[0],
255
- frame.size[1],
256
- rawmode,
257
- is_single_frame,
258
- )
259
-
260
- # Update frame index
261
- frame_idx += 1
262
-
263
- if not save_all:
264
- break
265
-
266
- finally:
267
- im.seek(cur_idx)
268
-
269
- # Get the final output from the encoder
270
- data = enc.finish()
271
- if data is None:
272
- raise OSError("cannot write file as AVIF (encoder returned None)")
273
-
274
- fp.write(data)
275
-
276
-
277
- Image.register_open(AvifImageFile.format, AvifImageFile, _accept)
278
- if SUPPORTED:
279
- Image.register_save(AvifImageFile.format, _save)
280
- Image.register_save_all(AvifImageFile.format, _save_all)
281
- Image.register_extensions(AvifImageFile.format, [".avif", ".avifs"])
282
- Image.register_mime(AvifImageFile.format, "image/avif")