pillow-avif-plugin 1.4.6__cp313-cp313-macosx_10_13_x86_64.whl → 1.5.1__cp313-cp313-macosx_10_13_x86_64.whl

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.

@@ -16,11 +16,9 @@ except ImportError:
16
16
  # to Image.open (see https://github.com/python-pillow/Pillow/issues/569)
17
17
  DECODE_CODEC_CHOICE = "auto"
18
18
  CHROMA_UPSAMPLING = "auto"
19
+ # Decoding is only affected by this for libavif **0.8.4** or greater.
19
20
  DEFAULT_MAX_THREADS = 0
20
21
 
21
- _VALID_AVIF_MODES = {"RGB", "RGBA"}
22
-
23
-
24
22
  if sys.version_info[0] == 2:
25
23
  text_type = unicode # noqa
26
24
  else:
@@ -29,18 +27,12 @@ else:
29
27
 
30
28
  def _accept(prefix):
31
29
  if prefix[4:8] != b"ftyp":
32
- return
33
- coding_brands = (b"avif", b"avis")
34
- container_brands = (b"mif1", b"msf1")
30
+ return False
35
31
  major_brand = prefix[8:12]
36
- if major_brand in coding_brands:
37
- if not SUPPORTED:
38
- return (
39
- "image file could not be identified because AVIF "
40
- "support not installed"
41
- )
42
- return True
43
- if major_brand in container_brands:
32
+ if major_brand in (
33
+ # coding brands
34
+ b"avif",
35
+ b"avis",
44
36
  # We accept files with AVIF container brands; we can't yet know if
45
37
  # the ftyp box has the correct compatible brands, but if it doesn't
46
38
  # then the plugin will raise a SyntaxError which Pillow will catch
@@ -48,67 +40,108 @@ def _accept(prefix):
48
40
  #
49
41
  # Also, because this file might not actually be an AVIF file, we
50
42
  # don't raise an error if AVIF support isn't properly compiled.
43
+ b"mif1",
44
+ b"msf1",
45
+ ):
46
+ if not SUPPORTED:
47
+ return (
48
+ "image file could not be identified because AVIF support not installed"
49
+ )
51
50
  return True
51
+ return False
52
52
 
53
53
 
54
54
  class AvifImageFile(ImageFile.ImageFile):
55
55
  format = "AVIF"
56
56
  format_description = "AVIF image"
57
- __loaded = -1
58
- __frame = 0
59
-
60
- def load_seek(self, pos):
61
- pass
57
+ __frame = -1
62
58
 
63
59
  def _open(self):
60
+ if not SUPPORTED:
61
+ msg = "image file could not be opened because AVIF support not installed"
62
+ raise SyntaxError(msg)
63
+
64
+ if DECODE_CODEC_CHOICE != "auto" and not _avif.decoder_codec_available(
65
+ DECODE_CODEC_CHOICE
66
+ ):
67
+ msg = "Invalid opening codec"
68
+ raise ValueError(msg)
64
69
  self._decoder = _avif.AvifDecoder(
65
70
  self.fp.read(), DECODE_CODEC_CHOICE, CHROMA_UPSAMPLING, DEFAULT_MAX_THREADS
66
71
  )
67
72
 
68
73
  # Get info from decoder
69
- width, height, n_frames, mode, icc, exif, xmp = self._decoder.get_info()
70
- self._size = width, height
71
- self.n_frames = n_frames
74
+ (
75
+ width,
76
+ height,
77
+ self.n_frames,
78
+ mode,
79
+ icc,
80
+ exif,
81
+ exif_orientation,
82
+ xmp,
83
+ ) = self._decoder.get_info()
84
+ self._size = (width, height)
72
85
  self.is_animated = self.n_frames > 1
73
86
  try:
74
87
  self.mode = self.rawmode = mode
75
88
  except AttributeError:
76
89
  self._mode = self.rawmode = mode
77
- self.tile = []
78
90
 
79
91
  if icc:
80
92
  self.info["icc_profile"] = icc
81
- if exif:
82
- self.info["exif"] = exif
83
93
  if xmp:
84
94
  self.info["xmp"] = xmp
85
95
 
96
+ if exif_orientation != 1 or exif:
97
+ exif_data = Image.Exif()
98
+ orientation_tag = next(
99
+ k for k, v in ExifTags.TAGS.items() if v == "Orientation"
100
+ )
101
+ if exif:
102
+ exif_data.load(exif)
103
+ original_orientation = exif_data.get(orientation_tag, 1)
104
+ else:
105
+ original_orientation = 1
106
+ if exif_orientation != original_orientation:
107
+ exif_data[orientation_tag] = exif_orientation
108
+ exif = exif_data.tobytes()
109
+ if exif:
110
+ self.info["exif"] = exif
111
+ self.seek(0)
112
+
86
113
  def seek(self, frame):
87
114
  if not self._seek_check(frame):
88
115
  return
89
116
 
117
+ # Set tile
90
118
  self.__frame = frame
119
+ if hasattr(ImageFile, "_Tile"):
120
+ self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)]
121
+ else:
122
+ self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
91
123
 
92
124
  def load(self):
93
- if self.__loaded != self.__frame:
125
+ if self.tile:
94
126
  # We need to load the image data for this frame
95
- data, timescale, tsp_in_ts, dur_in_ts = self._decoder.get_frame(
96
- self.__frame
97
- )
98
- timestamp = round(1000 * (tsp_in_ts / timescale))
99
- duration = round(1000 * (dur_in_ts / timescale))
100
- self.info["timestamp"] = timestamp
101
- self.info["duration"] = duration
102
- self.__loaded = self.__frame
127
+ (
128
+ data,
129
+ timescale,
130
+ pts_in_timescales,
131
+ duration_in_timescales,
132
+ ) = self._decoder.get_frame(self.__frame)
133
+ self.info["timestamp"] = round(1000 * (pts_in_timescales / timescale))
134
+ self.info["duration"] = round(1000 * (duration_in_timescales / timescale))
103
135
 
104
- # Set tile
105
136
  if self.fp and self._exclusive_fp:
106
137
  self.fp.close()
107
138
  self.fp = BytesIO(data)
108
- self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)]
109
139
 
110
140
  return super(AvifImageFile, self).load()
111
141
 
142
+ def load_seek(self, pos):
143
+ pass
144
+
112
145
  def tell(self):
113
146
  return self.__frame
114
147
 
@@ -128,19 +161,21 @@ def _save(im, fp, filename, save_all=False):
128
161
  for ims in [im] + append_images:
129
162
  total += getattr(ims, "n_frames", 1)
130
163
 
131
- is_single_frame = total == 1
132
-
133
164
  qmin = info.get("qmin", -1)
134
165
  qmax = info.get("qmax", -1)
135
166
  quality = info.get("quality", 75)
136
167
  if not isinstance(quality, int) or quality < 0 or quality > 100:
137
- raise ValueError("Invalid quality setting")
168
+ msg = "Invalid quality setting"
169
+ raise ValueError(msg)
138
170
 
139
171
  duration = info.get("duration", 0)
140
172
  subsampling = info.get("subsampling", "4:2:0")
141
173
  speed = info.get("speed", 6)
142
174
  max_threads = info.get("max_threads", DEFAULT_MAX_THREADS)
143
175
  codec = info.get("codec", "auto")
176
+ if codec != "auto" and not _avif.encoder_codec_available(codec):
177
+ msg = "Invalid saving codec"
178
+ raise ValueError(msg)
144
179
  range_ = info.get("range", "full")
145
180
  tile_rows_log2 = info.get("tile_rows", 0)
146
181
  tile_cols_log2 = info.get("tile_cols", 0)
@@ -171,20 +206,21 @@ def _save(im, fp, filename, save_all=False):
171
206
  xmp = xmp.encode("utf-8")
172
207
 
173
208
  advanced = info.get("advanced")
174
- if isinstance(advanced, dict):
175
- advanced = tuple([k, v] for (k, v) in advanced.items())
176
209
  if advanced is not None:
210
+ if isinstance(advanced, dict):
211
+ advanced = tuple(advanced.items())
177
212
  try:
178
213
  advanced = tuple(advanced)
179
214
  except TypeError:
180
215
  invalid = True
181
216
  else:
182
- invalid = all(isinstance(v, tuple) and len(v) == 2 for v in advanced)
217
+ invalid = any(not isinstance(v, tuple) or len(v) != 2 for v in advanced)
183
218
  if invalid:
184
- raise ValueError(
219
+ msg = (
185
220
  "advanced codec options must be a dict of key-value string "
186
221
  "pairs or a series of key-value two-tuples"
187
222
  )
223
+ raise ValueError(msg)
188
224
  advanced = tuple(
189
225
  [(str(k).encode("utf-8"), str(v).encode("utf-8")) for k, v in advanced]
190
226
  )
@@ -214,8 +250,9 @@ def _save(im, fp, filename, save_all=False):
214
250
 
215
251
  # Add each frame
216
252
  frame_idx = 0
217
- frame_dur = 0
253
+ frame_duration = 0
218
254
  cur_idx = im.tell()
255
+ is_single_frame = total == 1
219
256
  try:
220
257
  for ims in [im] + append_images:
221
258
  # Get # of frames in this image
@@ -228,7 +265,7 @@ def _save(im, fp, filename, save_all=False):
228
265
  # Make sure image mode is supported
229
266
  frame = ims
230
267
  rawmode = ims.mode
231
- if ims.mode not in _VALID_AVIF_MODES:
268
+ if ims.mode not in {"RGB", "RGBA"}:
232
269
  alpha = (
233
270
  "A" in ims.mode
234
271
  or "a" in ims.mode
@@ -243,14 +280,14 @@ def _save(im, fp, filename, save_all=False):
243
280
 
244
281
  # Update frame duration
245
282
  if isinstance(duration, (list, tuple)):
246
- frame_dur = duration[frame_idx]
283
+ frame_duration = duration[frame_idx]
247
284
  else:
248
- frame_dur = duration
285
+ frame_duration = duration
249
286
 
250
287
  # Append the frame to the animation encoder
251
288
  enc.add(
252
289
  frame.tobytes("raw", rawmode),
253
- frame_dur,
290
+ int(frame_duration),
254
291
  frame.size[0],
255
292
  frame.size[1],
256
293
  rawmode,
@@ -269,7 +306,8 @@ def _save(im, fp, filename, save_all=False):
269
306
  # Get the final output from the encoder
270
307
  data = enc.finish()
271
308
  if data is None:
272
- raise OSError("cannot write file as AVIF (encoder returned None)")
309
+ msg = "cannot write file as AVIF (encoder returned None)"
310
+ raise OSError(msg)
273
311
 
274
312
  fp.write(data)
275
313
 
pillow_avif/__init__.py CHANGED
@@ -2,4 +2,4 @@ from . import AvifImagePlugin
2
2
 
3
3
 
4
4
  __all__ = ["AvifImagePlugin"]
5
- __version__ = "1.4.6"
5
+ __version__ = "1.5.1"
Binary file
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pillow-avif-plugin
3
- Version: 1.4.6
3
+ Version: 1.5.1
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
@@ -29,11 +29,22 @@ Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
31
  Provides-Extra: tests
32
- Requires-Dist: pytest ; extra == 'tests'
33
- Requires-Dist: packaging ; extra == 'tests'
34
- Requires-Dist: pytest-cov ; extra == 'tests'
35
- Requires-Dist: test-image-results ; extra == 'tests'
36
- Requires-Dist: pillow ; extra == 'tests'
32
+ Requires-Dist: pytest; extra == "tests"
33
+ Requires-Dist: packaging; extra == "tests"
34
+ Requires-Dist: pytest-cov; extra == "tests"
35
+ Requires-Dist: test-image-results; extra == "tests"
36
+ Requires-Dist: pillow; extra == "tests"
37
+ Dynamic: author
38
+ Dynamic: author-email
39
+ Dynamic: classifier
40
+ Dynamic: description
41
+ Dynamic: description-content-type
42
+ Dynamic: download-url
43
+ Dynamic: home-page
44
+ Dynamic: license
45
+ Dynamic: license-file
46
+ Dynamic: provides-extra
47
+ Dynamic: summary
37
48
 
38
49
  # pillow-avif-plugin
39
50
 
@@ -0,0 +1,9 @@
1
+ pillow_avif_plugin-1.5.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
+ pillow_avif_plugin-1.5.1.dist-info/RECORD,,
3
+ pillow_avif_plugin-1.5.1.dist-info/WHEEL,sha256=IQOVuHkWhwZk8TanuqgaQRs7S_BzU-y5yZKJZm2wG5k,138
4
+ pillow_avif_plugin-1.5.1.dist-info/top_level.txt,sha256=xrg4zRnqDyl_JEyEQh3oCcAU4BHneocD-DCFDzf4g9E,12
5
+ pillow_avif_plugin-1.5.1.dist-info/METADATA,sha256=KhLcAf5Uq7bjWY5w8N-HPCJK6i7pPzMkK4EvQ8hTAxs,2143
6
+ pillow_avif_plugin-1.5.1.dist-info/licenses/LICENSE,sha256=2l49eVdgaNy9JTEPbstz1GaaPKw02gFdtuThTNUi730,25213
7
+ pillow_avif/AvifImagePlugin.py,sha256=B0w7AVoU3R-O9tTIOSqhjJdNAAEMYzMFhUcKf8Lczm4,9756
8
+ pillow_avif/__init__.py,sha256=ha9bUuJLoEM5BeveIZE1JwPGl8W2AUYVfXRwgEVCiI8,84
9
+ pillow_avif/_avif.cpython-313-darwin.so,sha256=CYEN122vlnwgGlmeE5ip3H7cz5K6QKb-6U3Zfpw0Nfg,20545904
@@ -1,5 +1,6 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (77.0.3)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp313-cp313-macosx_10_13_x86_64
5
+ Generator: delocate 0.13.0
5
6
 
@@ -1,9 +0,0 @@
1
- pillow_avif_plugin-1.4.6.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
- pillow_avif_plugin-1.4.6.dist-info/RECORD,,
3
- pillow_avif_plugin-1.4.6.dist-info/LICENSE,sha256=2l49eVdgaNy9JTEPbstz1GaaPKw02gFdtuThTNUi730,25213
4
- pillow_avif_plugin-1.4.6.dist-info/WHEEL,sha256=fPJFWGX4x9d-BCTIAKQ5r5jAILVgTlTgwZWGIUlxz04,111
5
- pillow_avif_plugin-1.4.6.dist-info/top_level.txt,sha256=xrg4zRnqDyl_JEyEQh3oCcAU4BHneocD-DCFDzf4g9E,12
6
- pillow_avif_plugin-1.4.6.dist-info/METADATA,sha256=arZMb94_qIVuxjND8P2bT5YgPLH3Fk5hlBl4IJlWWms,1914
7
- pillow_avif/AvifImagePlugin.py,sha256=MfIVDuN7jVDceVl68b9R5lTHxzrHG6wpt6Ca-NbFdAw,8486
8
- pillow_avif/__init__.py,sha256=qh2QBNJ1nd7wWVTqzyqnMpC5fVuC1799iGLWYCKq36g,84
9
- pillow_avif/_avif.cpython-313-darwin.so,sha256=H26dimMWnz-5NoCy0mhf0qtqYaiC76eSPRjnthTBNmU,24564784