f8"): ("F", "F;64BF", []),
+ ((1, 1, 2), "|u1"): ("LA", "LA", ["La", "PA"]),
+ ((1, 1, 3), "|u1"): ("RGB", "RGB", ["YCbCr", "LAB", "HSV"]),
+ ((1, 1, 4), "|u1"): ("RGBA", "RGBA", ["RGBa", "RGBX", "CMYK"]),
# shortcuts:
- ((1, 1), f"{_ENDIAN}i4"): ("I", "I"),
- ((1, 1), f"{_ENDIAN}f4"): ("F", "F"),
+ ((1, 1), f"{_ENDIAN}i4"): ("I", "I", []),
+ ((1, 1), f"{_ENDIAN}f4"): ("F", "F", []),
}
@@ -3410,8 +3490,6 @@ def open(
filename: str | bytes = ""
if is_path(fp):
filename = os.fspath(fp)
-
- if filename:
fp = builtins.open(filename, "rb")
exclusive_fp = True
else:
@@ -3490,9 +3568,8 @@ def alpha_composite(im1: Image, im2: Image) -> Image:
"""
Alpha composite im2 over im1.
- :param im1: The first image. Must have mode RGBA.
- :param im2: The second image. Must have mode RGBA, and the same size as
- the first image.
+ :param im1: The first image. Must have mode RGBA or LA.
+ :param im2: The second image. Must have the same mode and size as the first image.
:returns: An :py:class:`~PIL.Image.Image` object.
"""
@@ -3718,6 +3795,7 @@ def register_encoder(name: str, encoder: type[ImageFile.PyEncoder]) -> None:
def _show(image: Image, **options: Any) -> None:
from . import ImageShow
+ deprecate("Image._show", 13, "ImageShow.show")
ImageShow.show(image, **options)
@@ -4139,6 +4217,8 @@ class Exif(_ExifBase):
del self._info[tag]
else:
del self._data[tag]
+ if tag in self._ifds:
+ del self._ifds[tag]
def __iter__(self) -> Iterator[int]:
keys = set(self._data)
diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py
index fdfbee789..513e28acf 100644
--- a/src/PIL/ImageCms.py
+++ b/src/PIL/ImageCms.py
@@ -25,7 +25,7 @@ from enum import IntEnum, IntFlag
from functools import reduce
from typing import Any, Literal, SupportsFloat, SupportsInt, Union
-from . import Image, __version__
+from . import Image
from ._deprecate import deprecate
from ._typing import SupportsRead
@@ -108,20 +108,6 @@ pyCMS
_VERSION = "1.0.0 pil"
-def __getattr__(name: str) -> Any:
- if name == "DESCRIPTION":
- deprecate("PIL.ImageCms.DESCRIPTION", 12)
- return _DESCRIPTION
- elif name == "VERSION":
- deprecate("PIL.ImageCms.VERSION", 12)
- return _VERSION
- elif name == "FLAGS":
- deprecate("PIL.ImageCms.FLAGS", 12, "PIL.ImageCms.Flags")
- return _FLAGS
- msg = f"module '{__name__}' has no attribute '{name}'"
- raise AttributeError(msg)
-
-
# --------------------------------------------------------------------.
@@ -248,6 +234,7 @@ class ImageCmsProfile:
low-level profile object
"""
+ self.filename: str | None = None
if isinstance(profile, str):
if sys.platform == "win32":
@@ -256,22 +243,24 @@ class ImageCmsProfile:
profile_bytes_path.decode("ascii")
except UnicodeDecodeError:
with open(profile, "rb") as f:
- self._set(core.profile_frombytes(f.read()))
+ self.profile = core.profile_frombytes(f.read())
return
- self._set(core.profile_open(profile), profile)
+ self.filename = profile
+ self.profile = core.profile_open(profile)
elif hasattr(profile, "read"):
- self._set(core.profile_frombytes(profile.read()))
+ self.profile = core.profile_frombytes(profile.read())
elif isinstance(profile, core.CmsProfile):
- self._set(profile)
+ self.profile = profile
else:
msg = "Invalid type for Profile" # type: ignore[unreachable]
raise TypeError(msg)
- def _set(self, profile: core.CmsProfile, filename: str | None = None) -> None:
- self.profile = profile
- self.filename = filename
- self.product_name = None # profile.product_name
- self.product_info = None # profile.product_info
+ def __getattr__(self, name: str) -> Any:
+ if name in ("product_name", "product_info"):
+ deprecate(f"ImageCms.ImageCmsProfile.{name}", 13)
+ return None
+ msg = f"'{self.__class__.__name__}' object has no attribute '{name}'"
+ raise AttributeError(msg)
def tobytes(self) -> bytes:
"""
@@ -303,31 +292,6 @@ class ImageCmsTransform(Image.ImagePointHandler):
proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC,
flags: Flags = Flags.NONE,
):
- supported_modes = (
- "RGB",
- "RGBA",
- "RGBX",
- "CMYK",
- "I;16",
- "I;16L",
- "I;16B",
- "YCbCr",
- "LAB",
- "L",
- "1",
- )
- for mode in (input_mode, output_mode):
- if mode not in supported_modes:
- deprecate(
- mode,
- 12,
- {
- "L;16": "I;16 or I;16L",
- "L:16B": "I;16B",
- "YCCA": "YCbCr",
- "YCC": "YCbCr",
- }.get(mode),
- )
if proof is None:
self.transform = core.buildTransform(
input.profile, output.profile, input_mode, output_mode, intent, flags
@@ -1110,16 +1074,3 @@ def isIntentSupported(
return -1
except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) from v
-
-
-def versions() -> tuple[str, str | None, str, str]:
- """
- (pyCMS) Fetches versions.
- """
-
- deprecate(
- "PIL.ImageCms.versions()",
- 12,
- '(PIL.features.version("littlecms2"), sys.version, PIL.__version__)',
- )
- return _VERSION, core.littlecms_version, sys.version.split()[0], __version__
diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py
index c2ed9034d..8bcf2d8ee 100644
--- a/src/PIL/ImageDraw.py
+++ b/src/PIL/ImageDraw.py
@@ -34,21 +34,22 @@ from __future__ import annotations
import math
import struct
from collections.abc import Sequence
-from types import ModuleType
-from typing import TYPE_CHECKING, Any, AnyStr, Callable, Union, cast
+from typing import cast
-from . import Image, ImageColor
-from ._deprecate import deprecate
-from ._typing import Coords
+from . import Image, ImageColor, ImageText
+
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from collections.abc import Callable
+ from types import ModuleType
+ from typing import Any, AnyStr
+
+ from . import ImageDraw2, ImageFont
+ from ._typing import Coords, _Ink
# experimental access to the outline API
Outline: Callable[[], Image.core._Outline] = Image.core.outline
-if TYPE_CHECKING:
- from . import ImageDraw2, ImageFont
-
-_Ink = Union[float, tuple[int, ...], str]
-
"""
A simple 2D drawing interface for PIL images.
@@ -73,9 +74,7 @@ class ImageDraw:
must be the same as the image mode. If omitted, the mode
defaults to the mode of the image.
"""
- im.load()
- if im.readonly:
- im._copy() # make it writeable
+ im._ensure_mutable()
blend = 0
if mode is None:
mode = im.mode
@@ -364,22 +363,10 @@ class ImageDraw:
# use the fill as a mask
mask = Image.new("1", self.im.size)
mask_ink = self._getink(1)[0]
-
- fill_im = mask.copy()
- draw = Draw(fill_im)
+ draw = Draw(mask)
draw.draw.draw_polygon(xy, mask_ink, 1)
- ink_im = mask.copy()
- draw = Draw(ink_im)
- width = width * 2 - 1
- draw.draw.draw_polygon(xy, mask_ink, 0, width)
-
- mask.paste(ink_im, mask=fill_im)
-
- im = Image.new(self.mode, self.im.size)
- draw = Draw(im)
- draw.draw.draw_polygon(xy, ink, 0, width)
- self.im.paste(im.im, (0, 0) + im.size, mask.im)
+ self.draw.draw_polygon(xy, ink, 0, width * 2 - 1, mask.im)
def regular_polygon(
self,
@@ -548,15 +535,10 @@ class ImageDraw:
right[3] -= r + 1
self.draw.draw_rectangle(right, ink, 1)
- def _multiline_check(self, text: AnyStr) -> bool:
- split_character = "\n" if isinstance(text, str) else b"\n"
-
- return split_character in text
-
def text(
self,
xy: tuple[float, float],
- text: AnyStr,
+ text: AnyStr | ImageText.Text,
fill: _Ink | None = None,
font: (
ImageFont.ImageFont
@@ -577,29 +559,18 @@ class ImageDraw:
**kwargs: Any,
) -> None:
"""Draw text."""
- if embedded_color and self.mode not in ("RGB", "RGBA"):
- msg = "Embedded color supported only in RGB and RGBA modes"
- raise ValueError(msg)
-
- if font is None:
- font = self._getfont(kwargs.get("font_size"))
-
- if self._multiline_check(text):
- return self.multiline_text(
- xy,
- text,
- fill,
- font,
- anchor,
- spacing,
- align,
- direction,
- features,
- language,
- stroke_width,
- stroke_fill,
- embedded_color,
+ if isinstance(text, ImageText.Text):
+ image_text = text
+ else:
+ if font is None:
+ font = self._getfont(kwargs.get("font_size"))
+ image_text = ImageText.Text(
+ text, font, self.mode, spacing, direction, features, language
)
+ if embedded_color:
+ image_text.embed_color()
+ if stroke_width:
+ image_text.stroke(stroke_width, stroke_fill)
def getink(fill: _Ink | None) -> int:
ink, fill_ink = self._getink(fill)
@@ -608,70 +579,79 @@ class ImageDraw:
return fill_ink
return ink
- def draw_text(ink: int, stroke_width: float = 0) -> None:
- mode = self.fontmode
- if stroke_width == 0 and embedded_color:
- mode = "RGBA"
- coord = []
- for i in range(2):
- coord.append(int(xy[i]))
- start = (math.modf(xy[0])[0], math.modf(xy[1])[0])
- try:
- mask, offset = font.getmask2( # type: ignore[union-attr,misc]
- text,
- mode,
- direction=direction,
- features=features,
- language=language,
- stroke_width=stroke_width,
- stroke_filled=True,
- anchor=anchor,
- ink=ink,
- start=start,
- *args,
- **kwargs,
- )
- coord = [coord[0] + offset[0], coord[1] + offset[1]]
- except AttributeError:
+ ink = getink(fill)
+ if ink is None:
+ return
+
+ stroke_ink = None
+ if image_text.stroke_width:
+ stroke_ink = (
+ getink(image_text.stroke_fill)
+ if image_text.stroke_fill is not None
+ else ink
+ )
+
+ for xy, anchor, line in image_text._split(xy, anchor, align):
+
+ def draw_text(ink: int, stroke_width: float = 0) -> None:
+ mode = self.fontmode
+ if stroke_width == 0 and embedded_color:
+ mode = "RGBA"
+ coord = []
+ for i in range(2):
+ coord.append(int(xy[i]))
+ start = (math.modf(xy[0])[0], math.modf(xy[1])[0])
try:
- mask = font.getmask( # type: ignore[misc]
- text,
+ mask, offset = image_text.font.getmask2( # type: ignore[union-attr,misc]
+ line,
mode,
- direction,
- features,
- language,
- stroke_width,
- anchor,
- ink,
+ direction=direction,
+ features=features,
+ language=language,
+ stroke_width=stroke_width,
+ stroke_filled=True,
+ anchor=anchor,
+ ink=ink,
start=start,
*args,
**kwargs,
)
- except TypeError:
- mask = font.getmask(text)
- if mode == "RGBA":
- # font.getmask2(mode="RGBA") returns color in RGB bands and mask in A
- # extract mask and set text alpha
- color, mask = mask, mask.getband(3)
- ink_alpha = struct.pack("i", ink)[3]
- color.fillband(3, ink_alpha)
- x, y = coord
- if self.im is not None:
- self.im.paste(
- color, (x, y, x + mask.size[0], y + mask.size[1]), mask
- )
- else:
- self.draw.draw_bitmap(coord, mask, ink)
-
- ink = getink(fill)
- if ink is not None:
- stroke_ink = None
- if stroke_width:
- stroke_ink = getink(stroke_fill) if stroke_fill is not None else ink
+ coord = [coord[0] + offset[0], coord[1] + offset[1]]
+ except AttributeError:
+ try:
+ mask = image_text.font.getmask( # type: ignore[misc]
+ line,
+ mode,
+ direction,
+ features,
+ language,
+ stroke_width,
+ anchor,
+ ink,
+ start=start,
+ *args,
+ **kwargs,
+ )
+ except TypeError:
+ mask = image_text.font.getmask(line)
+ if mode == "RGBA":
+ # image_text.font.getmask2(mode="RGBA")
+ # returns color in RGB bands and mask in A
+ # extract mask and set text alpha
+ color, mask = mask, mask.getband(3)
+ ink_alpha = struct.pack("i", ink)[3]
+ color.fillband(3, ink_alpha)
+ x, y = coord
+ if self.im is not None:
+ self.im.paste(
+ color, (x, y, x + mask.size[0], y + mask.size[1]), mask
+ )
+ else:
+ self.draw.draw_bitmap(coord, mask, ink)
if stroke_ink is not None:
# Draw stroked text
- draw_text(stroke_ink, stroke_width)
+ draw_text(stroke_ink, image_text.stroke_width)
# Draw normal text
if ink != stroke_ink:
@@ -680,119 +660,6 @@ class ImageDraw:
# Only draw normal text
draw_text(ink)
- def _prepare_multiline_text(
- self,
- xy: tuple[float, float],
- text: AnyStr,
- font: (
- ImageFont.ImageFont
- | ImageFont.FreeTypeFont
- | ImageFont.TransposedFont
- | None
- ),
- anchor: str | None,
- spacing: float,
- align: str,
- direction: str | None,
- features: list[str] | None,
- language: str | None,
- stroke_width: float,
- embedded_color: bool,
- font_size: float | None,
- ) -> tuple[
- ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont,
- str,
- list[tuple[tuple[float, float], AnyStr]],
- ]:
- if direction == "ttb":
- msg = "ttb direction is unsupported for multiline text"
- raise ValueError(msg)
-
- if anchor is None:
- anchor = "la"
- elif len(anchor) != 2:
- msg = "anchor must be a 2 character string"
- raise ValueError(msg)
- elif anchor[1] in "tb":
- msg = "anchor not supported for multiline text"
- raise ValueError(msg)
-
- if font is None:
- font = self._getfont(font_size)
-
- widths = []
- max_width: float = 0
- lines = text.split("\n" if isinstance(text, str) else b"\n")
- line_spacing = (
- self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[3]
- + stroke_width
- + spacing
- )
-
- for line in lines:
- line_width = self.textlength(
- line,
- font,
- direction=direction,
- features=features,
- language=language,
- embedded_color=embedded_color,
- )
- widths.append(line_width)
- max_width = max(max_width, line_width)
-
- top = xy[1]
- if anchor[1] == "m":
- top -= (len(lines) - 1) * line_spacing / 2.0
- elif anchor[1] == "d":
- top -= (len(lines) - 1) * line_spacing
-
- parts = []
- for idx, line in enumerate(lines):
- left = xy[0]
- width_difference = max_width - widths[idx]
-
- # first align left by anchor
- if anchor[0] == "m":
- left -= width_difference / 2.0
- elif anchor[0] == "r":
- left -= width_difference
-
- # then align by align parameter
- if align in ("left", "justify"):
- pass
- elif align == "center":
- left += width_difference / 2.0
- elif align == "right":
- left += width_difference
- else:
- msg = 'align must be "left", "center", "right" or "justify"'
- raise ValueError(msg)
-
- if align == "justify" and width_difference != 0:
- words = line.split(" " if isinstance(text, str) else b" ")
- word_widths = [
- self.textlength(
- word,
- font,
- direction=direction,
- features=features,
- language=language,
- embedded_color=embedded_color,
- )
- for word in words
- ]
- width_difference = max_width - sum(word_widths)
- for i, word in enumerate(words):
- parts.append(((left, top), word))
- left += word_widths[i] + width_difference / (len(words) - 1)
- else:
- parts.append(((left, top), line))
-
- top += line_spacing
-
- return font, anchor, parts
-
def multiline_text(
self,
xy: tuple[float, float],
@@ -816,9 +683,10 @@ class ImageDraw:
*,
font_size: float | None = None,
) -> None:
- font, anchor, lines = self._prepare_multiline_text(
+ return self.text(
xy,
text,
+ fill,
font,
anchor,
spacing,
@@ -827,25 +695,11 @@ class ImageDraw:
features,
language,
stroke_width,
+ stroke_fill,
embedded_color,
- font_size,
+ font_size=font_size,
)
- for xy, line in lines:
- self.text(
- xy,
- line,
- fill,
- font,
- anchor,
- direction=direction,
- features=features,
- language=language,
- stroke_width=stroke_width,
- stroke_fill=stroke_fill,
- embedded_color=embedded_color,
- )
-
def textlength(
self,
text: AnyStr,
@@ -863,17 +717,19 @@ class ImageDraw:
font_size: float | None = None,
) -> float:
"""Get the length of a given string, in pixels with 1/64 precision."""
- if self._multiline_check(text):
- msg = "can't measure length of multiline text"
- raise ValueError(msg)
- if embedded_color and self.mode not in ("RGB", "RGBA"):
- msg = "Embedded color supported only in RGB and RGBA modes"
- raise ValueError(msg)
-
if font is None:
font = self._getfont(font_size)
- mode = "RGBA" if embedded_color else self.fontmode
- return font.getlength(text, mode, direction, features, language)
+ image_text = ImageText.Text(
+ text,
+ font,
+ self.mode,
+ direction=direction,
+ features=features,
+ language=language,
+ )
+ if embedded_color:
+ image_text.embed_color()
+ return image_text.get_length()
def textbbox(
self,
@@ -897,33 +753,16 @@ class ImageDraw:
font_size: float | None = None,
) -> tuple[float, float, float, float]:
"""Get the bounding box of a given string, in pixels."""
- if embedded_color and self.mode not in ("RGB", "RGBA"):
- msg = "Embedded color supported only in RGB and RGBA modes"
- raise ValueError(msg)
-
if font is None:
font = self._getfont(font_size)
-
- if self._multiline_check(text):
- return self.multiline_textbbox(
- xy,
- text,
- font,
- anchor,
- spacing,
- align,
- direction,
- features,
- language,
- stroke_width,
- embedded_color,
- )
-
- mode = "RGBA" if embedded_color else self.fontmode
- bbox = font.getbbox(
- text, mode, direction, features, language, stroke_width, anchor
+ image_text = ImageText.Text(
+ text, font, self.mode, spacing, direction, features, language
)
- return bbox[0] + xy[0], bbox[1] + xy[1], bbox[2] + xy[0], bbox[3] + xy[1]
+ if embedded_color:
+ image_text.embed_color()
+ if stroke_width:
+ image_text.stroke(stroke_width)
+ return image_text.get_bbox(xy, anchor, align)
def multiline_textbbox(
self,
@@ -946,7 +785,7 @@ class ImageDraw:
*,
font_size: float | None = None,
) -> tuple[float, float, float, float]:
- font, anchor, lines = self._prepare_multiline_text(
+ return self.textbbox(
xy,
text,
font,
@@ -958,37 +797,9 @@ class ImageDraw:
language,
stroke_width,
embedded_color,
- font_size,
+ font_size=font_size,
)
- bbox: tuple[float, float, float, float] | None = None
-
- for xy, line in lines:
- bbox_line = self.textbbox(
- xy,
- line,
- font,
- anchor,
- direction=direction,
- features=features,
- language=language,
- stroke_width=stroke_width,
- embedded_color=embedded_color,
- )
- if bbox is None:
- bbox = bbox_line
- else:
- bbox = (
- min(bbox[0], bbox_line[0]),
- min(bbox[1], bbox_line[1]),
- max(bbox[2], bbox_line[2]),
- max(bbox[3], bbox_line[3]),
- )
-
- if bbox is None:
- return xy[0], xy[1], xy[0], xy[1]
- return bbox
-
def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw:
"""
@@ -1007,16 +818,11 @@ def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw:
return ImageDraw(im, mode)
-def getdraw(
- im: Image.Image | None = None, hints: list[str] | None = None
-) -> tuple[ImageDraw2.Draw | None, ModuleType]:
+def getdraw(im: Image.Image | None = None) -> tuple[ImageDraw2.Draw | None, ModuleType]:
"""
:param im: The image to draw in.
- :param hints: An optional list of hints. Deprecated.
:returns: A (drawing context, drawing resource factory) tuple.
"""
- if hints is not None:
- deprecate("'hints' parameter", 12)
from . import ImageDraw2
draw = ImageDraw2.Draw(im) if im is not None else None
diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py
index 1bf8a7e5f..a1d98bd51 100644
--- a/src/PIL/ImageFile.py
+++ b/src/PIL/ImageFile.py
@@ -34,19 +34,30 @@ import itertools
import logging
import os
import struct
-import sys
-from typing import IO, TYPE_CHECKING, Any, NamedTuple, cast
+from typing import IO, Any, NamedTuple, cast
from . import ExifTags, Image
-from ._deprecate import deprecate
from ._util import DeferredError, is_path
+TYPE_CHECKING = False
if TYPE_CHECKING:
from ._typing import StrOrBytesPath
logger = logging.getLogger(__name__)
MAXBLOCK = 65536
+"""
+By default, Pillow processes image data in blocks. This helps to prevent excessive use
+of resources. Codecs may disable this behaviour with ``_pulls_fd`` or ``_pushes_fd``.
+
+When reading an image, this is the number of bytes to read at once.
+
+When writing an image, this is the number of bytes to write at once.
+If the image width times 4 is greater, then that will be used instead.
+Plugins may also set a greater number.
+
+User code may set this to another number.
+"""
SAFEBLOCK = 1024 * 1024
@@ -83,16 +94,6 @@ def _get_oserror(error: int, *, encoder: bool) -> OSError:
return OSError(msg)
-def raise_oserror(error: int) -> OSError:
- deprecate(
- "raise_oserror",
- 12,
- action="It is only useful for translating error codes returned by a codec's "
- "decode() method, which ImageFile already does automatically.",
- )
- raise _get_oserror(error, encoder=False)
-
-
def _tilesort(t: _Tile) -> int:
# sort on offset
return t[2]
@@ -167,7 +168,7 @@ class ImageFile(Image.Image):
pass
def _close_fp(self):
- if getattr(self, "_fp", False):
+ if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError):
if self._fp != self.fp:
self._fp.close()
self._fp = DeferredError(ValueError("Operation on closed image"))
@@ -252,8 +253,13 @@ class ImageFile(Image.Image):
return Image.MIME.get(self.format.upper())
return None
+ def __getstate__(self) -> list[Any]:
+ return super().__getstate__() + [self.filename]
+
def __setstate__(self, state: list[Any]) -> None:
self.tile = []
+ if len(state) > 5:
+ self.filename = state[5]
super().__setstate__(state)
def verify(self) -> None:
@@ -278,8 +284,6 @@ class ImageFile(Image.Image):
self.map: mmap.mmap | None = None
use_mmap = self.filename and len(self.tile) == 1
- # As of pypy 2.1.0, memory mapping was failing here.
- use_mmap = use_mmap and not hasattr(sys, "pypy_version_info")
readonly = 0
@@ -309,6 +313,9 @@ class ImageFile(Image.Image):
and args[0] == self.mode
and args[0] in Image._MAPMODES
):
+ if offset < 0:
+ msg = "Tile offset cannot be negative"
+ raise ValueError(msg)
try:
# use mmap, if possible
import mmap
@@ -345,7 +352,7 @@ class ImageFile(Image.Image):
self.tile, lambda tile: (tile[0], tile[1], tile[3])
)
]
- for decoder_name, extents, offset, args in self.tile:
+ for i, (decoder_name, extents, offset, args) in enumerate(self.tile):
seek(offset)
decoder = Image._getdecoder(
self.mode, decoder_name, args, self.decoderconfig
@@ -358,8 +365,13 @@ class ImageFile(Image.Image):
else:
b = prefix
while True:
+ read_bytes = self.decodermaxblock
+ if i + 1 < len(self.tile):
+ next_offset = self.tile[i + 1].offset
+ if next_offset > offset:
+ read_bytes = next_offset - offset
try:
- s = read(self.decodermaxblock)
+ s = read(read_bytes)
except (IndexError, struct.error) as e:
# truncated png/gif
if LOAD_TRUNCATED_IMAGES:
diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py
index 05829d0c6..9326eeeda 100644
--- a/src/PIL/ImageFilter.py
+++ b/src/PIL/ImageFilter.py
@@ -19,10 +19,14 @@ from __future__ import annotations
import abc
import functools
from collections.abc import Sequence
-from types import ModuleType
-from typing import TYPE_CHECKING, Any, Callable, cast
+from typing import cast
+TYPE_CHECKING = False
if TYPE_CHECKING:
+ from collections.abc import Callable
+ from types import ModuleType
+ from typing import Any
+
from . import _imaging
from ._typing import NumpyArray
diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py
index c8f05fbb7..92eb763a5 100644
--- a/src/PIL/ImageFont.py
+++ b/src/PIL/ImageFont.py
@@ -34,12 +34,13 @@ import warnings
from enum import IntEnum
from io import BytesIO
from types import ModuleType
-from typing import IO, TYPE_CHECKING, Any, BinaryIO, TypedDict, cast
+from typing import IO, Any, BinaryIO, TypedDict, cast
-from . import Image, features
+from . import Image
from ._typing import StrOrBytesPath
from ._util import DeferredError, is_path
+TYPE_CHECKING = False
if TYPE_CHECKING:
from . import ImageFile
from ._imaging import ImagingFont
@@ -124,11 +125,16 @@ class ImageFont:
image.close()
def _load_pilfont_data(self, file: IO[bytes], image: Image.Image) -> None:
+ # check image
+ if image.mode not in ("1", "L"):
+ msg = "invalid font image mode"
+ raise TypeError(msg)
+
# read PILfont header
- if file.readline() != b"PILfont\n":
+ if file.read(8) != b"PILfont\n":
msg = "Not a PILfont file"
raise SyntaxError(msg)
- file.readline().split(b";")
+ file.readline()
self.info = [] # FIXME: should be a dictionary
while True:
s = file.readline()
@@ -139,11 +145,6 @@ class ImageFont:
# read PILfont metrics
data = file.read(256 * 20)
- # check image
- if image.mode not in ("1", "L"):
- msg = "invalid font image mode"
- raise TypeError(msg)
-
image.load()
self.font = Image.core.font(image.im, data)
@@ -235,21 +236,6 @@ class FreeTypeFont:
self.index = index
self.encoding = encoding
- try:
- from packaging.version import parse as parse_version
- except ImportError:
- pass
- else:
- if freetype_version := features.version_module("freetype2"):
- if parse_version(freetype_version) < parse_version("2.9.1"):
- warnings.warn(
- "Support for FreeType 2.9.0 is deprecated and will be removed "
- "in Pillow 12 (2025-10-15). Please upgrade to FreeType 2.9.1 "
- "or newer, preferably FreeType 2.10.4 which fixes "
- "CVE-2020-15999.",
- DeprecationWarning,
- )
-
if layout_engine not in (Layout.BASIC, Layout.RAQM):
layout_engine = Layout.BASIC
if core.HAVE_RAQM:
@@ -685,11 +671,7 @@ class FreeTypeFont:
:returns: A list of the named styles in a variation font.
:exception OSError: If the font is not a variation font.
"""
- try:
- names = self.font.getvarnames()
- except AttributeError as e:
- msg = "FreeType 2.9.1 or greater is required"
- raise NotImplementedError(msg) from e
+ names = self.font.getvarnames()
return [name.replace(b"\x00", b"") for name in names]
def set_variation_by_name(self, name: str | bytes) -> None:
@@ -716,11 +698,7 @@ class FreeTypeFont:
:returns: A list of the axes in a variation font.
:exception OSError: If the font is not a variation font.
"""
- try:
- axes = self.font.getvaraxes()
- except AttributeError as e:
- msg = "FreeType 2.9.1 or greater is required"
- raise NotImplementedError(msg) from e
+ axes = self.font.getvaraxes()
for axis in axes:
if axis["name"]:
axis["name"] = axis["name"].replace(b"\x00", b"")
@@ -731,11 +709,7 @@ class FreeTypeFont:
:param axes: A list of values for each axis.
:exception OSError: If the font is not a variation font.
"""
- try:
- self.font.setvaraxes(axes)
- except AttributeError as e:
- msg = "FreeType 2.9.1 or greater is required"
- raise NotImplementedError(msg) from e
+ self.font.setvaraxes(axes)
class TransposedFont:
@@ -1092,7 +1066,7 @@ w7IkEbzhVQAAAABJRU5ErkJggg==
def load_default(size: float | None = None) -> FreeTypeFont | ImageFont:
"""If FreeType support is available, load a version of Aileron Regular,
- https://dotcolon.net/font/aileron, with a more limited character set.
+ https://dotcolon.net/fonts/aileron, with a more limited character set.
Otherwise, load a "better than nothing" font.
diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py
index fe27bfaeb..1eb450734 100644
--- a/src/PIL/ImageGrab.py
+++ b/src/PIL/ImageGrab.py
@@ -25,12 +25,17 @@ import tempfile
from . import Image
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from . import ImageWin
+
def grab(
bbox: tuple[int, int, int, int] | None = None,
include_layered_windows: bool = False,
all_screens: bool = False,
xdisplay: str | None = None,
+ window: int | ImageWin.HWND | None = None,
) -> Image.Image:
im: Image.Image
if xdisplay is None:
@@ -51,8 +56,12 @@ def grab(
return im_resized
return im
elif sys.platform == "win32":
+ if window is not None:
+ all_screens = -1
offset, size, data = Image.core.grabscreen_win32(
- include_layered_windows, all_screens
+ include_layered_windows,
+ all_screens,
+ int(window) if window is not None else 0,
)
im = Image.frombytes(
"RGB",
@@ -77,14 +86,18 @@ def grab(
raise OSError(msg)
size, data = Image.core.grabscreen_x11(display_name)
except OSError:
- if (
- display_name is None
- and sys.platform not in ("darwin", "win32")
- and shutil.which("gnome-screenshot")
- ):
+ if display_name is None and sys.platform not in ("darwin", "win32"):
+ if shutil.which("gnome-screenshot"):
+ args = ["gnome-screenshot", "-f"]
+ elif shutil.which("grim"):
+ args = ["grim"]
+ elif shutil.which("spectacle"):
+ args = ["spectacle", "-n", "-b", "-f", "-o"]
+ else:
+ raise
fh, filepath = tempfile.mkstemp(".png")
os.close(fh)
- subprocess.call(["gnome-screenshot", "-f", filepath])
+ subprocess.call(args + [filepath])
im = Image.open(filepath)
im.load()
os.unlink(filepath)
@@ -121,10 +134,10 @@ def grabclipboard() -> Image.Image | list[str] | None:
import struct
o = struct.unpack_from("I", data)[0]
- if data[16] != 0:
- files = data[o:].decode("utf-16le").split("\0")
- else:
+ if data[16] == 0:
files = data[o:].decode("mbcs").split("\0")
+ else:
+ files = data[o:].decode("utf-16le").split("\0")
return files[: files.index("")]
if isinstance(data, bytes):
data = io.BytesIO(data)
diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py
index 484797f91..dfdc50c05 100644
--- a/src/PIL/ImageMath.py
+++ b/src/PIL/ImageMath.py
@@ -17,11 +17,14 @@
from __future__ import annotations
import builtins
-from types import CodeType
-from typing import Any, Callable
from . import Image, _imagingmath
-from ._deprecate import deprecate
+
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from collections.abc import Callable
+ from types import CodeType
+ from typing import Any
class _Operand:
@@ -233,11 +236,7 @@ ops = {
}
-def lambda_eval(
- expression: Callable[[dict[str, Any]], Any],
- options: dict[str, Any] = {},
- **kw: Any,
-) -> Any:
+def lambda_eval(expression: Callable[[dict[str, Any]], Any], **kw: Any) -> Any:
"""
Returns the result of an image function.
@@ -246,23 +245,13 @@ def lambda_eval(
:py:func:`~PIL.Image.merge` function.
:param expression: A function that receives a dictionary.
- :param options: Values to add to the function's dictionary. Deprecated.
- You can instead use one or more keyword arguments.
:param **kw: Values to add to the function's dictionary.
:return: The expression result. This is usually an image object, but can
also be an integer, a floating point value, or a pixel tuple,
depending on the expression.
"""
- if options:
- deprecate(
- "ImageMath.lambda_eval options",
- 12,
- "ImageMath.lambda_eval keyword arguments",
- )
-
args: dict[str, Any] = ops.copy()
- args.update(options)
args.update(kw)
for k, v in args.items():
if isinstance(v, Image.Image):
@@ -275,11 +264,7 @@ def lambda_eval(
return out
-def unsafe_eval(
- expression: str,
- options: dict[str, Any] = {},
- **kw: Any,
-) -> Any:
+def unsafe_eval(expression: str, **kw: Any) -> Any:
"""
Evaluates an image expression. This uses Python's ``eval()`` function to process
the expression string, and carries the security risks of doing so. It is not
@@ -291,29 +276,19 @@ def unsafe_eval(
:py:func:`~PIL.Image.merge` function.
:param expression: A string containing a Python-style expression.
- :param options: Values to add to the evaluation context. Deprecated.
- You can instead use one or more keyword arguments.
:param **kw: Values to add to the evaluation context.
:return: The evaluated expression. This is usually an image object, but can
also be an integer, a floating point value, or a pixel tuple,
depending on the expression.
"""
- if options:
- deprecate(
- "ImageMath.unsafe_eval options",
- 12,
- "ImageMath.unsafe_eval keyword arguments",
- )
-
# build execution namespace
args: dict[str, Any] = ops.copy()
- for k in list(options.keys()) + list(kw.keys()):
+ for k in kw:
if "__" in k or hasattr(builtins, k):
msg = f"'{k}' not allowed"
raise ValueError(msg)
- args.update(options)
args.update(kw)
for k, v in args.items():
if isinstance(v, Image.Image):
@@ -337,32 +312,3 @@ def unsafe_eval(
return out.im
except AttributeError:
return out
-
-
-def eval(
- expression: str,
- _dict: dict[str, Any] = {},
- **kw: Any,
-) -> Any:
- """
- Evaluates an image expression.
-
- Deprecated. Use lambda_eval() or unsafe_eval() instead.
-
- :param expression: A string containing a Python-style expression.
- :param _dict: Values to add to the evaluation context. You
- can either use a dictionary, or one or more keyword
- arguments.
- :return: The evaluated expression. This is usually an image object, but can
- also be an integer, a floating point value, or a pixel tuple,
- depending on the expression.
-
- .. deprecated:: 10.3.0
- """
-
- deprecate(
- "ImageMath.eval",
- 12,
- "ImageMath.lambda_eval or ImageMath.unsafe_eval",
- )
- return unsafe_eval(expression, _dict, **kw)
diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py
index 92a08d2cb..b7c6c8636 100644
--- a/src/PIL/ImageMode.py
+++ b/src/PIL/ImageMode.py
@@ -18,8 +18,6 @@ import sys
from functools import lru_cache
from typing import NamedTuple
-from ._deprecate import deprecate
-
class ModeDescriptor(NamedTuple):
"""Wrapper for mode strings."""
@@ -57,16 +55,11 @@ def getmode(mode: str) -> ModeDescriptor:
"HSV": ("RGB", "L", ("H", "S", "V"), "|u1"),
# extra experimental modes
"RGBa": ("RGB", "L", ("R", "G", "B", "a"), "|u1"),
- "BGR;15": ("RGB", "L", ("B", "G", "R"), "|u1"),
- "BGR;16": ("RGB", "L", ("B", "G", "R"), "|u1"),
- "BGR;24": ("RGB", "L", ("B", "G", "R"), "|u1"),
"LA": ("L", "L", ("L", "A"), "|u1"),
"La": ("L", "L", ("L", "a"), "|u1"),
"PA": ("RGB", "L", ("P", "A"), "|u1"),
}
if mode in modes:
- if mode in ("BGR;15", "BGR;16", "BGR;24"):
- deprecate(mode, 12)
base_mode, base_type, bands, type_str = modes[mode]
return ModeDescriptor(mode, bands, base_mode, base_type, type_str)
diff --git a/src/PIL/ImageMorph.py b/src/PIL/ImageMorph.py
index f0a066b5b..bd70aff7b 100644
--- a/src/PIL/ImageMorph.py
+++ b/src/PIL/ImageMorph.py
@@ -150,7 +150,7 @@ class LutBuilder:
# Parse and create symmetries of the patterns strings
for p in self.patterns:
- m = re.search(r"(\w*):?\s*\((.+?)\)\s*->\s*(\d)", p.replace("\n", ""))
+ m = re.search(r"(\w):?\s*\((.+?)\)\s*->\s*(\d)", p.replace("\n", ""))
if not m:
msg = 'Syntax error in pattern "' + p + '"'
raise Exception(msg)
diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py
index da28854b5..42b10bd7b 100644
--- a/src/PIL/ImageOps.py
+++ b/src/PIL/ImageOps.py
@@ -499,14 +499,15 @@ def expand(
height = top + image.size[1] + bottom
color = _color(fill, image.mode)
if image.palette:
- palette = ImagePalette.ImagePalette(palette=image.getpalette())
+ mode = image.palette.mode
+ palette = ImagePalette.ImagePalette(mode, image.getpalette(mode))
if isinstance(color, tuple) and (len(color) == 3 or len(color) == 4):
color = palette.getcolor(color)
else:
palette = None
out = Image.new(image.mode, (width, height), color)
if palette:
- out.putpalette(palette.palette)
+ out.putpalette(palette.palette, mode)
out.paste(image, (left, top))
return out
diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py
index 183f85526..103697117 100644
--- a/src/PIL/ImagePalette.py
+++ b/src/PIL/ImagePalette.py
@@ -19,10 +19,11 @@ from __future__ import annotations
import array
from collections.abc import Sequence
-from typing import IO, TYPE_CHECKING
+from typing import IO
from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile
+TYPE_CHECKING = False
if TYPE_CHECKING:
from . import Image
diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py
index 2cc40f855..af4d0742d 100644
--- a/src/PIL/ImageQt.py
+++ b/src/PIL/ImageQt.py
@@ -19,22 +19,18 @@ from __future__ import annotations
import sys
from io import BytesIO
-from typing import TYPE_CHECKING, Any, Callable, Union
from . import Image
from ._util import is_path
+TYPE_CHECKING = False
if TYPE_CHECKING:
- import PyQt6
- import PySide6
+ from collections.abc import Callable
+ from typing import Any
from . import ImageFile
QBuffer: type
- QByteArray = Union[PyQt6.QtCore.QByteArray, PySide6.QtCore.QByteArray]
- QIODevice = Union[PyQt6.QtCore.QIODevice, PySide6.QtCore.QIODevice]
- QImage = Union[PyQt6.QtGui.QImage, PySide6.QtGui.QImage]
- QPixmap = Union[PyQt6.QtGui.QPixmap, PySide6.QtGui.QPixmap]
qt_version: str | None
qt_versions = [
@@ -48,11 +44,15 @@ for version, qt_module in qt_versions:
try:
qRgba: Callable[[int, int, int, int], int]
if qt_module == "PyQt6":
- from PyQt6.QtCore import QBuffer, QIODevice
+ from PyQt6.QtCore import QBuffer, QByteArray, QIODevice
from PyQt6.QtGui import QImage, QPixmap, qRgba
elif qt_module == "PySide6":
- from PySide6.QtCore import QBuffer, QIODevice
- from PySide6.QtGui import QImage, QPixmap, qRgba
+ from PySide6.QtCore import ( # type: ignore[assignment]
+ QBuffer,
+ QByteArray,
+ QIODevice,
+ )
+ from PySide6.QtGui import QImage, QPixmap, qRgba # type: ignore[assignment]
except (ImportError, RuntimeError):
continue
qt_is_installed = True
@@ -182,7 +182,7 @@ def _toqclass_helper(im: Image.Image | str | QByteArray) -> dict[str, Any]:
if qt_is_installed:
- class ImageQt(QImage): # type: ignore[misc]
+ class ImageQt(QImage):
def __init__(self, im: Image.Image | str | QByteArray) -> None:
"""
An PIL image wrapper for Qt. This is a subclass of PyQt's QImage
diff --git a/src/PIL/ImageSequence.py b/src/PIL/ImageSequence.py
index a6fc340d5..361be4897 100644
--- a/src/PIL/ImageSequence.py
+++ b/src/PIL/ImageSequence.py
@@ -16,10 +16,12 @@
##
from __future__ import annotations
-from typing import Callable
-
from . import Image
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from collections.abc import Callable
+
class Iterator:
"""
diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py
index dd240fb55..7705608e3 100644
--- a/src/PIL/ImageShow.py
+++ b/src/PIL/ImageShow.py
@@ -175,7 +175,9 @@ class MacViewer(Viewer):
if not os.path.exists(path):
raise FileNotFoundError
subprocess.call(["open", "-a", "Preview.app", path])
- executable = sys.executable or shutil.which("python3")
+
+ pyinstaller = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
+ executable = (not pyinstaller and sys.executable) or shutil.which("python3")
if executable:
subprocess.Popen(
[
diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py
index 8bc504526..3a1044ba4 100644
--- a/src/PIL/ImageStat.py
+++ b/src/PIL/ImageStat.py
@@ -120,7 +120,7 @@ class Stat:
@cached_property
def mean(self) -> list[float]:
"""Average (arithmetic mean) pixel level for each band in the image."""
- return [self.sum[i] / self.count[i] for i in self.bands]
+ return [self.sum[i] / self.count[i] if self.count[i] else 0 for i in self.bands]
@cached_property
def median(self) -> list[int]:
@@ -141,13 +141,20 @@ class Stat:
@cached_property
def rms(self) -> list[float]:
"""RMS (root-mean-square) for each band in the image."""
- return [math.sqrt(self.sum2[i] / self.count[i]) for i in self.bands]
+ return [
+ math.sqrt(self.sum2[i] / self.count[i]) if self.count[i] else 0
+ for i in self.bands
+ ]
@cached_property
def var(self) -> list[float]:
"""Variance for each band in the image."""
return [
- (self.sum2[i] - (self.sum[i] ** 2.0) / self.count[i]) / self.count[i]
+ (
+ (self.sum2[i] - (self.sum[i] ** 2.0) / self.count[i]) / self.count[i]
+ if self.count[i]
+ else 0
+ )
for i in self.bands
]
diff --git a/src/PIL/ImageText.py b/src/PIL/ImageText.py
new file mode 100644
index 000000000..c74570e69
--- /dev/null
+++ b/src/PIL/ImageText.py
@@ -0,0 +1,318 @@
+from __future__ import annotations
+
+from . import ImageFont
+from ._typing import _Ink
+
+
+class Text:
+ def __init__(
+ self,
+ text: str | bytes,
+ font: (
+ ImageFont.ImageFont
+ | ImageFont.FreeTypeFont
+ | ImageFont.TransposedFont
+ | None
+ ) = None,
+ mode: str = "RGB",
+ spacing: float = 4,
+ direction: str | None = None,
+ features: list[str] | None = None,
+ language: str | None = None,
+ ) -> None:
+ """
+ :param text: String to be drawn.
+ :param font: Either an :py:class:`~PIL.ImageFont.ImageFont` instance,
+ :py:class:`~PIL.ImageFont.FreeTypeFont` instance,
+ :py:class:`~PIL.ImageFont.TransposedFont` instance or ``None``. If
+ ``None``, the default font from :py:meth:`.ImageFont.load_default`
+ will be used.
+ :param mode: The image mode this will be used with.
+ :param spacing: The number of pixels between lines.
+ :param direction: Direction of the text. It can be ``"rtl"`` (right to left),
+ ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom).
+ Requires libraqm.
+ :param features: A list of OpenType font features to be used during text
+ layout. This is usually used to turn on optional font features
+ that are not enabled by default, for example ``"dlig"`` or
+ ``"ss01"``, but can be also used to turn off default font
+ features, for example ``"-liga"`` to disable ligatures or
+ ``"-kern"`` to disable kerning. To get all supported
+ features, see `OpenType docs`_.
+ Requires libraqm.
+ :param language: Language of the text. Different languages may use
+ different glyph shapes or ligatures. This parameter tells
+ the font which language the text is in, and to apply the
+ correct substitutions as appropriate, if available.
+ It should be a `BCP 47 language code`_.
+ Requires libraqm.
+ """
+ self.text = text
+ self.font = font or ImageFont.load_default()
+
+ self.mode = mode
+ self.spacing = spacing
+ self.direction = direction
+ self.features = features
+ self.language = language
+
+ self.embedded_color = False
+
+ self.stroke_width: float = 0
+ self.stroke_fill: _Ink | None = None
+
+ def embed_color(self) -> None:
+ """
+ Use embedded color glyphs (COLR, CBDT, SBIX).
+ """
+ if self.mode not in ("RGB", "RGBA"):
+ msg = "Embedded color supported only in RGB and RGBA modes"
+ raise ValueError(msg)
+ self.embedded_color = True
+
+ def stroke(self, width: float = 0, fill: _Ink | None = None) -> None:
+ """
+ :param width: The width of the text stroke.
+ :param fill: Color to use for the text stroke when drawing. If not given, will
+ default to the ``fill`` parameter from
+ :py:meth:`.ImageDraw.ImageDraw.text`.
+ """
+ self.stroke_width = width
+ self.stroke_fill = fill
+
+ def _get_fontmode(self) -> str:
+ if self.mode in ("1", "P", "I", "F"):
+ return "1"
+ elif self.embedded_color:
+ return "RGBA"
+ else:
+ return "L"
+
+ def get_length(self):
+ """
+ Returns length (in pixels with 1/64 precision) of text.
+
+ This is the amount by which following text should be offset.
+ Text bounding box may extend past the length in some fonts,
+ e.g. when using italics or accents.
+
+ The result is returned as a float; it is a whole number if using basic layout.
+
+ Note that the sum of two lengths may not equal the length of a concatenated
+ string due to kerning. If you need to adjust for kerning, include the following
+ character and subtract its length.
+
+ For example, instead of::
+
+ hello = ImageText.Text("Hello", font).get_length()
+ world = ImageText.Text("World", font).get_length()
+ helloworld = ImageText.Text("HelloWorld", font).get_length()
+ assert hello + world == helloworld
+
+ use::
+
+ hello = (
+ ImageText.Text("HelloW", font).get_length() -
+ ImageText.Text("W", font).get_length()
+ ) # adjusted for kerning
+ world = ImageText.Text("World", font).get_length()
+ helloworld = ImageText.Text("HelloWorld", font).get_length()
+ assert hello + world == helloworld
+
+ or disable kerning with (requires libraqm)::
+
+ hello = ImageText.Text("Hello", font, features=["-kern"]).get_length()
+ world = ImageText.Text("World", font, features=["-kern"]).get_length()
+ helloworld = ImageText.Text(
+ "HelloWorld", font, features=["-kern"]
+ ).get_length()
+ assert hello + world == helloworld
+
+ :return: Either width for horizontal text, or height for vertical text.
+ """
+ split_character = "\n" if isinstance(self.text, str) else b"\n"
+ if split_character in self.text:
+ msg = "can't measure length of multiline text"
+ raise ValueError(msg)
+ return self.font.getlength(
+ self.text,
+ self._get_fontmode(),
+ self.direction,
+ self.features,
+ self.language,
+ )
+
+ def _split(
+ self, xy: tuple[float, float], anchor: str | None, align: str
+ ) -> list[tuple[tuple[float, float], str, str | bytes]]:
+ if anchor is None:
+ anchor = "lt" if self.direction == "ttb" else "la"
+ elif len(anchor) != 2:
+ msg = "anchor must be a 2 character string"
+ raise ValueError(msg)
+
+ lines = (
+ self.text.split("\n")
+ if isinstance(self.text, str)
+ else self.text.split(b"\n")
+ )
+ if len(lines) == 1:
+ return [(xy, anchor, self.text)]
+
+ if anchor[1] in "tb" and self.direction != "ttb":
+ msg = "anchor not supported for multiline text"
+ raise ValueError(msg)
+
+ fontmode = self._get_fontmode()
+ line_spacing = (
+ self.font.getbbox(
+ "A",
+ fontmode,
+ None,
+ self.features,
+ self.language,
+ self.stroke_width,
+ )[3]
+ + self.stroke_width
+ + self.spacing
+ )
+
+ top = xy[1]
+ parts = []
+ if self.direction == "ttb":
+ left = xy[0]
+ for line in lines:
+ parts.append(((left, top), anchor, line))
+ left += line_spacing
+ else:
+ widths = []
+ max_width: float = 0
+ for line in lines:
+ line_width = self.font.getlength(
+ line, fontmode, self.direction, self.features, self.language
+ )
+ widths.append(line_width)
+ max_width = max(max_width, line_width)
+
+ if anchor[1] == "m":
+ top -= (len(lines) - 1) * line_spacing / 2.0
+ elif anchor[1] == "d":
+ top -= (len(lines) - 1) * line_spacing
+
+ idx = -1
+ for line in lines:
+ left = xy[0]
+ idx += 1
+ width_difference = max_width - widths[idx]
+
+ # align by align parameter
+ if align in ("left", "justify"):
+ pass
+ elif align == "center":
+ left += width_difference / 2.0
+ elif align == "right":
+ left += width_difference
+ else:
+ msg = 'align must be "left", "center", "right" or "justify"'
+ raise ValueError(msg)
+
+ if (
+ align == "justify"
+ and width_difference != 0
+ and idx != len(lines) - 1
+ ):
+ words = (
+ line.split(" ") if isinstance(line, str) else line.split(b" ")
+ )
+ if len(words) > 1:
+ # align left by anchor
+ if anchor[0] == "m":
+ left -= max_width / 2.0
+ elif anchor[0] == "r":
+ left -= max_width
+
+ word_widths = [
+ self.font.getlength(
+ word,
+ fontmode,
+ self.direction,
+ self.features,
+ self.language,
+ )
+ for word in words
+ ]
+ word_anchor = "l" + anchor[1]
+ width_difference = max_width - sum(word_widths)
+ i = 0
+ for word in words:
+ parts.append(((left, top), word_anchor, word))
+ left += word_widths[i] + width_difference / (len(words) - 1)
+ i += 1
+ top += line_spacing
+ continue
+
+ # align left by anchor
+ if anchor[0] == "m":
+ left -= width_difference / 2.0
+ elif anchor[0] == "r":
+ left -= width_difference
+ parts.append(((left, top), anchor, line))
+ top += line_spacing
+
+ return parts
+
+ def get_bbox(
+ self,
+ xy: tuple[float, float] = (0, 0),
+ anchor: str | None = None,
+ align: str = "left",
+ ) -> tuple[float, float, float, float]:
+ """
+ Returns bounding box (in pixels) of text.
+
+ Use :py:meth:`get_length` to get the offset of following text with 1/64 pixel
+ precision. The bounding box includes extra margins for some fonts, e.g. italics
+ or accents.
+
+ :param xy: The anchor coordinates of the text.
+ :param anchor: The text anchor alignment. Determines the relative location of
+ the anchor to the text. The default alignment is top left,
+ specifically ``la`` for horizontal text and ``lt`` for
+ vertical text. See :ref:`text-anchors` for details.
+ :param align: For multiline text, ``"left"``, ``"center"``, ``"right"`` or
+ ``"justify"`` determines the relative alignment of lines. Use the
+ ``anchor`` parameter to specify the alignment to ``xy``.
+
+ :return: ``(left, top, right, bottom)`` bounding box
+ """
+ bbox: tuple[float, float, float, float] | None = None
+ fontmode = self._get_fontmode()
+ for xy, anchor, line in self._split(xy, anchor, align):
+ bbox_line = self.font.getbbox(
+ line,
+ fontmode,
+ self.direction,
+ self.features,
+ self.language,
+ self.stroke_width,
+ anchor,
+ )
+ bbox_line = (
+ bbox_line[0] + xy[0],
+ bbox_line[1] + xy[1],
+ bbox_line[2] + xy[0],
+ bbox_line[3] + xy[1],
+ )
+ if bbox is None:
+ bbox = bbox_line
+ else:
+ bbox = (
+ min(bbox[0], bbox_line[0]),
+ min(bbox[1], bbox_line[1]),
+ max(bbox[2], bbox_line[2]),
+ max(bbox[3], bbox_line[3]),
+ )
+
+ if bbox is None:
+ return xy[0], xy[1], xy[0], xy[1]
+ return bbox
diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py
index e6a9d8eea..3a4cb81e9 100644
--- a/src/PIL/ImageTk.py
+++ b/src/PIL/ImageTk.py
@@ -28,10 +28,11 @@ from __future__ import annotations
import tkinter
from io import BytesIO
-from typing import TYPE_CHECKING, Any
+from typing import Any
from . import Image, ImageFile
+TYPE_CHECKING = False
if TYPE_CHECKING:
from ._typing import CapsuleType
diff --git a/src/PIL/IptcImagePlugin.py b/src/PIL/IptcImagePlugin.py
index 60ab7c83f..c28f4dcc7 100644
--- a/src/PIL/IptcImagePlugin.py
+++ b/src/PIL/IptcImagePlugin.py
@@ -16,26 +16,16 @@
#
from __future__ import annotations
-from collections.abc import Sequence
from io import BytesIO
from typing import cast
from . import Image, ImageFile
from ._binary import i16be as i16
from ._binary import i32be as i32
-from ._deprecate import deprecate
COMPRESSION = {1: "raw", 5: "jpeg"}
-def __getattr__(name: str) -> bytes:
- if name == "PAD":
- deprecate("IptcImagePlugin.PAD", 12)
- return b"\0\0\0\0"
- msg = f"module '{__name__}' has no attribute '{name}'"
- raise AttributeError(msg)
-
-
#
# Helpers
@@ -44,24 +34,6 @@ def _i(c: bytes) -> int:
return i32((b"\0\0\0\0" + c)[-4:])
-def _i8(c: int | bytes) -> int:
- return c if isinstance(c, int) else c[0]
-
-
-def i(c: bytes) -> int:
- """.. deprecated:: 10.2.0"""
- deprecate("IptcImagePlugin.i", 12)
- return _i(c)
-
-
-def dump(c: Sequence[int | bytes]) -> None:
- """.. deprecated:: 10.2.0"""
- deprecate("IptcImagePlugin.dump", 12)
- for i in c:
- print(f"{_i8(i):02x}", end=" ")
- print()
-
-
##
# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields
# from TIFF and JPEG files, use the getiptcinfo function.
@@ -124,16 +96,18 @@ class IptcImageFile(ImageFile.ImageFile):
# mode
layers = self.info[(3, 60)][0]
component = self.info[(3, 60)][1]
- if (3, 65) in self.info:
- id = self.info[(3, 65)][0] - 1
- else:
- id = 0
if layers == 1 and not component:
self._mode = "L"
- elif layers == 3 and component:
- self._mode = "RGB"[id]
- elif layers == 4 and component:
- self._mode = "CMYK"[id]
+ band = None
+ else:
+ if layers == 3 and component:
+ self._mode = "RGB"
+ elif layers == 4 and component:
+ self._mode = "CMYK"
+ if (3, 65) in self.info:
+ band = self.info[(3, 65)][0] - 1
+ else:
+ band = 0
# size
self._size = self.getint((3, 20)), self.getint((3, 30))
@@ -148,38 +122,44 @@ class IptcImageFile(ImageFile.ImageFile):
# tile
if tag == (8, 10):
self.tile = [
- ImageFile._Tile("iptc", (0, 0) + self.size, offset, compression)
+ ImageFile._Tile("iptc", (0, 0) + self.size, offset, (compression, band))
]
def load(self) -> Image.core.PixelAccess | None:
- if len(self.tile) != 1 or self.tile[0][0] != "iptc":
- return ImageFile.ImageFile.load(self)
+ if self.tile:
+ args = self.tile[0].args
+ assert isinstance(args, tuple)
+ compression, band = args
- offset, compression = self.tile[0][2:]
+ self.fp.seek(self.tile[0].offset)
- self.fp.seek(offset)
-
- # Copy image data to temporary file
- o = BytesIO()
- if compression == "raw":
- # To simplify access to the extracted file,
- # prepend a PPM header
- o.write(b"P5\n%d %d\n255\n" % self.size)
- while True:
- type, size = self.field()
- if type != (8, 10):
- break
- while size > 0:
- s = self.fp.read(min(size, 8192))
- if not s:
+ # Copy image data to temporary file
+ o = BytesIO()
+ if compression == "raw":
+ # To simplify access to the extracted file,
+ # prepend a PPM header
+ o.write(b"P5\n%d %d\n255\n" % self.size)
+ while True:
+ type, size = self.field()
+ if type != (8, 10):
break
- o.write(s)
- size -= len(s)
+ while size > 0:
+ s = self.fp.read(min(size, 8192))
+ if not s:
+ break
+ o.write(s)
+ size -= len(s)
- with Image.open(o) as _im:
- _im.load()
- self.im = _im.im
- return None
+ with Image.open(o) as _im:
+ if band is not None:
+ bands = [Image.new("L", _im.size)] * Image.getmodebands(self.mode)
+ bands[band] = _im
+ _im = Image.merge(self.mode, bands)
+ else:
+ _im.load()
+ self.im = _im.im
+ self.tile = []
+ return ImageFile.ImageFile.load(self)
Image.register_open(IptcImageFile.format, IptcImageFile)
@@ -219,7 +199,7 @@ def getiptcinfo(
# get raw data from the IPTC/NAA tag (PhotoShop tags the data
# as 4-byte integers, so we cannot use the get method...)
try:
- data = im.tag_v2[TiffImagePlugin.IPTC_NAA_CHUNK]
+ data = im.tag_v2._tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
except KeyError:
pass
diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py
index e0f4ecae5..4c85dd4e2 100644
--- a/src/PIL/Jpeg2KImagePlugin.py
+++ b/src/PIL/Jpeg2KImagePlugin.py
@@ -18,11 +18,15 @@ from __future__ import annotations
import io
import os
import struct
-from collections.abc import Callable
-from typing import IO, cast
+from typing import cast
from . import Image, ImageFile, ImagePalette, _binary
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from collections.abc import Callable
+ from typing import IO
+
class BoxReader:
"""
diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py
index db3413a3a..51d4f5e3e 100644
--- a/src/PIL/JpegImagePlugin.py
+++ b/src/PIL/JpegImagePlugin.py
@@ -42,17 +42,18 @@ import subprocess
import sys
import tempfile
import warnings
-from typing import IO, TYPE_CHECKING, Any
from . import Image, ImageFile
from ._binary import i16be as i16
from ._binary import i32be as i32
from ._binary import o8
from ._binary import o16be as o16
-from ._deprecate import deprecate
from .JpegPresets import presets
+TYPE_CHECKING = False
if TYPE_CHECKING:
+ from typing import IO, Any
+
from .MpoImagePlugin import MpoImageFile
#
@@ -192,6 +193,8 @@ def SOF(self: JpegImageFile, marker: int) -> None:
n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n)
self._size = i16(s, 3), i16(s, 1)
+ if self._im is not None and self.size != self.im.size:
+ self._im = None
self.bits = s[0]
if self.bits != 8:
@@ -392,18 +395,12 @@ class JpegImageFile(ImageFile.ImageFile):
self._read_dpi_from_exif()
- def __getattr__(self, name: str) -> Any:
- if name in ("huffman_ac", "huffman_dc"):
- deprecate(name, 12)
- return getattr(self, "_" + name)
- raise AttributeError(name)
-
def __getstate__(self) -> list[Any]:
return super().__getstate__() + [self.layers, self.layer]
def __setstate__(self, state: list[Any]) -> None:
+ self.layers, self.layer = state[6:]
super().__setstate__(state)
- self.layers, self.layer = state[5:]
def load_read(self, read_bytes: int) -> bytes:
"""
@@ -761,8 +758,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
extra = info.get("extra", b"")
MAX_BYTES_IN_MARKER = 65533
- xmp = info.get("xmp")
- if xmp:
+ if xmp := info.get("xmp"):
overhead_len = 29 # b"http://ns.adobe.com/xap/1.0/\x00"
max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len
if len(xmp) > max_data_bytes_in_marker:
@@ -771,8 +767,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
size = o16(2 + overhead_len + len(xmp))
extra += b"\xff\xe1" + size + b"http://ns.adobe.com/xap/1.0/\x00" + xmp
- icc_profile = info.get("icc_profile")
- if icc_profile:
+ if icc_profile := info.get("icc_profile"):
overhead_len = 14 # b"ICC_PROFILE\0" + o8(i) + o8(len(markers))
max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len
markers = []
@@ -831,7 +826,6 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
# in a shot. Guessing on the size, at im.size bytes. (raw pixel size is
# channels*size, this is a value that's been used in a django patch.
# https://github.com/matthewwithanm/django-imagekit/issues/50
- bufsize = 0
if optimize or progressive:
# CMYK can be bigger
if im.mode == "CMYK":
@@ -848,23 +842,13 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
else:
# The EXIF info needs to be written as one block, + APP1, + one spare byte.
# Ensure that our buffer is big enough. Same with the icc_profile block.
- bufsize = max(bufsize, len(exif) + 5, len(extra) + 1)
+ bufsize = max(len(exif) + 5, len(extra) + 1)
ImageFile._save(
im, fp, [ImageFile._Tile("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize
)
-def _save_cjpeg(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
- # ALTERNATIVE: handle JPEGs via the IJG command line utilities.
- tempfile = im._dump()
- subprocess.check_call(["cjpeg", "-outfile", filename, tempfile])
- try:
- os.unlink(tempfile)
- except OSError:
- pass
-
-
##
# Factory for making JPEG and MPO instances
def jpeg_factory(
diff --git a/src/PIL/McIdasImagePlugin.py b/src/PIL/McIdasImagePlugin.py
index b4460a9a5..9a47933b6 100644
--- a/src/PIL/McIdasImagePlugin.py
+++ b/src/PIL/McIdasImagePlugin.py
@@ -44,15 +44,13 @@ class McIdasImageFile(ImageFile.ImageFile):
raise SyntaxError(msg)
self.area_descriptor_raw = s
- self.area_descriptor = w = [0] + list(struct.unpack("!64i", s))
+ self.area_descriptor = w = [0, *struct.unpack("!64i", s)]
# get mode
if w[11] == 1:
mode = rawmode = "L"
elif w[11] == 2:
- # FIXME: add memory map support
- mode = "I"
- rawmode = "I;16B"
+ mode = rawmode = "I;16B"
elif w[11] == 4:
# FIXME: add memory map support
mode = "I"
diff --git a/src/PIL/MpegImagePlugin.py b/src/PIL/MpegImagePlugin.py
index 5aa00d05b..47ebe9d62 100644
--- a/src/PIL/MpegImagePlugin.py
+++ b/src/PIL/MpegImagePlugin.py
@@ -33,11 +33,7 @@ class BitStream:
def peek(self, bits: int) -> int:
while self.bits < bits:
- c = self.next()
- if c < 0:
- self.bits = 0
- continue
- self.bitbuffer = (self.bitbuffer << 8) + c
+ self.bitbuffer = (self.bitbuffer << 8) + self.next()
self.bits += 8
return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1
diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py
index e08f80b6b..b1ae07873 100644
--- a/src/PIL/MpoImagePlugin.py
+++ b/src/PIL/MpoImagePlugin.py
@@ -19,7 +19,6 @@
#
from __future__ import annotations
-import itertools
import os
import struct
from typing import IO, Any, cast
@@ -32,6 +31,7 @@ from . import (
TiffImagePlugin,
)
from ._binary import o32le
+from ._util import DeferredError
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
@@ -46,12 +46,18 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
mpf_offset = 28
offsets: list[int] = []
- for imSequence in itertools.chain([im], append_images):
- for im_frame in ImageSequence.Iterator(imSequence):
+ im_sequences = [im, *append_images]
+ total = sum(getattr(seq, "n_frames", 1) for seq in im_sequences)
+ for im_sequence in im_sequences:
+ for im_frame in ImageSequence.Iterator(im_sequence):
if not offsets:
# APP2 marker
+ ifd_length = 66 + 16 * total
im_frame.encoderinfo["extra"] = (
- b"\xff\xe2" + struct.pack(">H", 6 + 82) + b"MPF\0" + b" " * 82
+ b"\xff\xe2"
+ + struct.pack(">H", 6 + ifd_length)
+ + b"MPF\0"
+ + b" " * ifd_length
)
exif = im_frame.encoderinfo.get("exif")
if isinstance(exif, Image.Exif):
@@ -63,7 +69,9 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
JpegImagePlugin._save(im_frame, fp, filename)
offsets.append(fp.tell())
else:
+ encoderinfo = im_frame._attach_default_encoderinfo(im)
im_frame.save(fp, "JPEG")
+ im_frame.encoderinfo = encoderinfo
offsets.append(fp.tell() - offsets[-1])
ifd = TiffImagePlugin.ImageFileDirectory_v2()
@@ -125,11 +133,15 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
self.readonly = 1
def load_seek(self, pos: int) -> None:
+ if isinstance(self._fp, DeferredError):
+ raise self._fp.ex
self._fp.seek(pos)
def seek(self, frame: int) -> None:
if not self._seek_check(frame):
return
+ if isinstance(self._fp, DeferredError):
+ raise self._fp.ex
self.fp = self._fp
self.offset = self.__mpoffsets[frame]
diff --git a/src/PIL/PSDraw.py b/src/PIL/PSDraw.py
index 02939d26b..7fd4c5c94 100644
--- a/src/PIL/PSDraw.py
+++ b/src/PIL/PSDraw.py
@@ -17,10 +17,13 @@
from __future__ import annotations
import sys
-from typing import IO, TYPE_CHECKING
+from typing import IO
from . import EpsImagePlugin
+TYPE_CHECKING = False
+
+
##
# Simple PostScript graphics interface.
diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py
index b33245376..15f712908 100644
--- a/src/PIL/PalmImagePlugin.py
+++ b/src/PIL/PalmImagePlugin.py
@@ -116,9 +116,6 @@ _COMPRESSION_TYPES = {"none": 0xFF, "rle": 0x01, "scanline": 0x00}
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
if im.mode == "P":
- # we assume this is a color Palm image with the standard colormap,
- # unless the "info" dict has a "custom-colormap" field
-
rawmode = "P"
bpp = 8
version = 1
@@ -172,12 +169,11 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
compression_type = _COMPRESSION_TYPES["none"]
flags = 0
- if im.mode == "P" and "custom-colormap" in im.info:
- assert im.palette is not None
- flags = flags & _FLAGS["custom-colormap"]
- colormapsize = 4 * 256 + 2
- colormapmode = im.palette.mode
- colormap = im.getdata().getpalette()
+ if im.mode == "P":
+ flags |= _FLAGS["custom-colormap"]
+ colormap = im.im.getpalette()
+ colors = len(colormap) // 3
+ colormapsize = 4 * colors + 2
else:
colormapsize = 0
@@ -196,22 +192,11 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
# now write colormap if necessary
- if colormapsize > 0:
- fp.write(o16b(256))
- for i in range(256):
+ if colormapsize:
+ fp.write(o16b(colors))
+ for i in range(colors):
fp.write(o8(i))
- if colormapmode == "RGB":
- fp.write(
- o8(colormap[3 * i])
- + o8(colormap[3 * i + 1])
- + o8(colormap[3 * i + 2])
- )
- elif colormapmode == "RGBA":
- fp.write(
- o8(colormap[4 * i])
- + o8(colormap[4 * i + 1])
- + o8(colormap[4 * i + 2])
- )
+ fp.write(colormap[3 * i : 3 * i + 3])
# now convert data to raw form
ImageFile._save(
diff --git a/src/PIL/PcdImagePlugin.py b/src/PIL/PcdImagePlugin.py
index 3aa249988..296f3775b 100644
--- a/src/PIL/PcdImagePlugin.py
+++ b/src/PIL/PcdImagePlugin.py
@@ -32,7 +32,7 @@ class PcdImageFile(ImageFile.ImageFile):
assert self.fp is not None
self.fp.seek(2048)
- s = self.fp.read(2048)
+ s = self.fp.read(1539)
if not s.startswith(b"PCD_"):
msg = "not a PCD file"
@@ -43,17 +43,21 @@ class PcdImageFile(ImageFile.ImageFile):
if orientation == 1:
self.tile_post_rotate = 90
elif orientation == 3:
- self.tile_post_rotate = -90
+ self.tile_post_rotate = 270
self._mode = "RGB"
- self._size = 768, 512 # FIXME: not correct for rotated images!
- self.tile = [ImageFile._Tile("pcd", (0, 0) + self.size, 96 * 2048)]
+ self._size = (512, 768) if orientation in (1, 3) else (768, 512)
+ self.tile = [ImageFile._Tile("pcd", (0, 0, 768, 512), 96 * 2048)]
+
+ def load_prepare(self) -> None:
+ if self._im is None and self.tile_post_rotate:
+ self.im = Image.core.new(self.mode, (768, 512))
+ ImageFile.ImageFile.load_prepare(self)
def load_end(self) -> None:
if self.tile_post_rotate:
# Handle rotated PCDs
- self.im = self.im.rotate(self.tile_post_rotate)
- self._size = self.im.size
+ self.im = self.rotate(self.tile_post_rotate, expand=True).im
#
diff --git a/src/PIL/PcfFontFile.py b/src/PIL/PcfFontFile.py
index 0d1968b14..a00e9b919 100644
--- a/src/PIL/PcfFontFile.py
+++ b/src/PIL/PcfFontFile.py
@@ -18,7 +18,6 @@
from __future__ import annotations
import io
-from typing import BinaryIO, Callable
from . import FontFile, Image
from ._binary import i8
@@ -27,6 +26,11 @@ from ._binary import i16le as l16
from ._binary import i32be as b32
from ._binary import i32le as l32
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from collections.abc import Callable
+ from typing import BinaryIO
+
# --------------------------------------------------------------------
# declarations
diff --git a/src/PIL/PcxImagePlugin.py b/src/PIL/PcxImagePlugin.py
index 299405ae0..6b16d5385 100644
--- a/src/PIL/PcxImagePlugin.py
+++ b/src/PIL/PcxImagePlugin.py
@@ -39,7 +39,7 @@ logger = logging.getLogger(__name__)
def _accept(prefix: bytes) -> bool:
- return prefix[0] == 10 and prefix[1] in [0, 2, 3, 5]
+ return len(prefix) >= 2 and prefix[0] == 10 and prefix[1] in [0, 2, 3, 5]
##
@@ -54,7 +54,7 @@ class PcxImageFile(ImageFile.ImageFile):
# header
assert self.fp is not None
- s = self.fp.read(128)
+ s = self.fp.read(68)
if not _accept(s):
msg = "not a PCX file"
raise SyntaxError(msg)
@@ -66,6 +66,8 @@ class PcxImageFile(ImageFile.ImageFile):
raise SyntaxError(msg)
logger.debug("BBox: %s %s %s %s", *bbox)
+ offset = self.fp.tell() + 60
+
# format
version = s[1]
bits = s[3]
@@ -102,7 +104,6 @@ class PcxImageFile(ImageFile.ImageFile):
break
if mode == "P":
self.palette = ImagePalette.raw("RGB", s[1:])
- self.fp.seek(128)
elif version == 5 and bits == 8 and planes == 3:
mode = "RGB"
@@ -128,9 +129,7 @@ class PcxImageFile(ImageFile.ImageFile):
bbox = (0, 0) + self.size
logger.debug("size: %sx%s", *self.size)
- self.tile = [
- ImageFile._Tile("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))
- ]
+ self.tile = [ImageFile._Tile("pcx", bbox, offset, (rawmode, planes * stride))]
# --------------------------------------------------------------------
diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py
index e9c20ddc1..5594c7e0f 100644
--- a/src/PIL/PdfImagePlugin.py
+++ b/src/PIL/PdfImagePlugin.py
@@ -27,7 +27,7 @@ import os
import time
from typing import IO, Any
-from . import Image, ImageFile, ImageSequence, PdfParser, __version__, features
+from . import Image, ImageFile, ImageSequence, PdfParser, features
#
# --------------------------------------------------------------------
@@ -221,7 +221,7 @@ def _save(
existing_pdf.start_writing()
existing_pdf.write_header()
- existing_pdf.write_comment(f"created by Pillow {__version__} PDF driver")
+ existing_pdf.write_comment("created by Pillow PDF driver")
#
# pages
diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py
index 41b38ebbf..2c9031469 100644
--- a/src/PIL/PdfParser.py
+++ b/src/PIL/PdfParser.py
@@ -8,7 +8,15 @@ import os
import re
import time
import zlib
-from typing import IO, TYPE_CHECKING, Any, NamedTuple, Union
+from typing import Any, NamedTuple
+
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from typing import IO
+
+ _DictBase = collections.UserDict[str | bytes, Any]
+else:
+ _DictBase = collections.UserDict
# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set
@@ -251,12 +259,6 @@ class PdfArray(list[Any]):
return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]"
-if TYPE_CHECKING:
- _DictBase = collections.UserDict[Union[str, bytes], Any]
-else:
- _DictBase = collections.UserDict
-
-
class PdfDict(_DictBase):
def __setattr__(self, key: str, value: Any) -> None:
if key == "data":
diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py
index 4fc6217e1..d0f22f812 100644
--- a/src/PIL/PngImagePlugin.py
+++ b/src/PIL/PngImagePlugin.py
@@ -38,9 +38,8 @@ import re
import struct
import warnings
import zlib
-from collections.abc import Callable
from enum import IntEnum
-from typing import IO, TYPE_CHECKING, Any, NamedTuple, NoReturn, cast
+from typing import IO, NamedTuple, cast
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
from ._binary import i16be as i16
@@ -48,8 +47,14 @@ from ._binary import i32be as i32
from ._binary import o8
from ._binary import o16be as o16
from ._binary import o32be as o32
+from ._deprecate import deprecate
+from ._util import DeferredError
+TYPE_CHECKING = False
if TYPE_CHECKING:
+ from collections.abc import Callable
+ from typing import Any, NoReturn
+
from . import _imaging
logger = logging.getLogger(__name__)
@@ -869,6 +874,8 @@ class PngImageFile(ImageFile.ImageFile):
def _seek(self, frame: int, rewind: bool = False) -> None:
assert self.png is not None
+ if isinstance(self._fp, DeferredError):
+ raise self._fp.ex
self.dispose: _imaging.ImagingCore | None
dispose_extent = None
@@ -1364,6 +1371,8 @@ def _save(
except KeyError as e:
msg = f"cannot write mode {mode} as PNG"
raise OSError(msg) from e
+ if outmode == "I":
+ deprecate("Saving I mode images as PNG", 13, stacklevel=4)
#
# write minimal PNG file
diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py
index 03afa2d2e..307bc97ff 100644
--- a/src/PIL/PpmImagePlugin.py
+++ b/src/PIL/PpmImagePlugin.py
@@ -47,7 +47,7 @@ MODES = {
def _accept(prefix: bytes) -> bool:
- return prefix.startswith(b"P") and prefix[1] in b"0123456fy"
+ return len(prefix) >= 2 and prefix.startswith(b"P") and prefix[1] in b"0123456fy"
##
@@ -94,8 +94,8 @@ class PpmImageFile(ImageFile.ImageFile):
msg = "Reached EOF while reading header"
raise ValueError(msg)
elif len(token) > 10:
- msg = f"Token too long in file header: {token.decode()}"
- raise ValueError(msg)
+ msg_too_long = b"Token too long in file header: %s" % token
+ raise ValueError(msg_too_long)
return token
def _open(self) -> None:
diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py
index 0aada8a06..f49aaeeb1 100644
--- a/src/PIL/PsdImagePlugin.py
+++ b/src/PIL/PsdImagePlugin.py
@@ -27,6 +27,7 @@ from ._binary import i16be as i16
from ._binary import i32be as i32
from ._binary import si16be as si16
from ._binary import si32be as si32
+from ._util import DeferredError
MODES = {
# (photoshop mode, bits) -> (pil mode, required channels)
@@ -148,6 +149,8 @@ class PsdImageFile(ImageFile.ImageFile):
) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]:
layers = []
if self._layers_position is not None:
+ if isinstance(self._fp, DeferredError):
+ raise self._fp.ex
self._fp.seek(self._layers_position)
_layer_data = io.BytesIO(ImageFile._safe_read(self._fp, self._layers_size))
layers = _layerinfo(_layer_data, self._layers_size)
@@ -167,6 +170,8 @@ class PsdImageFile(ImageFile.ImageFile):
def seek(self, layer: int) -> None:
if not self._seek_check(layer):
return
+ if isinstance(self._fp, DeferredError):
+ raise self._fp.ex
# seek to given layer (1..max)
_, mode, _, tile = self.layers[layer - 1]
diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py
index df552243e..dba5d809f 100644
--- a/src/PIL/QoiImagePlugin.py
+++ b/src/PIL/QoiImagePlugin.py
@@ -8,9 +8,12 @@
from __future__ import annotations
import os
+from typing import IO
from . import Image, ImageFile
from ._binary import i32be as i32
+from ._binary import o8
+from ._binary import o32be as o32
def _accept(prefix: bytes) -> bool:
@@ -51,7 +54,7 @@ class QoiDecoder(ImageFile.PyDecoder):
assert self.fd is not None
self._previously_seen_pixels = {}
- self._add_to_previous_pixels(bytearray((0, 0, 0, 255)))
+ self._previous_pixel = bytearray((0, 0, 0, 255))
data = bytearray()
bands = Image.getmodebands(self.mode)
@@ -110,6 +113,122 @@ class QoiDecoder(ImageFile.PyDecoder):
return -1, 0
+def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
+ if im.mode == "RGB":
+ channels = 3
+ elif im.mode == "RGBA":
+ channels = 4
+ else:
+ msg = "Unsupported QOI image mode"
+ raise ValueError(msg)
+
+ colorspace = 0 if im.encoderinfo.get("colorspace") == "sRGB" else 1
+
+ fp.write(b"qoif")
+ fp.write(o32(im.size[0]))
+ fp.write(o32(im.size[1]))
+ fp.write(o8(channels))
+ fp.write(o8(colorspace))
+
+ ImageFile._save(im, fp, [ImageFile._Tile("qoi", (0, 0) + im.size)])
+
+
+class QoiEncoder(ImageFile.PyEncoder):
+ _pushes_fd = True
+ _previous_pixel: tuple[int, int, int, int] | None = None
+ _previously_seen_pixels: dict[int, tuple[int, int, int, int]] = {}
+ _run = 0
+
+ def _write_run(self) -> bytes:
+ data = o8(0b11000000 | (self._run - 1)) # QOI_OP_RUN
+ self._run = 0
+ return data
+
+ def _delta(self, left: int, right: int) -> int:
+ result = (left - right) & 255
+ if result >= 128:
+ result -= 256
+ return result
+
+ def encode(self, bufsize: int) -> tuple[int, int, bytes]:
+ assert self.im is not None
+
+ self._previously_seen_pixels = {0: (0, 0, 0, 0)}
+ self._previous_pixel = (0, 0, 0, 255)
+
+ data = bytearray()
+ w, h = self.im.size
+ bands = Image.getmodebands(self.mode)
+
+ for y in range(h):
+ for x in range(w):
+ pixel = self.im.getpixel((x, y))
+ if bands == 3:
+ pixel = (*pixel, 255)
+
+ if pixel == self._previous_pixel:
+ self._run += 1
+ if self._run == 62:
+ data += self._write_run()
+ else:
+ if self._run:
+ data += self._write_run()
+
+ r, g, b, a = pixel
+ hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64
+ if self._previously_seen_pixels.get(hash_value) == pixel:
+ data += o8(hash_value) # QOI_OP_INDEX
+ elif self._previous_pixel:
+ self._previously_seen_pixels[hash_value] = pixel
+
+ prev_r, prev_g, prev_b, prev_a = self._previous_pixel
+ if prev_a == a:
+ delta_r = self._delta(r, prev_r)
+ delta_g = self._delta(g, prev_g)
+ delta_b = self._delta(b, prev_b)
+
+ if (
+ -2 <= delta_r < 2
+ and -2 <= delta_g < 2
+ and -2 <= delta_b < 2
+ ):
+ data += o8(
+ 0b01000000
+ | (delta_r + 2) << 4
+ | (delta_g + 2) << 2
+ | (delta_b + 2)
+ ) # QOI_OP_DIFF
+ else:
+ delta_gr = self._delta(delta_r, delta_g)
+ delta_gb = self._delta(delta_b, delta_g)
+ if (
+ -8 <= delta_gr < 8
+ and -32 <= delta_g < 32
+ and -8 <= delta_gb < 8
+ ):
+ data += o8(
+ 0b10000000 | (delta_g + 32)
+ ) # QOI_OP_LUMA
+ data += o8((delta_gr + 8) << 4 | (delta_gb + 8))
+ else:
+ data += o8(0b11111110) # QOI_OP_RGB
+ data += bytes(pixel[:3])
+ else:
+ data += o8(0b11111111) # QOI_OP_RGBA
+ data += bytes(pixel)
+
+ self._previous_pixel = pixel
+
+ if self._run:
+ data += self._write_run()
+ data += bytes((0, 0, 0, 0, 0, 0, 0, 1)) # padding
+
+ return len(data), 0, data
+
+
Image.register_open(QoiImageFile.format, QoiImageFile, _accept)
Image.register_decoder("qoi", QoiDecoder)
Image.register_extension(QoiImageFile.format, ".qoi")
+
+Image.register_save(QoiImageFile.format, _save)
+Image.register_encoder("qoi", QoiEncoder)
diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py
index 44254b7a4..853022150 100644
--- a/src/PIL/SgiImagePlugin.py
+++ b/src/PIL/SgiImagePlugin.py
@@ -82,17 +82,10 @@ class SgiImageFile(ImageFile.ImageFile):
# zsize : channels count
zsize = i16(s, 10)
- # layout
- layout = bpc, dimension, zsize
-
# determine mode from bits/zsize
- rawmode = ""
try:
- rawmode = MODES[layout]
+ rawmode = MODES[(bpc, dimension, zsize)]
except KeyError:
- pass
-
- if rawmode == "":
msg = "Unsupported SGI image mode"
raise ValueError(msg)
@@ -156,24 +149,15 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
# Run-Length Encoding Compression - Unsupported at this time
rle = 0
- # Number of dimensions (x,y,z)
- dim = 3
# X Dimension = width / Y Dimension = height
x, y = im.size
- if im.mode == "L" and y == 1:
- dim = 1
- elif im.mode == "L":
- dim = 2
# Z Dimension: Number of channels
z = len(im.mode)
-
- if dim in {1, 2}:
- z = 1
-
- # assert we've got the right number of bands.
- if len(im.getbands()) != z:
- msg = f"incorrect number of bands in SGI write: {z} vs {len(im.getbands())}"
- raise ValueError(msg)
+ # Number of dimensions (x,y,z)
+ if im.mode == "L":
+ dimension = 1 if y == 1 else 2
+ else:
+ dimension = 3
# Minimum Byte value
pinmin = 0
@@ -188,7 +172,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
fp.write(struct.pack(">h", magic_number))
fp.write(o8(rle))
fp.write(o8(bpc))
- fp.write(struct.pack(">H", dim))
+ fp.write(struct.pack(">H", dimension))
fp.write(struct.pack(">H", x))
fp.write(struct.pack(">H", y))
fp.write(struct.pack(">H", z))
diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py
index b26e1a996..868019e80 100644
--- a/src/PIL/SpiderImagePlugin.py
+++ b/src/PIL/SpiderImagePlugin.py
@@ -37,9 +37,12 @@ from __future__ import annotations
import os
import struct
import sys
-from typing import IO, TYPE_CHECKING, Any, cast
+from typing import IO, Any, cast
from . import Image, ImageFile
+from ._util import DeferredError
+
+TYPE_CHECKING = False
def isInt(f: Any) -> int:
@@ -178,6 +181,8 @@ class SpiderImageFile(ImageFile.ImageFile):
raise EOFError(msg)
if not self._seek_check(frame):
return
+ if isinstance(self._fp, DeferredError):
+ raise self._fp.ex
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
self.fp = self._fp
self.fp.seek(self.stkoffset)
diff --git a/src/PIL/TarIO.py b/src/PIL/TarIO.py
index 779288b1c..86490a496 100644
--- a/src/PIL/TarIO.py
+++ b/src/PIL/TarIO.py
@@ -35,12 +35,16 @@ class TarIO(ContainerIO.ContainerIO[bytes]):
while True:
s = self.fh.read(512)
if len(s) != 512:
+ self.fh.close()
+
msg = "unexpected end of tar file"
raise OSError(msg)
name = s[:100].decode("utf-8")
i = name.find("\0")
if i == 0:
+ self.fh.close()
+
msg = "cannot find subfile"
raise OSError(msg)
if i > 0:
diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py
index 2b471abac..de2ce066e 100644
--- a/src/PIL/TiffImagePlugin.py
+++ b/src/PIL/TiffImagePlugin.py
@@ -47,22 +47,24 @@ import math
import os
import struct
import warnings
-from collections.abc import Iterator, MutableMapping
+from collections.abc import Callable, MutableMapping
from fractions import Fraction
from numbers import Number, Rational
-from typing import IO, TYPE_CHECKING, Any, Callable, NoReturn, cast
+from typing import IO, Any, cast
from . import ExifTags, Image, ImageFile, ImageOps, ImagePalette, TiffTags
from ._binary import i16be as i16
from ._binary import i32be as i32
from ._binary import o8
-from ._deprecate import deprecate
-from ._typing import StrOrBytesPath
-from ._util import is_path
+from ._util import DeferredError, is_path
from .TiffTags import TYPES
+TYPE_CHECKING = False
if TYPE_CHECKING:
- from ._typing import Buffer, IntegralLike
+ from collections.abc import Iterator
+ from typing import NoReturn
+
+ from ._typing import Buffer, IntegralLike, StrOrBytesPath
logger = logging.getLogger(__name__)
@@ -250,6 +252,7 @@ OPEN_INFO = {
(II, 3, (1,), 1, (8,), ()): ("P", "P"),
(MM, 3, (1,), 1, (8,), ()): ("P", "P"),
(II, 3, (1,), 1, (8, 8), (0,)): ("P", "PX"),
+ (MM, 3, (1,), 1, (8, 8), (0,)): ("P", "PX"),
(II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"),
(MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"),
(II, 3, (1,), 2, (8,), ()): ("P", "P;R"),
@@ -283,9 +286,6 @@ PREFIXES = [
b"II\x2b\x00", # BigTIFF with little-endian byte order
]
-if not getattr(Image.core, "libtiff_support_custom_tags", True):
- deprecate("Support for LibTIFF earlier than version 4", 12)
-
def _accept(prefix: bytes) -> bool:
return prefix.startswith(tuple(PREFIXES))
@@ -1178,6 +1178,7 @@ class TiffImageFile(ImageFile.ImageFile):
"""Open the first image in a TIFF file"""
# Header
+ assert self.fp is not None
ifh = self.fp.read(8)
if ifh[2] == 43:
ifh += self.fp.read(8)
@@ -1216,12 +1217,15 @@ class TiffImageFile(ImageFile.ImageFile):
return
self._seek(frame)
if self._im is not None and (
- self.im.size != self._tile_size or self.im.mode != self.mode
+ self.im.size != self._tile_size
+ or self.im.mode != self.mode
+ or self.readonly
):
- # The core image will no longer be used
self._im = None
def _seek(self, frame: int) -> None:
+ if isinstance(self._fp, DeferredError):
+ raise self._fp.ex
self.fp = self._fp
while len(self._frame_pos) <= frame:
@@ -1256,7 +1260,10 @@ class TiffImageFile(ImageFile.ImageFile):
self.fp.seek(self._frame_pos[frame])
self.tag_v2.load(self.fp)
if XMP in self.tag_v2:
- self.info["xmp"] = self.tag_v2[XMP]
+ xmp = self.tag_v2[XMP]
+ if isinstance(xmp, tuple) and len(xmp) == 1:
+ xmp = xmp[0]
+ self.info["xmp"] = xmp
elif "xmp" in self.info:
del self.info["xmp"]
self._reload_exif()
@@ -1338,6 +1345,7 @@ class TiffImageFile(ImageFile.ImageFile):
# To be nice on memory footprint, if there's a
# file descriptor, use that instead of reading
# into a string in python.
+ assert self.fp is not None
try:
fp = hasattr(self.fp, "fileno") and self.fp.fileno()
# flush the file descriptor, prevents error on pypy 2.4+
@@ -1608,6 +1616,10 @@ class TiffImageFile(ImageFile.ImageFile):
raise ValueError(msg)
w = tilewidth
+ if w == xsize and h == ysize and self._planar_configuration != 2:
+ # Every tile covers the image. Only use the last offset
+ offsets = offsets[-1:]
+
for offset in offsets:
if x + w > xsize:
stride = w * sum(bps_tuple) / 8 # bytes per line
@@ -1630,11 +1642,11 @@ class TiffImageFile(ImageFile.ImageFile):
args,
)
)
- x = x + w
+ x += w
if x >= xsize:
x, y = 0, y + h
if y >= ysize:
- x = y = 0
+ y = 0
layer += 1
else:
logger.debug("- unsupported data organization")
@@ -1669,7 +1681,7 @@ SAVE_INFO = {
"PA": ("PA", II, 3, 1, (8, 8), 2),
"I": ("I;32S", II, 1, 2, (32,), None),
"I;16": ("I;16", II, 1, 1, (16,), None),
- "I;16S": ("I;16S", II, 1, 2, (16,), None),
+ "I;16L": ("I;16L", II, 1, 1, (16,), None),
"F": ("F;32F", II, 1, 3, (32,), None),
"RGB": ("RGB", II, 2, 1, (8, 8, 8), None),
"RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0),
@@ -1677,10 +1689,7 @@ SAVE_INFO = {
"CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None),
"YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None),
"LAB": ("LAB", II, 8, 1, (8, 8, 8), None),
- "I;32BS": ("I;32BS", MM, 1, 2, (32,), None),
"I;16B": ("I;16B", MM, 1, 1, (16,), None),
- "I;16BS": ("I;16BS", MM, 1, 2, (16,), None),
- "F;32BF": ("F;32BF", MM, 1, 3, (32,), None),
}
@@ -1926,16 +1935,14 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
# Custom items are supported for int, float, unicode, string and byte
# values. Other types and tuples require a tagtype.
if tag not in TiffTags.LIBTIFF_CORE:
- if not getattr(Image.core, "libtiff_support_custom_tags", False):
- continue
-
if tag in TiffTags.TAGS_V2_GROUPS:
types[tag] = TiffTags.LONG8
elif tag in ifd.tagtype:
types[tag] = ifd.tagtype[tag]
- elif not (isinstance(value, (int, float, str, bytes))):
- continue
- else:
+ elif isinstance(value, (int, float, str, bytes)) or (
+ isinstance(value, tuple)
+ and all(isinstance(v, (int, float, IFDRational)) for v in value)
+ ):
type = TiffTags.lookup(tag).type
if type:
types[tag] = type
@@ -1956,7 +1963,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
# we're storing image byte order. So, if the rawmode
# contains I;16, we need to convert from native to image
# byte order.
- if im.mode in ("I;16B", "I;16"):
+ if im.mode in ("I;16", "I;16B", "I;16L"):
rawmode = "I;16N"
# Pass tags as sorted list so that the tags are set in a fixed order.
@@ -2303,8 +2310,7 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
try:
with AppendingTiffWriter(fp) as tf:
for ims in [im] + append_images:
- if not hasattr(ims, "encoderinfo"):
- ims.encoderinfo = {}
+ encoderinfo = ims._attach_default_encoderinfo(im)
if not hasattr(ims, "encoderconfig"):
ims.encoderconfig = ()
nfr = getattr(ims, "n_frames", 1)
@@ -2314,6 +2320,7 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
ims.load()
_save(ims, tf, filename)
tf.newFrame()
+ ims.encoderinfo = encoderinfo
finally:
im.seek(cur_idx)
diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py
index 86adaa458..761aa3f6b 100644
--- a/src/PIL/TiffTags.py
+++ b/src/PIL/TiffTags.py
@@ -203,6 +203,11 @@ _tags_v2: dict[int, tuple[str, int, int] | tuple[str, int, int, dict[str, int]]]
531: ("YCbCrPositioning", SHORT, 1),
532: ("ReferenceBlackWhite", RATIONAL, 6),
700: ("XMP", BYTE, 0),
+ # Four private SGI tags
+ 32995: ("Matteing", SHORT, 1),
+ 32996: ("DataType", SHORT, 0),
+ 32997: ("ImageDepth", LONG, 1),
+ 32998: ("TileDepth", LONG, 1),
33432: ("Copyright", ASCII, 1),
33723: ("IptcNaaInfo", UNDEFINED, 1),
34377: ("PhotoshopInfo", BYTE, 0),
diff --git a/src/PIL/WalImageFile.py b/src/PIL/WalImageFile.py
index 87e32878b..5494f62e8 100644
--- a/src/PIL/WalImageFile.py
+++ b/src/PIL/WalImageFile.py
@@ -49,8 +49,7 @@ class WalImageFile(ImageFile.ImageFile):
# strings are null-terminated
self.info["name"] = header[:32].split(b"\0", 1)[0]
- next_name = header[56 : 56 + 32].split(b"\0", 1)[0]
- if next_name:
+ if next_name := header[56 : 56 + 32].split(b"\0", 1)[0]:
self.info["next_name"] = next_name
def load(self) -> Image.core.PixelAccess | None:
diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py
index c2dde4431..2847fed20 100644
--- a/src/PIL/WebPImagePlugin.py
+++ b/src/PIL/WebPImagePlugin.py
@@ -1,7 +1,6 @@
from __future__ import annotations
from io import BytesIO
-from typing import IO, Any
from . import Image, ImageFile
@@ -12,6 +11,9 @@ try:
except ImportError:
SUPPORTED = False
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from typing import IO, Any
_VP8_MODES_BY_IDENTIFIER = {
b"VP8 ": "RGB",
@@ -238,7 +240,7 @@ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
cur_idx = im.tell()
try:
for ims in [im] + append_images:
- # Get # of frames in this image
+ # Get number of frames in this image
nfr = getattr(ims, "n_frames", 1)
for idx in range(nfr):
diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py
index 04abd52f0..de714d337 100644
--- a/src/PIL/WmfImagePlugin.py
+++ b/src/PIL/WmfImagePlugin.py
@@ -80,19 +80,18 @@ class WmfStubImageFile(ImageFile.StubImageFile):
format_description = "Windows Metafile"
def _open(self) -> None:
- self._inch = None
-
- # check placable header
- s = self.fp.read(80)
+ # check placeable header
+ s = self.fp.read(44)
if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"):
# placeable windows metafile
# get units per inch
- self._inch = word(s, 14)
- if self._inch == 0:
+ inch = word(s, 14)
+ if inch == 0:
msg = "Invalid inch"
raise ValueError(msg)
+ self._inch: tuple[float, float] = inch, inch
# get bounding box
x0 = short(s, 6)
@@ -103,8 +102,8 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# normalize size to 72 dots per inch
self.info["dpi"] = 72
size = (
- (x1 - x0) * self.info["dpi"] // self._inch,
- (y1 - y0) * self.info["dpi"] // self._inch,
+ (x1 - x0) * self.info["dpi"] // inch,
+ (y1 - y0) * self.info["dpi"] // inch,
)
self.info["wmf_bbox"] = x0, y0, x1, y1
@@ -138,6 +137,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
self.info["dpi"] = xdpi
else:
self.info["dpi"] = xdpi, ydpi
+ self._inch = xdpi, ydpi
else:
msg = "Unsupported file format"
@@ -153,13 +153,17 @@ class WmfStubImageFile(ImageFile.StubImageFile):
def _load(self) -> ImageFile.StubHandler | None:
return _handler
- def load(self, dpi: int | None = None) -> Image.core.PixelAccess | None:
- if dpi is not None and self._inch is not None:
+ def load(
+ self, dpi: float | tuple[float, float] | None = None
+ ) -> Image.core.PixelAccess | None:
+ if dpi is not None:
self.info["dpi"] = dpi
x0, y0, x1, y1 = self.info["wmf_bbox"]
+ if not isinstance(dpi, tuple):
+ dpi = dpi, dpi
self._size = (
- (x1 - x0) * self.info["dpi"] // self._inch,
- (y1 - y0) * self.info["dpi"] // self._inch,
+ int((x1 - x0) * dpi[0] / self._inch[0]),
+ int((y1 - y0) * dpi[1] / self._inch[1]),
)
return super().load()
diff --git a/src/PIL/XpmImagePlugin.py b/src/PIL/XpmImagePlugin.py
index 3c932c41b..3be240fbc 100644
--- a/src/PIL/XpmImagePlugin.py
+++ b/src/PIL/XpmImagePlugin.py
@@ -37,43 +37,36 @@ class XpmImageFile(ImageFile.ImageFile):
format_description = "X11 Pixel Map"
def _open(self) -> None:
+ assert self.fp is not None
if not _accept(self.fp.read(9)):
msg = "not an XPM file"
raise SyntaxError(msg)
# skip forward to next string
while True:
- s = self.fp.readline()
- if not s:
+ line = self.fp.readline()
+ if not line:
msg = "broken XPM file"
raise SyntaxError(msg)
- m = xpm_head.match(s)
+ m = xpm_head.match(line)
if m:
break
self._size = int(m.group(1)), int(m.group(2))
- pal = int(m.group(3))
+ palette_length = int(m.group(3))
bpp = int(m.group(4))
- if pal > 256 or bpp != 1:
- msg = "cannot read this XPM file"
- raise ValueError(msg)
-
#
# load palette description
- palette = [b"\0\0\0"] * 256
+ palette = {}
- for _ in range(pal):
- s = self.fp.readline()
- if s.endswith(b"\r\n"):
- s = s[:-2]
- elif s.endswith((b"\r", b"\n")):
- s = s[:-1]
+ for _ in range(palette_length):
+ line = self.fp.readline().rstrip()
- c = s[1]
- s = s[2:-2].split()
+ c = line[1 : bpp + 1]
+ s = line[bpp + 1 : -2].split()
for i in range(0, len(s), 2):
if s[i] == b"c":
@@ -82,10 +75,11 @@ class XpmImageFile(ImageFile.ImageFile):
if rgb == b"None":
self.info["transparency"] = c
elif rgb.startswith(b"#"):
- # FIXME: handle colour names (see ImagePalette.py)
- rgb = int(rgb[1:], 16)
+ rgb_int = int(rgb[1:], 16)
palette[c] = (
- o8((rgb >> 16) & 255) + o8((rgb >> 8) & 255) + o8(rgb & 255)
+ o8((rgb_int >> 16) & 255)
+ + o8((rgb_int >> 8) & 255)
+ + o8(rgb_int & 255)
)
else:
# unknown colour
@@ -98,10 +92,16 @@ class XpmImageFile(ImageFile.ImageFile):
msg = "cannot read this XPM file"
raise ValueError(msg)
- self._mode = "P"
- self.palette = ImagePalette.raw("RGB", b"".join(palette))
+ args: tuple[int, dict[bytes, bytes] | tuple[bytes, ...]]
+ if palette_length > 256:
+ self._mode = "RGB"
+ args = (bpp, palette)
+ else:
+ self._mode = "P"
+ self.palette = ImagePalette.raw("RGB", b"".join(palette.values()))
+ args = (bpp, tuple(palette.keys()))
- self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), "P")]
+ self.tile = [ImageFile._Tile("xpm", (0, 0) + self.size, self.fp.tell(), args)]
def load_read(self, read_bytes: int) -> bytes:
#
@@ -109,16 +109,48 @@ class XpmImageFile(ImageFile.ImageFile):
xsize, ysize = self.size
+ assert self.fp is not None
s = [self.fp.readline()[1 : xsize + 1].ljust(xsize) for i in range(ysize)]
return b"".join(s)
+class XpmDecoder(ImageFile.PyDecoder):
+ _pulls_fd = True
+
+ def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]:
+ assert self.fd is not None
+
+ data = bytearray()
+ bpp, palette = self.args
+ dest_length = self.state.xsize * self.state.ysize
+ if self.mode == "RGB":
+ dest_length *= 3
+ pixel_header = False
+ while len(data) < dest_length:
+ line = self.fd.readline()
+ if not line:
+ break
+ if line.rstrip() == b"/* pixels */" and not pixel_header:
+ pixel_header = True
+ continue
+ line = b'"'.join(line.split(b'"')[1:-1])
+ for i in range(0, len(line), bpp):
+ key = line[i : i + bpp]
+ if self.mode == "RGB":
+ data += palette[key]
+ else:
+ data += o8(palette.index(key))
+ self.set_as_raw(bytes(data))
+ return -1, 0
+
+
#
# Registry
Image.register_open(XpmImageFile.format, XpmImageFile, _accept)
+Image.register_decoder("xpm", XpmDecoder)
Image.register_extension(XpmImageFile.format, ".xpm")
diff --git a/src/PIL/__init__.py b/src/PIL/__init__.py
index 09546fe63..6e4c23f89 100644
--- a/src/PIL/__init__.py
+++ b/src/PIL/__init__.py
@@ -25,6 +25,7 @@ del _version
_plugins = [
+ "AvifImagePlugin",
"BlpImagePlugin",
"BmpImagePlugin",
"BufrStubImagePlugin",
diff --git a/src/PIL/_avif.pyi b/src/PIL/_avif.pyi
new file mode 100644
index 000000000..e27843e53
--- /dev/null
+++ b/src/PIL/_avif.pyi
@@ -0,0 +1,3 @@
+from typing import Any
+
+def __getattr__(name: str) -> Any: ...
diff --git a/src/PIL/_deprecate.py b/src/PIL/_deprecate.py
index 9f9d8bbc9..616a9aace 100644
--- a/src/PIL/_deprecate.py
+++ b/src/PIL/_deprecate.py
@@ -12,6 +12,7 @@ def deprecate(
*,
action: str | None = None,
plural: bool = False,
+ stacklevel: int = 3,
) -> None:
"""
Deprecations helper.
@@ -45,8 +46,6 @@ def deprecate(
elif when <= int(__version__.split(".")[0]):
msg = f"{deprecated} {is_} deprecated and should be removed."
raise RuntimeError(msg)
- elif when == 12:
- removed = "Pillow 12 (2025-10-15)"
elif when == 13:
removed = "Pillow 13 (2026-10-15)"
else:
@@ -67,5 +66,5 @@ def deprecate(
warnings.warn(
f"{deprecated} {is_} deprecated and will be removed in {removed}{action}",
DeprecationWarning,
- stacklevel=3,
+ stacklevel=stacklevel,
)
diff --git a/src/PIL/_imagingcms.pyi b/src/PIL/_imagingcms.pyi
index ddcf93ab1..4fc0d60ab 100644
--- a/src/PIL/_imagingcms.pyi
+++ b/src/PIL/_imagingcms.pyi
@@ -1,14 +1,14 @@
import datetime
import sys
-from typing import Literal, SupportsFloat, TypedDict
+from typing import Literal, SupportsFloat, TypeAlias, TypedDict
from ._typing import CapsuleType
littlecms_version: str | None
-_Tuple3f = tuple[float, float, float]
-_Tuple2x3f = tuple[_Tuple3f, _Tuple3f]
-_Tuple3x3f = tuple[_Tuple3f, _Tuple3f, _Tuple3f]
+_Tuple3f: TypeAlias = tuple[float, float, float]
+_Tuple2x3f: TypeAlias = tuple[_Tuple3f, _Tuple3f]
+_Tuple3x3f: TypeAlias = tuple[_Tuple3f, _Tuple3f, _Tuple3f]
class _IccMeasurementCondition(TypedDict):
observer: int
diff --git a/src/PIL/_imagingft.pyi b/src/PIL/_imagingft.pyi
index 1cb1429d6..2136810ba 100644
--- a/src/PIL/_imagingft.pyi
+++ b/src/PIL/_imagingft.pyi
@@ -1,4 +1,5 @@
-from typing import Any, Callable
+from collections.abc import Callable
+from typing import Any
from . import ImageFont, _imaging
diff --git a/src/PIL/_typing.py b/src/PIL/_typing.py
index 34a9a81e1..a941f8980 100644
--- a/src/PIL/_typing.py
+++ b/src/PIL/_typing.py
@@ -3,16 +3,17 @@ from __future__ import annotations
import os
import sys
from collections.abc import Sequence
-from typing import TYPE_CHECKING, Any, Protocol, TypeVar, Union
+from typing import Any, Protocol, TypeVar
+TYPE_CHECKING = False
if TYPE_CHECKING:
from numbers import _IntegralLike as IntegralLike
try:
import numpy.typing as npt
- NumpyArray = npt.NDArray[Any] # requires numpy>=1.21
- except (ImportError, AttributeError):
+ NumpyArray = npt.NDArray[Any]
+ except ImportError:
pass
if sys.version_info >= (3, 13):
@@ -25,19 +26,10 @@ if sys.version_info >= (3, 12):
else:
Buffer = Any
-if sys.version_info >= (3, 10):
- from typing import TypeGuard
-else:
- try:
- from typing_extensions import TypeGuard
- except ImportError:
- class TypeGuard: # type: ignore[no-redef]
- def __class_getitem__(cls, item: Any) -> type[bool]:
- return bool
+_Ink = float | tuple[int, ...] | str
-
-Coords = Union[Sequence[float], Sequence[Sequence[float]]]
+Coords = Sequence[float] | Sequence[Sequence[float]]
_T_co = TypeVar("_T_co", covariant=True)
@@ -47,7 +39,7 @@ class SupportsRead(Protocol[_T_co]):
def read(self, length: int = ..., /) -> _T_co: ...
-StrOrBytesPath = Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]
+StrOrBytesPath = str | bytes | os.PathLike[str] | os.PathLike[bytes]
-__all__ = ["Buffer", "IntegralLike", "StrOrBytesPath", "SupportsRead", "TypeGuard"]
+__all__ = ["Buffer", "IntegralLike", "StrOrBytesPath", "SupportsRead"]
diff --git a/src/PIL/_util.py b/src/PIL/_util.py
index 8ef0d36f7..b1fa6a0f3 100644
--- a/src/PIL/_util.py
+++ b/src/PIL/_util.py
@@ -1,9 +1,12 @@
from __future__ import annotations
import os
-from typing import Any, NoReturn
-from ._typing import StrOrBytesPath, TypeGuard
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from typing import Any, NoReturn, TypeGuard
+
+ from ._typing import StrOrBytesPath
def is_path(f: Any) -> TypeGuard[StrOrBytesPath]:
diff --git a/src/PIL/_version.py b/src/PIL/_version.py
index e93c7887b..41cb17a36 100644
--- a/src/PIL/_version.py
+++ b/src/PIL/_version.py
@@ -1,4 +1,4 @@
# Master version for Pillow
from __future__ import annotations
-__version__ = "11.2.0.dev0"
+__version__ = "12.1.0.dev0"
diff --git a/src/PIL/features.py b/src/PIL/features.py
index ae7ea4255..ff32c2510 100644
--- a/src/PIL/features.py
+++ b/src/PIL/features.py
@@ -9,7 +9,6 @@ from typing import IO
import PIL
from . import Image
-from ._deprecate import deprecate
modules = {
"pil": ("PIL._imaging", "PILLOW_VERSION"),
@@ -17,6 +16,7 @@ modules = {
"freetype2": ("PIL._imagingft", "freetype2_version"),
"littlecms2": ("PIL._imagingcms", "littlecms_version"),
"webp": ("PIL._webp", "webpdecoder_version"),
+ "avif": ("PIL._avif", "libavif_version"),
}
@@ -119,10 +119,7 @@ def get_supported_codecs() -> list[str]:
return [f for f in codecs if check_codec(f)]
-features: dict[str, tuple[str, str | bool, str | None]] = {
- "webp_anim": ("PIL._webp", True, None),
- "webp_mux": ("PIL._webp", True, None),
- "transp_webp": ("PIL._webp", True, None),
+features: dict[str, tuple[str, str, str | None]] = {
"raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
"fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"),
"harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"),
@@ -148,12 +145,8 @@ def check_feature(feature: str) -> bool | None:
module, flag, ver = features[feature]
- if isinstance(flag, bool):
- deprecate(f'check_feature("{feature}")', 12)
try:
imported_module = __import__(module, fromlist=["PIL"])
- if isinstance(flag, bool):
- return flag
return getattr(imported_module, flag)
except ModuleNotFoundError:
return None
@@ -183,17 +176,7 @@ def get_supported_features() -> list[str]:
"""
:returns: A list of all supported features.
"""
- supported_features = []
- for f, (module, flag, _) in features.items():
- if flag is True:
- for feature, (feature_module, _) in modules.items():
- if feature_module == module:
- if check_module(feature):
- supported_features.append(f)
- break
- elif check_feature(f):
- supported_features.append(f)
- return supported_features
+ return [f for f in features if check_feature(f)]
def check(feature: str) -> bool | None:
@@ -288,6 +271,7 @@ def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None:
("freetype2", "FREETYPE2"),
("littlecms2", "LITTLECMS2"),
("webp", "WEBP"),
+ ("avif", "AVIF"),
("jpg", "JPEG"),
("jpg_2000", "OPENJPEG (JPEG2000)"),
("zlib", "ZLIB (PNG/ZIP)"),
diff --git a/src/Tk/tkImaging.c b/src/Tk/tkImaging.c
index a36c3e0bd..834634bd7 100644
--- a/src/Tk/tkImaging.c
+++ b/src/Tk/tkImaging.c
@@ -121,15 +121,16 @@ PyImagingPhotoPut(
/* Mode */
- if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) {
+ if (im->mode == IMAGING_MODE_1 || im->mode == IMAGING_MODE_L) {
block.pixelSize = 1;
block.offset[0] = block.offset[1] = block.offset[2] = block.offset[3] = 0;
- } else if (strncmp(im->mode, "RGB", 3) == 0) {
+ } else if (im->mode == IMAGING_MODE_RGB || im->mode == IMAGING_MODE_RGBA ||
+ im->mode == IMAGING_MODE_RGBX || im->mode == IMAGING_MODE_RGBa) {
block.pixelSize = 4;
block.offset[0] = 0;
block.offset[1] = 1;
block.offset[2] = 2;
- if (strcmp(im->mode, "RGBA") == 0) {
+ if (im->mode == IMAGING_MODE_RGBA) {
block.offset[3] = 3; /* alpha (or reserved, under Tk 8.2) */
} else {
block.offset[3] = 0; /* no alpha */
diff --git a/src/_avif.c b/src/_avif.c
new file mode 100644
index 000000000..3585297fe
--- /dev/null
+++ b/src/_avif.c
@@ -0,0 +1,902 @@
+#define PY_SSIZE_T_CLEAN
+
+#include
+#include "avif/avif.h"
+
+// Encoder type
+typedef struct {
+ PyObject_HEAD avifEncoder *encoder;
+ avifImage *image;
+ int first_frame;
+} AvifEncoderObject;
+
+static PyTypeObject AvifEncoder_Type;
+
+// Decoder type
+typedef struct {
+ PyObject_HEAD avifDecoder *decoder;
+ Py_buffer buffer;
+} AvifDecoderObject;
+
+static PyTypeObject AvifDecoder_Type;
+
+static int
+normalize_tiles_log2(int value) {
+ if (value < 0) {
+ return 0;
+ } else if (value > 6) {
+ return 6;
+ } else {
+ return value;
+ }
+}
+
+static PyObject *
+exc_type_for_avif_result(avifResult result) {
+ switch (result) {
+ case AVIF_RESULT_INVALID_EXIF_PAYLOAD:
+ case AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION:
+ return PyExc_ValueError;
+ case AVIF_RESULT_INVALID_FTYP:
+ case AVIF_RESULT_BMFF_PARSE_FAILED:
+ case AVIF_RESULT_TRUNCATED_DATA:
+ case AVIF_RESULT_NO_CONTENT:
+ return PyExc_SyntaxError;
+ default:
+ return PyExc_RuntimeError;
+ }
+}
+
+static uint8_t
+irot_imir_to_exif_orientation(const avifImage *image) {
+ uint8_t axis = image->imir.axis;
+ int imir = image->transformFlags & AVIF_TRANSFORM_IMIR;
+ int irot = image->transformFlags & AVIF_TRANSFORM_IROT;
+ if (irot) {
+ uint8_t angle = image->irot.angle;
+ if (angle == 1) {
+ if (imir) {
+ return axis ? 7 // 90 degrees anti-clockwise then swap left and right.
+ : 5; // 90 degrees anti-clockwise then swap top and bottom.
+ }
+ return 8; // 90 degrees anti-clockwise.
+ }
+ if (angle == 2) {
+ if (imir) {
+ return axis
+ ? 4 // 180 degrees anti-clockwise then swap left and right.
+ : 2; // 180 degrees anti-clockwise then swap top and bottom.
+ }
+ return 3; // 180 degrees anti-clockwise.
+ }
+ if (angle == 3) {
+ if (imir) {
+ return axis
+ ? 5 // 270 degrees anti-clockwise then swap left and right.
+ : 7; // 270 degrees anti-clockwise then swap top and bottom.
+ }
+ return 6; // 270 degrees anti-clockwise.
+ }
+ }
+ if (imir) {
+ return axis ? 2 // Swap left and right.
+ : 4; // Swap top and bottom.
+ }
+ return 1; // Default orientation ("top-left", no-op).
+}
+
+static void
+exif_orientation_to_irot_imir(avifImage *image, int orientation) {
+ // Mapping from Exif orientation as defined in JEITA CP-3451C section 4.6.4.A
+ // Orientation to irot and imir boxes as defined in HEIF ISO/IEC 28002-12:2021
+ // sections 6.5.10 and 6.5.12.
+ switch (orientation) {
+ case 2: // The 0th row is at the visual top of the image, and the 0th column is
+ // the visual right-hand side.
+ image->transformFlags |= AVIF_TRANSFORM_IMIR;
+ image->imir.axis = 1;
+ break;
+ case 3: // The 0th row is at the visual bottom of the image, and the 0th column
+ // is the visual right-hand side.
+ image->transformFlags |= AVIF_TRANSFORM_IROT;
+ image->irot.angle = 2;
+ break;
+ case 4: // The 0th row is at the visual bottom of the image, and the 0th column
+ // is the visual left-hand side.
+ image->transformFlags |= AVIF_TRANSFORM_IMIR;
+ break;
+ case 5: // The 0th row is the visual left-hand side of the image, and the 0th
+ // column is the visual top.
+ image->transformFlags |= AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR;
+ image->irot.angle = 1; // applied before imir according to MIAF spec
+ // ISO/IEC 28002-12:2021 - section 7.3.6.7
+ break;
+ case 6: // The 0th row is the visual right-hand side of the image, and the 0th
+ // column is the visual top.
+ image->transformFlags |= AVIF_TRANSFORM_IROT;
+ image->irot.angle = 3;
+ break;
+ case 7: // The 0th row is the visual right-hand side of the image, and the 0th
+ // column is the visual bottom.
+ image->transformFlags |= AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR;
+ image->irot.angle = 3; // applied before imir according to MIAF spec
+ // ISO/IEC 28002-12:2021 - section 7.3.6.7
+ break;
+ case 8: // The 0th row is the visual left-hand side of the image, and the 0th
+ // column is the visual bottom.
+ image->transformFlags |= AVIF_TRANSFORM_IROT;
+ image->irot.angle = 1;
+ break;
+ }
+}
+
+static int
+_codec_available(const char *name, avifCodecFlags flags) {
+ avifCodecChoice codec = avifCodecChoiceFromName(name);
+ if (codec == AVIF_CODEC_CHOICE_AUTO) {
+ return 0;
+ }
+ const char *codec_name = avifCodecName(codec, flags);
+ return (codec_name == NULL) ? 0 : 1;
+}
+
+PyObject *
+_decoder_codec_available(PyObject *self, PyObject *args) {
+ char *codec_name;
+ if (!PyArg_ParseTuple(args, "s", &codec_name)) {
+ return NULL;
+ }
+ int is_available = _codec_available(codec_name, AVIF_CODEC_FLAG_CAN_DECODE);
+ return PyBool_FromLong(is_available);
+}
+
+PyObject *
+_encoder_codec_available(PyObject *self, PyObject *args) {
+ char *codec_name;
+ if (!PyArg_ParseTuple(args, "s", &codec_name)) {
+ return NULL;
+ }
+ int is_available = _codec_available(codec_name, AVIF_CODEC_FLAG_CAN_ENCODE);
+ return PyBool_FromLong(is_available);
+}
+
+PyObject *
+_codec_versions(PyObject *self, PyObject *args) {
+ char buffer[256];
+ avifCodecVersions(buffer);
+ return PyUnicode_FromString(buffer);
+}
+
+static int
+_add_codec_specific_options(avifEncoder *encoder, PyObject *opts) {
+ Py_ssize_t i, size;
+ PyObject *keyval, *py_key, *py_val;
+ if (!PyTuple_Check(opts)) {
+ PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options");
+ return 1;
+ }
+ size = PyTuple_GET_SIZE(opts);
+
+ for (i = 0; i < size; i++) {
+ keyval = PyTuple_GetItem(opts, i);
+ if (!PyTuple_Check(keyval) || PyTuple_GET_SIZE(keyval) != 2) {
+ PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options");
+ return 1;
+ }
+ py_key = PyTuple_GetItem(keyval, 0);
+ py_val = PyTuple_GetItem(keyval, 1);
+ if (!PyUnicode_Check(py_key) || !PyUnicode_Check(py_val)) {
+ PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options");
+ return 1;
+ }
+ const char *key = PyUnicode_AsUTF8(py_key);
+ const char *val = PyUnicode_AsUTF8(py_val);
+ if (key == NULL || val == NULL) {
+ PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options");
+ return 1;
+ }
+
+ avifResult result = avifEncoderSetCodecSpecificOption(encoder, key, val);
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Setting advanced codec options failed: %s",
+ avifResultToString(result)
+ );
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// Encoder functions
+PyObject *
+AvifEncoderNew(PyObject *self_, PyObject *args) {
+ unsigned int width, height;
+ AvifEncoderObject *self = NULL;
+ avifEncoder *encoder = NULL;
+
+ char *subsampling;
+ int quality;
+ int speed;
+ int exif_orientation;
+ int max_threads;
+ Py_buffer icc_buffer;
+ Py_buffer exif_buffer;
+ Py_buffer xmp_buffer;
+ int alpha_premultiplied;
+ int autotiling;
+ int tile_rows_log2;
+ int tile_cols_log2;
+
+ char *codec;
+ char *range;
+
+ PyObject *advanced;
+ int error = 0;
+
+ if (!PyArg_ParseTuple(
+ args,
+ "(II)siiissiippy*y*iy*O",
+ &width,
+ &height,
+ &subsampling,
+ &quality,
+ &speed,
+ &max_threads,
+ &codec,
+ &range,
+ &tile_rows_log2,
+ &tile_cols_log2,
+ &alpha_premultiplied,
+ &autotiling,
+ &icc_buffer,
+ &exif_buffer,
+ &exif_orientation,
+ &xmp_buffer,
+ &advanced
+ )) {
+ return NULL;
+ }
+
+ // Create a new animation encoder and picture frame
+ avifImage *image = avifImageCreateEmpty();
+ if (image == NULL) {
+ PyErr_SetString(PyExc_ValueError, "Image creation failed");
+ error = 1;
+ goto end;
+ }
+
+ // Set these in advance so any upcoming RGB -> YUV use the proper coefficients
+ if (strcmp(range, "full") == 0) {
+ image->yuvRange = AVIF_RANGE_FULL;
+ } else if (strcmp(range, "limited") == 0) {
+ image->yuvRange = AVIF_RANGE_LIMITED;
+ } else {
+ PyErr_SetString(PyExc_ValueError, "Invalid range");
+ error = 1;
+ goto end;
+ }
+ if (strcmp(subsampling, "4:0:0") == 0) {
+ image->yuvFormat = AVIF_PIXEL_FORMAT_YUV400;
+ } else if (strcmp(subsampling, "4:2:0") == 0) {
+ image->yuvFormat = AVIF_PIXEL_FORMAT_YUV420;
+ } else if (strcmp(subsampling, "4:2:2") == 0) {
+ image->yuvFormat = AVIF_PIXEL_FORMAT_YUV422;
+ } else if (strcmp(subsampling, "4:4:4") == 0) {
+ image->yuvFormat = AVIF_PIXEL_FORMAT_YUV444;
+ } else {
+ PyErr_Format(PyExc_ValueError, "Invalid subsampling: %s", subsampling);
+ error = 1;
+ goto end;
+ }
+
+ // Validate canvas dimensions
+ if (width == 0 || height == 0) {
+ PyErr_SetString(PyExc_ValueError, "invalid canvas dimensions");
+ error = 1;
+ goto end;
+ }
+ image->width = width;
+ image->height = height;
+
+ image->depth = 8;
+ image->alphaPremultiplied = alpha_premultiplied ? AVIF_TRUE : AVIF_FALSE;
+
+ encoder = avifEncoderCreate();
+ if (!encoder) {
+ PyErr_SetString(PyExc_MemoryError, "Can't allocate encoder");
+ error = 1;
+ goto end;
+ }
+
+ int is_aom_encode = strcmp(codec, "aom") == 0 ||
+ (strcmp(codec, "auto") == 0 &&
+ _codec_available("aom", AVIF_CODEC_FLAG_CAN_ENCODE));
+ encoder->maxThreads = is_aom_encode && max_threads > 64 ? 64 : max_threads;
+
+ encoder->quality = quality;
+
+ if (strcmp(codec, "auto") == 0) {
+ encoder->codecChoice = AVIF_CODEC_CHOICE_AUTO;
+ } else {
+ encoder->codecChoice = avifCodecChoiceFromName(codec);
+ }
+ if (speed < AVIF_SPEED_SLOWEST) {
+ speed = AVIF_SPEED_SLOWEST;
+ } else if (speed > AVIF_SPEED_FASTEST) {
+ speed = AVIF_SPEED_FASTEST;
+ }
+ encoder->speed = speed;
+ encoder->timescale = (uint64_t)1000;
+
+ encoder->autoTiling = autotiling ? AVIF_TRUE : AVIF_FALSE;
+ if (!autotiling) {
+ encoder->tileRowsLog2 = normalize_tiles_log2(tile_rows_log2);
+ encoder->tileColsLog2 = normalize_tiles_log2(tile_cols_log2);
+ }
+
+ if (advanced != Py_None && _add_codec_specific_options(encoder, advanced)) {
+ error = 1;
+ goto end;
+ }
+
+ self = PyObject_New(AvifEncoderObject, &AvifEncoder_Type);
+ if (!self) {
+ PyErr_SetString(PyExc_RuntimeError, "could not create encoder object");
+ error = 1;
+ goto end;
+ }
+ self->first_frame = 1;
+
+ avifResult result;
+ if (icc_buffer.len) {
+ result = avifImageSetProfileICC(image, icc_buffer.buf, icc_buffer.len);
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Setting ICC profile failed: %s",
+ avifResultToString(result)
+ );
+ error = 1;
+ goto end;
+ }
+ // colorPrimaries and transferCharacteristics are ignored when an ICC
+ // profile is present, so set them to UNSPECIFIED.
+ image->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED;
+ image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED;
+ } else {
+ image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
+ image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
+ }
+ image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601;
+
+ if (exif_buffer.len) {
+ result = avifImageSetMetadataExif(image, exif_buffer.buf, exif_buffer.len);
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Setting EXIF data failed: %s",
+ avifResultToString(result)
+ );
+ error = 1;
+ goto end;
+ }
+ }
+
+ if (xmp_buffer.len) {
+ result = avifImageSetMetadataXMP(image, xmp_buffer.buf, xmp_buffer.len);
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Setting XMP data failed: %s",
+ avifResultToString(result)
+ );
+ error = 1;
+ goto end;
+ }
+ }
+
+ if (exif_orientation > 1) {
+ exif_orientation_to_irot_imir(image, exif_orientation);
+ }
+
+ self->image = image;
+ self->encoder = encoder;
+
+end:
+ PyBuffer_Release(&icc_buffer);
+ PyBuffer_Release(&exif_buffer);
+ PyBuffer_Release(&xmp_buffer);
+
+ if (error) {
+ if (image) {
+ avifImageDestroy(image);
+ }
+ if (encoder) {
+ avifEncoderDestroy(encoder);
+ }
+ if (self) {
+ PyObject_Del(self);
+ }
+ return NULL;
+ }
+
+ return (PyObject *)self;
+}
+
+PyObject *
+_encoder_dealloc(AvifEncoderObject *self) {
+ if (self->encoder) {
+ avifEncoderDestroy(self->encoder);
+ }
+ if (self->image) {
+ avifImageDestroy(self->image);
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject *
+_encoder_add(AvifEncoderObject *self, PyObject *args) {
+ uint8_t *rgb_bytes;
+ Py_ssize_t size;
+ unsigned int duration;
+ unsigned int width;
+ unsigned int height;
+ char *mode;
+ unsigned int is_single_frame;
+ int error = 0;
+
+ avifRGBImage rgb;
+ avifResult result;
+
+ avifEncoder *encoder = self->encoder;
+ avifImage *image = self->image;
+ avifImage *frame = NULL;
+
+ if (!PyArg_ParseTuple(
+ args,
+ "y#I(II)sp",
+ (char **)&rgb_bytes,
+ &size,
+ &duration,
+ &width,
+ &height,
+ &mode,
+ &is_single_frame
+ )) {
+ return NULL;
+ }
+
+ if (image->width != width || image->height != height) {
+ PyErr_Format(
+ PyExc_ValueError,
+ "Image sequence dimensions mismatch, %ux%u != %ux%u",
+ image->width,
+ image->height,
+ width,
+ height
+ );
+ return NULL;
+ }
+
+ if (self->first_frame) {
+ // If we don't have an image populated with yuv planes, this is the first frame
+ frame = image;
+ } else {
+ frame = avifImageCreateEmpty();
+ if (image == NULL) {
+ PyErr_SetString(PyExc_ValueError, "Image creation failed");
+ return NULL;
+ }
+
+ frame->width = width;
+ frame->height = height;
+ frame->colorPrimaries = image->colorPrimaries;
+ frame->transferCharacteristics = image->transferCharacteristics;
+ frame->matrixCoefficients = image->matrixCoefficients;
+ frame->yuvRange = image->yuvRange;
+ frame->yuvFormat = image->yuvFormat;
+ frame->depth = image->depth;
+ frame->alphaPremultiplied = image->alphaPremultiplied;
+ }
+
+ avifRGBImageSetDefaults(&rgb, frame);
+
+ if (strcmp(mode, "RGBA") == 0) {
+ rgb.format = AVIF_RGB_FORMAT_RGBA;
+ } else {
+ rgb.format = AVIF_RGB_FORMAT_RGB;
+ }
+
+ result = avifRGBImageAllocatePixels(&rgb);
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Pixel allocation failed: %s",
+ avifResultToString(result)
+ );
+ error = 1;
+ goto end;
+ }
+
+ if (rgb.rowBytes * rgb.height != size) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "rgb data has incorrect size: %u * %u (%u) != %u",
+ rgb.rowBytes,
+ rgb.height,
+ rgb.rowBytes * rgb.height,
+ size
+ );
+ error = 1;
+ goto end;
+ }
+
+ // rgb.pixels is safe for writes
+ memcpy(rgb.pixels, rgb_bytes, size);
+
+ Py_BEGIN_ALLOW_THREADS;
+ result = avifImageRGBToYUV(frame, &rgb);
+ Py_END_ALLOW_THREADS;
+
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Conversion to YUV failed: %s",
+ avifResultToString(result)
+ );
+ error = 1;
+ goto end;
+ }
+
+ uint32_t addImageFlags =
+ is_single_frame ? AVIF_ADD_IMAGE_FLAG_SINGLE : AVIF_ADD_IMAGE_FLAG_NONE;
+
+ Py_BEGIN_ALLOW_THREADS;
+ result = avifEncoderAddImage(encoder, frame, duration, addImageFlags);
+ Py_END_ALLOW_THREADS;
+
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Failed to encode image: %s",
+ avifResultToString(result)
+ );
+ error = 1;
+ goto end;
+ }
+
+end:
+ avifRGBImageFreePixels(&rgb);
+ if (!self->first_frame) {
+ avifImageDestroy(frame);
+ }
+
+ if (error) {
+ return NULL;
+ }
+ self->first_frame = 0;
+ Py_RETURN_NONE;
+}
+
+PyObject *
+_encoder_finish(AvifEncoderObject *self) {
+ avifEncoder *encoder = self->encoder;
+
+ avifRWData raw = AVIF_DATA_EMPTY;
+ avifResult result;
+ PyObject *ret = NULL;
+
+ Py_BEGIN_ALLOW_THREADS;
+ result = avifEncoderFinish(encoder, &raw);
+ Py_END_ALLOW_THREADS;
+
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Failed to finish encoding: %s",
+ avifResultToString(result)
+ );
+ avifRWDataFree(&raw);
+ return NULL;
+ }
+
+ ret = PyBytes_FromStringAndSize((char *)raw.data, raw.size);
+
+ avifRWDataFree(&raw);
+
+ return ret;
+}
+
+// Decoder functions
+PyObject *
+AvifDecoderNew(PyObject *self_, PyObject *args) {
+ Py_buffer buffer;
+ AvifDecoderObject *self = NULL;
+ avifDecoder *decoder;
+
+ char *codec_str;
+ avifCodecChoice codec;
+ int max_threads;
+
+ avifResult result;
+
+ if (!PyArg_ParseTuple(args, "y*si", &buffer, &codec_str, &max_threads)) {
+ return NULL;
+ }
+
+ if (strcmp(codec_str, "auto") == 0) {
+ codec = AVIF_CODEC_CHOICE_AUTO;
+ } else {
+ codec = avifCodecChoiceFromName(codec_str);
+ }
+
+ self = PyObject_New(AvifDecoderObject, &AvifDecoder_Type);
+ if (!self) {
+ PyErr_SetString(PyExc_RuntimeError, "could not create decoder object");
+ PyBuffer_Release(&buffer);
+ return NULL;
+ }
+
+ decoder = avifDecoderCreate();
+ if (!decoder) {
+ PyErr_SetString(PyExc_MemoryError, "Can't allocate decoder");
+ PyBuffer_Release(&buffer);
+ PyObject_Del(self);
+ return NULL;
+ }
+ decoder->maxThreads = max_threads;
+ // Turn off libavif's 'clap' (clean aperture) property validation.
+ decoder->strictFlags &= ~AVIF_STRICT_CLAP_VALID;
+ // Allow the PixelInformationProperty ('pixi') to be missing in AV1 image
+ // items. libheif v1.11.0 and older does not add the 'pixi' item property to
+ // AV1 image items.
+ decoder->strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED;
+ decoder->codecChoice = codec;
+
+ result = avifDecoderSetIOMemory(decoder, buffer.buf, buffer.len);
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Setting IO memory failed: %s",
+ avifResultToString(result)
+ );
+ avifDecoderDestroy(decoder);
+ PyBuffer_Release(&buffer);
+ PyObject_Del(self);
+ return NULL;
+ }
+
+ result = avifDecoderParse(decoder);
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Failed to decode image: %s",
+ avifResultToString(result)
+ );
+ avifDecoderDestroy(decoder);
+ PyBuffer_Release(&buffer);
+ PyObject_Del(self);
+ return NULL;
+ }
+
+ self->decoder = decoder;
+ self->buffer = buffer;
+
+ return (PyObject *)self;
+}
+
+PyObject *
+_decoder_dealloc(AvifDecoderObject *self) {
+ if (self->decoder) {
+ avifDecoderDestroy(self->decoder);
+ }
+ PyBuffer_Release(&self->buffer);
+ Py_RETURN_NONE;
+}
+
+PyObject *
+_decoder_get_info(AvifDecoderObject *self) {
+ avifDecoder *decoder = self->decoder;
+ avifImage *image = decoder->image;
+
+ PyObject *icc = NULL;
+ PyObject *exif = NULL;
+ PyObject *xmp = NULL;
+ PyObject *ret = NULL;
+
+ if (image->xmp.size) {
+ xmp = PyBytes_FromStringAndSize((const char *)image->xmp.data, image->xmp.size);
+ }
+
+ if (image->exif.size) {
+ exif =
+ PyBytes_FromStringAndSize((const char *)image->exif.data, image->exif.size);
+ }
+
+ if (image->icc.size) {
+ icc = PyBytes_FromStringAndSize((const char *)image->icc.data, image->icc.size);
+ }
+
+ ret = Py_BuildValue(
+ "(II)IsSSIS",
+ image->width,
+ image->height,
+ decoder->imageCount,
+ decoder->alphaPresent ? "RGBA" : "RGB",
+ NULL == icc ? Py_None : icc,
+ NULL == exif ? Py_None : exif,
+ irot_imir_to_exif_orientation(image),
+ NULL == xmp ? Py_None : xmp
+ );
+
+ Py_XDECREF(xmp);
+ Py_XDECREF(exif);
+ Py_XDECREF(icc);
+
+ return ret;
+}
+
+PyObject *
+_decoder_get_frame(AvifDecoderObject *self, PyObject *args) {
+ PyObject *bytes;
+ PyObject *ret;
+ Py_ssize_t size;
+ avifResult result;
+ avifRGBImage rgb;
+ avifDecoder *decoder;
+ avifImage *image;
+ uint32_t frame_index;
+
+ decoder = self->decoder;
+
+ if (!PyArg_ParseTuple(args, "I", &frame_index)) {
+ return NULL;
+ }
+
+ result = avifDecoderNthImage(decoder, frame_index);
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Failed to decode frame %u: %s",
+ frame_index,
+ avifResultToString(result)
+ );
+ return NULL;
+ }
+
+ image = decoder->image;
+
+ avifRGBImageSetDefaults(&rgb, image);
+
+ rgb.depth = 8;
+ rgb.format = decoder->alphaPresent ? AVIF_RGB_FORMAT_RGBA : AVIF_RGB_FORMAT_RGB;
+
+ result = avifRGBImageAllocatePixels(&rgb);
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Pixel allocation failed: %s",
+ avifResultToString(result)
+ );
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS;
+ result = avifImageYUVToRGB(image, &rgb);
+ Py_END_ALLOW_THREADS;
+
+ if (result != AVIF_RESULT_OK) {
+ PyErr_Format(
+ exc_type_for_avif_result(result),
+ "Conversion from YUV failed: %s",
+ avifResultToString(result)
+ );
+ avifRGBImageFreePixels(&rgb);
+ return NULL;
+ }
+
+ if (rgb.height > PY_SSIZE_T_MAX / rgb.rowBytes) {
+ PyErr_SetString(PyExc_MemoryError, "Integer overflow in pixel size");
+ return NULL;
+ }
+
+ size = rgb.rowBytes * rgb.height;
+
+ bytes = PyBytes_FromStringAndSize((char *)rgb.pixels, size);
+ avifRGBImageFreePixels(&rgb);
+
+ ret = Py_BuildValue(
+ "SKKK",
+ bytes,
+ decoder->timescale,
+ decoder->imageTiming.ptsInTimescales,
+ decoder->imageTiming.durationInTimescales
+ );
+
+ Py_DECREF(bytes);
+
+ return ret;
+}
+
+/* -------------------------------------------------------------------- */
+/* Type Definitions */
+/* -------------------------------------------------------------------- */
+
+// AvifEncoder methods
+static struct PyMethodDef _encoder_methods[] = {
+ {"add", (PyCFunction)_encoder_add, METH_VARARGS},
+ {"finish", (PyCFunction)_encoder_finish, METH_NOARGS},
+ {NULL, NULL} /* sentinel */
+};
+
+// AvifEncoder type definition
+static PyTypeObject AvifEncoder_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0).tp_name = "AvifEncoder",
+ .tp_basicsize = sizeof(AvifEncoderObject),
+ .tp_dealloc = (destructor)_encoder_dealloc,
+ .tp_methods = _encoder_methods,
+};
+
+// AvifDecoder methods
+static struct PyMethodDef _decoder_methods[] = {
+ {"get_info", (PyCFunction)_decoder_get_info, METH_NOARGS},
+ {"get_frame", (PyCFunction)_decoder_get_frame, METH_VARARGS},
+ {NULL, NULL} /* sentinel */
+};
+
+// AvifDecoder type definition
+static PyTypeObject AvifDecoder_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0).tp_name = "AvifDecoder",
+ .tp_basicsize = sizeof(AvifDecoderObject),
+ .tp_dealloc = (destructor)_decoder_dealloc,
+ .tp_methods = _decoder_methods,
+};
+
+/* -------------------------------------------------------------------- */
+/* Module Setup */
+/* -------------------------------------------------------------------- */
+
+static PyMethodDef avifMethods[] = {
+ {"AvifDecoder", AvifDecoderNew, METH_VARARGS},
+ {"AvifEncoder", AvifEncoderNew, METH_VARARGS},
+ {"decoder_codec_available", _decoder_codec_available, METH_VARARGS},
+ {"encoder_codec_available", _encoder_codec_available, METH_VARARGS},
+ {"codec_versions", _codec_versions, METH_NOARGS},
+ {NULL, NULL}
+};
+
+static int
+setup_module(PyObject *m) {
+ if (PyType_Ready(&AvifDecoder_Type) < 0 || PyType_Ready(&AvifEncoder_Type) < 0) {
+ return -1;
+ }
+
+ PyObject *d = PyModule_GetDict(m);
+ PyObject *v = PyUnicode_FromString(avifVersion());
+ PyDict_SetItemString(d, "libavif_version", v ? v : Py_None);
+ Py_XDECREF(v);
+
+ return 0;
+}
+
+static PyModuleDef_Slot slots[] = {
+ {Py_mod_exec, setup_module},
+#ifdef Py_GIL_DISABLED
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+#endif
+ {0, NULL}
+};
+
+PyMODINIT_FUNC
+PyInit__avif(void) {
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "_avif",
+ .m_methods = avifMethods,
+ .m_slots = slots
+ };
+
+ return PyModuleDef_Init(&module_def);
+}
diff --git a/src/_imaging.c b/src/_imaging.c
index fa38dcc05..41af72568 100644
--- a/src/_imaging.c
+++ b/src/_imaging.c
@@ -230,6 +230,96 @@ PyImaging_GetBuffer(PyObject *buffer, Py_buffer *view) {
return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE);
}
+/* -------------------------------------------------------------------- */
+/* Arrow HANDLING */
+/* -------------------------------------------------------------------- */
+
+PyObject *
+ArrowError(int err) {
+ if (err == IMAGING_CODEC_MEMORY) {
+ return ImagingError_MemoryError();
+ }
+ if (err == IMAGING_ARROW_INCOMPATIBLE_MODE) {
+ return ImagingError_ValueError("Incompatible Pillow mode for Arrow array");
+ }
+ if (err == IMAGING_ARROW_MEMORY_LAYOUT) {
+ return ImagingError_ValueError(
+ "Image is in multiple array blocks, use imaging_new_block for zero copy"
+ );
+ }
+ return ImagingError_ValueError("Unknown error");
+}
+
+void
+ReleaseArrowSchemaPyCapsule(PyObject *capsule) {
+ struct ArrowSchema *schema =
+ (struct ArrowSchema *)PyCapsule_GetPointer(capsule, "arrow_schema");
+ if (schema->release != NULL) {
+ schema->release(schema);
+ }
+ free(schema);
+}
+
+PyObject *
+ExportArrowSchemaPyCapsule(ImagingObject *self) {
+ struct ArrowSchema *schema =
+ (struct ArrowSchema *)calloc(1, sizeof(struct ArrowSchema));
+ int err = export_imaging_schema(self->image, schema);
+ if (err == 0) {
+ return PyCapsule_New(schema, "arrow_schema", ReleaseArrowSchemaPyCapsule);
+ }
+ free(schema);
+ return ArrowError(err);
+}
+
+void
+ReleaseArrowArrayPyCapsule(PyObject *capsule) {
+ struct ArrowArray *array =
+ (struct ArrowArray *)PyCapsule_GetPointer(capsule, "arrow_array");
+ if (array->release != NULL) {
+ array->release(array);
+ }
+ free(array);
+}
+
+PyObject *
+ExportArrowArrayPyCapsule(ImagingObject *self) {
+ struct ArrowArray *array =
+ (struct ArrowArray *)calloc(1, sizeof(struct ArrowArray));
+ int err = export_imaging_array(self->image, array);
+ if (err == 0) {
+ return PyCapsule_New(array, "arrow_array", ReleaseArrowArrayPyCapsule);
+ }
+ free(array);
+ return ArrowError(err);
+}
+
+static PyObject *
+_new_arrow(PyObject *self, PyObject *args) {
+ char *mode;
+ ModeID mode_id;
+ int xsize, ysize;
+ PyObject *schema_capsule, *array_capsule;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(
+ args, "s(ii)OO", &mode, &xsize, &ysize, &schema_capsule, &array_capsule
+ )) {
+ return NULL;
+ }
+
+ mode_id = findModeID(mode);
+
+ // ImagingBorrowArrow is responsible for retaining the array_capsule
+ ret = PyImagingNew(
+ ImagingNewArrow(mode_id, xsize, ysize, schema_capsule, array_capsule)
+ );
+ if (!ret) {
+ return ImagingError_ValueError("Invalid Arrow array mode or size mismatch");
+ }
+ return ret;
+}
+
/* -------------------------------------------------------------------- */
/* EXCEPTION REROUTING */
/* -------------------------------------------------------------------- */
@@ -251,12 +341,6 @@ static const char *no_palette = "image has no palette";
static const char *readonly = "image is readonly";
/* static const char* no_content = "image has no content"; */
-void *
-ImagingError_OSError(void) {
- PyErr_SetString(PyExc_OSError, "error when accessing file");
- return NULL;
-}
-
void *
ImagingError_MemoryError(void) {
return PyErr_NoMemory();
@@ -282,17 +366,12 @@ ImagingError_ValueError(const char *message) {
return NULL;
}
-void
-ImagingError_Clear(void) {
- PyErr_Clear();
-}
-
/* -------------------------------------------------------------------- */
/* HELPERS */
/* -------------------------------------------------------------------- */
static int
-getbands(const char *mode) {
+getbands(const ModeID mode) {
Imaging im;
int bands;
@@ -586,7 +665,7 @@ getink(PyObject *color, Imaging im, char *ink) {
memcpy(ink, &ftmp, sizeof(ftmp));
return ink;
case IMAGING_TYPE_SPECIAL:
- if (strncmp(im->mode, "I;16", 4) == 0) {
+ if (isModeI16(im->mode)) {
ink[0] = (UINT8)r;
ink[1] = (UINT8)(r >> 8);
ink[2] = ink[3] = 0;
@@ -605,30 +684,6 @@ getink(PyObject *color, Imaging im, char *ink) {
} else if (!PyArg_ParseTuple(color, "iiL", &b, &g, &r)) {
return NULL;
}
- if (!strcmp(im->mode, "BGR;15")) {
- UINT16 v = ((((UINT16)r) << 7) & 0x7c00) +
- ((((UINT16)g) << 2) & 0x03e0) +
- ((((UINT16)b) >> 3) & 0x001f);
-
- ink[0] = (UINT8)v;
- ink[1] = (UINT8)(v >> 8);
- ink[2] = ink[3] = 0;
- return ink;
- } else if (!strcmp(im->mode, "BGR;16")) {
- UINT16 v = ((((UINT16)r) << 8) & 0xf800) +
- ((((UINT16)g) << 3) & 0x07e0) +
- ((((UINT16)b) >> 3) & 0x001f);
- ink[0] = (UINT8)v;
- ink[1] = (UINT8)(v >> 8);
- ink[2] = ink[3] = 0;
- return ink;
- } else if (!strcmp(im->mode, "BGR;24")) {
- ink[0] = (UINT8)b;
- ink[1] = (UINT8)g;
- ink[2] = (UINT8)r;
- ink[3] = 0;
- return ink;
- }
}
}
@@ -642,7 +697,7 @@ getink(PyObject *color, Imaging im, char *ink) {
static PyObject *
_fill(PyObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
int xsize, ysize;
PyObject *color;
char buffer[4];
@@ -651,10 +706,12 @@ _fill(PyObject *self, PyObject *args) {
xsize = ysize = 256;
color = NULL;
- if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color)) {
+ if (!PyArg_ParseTuple(args, "s|(ii)O", &mode_name, &xsize, &ysize, &color)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+
im = ImagingNewDirty(mode, xsize, ysize);
if (!im) {
return NULL;
@@ -675,47 +732,55 @@ _fill(PyObject *self, PyObject *args) {
static PyObject *
_new(PyObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
int xsize, ysize;
- if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) {
+ if (!PyArg_ParseTuple(args, "s(ii)", &mode_name, &xsize, &ysize)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+
return PyImagingNew(ImagingNew(mode, xsize, ysize));
}
static PyObject *
_new_block(PyObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
int xsize, ysize;
- if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) {
+ if (!PyArg_ParseTuple(args, "s(ii)", &mode_name, &xsize, &ysize)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+
return PyImagingNew(ImagingNewBlock(mode, xsize, ysize));
}
static PyObject *
_linear_gradient(PyObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
- if (!PyArg_ParseTuple(args, "s", &mode)) {
+ if (!PyArg_ParseTuple(args, "s", &mode_name)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+
return PyImagingNew(ImagingFillLinearGradient(mode));
}
static PyObject *
_radial_gradient(PyObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
- if (!PyArg_ParseTuple(args, "s", &mode)) {
+ if (!PyArg_ParseTuple(args, "s", &mode_name)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+
return PyImagingNew(ImagingFillRadialGradient(mode));
}
@@ -855,7 +920,7 @@ _prepare_lut_table(PyObject *table, Py_ssize_t table_size) {
static PyObject *
_color_lut_3d(ImagingObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
int filter;
int table_channels;
int size1D, size2D, size3D;
@@ -867,7 +932,7 @@ _color_lut_3d(ImagingObject *self, PyObject *args) {
if (!PyArg_ParseTuple(
args,
"sii(iii)O:color_lut_3d",
- &mode,
+ &mode_name,
&filter,
&table_channels,
&size1D,
@@ -878,6 +943,8 @@ _color_lut_3d(ImagingObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+
/* actually, it is trilinear */
if (filter != IMAGING_TRANSFORM_BILINEAR) {
PyErr_SetString(PyExc_ValueError, "Only LINEAR filter is supported.");
@@ -924,11 +991,11 @@ _color_lut_3d(ImagingObject *self, PyObject *args) {
static PyObject *
_convert(ImagingObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
int dither = 0;
ImagingObject *paletteimage = NULL;
- if (!PyArg_ParseTuple(args, "s|iO", &mode, &dither, &paletteimage)) {
+ if (!PyArg_ParseTuple(args, "s|iO", &mode_name, &dither, &paletteimage)) {
return NULL;
}
if (paletteimage != NULL) {
@@ -945,6 +1012,8 @@ _convert(ImagingObject *self, PyObject *args) {
}
}
+ const ModeID mode = findModeID(mode_name);
+
return PyImagingNew(ImagingConvert(
self->image, mode, paletteimage ? paletteimage->image->palette : NULL, dither
));
@@ -969,14 +1038,14 @@ _convert2(ImagingObject *self, PyObject *args) {
static PyObject *
_convert_matrix(ImagingObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
float m[12];
- if (!PyArg_ParseTuple(args, "s(ffff)", &mode, m + 0, m + 1, m + 2, m + 3)) {
+ if (!PyArg_ParseTuple(args, "s(ffff)", &mode_name, m + 0, m + 1, m + 2, m + 3)) {
PyErr_Clear();
if (!PyArg_ParseTuple(
args,
"s(ffffffffffff)",
- &mode,
+ &mode_name,
m + 0,
m + 1,
m + 2,
@@ -994,18 +1063,22 @@ _convert_matrix(ImagingObject *self, PyObject *args) {
}
}
+ const ModeID mode = findModeID(mode_name);
+
return PyImagingNew(ImagingConvertMatrix(self->image, mode, m));
}
static PyObject *
_convert_transparent(ImagingObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
int r, g, b;
- if (PyArg_ParseTuple(args, "s(iii)", &mode, &r, &g, &b)) {
+ if (PyArg_ParseTuple(args, "s(iii)", &mode_name, &r, &g, &b)) {
+ const ModeID mode = findModeID(mode_name);
return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, g, b));
}
PyErr_Clear();
- if (PyArg_ParseTuple(args, "si", &mode, &r)) {
+ if (PyArg_ParseTuple(args, "si", &mode_name, &r)) {
+ const ModeID mode = findModeID(mode_name);
return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, 0, 0));
}
return NULL;
@@ -1104,9 +1177,9 @@ _getpalette(ImagingObject *self, PyObject *args) {
int bits;
ImagingShuffler pack;
- char *mode = "RGB";
- char *rawmode = "RGB";
- if (!PyArg_ParseTuple(args, "|ss", &mode, &rawmode)) {
+ char *mode_name = "RGB";
+ char *rawmode_name = "RGB";
+ if (!PyArg_ParseTuple(args, "|ss", &mode_name, &rawmode_name)) {
return NULL;
}
@@ -1115,6 +1188,9 @@ _getpalette(ImagingObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
pack = ImagingFindPacker(mode, rawmode, &bits);
if (!pack) {
PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
@@ -1141,7 +1217,7 @@ _getpalettemode(ImagingObject *self) {
return NULL;
}
- return PyUnicode_FromString(self->image->palette->mode);
+ return PyUnicode_FromString(getModeData(self->image->palette->mode)->name);
}
static inline int
@@ -1422,12 +1498,14 @@ _point(ImagingObject *self, PyObject *args) {
Imaging im;
PyObject *list;
- char *mode;
- if (!PyArg_ParseTuple(args, "Oz", &list, &mode)) {
+ char *mode_name;
+ if (!PyArg_ParseTuple(args, "Oz", &list, &mode_name)) {
return NULL;
}
- if (mode && !strcmp(mode, "F")) {
+ const ModeID mode = findModeID(mode_name);
+
+ if (mode == IMAGING_MODE_F) {
FLOAT32 *data;
/* map from 8-bit data to floating point */
@@ -1438,8 +1516,7 @@ _point(ImagingObject *self, PyObject *args) {
}
im = ImagingPoint(self->image, mode, (void *)data);
free(data);
-
- } else if (!strcmp(self->image->mode, "I") && mode && !strcmp(mode, "L")) {
+ } else if (self->image->mode == IMAGING_MODE_I && mode == IMAGING_MODE_L) {
UINT8 *data;
/* map from 16-bit subset of 32-bit data to 8-bit */
@@ -1451,7 +1528,6 @@ _point(ImagingObject *self, PyObject *args) {
}
im = ImagingPoint(self->image, mode, (void *)data);
free(data);
-
} else {
INT32 *data;
UINT8 lut[1024];
@@ -1472,7 +1548,7 @@ _point(ImagingObject *self, PyObject *args) {
return NULL;
}
- if (mode && !strcmp(mode, "I")) {
+ if (mode == IMAGING_MODE_I) {
im = ImagingPoint(self->image, mode, (void *)data);
} else if (mode && bands > 1) {
for (i = 0; i < 256; i++) {
@@ -1574,53 +1650,33 @@ _putdata(ImagingObject *self, PyObject *args) {
return NULL;
}
double value;
- if (image->bands == 1) {
- int bigendian = 0;
- if (image->type == IMAGING_TYPE_SPECIAL) {
- // I;16*
- if (strcmp(image->mode, "I;16B") == 0
+ int bigendian = 0;
+ if (image->type == IMAGING_TYPE_SPECIAL) {
+ // I;16*
+ if (
+ image->mode == IMAGING_MODE_I_16B
#ifdef WORDS_BIGENDIAN
- || strcmp(image->mode, "I;16N") == 0
+ || image->mode == IMAGING_MODE_I_16N
#endif
- ) {
- bigendian = 1;
- }
+ ) {
+ bigendian = 1;
}
- for (i = x = y = 0; i < n; i++) {
- set_value_to_item(seq, i);
- if (scale != 1.0 || offset != 0.0) {
- value = value * scale + offset;
- }
- if (image->type == IMAGING_TYPE_SPECIAL) {
- image->image8[y][x * 2 + (bigendian ? 1 : 0)] =
- CLIP8((int)value % 256);
- image->image8[y][x * 2 + (bigendian ? 0 : 1)] =
- CLIP8((int)value >> 8);
- } else {
- image->image8[y][x] = (UINT8)CLIP8(value);
- }
- if (++x >= (int)image->xsize) {
- x = 0, y++;
- }
+ }
+ for (i = x = y = 0; i < n; i++) {
+ set_value_to_item(seq, i);
+ if (scale != 1.0 || offset != 0.0) {
+ value = value * scale + offset;
}
- } else {
- // BGR;*
- int b;
- for (i = x = y = 0; i < n; i++) {
- char ink[4];
-
- op = PySequence_Fast_GET_ITEM(seq, i);
- if (!op || !getink(op, image, ink)) {
- Py_DECREF(seq);
- return NULL;
- }
- /* FIXME: what about scale and offset? */
- for (b = 0; b < image->pixelsize; b++) {
- image->image8[y][x * image->pixelsize + b] = ink[b];
- }
- if (++x >= (int)image->xsize) {
- x = 0, y++;
- }
+ if (image->type == IMAGING_TYPE_SPECIAL) {
+ image->image8[y][x * 2 + (bigendian ? 1 : 0)] =
+ CLIP8((int)value % 256);
+ image->image8[y][x * 2 + (bigendian ? 0 : 1)] =
+ CLIP8((int)value >> 8);
+ } else {
+ image->image8[y][x] = (UINT8)CLIP8(value);
+ }
+ if (++x >= (int)image->xsize) {
+ x = 0, y++;
}
}
PyErr_Clear(); /* Avoid weird exceptions */
@@ -1697,7 +1753,9 @@ _quantize(ImagingObject *self, PyObject *args) {
if (!self->image->xsize || !self->image->ysize) {
/* no content; return an empty image */
- return PyImagingNew(ImagingNew("P", self->image->xsize, self->image->ysize));
+ return PyImagingNew(
+ ImagingNew(IMAGING_MODE_P, self->image->xsize, self->image->ysize)
+ );
}
return PyImagingNew(ImagingQuantize(self->image, colours, method, kmeans));
@@ -1708,21 +1766,33 @@ _putpalette(ImagingObject *self, PyObject *args) {
ImagingShuffler unpack;
int bits;
- char *palette_mode, *rawmode;
+ char *palette_mode_name, *rawmode_name;
UINT8 *palette;
Py_ssize_t palettesize;
if (!PyArg_ParseTuple(
- args, "ssy#", &palette_mode, &rawmode, &palette, &palettesize
+ args, "ssy#", &palette_mode_name, &rawmode_name, &palette, &palettesize
)) {
return NULL;
}
- if (strcmp(self->image->mode, "L") && strcmp(self->image->mode, "LA") &&
- strcmp(self->image->mode, "P") && strcmp(self->image->mode, "PA")) {
+ if (self->image->mode != IMAGING_MODE_L && self->image->mode != IMAGING_MODE_LA &&
+ self->image->mode != IMAGING_MODE_P && self->image->mode != IMAGING_MODE_PA) {
PyErr_SetString(PyExc_ValueError, wrong_mode);
return NULL;
}
+ const ModeID palette_mode = findModeID(palette_mode_name);
+ if (palette_mode == IMAGING_MODE_UNKNOWN) {
+ PyErr_SetString(PyExc_ValueError, wrong_mode);
+ return NULL;
+ }
+
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+ if (rawmode == IMAGING_RAWMODE_UNKNOWN) {
+ PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
+ return NULL;
+ }
+
unpack = ImagingFindUnpacker(palette_mode, rawmode, &bits);
if (!unpack) {
PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
@@ -1736,7 +1806,13 @@ _putpalette(ImagingObject *self, PyObject *args) {
ImagingPaletteDelete(self->image->palette);
- strcpy(self->image->mode, strlen(self->image->mode) == 2 ? "PA" : "P");
+ if (self->image->mode == IMAGING_MODE_LA) {
+ self->image->mode = IMAGING_MODE_PA;
+ } else if (self->image->mode == IMAGING_MODE_L) {
+ self->image->mode = IMAGING_MODE_P;
+ } else {
+ // The image already has a palette mode so we don't need to change it.
+ }
self->image->palette = ImagingPaletteNew(palette_mode);
@@ -1764,7 +1840,7 @@ _putpalettealpha(ImagingObject *self, PyObject *args) {
return NULL;
}
- strcpy(self->image->palette->mode, "RGBA");
+ self->image->palette->mode = IMAGING_MODE_RGBA;
self->image->palette->palette[index * 4 + 3] = (UINT8)alpha;
Py_RETURN_NONE;
@@ -1789,7 +1865,7 @@ _putpalettealphas(ImagingObject *self, PyObject *args) {
return NULL;
}
- strcpy(self->image->palette->mode, "RGBA");
+ self->image->palette->mode = IMAGING_MODE_RGBA;
for (i = 0; i < length; i++) {
self->image->palette->palette[i * 4 + 3] = (UINT8)values[i];
}
@@ -1957,8 +2033,11 @@ _reduce(ImagingObject *self, PyObject *args) {
return PyImagingNew(imOut);
}
-#define IS_RGB(mode) \
- (!strcmp(mode, "RGB") || !strcmp(mode, "RGBA") || !strcmp(mode, "RGBX"))
+static int
+isRGB(const ModeID mode) {
+ return mode == IMAGING_MODE_RGB || mode == IMAGING_MODE_RGBA ||
+ mode == IMAGING_MODE_RGBX;
+}
static PyObject *
im_setmode(ImagingObject *self, PyObject *args) {
@@ -1966,23 +2045,25 @@ im_setmode(ImagingObject *self, PyObject *args) {
Imaging im;
- char *mode;
+ char *mode_name;
Py_ssize_t modelen;
- if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen)) {
+ if (!PyArg_ParseTuple(args, "s#:setmode", &mode_name, &modelen)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+
im = self->image;
/* move all logic in here to the libImaging primitive */
- if (!strcmp(im->mode, mode)) {
+ if (im->mode == mode) {
; /* same mode; always succeeds */
- } else if (IS_RGB(im->mode) && IS_RGB(mode)) {
+ } else if (isRGB(im->mode) && isRGB(mode)) {
/* color to color */
- strcpy(im->mode, mode);
+ im->mode = mode;
im->bands = modelen;
- if (!strcmp(mode, "RGBA")) {
+ if (mode == IMAGING_MODE_RGBA) {
(void)ImagingFillBand(im, 3, 255);
}
} else {
@@ -2139,6 +2220,7 @@ _unsharp_mask(ImagingObject *self, PyObject *args) {
}
if (!ImagingUnsharpMask(imOut, imIn, radius, percent, threshold)) {
+ ImagingDelete(imOut);
return NULL;
}
@@ -2261,7 +2343,7 @@ _getextrema(ImagingObject *self) {
case IMAGING_TYPE_FLOAT32:
return Py_BuildValue("dd", extrema.f[0], extrema.f[1]);
case IMAGING_TYPE_SPECIAL:
- if (strcmp(self->image->mode, "I;16") == 0) {
+ if (self->image->mode == IMAGING_MODE_I_16) {
return Py_BuildValue("HH", extrema.s[0], extrema.s[1]);
}
}
@@ -2350,7 +2432,7 @@ _putband(ImagingObject *self, PyObject *args) {
static PyObject *
_merge(PyObject *self, PyObject *args) {
- char *mode;
+ char *mode_name;
ImagingObject *band0 = NULL;
ImagingObject *band1 = NULL;
ImagingObject *band2 = NULL;
@@ -2360,7 +2442,7 @@ _merge(PyObject *self, PyObject *args) {
if (!PyArg_ParseTuple(
args,
"sO!|O!O!O!",
- &mode,
+ &mode_name,
&Imaging_Type,
&band0,
&Imaging_Type,
@@ -2373,6 +2455,8 @@ _merge(PyObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+
if (band0) {
bands[0] = band0->image;
}
@@ -2386,7 +2470,12 @@ _merge(PyObject *self, PyObject *args) {
bands[3] = band3->image;
}
- return PyImagingNew(ImagingMerge(mode, bands));
+ Imaging imOut = ImagingMerge(mode, bands);
+ if (!imOut) {
+ return NULL;
+ }
+ ImagingCopyPalette(imOut, bands[0]);
+ return PyImagingNew(imOut);
}
static PyObject *
@@ -3131,7 +3220,8 @@ _draw_lines(ImagingDrawObject *self, PyObject *args) {
(int)p[3],
&ink,
width,
- self->blend
+ self->blend,
+ NULL
) < 0) {
free(xy);
return NULL;
@@ -3269,7 +3359,10 @@ _draw_polygon(ImagingDrawObject *self, PyObject *args) {
int ink;
int fill = 0;
int width = 0;
- if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) {
+ ImagingObject *maskp = NULL;
+ if (!PyArg_ParseTuple(
+ args, "Oi|iiO!", &data, &ink, &fill, &width, &Imaging_Type, &maskp
+ )) {
return NULL;
}
@@ -3299,8 +3392,16 @@ _draw_polygon(ImagingDrawObject *self, PyObject *args) {
free(xy);
- if (ImagingDrawPolygon(self->image->image, n, ixy, &ink, fill, width, self->blend) <
- 0) {
+ if (ImagingDrawPolygon(
+ self->image->image,
+ n,
+ ixy,
+ &ink,
+ fill,
+ width,
+ self->blend,
+ maskp ? maskp->image : NULL
+ ) < 0) {
free(ixy);
return NULL;
}
@@ -3655,6 +3756,10 @@ static struct PyMethodDef methods[] = {
/* Misc. */
{"save_ppm", (PyCFunction)_save_ppm, METH_VARARGS},
+ /* arrow */
+ {"__arrow_c_schema__", (PyCFunction)ExportArrowSchemaPyCapsule, METH_VARARGS},
+ {"__arrow_c_array__", (PyCFunction)ExportArrowArrayPyCapsule, METH_VARARGS},
+
{NULL, NULL} /* sentinel */
};
@@ -3662,7 +3767,7 @@ static struct PyMethodDef methods[] = {
static PyObject *
_getattr_mode(ImagingObject *self, void *closure) {
- return PyUnicode_FromString(self->image->mode);
+ return PyUnicode_FromString(getModeData(self->image->mode)->name);
}
static PyObject *
@@ -3675,18 +3780,6 @@ _getattr_bands(ImagingObject *self, void *closure) {
return PyLong_FromLong(self->image->bands);
}
-static PyObject *
-_getattr_id(ImagingObject *self, void *closure) {
- if (PyErr_WarnEx(
- PyExc_DeprecationWarning,
- "id property is deprecated and will be removed in Pillow 12 (2025-10-15)",
- 1
- ) < 0) {
- return NULL;
- }
- return PyLong_FromSsize_t((Py_ssize_t)self->image);
-}
-
static void
_ptr_destructor(PyObject *capsule) {
PyObject *self = (PyObject *)PyCapsule_GetContext(capsule);
@@ -3702,33 +3795,16 @@ _getattr_ptr(ImagingObject *self, void *closure) {
}
static PyObject *
-_getattr_unsafe_ptrs(ImagingObject *self, void *closure) {
- if (PyErr_WarnEx(
- PyExc_DeprecationWarning,
- "unsafe_ptrs property is deprecated and will be removed in Pillow 12 "
- "(2025-10-15)",
- 1
- ) < 0) {
- return NULL;
- }
- return Py_BuildValue(
- "(sn)(sn)(sn)",
- "image8",
- self->image->image8,
- "image32",
- self->image->image32,
- "image",
- self->image->image
- );
+_getattr_readonly(ImagingObject *self, void *closure) {
+ return PyLong_FromLong(self->image->read_only);
}
static struct PyGetSetDef getsetters[] = {
{"mode", (getter)_getattr_mode},
{"size", (getter)_getattr_size},
{"bands", (getter)_getattr_bands},
- {"id", (getter)_getattr_id},
{"ptr", (getter)_getattr_ptr},
- {"unsafe_ptrs", (getter)_getattr_unsafe_ptrs},
+ {"readonly", (getter)_getattr_readonly},
{NULL}
};
@@ -3983,6 +4059,21 @@ _set_blocks_max(PyObject *self, PyObject *args) {
Py_RETURN_NONE;
}
+static PyObject *
+_set_use_block_allocator(PyObject *self, PyObject *args) {
+ int use_block_allocator;
+ if (!PyArg_ParseTuple(args, "i:set_use_block_allocator", &use_block_allocator)) {
+ return NULL;
+ }
+ ImagingMemorySetBlockAllocator(&ImagingDefaultArena, use_block_allocator);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+_get_use_block_allocator(PyObject *self, PyObject *args) {
+ return PyLong_FromLong(ImagingDefaultArena.use_block_allocator);
+}
+
static PyObject *
_clear_cache(PyObject *self, PyObject *args) {
int i = 0;
@@ -4041,6 +4132,8 @@ PyImaging_ZipDecoderNew(PyObject *self, PyObject *args);
/* Encoders (in encode.c) */
extern PyObject *
+PyImaging_BcnEncoderNew(PyObject *self, PyObject *args);
+extern PyObject *
PyImaging_EpsEncoderNew(PyObject *self, PyObject *args);
extern PyObject *
PyImaging_GifEncoderNew(PyObject *self, PyObject *args);
@@ -4102,6 +4195,7 @@ static PyMethodDef functions[] = {
{"fill", (PyCFunction)_fill, METH_VARARGS},
{"new", (PyCFunction)_new, METH_VARARGS},
{"new_block", (PyCFunction)_new_block, METH_VARARGS},
+ {"new_arrow", (PyCFunction)_new_arrow, METH_VARARGS},
{"merge", (PyCFunction)_merge, METH_VARARGS},
/* Functions */
@@ -4109,6 +4203,7 @@ static PyMethodDef functions[] = {
/* Codecs */
{"bcn_decoder", (PyCFunction)PyImaging_BcnDecoderNew, METH_VARARGS},
+ {"bcn_encoder", (PyCFunction)PyImaging_BcnEncoderNew, METH_VARARGS},
{"bit_decoder", (PyCFunction)PyImaging_BitDecoderNew, METH_VARARGS},
{"eps_encoder", (PyCFunction)PyImaging_EpsEncoderNew, METH_VARARGS},
{"fli_decoder", (PyCFunction)PyImaging_FliDecoderNew, METH_VARARGS},
@@ -4187,9 +4282,11 @@ static PyMethodDef functions[] = {
{"get_alignment", (PyCFunction)_get_alignment, METH_VARARGS},
{"get_block_size", (PyCFunction)_get_block_size, METH_VARARGS},
{"get_blocks_max", (PyCFunction)_get_blocks_max, METH_VARARGS},
+ {"get_use_block_allocator", (PyCFunction)_get_use_block_allocator, METH_VARARGS},
{"set_alignment", (PyCFunction)_set_alignment, METH_VARARGS},
{"set_block_size", (PyCFunction)_set_block_size, METH_VARARGS},
{"set_blocks_max", (PyCFunction)_set_blocks_max, METH_VARARGS},
+ {"set_use_block_allocator", (PyCFunction)_set_use_block_allocator, METH_VARARGS},
{"clear_cache", (PyCFunction)_clear_cache, METH_VARARGS},
{NULL, NULL} /* sentinel */
@@ -4214,8 +4311,6 @@ setup_module(PyObject *m) {
return -1;
}
- ImagingAccessInit();
-
#ifdef HAVE_LIBJPEG
{
extern const char *ImagingJpegVersion(void);
@@ -4311,16 +4406,6 @@ setup_module(PyObject *m) {
PyObject *v = PyUnicode_FromString(ImagingTiffVersion());
PyDict_SetItemString(d, "libtiff_version", v ? v : Py_None);
Py_XDECREF(v);
-
- // Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7
- PyObject *support_custom_tags;
-#if TIFFLIB_VERSION >= 20111221 && TIFFLIB_VERSION != 20120218 && \
- TIFFLIB_VERSION != 20120922
- support_custom_tags = Py_True;
-#else
- support_custom_tags = Py_False;
-#endif
- PyDict_SetItemString(d, "libtiff_support_custom_tags", support_custom_tags);
}
#endif
@@ -4342,27 +4427,22 @@ setup_module(PyObject *m) {
return 0;
}
+static PyModuleDef_Slot slots[] = {
+ {Py_mod_exec, setup_module},
+#ifdef Py_GIL_DISABLED
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+#endif
+ {0, NULL}
+};
+
PyMODINIT_FUNC
PyInit__imaging(void) {
- PyObject *m;
-
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "_imaging",
- .m_size = -1,
.m_methods = functions,
+ .m_slots = slots
};
- m = PyModule_Create(&module_def);
-
- if (setup_module(m) < 0) {
- Py_DECREF(m);
- return NULL;
- }
-
-#ifdef Py_GIL_DISABLED
- PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
-#endif
-
- return m;
+ return PyModuleDef_Init(&module_def);
}
diff --git a/src/_imagingcms.c b/src/_imagingcms.c
index ea2f70186..ad3b27896 100644
--- a/src/_imagingcms.c
+++ b/src/_imagingcms.c
@@ -212,32 +212,44 @@ cms_transform_dealloc(CmsTransformObject *self) {
/* internal functions */
static cmsUInt32Number
-findLCMStype(char *PILmode) {
- if (strcmp(PILmode, "RGB") == 0 || strcmp(PILmode, "RGBA") == 0 ||
- strcmp(PILmode, "RGBX") == 0) {
- return TYPE_RGBA_8;
+findLCMStype(const char *const mode_name) {
+ const ModeID mode = findModeID(mode_name);
+ switch (mode) {
+ case IMAGING_MODE_RGB:
+ case IMAGING_MODE_RGBA:
+ case IMAGING_MODE_RGBX:
+ return TYPE_RGBA_8;
+ case IMAGING_MODE_CMYK:
+ return TYPE_CMYK_8;
+ case IMAGING_MODE_I_16:
+ case IMAGING_MODE_I_16L:
+ return TYPE_GRAY_16;
+ case IMAGING_MODE_I_16B:
+ return TYPE_GRAY_16_SE;
+ case IMAGING_MODE_YCbCr:
+ return TYPE_YCbCr_8;
+ case IMAGING_MODE_LAB:
+ // LabX equivalent like ALab, but not reversed -- no #define in lcms2
+ return (
+ COLORSPACE_SH(PT_LabV2) | CHANNELS_SH(3) | BYTES_SH(1) | EXTRA_SH(1)
+ );
+ default:
+ // This function only accepts a subset of the imaging modes Pillow has.
+ break;
}
- if (strcmp(PILmode, "RGBA;16B") == 0) {
+ // The following modes are not valid PIL Image modes.
+ if (strcmp(mode_name, "RGBA;16B") == 0) {
return TYPE_RGBA_16;
}
- if (strcmp(PILmode, "CMYK") == 0) {
- return TYPE_CMYK_8;
- }
- if (strcmp(PILmode, "I;16") == 0 || strcmp(PILmode, "I;16L") == 0 ||
- strcmp(PILmode, "L;16") == 0) {
+ if (strcmp(mode_name, "L;16") == 0) {
return TYPE_GRAY_16;
}
- if (strcmp(PILmode, "I;16B") == 0 || strcmp(PILmode, "L;16B") == 0) {
+ if (strcmp(mode_name, "L;16B") == 0) {
return TYPE_GRAY_16_SE;
}
- if (strcmp(PILmode, "YCbCr") == 0 || strcmp(PILmode, "YCCA") == 0 ||
- strcmp(PILmode, "YCC") == 0) {
+ if (strcmp(mode_name, "YCCA") == 0 || strcmp(mode_name, "YCC") == 0) {
return TYPE_YCbCr_8;
}
- if (strcmp(PILmode, "LAB") == 0) {
- // LabX equivalent like ALab, but not reversed -- no #define in lcms2
- return (COLORSPACE_SH(PT_LabV2) | CHANNELS_SH(3) | BYTES_SH(1) | EXTRA_SH(1));
- }
/* presume "1" or "L" by default */
return TYPE_GRAY_8;
}
@@ -1402,8 +1414,8 @@ static struct PyGetSetDef cms_profile_getsetters[] = {
{"colorant_table_out", (getter)cms_profile_getattr_colorant_table_out},
{"intent_supported", (getter)cms_profile_getattr_is_intent_supported},
{"clut", (getter)cms_profile_getattr_is_clut},
- {"icc_measurement_condition", (getter)cms_profile_getattr_icc_measurement_condition
- },
+ {"icc_measurement_condition",
+ (getter)cms_profile_getattr_icc_measurement_condition},
{"icc_viewing_condition", (getter)cms_profile_getattr_icc_viewing_condition},
{NULL}
@@ -1463,28 +1475,24 @@ setup_module(PyObject *m) {
return 0;
}
+static PyModuleDef_Slot slots[] = {
+ {Py_mod_exec, setup_module},
+#ifdef Py_GIL_DISABLED
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+#endif
+ {0, NULL}
+};
+
PyMODINIT_FUNC
PyInit__imagingcms(void) {
- PyObject *m;
+ PyDateTime_IMPORT;
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "_imagingcms",
- .m_size = -1,
.m_methods = pyCMSdll_methods,
+ .m_slots = slots
};
- m = PyModule_Create(&module_def);
-
- if (setup_module(m) < 0) {
- return NULL;
- }
-
- PyDateTime_IMPORT;
-
-#ifdef Py_GIL_DISABLED
- PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
-#endif
-
- return m;
+ return PyModuleDef_Init(&module_def);
}
diff --git a/src/_imagingft.c b/src/_imagingft.c
index 62dab73e5..d0af25b30 100644
--- a/src/_imagingft.c
+++ b/src/_imagingft.c
@@ -275,6 +275,7 @@ text_layout_raqm(
if (!text || !size) {
/* return 0 and clean up, no glyphs==no size,
and raqm fails with empty strings */
+ PyMem_Free(text);
goto failed;
}
set_text = raqm_set_text(rq, text, size);
@@ -425,6 +426,7 @@ text_layout_fallback(
"setting text direction, language or font features is not supported "
"without libraqm"
);
+ return 0;
}
if (PyUnicode_Check(string)) {
@@ -523,7 +525,7 @@ font_getlength(FontObject *self, PyObject *args) {
int horizontal_dir; /* is primary axis horizontal? */
int mask = 0; /* is FT_LOAD_TARGET_MONO enabled? */
int color = 0; /* is FT_LOAD_COLOR enabled? */
- const char *mode = NULL;
+ const char *mode_name = NULL;
const char *dir = NULL;
const char *lang = NULL;
PyObject *features = Py_None;
@@ -532,15 +534,16 @@ font_getlength(FontObject *self, PyObject *args) {
/* calculate size and bearing for a given string */
if (!PyArg_ParseTuple(
- args, "O|zzOz:getlength", &string, &mode, &dir, &features, &lang
+ args, "O|zzOz:getlength", &string, &mode_name, &dir, &features, &lang
)) {
return NULL;
}
horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1;
- mask = mode && strcmp(mode, "1") == 0;
- color = mode && strcmp(mode, "RGBA") == 0;
+ const ModeID mode = findModeID(mode_name);
+ mask = mode == IMAGING_MODE_1;
+ color = mode == IMAGING_MODE_RGBA;
count = text_layout(string, self, dir, features, lang, &glyph_info, mask, color);
if (PyErr_Occurred()) {
@@ -752,7 +755,7 @@ font_getsize(FontObject *self, PyObject *args) {
int horizontal_dir; /* is primary axis horizontal? */
int mask = 0; /* is FT_LOAD_TARGET_MONO enabled? */
int color = 0; /* is FT_LOAD_COLOR enabled? */
- const char *mode = NULL;
+ const char *mode_name = NULL;
const char *dir = NULL;
const char *lang = NULL;
const char *anchor = NULL;
@@ -762,15 +765,23 @@ font_getsize(FontObject *self, PyObject *args) {
/* calculate size and bearing for a given string */
if (!PyArg_ParseTuple(
- args, "O|zzOzz:getsize", &string, &mode, &dir, &features, &lang, &anchor
+ args,
+ "O|zzOzz:getsize",
+ &string,
+ &mode_name,
+ &dir,
+ &features,
+ &lang,
+ &anchor
)) {
return NULL;
}
horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1;
- mask = mode && strcmp(mode, "1") == 0;
- color = mode && strcmp(mode, "RGBA") == 0;
+ const ModeID mode = findModeID(mode_name);
+ mask = mode == IMAGING_MODE_1;
+ color = mode == IMAGING_MODE_RGBA;
count = text_layout(string, self, dir, features, lang, &glyph_info, mask, color);
if (PyErr_Occurred()) {
@@ -837,7 +848,7 @@ font_render(FontObject *self, PyObject *args) {
int stroke_filled = 0;
PY_LONG_LONG foreground_ink_long = 0;
unsigned int foreground_ink;
- const char *mode = NULL;
+ const char *mode_name = NULL;
const char *dir = NULL;
const char *lang = NULL;
const char *anchor = NULL;
@@ -857,7 +868,7 @@ font_render(FontObject *self, PyObject *args) {
"OO|zzOzfpzL(ff):render",
&string,
&fill,
- &mode,
+ &mode_name,
&dir,
&features,
&lang,
@@ -871,8 +882,9 @@ font_render(FontObject *self, PyObject *args) {
return NULL;
}
- mask = mode && strcmp(mode, "1") == 0;
- color = mode && strcmp(mode, "RGBA") == 0;
+ const ModeID mode = findModeID(mode_name);
+ mask = mode == IMAGING_MODE_1;
+ color = mode == IMAGING_MODE_RGBA;
foreground_ink = foreground_ink_long;
@@ -1219,8 +1231,6 @@ glyph_error:
return NULL;
}
-#if FREETYPE_MAJOR > 2 || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) || \
- (FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1)
static PyObject *
font_getvarnames(FontObject *self) {
int error;
@@ -1430,7 +1440,6 @@ font_setvaraxes(FontObject *self, PyObject *args) {
Py_RETURN_NONE;
}
-#endif
static void
font_dealloc(FontObject *self) {
@@ -1449,13 +1458,10 @@ static PyMethodDef font_methods[] = {
{"render", (PyCFunction)font_render, METH_VARARGS},
{"getsize", (PyCFunction)font_getsize, METH_VARARGS},
{"getlength", (PyCFunction)font_getlength, METH_VARARGS},
-#if FREETYPE_MAJOR > 2 || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) || \
- (FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1)
{"getvarnames", (PyCFunction)font_getvarnames, METH_NOARGS},
{"getvaraxes", (PyCFunction)font_getvaraxes, METH_NOARGS},
{"setvarname", (PyCFunction)font_setvarname, METH_VARARGS},
{"setvaraxes", (PyCFunction)font_setvaraxes, METH_VARARGS},
-#endif
{NULL, NULL}
};
@@ -1599,26 +1605,22 @@ setup_module(PyObject *m) {
return 0;
}
+static PyModuleDef_Slot slots[] = {
+ {Py_mod_exec, setup_module},
+#ifdef Py_GIL_DISABLED
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+#endif
+ {0, NULL}
+};
+
PyMODINIT_FUNC
PyInit__imagingft(void) {
- PyObject *m;
-
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "_imagingft",
- .m_size = -1,
.m_methods = _functions,
+ .m_slots = slots
};
- m = PyModule_Create(&module_def);
-
- if (setup_module(m) < 0) {
- return NULL;
- }
-
-#ifdef Py_GIL_DISABLED
- PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
-#endif
-
- return m;
+ return PyModuleDef_Init(&module_def);
}
diff --git a/src/_imagingmath.c b/src/_imagingmath.c
index 4b9bf08ba..48c395900 100644
--- a/src/_imagingmath.c
+++ b/src/_imagingmath.c
@@ -302,26 +302,22 @@ setup_module(PyObject *m) {
return 0;
}
+static PyModuleDef_Slot slots[] = {
+ {Py_mod_exec, setup_module},
+#ifdef Py_GIL_DISABLED
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+#endif
+ {0, NULL}
+};
+
PyMODINIT_FUNC
PyInit__imagingmath(void) {
- PyObject *m;
-
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "_imagingmath",
- .m_size = -1,
.m_methods = _functions,
+ .m_slots = slots
};
- m = PyModule_Create(&module_def);
-
- if (setup_module(m) < 0) {
- return NULL;
- }
-
-#ifdef Py_GIL_DISABLED
- PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
-#endif
-
- return m;
+ return PyModuleDef_Init(&module_def);
}
diff --git a/src/_imagingmorph.c b/src/_imagingmorph.c
index a20888294..5995f9d42 100644
--- a/src/_imagingmorph.c
+++ b/src/_imagingmorph.c
@@ -246,23 +246,22 @@ static PyMethodDef functions[] = {
{NULL, NULL, 0, NULL}
};
+static PyModuleDef_Slot slots[] = {
+#ifdef Py_GIL_DISABLED
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+#endif
+ {0, NULL}
+};
+
PyMODINIT_FUNC
PyInit__imagingmorph(void) {
- PyObject *m;
-
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "_imagingmorph",
.m_doc = "A module for doing image morphology",
- .m_size = -1,
.m_methods = functions,
+ .m_slots = slots
};
- m = PyModule_Create(&module_def);
-
-#ifdef Py_GIL_DISABLED
- PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
-#endif
-
- return m;
+ return PyModuleDef_Init(&module_def);
}
diff --git a/src/_imagingtk.c b/src/_imagingtk.c
index 4e06fe9b8..68d7bf4cd 100644
--- a/src/_imagingtk.c
+++ b/src/_imagingtk.c
@@ -46,24 +46,22 @@ static PyMethodDef functions[] = {
{NULL, NULL} /* sentinel */
};
+static PyModuleDef_Slot slots[] = {
+ {Py_mod_exec, load_tkinter_funcs},
+#ifdef Py_GIL_DISABLED
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+#endif
+ {0, NULL}
+};
+
PyMODINIT_FUNC
PyInit__imagingtk(void) {
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "_imagingtk",
- .m_size = -1,
.m_methods = functions,
+ .m_slots = slots
};
- PyObject *m;
- m = PyModule_Create(&module_def);
- if (load_tkinter_funcs() != 0) {
- Py_DECREF(m);
- return NULL;
- }
-#ifdef Py_GIL_DISABLED
- PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
-#endif
-
- return m;
+ return PyModuleDef_Init(&module_def);
}
diff --git a/src/_webp.c b/src/_webp.c
index 3aa4c408b..d065e329c 100644
--- a/src/_webp.c
+++ b/src/_webp.c
@@ -89,8 +89,8 @@ HandleMuxError(WebPMuxError err, char *chunk) {
static int
import_frame_libwebp(WebPPicture *frame, Imaging im) {
- if (strcmp(im->mode, "RGBA") && strcmp(im->mode, "RGB") &&
- strcmp(im->mode, "RGBX")) {
+ if (im->mode != IMAGING_MODE_RGBA && im->mode != IMAGING_MODE_RGB &&
+ im->mode != IMAGING_MODE_RGBX) {
PyErr_SetString(PyExc_ValueError, "unsupported image mode");
return -1;
}
@@ -104,7 +104,7 @@ import_frame_libwebp(WebPPicture *frame, Imaging im) {
return -2;
}
- int ignore_fourth_channel = strcmp(im->mode, "RGBA");
+ int ignore_fourth_channel = im->mode != IMAGING_MODE_RGBA;
for (int y = 0; y < im->ysize; ++y) {
UINT8 *src = (UINT8 *)im->image32[y];
UINT32 *dst = frame->argb + frame->argb_stride * y;
@@ -143,7 +143,7 @@ typedef struct {
PyObject_HEAD WebPAnimDecoder *dec;
WebPAnimInfo info;
WebPData data;
- char *mode;
+ ModeID mode;
} WebPAnimDecoderObject;
static PyTypeObject WebPAnimDecoder_Type;
@@ -396,7 +396,7 @@ _anim_decoder_new(PyObject *self, PyObject *args) {
const uint8_t *webp;
Py_ssize_t size;
WebPData webp_src;
- char *mode;
+ ModeID mode;
WebPDecoderConfig config;
WebPAnimDecoderObject *decp = NULL;
WebPAnimDecoder *dec = NULL;
@@ -409,10 +409,10 @@ _anim_decoder_new(PyObject *self, PyObject *args) {
webp_src.size = size;
// Sniff the mode, since the decoder API doesn't tell us
- mode = "RGBA";
+ mode = IMAGING_MODE_RGBA;
if (WebPGetFeatures(webp, size, &config.input) == VP8_STATUS_OK) {
if (!config.input.has_alpha) {
- mode = "RGBX";
+ mode = IMAGING_MODE_RGBX;
}
}
@@ -455,7 +455,7 @@ _anim_decoder_get_info(PyObject *self) {
info->loop_count,
info->bgcolor,
info->frame_count,
- decp->mode
+ getModeData(decp->mode)->name
);
}
@@ -641,6 +641,10 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
ImagingSectionLeave(&cookie);
WebPPictureFree(&pic);
+
+ output = writer.mem;
+ ret_size = writer.size;
+
if (!ok) {
int error_code = (&pic)->error_code;
char message[50] = "";
@@ -652,10 +656,9 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
);
}
PyErr_Format(PyExc_ValueError, "encoding error %d%s", error_code, message);
+ free(output);
return NULL;
}
- output = writer.mem;
- ret_size = writer.size;
{
/* I want to truncate the *_size items that get passed into WebP
@@ -777,26 +780,22 @@ setup_module(PyObject *m) {
return 0;
}
+static PyModuleDef_Slot slots[] = {
+ {Py_mod_exec, setup_module},
+#ifdef Py_GIL_DISABLED
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+#endif
+ {0, NULL}
+};
+
PyMODINIT_FUNC
PyInit__webp(void) {
- PyObject *m;
-
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "_webp",
- .m_size = -1,
.m_methods = webpMethods,
+ .m_slots = slots
};
- m = PyModule_Create(&module_def);
- if (setup_module(m) < 0) {
- Py_DECREF(m);
- return NULL;
- }
-
-#ifdef Py_GIL_DISABLED
- PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
-#endif
-
- return m;
+ return PyModuleDef_Init(&module_def);
}
diff --git a/src/decode.c b/src/decode.c
index 03db1ce35..051623ed4 100644
--- a/src/decode.c
+++ b/src/decode.c
@@ -266,7 +266,9 @@ static PyTypeObject ImagingDecoderType = {
/* -------------------------------------------------------------------- */
int
-get_unpacker(ImagingDecoderObject *decoder, const char *mode, const char *rawmode) {
+get_unpacker(
+ ImagingDecoderObject *decoder, const ModeID mode, const RawModeID rawmode
+) {
int bits;
ImagingShuffler unpack;
@@ -291,17 +293,20 @@ PyObject *
PyImaging_BitDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
+ const char *mode_name;
int bits = 8;
int pad = 8;
int fill = 0;
int sign = 0;
int ystep = 1;
- if (!PyArg_ParseTuple(args, "s|iiiii", &mode, &bits, &pad, &fill, &sign, &ystep)) {
+ if (!PyArg_ParseTuple(
+ args, "s|iiiii", &mode_name, &bits, &pad, &fill, &sign, &ystep
+ )) {
return NULL;
}
- if (strcmp(mode, "F") != 0) {
+ const ModeID mode = findModeID(mode_name);
+ if (mode != IMAGING_MODE_F) {
PyErr_SetString(PyExc_ValueError, "bad image mode");
return NULL;
}
@@ -331,34 +336,36 @@ PyObject *
PyImaging_BcnDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *actual;
+ char *mode_name;
int n = 0;
char *pixel_format = "";
- if (!PyArg_ParseTuple(args, "si|s", &mode, &n, &pixel_format)) {
+ if (!PyArg_ParseTuple(args, "si|s", &mode_name, &n, &pixel_format)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ ModeID actual;
+
switch (n) {
case 1: /* BC1: 565 color, 1-bit alpha */
case 2: /* BC2: 565 color, 4-bit alpha */
case 3: /* BC3: 565 color, 2-endpoint 8-bit interpolated alpha */
case 7: /* BC7: 4-channel 8-bit via everything */
- actual = "RGBA";
+ actual = IMAGING_MODE_RGBA;
break;
case 4: /* BC4: 1-channel 8-bit via 1 BC3 alpha block */
- actual = "L";
+ actual = IMAGING_MODE_L;
break;
case 5: /* BC5: 2-channel 8-bit via 2 BC3 alpha blocks */
case 6: /* BC6: 3-channel 16-bit float */
- actual = "RGB";
+ actual = IMAGING_MODE_RGB;
break;
default:
PyErr_SetString(PyExc_ValueError, "block compression type unknown");
return NULL;
}
- if (strcmp(mode, actual) != 0) {
+ if (mode != actual) {
PyErr_SetString(PyExc_ValueError, "bad image mode");
return NULL;
}
@@ -401,15 +408,18 @@ PyObject *
PyImaging_GifDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
+ const char *mode_name;
int bits = 8;
int interlace = 0;
int transparency = -1;
- if (!PyArg_ParseTuple(args, "s|iii", &mode, &bits, &interlace, &transparency)) {
+ if (!PyArg_ParseTuple(
+ args, "s|iii", &mode_name, &bits, &interlace, &transparency
+ )) {
return NULL;
}
- if (strcmp(mode, "L") != 0 && strcmp(mode, "P") != 0) {
+ const ModeID mode = findModeID(mode_name);
+ if (mode != IMAGING_MODE_L && mode != IMAGING_MODE_P) {
PyErr_SetString(PyExc_ValueError, "bad image mode");
return NULL;
}
@@ -436,12 +446,14 @@ PyObject *
PyImaging_HexDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode;
- if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) {
+ char *mode_name, *rawmode_name;
+ if (!PyArg_ParseTuple(args, "ss", &mode_name, &rawmode_name)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL) {
return NULL;
@@ -469,16 +481,21 @@ PyImaging_HexDecoderNew(PyObject *self, PyObject *args) {
PyObject *
PyImaging_LibTiffDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode;
+ char *mode_name;
+ char *rawmode_name;
char *compname;
int fp;
uint32_t ifdoffset;
- if (!PyArg_ParseTuple(args, "sssiI", &mode, &rawmode, &compname, &fp, &ifdoffset)) {
+ if (!PyArg_ParseTuple(
+ args, "sssiI", &mode_name, &rawmode_name, &compname, &fp, &ifdoffset
+ )) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
TRACE(("new tiff decoder %s\n", compname));
decoder = PyImaging_DecoderNew(sizeof(TIFFSTATE));
@@ -511,12 +528,15 @@ PyObject *
PyImaging_PackbitsDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode;
- if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) {
+ char *mode_name;
+ char *rawmode_name;
+ if (!PyArg_ParseTuple(args, "ss", &mode_name, &rawmode_name)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL) {
return NULL;
@@ -545,7 +565,7 @@ PyImaging_PcdDecoderNew(PyObject *self, PyObject *args) {
}
/* Unpack from PhotoYCC to RGB */
- if (get_unpacker(decoder, "RGB", "YCC;P") < 0) {
+ if (get_unpacker(decoder, IMAGING_MODE_RGB, IMAGING_RAWMODE_YCC_P) < 0) {
return NULL;
}
@@ -562,13 +582,15 @@ PyObject *
PyImaging_PcxDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode;
+ char *mode_name, *rawmode_name;
int stride;
- if (!PyArg_ParseTuple(args, "ssi", &mode, &rawmode, &stride)) {
+ if (!PyArg_ParseTuple(args, "ssi", &mode_name, &rawmode_name, &stride)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL) {
return NULL;
@@ -593,14 +615,16 @@ PyObject *
PyImaging_RawDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode;
+ char *mode_name, *rawmode_name;
int stride = 0;
int ystep = 1;
- if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep)) {
+ if (!PyArg_ParseTuple(args, "ss|ii", &mode_name, &rawmode_name, &stride, &ystep)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
decoder = PyImaging_DecoderNew(sizeof(RAWSTATE));
if (decoder == NULL) {
return NULL;
@@ -627,14 +651,16 @@ PyObject *
PyImaging_SgiRleDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode;
+ char *mode_name, *rawmode_name;
int ystep = 1;
int bpc = 1;
- if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &bpc)) {
+ if (!PyArg_ParseTuple(args, "ss|ii", &mode_name, &rawmode_name, &ystep, &bpc)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
decoder = PyImaging_DecoderNew(sizeof(SGISTATE));
if (decoder == NULL) {
return NULL;
@@ -661,12 +687,14 @@ PyObject *
PyImaging_SunRleDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode;
- if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) {
+ char *mode_name, *rawmode_name;
+ if (!PyArg_ParseTuple(args, "ss", &mode_name, &rawmode_name)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL) {
return NULL;
@@ -689,14 +717,16 @@ PyObject *
PyImaging_TgaRleDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode;
+ char *mode_name, *rawmode_name;
int ystep = 1;
int depth = 8;
- if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &depth)) {
+ if (!PyArg_ParseTuple(args, "ss|ii", &mode_name, &rawmode_name, &ystep, &depth)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL) {
return NULL;
@@ -727,7 +757,7 @@ PyImaging_XbmDecoderNew(PyObject *self, PyObject *args) {
return NULL;
}
- if (get_unpacker(decoder, "1", "1;R") < 0) {
+ if (get_unpacker(decoder, IMAGING_MODE_1, IMAGING_RAWMODE_1_R) < 0) {
return NULL;
}
@@ -748,13 +778,15 @@ PyObject *
PyImaging_ZipDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode;
+ char *mode_name, *rawmode_name;
int interlaced = 0;
- if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &interlaced)) {
+ if (!PyArg_ParseTuple(args, "ss|i", &mode_name, &rawmode_name, &interlaced)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
decoder = PyImaging_DecoderNew(sizeof(ZIPSTATE));
if (decoder == NULL) {
return NULL;
@@ -798,19 +830,21 @@ PyObject *
PyImaging_JpegDecoderNew(PyObject *self, PyObject *args) {
ImagingDecoderObject *decoder;
- char *mode;
- char *rawmode; /* what we want from the decoder */
- char *jpegmode; /* what's in the file */
+ char *mode_name;
+ char *rawmode_name; /* what we want from the decoder */
+ char *jpegmode_name; /* what's in the file */
int scale = 1;
int draft = 0;
- if (!PyArg_ParseTuple(args, "ssz|ii", &mode, &rawmode, &jpegmode, &scale, &draft)) {
+ if (!PyArg_ParseTuple(
+ args, "ssz|ii", &mode_name, &rawmode_name, &jpegmode_name, &scale, &draft
+ )) {
return NULL;
}
- if (!jpegmode) {
- jpegmode = "";
- }
+ const ModeID mode = findModeID(mode_name);
+ RawModeID rawmode = findRawModeID(rawmode_name);
+ const RawModeID jpegmode = findRawModeID(jpegmode_name);
decoder = PyImaging_DecoderNew(sizeof(JPEGSTATE));
if (decoder == NULL) {
@@ -820,8 +854,8 @@ PyImaging_JpegDecoderNew(PyObject *self, PyObject *args) {
// libjpeg-turbo supports different output formats.
// We are choosing Pillow's native format (3 color bytes + 1 padding)
// to avoid extra conversion in Unpack.c.
- if (ImagingJpegUseJCSExtensions() && strcmp(rawmode, "RGB") == 0) {
- rawmode = "RGBX";
+ if (ImagingJpegUseJCSExtensions() && rawmode == IMAGING_RAWMODE_RGB) {
+ rawmode = IMAGING_RAWMODE_RGBX;
}
if (get_unpacker(decoder, mode, rawmode) < 0) {
@@ -831,11 +865,13 @@ PyImaging_JpegDecoderNew(PyObject *self, PyObject *args) {
decoder->decode = ImagingJpegDecode;
decoder->cleanup = ImagingJpegDecodeCleanup;
- strncpy(((JPEGSTATE *)decoder->state.context)->rawmode, rawmode, 8);
- strncpy(((JPEGSTATE *)decoder->state.context)->jpegmode, jpegmode, 8);
+ JPEGSTATE *jpeg_decoder_state_context = (JPEGSTATE *)decoder->state.context;
- ((JPEGSTATE *)decoder->state.context)->scale = scale;
- ((JPEGSTATE *)decoder->state.context)->draft = draft;
+ jpeg_decoder_state_context->rawmode = rawmode;
+ jpeg_decoder_state_context->jpegmode = jpegmode;
+
+ jpeg_decoder_state_context->scale = scale;
+ jpeg_decoder_state_context->draft = draft;
return (PyObject *)decoder;
}
@@ -870,8 +906,6 @@ PyImaging_Jpeg2KDecoderNew(PyObject *self, PyObject *args) {
if (strcmp(format, "j2k") == 0) {
codec_format = OPJ_CODEC_J2K;
- } else if (strcmp(format, "jpt") == 0) {
- codec_format = OPJ_CODEC_JPT;
} else if (strcmp(format, "jp2") == 0) {
codec_format = OPJ_CODEC_JP2;
} else {
diff --git a/src/display.c b/src/display.c
index a05387504..5b5853a3c 100644
--- a/src/display.c
+++ b/src/display.c
@@ -47,7 +47,7 @@ typedef struct {
static PyTypeObject ImagingDisplayType;
static ImagingDisplayObject *
-_new(const char *mode, int xsize, int ysize) {
+_new(const ModeID mode, int xsize, int ysize) {
ImagingDisplayObject *display;
if (PyType_Ready(&ImagingDisplayType) < 0) {
@@ -235,7 +235,7 @@ static struct PyMethodDef methods[] = {
static PyObject *
_getattr_mode(ImagingDisplayObject *self, void *closure) {
- return Py_BuildValue("s", self->dib->mode);
+ return Py_BuildValue("s", getModeData(self->dib->mode)->name);
}
static PyObject *
@@ -258,13 +258,14 @@ static PyTypeObject ImagingDisplayType = {
PyObject *
PyImaging_DisplayWin32(PyObject *self, PyObject *args) {
ImagingDisplayObject *display;
- char *mode;
+ char *mode_name;
int xsize, ysize;
- if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) {
+ if (!PyArg_ParseTuple(args, "s(ii)", &mode_name, &xsize, &ysize)) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
display = _new(mode, xsize, ysize);
if (display == NULL) {
return NULL;
@@ -275,57 +276,80 @@ PyImaging_DisplayWin32(PyObject *self, PyObject *args) {
PyObject *
PyImaging_DisplayModeWin32(PyObject *self, PyObject *args) {
- char *mode;
int size[2];
-
- mode = ImagingGetModeDIB(size);
-
- return Py_BuildValue("s(ii)", mode, size[0], size[1]);
+ const ModeID mode = ImagingGetModeDIB(size);
+ return Py_BuildValue("s(ii)", getModeData(mode)->name, size[0], size[1]);
}
/* -------------------------------------------------------------------- */
/* Windows screen grabber */
+typedef HANDLE(__stdcall *Func_GetWindowDpiAwarenessContext)(HANDLE);
typedef HANDLE(__stdcall *Func_SetThreadDpiAwarenessContext)(HANDLE);
PyObject *
PyImaging_GrabScreenWin32(PyObject *self, PyObject *args) {
- int x = 0, y = 0, width, height;
- int includeLayeredWindows = 0, all_screens = 0;
+ int x = 0, y = 0, width = -1, height;
+ int includeLayeredWindows = 0, screens = 0;
HBITMAP bitmap;
BITMAPCOREHEADER core;
HDC screen, screen_copy;
+ HWND wnd;
DWORD rop;
PyObject *buffer;
- HANDLE dpiAwareness;
+ HANDLE dpiAwareness = NULL;
HMODULE user32;
+ Func_GetWindowDpiAwarenessContext GetWindowDpiAwarenessContext_function;
Func_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContext_function;
- if (!PyArg_ParseTuple(args, "|ii", &includeLayeredWindows, &all_screens)) {
+ if (!PyArg_ParseTuple(
+ args, "|ii" F_HANDLE, &includeLayeredWindows, &screens, &wnd
+ )) {
return NULL;
}
/* step 1: create a memory DC large enough to hold the
entire screen */
- screen = CreateDC("DISPLAY", NULL, NULL, NULL);
+ if (screens == -1) {
+ screen = GetDC(wnd);
+ if (screen == NULL) {
+ PyErr_SetString(PyExc_OSError, "unable to get device context for handle");
+ return NULL;
+ }
+ } else {
+ screen = CreateDC("DISPLAY", NULL, NULL, NULL);
+ }
screen_copy = CreateCompatibleDC(screen);
// added in Windows 10 (1607)
// loaded dynamically to avoid link errors
user32 = LoadLibraryA("User32.dll");
- SetThreadDpiAwarenessContext_function = (Func_SetThreadDpiAwarenessContext
- )GetProcAddress(user32, "SetThreadDpiAwarenessContext");
+ SetThreadDpiAwarenessContext_function = (Func_SetThreadDpiAwarenessContext)
+ GetProcAddress(user32, "SetThreadDpiAwarenessContext");
if (SetThreadDpiAwarenessContext_function != NULL) {
+ GetWindowDpiAwarenessContext_function = (Func_GetWindowDpiAwarenessContext)
+ GetProcAddress(user32, "GetWindowDpiAwarenessContext");
+ if (screens == -1 && GetWindowDpiAwarenessContext_function != NULL) {
+ dpiAwareness = GetWindowDpiAwarenessContext_function(wnd);
+ }
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = ((DPI_CONTEXT_HANDLE)-3)
- dpiAwareness = SetThreadDpiAwarenessContext_function((HANDLE)-3);
+ dpiAwareness = SetThreadDpiAwarenessContext_function(
+ dpiAwareness == NULL ? (HANDLE)-3 : dpiAwareness
+ );
}
- if (all_screens) {
+ if (screens == 1) {
x = GetSystemMetrics(SM_XVIRTUALSCREEN);
y = GetSystemMetrics(SM_YVIRTUALSCREEN);
width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ } else if (screens == -1) {
+ RECT rect;
+ if (GetClientRect(wnd, &rect)) {
+ width = rect.right;
+ height = rect.bottom;
+ }
} else {
width = GetDeviceCaps(screen, HORZRES);
height = GetDeviceCaps(screen, VERTRES);
@@ -337,6 +361,10 @@ PyImaging_GrabScreenWin32(PyObject *self, PyObject *args) {
FreeLibrary(user32);
+ if (width == -1) {
+ goto error;
+ }
+
bitmap = CreateCompatibleBitmap(screen, width, height);
if (!bitmap) {
goto error;
@@ -382,7 +410,11 @@ PyImaging_GrabScreenWin32(PyObject *self, PyObject *args) {
DeleteObject(bitmap);
DeleteDC(screen_copy);
- DeleteDC(screen);
+ if (screens == -1) {
+ ReleaseDC(wnd, screen);
+ } else {
+ DeleteDC(screen);
+ }
return Py_BuildValue("(ii)(ii)N", x, y, width, height, buffer);
@@ -390,7 +422,11 @@ error:
PyErr_SetString(PyExc_OSError, "screen grab failed");
DeleteDC(screen_copy);
- DeleteDC(screen);
+ if (screens == -1) {
+ ReleaseDC(wnd, screen);
+ } else {
+ DeleteDC(screen);
+ }
return NULL;
}
@@ -687,6 +723,14 @@ PyImaging_EventLoopWin32(PyObject *self, PyObject *args) {
#define GET32(p, o) ((DWORD *)(p + o))[0]
+static int CALLBACK
+enhMetaFileProc(
+ HDC hdc, HANDLETABLE *lpht, const ENHMETARECORD *lpmr, int nHandles, LPARAM data
+) {
+ PlayEnhMetaFileRecord(hdc, lpht, lpmr, nHandles);
+ return 1;
+}
+
PyObject *
PyImaging_DrawWmf(PyObject *self, PyObject *args) {
HBITMAP bitmap;
@@ -767,10 +811,7 @@ PyImaging_DrawWmf(PyObject *self, PyObject *args) {
/* FIXME: make background transparent? configurable? */
FillRect(dc, &rect, GetStockObject(WHITE_BRUSH));
- if (!PlayEnhMetaFile(dc, meta, &rect)) {
- PyErr_SetString(PyExc_OSError, "cannot render metafile");
- goto error;
- }
+ EnumEnhMetaFile(dc, meta, enhMetaFileProc, NULL, &rect);
/* step 4: extract bits from bitmap */
diff --git a/src/encode.c b/src/encode.c
index 80a7acf0e..e49359974 100644
--- a/src/encode.c
+++ b/src/encode.c
@@ -27,6 +27,7 @@
#include "thirdparty/pythoncapi_compat.h"
#include "libImaging/Imaging.h"
+#include "libImaging/Bcn.h"
#include "libImaging/Gif.h"
#ifdef HAVE_UNISTD_H
@@ -333,14 +334,19 @@ static PyTypeObject ImagingEncoderType = {
/* -------------------------------------------------------------------- */
int
-get_packer(ImagingEncoderObject *encoder, const char *mode, const char *rawmode) {
+get_packer(ImagingEncoderObject *encoder, const ModeID mode, const RawModeID rawmode) {
int bits;
ImagingShuffler pack;
pack = ImagingFindPacker(mode, rawmode, &bits);
if (!pack) {
Py_DECREF(encoder);
- PyErr_Format(PyExc_ValueError, "No packer found from %s to %s", mode, rawmode);
+ PyErr_Format(
+ PyExc_ValueError,
+ "No packer found from %s to %s",
+ getModeData(mode)->name,
+ getRawModeData(rawmode)->name
+ );
return -1;
}
@@ -350,6 +356,31 @@ get_packer(ImagingEncoderObject *encoder, const char *mode, const char *rawmode)
return 0;
}
+/* -------------------------------------------------------------------- */
+/* BCN */
+/* -------------------------------------------------------------------- */
+
+PyObject *
+PyImaging_BcnEncoderNew(PyObject *self, PyObject *args) {
+ ImagingEncoderObject *encoder;
+
+ char *mode;
+ int n;
+ if (!PyArg_ParseTuple(args, "si", &mode, &n)) {
+ return NULL;
+ }
+
+ encoder = PyImaging_EncoderNew(0);
+ if (encoder == NULL) {
+ return NULL;
+ }
+
+ encoder->encode = ImagingBcnEncode;
+ encoder->state.state = n;
+
+ return (PyObject *)encoder;
+}
+
/* -------------------------------------------------------------------- */
/* EPS */
/* -------------------------------------------------------------------- */
@@ -376,11 +407,13 @@ PyObject *
PyImaging_GifEncoderNew(PyObject *self, PyObject *args) {
ImagingEncoderObject *encoder;
- char *mode;
- char *rawmode;
+ char *mode_name;
+ char *rawmode_name;
Py_ssize_t bits = 8;
Py_ssize_t interlace = 0;
- if (!PyArg_ParseTuple(args, "ss|nn", &mode, &rawmode, &bits, &interlace)) {
+ if (!PyArg_ParseTuple(
+ args, "ss|nn", &mode_name, &rawmode_name, &bits, &interlace
+ )) {
return NULL;
}
@@ -389,6 +422,9 @@ PyImaging_GifEncoderNew(PyObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
if (get_packer(encoder, mode, rawmode) < 0) {
return NULL;
}
@@ -409,11 +445,11 @@ PyObject *
PyImaging_PcxEncoderNew(PyObject *self, PyObject *args) {
ImagingEncoderObject *encoder;
- char *mode;
- char *rawmode;
+ char *mode_name;
+ char *rawmode_name;
Py_ssize_t bits = 8;
- if (!PyArg_ParseTuple(args, "ss|n", &mode, &rawmode, &bits)) {
+ if (!PyArg_ParseTuple(args, "ss|n", &mode_name, &rawmode_name, &bits)) {
return NULL;
}
@@ -422,6 +458,9 @@ PyImaging_PcxEncoderNew(PyObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
if (get_packer(encoder, mode, rawmode) < 0) {
return NULL;
}
@@ -439,12 +478,12 @@ PyObject *
PyImaging_RawEncoderNew(PyObject *self, PyObject *args) {
ImagingEncoderObject *encoder;
- char *mode;
- char *rawmode;
+ char *mode_name;
+ char *rawmode_name;
Py_ssize_t stride = 0;
Py_ssize_t ystep = 1;
- if (!PyArg_ParseTuple(args, "ss|nn", &mode, &rawmode, &stride, &ystep)) {
+ if (!PyArg_ParseTuple(args, "ss|nn", &mode_name, &rawmode_name, &stride, &ystep)) {
return NULL;
}
@@ -453,6 +492,9 @@ PyImaging_RawEncoderNew(PyObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
if (get_packer(encoder, mode, rawmode) < 0) {
return NULL;
}
@@ -473,11 +515,11 @@ PyObject *
PyImaging_TgaRleEncoderNew(PyObject *self, PyObject *args) {
ImagingEncoderObject *encoder;
- char *mode;
- char *rawmode;
+ char *mode_name;
+ char *rawmode_name;
Py_ssize_t ystep = 1;
- if (!PyArg_ParseTuple(args, "ss|n", &mode, &rawmode, &ystep)) {
+ if (!PyArg_ParseTuple(args, "ss|n", &mode_name, &rawmode_name, &ystep)) {
return NULL;
}
@@ -486,6 +528,9 @@ PyImaging_TgaRleEncoderNew(PyObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
if (get_packer(encoder, mode, rawmode) < 0) {
return NULL;
}
@@ -510,7 +555,7 @@ PyImaging_XbmEncoderNew(PyObject *self, PyObject *args) {
return NULL;
}
- if (get_packer(encoder, "1", "1;R") < 0) {
+ if (get_packer(encoder, IMAGING_MODE_1, IMAGING_RAWMODE_1_R) < 0) {
return NULL;
}
@@ -531,8 +576,8 @@ PyObject *
PyImaging_ZipEncoderNew(PyObject *self, PyObject *args) {
ImagingEncoderObject *encoder;
- char *mode;
- char *rawmode;
+ char *mode_name;
+ char *rawmode_name;
Py_ssize_t optimize = 0;
Py_ssize_t compress_level = -1;
Py_ssize_t compress_type = -1;
@@ -541,8 +586,8 @@ PyImaging_ZipEncoderNew(PyObject *self, PyObject *args) {
if (!PyArg_ParseTuple(
args,
"ss|nnny#",
- &mode,
- &rawmode,
+ &mode_name,
+ &rawmode_name,
&optimize,
&compress_level,
&compress_type,
@@ -571,6 +616,9 @@ PyImaging_ZipEncoderNew(PyObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
if (get_packer(encoder, mode, rawmode) < 0) {
free(dictionary);
return NULL;
@@ -579,7 +627,7 @@ PyImaging_ZipEncoderNew(PyObject *self, PyObject *args) {
encoder->encode = ImagingZipEncode;
encoder->cleanup = ImagingZipEncodeCleanup;
- if (rawmode[0] == 'P') {
+ if (rawmode == IMAGING_RAWMODE_P || rawmode == IMAGING_RAWMODE_PA) {
/* disable filtering */
((ZIPSTATE *)encoder->state.context)->mode = ZIP_PNG_PALETTE;
}
@@ -608,8 +656,8 @@ PyObject *
PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
ImagingEncoderObject *encoder;
- char *mode;
- char *rawmode;
+ char *mode_name;
+ char *rawmode_name;
char *compname;
char *filename;
Py_ssize_t fp;
@@ -629,7 +677,15 @@ PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
PyObject *item;
if (!PyArg_ParseTuple(
- args, "sssnsOO", &mode, &rawmode, &compname, &fp, &filename, &tags, &types
+ args,
+ "sssnsOO",
+ &mode_name,
+ &rawmode_name,
+ &compname,
+ &fp,
+ &filename,
+ &tags,
+ &types
)) {
return NULL;
}
@@ -667,6 +723,9 @@ PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ const RawModeID rawmode = findRawModeID(rawmode_name);
+
if (get_packer(encoder, mode, rawmode) < 0) {
return NULL;
}
@@ -677,6 +736,8 @@ PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
return NULL;
}
+ encoder->cleanup = ImagingLibTiffEncodeCleanup;
+
num_core_tags = sizeof(core_tags) / sizeof(int);
for (pos = 0; pos < tags_size; pos++) {
item = PyList_GetItemRef(tags, pos);
@@ -894,6 +955,18 @@ PyImaging_LibTiffEncoderNew(PyObject *self, PyObject *args) {
);
free(av);
}
+ } else if (type == TIFF_RATIONAL) {
+ FLOAT32 *av;
+ /* malloc check ok, calloc checks for overflow */
+ av = calloc(len, sizeof(FLOAT32));
+ if (av) {
+ for (i = 0; i < len; i++) {
+ av[i] = (FLOAT32)PyFloat_AsDouble(PyTuple_GetItem(value, i));
+ }
+ status =
+ ImagingLibTiffSetField(&encoder->state, (ttag_t)key_int, av);
+ free(av);
+ }
}
} else {
if (type == TIFF_SHORT) {
@@ -1048,8 +1121,8 @@ PyObject *
PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) {
ImagingEncoderObject *encoder;
- char *mode;
- char *rawmode;
+ char *mode_name;
+ char *rawmode_name;
Py_ssize_t quality = 0;
Py_ssize_t progressive = 0;
Py_ssize_t smooth = 0;
@@ -1074,8 +1147,8 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) {
if (!PyArg_ParseTuple(
args,
"ss|nnnnppn(nn)nnnOz#y#y#",
- &mode,
- &rawmode,
+ &mode_name,
+ &rawmode_name,
&quality,
&progressive,
&smooth,
@@ -1104,11 +1177,14 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+ RawModeID rawmode = findRawModeID(rawmode_name);
+
// libjpeg-turbo supports different output formats.
// We are choosing Pillow's native format (3 color bytes + 1 padding)
// to avoid extra conversion in Pack.c.
- if (ImagingJpegUseJCSExtensions() && strcmp(rawmode, "RGB") == 0) {
- rawmode = "RGBX";
+ if (ImagingJpegUseJCSExtensions() && rawmode == IMAGING_RAWMODE_RGB) {
+ rawmode = IMAGING_RAWMODE_RGBX;
}
if (get_packer(encoder, mode, rawmode) < 0) {
@@ -1166,7 +1242,7 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) {
encoder->encode = ImagingJpegEncode;
JPEGENCODERSTATE *jpeg_encoder_state = (JPEGENCODERSTATE *)encoder->state.context;
- strncpy(jpeg_encoder_state->rawmode, rawmode, 8);
+ jpeg_encoder_state->rawmode = rawmode;
jpeg_encoder_state->keep_rgb = keep_rgb;
jpeg_encoder_state->no_default_app_segments = no_default_app_segments;
jpeg_encoder_state->quality = quality;
diff --git a/src/libImaging/Access.c b/src/libImaging/Access.c
index 1c1937105..c77a9c21c 100644
--- a/src/libImaging/Access.c
+++ b/src/libImaging/Access.c
@@ -11,39 +11,6 @@
#include "Imaging.h"
-/* use make_hash.py from the pillow-scripts repository to calculate these values */
-#define ACCESS_TABLE_SIZE 35
-#define ACCESS_TABLE_HASH 8940
-
-static struct ImagingAccessInstance access_table[ACCESS_TABLE_SIZE];
-
-static inline UINT32
-hash(const char *mode) {
- UINT32 i = ACCESS_TABLE_HASH;
- while (*mode) {
- i = ((i << 5) + i) ^ (UINT8)*mode++;
- }
- return i % ACCESS_TABLE_SIZE;
-}
-
-static ImagingAccess
-add_item(const char *mode) {
- UINT32 i = hash(mode);
- /* printf("hash %s => %d\n", mode, i); */
- if (access_table[i].mode && strcmp(access_table[i].mode, mode) != 0) {
- fprintf(
- stderr,
- "AccessInit: hash collision: %d for both %s and %s\n",
- i,
- mode,
- access_table[i].mode
- );
- exit(1);
- }
- access_table[i].mode = mode;
- return &access_table[i];
-}
-
/* fetch individual pixel */
static void
@@ -64,7 +31,7 @@ static void
get_pixel_16L(Imaging im, int x, int y, void *color) {
UINT8 *in = (UINT8 *)&im->image[y][x + x];
#ifdef WORDS_BIGENDIAN
- UINT16 out = in[0] + (in[1] << 8);
+ UINT16 out = in[0] + ((UINT16)in[1] << 8);
memcpy(color, &out, sizeof(out));
#else
memcpy(color, in, sizeof(UINT16));
@@ -77,63 +44,16 @@ get_pixel_16B(Imaging im, int x, int y, void *color) {
#ifdef WORDS_BIGENDIAN
memcpy(color, in, sizeof(UINT16));
#else
- UINT16 out = in[1] + (in[0] << 8);
+ UINT16 out = in[1] + ((UINT16)in[0] << 8);
memcpy(color, &out, sizeof(out));
#endif
}
-static void
-get_pixel_BGR15(Imaging im, int x, int y, void *color) {
- UINT8 *in = (UINT8 *)&im->image8[y][x * 2];
- UINT16 pixel = in[0] + (in[1] << 8);
- char *out = color;
- out[0] = (pixel & 31) * 255 / 31;
- out[1] = ((pixel >> 5) & 31) * 255 / 31;
- out[2] = ((pixel >> 10) & 31) * 255 / 31;
-}
-
-static void
-get_pixel_BGR16(Imaging im, int x, int y, void *color) {
- UINT8 *in = (UINT8 *)&im->image8[y][x * 2];
- UINT16 pixel = in[0] + (in[1] << 8);
- char *out = color;
- out[0] = (pixel & 31) * 255 / 31;
- out[1] = ((pixel >> 5) & 63) * 255 / 63;
- out[2] = ((pixel >> 11) & 31) * 255 / 31;
-}
-
-static void
-get_pixel_BGR24(Imaging im, int x, int y, void *color) {
- memcpy(color, &im->image8[y][x * 3], sizeof(UINT8) * 3);
-}
-
static void
get_pixel_32(Imaging im, int x, int y, void *color) {
memcpy(color, &im->image32[y][x], sizeof(INT32));
}
-static void
-get_pixel_32L(Imaging im, int x, int y, void *color) {
- UINT8 *in = (UINT8 *)&im->image[y][x * 4];
-#ifdef WORDS_BIGENDIAN
- INT32 out = in[0] + (in[1] << 8) + (in[2] << 16) + (in[3] << 24);
- memcpy(color, &out, sizeof(out));
-#else
- memcpy(color, in, sizeof(INT32));
-#endif
-}
-
-static void
-get_pixel_32B(Imaging im, int x, int y, void *color) {
- UINT8 *in = (UINT8 *)&im->image[y][x * 4];
-#ifdef WORDS_BIGENDIAN
- memcpy(color, in, sizeof(INT32));
-#else
- INT32 out = in[3] + (in[2] << 8) + (in[1] << 16) + (in[0] << 24);
- memcpy(color, &out, sizeof(out));
-#endif
-}
-
/* store individual pixel */
static void
@@ -154,84 +74,46 @@ put_pixel_16B(Imaging im, int x, int y, const void *color) {
out[1] = in[0];
}
-static void
-put_pixel_BGR1516(Imaging im, int x, int y, const void *color) {
- memcpy(&im->image8[y][x * 2], color, 2);
-}
-
-static void
-put_pixel_BGR24(Imaging im, int x, int y, const void *color) {
- memcpy(&im->image8[y][x * 3], color, 3);
-}
-
-static void
-put_pixel_32L(Imaging im, int x, int y, const void *color) {
- memcpy(&im->image8[y][x * 4], color, 4);
-}
-
-static void
-put_pixel_32B(Imaging im, int x, int y, const void *color) {
- const char *in = color;
- UINT8 *out = (UINT8 *)&im->image8[y][x * 4];
- out[0] = in[3];
- out[1] = in[2];
- out[2] = in[1];
- out[3] = in[0];
-}
-
static void
put_pixel_32(Imaging im, int x, int y, const void *color) {
memcpy(&im->image32[y][x], color, sizeof(INT32));
}
-void
-ImagingAccessInit(void) {
-#define ADD(mode_, get_pixel_, put_pixel_) \
- { \
- ImagingAccess access = add_item(mode_); \
- access->get_pixel = get_pixel_; \
- access->put_pixel = put_pixel_; \
- }
-
- /* populate access table */
- ADD("1", get_pixel_8, put_pixel_8);
- ADD("L", get_pixel_8, put_pixel_8);
- ADD("LA", get_pixel_32_2bands, put_pixel_32);
- ADD("La", get_pixel_32_2bands, put_pixel_32);
- ADD("I", get_pixel_32, put_pixel_32);
- ADD("I;16", get_pixel_16L, put_pixel_16L);
- ADD("I;16L", get_pixel_16L, put_pixel_16L);
- ADD("I;16B", get_pixel_16B, put_pixel_16B);
+static struct ImagingAccessInstance accessors[] = {
+ {IMAGING_MODE_1, get_pixel_8, put_pixel_8},
+ {IMAGING_MODE_L, get_pixel_8, put_pixel_8},
+ {IMAGING_MODE_LA, get_pixel_32_2bands, put_pixel_32},
+ {IMAGING_MODE_La, get_pixel_32_2bands, put_pixel_32},
+ {IMAGING_MODE_I, get_pixel_32, put_pixel_32},
+ {IMAGING_MODE_I_16, get_pixel_16L, put_pixel_16L},
+ {IMAGING_MODE_I_16L, get_pixel_16L, put_pixel_16L},
+ {IMAGING_MODE_I_16B, get_pixel_16B, put_pixel_16B},
#ifdef WORDS_BIGENDIAN
- ADD("I;16N", get_pixel_16B, put_pixel_16B);
+ {IMAGING_MODE_I_16N, get_pixel_16B, put_pixel_16B},
#else
- ADD("I;16N", get_pixel_16L, put_pixel_16L);
+ {IMAGING_MODE_I_16N, get_pixel_16L, put_pixel_16L},
#endif
- ADD("I;32L", get_pixel_32L, put_pixel_32L);
- ADD("I;32B", get_pixel_32B, put_pixel_32B);
- ADD("F", get_pixel_32, put_pixel_32);
- ADD("P", get_pixel_8, put_pixel_8);
- ADD("PA", get_pixel_32_2bands, put_pixel_32);
- ADD("BGR;15", get_pixel_BGR15, put_pixel_BGR1516);
- ADD("BGR;16", get_pixel_BGR16, put_pixel_BGR1516);
- ADD("BGR;24", get_pixel_BGR24, put_pixel_BGR24);
- ADD("RGB", get_pixel_32, put_pixel_32);
- ADD("RGBA", get_pixel_32, put_pixel_32);
- ADD("RGBa", get_pixel_32, put_pixel_32);
- ADD("RGBX", get_pixel_32, put_pixel_32);
- ADD("CMYK", get_pixel_32, put_pixel_32);
- ADD("YCbCr", get_pixel_32, put_pixel_32);
- ADD("LAB", get_pixel_32, put_pixel_32);
- ADD("HSV", get_pixel_32, put_pixel_32);
-}
+ {IMAGING_MODE_F, get_pixel_32, put_pixel_32},
+ {IMAGING_MODE_P, get_pixel_8, put_pixel_8},
+ {IMAGING_MODE_PA, get_pixel_32_2bands, put_pixel_32},
+ {IMAGING_MODE_RGB, get_pixel_32, put_pixel_32},
+ {IMAGING_MODE_RGBA, get_pixel_32, put_pixel_32},
+ {IMAGING_MODE_RGBa, get_pixel_32, put_pixel_32},
+ {IMAGING_MODE_RGBX, get_pixel_32, put_pixel_32},
+ {IMAGING_MODE_CMYK, get_pixel_32, put_pixel_32},
+ {IMAGING_MODE_YCbCr, get_pixel_32, put_pixel_32},
+ {IMAGING_MODE_LAB, get_pixel_32, put_pixel_32},
+ {IMAGING_MODE_HSV, get_pixel_32, put_pixel_32},
+};
ImagingAccess
-ImagingAccessNew(Imaging im) {
- ImagingAccess access = &access_table[hash(im->mode)];
- if (im->mode[0] != access->mode[0] || strcmp(im->mode, access->mode) != 0) {
- return NULL;
+ImagingAccessNew(const Imaging im) {
+ for (size_t i = 0; i < sizeof(accessors) / sizeof(*accessors); i++) {
+ if (im->mode == accessors[i].mode) {
+ return &accessors[i];
+ }
}
- return access;
+ return NULL;
}
void
diff --git a/src/libImaging/AlphaComposite.c b/src/libImaging/AlphaComposite.c
index 6d728f908..280277e83 100644
--- a/src/libImaging/AlphaComposite.c
+++ b/src/libImaging/AlphaComposite.c
@@ -25,13 +25,12 @@ ImagingAlphaComposite(Imaging imDst, Imaging imSrc) {
int x, y;
/* Check arguments */
- if (!imDst || !imSrc || strcmp(imDst->mode, "RGBA") ||
- imDst->type != IMAGING_TYPE_UINT8 || imDst->bands != 4) {
+ if (!imDst || !imSrc ||
+ (imDst->mode != IMAGING_MODE_RGBA && imDst->mode != IMAGING_MODE_LA)) {
return ImagingError_ModeError();
}
- if (strcmp(imDst->mode, imSrc->mode) || imDst->type != imSrc->type ||
- imDst->bands != imSrc->bands || imDst->xsize != imSrc->xsize ||
+ if (imDst->mode != imSrc->mode || imDst->xsize != imSrc->xsize ||
imDst->ysize != imSrc->ysize) {
return ImagingError_Mismatch();
}
diff --git a/src/libImaging/Arrow.c b/src/libImaging/Arrow.c
new file mode 100644
index 000000000..d2ed10f0a
--- /dev/null
+++ b/src/libImaging/Arrow.c
@@ -0,0 +1,417 @@
+
+#include "Arrow.h"
+#include "Imaging.h"
+#include
+
+/* struct ArrowSchema* */
+/* _arrow_schema_channel(char* channel, char* format) { */
+
+/* } */
+
+static void
+ReleaseExportedSchema(struct ArrowSchema *array) {
+ // This should not be called on already released array
+ // assert(array->release != NULL);
+
+ if (!array->release) {
+ return;
+ }
+ if (array->format) {
+ free((void *)array->format);
+ array->format = NULL;
+ }
+ if (array->name) {
+ free((void *)array->name);
+ array->name = NULL;
+ }
+ if (array->metadata) {
+ free((void *)array->metadata);
+ array->metadata = NULL;
+ }
+
+ // Release children
+ for (int64_t i = 0; i < array->n_children; ++i) {
+ struct ArrowSchema *child = array->children[i];
+ if (child->release != NULL) {
+ child->release(child);
+ child->release = NULL;
+ }
+ free(array->children[i]);
+ }
+ if (array->children) {
+ free(array->children);
+ }
+
+ // Release dictionary
+ struct ArrowSchema *dict = array->dictionary;
+ if (dict != NULL && dict->release != NULL) {
+ dict->release(dict);
+ dict->release = NULL;
+ }
+
+ // TODO here: release and/or deallocate all data directly owned by
+ // the ArrowArray struct, such as the private_data.
+
+ // Mark array released
+ array->release = NULL;
+}
+char *
+image_band_json(Imaging im) {
+ char *format = "{\"bands\": [\"%s\", \"%s\", \"%s\", \"%s\"]}";
+ char *json;
+ // Bands can be 4 bands * 2 characters each
+ int len = strlen(format) + 8 + 1;
+ int err;
+
+ json = calloc(1, len);
+
+ if (!json) {
+ return NULL;
+ }
+
+ err = PyOS_snprintf(
+ json,
+ len,
+ format,
+ im->band_names[0],
+ im->band_names[1],
+ im->band_names[2],
+ im->band_names[3]
+ );
+ if (err < 0) {
+ return NULL;
+ }
+ return json;
+}
+
+char *
+single_band_json(Imaging im) {
+ char *format = "{\"bands\": [\"%s\"]}";
+ char *json;
+ // Bands can be 1 band * (maybe but probably not) 2 characters each
+ int len = strlen(format) + 2 + 1;
+ int err;
+
+ json = calloc(1, len);
+
+ if (!json) {
+ return NULL;
+ }
+
+ err = PyOS_snprintf(json, len, format, im->band_names[0]);
+ if (err < 0) {
+ return NULL;
+ }
+ return json;
+}
+
+char *
+assemble_metadata(const char *band_json) {
+ /* format is
+ int32: number of key/value pairs (noted N below)
+ int32: byte length of key 0
+ key 0 (not null-terminated)
+ int32: byte length of value 0
+ value 0 (not null-terminated)
+ ...
+ int32: byte length of key N - 1
+ key N - 1 (not null-terminated)
+ int32: byte length of value N - 1
+ value N - 1 (not null-terminated)
+ */
+ const char *key = "image";
+ INT32 key_len = strlen(key);
+ INT32 band_json_len = strlen(band_json);
+
+ char *buf;
+ INT32 *dest_int;
+ char *dest;
+
+ buf = calloc(1, key_len + band_json_len + 4 + 1 * 8);
+ if (!buf) {
+ return NULL;
+ }
+
+ dest_int = (void *)buf;
+
+ dest_int[0] = 1;
+ dest_int[1] = key_len;
+ dest_int += 2;
+ dest = (void *)dest_int;
+ memcpy(dest, key, key_len);
+ dest += key_len;
+ dest_int = (void *)dest;
+ dest_int[0] = band_json_len;
+ dest_int += 1;
+ memcpy(dest_int, band_json, band_json_len);
+
+ return buf;
+}
+
+int
+export_named_type(struct ArrowSchema *schema, char *format, const char *name) {
+ char *formatp;
+ char *namep;
+ size_t format_len = strlen(format) + 1;
+ size_t name_len = strlen(name) + 1;
+
+ formatp = calloc(format_len, 1);
+
+ if (!formatp) {
+ return IMAGING_CODEC_MEMORY;
+ }
+
+ namep = calloc(name_len, 1);
+ if (!namep) {
+ free(formatp);
+ return IMAGING_CODEC_MEMORY;
+ }
+
+ strncpy(formatp, format, format_len);
+ strncpy(namep, name, name_len);
+
+ *schema = (struct ArrowSchema){// Type description
+ .format = formatp,
+ .name = namep,
+ .metadata = NULL,
+ .flags = 0,
+ .n_children = 0,
+ .children = NULL,
+ .dictionary = NULL,
+ // Bookkeeping
+ .release = &ReleaseExportedSchema
+ };
+ return 0;
+}
+
+int
+export_imaging_schema(Imaging im, struct ArrowSchema *schema) {
+ int retval = 0;
+ char *band_json;
+
+ if (strcmp(im->arrow_band_format, "") == 0) {
+ return IMAGING_ARROW_INCOMPATIBLE_MODE;
+ }
+
+ /* for now, single block images */
+ if (im->blocks_count > 1) {
+ return IMAGING_ARROW_MEMORY_LAYOUT;
+ }
+
+ if (im->bands == 1) {
+ retval = export_named_type(schema, im->arrow_band_format, im->band_names[0]);
+ if (retval != 0) {
+ return retval;
+ }
+ // band related metadata
+ band_json = single_band_json(im);
+ if (band_json) {
+ schema->metadata = assemble_metadata(band_json);
+ free(band_json);
+ }
+ return retval;
+ }
+
+ retval = export_named_type(schema, "+w:4", "");
+ if (retval != 0) {
+ return retval;
+ }
+ // if it's not 1 band, it's an int32 at the moment. 4 uint8 bands.
+ schema->n_children = 1;
+ schema->children = calloc(1, sizeof(struct ArrowSchema *));
+ schema->children[0] = (struct ArrowSchema *)calloc(1, sizeof(struct ArrowSchema));
+ retval = export_named_type(
+ schema->children[0], im->arrow_band_format, getModeData(im->mode)->name
+ );
+ if (retval != 0) {
+ free(schema->children[0]);
+ free(schema->children);
+ schema->release(schema);
+ return retval;
+ }
+
+ // band related metadata
+ band_json = image_band_json(im);
+ if (band_json) {
+ // adding the metadata to the child array.
+ // Accessible in pyarrow via pa.array(img).type.field(0).metadata
+ // adding it to the top level is not accessible.
+ schema->children[0]->metadata = assemble_metadata(band_json);
+ free(band_json);
+ }
+
+ return 0;
+}
+
+static void
+release_const_array(struct ArrowArray *array) {
+ Imaging im = (Imaging)array->private_data;
+
+ ImagingDelete(im);
+
+ // Free the buffers and the buffers array
+ if (array->buffers) {
+ free(array->buffers);
+ array->buffers = NULL;
+ }
+ if (array->children) {
+ // undone -- does arrow release all the children recursively?
+ for (int i = 0; i < array->n_children; i++) {
+ if (array->children[i]->release) {
+ array->children[i]->release(array->children[i]);
+ array->children[i]->release = NULL;
+ free(array->children[i]);
+ }
+ }
+ free(array->children);
+ array->children = NULL;
+ }
+ // Mark released
+ array->release = NULL;
+}
+
+int
+export_single_channel_array(Imaging im, struct ArrowArray *array) {
+ int length = im->xsize * im->ysize;
+
+ /* for now, single block images */
+ if (im->blocks_count > 1) {
+ return IMAGING_ARROW_MEMORY_LAYOUT;
+ }
+
+ if (im->lines_per_block && im->lines_per_block < im->ysize) {
+ length = im->xsize * im->lines_per_block;
+ }
+
+ MUTEX_LOCK(&im->mutex);
+ im->refcount++;
+ MUTEX_UNLOCK(&im->mutex);
+ // Initialize primitive fields
+ *array = (struct ArrowArray){// Data description
+ .length = length,
+ .offset = 0,
+ .null_count = 0,
+ .n_buffers = 2,
+ .n_children = 0,
+ .children = NULL,
+ .dictionary = NULL,
+ // Bookkeeping
+ .release = &release_const_array,
+ .private_data = im
+ };
+
+ // Allocate list of buffers
+ array->buffers = (const void **)malloc(sizeof(void *) * array->n_buffers);
+ // assert(array->buffers != NULL);
+ array->buffers[0] = NULL; // no nulls, null bitmap can be omitted
+
+ if (im->block) {
+ array->buffers[1] = im->block;
+ } else {
+ array->buffers[1] = im->blocks[0].ptr;
+ }
+ return 0;
+}
+
+int
+export_fixed_pixel_array(Imaging im, struct ArrowArray *array) {
+ int length = im->xsize * im->ysize;
+
+ /* for now, single block images */
+ if (im->blocks_count > 1) {
+ return IMAGING_ARROW_MEMORY_LAYOUT;
+ }
+
+ if (im->lines_per_block && im->lines_per_block < im->ysize) {
+ length = im->xsize * im->lines_per_block;
+ }
+
+ MUTEX_LOCK(&im->mutex);
+ im->refcount++;
+ MUTEX_UNLOCK(&im->mutex);
+ // Initialize primitive fields
+ // Fixed length arrays are 1 buffer of validity, and the length in pixels.
+ // Data is in a child array.
+ *array = (struct ArrowArray){// Data description
+ .length = length,
+ .offset = 0,
+ .null_count = 0,
+ .n_buffers = 1,
+ .n_children = 1,
+ .children = NULL,
+ .dictionary = NULL,
+ // Bookkeeping
+ .release = &release_const_array,
+ .private_data = im
+ };
+
+ // Allocate list of buffers
+ array->buffers = (const void **)calloc(1, sizeof(void *) * array->n_buffers);
+ if (!array->buffers) {
+ goto err;
+ }
+ // assert(array->buffers != NULL);
+ array->buffers[0] = NULL; // no nulls, null bitmap can be omitted
+
+ // if it's not 1 band, it's an int32 at the moment. 4 uint8 bands.
+ array->n_children = 1;
+ array->children = calloc(1, sizeof(struct ArrowArray *));
+ if (!array->children) {
+ goto err;
+ }
+ array->children[0] = (struct ArrowArray *)calloc(1, sizeof(struct ArrowArray));
+ if (!array->children[0]) {
+ goto err;
+ }
+
+ MUTEX_LOCK(&im->mutex);
+ im->refcount++;
+ MUTEX_UNLOCK(&im->mutex);
+ *array->children[0] = (struct ArrowArray){// Data description
+ .length = length * 4,
+ .offset = 0,
+ .null_count = 0,
+ .n_buffers = 2,
+ .n_children = 0,
+ .children = NULL,
+ .dictionary = NULL,
+ // Bookkeeping
+ .release = &release_const_array,
+ .private_data = im
+ };
+
+ array->children[0]->buffers =
+ (const void **)calloc(2, sizeof(void *) * array->n_buffers);
+
+ if (im->block) {
+ array->children[0]->buffers[1] = im->block;
+ } else {
+ array->children[0]->buffers[1] = im->blocks[0].ptr;
+ }
+ return 0;
+
+err:
+ if (array->children[0]) {
+ free(array->children[0]);
+ }
+ if (array->children) {
+ free(array->children);
+ }
+ if (array->buffers) {
+ free(array->buffers);
+ }
+ return IMAGING_CODEC_MEMORY;
+}
+
+int
+export_imaging_array(Imaging im, struct ArrowArray *array) {
+ if (strcmp(im->arrow_band_format, "") == 0) {
+ return IMAGING_ARROW_INCOMPATIBLE_MODE;
+ }
+
+ if (im->bands == 1) {
+ return export_single_channel_array(im, array);
+ }
+
+ return export_fixed_pixel_array(im, array);
+}
diff --git a/src/libImaging/Arrow.h b/src/libImaging/Arrow.h
new file mode 100644
index 000000000..0b285fe80
--- /dev/null
+++ b/src/libImaging/Arrow.h
@@ -0,0 +1,48 @@
+#include
+#include
+
+// Apache License 2.0.
+// Source apache arrow project
+// https://arrow.apache.org/docs/format/CDataInterface.html
+
+#ifndef ARROW_C_DATA_INTERFACE
+#define ARROW_C_DATA_INTERFACE
+
+#define ARROW_FLAG_DICTIONARY_ORDERED 1
+#define ARROW_FLAG_NULLABLE 2
+#define ARROW_FLAG_MAP_KEYS_SORTED 4
+
+struct ArrowSchema {
+ // Array type description
+ const char *format;
+ const char *name;
+ const char *metadata;
+ int64_t flags;
+ int64_t n_children;
+ struct ArrowSchema **children;
+ struct ArrowSchema *dictionary;
+
+ // Release callback
+ void (*release)(struct ArrowSchema *);
+ // Opaque producer-specific data
+ void *private_data;
+};
+
+struct ArrowArray {
+ // Array data description
+ int64_t length;
+ int64_t null_count;
+ int64_t offset;
+ int64_t n_buffers;
+ int64_t n_children;
+ const void **buffers;
+ struct ArrowArray **children;
+ struct ArrowArray *dictionary;
+
+ // Release callback
+ void (*release)(struct ArrowArray *);
+ // Opaque producer-specific data
+ void *private_data;
+};
+
+#endif // ARROW_C_DATA_INTERFACE
diff --git a/src/libImaging/Bands.c b/src/libImaging/Bands.c
index e1b16b34a..d1b0ebc4e 100644
--- a/src/libImaging/Bands.c
+++ b/src/libImaging/Bands.c
@@ -41,7 +41,7 @@ ImagingGetBand(Imaging imIn, int band) {
band = 3;
}
- imOut = ImagingNewDirty("L", imIn->xsize, imIn->ysize);
+ imOut = ImagingNewDirty(IMAGING_MODE_L, imIn->xsize, imIn->ysize);
if (!imOut) {
return NULL;
}
@@ -82,7 +82,7 @@ ImagingSplit(Imaging imIn, Imaging bands[4]) {
}
for (i = 0; i < imIn->bands; i++) {
- bands[i] = ImagingNewDirty("L", imIn->xsize, imIn->ysize);
+ bands[i] = ImagingNewDirty(IMAGING_MODE_L, imIn->xsize, imIn->ysize);
if (!bands[i]) {
for (j = 0; j < i; ++j) {
ImagingDelete(bands[j]);
@@ -240,7 +240,7 @@ ImagingFillBand(Imaging imOut, int band, int color) {
}
Imaging
-ImagingMerge(const char *mode, Imaging bands[4]) {
+ImagingMerge(const ModeID mode, Imaging bands[4]) {
int i, x, y;
int bandsCount = 0;
Imaging imOut;
diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c
index 9a41febc7..ac81ed6df 100644
--- a/src/libImaging/BcnDecode.c
+++ b/src/libImaging/BcnDecode.c
@@ -25,7 +25,6 @@ typedef struct {
typedef struct {
UINT16 c0, c1;
- UINT32 lut;
} bc1_color;
typedef struct {
@@ -40,13 +39,10 @@ typedef struct {
#define LOAD16(p) (p)[0] | ((p)[1] << 8)
-#define LOAD32(p) (p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24)
-
static void
bc1_color_load(bc1_color *dst, const UINT8 *src) {
dst->c0 = LOAD16(src);
dst->c1 = LOAD16(src + 2);
- dst->lut = LOAD32(src + 4);
}
static rgba
@@ -70,7 +66,7 @@ static void
decode_bc1_color(rgba *dst, const UINT8 *src, int separate_alpha) {
bc1_color col;
rgba p[4];
- int n, cw;
+ int n, o, cw;
UINT16 r0, g0, b0, r1, g1, b1;
bc1_color_load(&col, src);
@@ -103,9 +99,11 @@ decode_bc1_color(rgba *dst, const UINT8 *src, int separate_alpha) {
p[3].b = 0;
p[3].a = 0;
}
- for (n = 0; n < 16; n++) {
- cw = 3 & (col.lut >> (2 * n));
- dst[n] = p[cw];
+ for (n = 0; n < 4; n++) {
+ for (o = 0; o < 4; o++) {
+ cw = 3 & ((src + 4)[n] >> (2 * o));
+ dst[n * 4 + o] = p[cw];
+ }
}
}
@@ -605,7 +603,7 @@ static void
bc6_sign_extend(UINT16 *v, int prec) {
int x = *v;
if (x & (1 << (prec - 1))) {
- x |= -1 << prec;
+ x |= -(1 << prec);
}
*v = (UINT16)x;
}
diff --git a/src/libImaging/BcnEncode.c b/src/libImaging/BcnEncode.c
new file mode 100644
index 000000000..973a7a2fa
--- /dev/null
+++ b/src/libImaging/BcnEncode.c
@@ -0,0 +1,302 @@
+/*
+ * The Python Imaging Library
+ *
+ * encoder for DXT1-compressed data
+ *
+ * Format documentation:
+ * https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt
+ *
+ */
+
+#include "Imaging.h"
+
+typedef struct {
+ UINT8 color[3];
+} rgb;
+
+typedef struct {
+ UINT8 color[4];
+} rgba;
+
+static rgb
+decode_565(UINT16 x) {
+ rgb item;
+ int r, g, b;
+ r = (x & 0xf800) >> 8;
+ r |= r >> 5;
+ item.color[0] = r;
+ g = (x & 0x7e0) >> 3;
+ g |= g >> 6;
+ item.color[1] = g;
+ b = (x & 0x1f) << 3;
+ b |= b >> 5;
+ item.color[2] = b;
+ return item;
+}
+
+static UINT16
+encode_565(rgba item) {
+ UINT16 r = item.color[0] >> (8 - 5);
+ UINT8 g = item.color[1] >> (8 - 6);
+ UINT8 b = item.color[2] >> (8 - 5);
+ return (r << (5 + 6)) | (g << 5) | b;
+}
+
+static void
+encode_bc1_color(Imaging im, ImagingCodecState state, UINT8 *dst, int separate_alpha) {
+ int i, j, k;
+ UINT16 color_min = 0, color_max = 0;
+ rgb color_min_rgb, color_max_rgb;
+ rgba block[16], *current_rgba;
+
+ // Determine the min and max colors in this 4x4 block
+ int first = 1;
+ int transparency = 0;
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ current_rgba = &block[i + j * 4];
+
+ int x = state->x + i * im->pixelsize;
+ int y = state->y + j;
+ if (x >= state->xsize * im->pixelsize || y >= state->ysize) {
+ // The 4x4 block extends past the edge of the image
+ for (k = 0; k < 3; k++) {
+ current_rgba->color[k] = 0;
+ }
+ continue;
+ }
+
+ for (k = 0; k < 3; k++) {
+ current_rgba->color[k] =
+ (UINT8)im->image[y][x + (im->pixelsize == 1 ? 0 : k)];
+ }
+ if (separate_alpha) {
+ if ((UINT8)im->image[y][x + 3] == 0) {
+ current_rgba->color[3] = 0;
+ transparency = 1;
+ continue;
+ } else {
+ current_rgba->color[3] = 1;
+ }
+ }
+
+ UINT16 color = encode_565(*current_rgba);
+ if (first || color < color_min) {
+ color_min = color;
+ }
+ if (first || color > color_max) {
+ color_max = color;
+ }
+ first = 0;
+ }
+ }
+
+ if (transparency) {
+ *dst++ = color_min;
+ *dst++ = color_min >> 8;
+ }
+ *dst++ = color_max;
+ *dst++ = color_max >> 8;
+ if (!transparency) {
+ *dst++ = color_min;
+ *dst++ = color_min >> 8;
+ }
+
+ color_min_rgb = decode_565(color_min);
+ color_max_rgb = decode_565(color_max);
+ for (i = 0; i < 4; i++) {
+ UINT8 l = 0;
+ for (j = 3; j > -1; j--) {
+ current_rgba = &block[i * 4 + j];
+ if (transparency && !current_rgba->color[3]) {
+ l |= 3 << (j * 2);
+ continue;
+ }
+
+ float distance = 0;
+ int total = 0;
+ for (k = 0; k < 3; k++) {
+ float denom =
+ (float)abs(color_max_rgb.color[k] - color_min_rgb.color[k]);
+ if (denom != 0) {
+ distance +=
+ abs(current_rgba->color[k] - color_min_rgb.color[k]) / denom;
+ total += 1;
+ }
+ }
+ if (total == 0) {
+ continue;
+ }
+ if (transparency) {
+ distance *= 4 / total;
+ if (distance < 1) {
+ // color_max
+ } else if (distance < 3) {
+ l |= 2 << (j * 2); // 1/2 * color_min + 1/2 * color_max
+ } else {
+ l |= 1 << (j * 2); // color_min
+ }
+ } else {
+ distance *= 6 / total;
+ if (distance < 1) {
+ l |= 1 << (j * 2); // color_min
+ } else if (distance < 3) {
+ l |= 3 << (j * 2); // 1/3 * color_min + 2/3 * color_max
+ } else if (distance < 5) {
+ l |= 2 << (j * 2); // 2/3 * color_min + 1/3 * color_max
+ } else {
+ // color_max
+ }
+ }
+ }
+ *dst++ = l;
+ }
+}
+
+static void
+encode_bc2_block(Imaging im, ImagingCodecState state, UINT8 *dst) {
+ int i, j;
+ UINT8 block[16];
+ UINT32 current_alpha;
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ int x = state->x + i * im->pixelsize;
+ int y = state->y + j;
+ if (x >= state->xsize * im->pixelsize || y >= state->ysize) {
+ // The 4x4 block extends past the edge of the image
+ block[i + j * 4] = 0;
+ continue;
+ }
+
+ current_alpha = (UINT8)im->image[y][x + 3];
+ block[i + j * 4] = current_alpha;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ UINT16 l = 0;
+ for (j = 3; j > -1; j--) {
+ current_alpha = block[i * 4 + j];
+ l |= current_alpha << (j * 4);
+ }
+ *dst++ = l;
+ *dst++ = l >> 8;
+ }
+}
+
+static void
+encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst, int o) {
+ int i, j;
+ UINT8 alpha_min = 0, alpha_max = 0;
+ UINT8 block[16], current_alpha;
+
+ // Determine the min and max colors in this 4x4 block
+ int first = 1;
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ int x = state->x + i * im->pixelsize;
+ int y = state->y + j;
+ if (x >= state->xsize * im->pixelsize || y >= state->ysize) {
+ // The 4x4 block extends past the edge of the image
+ block[i + j * 4] = 0;
+ continue;
+ }
+
+ current_alpha = (UINT8)im->image[y][x + o];
+ block[i + j * 4] = current_alpha;
+
+ if (first || current_alpha < alpha_min) {
+ alpha_min = current_alpha;
+ }
+ if (first || current_alpha > alpha_max) {
+ alpha_max = current_alpha;
+ }
+ first = 0;
+ }
+ }
+
+ *dst++ = alpha_min;
+ *dst++ = alpha_max;
+
+ float denom = (float)abs(alpha_max - alpha_min);
+ for (i = 0; i < 2; i++) {
+ UINT32 l = 0;
+ for (j = 7; j > -1; j--) {
+ current_alpha = block[i * 8 + j];
+ if (!current_alpha) {
+ l |= 6 << (j * 3);
+ continue;
+ } else if (current_alpha == 255) {
+ l |= 7 << (j * 3);
+ continue;
+ }
+
+ float distance =
+ denom == 0 ? 0 : abs(current_alpha - alpha_min) / denom * 10;
+ if (distance < 3) {
+ l |= 2 << (j * 3); // 4/5 * alpha_min + 1/5 * alpha_max
+ } else if (distance < 5) {
+ l |= 3 << (j * 3); // 3/5 * alpha_min + 2/5 * alpha_max
+ } else if (distance < 7) {
+ l |= 4 << (j * 3); // 2/5 * alpha_min + 3/5 * alpha_max
+ } else {
+ l |= 5 << (j * 3); // 1/5 * alpha_min + 4/5 * alpha_max
+ }
+ }
+ *dst++ = l;
+ *dst++ = l >> 8;
+ *dst++ = l >> 16;
+ }
+}
+
+int
+ImagingBcnEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
+ int n = state->state;
+ int has_alpha_channel =
+ im->mode == IMAGING_MODE_RGBA || im->mode == IMAGING_MODE_LA;
+
+ UINT8 *dst = buf;
+
+ for (;;) {
+ // Loop writes a max of 16 bytes per iteration
+ if (dst + 16 >= bytes + buf) {
+ break;
+ }
+ if (n == 5) {
+ encode_bc3_alpha(im, state, dst, 0);
+ dst += 8;
+
+ encode_bc3_alpha(im, state, dst, 1);
+ } else {
+ if (n == 2 || n == 3) {
+ if (has_alpha_channel) {
+ if (n == 2) {
+ encode_bc2_block(im, state, dst);
+ } else {
+ encode_bc3_alpha(im, state, dst, 3);
+ }
+ dst += 8;
+ } else {
+ for (int i = 0; i < 8; i++) {
+ *dst++ = 0xff;
+ }
+ }
+ }
+ encode_bc1_color(im, state, dst, n == 1 && has_alpha_channel);
+ }
+ dst += 8;
+
+ state->x += im->pixelsize * 4;
+
+ if (state->x >= state->xsize * im->pixelsize) {
+ state->x = 0;
+ state->y += 4;
+ if (state->y >= state->ysize) {
+ state->errcode = IMAGING_CODEC_END;
+ break;
+ }
+ }
+ }
+
+ return dst - buf;
+}
diff --git a/src/libImaging/Blend.c b/src/libImaging/Blend.c
index a53ae0fad..df94920f6 100644
--- a/src/libImaging/Blend.c
+++ b/src/libImaging/Blend.c
@@ -24,8 +24,8 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) {
/* Check arguments */
if (!imIn1 || !imIn2 || imIn1->type != IMAGING_TYPE_UINT8 || imIn1->palette ||
- strcmp(imIn1->mode, "1") == 0 || imIn2->palette ||
- strcmp(imIn2->mode, "1") == 0) {
+ imIn1->mode == IMAGING_MODE_1 || imIn2->palette ||
+ imIn2->mode == IMAGING_MODE_1) {
return ImagingError_ModeError();
}
diff --git a/src/libImaging/BoxBlur.c b/src/libImaging/BoxBlur.c
index ed91541fe..4fea4fe44 100644
--- a/src/libImaging/BoxBlur.c
+++ b/src/libImaging/BoxBlur.c
@@ -248,7 +248,7 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n)
return ImagingError_ValueError("radius must be >= 0");
}
- if (strcmp(imIn->mode, imOut->mode) || imIn->type != imOut->type ||
+ if (imIn->mode != imOut->mode || imIn->type != imOut->type ||
imIn->bands != imOut->bands || imIn->xsize != imOut->xsize ||
imIn->ysize != imOut->ysize) {
return ImagingError_Mismatch();
@@ -258,10 +258,10 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n)
return ImagingError_ModeError();
}
- if (!(strcmp(imIn->mode, "RGB") == 0 || strcmp(imIn->mode, "RGBA") == 0 ||
- strcmp(imIn->mode, "RGBa") == 0 || strcmp(imIn->mode, "RGBX") == 0 ||
- strcmp(imIn->mode, "CMYK") == 0 || strcmp(imIn->mode, "L") == 0 ||
- strcmp(imIn->mode, "LA") == 0 || strcmp(imIn->mode, "La") == 0)) {
+ if (imIn->mode != IMAGING_MODE_RGB && imIn->mode != IMAGING_MODE_RGBA &&
+ imIn->mode != IMAGING_MODE_RGBa && imIn->mode != IMAGING_MODE_RGBX &&
+ imIn->mode != IMAGING_MODE_CMYK && imIn->mode != IMAGING_MODE_L &&
+ imIn->mode != IMAGING_MODE_LA && imIn->mode != IMAGING_MODE_La) {
return ImagingError_ModeError();
}
diff --git a/src/libImaging/Chops.c b/src/libImaging/Chops.c
index f326d402f..3ce8a0903 100644
--- a/src/libImaging/Chops.c
+++ b/src/libImaging/Chops.c
@@ -18,28 +18,28 @@
#include "Imaging.h"
-#define CHOP(operation) \
- int x, y; \
- Imaging imOut; \
- imOut = create(imIn1, imIn2, NULL); \
- if (!imOut) { \
- return NULL; \
- } \
- for (y = 0; y < imOut->ysize; y++) { \
- UINT8 *out = (UINT8 *)imOut->image[y]; \
- UINT8 *in1 = (UINT8 *)imIn1->image[y]; \
- UINT8 *in2 = (UINT8 *)imIn2->image[y]; \
- for (x = 0; x < imOut->linesize; x++) { \
- int temp = operation; \
- if (temp <= 0) { \
- out[x] = 0; \
- } else if (temp >= 255) { \
- out[x] = 255; \
- } else { \
- out[x] = temp; \
- } \
- } \
- } \
+#define CHOP(operation) \
+ int x, y; \
+ Imaging imOut; \
+ imOut = create(imIn1, imIn2, IMAGING_MODE_UNKNOWN); \
+ if (!imOut) { \
+ return NULL; \
+ } \
+ for (y = 0; y < imOut->ysize; y++) { \
+ UINT8 *out = (UINT8 *)imOut->image[y]; \
+ UINT8 *in1 = (UINT8 *)imIn1->image[y]; \
+ UINT8 *in2 = (UINT8 *)imIn2->image[y]; \
+ for (x = 0; x < imOut->linesize; x++) { \
+ int temp = operation; \
+ if (temp <= 0) { \
+ out[x] = 0; \
+ } else if (temp >= 255) { \
+ out[x] = 255; \
+ } else { \
+ out[x] = temp; \
+ } \
+ } \
+ } \
return imOut;
#define CHOP2(operation, mode) \
@@ -60,11 +60,12 @@
return imOut;
static Imaging
-create(Imaging im1, Imaging im2, char *mode) {
+create(Imaging im1, Imaging im2, const ModeID mode) {
int xsize, ysize;
if (!im1 || !im2 || im1->type != IMAGING_TYPE_UINT8 ||
- (mode != NULL && (strcmp(im1->mode, "1") || strcmp(im2->mode, "1")))) {
+ (mode != IMAGING_MODE_UNKNOWN &&
+ (im1->mode != IMAGING_MODE_1 || im2->mode != IMAGING_MODE_1))) {
return (Imaging)ImagingError_ModeError();
}
if (im1->type != im2->type || im1->bands != im2->bands) {
@@ -114,27 +115,27 @@ ImagingChopSubtract(Imaging imIn1, Imaging imIn2, float scale, int offset) {
Imaging
ImagingChopAnd(Imaging imIn1, Imaging imIn2) {
- CHOP2((in1[x] && in2[x]) ? 255 : 0, "1");
+ CHOP2((in1[x] && in2[x]) ? 255 : 0, IMAGING_MODE_1);
}
Imaging
ImagingChopOr(Imaging imIn1, Imaging imIn2) {
- CHOP2((in1[x] || in2[x]) ? 255 : 0, "1");
+ CHOP2((in1[x] || in2[x]) ? 255 : 0, IMAGING_MODE_1);
}
Imaging
ImagingChopXor(Imaging imIn1, Imaging imIn2) {
- CHOP2(((in1[x] != 0) ^ (in2[x] != 0)) ? 255 : 0, "1");
+ CHOP2(((in1[x] != 0) ^ (in2[x] != 0)) ? 255 : 0, IMAGING_MODE_1);
}
Imaging
ImagingChopAddModulo(Imaging imIn1, Imaging imIn2) {
- CHOP2(in1[x] + in2[x], NULL);
+ CHOP2(in1[x] + in2[x], IMAGING_MODE_UNKNOWN);
}
Imaging
ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2) {
- CHOP2(in1[x] - in2[x], NULL);
+ CHOP2(in1[x] - in2[x], IMAGING_MODE_UNKNOWN);
}
Imaging
@@ -142,7 +143,7 @@ ImagingChopSoftLight(Imaging imIn1, Imaging imIn2) {
CHOP2(
(((255 - in1[x]) * (in1[x] * in2[x])) / 65536) +
(in1[x] * (255 - ((255 - in1[x]) * (255 - in2[x]) / 255))) / 255,
- NULL
+ IMAGING_MODE_UNKNOWN
);
}
@@ -151,7 +152,7 @@ ImagingChopHardLight(Imaging imIn1, Imaging imIn2) {
CHOP2(
(in2[x] < 128) ? ((in1[x] * in2[x]) / 127)
: 255 - (((255 - in2[x]) * (255 - in1[x])) / 127),
- NULL
+ IMAGING_MODE_UNKNOWN
);
}
@@ -160,6 +161,6 @@ ImagingOverlay(Imaging imIn1, Imaging imIn2) {
CHOP2(
(in1[x] < 128) ? ((in1[x] * in2[x]) / 127)
: 255 - (((255 - in1[x]) * (255 - in2[x])) / 127),
- NULL
+ IMAGING_MODE_UNKNOWN
);
}
diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c
index c8f234261..330e5325c 100644
--- a/src/libImaging/Convert.c
+++ b/src/libImaging/Convert.c
@@ -277,38 +277,6 @@ rgb2f(UINT8 *out_, const UINT8 *in, int xsize) {
}
}
-static void
-rgb2bgr15(UINT8 *out_, const UINT8 *in, int xsize) {
- int x;
- for (x = 0; x < xsize; x++, in += 4, out_ += 2) {
- UINT16 v = ((((UINT16)in[0]) << 7) & 0x7c00) +
- ((((UINT16)in[1]) << 2) & 0x03e0) +
- ((((UINT16)in[2]) >> 3) & 0x001f);
- memcpy(out_, &v, sizeof(v));
- }
-}
-
-static void
-rgb2bgr16(UINT8 *out_, const UINT8 *in, int xsize) {
- int x;
- for (x = 0; x < xsize; x++, in += 4, out_ += 2) {
- UINT16 v = ((((UINT16)in[0]) << 8) & 0xf800) +
- ((((UINT16)in[1]) << 3) & 0x07e0) +
- ((((UINT16)in[2]) >> 3) & 0x001f);
- memcpy(out_, &v, sizeof(v));
- }
-}
-
-static void
-rgb2bgr24(UINT8 *out, const UINT8 *in, int xsize) {
- int x;
- for (x = 0; x < xsize; x++, in += 4) {
- *out++ = in[2];
- *out++ = in[1];
- *out++ = in[0];
- }
-}
-
static void
rgb2hsv_row(UINT8 *out, const UINT8 *in) { // following colorsys.py
float h, s, rc, gc, bc, cr;
@@ -909,150 +877,12 @@ I16_RGB(UINT8 *out, const UINT8 *in, int xsize) {
}
}
-static struct {
- const char *from;
- const char *to;
- ImagingShuffler convert;
-} converters[] = {
-
- {"1", "L", bit2l},
- {"1", "I", bit2i},
- {"1", "F", bit2f},
- {"1", "RGB", bit2rgb},
- {"1", "RGBA", bit2rgb},
- {"1", "RGBX", bit2rgb},
- {"1", "CMYK", bit2cmyk},
- {"1", "YCbCr", bit2ycbcr},
- {"1", "HSV", bit2hsv},
-
- {"L", "1", l2bit},
- {"L", "LA", l2la},
- {"L", "I", l2i},
- {"L", "F", l2f},
- {"L", "RGB", l2rgb},
- {"L", "RGBA", l2rgb},
- {"L", "RGBX", l2rgb},
- {"L", "CMYK", l2cmyk},
- {"L", "YCbCr", l2ycbcr},
- {"L", "HSV", l2hsv},
-
- {"LA", "L", la2l},
- {"LA", "La", lA2la},
- {"LA", "RGB", la2rgb},
- {"LA", "RGBA", la2rgb},
- {"LA", "RGBX", la2rgb},
- {"LA", "CMYK", la2cmyk},
- {"LA", "YCbCr", la2ycbcr},
- {"LA", "HSV", la2hsv},
-
- {"La", "LA", la2lA},
-
- {"I", "L", i2l},
- {"I", "F", i2f},
- {"I", "RGB", i2rgb},
- {"I", "RGBA", i2rgb},
- {"I", "RGBX", i2rgb},
- {"I", "HSV", i2hsv},
-
- {"F", "L", f2l},
- {"F", "I", f2i},
-
- {"RGB", "1", rgb2bit},
- {"RGB", "L", rgb2l},
- {"RGB", "LA", rgb2la},
- {"RGB", "La", rgb2la},
- {"RGB", "I", rgb2i},
- {"RGB", "I;16", rgb2i16l},
- {"RGB", "I;16L", rgb2i16l},
- {"RGB", "I;16B", rgb2i16b},
-#ifdef WORDS_BIGENDIAN
- {"RGB", "I;16N", rgb2i16b},
-#else
- {"RGB", "I;16N", rgb2i16l},
-#endif
- {"RGB", "F", rgb2f},
- {"RGB", "BGR;15", rgb2bgr15},
- {"RGB", "BGR;16", rgb2bgr16},
- {"RGB", "BGR;24", rgb2bgr24},
- {"RGB", "RGBA", rgb2rgba},
- {"RGB", "RGBa", rgb2rgba},
- {"RGB", "RGBX", rgb2rgba},
- {"RGB", "CMYK", rgb2cmyk},
- {"RGB", "YCbCr", ImagingConvertRGB2YCbCr},
- {"RGB", "HSV", rgb2hsv},
-
- {"RGBA", "1", rgb2bit},
- {"RGBA", "L", rgb2l},
- {"RGBA", "LA", rgba2la},
- {"RGBA", "I", rgb2i},
- {"RGBA", "F", rgb2f},
- {"RGBA", "RGB", rgba2rgb},
- {"RGBA", "RGBa", rgbA2rgba},
- {"RGBA", "RGBX", rgb2rgba},
- {"RGBA", "CMYK", rgb2cmyk},
- {"RGBA", "YCbCr", ImagingConvertRGB2YCbCr},
- {"RGBA", "HSV", rgb2hsv},
-
- {"RGBa", "RGBA", rgba2rgbA},
- {"RGBa", "RGB", rgba2rgb_},
-
- {"RGBX", "1", rgb2bit},
- {"RGBX", "L", rgb2l},
- {"RGBX", "LA", rgb2la},
- {"RGBX", "I", rgb2i},
- {"RGBX", "F", rgb2f},
- {"RGBX", "RGB", rgba2rgb},
- {"RGBX", "CMYK", rgb2cmyk},
- {"RGBX", "YCbCr", ImagingConvertRGB2YCbCr},
- {"RGBX", "HSV", rgb2hsv},
-
- {"CMYK", "RGB", cmyk2rgb},
- {"CMYK", "RGBA", cmyk2rgb},
- {"CMYK", "RGBX", cmyk2rgb},
- {"CMYK", "HSV", cmyk2hsv},
-
- {"YCbCr", "L", ycbcr2l},
- {"YCbCr", "LA", ycbcr2la},
- {"YCbCr", "RGB", ImagingConvertYCbCr2RGB},
-
- {"HSV", "RGB", hsv2rgb},
-
- {"I", "I;16", I_I16L},
- {"I;16", "I", I16L_I},
- {"I;16", "RGB", I16_RGB},
- {"L", "I;16", L_I16L},
- {"I;16", "L", I16L_L},
-
- {"I", "I;16L", I_I16L},
- {"I;16L", "I", I16L_I},
- {"I", "I;16B", I_I16B},
- {"I;16B", "I", I16B_I},
-
- {"L", "I;16L", L_I16L},
- {"I;16L", "L", I16L_L},
- {"L", "I;16B", L_I16B},
- {"I;16B", "L", I16B_L},
-#ifdef WORDS_BIGENDIAN
- {"L", "I;16N", L_I16B},
- {"I;16N", "L", I16B_L},
-#else
- {"L", "I;16N", L_I16L},
- {"I;16N", "L", I16L_L},
-#endif
-
- {"I;16", "F", I16L_F},
- {"I;16L", "F", I16L_F},
- {"I;16B", "F", I16B_F},
-
- {NULL}
-};
-
-/* FIXME: translate indexed versions to pointer versions below this line */
-
/* ------------------- */
/* Palette conversions */
/* ------------------- */
+/* FIXME: translate indexed versions to pointer versions below this line */
+
static void
p2bit(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
@@ -1100,13 +930,13 @@ pa2p(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
static void
p2pa(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
- int rgb = strcmp(palette->mode, "RGB");
+ const int rgb = palette->mode == IMAGING_MODE_RGB;
for (x = 0; x < xsize; x++, in++) {
const UINT8 *rgba = &palette->palette[in[0] * 4];
*out++ = in[0];
*out++ = in[0];
*out++ = in[0];
- *out++ = rgb == 0 ? 255 : rgba[3];
+ *out++ = rgb ? 255 : rgba[3];
}
}
@@ -1260,7 +1090,7 @@ pa2ycbcr(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
}
static Imaging
-frompalette(Imaging imOut, Imaging imIn, const char *mode) {
+frompalette(Imaging imOut, Imaging imIn, const ModeID mode) {
ImagingSectionCookie cookie;
int alpha;
int y;
@@ -1272,31 +1102,31 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) {
return (Imaging)ImagingError_ValueError("no palette");
}
- alpha = !strcmp(imIn->mode, "PA");
+ alpha = imIn->mode == IMAGING_MODE_PA;
- if (strcmp(mode, "1") == 0) {
+ if (mode == IMAGING_MODE_1) {
convert = alpha ? pa2bit : p2bit;
- } else if (strcmp(mode, "L") == 0) {
+ } else if (mode == IMAGING_MODE_L) {
convert = alpha ? pa2l : p2l;
- } else if (strcmp(mode, "LA") == 0) {
+ } else if (mode == IMAGING_MODE_LA) {
convert = alpha ? pa2la : p2la;
- } else if (strcmp(mode, "P") == 0) {
+ } else if (mode == IMAGING_MODE_P) {
convert = pa2p;
- } else if (strcmp(mode, "PA") == 0) {
+ } else if (mode == IMAGING_MODE_PA) {
convert = p2pa;
- } else if (strcmp(mode, "I") == 0) {
+ } else if (mode == IMAGING_MODE_I) {
convert = alpha ? pa2i : p2i;
- } else if (strcmp(mode, "F") == 0) {
+ } else if (mode == IMAGING_MODE_F) {
convert = alpha ? pa2f : p2f;
- } else if (strcmp(mode, "RGB") == 0) {
+ } else if (mode == IMAGING_MODE_RGB) {
convert = alpha ? pa2rgb : p2rgb;
- } else if (strcmp(mode, "RGBA") == 0 || strcmp(mode, "RGBX") == 0) {
+ } else if (mode == IMAGING_MODE_RGBA || mode == IMAGING_MODE_RGBX) {
convert = alpha ? pa2rgba : p2rgba;
- } else if (strcmp(mode, "CMYK") == 0) {
+ } else if (mode == IMAGING_MODE_CMYK) {
convert = alpha ? pa2cmyk : p2cmyk;
- } else if (strcmp(mode, "YCbCr") == 0) {
+ } else if (mode == IMAGING_MODE_YCbCr) {
convert = alpha ? pa2ycbcr : p2ycbcr;
- } else if (strcmp(mode, "HSV") == 0) {
+ } else if (mode == IMAGING_MODE_HSV) {
convert = alpha ? pa2hsv : p2hsv;
} else {
return (Imaging)ImagingError_ValueError("conversion not supported");
@@ -1306,7 +1136,7 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) {
if (!imOut) {
return NULL;
}
- if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0) {
+ if (mode == IMAGING_MODE_P || mode == IMAGING_MODE_PA) {
ImagingPaletteDelete(imOut->palette);
imOut->palette = ImagingPaletteDuplicate(imIn->palette);
}
@@ -1330,24 +1160,26 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) {
#endif
static Imaging
topalette(
- Imaging imOut, Imaging imIn, const char *mode, ImagingPalette inpalette, int dither
+ Imaging imOut, Imaging imIn, const ModeID mode, ImagingPalette inpalette, int dither
) {
ImagingSectionCookie cookie;
int alpha;
int x, y;
ImagingPalette palette = inpalette;
- /* Map L or RGB/RGBX/RGBA to palette image */
- if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0) {
+ /* Map L or RGB/RGBX/RGBA/RGBa to palette image */
+ if (imIn->mode != IMAGING_MODE_L && imIn->mode != IMAGING_MODE_RGB &&
+ imIn->mode != IMAGING_MODE_RGBX && imIn->mode != IMAGING_MODE_RGBA &&
+ imIn->mode != IMAGING_MODE_RGBa) {
return (Imaging)ImagingError_ValueError("conversion not supported");
}
- alpha = !strcmp(mode, "PA");
+ alpha = mode == IMAGING_MODE_PA;
if (palette == NULL) {
/* FIXME: make user configurable */
if (imIn->bands == 1) {
- palette = ImagingPaletteNew("RGB");
+ palette = ImagingPaletteNew(IMAGING_MODE_RGB);
palette->size = 256;
int i;
@@ -1534,11 +1366,11 @@ tobilevel(Imaging imOut, Imaging imIn) {
int *errors;
/* Map L or RGB to dithered 1 image */
- if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0) {
+ if (imIn->mode != IMAGING_MODE_L && imIn->mode != IMAGING_MODE_RGB) {
return (Imaging)ImagingError_ValueError("conversion not supported");
}
- imOut = ImagingNew2Dirty("1", imOut, imIn);
+ imOut = ImagingNew2Dirty(IMAGING_MODE_1, imOut, imIn);
if (!imOut) {
return NULL;
}
@@ -1620,19 +1452,152 @@ tobilevel(Imaging imOut, Imaging imIn) {
#pragma optimize("", on)
#endif
+/* ------------------- */
+/* Conversion handlers */
+/* ------------------- */
+
+static struct {
+ const ModeID from;
+ const ModeID to;
+ ImagingShuffler convert;
+} converters[] = {
+ {IMAGING_MODE_1, IMAGING_MODE_L, bit2l},
+ {IMAGING_MODE_1, IMAGING_MODE_I, bit2i},
+ {IMAGING_MODE_1, IMAGING_MODE_F, bit2f},
+ {IMAGING_MODE_1, IMAGING_MODE_RGB, bit2rgb},
+ {IMAGING_MODE_1, IMAGING_MODE_RGBA, bit2rgb},
+ {IMAGING_MODE_1, IMAGING_MODE_RGBX, bit2rgb},
+ {IMAGING_MODE_1, IMAGING_MODE_CMYK, bit2cmyk},
+ {IMAGING_MODE_1, IMAGING_MODE_YCbCr, bit2ycbcr},
+ {IMAGING_MODE_1, IMAGING_MODE_HSV, bit2hsv},
+
+ {IMAGING_MODE_L, IMAGING_MODE_1, l2bit},
+ {IMAGING_MODE_L, IMAGING_MODE_LA, l2la},
+ {IMAGING_MODE_L, IMAGING_MODE_I, l2i},
+ {IMAGING_MODE_L, IMAGING_MODE_F, l2f},
+ {IMAGING_MODE_L, IMAGING_MODE_RGB, l2rgb},
+ {IMAGING_MODE_L, IMAGING_MODE_RGBA, l2rgb},
+ {IMAGING_MODE_L, IMAGING_MODE_RGBX, l2rgb},
+ {IMAGING_MODE_L, IMAGING_MODE_CMYK, l2cmyk},
+ {IMAGING_MODE_L, IMAGING_MODE_YCbCr, l2ycbcr},
+ {IMAGING_MODE_L, IMAGING_MODE_HSV, l2hsv},
+
+ {IMAGING_MODE_LA, IMAGING_MODE_L, la2l},
+ {IMAGING_MODE_LA, IMAGING_MODE_La, lA2la},
+ {IMAGING_MODE_LA, IMAGING_MODE_RGB, la2rgb},
+ {IMAGING_MODE_LA, IMAGING_MODE_RGBA, la2rgb},
+ {IMAGING_MODE_LA, IMAGING_MODE_RGBX, la2rgb},
+ {IMAGING_MODE_LA, IMAGING_MODE_CMYK, la2cmyk},
+ {IMAGING_MODE_LA, IMAGING_MODE_YCbCr, la2ycbcr},
+ {IMAGING_MODE_LA, IMAGING_MODE_HSV, la2hsv},
+
+ {IMAGING_MODE_La, IMAGING_MODE_LA, la2lA},
+
+ {IMAGING_MODE_I, IMAGING_MODE_L, i2l},
+ {IMAGING_MODE_I, IMAGING_MODE_F, i2f},
+ {IMAGING_MODE_I, IMAGING_MODE_RGB, i2rgb},
+ {IMAGING_MODE_I, IMAGING_MODE_RGBA, i2rgb},
+ {IMAGING_MODE_I, IMAGING_MODE_RGBX, i2rgb},
+ {IMAGING_MODE_I, IMAGING_MODE_HSV, i2hsv},
+
+ {IMAGING_MODE_F, IMAGING_MODE_L, f2l},
+ {IMAGING_MODE_F, IMAGING_MODE_I, f2i},
+
+ {IMAGING_MODE_RGB, IMAGING_MODE_1, rgb2bit},
+ {IMAGING_MODE_RGB, IMAGING_MODE_L, rgb2l},
+ {IMAGING_MODE_RGB, IMAGING_MODE_LA, rgb2la},
+ {IMAGING_MODE_RGB, IMAGING_MODE_La, rgb2la},
+ {IMAGING_MODE_RGB, IMAGING_MODE_I, rgb2i},
+ {IMAGING_MODE_RGB, IMAGING_MODE_I_16, rgb2i16l},
+ {IMAGING_MODE_RGB, IMAGING_MODE_I_16L, rgb2i16l},
+ {IMAGING_MODE_RGB, IMAGING_MODE_I_16B, rgb2i16b},
+#ifdef WORDS_BIGENDIAN
+ {IMAGING_MODE_RGB, IMAGING_MODE_I_16N, rgb2i16b},
+#else
+ {IMAGING_MODE_RGB, IMAGING_MODE_I_16N, rgb2i16l},
+#endif
+ {IMAGING_MODE_RGB, IMAGING_MODE_F, rgb2f},
+ {IMAGING_MODE_RGB, IMAGING_MODE_RGBA, rgb2rgba},
+ {IMAGING_MODE_RGB, IMAGING_MODE_RGBa, rgb2rgba},
+ {IMAGING_MODE_RGB, IMAGING_MODE_RGBX, rgb2rgba},
+ {IMAGING_MODE_RGB, IMAGING_MODE_CMYK, rgb2cmyk},
+ {IMAGING_MODE_RGB, IMAGING_MODE_YCbCr, ImagingConvertRGB2YCbCr},
+ {IMAGING_MODE_RGB, IMAGING_MODE_HSV, rgb2hsv},
+
+ {IMAGING_MODE_RGBA, IMAGING_MODE_1, rgb2bit},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_L, rgb2l},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_LA, rgba2la},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_I, rgb2i},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_F, rgb2f},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_RGB, rgba2rgb},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_RGBa, rgbA2rgba},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_RGBX, rgb2rgba},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_CMYK, rgb2cmyk},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_YCbCr, ImagingConvertRGB2YCbCr},
+ {IMAGING_MODE_RGBA, IMAGING_MODE_HSV, rgb2hsv},
+
+ {IMAGING_MODE_RGBa, IMAGING_MODE_RGBA, rgba2rgbA},
+ {IMAGING_MODE_RGBa, IMAGING_MODE_RGB, rgba2rgb_},
+
+ {IMAGING_MODE_RGBX, IMAGING_MODE_1, rgb2bit},
+ {IMAGING_MODE_RGBX, IMAGING_MODE_L, rgb2l},
+ {IMAGING_MODE_RGBX, IMAGING_MODE_LA, rgb2la},
+ {IMAGING_MODE_RGBX, IMAGING_MODE_I, rgb2i},
+ {IMAGING_MODE_RGBX, IMAGING_MODE_F, rgb2f},
+ {IMAGING_MODE_RGBX, IMAGING_MODE_RGB, rgba2rgb},
+ {IMAGING_MODE_RGBX, IMAGING_MODE_CMYK, rgb2cmyk},
+ {IMAGING_MODE_RGBX, IMAGING_MODE_YCbCr, ImagingConvertRGB2YCbCr},
+ {IMAGING_MODE_RGBX, IMAGING_MODE_HSV, rgb2hsv},
+
+ {IMAGING_MODE_CMYK, IMAGING_MODE_RGB, cmyk2rgb},
+ {IMAGING_MODE_CMYK, IMAGING_MODE_RGBA, cmyk2rgb},
+ {IMAGING_MODE_CMYK, IMAGING_MODE_RGBX, cmyk2rgb},
+ {IMAGING_MODE_CMYK, IMAGING_MODE_HSV, cmyk2hsv},
+
+ {IMAGING_MODE_YCbCr, IMAGING_MODE_L, ycbcr2l},
+ {IMAGING_MODE_YCbCr, IMAGING_MODE_LA, ycbcr2la},
+ {IMAGING_MODE_YCbCr, IMAGING_MODE_RGB, ImagingConvertYCbCr2RGB},
+
+ {IMAGING_MODE_HSV, IMAGING_MODE_RGB, hsv2rgb},
+
+ {IMAGING_MODE_I, IMAGING_MODE_I_16, I_I16L},
+ {IMAGING_MODE_I_16, IMAGING_MODE_I, I16L_I},
+ {IMAGING_MODE_I_16, IMAGING_MODE_RGB, I16_RGB},
+ {IMAGING_MODE_L, IMAGING_MODE_I_16, L_I16L},
+ {IMAGING_MODE_I_16, IMAGING_MODE_L, I16L_L},
+
+ {IMAGING_MODE_I, IMAGING_MODE_I_16L, I_I16L},
+ {IMAGING_MODE_I_16L, IMAGING_MODE_I, I16L_I},
+ {IMAGING_MODE_I, IMAGING_MODE_I_16B, I_I16B},
+ {IMAGING_MODE_I_16B, IMAGING_MODE_I, I16B_I},
+
+ {IMAGING_MODE_L, IMAGING_MODE_I_16L, L_I16L},
+ {IMAGING_MODE_I_16L, IMAGING_MODE_L, I16L_L},
+ {IMAGING_MODE_L, IMAGING_MODE_I_16B, L_I16B},
+ {IMAGING_MODE_I_16B, IMAGING_MODE_L, I16B_L},
+#ifdef WORDS_BIGENDIAN
+ {IMAGING_MODE_L, IMAGING_MODE_I_16N, L_I16B},
+ {IMAGING_MODE_I_16N, IMAGING_MODE_L, I16B_L},
+#else
+ {IMAGING_MODE_L, IMAGING_MODE_I_16N, L_I16L},
+ {IMAGING_MODE_I_16N, IMAGING_MODE_L, I16L_L},
+#endif
+
+ {IMAGING_MODE_I_16, IMAGING_MODE_F, I16L_F},
+ {IMAGING_MODE_I_16L, IMAGING_MODE_F, I16L_F},
+ {IMAGING_MODE_I_16B, IMAGING_MODE_F, I16B_F}
+};
+
static Imaging
-convert(
- Imaging imOut, Imaging imIn, const char *mode, ImagingPalette palette, int dither
-) {
+convert(Imaging imOut, Imaging imIn, ModeID mode, ImagingPalette palette, int dither) {
ImagingSectionCookie cookie;
ImagingShuffler convert;
- int y;
if (!imIn) {
return (Imaging)ImagingError_ModeError();
}
- if (!mode) {
+ if (mode == IMAGING_MODE_UNKNOWN) {
/* Map palette image to full depth */
if (!imIn->palette) {
return (Imaging)ImagingError_ModeError();
@@ -1640,33 +1605,31 @@ convert(
mode = imIn->palette->mode;
} else {
/* Same mode? */
- if (!strcmp(imIn->mode, mode)) {
+ if (imIn->mode == mode) {
return ImagingCopy2(imOut, imIn);
}
}
/* test for special conversions */
- if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0) {
+ if (imIn->mode == IMAGING_MODE_P || imIn->mode == IMAGING_MODE_PA) {
return frompalette(imOut, imIn, mode);
}
- if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0) {
+ if (mode == IMAGING_MODE_P || mode == IMAGING_MODE_PA) {
return topalette(imOut, imIn, mode, palette, dither);
}
- if (dither && strcmp(mode, "1") == 0) {
+ if (dither && mode == IMAGING_MODE_1) {
return tobilevel(imOut, imIn);
}
/* standard conversion machinery */
convert = NULL;
-
- for (y = 0; converters[y].from; y++) {
- if (!strcmp(imIn->mode, converters[y].from) &&
- !strcmp(mode, converters[y].to)) {
- convert = converters[y].convert;
+ for (size_t i = 0; i < sizeof(converters) / sizeof(*converters); i++) {
+ if (imIn->mode == converters[i].from && mode == converters[i].to) {
+ convert = converters[i].convert;
break;
}
}
@@ -1677,7 +1640,11 @@ convert(
#else
static char buf[100];
snprintf(
- buf, 100, "conversion from %.10s to %.10s not supported", imIn->mode, mode
+ buf,
+ 100,
+ "conversion from %.10s to %.10s not supported",
+ getModeData(imIn->mode)->name,
+ getModeData(mode)->name
);
return (Imaging)ImagingError_ValueError(buf);
#endif
@@ -1689,7 +1656,7 @@ convert(
}
ImagingSectionEnter(&cookie);
- for (y = 0; y < imIn->ysize; y++) {
+ for (int y = 0; y < imIn->ysize; y++) {
(*convert)((UINT8 *)imOut->image[y], (UINT8 *)imIn->image[y], imIn->xsize);
}
ImagingSectionLeave(&cookie);
@@ -1698,7 +1665,7 @@ convert(
}
Imaging
-ImagingConvert(Imaging imIn, const char *mode, ImagingPalette palette, int dither) {
+ImagingConvert(Imaging imIn, const ModeID mode, ImagingPalette palette, int dither) {
return convert(NULL, imIn, mode, palette, dither);
}
@@ -1708,7 +1675,7 @@ ImagingConvert2(Imaging imOut, Imaging imIn) {
}
Imaging
-ImagingConvertTransparent(Imaging imIn, const char *mode, int r, int g, int b) {
+ImagingConvertTransparent(Imaging imIn, const ModeID mode, int r, int g, int b) {
ImagingSectionCookie cookie;
ImagingShuffler convert;
Imaging imOut = NULL;
@@ -1722,27 +1689,27 @@ ImagingConvertTransparent(Imaging imIn, const char *mode, int r, int g, int b) {
return (Imaging)ImagingError_ModeError();
}
- if (strcmp(imIn->mode, "RGB") == 0 &&
- (strcmp(mode, "RGBA") == 0 || strcmp(mode, "RGBa") == 0)) {
+ if (imIn->mode == IMAGING_MODE_RGB &&
+ (mode == IMAGING_MODE_RGBA || mode == IMAGING_MODE_RGBa)) {
convert = rgb2rgba;
- if (strcmp(mode, "RGBa") == 0) {
+ if (mode == IMAGING_MODE_RGBa) {
premultiplied = 1;
}
- } else if (strcmp(imIn->mode, "RGB") == 0 &&
- (strcmp(mode, "LA") == 0 || strcmp(mode, "La") == 0)) {
+ } else if (imIn->mode == IMAGING_MODE_RGB &&
+ (mode == IMAGING_MODE_LA || mode == IMAGING_MODE_La)) {
convert = rgb2la;
source_transparency = 1;
- if (strcmp(mode, "La") == 0) {
+ if (mode == IMAGING_MODE_La) {
premultiplied = 1;
}
- } else if ((strcmp(imIn->mode, "1") == 0 || strcmp(imIn->mode, "I") == 0 ||
- strcmp(imIn->mode, "I;16") == 0 || strcmp(imIn->mode, "L") == 0) &&
- (strcmp(mode, "RGBA") == 0 || strcmp(mode, "LA") == 0)) {
- if (strcmp(imIn->mode, "1") == 0) {
+ } else if ((imIn->mode == IMAGING_MODE_1 || imIn->mode == IMAGING_MODE_I ||
+ imIn->mode == IMAGING_MODE_I_16 || imIn->mode == IMAGING_MODE_L) &&
+ (mode == IMAGING_MODE_RGBA || mode == IMAGING_MODE_LA)) {
+ if (imIn->mode == IMAGING_MODE_1) {
convert = bit2rgb;
- } else if (strcmp(imIn->mode, "I") == 0) {
+ } else if (imIn->mode == IMAGING_MODE_I) {
convert = i2rgb;
- } else if (strcmp(imIn->mode, "I;16") == 0) {
+ } else if (imIn->mode == IMAGING_MODE_I_16) {
convert = I16_RGB;
} else {
convert = l2rgb;
@@ -1754,8 +1721,8 @@ ImagingConvertTransparent(Imaging imIn, const char *mode, int r, int g, int b) {
buf,
100,
"conversion from %.10s to %.10s not supported in convert_transparent",
- imIn->mode,
- mode
+ getModeData(imIn->mode)->name,
+ getModeData(mode)->name
);
return (Imaging)ImagingError_ValueError(buf);
}
@@ -1778,15 +1745,15 @@ ImagingConvertTransparent(Imaging imIn, const char *mode, int r, int g, int b) {
}
Imaging
-ImagingConvertInPlace(Imaging imIn, const char *mode) {
+ImagingConvertInPlace(Imaging imIn, const ModeID mode) {
ImagingSectionCookie cookie;
ImagingShuffler convert;
int y;
/* limited support for inplace conversion */
- if (strcmp(imIn->mode, "L") == 0 && strcmp(mode, "1") == 0) {
+ if (imIn->mode == IMAGING_MODE_L && mode == IMAGING_MODE_1) {
convert = l2bit;
- } else if (strcmp(imIn->mode, "1") == 0 && strcmp(mode, "L") == 0) {
+ } else if (imIn->mode == IMAGING_MODE_1 && mode == IMAGING_MODE_L) {
convert = bit2l;
} else {
return ImagingError_ModeError();
diff --git a/src/libImaging/Dib.c b/src/libImaging/Dib.c
index c69e9e552..2afe71d4a 100644
--- a/src/libImaging/Dib.c
+++ b/src/libImaging/Dib.c
@@ -25,20 +25,17 @@
#include "ImDib.h"
-char *
+ModeID
ImagingGetModeDIB(int size_out[2]) {
/* Get device characteristics */
- HDC dc;
- char *mode;
+ const HDC dc = CreateCompatibleDC(NULL);
- dc = CreateCompatibleDC(NULL);
-
- mode = "P";
+ ModeID mode = IMAGING_MODE_P;
if (!(GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE)) {
- mode = "RGB";
+ mode = IMAGING_MODE_RGB;
if (GetDeviceCaps(dc, BITSPIXEL) == 1) {
- mode = "1";
+ mode = IMAGING_MODE_1;
}
}
@@ -53,7 +50,7 @@ ImagingGetModeDIB(int size_out[2]) {
}
ImagingDIB
-ImagingNewDIB(const char *mode, int xsize, int ysize) {
+ImagingNewDIB(const ModeID mode, int xsize, int ysize) {
/* Create a Windows bitmap */
ImagingDIB dib;
@@ -61,10 +58,12 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) {
int i;
/* Check mode */
- if (strcmp(mode, "1") != 0 && strcmp(mode, "L") != 0 && strcmp(mode, "RGB") != 0) {
+ if (mode != IMAGING_MODE_1 && mode != IMAGING_MODE_L && mode != IMAGING_MODE_RGB) {
return (ImagingDIB)ImagingError_ModeError();
}
+ const int pixelsize = mode == IMAGING_MODE_RGB ? 3 : 1;
+
/* Create DIB context and info header */
/* malloc check ok, small constant allocation */
dib = (ImagingDIB)malloc(sizeof(*dib));
@@ -83,7 +82,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) {
dib->info->bmiHeader.biWidth = xsize;
dib->info->bmiHeader.biHeight = ysize;
dib->info->bmiHeader.biPlanes = 1;
- dib->info->bmiHeader.biBitCount = strlen(mode) * 8;
+ dib->info->bmiHeader.biBitCount = pixelsize * 8;
dib->info->bmiHeader.biCompression = BI_RGB;
/* Create DIB */
@@ -103,12 +102,12 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) {
return (ImagingDIB)ImagingError_MemoryError();
}
- strcpy(dib->mode, mode);
+ dib->mode = mode;
dib->xsize = xsize;
dib->ysize = ysize;
- dib->pixelsize = strlen(mode);
- dib->linesize = (xsize * dib->pixelsize + 3) & -4;
+ dib->pixelsize = pixelsize;
+ dib->linesize = (xsize * pixelsize + 3) & -4;
if (dib->pixelsize == 1) {
dib->pack = dib->unpack = (ImagingShuffler)memcpy;
@@ -132,7 +131,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) {
}
/* Create an associated palette (for 8-bit displays only) */
- if (strcmp(ImagingGetModeDIB(NULL), "P") == 0) {
+ if (ImagingGetModeDIB(NULL) == IMAGING_MODE_P) {
char palbuf[sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)];
LPLOGPALETTE pal = (LPLOGPALETTE)palbuf;
int i, r, g, b;
@@ -142,7 +141,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) {
pal->palNumEntries = 256;
GetSystemPaletteEntries(dib->dc, 0, 256, pal->palPalEntry);
- if (strcmp(mode, "L") == 0) {
+ if (mode == IMAGING_MODE_L) {
/* Grayscale DIB. Fill all 236 slots with a grayscale ramp
* (this is usually overkill on Windows since VGA only offers
* 6 bits grayscale resolution). Ignore the slots already
@@ -156,8 +155,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) {
}
dib->palette = CreatePalette(pal);
-
- } else if (strcmp(mode, "RGB") == 0) {
+ } else if (mode == IMAGING_MODE_RGB) {
#ifdef CUBE216
/* Colour DIB. Create a 6x6x6 colour cube (216 entries) and
diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c
index ea6f8805e..d28980432 100644
--- a/src/libImaging/Draw.c
+++ b/src/libImaging/Draw.c
@@ -63,12 +63,12 @@ typedef struct {
} Edge;
/* Type used in "polygon*" functions */
-typedef void (*hline_handler)(Imaging, int, int, int, int);
+typedef void (*hline_handler)(Imaging, int, int, int, int, Imaging);
static inline void
point8(Imaging im, int x, int y, int ink) {
if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {
- if (strncmp(im->mode, "I;16", 4) == 0) {
+ if (isModeI16(im->mode)) {
#ifdef WORDS_BIGENDIAN
im->image8[y][x * 2] = (UINT8)(ink >> 8);
im->image8[y][x * 2 + 1] = (UINT8)ink;
@@ -103,9 +103,7 @@ point32rgba(Imaging im, int x, int y, int ink) {
}
static inline void
-hline8(Imaging im, int x0, int y0, int x1, int ink) {
- int pixelwidth;
-
+hline8(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
if (y0 >= 0 && y0 < im->ysize) {
if (x0 < 0) {
x0 = 0;
@@ -118,16 +116,41 @@ hline8(Imaging im, int x0, int y0, int x1, int ink) {
x1 = im->xsize - 1;
}
if (x0 <= x1) {
- pixelwidth = strncmp(im->mode, "I;16", 4) == 0 ? 2 : 1;
- memset(
- im->image8[y0] + x0 * pixelwidth, (UINT8)ink, (x1 - x0 + 1) * pixelwidth
- );
+ int bigendian = -1;
+ if (isModeI16(im->mode)) {
+ bigendian =
+ (
+#ifdef WORDS_BIGENDIAN
+ im->mode == IMAGING_MODE_I_16 || im->mode == IMAGING_MODE_I_16L
+#else
+ im->mode == IMAGING_MODE_I_16B
+#endif
+ )
+ ? 1
+ : 0;
+ }
+ if (mask == NULL && bigendian == -1) {
+ memset(im->image8[y0] + x0, (UINT8)ink, (x1 - x0 + 1));
+ } else {
+ UINT8 *p = im->image8[y0];
+ while (x0 <= x1) {
+ if (mask == NULL || mask->image8[y0][x0]) {
+ if (bigendian == -1) {
+ p[x0] = ink;
+ } else {
+ p[x0 * 2 + (bigendian ? 1 : 0)] = ink;
+ p[x0 * 2 + (bigendian ? 0 : 1)] = ink >> 8;
+ }
+ }
+ x0++;
+ }
+ }
}
}
}
static inline void
-hline32(Imaging im, int x0, int y0, int x1, int ink) {
+hline32(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
INT32 *p;
if (y0 >= 0 && y0 < im->ysize) {
@@ -143,13 +166,16 @@ hline32(Imaging im, int x0, int y0, int x1, int ink) {
}
p = im->image32[y0];
while (x0 <= x1) {
- p[x0++] = ink;
+ if (mask == NULL || mask->image8[y0][x0]) {
+ p[x0] = ink;
+ }
+ x0++;
}
}
}
static inline void
-hline32rgba(Imaging im, int x0, int y0, int x1, int ink) {
+hline32rgba(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
unsigned int tmp;
if (y0 >= 0 && y0 < im->ysize) {
@@ -167,9 +193,11 @@ hline32rgba(Imaging im, int x0, int y0, int x1, int ink) {
UINT8 *out = (UINT8 *)im->image[y0] + x0 * 4;
UINT8 *in = (UINT8 *)&ink;
while (x0 <= x1) {
- out[0] = BLEND(in[3], out[0], in[0], tmp);
- out[1] = BLEND(in[3], out[1], in[1], tmp);
- out[2] = BLEND(in[3], out[2], in[2], tmp);
+ if (mask == NULL || mask->image8[y0][x0]) {
+ out[0] = BLEND(in[3], out[0], in[0], tmp);
+ out[1] = BLEND(in[3], out[1], in[1], tmp);
+ out[2] = BLEND(in[3], out[2], in[2], tmp);
+ }
x0++;
out += 4;
}
@@ -407,7 +435,14 @@ x_cmp(const void *x0, const void *x1) {
static void
draw_horizontal_lines(
- Imaging im, int n, Edge *e, int ink, int *x_pos, int y, hline_handler hline
+ Imaging im,
+ int n,
+ Edge *e,
+ int ink,
+ int *x_pos,
+ int y,
+ hline_handler hline,
+ Imaging mask
) {
int i;
for (i = 0; i < n; i++) {
@@ -429,7 +464,7 @@ draw_horizontal_lines(
}
}
- (*hline)(im, xmin, e[i].ymin, xmax, ink);
+ (*hline)(im, xmin, e[i].ymin, xmax, ink, mask);
*x_pos = xmax + 1;
}
}
@@ -440,7 +475,7 @@ draw_horizontal_lines(
*/
static inline int
polygon_generic(
- Imaging im, int n, Edge *e, int ink, int eofill, hline_handler hline, int hasAlpha
+ Imaging im, int n, Edge *e, int ink, int eofill, hline_handler hline, Imaging mask
) {
Edge **edge_table;
float *xx;
@@ -461,6 +496,7 @@ polygon_generic(
return -1;
}
+ int hasAlpha = hline == hline32rgba;
for (i = 0; i < n; i++) {
if (ymin > e[i].ymin) {
ymin = e[i].ymin;
@@ -470,7 +506,7 @@ polygon_generic(
}
if (e[i].ymin == e[i].ymax) {
if (hasAlpha != 1) {
- (*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink);
+ (*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink, mask);
}
continue;
}
@@ -501,55 +537,49 @@ polygon_generic(
// Needed to draw consistent polygons
xx[j] = xx[j - 1];
j++;
- } else if (current->dx != 0 && j % 2 == 1 &&
- roundf(xx[j - 1]) == xx[j - 1]) {
+ } else if ((ymin == current->ymin || ymin == current->ymax) &&
+ current->dx != 0) {
// Connect discontiguous corners
for (k = 0; k < i; k++) {
Edge *other_edge = edge_table[k];
- if ((current->dx > 0 && other_edge->dx <= 0) ||
- (current->dx < 0 && other_edge->dx >= 0)) {
+ if ((ymin != other_edge->ymin && ymin != other_edge->ymax) ||
+ other_edge->dx == 0) {
continue;
}
// Check if the two edges join to make a corner
- if (xx[j - 1] ==
- (ymin - other_edge->y0) * other_edge->dx + other_edge->x0) {
+ if (roundf(xx[j - 1]) ==
+ roundf(
+ (ymin - other_edge->y0) * other_edge->dx +
+ other_edge->x0
+ )) {
// Determine points from the edges on the next row
// Or if this is the last row, check the previous row
- int offset = ymin == ymax ? -1 : 1;
+ int offset = ymin == current->ymax ? -1 : 1;
adjacent_line_x =
(ymin + offset - current->y0) * current->dx +
current->x0;
- adjacent_line_x_other_edge =
- (ymin + offset - other_edge->y0) * other_edge->dx +
- other_edge->x0;
- if (ymin == current->ymax) {
- if (current->dx > 0) {
- xx[k] =
- fmax(
+ if (ymin + offset >= other_edge->ymin &&
+ ymin + offset <= other_edge->ymax) {
+ adjacent_line_x_other_edge =
+ (ymin + offset - other_edge->y0) * other_edge->dx +
+ other_edge->x0;
+ if (xx[j - 1] > adjacent_line_x + 1 &&
+ xx[j - 1] > adjacent_line_x_other_edge + 1) {
+ xx[j - 1] =
+ roundf(fmax(
adjacent_line_x, adjacent_line_x_other_edge
- ) +
+ )) +
1;
- } else {
- xx[k] =
- fmin(
+ } else if (xx[j - 1] < adjacent_line_x - 1 &&
+ xx[j - 1] < adjacent_line_x_other_edge - 1) {
+ xx[j - 1] =
+ roundf(fmin(
adjacent_line_x, adjacent_line_x_other_edge
- ) -
- 1;
- }
- } else {
- if (current->dx > 0) {
- xx[k] = fmin(
- adjacent_line_x, adjacent_line_x_other_edge
- );
- } else {
- xx[k] =
- fmax(
- adjacent_line_x, adjacent_line_x_other_edge
- ) +
+ )) -
1;
}
+ break;
}
- break;
}
}
}
@@ -564,7 +594,7 @@ polygon_generic(
// Line would be before the current position
continue;
}
- draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);
+ draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline, mask);
if (x_end < x_pos) {
// Line would be before the current position
continue;
@@ -580,13 +610,13 @@ polygon_generic(
continue;
}
}
- (*hline)(im, x_start, ymin, x_end, ink);
+ (*hline)(im, x_start, ymin, x_end, ink, mask);
x_pos = x_end + 1;
}
- draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);
+ draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline, mask);
} else {
for (i = 1; i < j; i += 2) {
- (*hline)(im, ROUND_UP(xx[i - 1]), ymin, ROUND_DOWN(xx[i]), ink);
+ (*hline)(im, ROUND_UP(xx[i - 1]), ymin, ROUND_DOWN(xx[i]), ink, mask);
}
}
}
@@ -596,21 +626,6 @@ polygon_generic(
return 0;
}
-static inline int
-polygon8(Imaging im, int n, Edge *e, int ink, int eofill) {
- return polygon_generic(im, n, e, ink, eofill, hline8, 0);
-}
-
-static inline int
-polygon32(Imaging im, int n, Edge *e, int ink, int eofill) {
- return polygon_generic(im, n, e, ink, eofill, hline32, 0);
-}
-
-static inline int
-polygon32rgba(Imaging im, int n, Edge *e, int ink, int eofill) {
- return polygon_generic(im, n, e, ink, eofill, hline32rgba, 1);
-}
-
static inline void
add_edge(Edge *e, int x0, int y0, int x1, int y1) {
/* printf("edge %d %d %d %d\n", x0, y0, x1, y1); */
@@ -645,30 +660,29 @@ add_edge(Edge *e, int x0, int y0, int x1, int y1) {
typedef struct {
void (*point)(Imaging im, int x, int y, int ink);
- void (*hline)(Imaging im, int x0, int y0, int x1, int ink);
+ void (*hline)(Imaging im, int x0, int y0, int x1, int ink, Imaging mask);
void (*line)(Imaging im, int x0, int y0, int x1, int y1, int ink);
- int (*polygon)(Imaging im, int n, Edge *e, int ink, int eofill);
} DRAW;
-DRAW draw8 = {point8, hline8, line8, polygon8};
-DRAW draw32 = {point32, hline32, line32, polygon32};
-DRAW draw32rgba = {point32rgba, hline32rgba, line32rgba, polygon32rgba};
+DRAW draw8 = {point8, hline8, line8};
+DRAW draw32 = {point32, hline32, line32};
+DRAW draw32rgba = {point32rgba, hline32rgba, line32rgba};
/* -------------------------------------------------------------------- */
/* Interface */
/* -------------------------------------------------------------------- */
-#define DRAWINIT() \
- if (im->image8) { \
- draw = &draw8; \
- if (strncmp(im->mode, "I;16", 4) == 0) { \
- ink = INK16(ink_); \
- } else { \
- ink = INK8(ink_); \
- } \
- } else { \
- draw = (op) ? &draw32rgba : &draw32; \
- memcpy(&ink, ink_, sizeof(ink)); \
+#define DRAWINIT() \
+ if (im->image8) { \
+ draw = &draw8; \
+ if (isModeI16(im->mode)) { \
+ ink = INK16(ink_); \
+ } else { \
+ ink = INK8(ink_); \
+ } \
+ } else { \
+ draw = (op) ? &draw32rgba : &draw32; \
+ memcpy(&ink, ink_, sizeof(ink)); \
}
int
@@ -697,7 +711,15 @@ ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void *ink_, in
int
ImagingDrawWideLine(
- Imaging im, int x0, int y0, int x1, int y1, const void *ink_, int width, int op
+ Imaging im,
+ int x0,
+ int y0,
+ int x1,
+ int y1,
+ const void *ink_,
+ int width,
+ int op,
+ Imaging mask
) {
DRAW *draw;
INT32 ink;
@@ -737,7 +759,7 @@ ImagingDrawWideLine(
add_edge(e + 2, vertices[2][0], vertices[2][1], vertices[3][0], vertices[3][1]);
add_edge(e + 3, vertices[3][0], vertices[3][1], vertices[0][0], vertices[0][1]);
- draw->polygon(im, 4, e, ink, 0);
+ polygon_generic(im, 4, e, ink, 0, draw->hline, mask);
}
return 0;
}
@@ -780,7 +802,7 @@ ImagingDrawRectangle(
}
for (y = y0; y <= y1; y++) {
- draw->hline(im, x0, y, x1, ink);
+ draw->hline(im, x0, y, x1, ink, NULL);
}
} else {
@@ -789,8 +811,8 @@ ImagingDrawRectangle(
width = 1;
}
for (i = 0; i < width; i++) {
- draw->hline(im, x0, y0 + i, x1, ink);
- draw->hline(im, x0, y1 - i, x1, ink);
+ draw->hline(im, x0, y0 + i, x1, ink, NULL);
+ draw->hline(im, x0, y1 - i, x1, ink, NULL);
draw->line(im, x1 - i, y0 + width, x1 - i, y1 - width + 1, ink);
draw->line(im, x0 + i, y0 + width, x0 + i, y1 - width + 1, ink);
}
@@ -801,7 +823,14 @@ ImagingDrawRectangle(
int
ImagingDrawPolygon(
- Imaging im, int count, int *xy, const void *ink_, int fill, int width, int op
+ Imaging im,
+ int count,
+ int *xy,
+ const void *ink_,
+ int fill,
+ int width,
+ int op,
+ Imaging mask
) {
int i, n, x0, y0, x1, y1;
DRAW *draw;
@@ -845,7 +874,7 @@ ImagingDrawPolygon(
if (xy[i * 2] != xy[0] || xy[i * 2 + 1] != xy[1]) {
add_edge(&e[n++], xy[i * 2], xy[i * 2 + 1], xy[0], xy[1]);
}
- draw->polygon(im, n, e, ink, 0);
+ polygon_generic(im, n, e, ink, 0, draw->hline, mask);
free(e);
} else {
@@ -867,11 +896,12 @@ ImagingDrawPolygon(
xy[i * 2 + 3],
ink_,
width,
- op
+ op,
+ mask
);
}
ImagingDrawWideLine(
- im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink_, width, op
+ im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink_, width, op, mask
);
}
}
@@ -1542,7 +1572,9 @@ ellipseNew(
ellipse_init(&st, a, b, width);
int32_t X0, Y, X1;
while (ellipse_next(&st, &X0, &Y, &X1) != -1) {
- draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink);
+ draw->hline(
+ im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink, NULL
+ );
}
return 0;
}
@@ -1577,7 +1609,9 @@ clipEllipseNew(
int32_t X0, Y, X1;
int next_code;
while ((next_code = clip_ellipse_next(&st, &X0, &Y, &X1)) >= 0) {
- draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink);
+ draw->hline(
+ im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink, NULL
+ );
}
clip_ellipse_free(&st);
return next_code == -1 ? 0 : -1;
@@ -1995,7 +2029,7 @@ ImagingDrawOutline(
DRAWINIT();
- draw->polygon(im, outline->count, outline->edges, ink, 0);
+ polygon_generic(im, outline->count, outline->edges, ink, 0, draw->hline, NULL);
return 0;
}
diff --git a/src/libImaging/Effects.c b/src/libImaging/Effects.c
index 93e7af0bc..c05c5764e 100644
--- a/src/libImaging/Effects.c
+++ b/src/libImaging/Effects.c
@@ -36,7 +36,7 @@ ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality) {
return (Imaging)ImagingError_ValueError(NULL);
}
- im = ImagingNewDirty("L", xsize, ysize);
+ im = ImagingNewDirty(IMAGING_MODE_L, xsize, ysize);
if (!im) {
return NULL;
}
@@ -80,7 +80,7 @@ ImagingEffectNoise(int xsize, int ysize, float sigma) {
int nextok;
double this, next;
- imOut = ImagingNewDirty("L", xsize, ysize);
+ imOut = ImagingNewDirty(IMAGING_MODE_L, xsize, ysize);
if (!imOut) {
return NULL;
}
diff --git a/src/libImaging/File.c b/src/libImaging/File.c
index 76d0abccc..435dbeca0 100644
--- a/src/libImaging/File.c
+++ b/src/libImaging/File.c
@@ -23,14 +23,13 @@ int
ImagingSaveRaw(Imaging im, FILE *fp) {
int x, y, i;
- if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) {
+ if (im->mode == IMAGING_MODE_1 || im->mode == IMAGING_MODE_L) {
/* @PIL227: FIXME: for mode "1", map != 0 to 255 */
/* PGM "L" */
for (y = 0; y < im->ysize; y++) {
fwrite(im->image[y], 1, im->xsize, fp);
}
-
} else {
/* PPM "RGB" or other internal format */
for (y = 0; y < im->ysize; y++) {
@@ -54,14 +53,14 @@ ImagingSavePPM(Imaging im, const char *outfile) {
fp = fopen(outfile, "wb");
if (!fp) {
- (void)ImagingError_OSError();
+ PyErr_SetString(PyExc_OSError, "error when accessing file");
return 0;
}
- if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) {
+ if (im->mode == IMAGING_MODE_1 || im->mode == IMAGING_MODE_L) {
/* Write "PGM" */
fprintf(fp, "P5\n%d %d\n255\n", im->xsize, im->ysize);
- } else if (strcmp(im->mode, "RGB") == 0) {
+ } else if (im->mode == IMAGING_MODE_RGB) {
/* Write "PPM" */
fprintf(fp, "P6\n%d %d\n255\n", im->xsize, im->ysize);
} else {
diff --git a/src/libImaging/Fill.c b/src/libImaging/Fill.c
index 8fb481e7e..cbd303204 100644
--- a/src/libImaging/Fill.c
+++ b/src/libImaging/Fill.c
@@ -68,11 +68,12 @@ ImagingFill(Imaging im, const void *colour) {
}
Imaging
-ImagingFillLinearGradient(const char *mode) {
+ImagingFillLinearGradient(const ModeID mode) {
Imaging im;
int y;
- if (strlen(mode) != 1) {
+ if (mode != IMAGING_MODE_1 && mode != IMAGING_MODE_F && mode != IMAGING_MODE_I &&
+ mode != IMAGING_MODE_L && mode != IMAGING_MODE_P) {
return (Imaging)ImagingError_ModeError();
}
@@ -102,12 +103,13 @@ ImagingFillLinearGradient(const char *mode) {
}
Imaging
-ImagingFillRadialGradient(const char *mode) {
+ImagingFillRadialGradient(const ModeID mode) {
Imaging im;
int x, y;
int d;
- if (strlen(mode) != 1) {
+ if (mode != IMAGING_MODE_1 && mode != IMAGING_MODE_F && mode != IMAGING_MODE_I &&
+ mode != IMAGING_MODE_L && mode != IMAGING_MODE_P) {
return (Imaging)ImagingError_ModeError();
}
@@ -118,8 +120,9 @@ ImagingFillRadialGradient(const char *mode) {
for (y = 0; y < 256; y++) {
for (x = 0; x < 256; x++) {
- d = (int
- )sqrt((double)((x - 128) * (x - 128) + (y - 128) * (y - 128)) * 2.0);
+ d = (int)sqrt(
+ (double)((x - 128) * (x - 128) + (y - 128) * (y - 128)) * 2.0
+ );
if (d >= 255) {
d = 255;
}
diff --git a/src/libImaging/Filter.c b/src/libImaging/Filter.c
index 7b7b2e429..cefb8fcdc 100644
--- a/src/libImaging/Filter.c
+++ b/src/libImaging/Filter.c
@@ -155,9 +155,10 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
} else {
int bigendian = 0;
if (im->type == IMAGING_TYPE_SPECIAL) {
- if (strcmp(im->mode, "I;16B") == 0
+ if (
+ im->mode == IMAGING_MODE_I_16B
#ifdef WORDS_BIGENDIAN
- || strcmp(im->mode, "I;16N") == 0
+ || im->mode == IMAGING_MODE_I_16N
#endif
) {
bigendian = 1;
@@ -308,9 +309,10 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
} else {
int bigendian = 0;
if (im->type == IMAGING_TYPE_SPECIAL) {
- if (strcmp(im->mode, "I;16B") == 0
+ if (
+ im->mode == IMAGING_MODE_I_16B
#ifdef WORDS_BIGENDIAN
- || strcmp(im->mode, "I;16N") == 0
+ || im->mode == IMAGING_MODE_I_16N
#endif
) {
bigendian = 1;
diff --git a/src/libImaging/FliDecode.c b/src/libImaging/FliDecode.c
index 130ecb7f7..44994823e 100644
--- a/src/libImaging/FliDecode.c
+++ b/src/libImaging/FliDecode.c
@@ -16,9 +16,11 @@
#include "Imaging.h"
-#define I16(ptr) ((ptr)[0] + ((ptr)[1] << 8))
+#define I16(ptr) ((ptr)[0] + ((int)(ptr)[1] << 8))
-#define I32(ptr) ((ptr)[0] + ((ptr)[1] << 8) + ((ptr)[2] << 16) + ((ptr)[3] << 24))
+#define I32(ptr) \
+ ((ptr)[0] + ((INT32)(ptr)[1] << 8) + ((INT32)(ptr)[2] << 16) + \
+ ((INT32)(ptr)[3] << 24))
#define ERR_IF_DATA_OOB(offset) \
if ((data + (offset)) > ptr + bytes) { \
diff --git a/src/libImaging/Geometry.c b/src/libImaging/Geometry.c
index 1e2abd7e7..80ecd7cb6 100644
--- a/src/libImaging/Geometry.c
+++ b/src/libImaging/Geometry.c
@@ -19,7 +19,7 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn) {
ImagingSectionCookie cookie;
int x, y, xr;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) {
@@ -41,7 +41,7 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn) {
ImagingSectionEnter(&cookie);
if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
+ if (isModeI16(imIn->mode)) {
FLIP_LEFT_RIGHT(UINT16, image8)
} else {
FLIP_LEFT_RIGHT(UINT8, image8)
@@ -62,7 +62,7 @@ ImagingFlipTopBottom(Imaging imOut, Imaging imIn) {
ImagingSectionCookie cookie;
int y, yr;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) {
@@ -89,7 +89,7 @@ ImagingRotate90(Imaging imOut, Imaging imIn) {
int x, y, xx, yy, xr, xxsize, yysize;
int xxx, yyy, xxxsize, yyysize;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
@@ -127,7 +127,7 @@ ImagingRotate90(Imaging imOut, Imaging imIn) {
ImagingSectionEnter(&cookie);
if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
+ if (isModeI16(imIn->mode)) {
ROTATE_90(UINT16, image8);
} else {
ROTATE_90(UINT8, image8);
@@ -149,7 +149,7 @@ ImagingTranspose(Imaging imOut, Imaging imIn) {
int x, y, xx, yy, xxsize, yysize;
int xxx, yyy, xxxsize, yyysize;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
@@ -186,7 +186,7 @@ ImagingTranspose(Imaging imOut, Imaging imIn) {
ImagingSectionEnter(&cookie);
if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
+ if (isModeI16(imIn->mode)) {
TRANSPOSE(UINT16, image8);
} else {
TRANSPOSE(UINT8, image8);
@@ -208,7 +208,7 @@ ImagingTransverse(Imaging imOut, Imaging imIn) {
int x, y, xr, yr, xx, yy, xxsize, yysize;
int xxx, yyy, xxxsize, yyysize;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
@@ -247,7 +247,7 @@ ImagingTransverse(Imaging imOut, Imaging imIn) {
ImagingSectionEnter(&cookie);
if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
+ if (isModeI16(imIn->mode)) {
TRANSVERSE(UINT16, image8);
} else {
TRANSVERSE(UINT8, image8);
@@ -268,7 +268,7 @@ ImagingRotate180(Imaging imOut, Imaging imIn) {
ImagingSectionCookie cookie;
int x, y, xr, yr;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) {
@@ -291,7 +291,7 @@ ImagingRotate180(Imaging imOut, Imaging imIn) {
yr = imIn->ysize - 1;
if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
+ if (isModeI16(imIn->mode)) {
ROTATE_180(UINT16, image8)
} else {
ROTATE_180(UINT8, image8)
@@ -313,7 +313,7 @@ ImagingRotate270(Imaging imOut, Imaging imIn) {
int x, y, xx, yy, yr, xxsize, yysize;
int xxx, yyy, xxxsize, yyysize;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
@@ -351,7 +351,7 @@ ImagingRotate270(Imaging imOut, Imaging imIn) {
ImagingSectionEnter(&cookie);
if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
+ if (isModeI16(imIn->mode)) {
ROTATE_270(UINT16, image8);
} else {
ROTATE_270(UINT8, image8);
@@ -791,7 +791,7 @@ ImagingGenericTransform(
char *out;
double xx, yy;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
@@ -848,7 +848,7 @@ ImagingScaleAffine(
int xmin, xmax;
int *xintab;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
@@ -1035,7 +1035,7 @@ ImagingTransformAffine(
double xx, yy;
double xo, yo;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
+ if (!imOut || !imIn || imIn->mode != imOut->mode) {
return (Imaging)ImagingError_ModeError();
}
diff --git a/src/libImaging/GetBBox.c b/src/libImaging/GetBBox.c
index d430893dd..d336121d5 100644
--- a/src/libImaging/GetBBox.c
+++ b/src/libImaging/GetBBox.c
@@ -90,9 +90,9 @@ ImagingGetBBox(Imaging im, int bbox[4], int alpha_only) {
if (im->bands == 3) {
((UINT8 *)&mask)[3] = 0;
} else if (alpha_only &&
- (strcmp(im->mode, "RGBa") == 0 || strcmp(im->mode, "RGBA") == 0 ||
- strcmp(im->mode, "La") == 0 || strcmp(im->mode, "LA") == 0 ||
- strcmp(im->mode, "PA") == 0)) {
+ (im->mode == IMAGING_MODE_RGBa || im->mode == IMAGING_MODE_RGBA ||
+ im->mode == IMAGING_MODE_La || im->mode == IMAGING_MODE_LA ||
+ im->mode == IMAGING_MODE_PA)) {
#ifdef WORDS_BIGENDIAN
mask = 0x000000ff;
#else
@@ -208,11 +208,11 @@ ImagingGetExtrema(Imaging im, void *extrema) {
memcpy(((char *)extrema) + sizeof(fmin), &fmax, sizeof(fmax));
break;
case IMAGING_TYPE_SPECIAL:
- if (strcmp(im->mode, "I;16") == 0) {
+ if (im->mode == IMAGING_MODE_I_16) {
UINT16 v;
UINT8 *pixel = *im->image8;
#ifdef WORDS_BIGENDIAN
- v = pixel[0] + (pixel[1] << 8);
+ v = pixel[0] + ((UINT16)pixel[1] << 8);
#else
memcpy(&v, pixel, sizeof(v));
#endif
@@ -221,7 +221,7 @@ ImagingGetExtrema(Imaging im, void *extrema) {
for (x = 0; x < im->xsize; x++) {
pixel = (UINT8 *)im->image[y] + x * sizeof(v);
#ifdef WORDS_BIGENDIAN
- v = pixel[0] + (pixel[1] << 8);
+ v = pixel[0] + ((UINT16)pixel[1] << 8);
#else
memcpy(&v, pixel, sizeof(v));
#endif
diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c
index c5a547a64..7af600035 100644
--- a/src/libImaging/Histo.c
+++ b/src/libImaging/Histo.c
@@ -43,10 +43,10 @@ ImagingHistogramNew(Imaging im) {
if (!h) {
return (ImagingHistogram)ImagingError_MemoryError();
}
- strncpy(h->mode, im->mode, IMAGING_MODE_LENGTH - 1);
- h->mode[IMAGING_MODE_LENGTH - 1] = 0;
+ h->mode = im->mode;
h->bands = im->bands;
+
h->histogram = calloc(im->pixelsize, 256 * sizeof(long));
if (!h->histogram) {
free(h);
@@ -73,7 +73,7 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void *minmax) {
if (im->xsize != imMask->xsize || im->ysize != imMask->ysize) {
return ImagingError_Mismatch();
}
- if (strcmp(imMask->mode, "1") != 0 && strcmp(imMask->mode, "L") != 0) {
+ if (imMask->mode != IMAGING_MODE_1 && imMask->mode != IMAGING_MODE_L) {
return ImagingError_ValueError("bad transparency mask");
}
}
@@ -132,11 +132,15 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void *minmax) {
ImagingSectionEnter(&cookie);
for (y = 0; y < im->ysize; y++) {
UINT8 *in = (UINT8 *)im->image[y];
- for (x = 0; x < im->xsize; x++) {
- h->histogram[(*in++)]++;
- h->histogram[(*in++) + 256]++;
- h->histogram[(*in++) + 512]++;
- h->histogram[(*in++) + 768]++;
+ for (x = 0; x < im->xsize; x++, in += 4) {
+ h->histogram[*in]++;
+ if (im->bands == 2) {
+ h->histogram[*(in + 3) + 256]++;
+ } else {
+ h->histogram[*(in + 1) + 256]++;
+ h->histogram[*(in + 2) + 512]++;
+ h->histogram[*(in + 3) + 768]++;
+ }
}
}
ImagingSectionLeave(&cookie);
diff --git a/src/libImaging/ImDib.h b/src/libImaging/ImDib.h
index 91ff3f322..65f090f92 100644
--- a/src/libImaging/ImDib.h
+++ b/src/libImaging/ImDib.h
@@ -27,7 +27,7 @@ struct ImagingDIBInstance {
UINT8 *bits;
HPALETTE palette;
/* Used by cut and paste */
- char mode[4];
+ ModeID mode;
int xsize, ysize;
int pixelsize;
int linesize;
@@ -37,11 +37,11 @@ struct ImagingDIBInstance {
typedef struct ImagingDIBInstance *ImagingDIB;
-extern char *
+extern ModeID
ImagingGetModeDIB(int size_out[2]);
extern ImagingDIB
-ImagingNewDIB(const char *mode, int xsize, int ysize);
+ImagingNewDIB(ModeID mode, int xsize, int ysize);
extern void
ImagingDeleteDIB(ImagingDIB im);
diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h
index 0c2d3fc2e..f7049c892 100644
--- a/src/libImaging/Imaging.h
+++ b/src/libImaging/Imaging.h
@@ -11,6 +11,7 @@
*/
#include "ImPlatform.h"
+#include "Mode.h"
#if defined(__cplusplus)
extern "C" {
@@ -20,6 +21,8 @@ extern "C" {
#define M_PI 3.1415926535897932384626433832795
#endif
+#include "Arrow.h"
+
/* -------------------------------------------------------------------- */
/*
@@ -69,9 +72,6 @@ typedef struct ImagingPaletteInstance *ImagingPalette;
#define IMAGING_TYPE_FLOAT32 2
#define IMAGING_TYPE_SPECIAL 3 /* check mode for details */
-#define IMAGING_MODE_LENGTH \
- 6 + 1 /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */
-
typedef struct {
char *ptr;
int size;
@@ -79,12 +79,11 @@ typedef struct {
struct ImagingMemoryInstance {
/* Format */
- char mode[IMAGING_MODE_LENGTH]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK",
- "YCbCr", "BGR;xy") */
- int type; /* Data type (IMAGING_TYPE_*) */
- int depth; /* Depth (ignored in this version) */
- int bands; /* Number of bands (1, 2, 3, or 4) */
- int xsize; /* Image dimension. */
+ ModeID mode; /* Image mode (IMAGING_MODE_*) */
+ int type; /* Data type (IMAGING_TYPE_*) */
+ int depth; /* Depth (ignored in this version) */
+ int bands; /* Number of bands (1, 2, 3, or 4) */
+ int xsize; /* Image dimension. */
int ysize;
/* Colour palette (for "P" images only) */
@@ -104,6 +103,21 @@ struct ImagingMemoryInstance {
/* Virtual methods */
void (*destroy)(Imaging im);
+
+ /* arrow */
+ int refcount; /* Number of arrow arrays that have been allocated */
+ char band_names[4][3]; /* names of bands, max 2 char + null terminator */
+ char arrow_band_format[2]; /* single character + null terminator */
+
+ int read_only; /* flag for read-only. set for arrow borrowed arrays */
+ PyObject *arrow_array_capsule; /* upstream arrow array source */
+
+ int blocks_count; /* Number of blocks that have been allocated */
+ int lines_per_block; /* Number of lines in a block have been allocated */
+
+#ifdef Py_GIL_DISABLED
+ PyMutex mutex;
+#endif
};
#define IMAGING_PIXEL_1(im, x, y) ((im)->image8[(y)][(x)])
@@ -123,15 +137,15 @@ struct ImagingMemoryInstance {
#define IMAGING_PIXEL_FLOAT32(im, x, y) (((FLOAT32 *)(im)->image32[y])[x])
struct ImagingAccessInstance {
- const char *mode;
+ ModeID mode;
void (*get_pixel)(Imaging im, int x, int y, void *pixel);
void (*put_pixel)(Imaging im, int x, int y, const void *pixel);
};
struct ImagingHistogramInstance {
/* Format */
- char mode[IMAGING_MODE_LENGTH]; /* Band names (of corresponding source image) */
- int bands; /* Number of bands (1, 3, or 4) */
+ ModeID mode; /* Mode ID of corresponding source image */
+ int bands; /* Number of bands (1, 2, 3, or 4) */
/* Data */
long *histogram; /* Histogram (bands*256 longs) */
@@ -139,7 +153,7 @@ struct ImagingHistogramInstance {
struct ImagingPaletteInstance {
/* Format */
- char mode[IMAGING_MODE_LENGTH]; /* Band names */
+ ModeID mode;
/* Data */
int size;
@@ -161,6 +175,7 @@ typedef struct ImagingMemoryArena {
int stats_reallocated_blocks; /* Number of blocks which were actually reallocated
after retrieving */
int stats_freed_blocks; /* Number of freed blocks */
+ int use_block_allocator; /* don't use arena, use block allocator */
#ifdef Py_GIL_DISABLED
PyMutex mutex;
#endif
@@ -174,23 +189,34 @@ extern int
ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max);
extern void
ImagingMemoryClearCache(ImagingMemoryArena arena, int new_size);
+extern void
+ImagingMemorySetBlockAllocator(ImagingMemoryArena arena, int use_block_allocator);
extern Imaging
-ImagingNew(const char *mode, int xsize, int ysize);
+ImagingNew(ModeID mode, int xsize, int ysize);
extern Imaging
-ImagingNewDirty(const char *mode, int xsize, int ysize);
+ImagingNewDirty(ModeID mode, int xsize, int ysize);
extern Imaging
-ImagingNew2Dirty(const char *mode, Imaging imOut, Imaging imIn);
+ImagingNew2Dirty(ModeID mode, Imaging imOut, Imaging imIn);
extern void
ImagingDelete(Imaging im);
extern Imaging
-ImagingNewBlock(const char *mode, int xsize, int ysize);
+ImagingNewBlock(ModeID mode, int xsize, int ysize);
extern Imaging
-ImagingNewPrologue(const char *mode, int xsize, int ysize);
+ImagingNewArrow(
+ const ModeID mode,
+ int xsize,
+ int ysize,
+ PyObject *schema_capsule,
+ PyObject *array_capsule
+);
+
extern Imaging
-ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int structure_size);
+ImagingNewPrologue(ModeID mode, int xsize, int ysize);
+extern Imaging
+ImagingNewPrologueSubtype(ModeID mode, int xsize, int ysize, int structure_size);
extern void
ImagingCopyPalette(Imaging destination, Imaging source);
@@ -198,8 +224,6 @@ ImagingCopyPalette(Imaging destination, Imaging source);
extern void
ImagingHistogramDelete(ImagingHistogram histogram);
-extern void
-ImagingAccessInit(void);
extern ImagingAccess
ImagingAccessNew(Imaging im);
extern void
@@ -207,7 +231,7 @@ _ImagingAccessDelete(Imaging im, ImagingAccess access);
#define ImagingAccessDelete(im, access) /* nop, for now */
extern ImagingPalette
-ImagingPaletteNew(const char *mode);
+ImagingPaletteNew(ModeID mode);
extern ImagingPalette
ImagingPaletteNewBrowser(void);
extern ImagingPalette
@@ -241,8 +265,6 @@ ImagingSectionLeave(ImagingSectionCookie *cookie);
/* Exceptions */
/* ---------- */
-extern void *
-ImagingError_OSError(void);
extern void *
ImagingError_MemoryError(void);
extern void *
@@ -251,8 +273,6 @@ extern void *
ImagingError_Mismatch(void); /* maps to ValueError by default */
extern void *
ImagingError_ValueError(const char *message);
-extern void
-ImagingError_Clear(void);
/* Transform callbacks */
/* ------------------- */
@@ -283,13 +303,13 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha);
extern Imaging
ImagingCopy(Imaging im);
extern Imaging
-ImagingConvert(Imaging im, const char *mode, ImagingPalette palette, int dither);
+ImagingConvert(Imaging im, ModeID mode, ImagingPalette palette, int dither);
extern Imaging
-ImagingConvertInPlace(Imaging im, const char *mode);
+ImagingConvertInPlace(Imaging im, ModeID mode);
extern Imaging
-ImagingConvertMatrix(Imaging im, const char *mode, float m[]);
+ImagingConvertMatrix(Imaging im, ModeID mode, float m[]);
extern Imaging
-ImagingConvertTransparent(Imaging im, const char *mode, int r, int g, int b);
+ImagingConvertTransparent(Imaging im, ModeID mode, int r, int g, int b);
extern Imaging
ImagingCrop(Imaging im, int x0, int y0, int x1, int y1);
extern Imaging
@@ -303,9 +323,9 @@ ImagingFill2(
extern Imaging
ImagingFillBand(Imaging im, int band, int color);
extern Imaging
-ImagingFillLinearGradient(const char *mode);
+ImagingFillLinearGradient(ModeID mode);
extern Imaging
-ImagingFillRadialGradient(const char *mode);
+ImagingFillRadialGradient(ModeID mode);
extern Imaging
ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32 *kernel, FLOAT32 offset);
extern Imaging
@@ -319,7 +339,7 @@ ImagingGaussianBlur(
extern Imaging
ImagingGetBand(Imaging im, int band);
extern Imaging
-ImagingMerge(const char *mode, Imaging bands[4]);
+ImagingMerge(ModeID mode, Imaging bands[4]);
extern int
ImagingSplit(Imaging im, Imaging bands[4]);
extern int
@@ -346,7 +366,7 @@ ImagingOffset(Imaging im, int xoffset, int yoffset);
extern int
ImagingPaste(Imaging into, Imaging im, Imaging mask, int x0, int y0, int x1, int y1);
extern Imaging
-ImagingPoint(Imaging im, const char *tablemode, const void *table);
+ImagingPoint(Imaging im, ModeID tablemode, const void *table);
extern Imaging
ImagingPointTransform(Imaging imIn, double scale, double offset);
extern Imaging
@@ -481,7 +501,15 @@ extern int
ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void *ink, int op);
extern int
ImagingDrawWideLine(
- Imaging im, int x0, int y0, int x1, int y1, const void *ink, int width, int op
+ Imaging im,
+ int x0,
+ int y0,
+ int x1,
+ int y1,
+ const void *ink,
+ int width,
+ int op,
+ Imaging mask
);
extern int
ImagingDrawPieslice(
@@ -501,7 +529,14 @@ extern int
ImagingDrawPoint(Imaging im, int x, int y, const void *ink, int op);
extern int
ImagingDrawPolygon(
- Imaging im, int points, int *xy, const void *ink, int fill, int width, int op
+ Imaging im,
+ int points,
+ int *xy,
+ const void *ink,
+ int fill,
+ int width,
+ int op,
+ Imaging mask
);
extern int
ImagingDrawRectangle(
@@ -567,6 +602,8 @@ typedef int (*ImagingCodec)(
extern int
ImagingBcnDecode(Imaging im, ImagingCodecState state, UINT8 *buffer, Py_ssize_t bytes);
extern int
+ImagingBcnEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int bytes);
+extern int
ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8 *buffer, Py_ssize_t bytes);
extern int
ImagingEpsEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int bytes);
@@ -670,9 +707,9 @@ extern void
ImagingConvertYCbCr2RGB(UINT8 *out, const UINT8 *in, int pixels);
extern ImagingShuffler
-ImagingFindUnpacker(const char *mode, const char *rawmode, int *bits_out);
+ImagingFindUnpacker(ModeID mode, RawModeID rawmode, int *bits_out);
extern ImagingShuffler
-ImagingFindPacker(const char *mode, const char *rawmode, int *bits_out);
+ImagingFindPacker(ModeID mode, RawModeID rawmode, int *bits_out);
struct ImagingCodecStateInstance {
int count;
@@ -698,6 +735,13 @@ _imaging_seek_pyFd(PyObject *fd, Py_ssize_t offset, int whence);
extern Py_ssize_t
_imaging_tell_pyFd(PyObject *fd);
+/* Arrow */
+
+extern int
+export_imaging_array(Imaging im, struct ArrowArray *array);
+extern int
+export_imaging_schema(Imaging im, struct ArrowSchema *schema);
+
/* Errcodes */
#define IMAGING_CODEC_END 1
#define IMAGING_CODEC_OVERRUN -1
@@ -705,6 +749,8 @@ _imaging_tell_pyFd(PyObject *fd);
#define IMAGING_CODEC_UNKNOWN -3
#define IMAGING_CODEC_CONFIG -8
#define IMAGING_CODEC_MEMORY -9
+#define IMAGING_ARROW_INCOMPATIBLE_MODE -10
+#define IMAGING_ARROW_MEMORY_LAYOUT -11
#include "ImagingUtils.h"
extern UINT8 *clip8_lookups;
diff --git a/src/libImaging/Jpeg.h b/src/libImaging/Jpeg.h
index 0fad2f7cd..c85db5c8a 100644
--- a/src/libImaging/Jpeg.h
+++ b/src/libImaging/Jpeg.h
@@ -28,12 +28,12 @@ typedef struct {
typedef struct {
/* CONFIGURATION */
- /* Jpeg file mode (empty if not known) */
- char jpegmode[8 + 1];
+ /* Jpeg file mode */
+ RawModeID jpegmode;
- /* Converter output mode (input to the shuffler). If empty,
- convert conversions are disabled */
- char rawmode[8 + 1];
+ /* Converter output mode (input to the shuffler) */
+ /* If not a valid mode, convert conversions are disabled */
+ RawModeID rawmode;
/* If set, trade quality for speed */
int draft;
@@ -94,7 +94,7 @@ typedef struct {
unsigned int restart_marker_rows;
/* Converter input mode (input to the shuffler) */
- char rawmode[8 + 1];
+ RawModeID rawmode;
/* Custom quantization tables () */
unsigned int *qtables;
diff --git a/src/libImaging/Jpeg2KDecode.c b/src/libImaging/Jpeg2KDecode.c
index cc6955ca5..1b496f45e 100644
--- a/src/libImaging/Jpeg2KDecode.c
+++ b/src/libImaging/Jpeg2KDecode.c
@@ -71,7 +71,7 @@ typedef void (*j2k_unpacker_t)(
);
struct j2k_decode_unpacker {
- const char *mode;
+ const ModeID mode;
OPJ_COLOR_SPACE color_space;
unsigned components;
/* bool indicating if unpacker supports subsampling */
@@ -599,26 +599,26 @@ j2ku_sycca_rgba(
}
static const struct j2k_decode_unpacker j2k_unpackers[] = {
- {"L", OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_l},
- {"P", OPJ_CLRSPC_SRGB, 1, 0, j2ku_gray_l},
- {"PA", OPJ_CLRSPC_SRGB, 2, 0, j2ku_graya_la},
- {"I;16", OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_i},
- {"I;16B", OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_i},
- {"LA", OPJ_CLRSPC_GRAY, 2, 0, j2ku_graya_la},
- {"RGB", OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_rgb},
- {"RGB", OPJ_CLRSPC_GRAY, 2, 0, j2ku_gray_rgb},
- {"RGB", OPJ_CLRSPC_SRGB, 3, 1, j2ku_srgb_rgb},
- {"RGB", OPJ_CLRSPC_SYCC, 3, 1, j2ku_sycc_rgb},
- {"RGB", OPJ_CLRSPC_SRGB, 4, 1, j2ku_srgb_rgb},
- {"RGB", OPJ_CLRSPC_SYCC, 4, 1, j2ku_sycc_rgb},
- {"RGBA", OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_rgb},
- {"RGBA", OPJ_CLRSPC_GRAY, 2, 0, j2ku_graya_la},
- {"RGBA", OPJ_CLRSPC_SRGB, 3, 1, j2ku_srgb_rgb},
- {"RGBA", OPJ_CLRSPC_SYCC, 3, 1, j2ku_sycc_rgb},
- {"RGBA", OPJ_CLRSPC_GRAY, 4, 1, j2ku_srgba_rgba},
- {"RGBA", OPJ_CLRSPC_SRGB, 4, 1, j2ku_srgba_rgba},
- {"RGBA", OPJ_CLRSPC_SYCC, 4, 1, j2ku_sycca_rgba},
- {"CMYK", OPJ_CLRSPC_CMYK, 4, 1, j2ku_srgba_rgba},
+ {IMAGING_MODE_L, OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_l},
+ {IMAGING_MODE_P, OPJ_CLRSPC_SRGB, 1, 0, j2ku_gray_l},
+ {IMAGING_MODE_PA, OPJ_CLRSPC_SRGB, 2, 0, j2ku_graya_la},
+ {IMAGING_MODE_I_16, OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_i},
+ {IMAGING_MODE_I_16B, OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_i},
+ {IMAGING_MODE_LA, OPJ_CLRSPC_GRAY, 2, 0, j2ku_graya_la},
+ {IMAGING_MODE_RGB, OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_rgb},
+ {IMAGING_MODE_RGB, OPJ_CLRSPC_GRAY, 2, 0, j2ku_gray_rgb},
+ {IMAGING_MODE_RGB, OPJ_CLRSPC_SRGB, 3, 1, j2ku_srgb_rgb},
+ {IMAGING_MODE_RGB, OPJ_CLRSPC_SYCC, 3, 1, j2ku_sycc_rgb},
+ {IMAGING_MODE_RGB, OPJ_CLRSPC_SRGB, 4, 1, j2ku_srgb_rgb},
+ {IMAGING_MODE_RGB, OPJ_CLRSPC_SYCC, 4, 1, j2ku_sycc_rgb},
+ {IMAGING_MODE_RGBA, OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_rgb},
+ {IMAGING_MODE_RGBA, OPJ_CLRSPC_GRAY, 2, 0, j2ku_graya_la},
+ {IMAGING_MODE_RGBA, OPJ_CLRSPC_SRGB, 3, 1, j2ku_srgb_rgb},
+ {IMAGING_MODE_RGBA, OPJ_CLRSPC_SYCC, 3, 1, j2ku_sycc_rgb},
+ {IMAGING_MODE_RGBA, OPJ_CLRSPC_GRAY, 4, 1, j2ku_srgba_rgba},
+ {IMAGING_MODE_RGBA, OPJ_CLRSPC_SRGB, 4, 1, j2ku_srgba_rgba},
+ {IMAGING_MODE_RGBA, OPJ_CLRSPC_SYCC, 4, 1, j2ku_sycca_rgba},
+ {IMAGING_MODE_CMYK, OPJ_CLRSPC_CMYK, 4, 1, j2ku_srgba_rgba},
};
/* -------------------------------------------------------------------- */
@@ -771,7 +771,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) {
if (color_space == j2k_unpackers[n].color_space &&
image->numcomps == j2k_unpackers[n].components &&
(j2k_unpackers[n].subsampling || (subsampling == -1)) &&
- strcmp(im->mode, j2k_unpackers[n].mode) == 0) {
+ im->mode == j2k_unpackers[n].mode) {
unpack = j2k_unpackers[n].unpacker;
break;
}
diff --git a/src/libImaging/Jpeg2KEncode.c b/src/libImaging/Jpeg2KEncode.c
index 34d1a2294..fdfbde2d7 100644
--- a/src/libImaging/Jpeg2KEncode.c
+++ b/src/libImaging/Jpeg2KEncode.c
@@ -207,8 +207,8 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) {
if (params->cp_cinema == OPJ_CINEMA4K_24) {
float max_rate =
- ((float)(components * im->xsize * im->ysize * 8) / (CINEMA_24_CS_LENGTH * 8)
- );
+ ((float)(components * im->xsize * im->ysize * 8) /
+ (CINEMA_24_CS_LENGTH * 8));
params->POC[0].tile = 1;
params->POC[0].resno0 = 0;
@@ -243,8 +243,8 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) {
params->max_comp_size = COMP_24_CS_MAX_LENGTH;
} else {
float max_rate =
- ((float)(components * im->xsize * im->ysize * 8) / (CINEMA_48_CS_LENGTH * 8)
- );
+ ((float)(components * im->xsize * im->ysize * 8) /
+ (CINEMA_48_CS_LENGTH * 8));
for (n = 0; n < params->tcp_numlayers; ++n) {
rate = 0;
@@ -305,34 +305,34 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
#endif
/* Setup an opj_image */
- if (strcmp(im->mode, "L") == 0) {
+ if (im->mode == IMAGING_MODE_L) {
components = 1;
color_space = OPJ_CLRSPC_GRAY;
pack = j2k_pack_l;
- } else if (strcmp(im->mode, "I;16") == 0 || strcmp(im->mode, "I;16B") == 0) {
+ } else if (im->mode == IMAGING_MODE_I_16 || im->mode == IMAGING_MODE_I_16B) {
components = 1;
color_space = OPJ_CLRSPC_GRAY;
pack = j2k_pack_i16;
prec = 16;
- } else if (strcmp(im->mode, "LA") == 0) {
+ } else if (im->mode == IMAGING_MODE_LA) {
components = 2;
color_space = OPJ_CLRSPC_GRAY;
pack = j2k_pack_la;
- } else if (strcmp(im->mode, "RGB") == 0) {
+ } else if (im->mode == IMAGING_MODE_RGB) {
components = 3;
color_space = OPJ_CLRSPC_SRGB;
pack = j2k_pack_rgb;
- } else if (strcmp(im->mode, "YCbCr") == 0) {
+ } else if (im->mode == IMAGING_MODE_YCbCr) {
components = 3;
color_space = OPJ_CLRSPC_SYCC;
pack = j2k_pack_rgb;
- } else if (strcmp(im->mode, "RGBA") == 0) {
+ } else if (im->mode == IMAGING_MODE_RGBA) {
components = 4;
color_space = OPJ_CLRSPC_SRGB;
pack = j2k_pack_rgba;
#if ((OPJ_VERSION_MAJOR == 2 && OPJ_VERSION_MINOR == 5 && OPJ_VERSION_BUILD >= 3) || \
(OPJ_VERSION_MAJOR == 2 && OPJ_VERSION_MINOR > 5) || OPJ_VERSION_MAJOR > 2)
- } else if (strcmp(im->mode, "CMYK") == 0) {
+ } else if (im->mode == IMAGING_MODE_CMYK) {
components = 4;
color_space = OPJ_CLRSPC_CMYK;
pack = j2k_pack_rgba;
@@ -497,9 +497,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) {
goto quick_exit;
}
- if (strcmp(im->mode, "RGBA") == 0) {
+ if (im->mode == IMAGING_MODE_RGBA) {
image->comps[3].alpha = 1;
- } else if (strcmp(im->mode, "LA") == 0) {
+ } else if (im->mode == IMAGING_MODE_LA) {
image->comps[1].alpha = 1;
}
diff --git a/src/libImaging/JpegDecode.c b/src/libImaging/JpegDecode.c
index 2970f56d1..ae3274456 100644
--- a/src/libImaging/JpegDecode.c
+++ b/src/libImaging/JpegDecode.c
@@ -180,41 +180,41 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t by
/* Decoder settings */
- /* jpegmode indicates what's in the file; if not set, we'll
- trust the decoder */
- if (strcmp(context->jpegmode, "L") == 0) {
+ /* jpegmode indicates what's in the file. */
+ /* If not valid, we'll trust the decoder. */
+ if (context->jpegmode == IMAGING_RAWMODE_L) {
context->cinfo.jpeg_color_space = JCS_GRAYSCALE;
- } else if (strcmp(context->jpegmode, "RGB") == 0) {
+ } else if (context->jpegmode == IMAGING_RAWMODE_RGB) {
context->cinfo.jpeg_color_space = JCS_RGB;
- } else if (strcmp(context->jpegmode, "CMYK") == 0) {
+ } else if (context->jpegmode == IMAGING_RAWMODE_CMYK) {
context->cinfo.jpeg_color_space = JCS_CMYK;
- } else if (strcmp(context->jpegmode, "YCbCr") == 0) {
+ } else if (context->jpegmode == IMAGING_RAWMODE_YCbCr) {
context->cinfo.jpeg_color_space = JCS_YCbCr;
- } else if (strcmp(context->jpegmode, "YCbCrK") == 0) {
+ } else if (context->jpegmode == IMAGING_RAWMODE_YCbCrK) {
context->cinfo.jpeg_color_space = JCS_YCCK;
}
- /* rawmode indicates what we want from the decoder. if not
- set, conversions are disabled */
- if (strcmp(context->rawmode, "L") == 0) {
+ /* rawmode indicates what we want from the decoder. */
+ /* If not valid, conversions are disabled. */
+ if (context->rawmode == IMAGING_RAWMODE_L) {
context->cinfo.out_color_space = JCS_GRAYSCALE;
- } else if (strcmp(context->rawmode, "RGB") == 0) {
+ } else if (context->rawmode == IMAGING_RAWMODE_RGB) {
context->cinfo.out_color_space = JCS_RGB;
}
#ifdef JCS_EXTENSIONS
- else if (strcmp(context->rawmode, "RGBX") == 0) {
+ else if (context->rawmode == IMAGING_RAWMODE_RGBX) {
context->cinfo.out_color_space = JCS_EXT_RGBX;
}
#endif
- else if (strcmp(context->rawmode, "CMYK") == 0 ||
- strcmp(context->rawmode, "CMYK;I") == 0) {
+ else if (context->rawmode == IMAGING_RAWMODE_CMYK ||
+ context->rawmode == IMAGING_RAWMODE_CMYK_I) {
context->cinfo.out_color_space = JCS_CMYK;
- } else if (strcmp(context->rawmode, "YCbCr") == 0) {
+ } else if (context->rawmode == IMAGING_RAWMODE_YCbCr) {
context->cinfo.out_color_space = JCS_YCbCr;
- } else if (strcmp(context->rawmode, "YCbCrK") == 0) {
+ } else if (context->rawmode == IMAGING_RAWMODE_YCbCrK) {
context->cinfo.out_color_space = JCS_YCCK;
} else {
- /* Disable decoder conversions */
+ /* Disable decoder conversions. */
context->cinfo.jpeg_color_space = JCS_UNKNOWN;
context->cinfo.out_color_space = JCS_UNKNOWN;
}
diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c
index 304daf0af..a05b3b580 100644
--- a/src/libImaging/JpegEncode.c
+++ b/src/libImaging/JpegEncode.c
@@ -114,7 +114,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
break;
case 24:
context->cinfo.input_components = 3;
- if (strcmp(im->mode, "YCbCr") == 0) {
+ if (im->mode == IMAGING_MODE_YCbCr) {
context->cinfo.in_color_space = JCS_YCbCr;
} else {
context->cinfo.in_color_space = JCS_RGB;
@@ -124,13 +124,14 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
context->cinfo.input_components = 4;
context->cinfo.in_color_space = JCS_CMYK;
#ifdef JCS_EXTENSIONS
- if (strcmp(context->rawmode, "RGBX") == 0) {
+ if (context->rawmode == IMAGING_RAWMODE_RGBX) {
context->cinfo.in_color_space = JCS_EXT_RGBX;
}
#endif
break;
default:
state->errcode = IMAGING_CODEC_CONFIG;
+ jpeg_destroy_compress(&context->cinfo);
return -1;
}
@@ -161,6 +162,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
/* Would subsample the green and blue
channels, which doesn't make sense */
state->errcode = IMAGING_CODEC_CONFIG;
+ jpeg_destroy_compress(&context->cinfo);
return -1;
}
jpeg_set_colorspace(&context->cinfo, JCS_RGB);
@@ -180,18 +182,21 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
/* Use custom quantization tables */
if (context->qtables) {
int i;
- int quality = 100;
+ int quality = 50;
int last_q = 0;
+ boolean force_baseline = FALSE;
if (context->quality != -1) {
quality = context->quality;
+ force_baseline = TRUE;
}
+ int scale_factor = jpeg_quality_scaling(quality);
for (i = 0; i < context->qtablesLen; i++) {
jpeg_add_quant_table(
&context->cinfo,
i,
&context->qtables[i * DCTSIZE2],
- quality,
- FALSE
+ scale_factor,
+ force_baseline
);
context->cinfo.comp_info[i].quant_tbl_no = i;
last_q = i;
@@ -200,7 +205,11 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
// jpeg_set_defaults created two qtables internally, but we only
// wanted one.
jpeg_add_quant_table(
- &context->cinfo, 1, &context->qtables[0], quality, FALSE
+ &context->cinfo,
+ 1,
+ &context->qtables[0],
+ scale_factor,
+ force_baseline
);
}
for (i = last_q; i < context->cinfo.num_components; i++) {
diff --git a/src/libImaging/Matrix.c b/src/libImaging/Matrix.c
index ec7f4d93e..d28e04edf 100644
--- a/src/libImaging/Matrix.c
+++ b/src/libImaging/Matrix.c
@@ -18,7 +18,7 @@
#define CLIPF(v) ((v <= 0.0) ? 0 : (v >= 255.0F) ? 255 : (UINT8)v)
Imaging
-ImagingConvertMatrix(Imaging im, const char *mode, float m[]) {
+ImagingConvertMatrix(Imaging im, const ModeID mode, float m[]) {
Imaging imOut;
int x, y;
ImagingSectionCookie cookie;
@@ -28,8 +28,8 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) {
return (Imaging)ImagingError_ModeError();
}
- if (strcmp(mode, "L") == 0) {
- imOut = ImagingNewDirty("L", im->xsize, im->ysize);
+ if (mode == IMAGING_MODE_L) {
+ imOut = ImagingNewDirty(IMAGING_MODE_L, im->xsize, im->ysize);
if (!imOut) {
return NULL;
}
@@ -46,8 +46,8 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) {
}
}
ImagingSectionLeave(&cookie);
-
- } else if (strlen(mode) == 3) {
+ } else if (mode == IMAGING_MODE_HSV || mode == IMAGING_MODE_LAB ||
+ mode == IMAGING_MODE_RGB) {
imOut = ImagingNewDirty(mode, im->xsize, im->ysize);
if (!imOut) {
return NULL;
diff --git a/src/libImaging/Mode.c b/src/libImaging/Mode.c
new file mode 100644
index 000000000..7521f4cda
--- /dev/null
+++ b/src/libImaging/Mode.c
@@ -0,0 +1,259 @@
+#include "Mode.h"
+#include
+
+#ifdef NDEBUG
+#include
+#include
+#endif
+
+const ModeData MODES[] = {
+ [IMAGING_MODE_UNKNOWN] = {""},
+
+ [IMAGING_MODE_1] = {"1"}, [IMAGING_MODE_CMYK] = {"CMYK"},
+ [IMAGING_MODE_F] = {"F"}, [IMAGING_MODE_HSV] = {"HSV"},
+ [IMAGING_MODE_I] = {"I"}, [IMAGING_MODE_L] = {"L"},
+ [IMAGING_MODE_LA] = {"LA"}, [IMAGING_MODE_LAB] = {"LAB"},
+ [IMAGING_MODE_La] = {"La"}, [IMAGING_MODE_P] = {"P"},
+ [IMAGING_MODE_PA] = {"PA"}, [IMAGING_MODE_RGB] = {"RGB"},
+ [IMAGING_MODE_RGBA] = {"RGBA"}, [IMAGING_MODE_RGBX] = {"RGBX"},
+ [IMAGING_MODE_RGBa] = {"RGBa"}, [IMAGING_MODE_YCbCr] = {"YCbCr"},
+
+ [IMAGING_MODE_I_16] = {"I;16"}, [IMAGING_MODE_I_16L] = {"I;16L"},
+ [IMAGING_MODE_I_16B] = {"I;16B"}, [IMAGING_MODE_I_16N] = {"I;16N"},
+ [IMAGING_MODE_I_32L] = {"I;32L"}, [IMAGING_MODE_I_32B] = {"I;32B"},
+};
+
+const ModeID
+findModeID(const char *const name) {
+ if (name == NULL) {
+ return IMAGING_MODE_UNKNOWN;
+ }
+ for (size_t i = 0; i < sizeof(MODES) / sizeof(*MODES); i++) {
+#ifdef NDEBUG
+ if (MODES[i].name == NULL) {
+ fprintf(stderr, "Mode ID %zu is not defined.\n", (size_t)i);
+ } else
+#endif
+ if (strcmp(MODES[i].name, name) == 0) {
+ return (ModeID)i;
+ }
+ }
+ return IMAGING_MODE_UNKNOWN;
+}
+
+const ModeData *const
+getModeData(const ModeID id) {
+ if (id < 0 || id > sizeof(MODES) / sizeof(*MODES)) {
+ return &MODES[IMAGING_MODE_UNKNOWN];
+ }
+ return &MODES[id];
+}
+
+const RawModeData RAWMODES[] = {
+ [IMAGING_RAWMODE_UNKNOWN] = {""},
+
+ [IMAGING_RAWMODE_1] = {"1"},
+ [IMAGING_RAWMODE_CMYK] = {"CMYK"},
+ [IMAGING_RAWMODE_F] = {"F"},
+ [IMAGING_RAWMODE_HSV] = {"HSV"},
+ [IMAGING_RAWMODE_I] = {"I"},
+ [IMAGING_RAWMODE_L] = {"L"},
+ [IMAGING_RAWMODE_LA] = {"LA"},
+ [IMAGING_RAWMODE_LAB] = {"LAB"},
+ [IMAGING_RAWMODE_La] = {"La"},
+ [IMAGING_RAWMODE_P] = {"P"},
+ [IMAGING_RAWMODE_PA] = {"PA"},
+ [IMAGING_RAWMODE_RGB] = {"RGB"},
+ [IMAGING_RAWMODE_RGBA] = {"RGBA"},
+ [IMAGING_RAWMODE_RGBX] = {"RGBX"},
+ [IMAGING_RAWMODE_RGBa] = {"RGBa"},
+ [IMAGING_RAWMODE_YCbCr] = {"YCbCr"},
+
+ [IMAGING_RAWMODE_BGR_15] = {"BGR;15"},
+ [IMAGING_RAWMODE_BGR_16] = {"BGR;16"},
+
+ [IMAGING_RAWMODE_I_16] = {"I;16"},
+ [IMAGING_RAWMODE_I_16L] = {"I;16L"},
+ [IMAGING_RAWMODE_I_16B] = {"I;16B"},
+ [IMAGING_RAWMODE_I_16N] = {"I;16N"},
+ [IMAGING_RAWMODE_I_32L] = {"I;32L"},
+ [IMAGING_RAWMODE_I_32B] = {"I;32B"},
+
+ [IMAGING_RAWMODE_1_8] = {"1;8"},
+ [IMAGING_RAWMODE_1_I] = {"1;I"},
+ [IMAGING_RAWMODE_1_IR] = {"1;IR"},
+ [IMAGING_RAWMODE_1_R] = {"1;R"},
+ [IMAGING_RAWMODE_A] = {"A"},
+ [IMAGING_RAWMODE_ABGR] = {"ABGR"},
+ [IMAGING_RAWMODE_ARGB] = {"ARGB"},
+ [IMAGING_RAWMODE_A_16B] = {"A;16B"},
+ [IMAGING_RAWMODE_A_16L] = {"A;16L"},
+ [IMAGING_RAWMODE_A_16N] = {"A;16N"},
+ [IMAGING_RAWMODE_B] = {"B"},
+ [IMAGING_RAWMODE_BGAR] = {"BGAR"},
+ [IMAGING_RAWMODE_BGR] = {"BGR"},
+ [IMAGING_RAWMODE_BGRA] = {"BGRA"},
+ [IMAGING_RAWMODE_BGRA_15] = {"BGRA;15"},
+ [IMAGING_RAWMODE_BGRA_15Z] = {"BGRA;15Z"},
+ [IMAGING_RAWMODE_BGRA_16B] = {"BGRA;16B"},
+ [IMAGING_RAWMODE_BGRA_16L] = {"BGRA;16L"},
+ [IMAGING_RAWMODE_BGRX] = {"BGRX"},
+ [IMAGING_RAWMODE_BGR_5] = {"BGR;5"},
+ [IMAGING_RAWMODE_BGRa] = {"BGRa"},
+ [IMAGING_RAWMODE_BGXR] = {"BGXR"},
+ [IMAGING_RAWMODE_B_16B] = {"B;16B"},
+ [IMAGING_RAWMODE_B_16L] = {"B;16L"},
+ [IMAGING_RAWMODE_B_16N] = {"B;16N"},
+ [IMAGING_RAWMODE_C] = {"C"},
+ [IMAGING_RAWMODE_CMYKX] = {"CMYKX"},
+ [IMAGING_RAWMODE_CMYKXX] = {"CMYKXX"},
+ [IMAGING_RAWMODE_CMYK_16B] = {"CMYK;16B"},
+ [IMAGING_RAWMODE_CMYK_16L] = {"CMYK;16L"},
+ [IMAGING_RAWMODE_CMYK_16N] = {"CMYK;16N"},
+ [IMAGING_RAWMODE_CMYK_I] = {"CMYK;I"},
+ [IMAGING_RAWMODE_CMYK_L] = {"CMYK;L"},
+ [IMAGING_RAWMODE_C_I] = {"C;I"},
+ [IMAGING_RAWMODE_Cb] = {"Cb"},
+ [IMAGING_RAWMODE_Cr] = {"Cr"},
+ [IMAGING_RAWMODE_F_16] = {"F;16"},
+ [IMAGING_RAWMODE_F_16B] = {"F;16B"},
+ [IMAGING_RAWMODE_F_16BS] = {"F;16BS"},
+ [IMAGING_RAWMODE_F_16N] = {"F;16N"},
+ [IMAGING_RAWMODE_F_16NS] = {"F;16NS"},
+ [IMAGING_RAWMODE_F_16S] = {"F;16S"},
+ [IMAGING_RAWMODE_F_32] = {"F;32"},
+ [IMAGING_RAWMODE_F_32B] = {"F;32B"},
+ [IMAGING_RAWMODE_F_32BF] = {"F;32BF"},
+ [IMAGING_RAWMODE_F_32BS] = {"F;32BS"},
+ [IMAGING_RAWMODE_F_32F] = {"F;32F"},
+ [IMAGING_RAWMODE_F_32N] = {"F;32N"},
+ [IMAGING_RAWMODE_F_32NF] = {"F;32NF"},
+ [IMAGING_RAWMODE_F_32NS] = {"F;32NS"},
+ [IMAGING_RAWMODE_F_32S] = {"F;32S"},
+ [IMAGING_RAWMODE_F_64BF] = {"F;64BF"},
+ [IMAGING_RAWMODE_F_64F] = {"F;64F"},
+ [IMAGING_RAWMODE_F_64NF] = {"F;64NF"},
+ [IMAGING_RAWMODE_F_8] = {"F;8"},
+ [IMAGING_RAWMODE_F_8S] = {"F;8S"},
+ [IMAGING_RAWMODE_G] = {"G"},
+ [IMAGING_RAWMODE_G_16B] = {"G;16B"},
+ [IMAGING_RAWMODE_G_16L] = {"G;16L"},
+ [IMAGING_RAWMODE_G_16N] = {"G;16N"},
+ [IMAGING_RAWMODE_H] = {"H"},
+ [IMAGING_RAWMODE_I_12] = {"I;12"},
+ [IMAGING_RAWMODE_I_16BS] = {"I;16BS"},
+ [IMAGING_RAWMODE_I_16NS] = {"I;16NS"},
+ [IMAGING_RAWMODE_I_16R] = {"I;16R"},
+ [IMAGING_RAWMODE_I_16S] = {"I;16S"},
+ [IMAGING_RAWMODE_I_32] = {"I;32"},
+ [IMAGING_RAWMODE_I_32BS] = {"I;32BS"},
+ [IMAGING_RAWMODE_I_32N] = {"I;32N"},
+ [IMAGING_RAWMODE_I_32NS] = {"I;32NS"},
+ [IMAGING_RAWMODE_I_32S] = {"I;32S"},
+ [IMAGING_RAWMODE_I_8] = {"I;8"},
+ [IMAGING_RAWMODE_I_8S] = {"I;8S"},
+ [IMAGING_RAWMODE_K] = {"K"},
+ [IMAGING_RAWMODE_K_I] = {"K;I"},
+ [IMAGING_RAWMODE_LA_16B] = {"LA;16B"},
+ [IMAGING_RAWMODE_LA_L] = {"LA;L"},
+ [IMAGING_RAWMODE_L_16] = {"L;16"},
+ [IMAGING_RAWMODE_L_16B] = {"L;16B"},
+ [IMAGING_RAWMODE_L_2] = {"L;2"},
+ [IMAGING_RAWMODE_L_2I] = {"L;2I"},
+ [IMAGING_RAWMODE_L_2IR] = {"L;2IR"},
+ [IMAGING_RAWMODE_L_2R] = {"L;2R"},
+ [IMAGING_RAWMODE_L_4] = {"L;4"},
+ [IMAGING_RAWMODE_L_4I] = {"L;4I"},
+ [IMAGING_RAWMODE_L_4IR] = {"L;4IR"},
+ [IMAGING_RAWMODE_L_4R] = {"L;4R"},
+ [IMAGING_RAWMODE_L_I] = {"L;I"},
+ [IMAGING_RAWMODE_L_R] = {"L;R"},
+ [IMAGING_RAWMODE_M] = {"M"},
+ [IMAGING_RAWMODE_M_I] = {"M;I"},
+ [IMAGING_RAWMODE_PA_L] = {"PA;L"},
+ [IMAGING_RAWMODE_PX] = {"PX"},
+ [IMAGING_RAWMODE_P_1] = {"P;1"},
+ [IMAGING_RAWMODE_P_2] = {"P;2"},
+ [IMAGING_RAWMODE_P_2L] = {"P;2L"},
+ [IMAGING_RAWMODE_P_4] = {"P;4"},
+ [IMAGING_RAWMODE_P_4L] = {"P;4L"},
+ [IMAGING_RAWMODE_P_R] = {"P;R"},
+ [IMAGING_RAWMODE_R] = {"R"},
+ [IMAGING_RAWMODE_RGBAX] = {"RGBAX"},
+ [IMAGING_RAWMODE_RGBAXX] = {"RGBAXX"},
+ [IMAGING_RAWMODE_RGBA_15] = {"RGBA;15"},
+ [IMAGING_RAWMODE_RGBA_16B] = {"RGBA;16B"},
+ [IMAGING_RAWMODE_RGBA_16L] = {"RGBA;16L"},
+ [IMAGING_RAWMODE_RGBA_16N] = {"RGBA;16N"},
+ [IMAGING_RAWMODE_RGBA_4B] = {"RGBA;4B"},
+ [IMAGING_RAWMODE_RGBA_I] = {"RGBA;I"},
+ [IMAGING_RAWMODE_RGBA_L] = {"RGBA;L"},
+ [IMAGING_RAWMODE_RGBXX] = {"RGBXX"},
+ [IMAGING_RAWMODE_RGBXXX] = {"RGBXXX"},
+ [IMAGING_RAWMODE_RGBX_16B] = {"RGBX;16B"},
+ [IMAGING_RAWMODE_RGBX_16L] = {"RGBX;16L"},
+ [IMAGING_RAWMODE_RGBX_16N] = {"RGBX;16N"},
+ [IMAGING_RAWMODE_RGBX_L] = {"RGBX;L"},
+ [IMAGING_RAWMODE_RGB_15] = {"RGB;15"},
+ [IMAGING_RAWMODE_RGB_16] = {"RGB;16"},
+ [IMAGING_RAWMODE_RGB_16B] = {"RGB;16B"},
+ [IMAGING_RAWMODE_RGB_16L] = {"RGB;16L"},
+ [IMAGING_RAWMODE_RGB_16N] = {"RGB;16N"},
+ [IMAGING_RAWMODE_RGB_4B] = {"RGB;4B"},
+ [IMAGING_RAWMODE_RGB_L] = {"RGB;L"},
+ [IMAGING_RAWMODE_RGB_R] = {"RGB;R"},
+ [IMAGING_RAWMODE_RGBaX] = {"RGBaX"},
+ [IMAGING_RAWMODE_RGBaXX] = {"RGBaXX"},
+ [IMAGING_RAWMODE_RGBa_16B] = {"RGBa;16B"},
+ [IMAGING_RAWMODE_RGBa_16L] = {"RGBa;16L"},
+ [IMAGING_RAWMODE_RGBa_16N] = {"RGBa;16N"},
+ [IMAGING_RAWMODE_R_16B] = {"R;16B"},
+ [IMAGING_RAWMODE_R_16L] = {"R;16L"},
+ [IMAGING_RAWMODE_R_16N] = {"R;16N"},
+ [IMAGING_RAWMODE_S] = {"S"},
+ [IMAGING_RAWMODE_V] = {"V"},
+ [IMAGING_RAWMODE_X] = {"X"},
+ [IMAGING_RAWMODE_XBGR] = {"XBGR"},
+ [IMAGING_RAWMODE_XRGB] = {"XRGB"},
+ [IMAGING_RAWMODE_Y] = {"Y"},
+ [IMAGING_RAWMODE_YCCA_P] = {"YCCA;P"},
+ [IMAGING_RAWMODE_YCC_P] = {"YCC;P"},
+ [IMAGING_RAWMODE_YCbCrK] = {"YCbCrK"},
+ [IMAGING_RAWMODE_YCbCrX] = {"YCbCrX"},
+ [IMAGING_RAWMODE_YCbCr_L] = {"YCbCr;L"},
+ [IMAGING_RAWMODE_Y_I] = {"Y;I"},
+ [IMAGING_RAWMODE_aBGR] = {"aBGR"},
+ [IMAGING_RAWMODE_aRGB] = {"aRGB"},
+};
+
+const RawModeID
+findRawModeID(const char *const name) {
+ if (name == NULL) {
+ return IMAGING_RAWMODE_UNKNOWN;
+ }
+ for (size_t i = 0; i < sizeof(RAWMODES) / sizeof(*RAWMODES); i++) {
+#ifdef NDEBUG
+ if (RAWMODES[i].name == NULL) {
+ fprintf(stderr, "Rawmode ID %zu is not defined.\n", (size_t)i);
+ } else
+#endif
+ if (strcmp(RAWMODES[i].name, name) == 0) {
+ return (RawModeID)i;
+ }
+ }
+ return IMAGING_RAWMODE_UNKNOWN;
+}
+
+const RawModeData *const
+getRawModeData(const RawModeID id) {
+ if (id < 0 || id > sizeof(RAWMODES) / sizeof(*RAWMODES)) {
+ return &RAWMODES[IMAGING_RAWMODE_UNKNOWN];
+ }
+ return &RAWMODES[id];
+}
+
+int
+isModeI16(const ModeID mode) {
+ return mode == IMAGING_MODE_I_16 || mode == IMAGING_MODE_I_16L ||
+ mode == IMAGING_MODE_I_16B || mode == IMAGING_MODE_I_16N;
+}
diff --git a/src/libImaging/Mode.h b/src/libImaging/Mode.h
new file mode 100644
index 000000000..a3eb3d86d
--- /dev/null
+++ b/src/libImaging/Mode.h
@@ -0,0 +1,232 @@
+#ifndef __MODE_H__
+#define __MODE_H__
+
+typedef enum {
+ IMAGING_MODE_UNKNOWN,
+
+ IMAGING_MODE_1,
+ IMAGING_MODE_CMYK,
+ IMAGING_MODE_F,
+ IMAGING_MODE_HSV,
+ IMAGING_MODE_I,
+ IMAGING_MODE_L,
+ IMAGING_MODE_LA,
+ IMAGING_MODE_LAB,
+ IMAGING_MODE_La,
+ IMAGING_MODE_P,
+ IMAGING_MODE_PA,
+ IMAGING_MODE_RGB,
+ IMAGING_MODE_RGBA,
+ IMAGING_MODE_RGBX,
+ IMAGING_MODE_RGBa,
+ IMAGING_MODE_YCbCr,
+
+ IMAGING_MODE_I_16,
+ IMAGING_MODE_I_16L,
+ IMAGING_MODE_I_16B,
+ IMAGING_MODE_I_16N,
+ IMAGING_MODE_I_32L,
+ IMAGING_MODE_I_32B,
+} ModeID;
+
+typedef struct {
+ const char *const name;
+} ModeData;
+
+const ModeID
+findModeID(const char *const name);
+const ModeData *const
+getModeData(const ModeID id);
+
+typedef enum {
+ IMAGING_RAWMODE_UNKNOWN,
+
+ // Non-rawmode aliases.
+ IMAGING_RAWMODE_1,
+ IMAGING_RAWMODE_CMYK,
+ IMAGING_RAWMODE_F,
+ IMAGING_RAWMODE_HSV,
+ IMAGING_RAWMODE_I,
+ IMAGING_RAWMODE_L,
+ IMAGING_RAWMODE_LA,
+ IMAGING_RAWMODE_LAB,
+ IMAGING_RAWMODE_La,
+ IMAGING_RAWMODE_P,
+ IMAGING_RAWMODE_PA,
+ IMAGING_RAWMODE_RGB,
+ IMAGING_RAWMODE_RGBA,
+ IMAGING_RAWMODE_RGBX,
+ IMAGING_RAWMODE_RGBa,
+ IMAGING_RAWMODE_YCbCr,
+
+ // I;* modes.
+ IMAGING_RAWMODE_I_16,
+ IMAGING_RAWMODE_I_16L,
+ IMAGING_RAWMODE_I_16B,
+ IMAGING_RAWMODE_I_16N,
+ IMAGING_RAWMODE_I_32L,
+ IMAGING_RAWMODE_I_32B,
+
+ // Rawmodes
+ IMAGING_RAWMODE_1_8,
+ IMAGING_RAWMODE_1_I,
+ IMAGING_RAWMODE_1_IR,
+ IMAGING_RAWMODE_1_R,
+ IMAGING_RAWMODE_A,
+ IMAGING_RAWMODE_ABGR,
+ IMAGING_RAWMODE_ARGB,
+ IMAGING_RAWMODE_A_16B,
+ IMAGING_RAWMODE_A_16L,
+ IMAGING_RAWMODE_A_16N,
+ IMAGING_RAWMODE_B,
+ IMAGING_RAWMODE_BGAR,
+ IMAGING_RAWMODE_BGR,
+ IMAGING_RAWMODE_BGRA,
+ IMAGING_RAWMODE_BGRA_15,
+ IMAGING_RAWMODE_BGRA_15Z,
+ IMAGING_RAWMODE_BGRA_16B,
+ IMAGING_RAWMODE_BGRA_16L,
+ IMAGING_RAWMODE_BGRX,
+ IMAGING_RAWMODE_BGR_5,
+ IMAGING_RAWMODE_BGR_15,
+ IMAGING_RAWMODE_BGR_16,
+ IMAGING_RAWMODE_BGRa,
+ IMAGING_RAWMODE_BGXR,
+ IMAGING_RAWMODE_B_16B,
+ IMAGING_RAWMODE_B_16L,
+ IMAGING_RAWMODE_B_16N,
+ IMAGING_RAWMODE_C,
+ IMAGING_RAWMODE_CMYKX,
+ IMAGING_RAWMODE_CMYKXX,
+ IMAGING_RAWMODE_CMYK_16B,
+ IMAGING_RAWMODE_CMYK_16L,
+ IMAGING_RAWMODE_CMYK_16N,
+ IMAGING_RAWMODE_CMYK_I,
+ IMAGING_RAWMODE_CMYK_L,
+ IMAGING_RAWMODE_C_I,
+ IMAGING_RAWMODE_Cb,
+ IMAGING_RAWMODE_Cr,
+ IMAGING_RAWMODE_F_16,
+ IMAGING_RAWMODE_F_16B,
+ IMAGING_RAWMODE_F_16BS,
+ IMAGING_RAWMODE_F_16N,
+ IMAGING_RAWMODE_F_16NS,
+ IMAGING_RAWMODE_F_16S,
+ IMAGING_RAWMODE_F_32,
+ IMAGING_RAWMODE_F_32B,
+ IMAGING_RAWMODE_F_32BF,
+ IMAGING_RAWMODE_F_32BS,
+ IMAGING_RAWMODE_F_32F,
+ IMAGING_RAWMODE_F_32N,
+ IMAGING_RAWMODE_F_32NF,
+ IMAGING_RAWMODE_F_32NS,
+ IMAGING_RAWMODE_F_32S,
+ IMAGING_RAWMODE_F_64BF,
+ IMAGING_RAWMODE_F_64F,
+ IMAGING_RAWMODE_F_64NF,
+ IMAGING_RAWMODE_F_8,
+ IMAGING_RAWMODE_F_8S,
+ IMAGING_RAWMODE_G,
+ IMAGING_RAWMODE_G_16B,
+ IMAGING_RAWMODE_G_16L,
+ IMAGING_RAWMODE_G_16N,
+ IMAGING_RAWMODE_H,
+ IMAGING_RAWMODE_I_12,
+ IMAGING_RAWMODE_I_16BS,
+ IMAGING_RAWMODE_I_16NS,
+ IMAGING_RAWMODE_I_16R,
+ IMAGING_RAWMODE_I_16S,
+ IMAGING_RAWMODE_I_32,
+ IMAGING_RAWMODE_I_32BS,
+ IMAGING_RAWMODE_I_32N,
+ IMAGING_RAWMODE_I_32NS,
+ IMAGING_RAWMODE_I_32S,
+ IMAGING_RAWMODE_I_8,
+ IMAGING_RAWMODE_I_8S,
+ IMAGING_RAWMODE_K,
+ IMAGING_RAWMODE_K_I,
+ IMAGING_RAWMODE_LA_16B,
+ IMAGING_RAWMODE_LA_L,
+ IMAGING_RAWMODE_L_16,
+ IMAGING_RAWMODE_L_16B,
+ IMAGING_RAWMODE_L_2,
+ IMAGING_RAWMODE_L_2I,
+ IMAGING_RAWMODE_L_2IR,
+ IMAGING_RAWMODE_L_2R,
+ IMAGING_RAWMODE_L_4,
+ IMAGING_RAWMODE_L_4I,
+ IMAGING_RAWMODE_L_4IR,
+ IMAGING_RAWMODE_L_4R,
+ IMAGING_RAWMODE_L_I,
+ IMAGING_RAWMODE_L_R,
+ IMAGING_RAWMODE_M,
+ IMAGING_RAWMODE_M_I,
+ IMAGING_RAWMODE_PA_L,
+ IMAGING_RAWMODE_PX,
+ IMAGING_RAWMODE_P_1,
+ IMAGING_RAWMODE_P_2,
+ IMAGING_RAWMODE_P_2L,
+ IMAGING_RAWMODE_P_4,
+ IMAGING_RAWMODE_P_4L,
+ IMAGING_RAWMODE_P_R,
+ IMAGING_RAWMODE_R,
+ IMAGING_RAWMODE_RGBAX,
+ IMAGING_RAWMODE_RGBAXX,
+ IMAGING_RAWMODE_RGBA_15,
+ IMAGING_RAWMODE_RGBA_16B,
+ IMAGING_RAWMODE_RGBA_16L,
+ IMAGING_RAWMODE_RGBA_16N,
+ IMAGING_RAWMODE_RGBA_4B,
+ IMAGING_RAWMODE_RGBA_I,
+ IMAGING_RAWMODE_RGBA_L,
+ IMAGING_RAWMODE_RGBXX,
+ IMAGING_RAWMODE_RGBXXX,
+ IMAGING_RAWMODE_RGBX_16B,
+ IMAGING_RAWMODE_RGBX_16L,
+ IMAGING_RAWMODE_RGBX_16N,
+ IMAGING_RAWMODE_RGBX_L,
+ IMAGING_RAWMODE_RGB_15,
+ IMAGING_RAWMODE_RGB_16,
+ IMAGING_RAWMODE_RGB_16B,
+ IMAGING_RAWMODE_RGB_16L,
+ IMAGING_RAWMODE_RGB_16N,
+ IMAGING_RAWMODE_RGB_4B,
+ IMAGING_RAWMODE_RGB_L,
+ IMAGING_RAWMODE_RGB_R,
+ IMAGING_RAWMODE_RGBaX,
+ IMAGING_RAWMODE_RGBaXX,
+ IMAGING_RAWMODE_RGBa_16B,
+ IMAGING_RAWMODE_RGBa_16L,
+ IMAGING_RAWMODE_RGBa_16N,
+ IMAGING_RAWMODE_R_16B,
+ IMAGING_RAWMODE_R_16L,
+ IMAGING_RAWMODE_R_16N,
+ IMAGING_RAWMODE_S,
+ IMAGING_RAWMODE_V,
+ IMAGING_RAWMODE_X,
+ IMAGING_RAWMODE_XBGR,
+ IMAGING_RAWMODE_XRGB,
+ IMAGING_RAWMODE_Y,
+ IMAGING_RAWMODE_YCCA_P,
+ IMAGING_RAWMODE_YCC_P,
+ IMAGING_RAWMODE_YCbCrK,
+ IMAGING_RAWMODE_YCbCrX,
+ IMAGING_RAWMODE_YCbCr_L,
+ IMAGING_RAWMODE_Y_I,
+ IMAGING_RAWMODE_aBGR,
+ IMAGING_RAWMODE_aRGB,
+} RawModeID;
+
+typedef struct {
+ const char *const name;
+} RawModeData;
+
+const RawModeID
+findRawModeID(const char *const name);
+const RawModeData *const
+getRawModeData(const RawModeID id);
+
+int
+isModeI16(const ModeID mode);
+
+#endif // __MODE_H__
diff --git a/src/libImaging/Pack.c b/src/libImaging/Pack.c
index c29473d90..fdf5a72aa 100644
--- a/src/libImaging/Pack.c
+++ b/src/libImaging/Pack.c
@@ -471,12 +471,6 @@ copy2(UINT8 *out, const UINT8 *in, int pixels) {
memcpy(out, in, pixels * 2);
}
-static void
-copy3(UINT8 *out, const UINT8 *in, int pixels) {
- /* BGR;24, etc */
- memcpy(out, in, pixels * 3);
-}
-
static void
copy4(UINT8 *out, const UINT8 *in, int pixels) {
/* RGBA, CMYK quadruples */
@@ -525,153 +519,145 @@ band3(UINT8 *out, const UINT8 *in, int pixels) {
}
static struct {
- const char *mode;
- const char *rawmode;
+ const ModeID mode;
+ const RawModeID rawmode;
int bits;
ImagingShuffler pack;
} packers[] = {
/* bilevel */
- {"1", "1", 1, pack1},
- {"1", "1;I", 1, pack1I},
- {"1", "1;R", 1, pack1R},
- {"1", "1;IR", 1, pack1IR},
- {"1", "L", 8, pack1L},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_1, 1, pack1},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_1_I, 1, pack1I},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_1_R, 1, pack1R},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_1_IR, 1, pack1IR},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_L, 8, pack1L},
/* grayscale */
- {"L", "L", 8, copy1},
- {"L", "L;16", 16, packL16},
- {"L", "L;16B", 16, packL16B},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L, 8, copy1},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_16, 16, packL16},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_16B, 16, packL16B},
/* grayscale w. alpha */
- {"LA", "LA", 16, packLA},
- {"LA", "LA;L", 16, packLAL},
+ {IMAGING_MODE_LA, IMAGING_RAWMODE_LA, 16, packLA},
+ {IMAGING_MODE_LA, IMAGING_RAWMODE_LA_L, 16, packLAL},
/* grayscale w. alpha premultiplied */
- {"La", "La", 16, packLA},
+ {IMAGING_MODE_La, IMAGING_RAWMODE_La, 16, packLA},
/* palette */
- {"P", "P;1", 1, pack1},
- {"P", "P;2", 2, packP2},
- {"P", "P;4", 4, packP4},
- {"P", "P", 8, copy1},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P_1, 1, pack1},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P_2, 2, packP2},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P_4, 4, packP4},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P, 8, copy1},
/* palette w. alpha */
- {"PA", "PA", 16, packLA},
- {"PA", "PA;L", 16, packLAL},
+ {IMAGING_MODE_PA, IMAGING_RAWMODE_PA, 16, packLA},
+ {IMAGING_MODE_PA, IMAGING_RAWMODE_PA_L, 16, packLAL},
/* true colour */
- {"RGB", "RGB", 24, ImagingPackRGB},
- {"RGB", "RGBX", 32, copy4},
- {"RGB", "RGBA", 32, copy4},
- {"RGB", "XRGB", 32, ImagingPackXRGB},
- {"RGB", "BGR", 24, ImagingPackBGR},
- {"RGB", "BGRX", 32, ImagingPackBGRX},
- {"RGB", "XBGR", 32, ImagingPackXBGR},
- {"RGB", "RGB;L", 24, packRGBL},
- {"RGB", "R", 8, band0},
- {"RGB", "G", 8, band1},
- {"RGB", "B", 8, band2},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB, 24, ImagingPackRGB},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBX, 32, copy4},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBA, 32, copy4},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_XRGB, 32, ImagingPackXRGB},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_BGR, 24, ImagingPackBGR},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_BGRX, 32, ImagingPackBGRX},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_XBGR, 32, ImagingPackXBGR},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_L, 24, packRGBL},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_R, 8, band0},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_G, 8, band1},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_B, 8, band2},
/* true colour w. alpha */
- {"RGBA", "RGBA", 32, copy4},
- {"RGBA", "RGBA;L", 32, packRGBXL},
- {"RGBA", "RGB", 24, ImagingPackRGB},
- {"RGBA", "BGR", 24, ImagingPackBGR},
- {"RGBA", "BGRA", 32, ImagingPackBGRA},
- {"RGBA", "ABGR", 32, ImagingPackABGR},
- {"RGBA", "BGRa", 32, ImagingPackBGRa},
- {"RGBA", "R", 8, band0},
- {"RGBA", "G", 8, band1},
- {"RGBA", "B", 8, band2},
- {"RGBA", "A", 8, band3},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA, 32, copy4},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA_L, 32, packRGBXL},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGB, 24, ImagingPackRGB},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGR, 24, ImagingPackBGR},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGRA, 32, ImagingPackBGRA},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_ABGR, 32, ImagingPackABGR},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGRa, 32, ImagingPackBGRa},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_R, 8, band0},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_G, 8, band1},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_B, 8, band2},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_A, 8, band3},
/* true colour w. alpha premultiplied */
- {"RGBa", "RGBa", 32, copy4},
- {"RGBa", "BGRa", 32, ImagingPackBGRA},
- {"RGBa", "aBGR", 32, ImagingPackABGR},
+ {IMAGING_MODE_RGBa, IMAGING_RAWMODE_RGBa, 32, copy4},
+ {IMAGING_MODE_RGBa, IMAGING_RAWMODE_BGRa, 32, ImagingPackBGRA},
+ {IMAGING_MODE_RGBa, IMAGING_RAWMODE_aBGR, 32, ImagingPackABGR},
/* true colour w. padding */
- {"RGBX", "RGBX", 32, copy4},
- {"RGBX", "RGBX;L", 32, packRGBXL},
- {"RGBX", "RGB", 24, ImagingPackRGB},
- {"RGBX", "BGR", 24, ImagingPackBGR},
- {"RGBX", "BGRX", 32, ImagingPackBGRX},
- {"RGBX", "XBGR", 32, ImagingPackXBGR},
- {"RGBX", "R", 8, band0},
- {"RGBX", "G", 8, band1},
- {"RGBX", "B", 8, band2},
- {"RGBX", "X", 8, band3},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBX, 32, copy4},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBX_L, 32, packRGBXL},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGB, 24, ImagingPackRGB},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_BGR, 24, ImagingPackBGR},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_BGRX, 32, ImagingPackBGRX},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_XBGR, 32, ImagingPackXBGR},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_R, 8, band0},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_G, 8, band1},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_B, 8, band2},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_X, 8, band3},
/* colour separation */
- {"CMYK", "CMYK", 32, copy4},
- {"CMYK", "CMYK;I", 32, copy4I},
- {"CMYK", "CMYK;L", 32, packRGBXL},
- {"CMYK", "C", 8, band0},
- {"CMYK", "M", 8, band1},
- {"CMYK", "Y", 8, band2},
- {"CMYK", "K", 8, band3},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK, 32, copy4},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK_I, 32, copy4I},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK_L, 32, packRGBXL},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_C, 8, band0},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_M, 8, band1},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_Y, 8, band2},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_K, 8, band3},
/* video (YCbCr) */
- {"YCbCr", "YCbCr", 24, ImagingPackRGB},
- {"YCbCr", "YCbCr;L", 24, packRGBL},
- {"YCbCr", "YCbCrX", 32, copy4},
- {"YCbCr", "YCbCrK", 32, copy4},
- {"YCbCr", "Y", 8, band0},
- {"YCbCr", "Cb", 8, band1},
- {"YCbCr", "Cr", 8, band2},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_YCbCr, 24, ImagingPackRGB},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_YCbCr_L, 24, packRGBL},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_YCbCrX, 32, copy4},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_YCbCrK, 32, copy4},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_Y, 8, band0},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_Cb, 8, band1},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_Cr, 8, band2},
/* LAB Color */
- {"LAB", "LAB", 24, ImagingPackLAB},
- {"LAB", "L", 8, band0},
- {"LAB", "A", 8, band1},
- {"LAB", "B", 8, band2},
+ {IMAGING_MODE_LAB, IMAGING_RAWMODE_LAB, 24, ImagingPackLAB},
+ {IMAGING_MODE_LAB, IMAGING_RAWMODE_L, 8, band0},
+ {IMAGING_MODE_LAB, IMAGING_RAWMODE_A, 8, band1},
+ {IMAGING_MODE_LAB, IMAGING_RAWMODE_B, 8, band2},
/* HSV */
- {"HSV", "HSV", 24, ImagingPackRGB},
- {"HSV", "H", 8, band0},
- {"HSV", "S", 8, band1},
- {"HSV", "V", 8, band2},
+ {IMAGING_MODE_HSV, IMAGING_RAWMODE_HSV, 24, ImagingPackRGB},
+ {IMAGING_MODE_HSV, IMAGING_RAWMODE_H, 8, band0},
+ {IMAGING_MODE_HSV, IMAGING_RAWMODE_S, 8, band1},
+ {IMAGING_MODE_HSV, IMAGING_RAWMODE_V, 8, band2},
/* integer */
- {"I", "I", 32, copy4},
- {"I", "I;16B", 16, packI16B},
- {"I", "I;32S", 32, packI32S},
- {"I", "I;32NS", 32, copy4},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I, 32, copy4},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_16B, 16, packI16B},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_32S, 32, packI32S},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_32NS, 32, copy4},
/* floating point */
- {"F", "F", 32, copy4},
- {"F", "F;32F", 32, packI32S},
- {"F", "F;32NF", 32, copy4},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F, 32, copy4},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32F, 32, packI32S},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32NF, 32, copy4},
/* storage modes */
- {"I;16", "I;16", 16, copy2},
+ {IMAGING_MODE_I_16, IMAGING_RAWMODE_I_16, 16, copy2},
#ifdef WORDS_BIGENDIAN
- {"I;16", "I;16B", 16, packI16N_I16},
+ {IMAGING_MODE_I_16, IMAGING_RAWMODE_I_16B, 16, packI16N_I16},
#else
- {"I;16", "I;16B", 16, packI16N_I16B},
+ {IMAGING_MODE_I_16, IMAGING_RAWMODE_I_16B, 16, packI16N_I16B},
#endif
- {"I;16B", "I;16B", 16, copy2},
- {"I;16L", "I;16L", 16, copy2},
- {"I;16N", "I;16N", 16, copy2},
- {"I;16", "I;16N", 16, packI16N_I16}, // LibTiff native->image endian.
- {"I;16L", "I;16N", 16, packI16N_I16},
- {"I;16B", "I;16N", 16, packI16N_I16B},
- {"BGR;15", "BGR;15", 16, copy2},
- {"BGR;16", "BGR;16", 16, copy2},
- {"BGR;24", "BGR;24", 24, copy3},
-
- {NULL} /* sentinel */
+ {IMAGING_MODE_I_16B, IMAGING_RAWMODE_I_16B, 16, copy2},
+ {IMAGING_MODE_I_16L, IMAGING_RAWMODE_I_16L, 16, copy2},
+ {IMAGING_MODE_I_16N, IMAGING_RAWMODE_I_16N, 16, copy2},
+ // LibTiff native->image endian.
+ {IMAGING_MODE_I_16, IMAGING_RAWMODE_I_16N, 16, packI16N_I16},
+ {IMAGING_MODE_I_16L, IMAGING_RAWMODE_I_16N, 16, packI16N_I16},
+ {IMAGING_MODE_I_16B, IMAGING_RAWMODE_I_16N, 16, packI16N_I16B}
};
ImagingShuffler
-ImagingFindPacker(const char *mode, const char *rawmode, int *bits_out) {
- int i;
-
- /* find a suitable pixel packer */
- for (i = 0; packers[i].rawmode; i++) {
- if (strcmp(packers[i].mode, mode) == 0 &&
- strcmp(packers[i].rawmode, rawmode) == 0) {
+ImagingFindPacker(const ModeID mode, const RawModeID rawmode, int *bits_out) {
+ for (size_t i = 0; i < sizeof(packers) / sizeof(*packers); i++) {
+ if (packers[i].mode == mode && packers[i].rawmode == rawmode) {
if (bits_out) {
*bits_out = packers[i].bits;
}
diff --git a/src/libImaging/Palette.c b/src/libImaging/Palette.c
index 78916bca5..371ba644b 100644
--- a/src/libImaging/Palette.c
+++ b/src/libImaging/Palette.c
@@ -21,13 +21,13 @@
#include
ImagingPalette
-ImagingPaletteNew(const char *mode) {
+ImagingPaletteNew(const ModeID mode) {
/* Create a palette object */
int i;
ImagingPalette palette;
- if (strcmp(mode, "RGB") && strcmp(mode, "RGBA")) {
+ if (mode != IMAGING_MODE_RGB && mode != IMAGING_MODE_RGBA) {
return (ImagingPalette)ImagingError_ModeError();
}
@@ -36,8 +36,7 @@ ImagingPaletteNew(const char *mode) {
return (ImagingPalette)ImagingError_MemoryError();
}
- strncpy(palette->mode, mode, IMAGING_MODE_LENGTH - 1);
- palette->mode[IMAGING_MODE_LENGTH - 1] = 0;
+ palette->mode = mode;
palette->size = 0;
for (i = 0; i < 256; i++) {
@@ -54,7 +53,7 @@ ImagingPaletteNewBrowser(void) {
int i, r, g, b;
ImagingPalette palette;
- palette = ImagingPaletteNew("RGB");
+ palette = ImagingPaletteNew(IMAGING_MODE_RGB);
if (!palette) {
return NULL;
}
@@ -148,7 +147,7 @@ ImagingPaletteDelete(ImagingPalette palette) {
#define BOX 8
-#define BOXVOLUME BOX *BOX *BOX
+#define BOXVOLUME BOX * BOX * BOX
void
ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) {
diff --git a/src/libImaging/Paste.c b/src/libImaging/Paste.c
index 86085942a..25941ab3d 100644
--- a/src/libImaging/Paste.c
+++ b/src/libImaging/Paste.c
@@ -23,6 +23,18 @@
#include "Imaging.h"
+#define PREPARE_PASTE_LOOP() \
+ int y, y_end, offset; \
+ if (imOut == imIn && dy > sy) { \
+ y = ysize - 1; \
+ y_end = -1; \
+ offset = -1; \
+ } else { \
+ y = 0; \
+ y_end = ysize; \
+ offset = 1; \
+ }
+
static inline void
paste(
Imaging imOut,
@@ -37,14 +49,13 @@ paste(
) {
/* paste opaque region */
- int y;
-
dx *= pixelsize;
sx *= pixelsize;
xsize *= pixelsize;
- for (y = 0; y < ysize; y++) {
+ PREPARE_PASTE_LOOP();
+ for (; y != y_end; y += offset) {
memcpy(imOut->image[y + dy] + dx, imIn->image[y + sy] + sx, xsize);
}
}
@@ -64,12 +75,13 @@ paste_mask_1(
) {
/* paste with mode "1" mask */
- int x, y;
+ int x;
+ PREPARE_PASTE_LOOP();
if (imOut->image8) {
- int in_i16 = strncmp(imIn->mode, "I;16", 4) == 0;
- int out_i16 = strncmp(imOut->mode, "I;16", 4) == 0;
- for (y = 0; y < ysize; y++) {
+ int in_i16 = isModeI16(imIn->mode);
+ int out_i16 = isModeI16(imOut->mode);
+ for (; y != y_end; y += offset) {
UINT8 *out = imOut->image8[y + dy] + dx;
if (out_i16) {
out += dx;
@@ -97,7 +109,7 @@ paste_mask_1(
}
} else {
- for (y = 0; y < ysize; y++) {
+ for (; y != y_end; y += offset) {
INT32 *out = imOut->image32[y + dy] + dx;
INT32 *in = imIn->image32[y + sy] + sx;
UINT8 *mask = imMask->image8[y + sy] + sx;
@@ -126,11 +138,12 @@ paste_mask_L(
) {
/* paste with mode "L" matte */
- int x, y;
+ int x;
unsigned int tmp1;
+ PREPARE_PASTE_LOOP();
if (imOut->image8) {
- for (y = 0; y < ysize; y++) {
+ for (; y != y_end; y += offset) {
UINT8 *out = imOut->image8[y + dy] + dx;
UINT8 *in = imIn->image8[y + sy] + sx;
UINT8 *mask = imMask->image8[y + sy] + sx;
@@ -141,7 +154,7 @@ paste_mask_L(
}
} else {
- for (y = 0; y < ysize; y++) {
+ for (; y != y_end; y += offset) {
UINT8 *out = (UINT8 *)(imOut->image32[y + dy] + dx);
UINT8 *in = (UINT8 *)(imIn->image32[y + sy] + sx);
UINT8 *mask = (UINT8 *)(imMask->image8[y + sy] + sx);
@@ -174,11 +187,12 @@ paste_mask_RGBA(
) {
/* paste with mode "RGBA" matte */
- int x, y;
+ int x;
unsigned int tmp1;
+ PREPARE_PASTE_LOOP();
if (imOut->image8) {
- for (y = 0; y < ysize; y++) {
+ for (; y != y_end; y += offset) {
UINT8 *out = imOut->image8[y + dy] + dx;
UINT8 *in = imIn->image8[y + sy] + sx;
UINT8 *mask = (UINT8 *)imMask->image[y + sy] + sx * 4 + 3;
@@ -189,7 +203,7 @@ paste_mask_RGBA(
}
} else {
- for (y = 0; y < ysize; y++) {
+ for (; y != y_end; y += offset) {
UINT8 *out = (UINT8 *)(imOut->image32[y + dy] + dx);
UINT8 *in = (UINT8 *)(imIn->image32[y + sy] + sx);
UINT8 *mask = (UINT8 *)(imMask->image32[y + sy] + sx);
@@ -222,11 +236,12 @@ paste_mask_RGBa(
) {
/* paste with mode "RGBa" matte */
- int x, y;
+ int x;
unsigned int tmp1;
+ PREPARE_PASTE_LOOP();
if (imOut->image8) {
- for (y = 0; y < ysize; y++) {
+ for (; y != y_end; y += offset) {
UINT8 *out = imOut->image8[y + dy] + dx;
UINT8 *in = imIn->image8[y + sy] + sx;
UINT8 *mask = (UINT8 *)imMask->image[y + sy] + sx * 4 + 3;
@@ -237,7 +252,7 @@ paste_mask_RGBa(
}
} else {
- for (y = 0; y < ysize; y++) {
+ for (; y != y_end; y += offset) {
UINT8 *out = (UINT8 *)(imOut->image32[y + dy] + dx);
UINT8 *in = (UINT8 *)(imIn->image32[y + sy] + sx);
UINT8 *mask = (UINT8 *)(imMask->image32[y + sy] + sx);
@@ -307,31 +322,26 @@ ImagingPaste(
ImagingSectionEnter(&cookie);
paste(imOut, imIn, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize);
ImagingSectionLeave(&cookie);
-
- } else if (strcmp(imMask->mode, "1") == 0) {
+ } else if (imMask->mode == IMAGING_MODE_1) {
ImagingSectionEnter(&cookie);
paste_mask_1(imOut, imIn, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize);
ImagingSectionLeave(&cookie);
-
- } else if (strcmp(imMask->mode, "L") == 0) {
+ } else if (imMask->mode == IMAGING_MODE_L) {
ImagingSectionEnter(&cookie);
paste_mask_L(imOut, imIn, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize);
ImagingSectionLeave(&cookie);
-
- } else if (strcmp(imMask->mode, "LA") == 0 || strcmp(imMask->mode, "RGBA") == 0) {
+ } else if (imMask->mode == IMAGING_MODE_LA || imMask->mode == IMAGING_MODE_RGBA) {
ImagingSectionEnter(&cookie);
paste_mask_RGBA(
imOut, imIn, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize
);
ImagingSectionLeave(&cookie);
-
- } else if (strcmp(imMask->mode, "RGBa") == 0) {
+ } else if (imMask->mode == IMAGING_MODE_RGBa) {
ImagingSectionEnter(&cookie);
paste_mask_RGBa(
imOut, imIn, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize
);
ImagingSectionLeave(&cookie);
-
} else {
(void)ImagingError_ValueError("bad transparency mask");
return -1;
@@ -437,7 +447,7 @@ fill_mask_L(
unsigned int tmp1;
if (imOut->image8) {
- int i16 = strncmp(imOut->mode, "I;16", 4) == 0;
+ int i16 = isModeI16(imOut->mode);
for (y = 0; y < ysize; y++) {
UINT8 *out = imOut->image8[y + dy] + dx;
if (i16) {
@@ -456,9 +466,9 @@ fill_mask_L(
} else {
int alpha_channel =
- strcmp(imOut->mode, "RGBa") == 0 || strcmp(imOut->mode, "RGBA") == 0 ||
- strcmp(imOut->mode, "La") == 0 || strcmp(imOut->mode, "LA") == 0 ||
- strcmp(imOut->mode, "PA") == 0;
+ imOut->mode == IMAGING_MODE_RGBa || imOut->mode == IMAGING_MODE_RGBA ||
+ imOut->mode == IMAGING_MODE_La || imOut->mode == IMAGING_MODE_LA ||
+ imOut->mode == IMAGING_MODE_PA;
for (y = 0; y < ysize; y++) {
UINT8 *out = (UINT8 *)imOut->image[y + dy] + dx * pixelsize;
UINT8 *mask = (UINT8 *)imMask->image[y + sy] + sx;
@@ -617,27 +627,22 @@ ImagingFill2(
ImagingSectionEnter(&cookie);
fill(imOut, ink, dx0, dy0, xsize, ysize, pixelsize);
ImagingSectionLeave(&cookie);
-
- } else if (strcmp(imMask->mode, "1") == 0) {
+ } else if (imMask->mode == IMAGING_MODE_1) {
ImagingSectionEnter(&cookie);
fill_mask_1(imOut, ink, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize);
ImagingSectionLeave(&cookie);
-
- } else if (strcmp(imMask->mode, "L") == 0) {
+ } else if (imMask->mode == IMAGING_MODE_L) {
ImagingSectionEnter(&cookie);
fill_mask_L(imOut, ink, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize);
ImagingSectionLeave(&cookie);
-
- } else if (strcmp(imMask->mode, "RGBA") == 0) {
+ } else if (imMask->mode == IMAGING_MODE_RGBA) {
ImagingSectionEnter(&cookie);
fill_mask_RGBA(imOut, ink, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize);
ImagingSectionLeave(&cookie);
-
- } else if (strcmp(imMask->mode, "RGBa") == 0) {
+ } else if (imMask->mode == IMAGING_MODE_RGBa) {
ImagingSectionEnter(&cookie);
fill_mask_RGBa(imOut, ink, imMask, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize);
ImagingSectionLeave(&cookie);
-
} else {
(void)ImagingError_ValueError("bad transparency mask");
return -1;
diff --git a/src/libImaging/PcxDecode.c b/src/libImaging/PcxDecode.c
index 942c8dc22..a65952fb1 100644
--- a/src/libImaging/PcxDecode.c
+++ b/src/libImaging/PcxDecode.c
@@ -60,15 +60,25 @@ ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t byt
}
if (state->x >= state->bytes) {
- if (state->bytes % state->xsize && state->bytes > state->xsize) {
- int bands = state->bytes / state->xsize;
- int stride = state->bytes / bands;
+ int bands;
+ int xsize = 0;
+ int stride = 0;
+ if (state->bits == 2 || state->bits == 4) {
+ xsize = (state->xsize + 7) / 8;
+ bands = state->bits;
+ stride = state->bytes / state->bits;
+ } else {
+ xsize = state->xsize;
+ bands = state->bytes / state->xsize;
+ if (bands != 0) {
+ stride = state->bytes / bands;
+ }
+ }
+ if (stride > xsize) {
int i;
for (i = 1; i < bands; i++) { // note -- skipping first band
memmove(
- &state->buffer[i * state->xsize],
- &state->buffer[i * stride],
- state->xsize
+ &state->buffer[i * xsize], &state->buffer[i * stride], xsize
);
}
}
diff --git a/src/libImaging/Point.c b/src/libImaging/Point.c
index 6a4060b4b..8f6d47c77 100644
--- a/src/libImaging/Point.c
+++ b/src/libImaging/Point.c
@@ -128,7 +128,7 @@ im_point_32_8(Imaging imOut, Imaging imIn, im_point_context *context) {
}
Imaging
-ImagingPoint(Imaging imIn, const char *mode, const void *table) {
+ImagingPoint(Imaging imIn, ModeID mode, const void *table) {
/* lookup table transform */
ImagingSectionCookie cookie;
@@ -140,15 +140,15 @@ ImagingPoint(Imaging imIn, const char *mode, const void *table) {
return (Imaging)ImagingError_ModeError();
}
- if (!mode) {
+ if (mode == IMAGING_MODE_UNKNOWN) {
mode = imIn->mode;
}
if (imIn->type != IMAGING_TYPE_UINT8) {
- if (imIn->type != IMAGING_TYPE_INT32 || strcmp(mode, "L") != 0) {
+ if (imIn->type != IMAGING_TYPE_INT32 || mode != IMAGING_MODE_L) {
goto mode_mismatch;
}
- } else if (!imIn->image8 && strcmp(imIn->mode, mode) != 0) {
+ } else if (!imIn->image8 && imIn->mode != mode) {
goto mode_mismatch;
}
@@ -197,8 +197,9 @@ ImagingPoint(Imaging imIn, const char *mode, const void *table) {
return imOut;
mode_mismatch:
- return (Imaging
- )ImagingError_ValueError("point operation not supported for this mode");
+ return (Imaging)ImagingError_ValueError(
+ "point operation not supported for this mode"
+ );
}
Imaging
@@ -209,8 +210,8 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) {
Imaging imOut;
int x, y;
- if (!imIn || (strcmp(imIn->mode, "I") != 0 && strcmp(imIn->mode, "I;16") != 0 &&
- strcmp(imIn->mode, "F") != 0)) {
+ if (!imIn || (imIn->mode != IMAGING_MODE_I && imIn->mode != IMAGING_MODE_I_16 &&
+ imIn->mode != IMAGING_MODE_F)) {
return (Imaging)ImagingError_ModeError();
}
@@ -244,7 +245,7 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) {
ImagingSectionLeave(&cookie);
break;
case IMAGING_TYPE_SPECIAL:
- if (strcmp(imIn->mode, "I;16") == 0) {
+ if (imIn->mode == IMAGING_MODE_I_16) {
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++) {
char *in = (char *)imIn->image[y];
diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c
index a489a882d..7e3df1808 100644
--- a/src/libImaging/Quant.c
+++ b/src/libImaging/Quant.c
@@ -1684,13 +1684,13 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
return (Imaging)ImagingError_ValueError("bad number of colors");
}
- if (strcmp(im->mode, "L") != 0 && strcmp(im->mode, "P") != 0 &&
- strcmp(im->mode, "RGB") != 0 && strcmp(im->mode, "RGBA") != 0) {
+ if (im->mode != IMAGING_MODE_L && im->mode != IMAGING_MODE_P &&
+ im->mode != IMAGING_MODE_RGB && im->mode != IMAGING_MODE_RGBA) {
return ImagingError_ModeError();
}
/* only octree and imagequant supports RGBA */
- if (!strcmp(im->mode, "RGBA") && mode != 2 && mode != 3) {
+ if (im->mode == IMAGING_MODE_RGBA && mode != 2 && mode != 3) {
return ImagingError_ModeError();
}
@@ -1708,7 +1708,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
/* FIXME: maybe we could load the hash tables directly from the
image data? */
- if (!strcmp(im->mode, "L")) {
+ if (im->mode == IMAGING_MODE_L) {
/* grayscale */
/* FIXME: converting a "L" image to "P" with 256 colors
@@ -1721,7 +1721,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
}
}
- } else if (!strcmp(im->mode, "P")) {
+ } else if (im->mode == IMAGING_MODE_P) {
/* palette */
pp = im->palette->palette;
@@ -1736,28 +1736,32 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
}
}
- } else if (!strcmp(im->mode, "RGB") || !strcmp(im->mode, "RGBA")) {
+ } else if (im->mode == IMAGING_MODE_RGB || im->mode == IMAGING_MODE_RGBA) {
/* true colour */
- withAlpha = !strcmp(im->mode, "RGBA");
+ withAlpha = im->mode == IMAGING_MODE_RGBA;
int transparency = 0;
unsigned char r = 0, g = 0, b = 0;
for (i = y = 0; y < im->ysize; y++) {
for (x = 0; x < im->xsize; x++, i++) {
p[i].v = im->image32[y][x];
- if (withAlpha && p[i].c.a == 0) {
- if (transparency == 0) {
- transparency = 1;
- r = p[i].c.r;
- g = p[i].c.g;
- b = p[i].c.b;
- } else {
- /* Set all subsequent transparent pixels
- to the same colour as the first */
- p[i].c.r = r;
- p[i].c.g = g;
- p[i].c.b = b;
+ if (withAlpha) {
+ if (p[i].c.a == 0) {
+ if (transparency == 0) {
+ transparency = 1;
+ r = p[i].c.r;
+ g = p[i].c.g;
+ b = p[i].c.b;
+ } else {
+ /* Set all subsequent transparent pixels
+ to the same colour as the first */
+ p[i].c.r = r;
+ p[i].c.g = g;
+ p[i].c.b = b;
+ }
}
+ } else {
+ p[i].c.a = 255;
}
}
}
@@ -1830,7 +1834,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
ImagingSectionLeave(&cookie);
if (result > 0) {
- imOut = ImagingNewDirty("P", im->xsize, im->ysize);
+ imOut = ImagingNewDirty(IMAGING_MODE_P, im->xsize, im->ysize);
ImagingSectionEnter(&cookie);
for (i = y = 0; y < im->ysize; y++) {
@@ -1855,7 +1859,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) {
}
if (withAlpha) {
- strcpy(imOut->palette->mode, "RGBA");
+ imOut->palette->mode = IMAGING_MODE_RGBA;
}
free(palette);
diff --git a/src/libImaging/QuantHash.h b/src/libImaging/QuantHash.h
index 0462cfd49..d75d55ce0 100644
--- a/src/libImaging/QuantHash.h
+++ b/src/libImaging/QuantHash.h
@@ -20,8 +20,12 @@ typedef uint32_t HashVal_t;
typedef uint32_t (*HashFunc)(const HashTable *, const HashKey_t);
typedef int (*HashCmpFunc)(const HashTable *, const HashKey_t, const HashKey_t);
-typedef void (*IteratorFunc)(const HashTable *, const HashKey_t, const HashVal_t, void *);
-typedef void (*IteratorUpdateFunc)(const HashTable *, const HashKey_t, HashVal_t *, void *);
+typedef void (*IteratorFunc)(
+ const HashTable *, const HashKey_t, const HashVal_t, void *
+);
+typedef void (*IteratorUpdateFunc)(
+ const HashTable *, const HashKey_t, HashVal_t *, void *
+);
typedef void (*ComputeFunc)(const HashTable *, const HashKey_t, HashVal_t *);
typedef void (*CollisionFunc)(
const HashTable *, HashKey_t *, HashVal_t *, HashKey_t, HashVal_t
diff --git a/src/libImaging/Reduce.c b/src/libImaging/Reduce.c
index 022daa000..a4e58ced8 100644
--- a/src/libImaging/Reduce.c
+++ b/src/libImaging/Reduce.c
@@ -1452,7 +1452,7 @@ ImagingReduce(Imaging imIn, int xscale, int yscale, int box[4]) {
ImagingSectionCookie cookie;
Imaging imOut = NULL;
- if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0) {
+ if (imIn->mode == IMAGING_MODE_P || imIn->mode == IMAGING_MODE_1) {
return (Imaging)ImagingError_ModeError();
}
diff --git a/src/libImaging/Resample.c b/src/libImaging/Resample.c
index f5e386dc2..cbd18d0c1 100644
--- a/src/libImaging/Resample.c
+++ b/src/libImaging/Resample.c
@@ -470,9 +470,10 @@ ImagingResampleHorizontal_16bpc(
double *k;
int bigendian = 0;
- if (strcmp(imIn->mode, "I;16N") == 0
+ if (
+ imIn->mode == IMAGING_MODE_I_16N
#ifdef WORDS_BIGENDIAN
- || strcmp(imIn->mode, "I;16B") == 0
+ || imIn->mode == IMAGING_MODE_I_16B
#endif
) {
bigendian = 1;
@@ -509,9 +510,10 @@ ImagingResampleVertical_16bpc(
double *k;
int bigendian = 0;
- if (strcmp(imIn->mode, "I;16N") == 0
+ if (
+ imIn->mode == IMAGING_MODE_I_16N
#ifdef WORDS_BIGENDIAN
- || strcmp(imIn->mode, "I;16B") == 0
+ || imIn->mode == IMAGING_MODE_I_16B
#endif
) {
bigendian = 1;
@@ -646,12 +648,12 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]) {
ResampleFunction ResampleHorizontal;
ResampleFunction ResampleVertical;
- if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0) {
+ if (imIn->mode == IMAGING_MODE_P || imIn->mode == IMAGING_MODE_1) {
return (Imaging)ImagingError_ModeError();
}
if (imIn->type == IMAGING_TYPE_SPECIAL) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
+ if (isModeI16(imIn->mode)) {
ResampleHorizontal = ImagingResampleHorizontal_16bpc;
ResampleVertical = ImagingResampleVertical_16bpc;
} else {
diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c
index e60468990..a562f582c 100644
--- a/src/libImaging/SgiRleDecode.c
+++ b/src/libImaging/SgiRleDecode.c
@@ -22,7 +22,8 @@
static void
read4B(UINT32 *dest, UINT8 *buf) {
- *dest = (UINT32)((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
+ *dest = ((UINT32)buf[0] << 24) | ((UINT32)buf[1] << 16) | ((UINT32)buf[2] << 8) |
+ buf[3];
}
/*
diff --git a/src/libImaging/Storage.c b/src/libImaging/Storage.c
index 522e9f375..c09062c92 100644
--- a/src/libImaging/Storage.c
+++ b/src/libImaging/Storage.c
@@ -42,7 +42,7 @@
*/
Imaging
-ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
+ImagingNewPrologueSubtype(const ModeID mode, int xsize, int ysize, int size) {
Imaging im;
/* linesize overflow check, roughly the current largest space req'd */
@@ -58,136 +58,165 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
/* Setup image descriptor */
im->xsize = xsize;
im->ysize = ysize;
-
+ im->refcount = 1;
im->type = IMAGING_TYPE_UINT8;
+ strcpy(im->arrow_band_format, "C");
- if (strcmp(mode, "1") == 0) {
+ if (mode == IMAGING_MODE_1) {
/* 1-bit images */
im->bands = im->pixelsize = 1;
im->linesize = xsize;
+ strcpy(im->band_names[0], "1");
- } else if (strcmp(mode, "P") == 0) {
+ } else if (mode == IMAGING_MODE_P) {
/* 8-bit palette mapped images */
im->bands = im->pixelsize = 1;
im->linesize = xsize;
- im->palette = ImagingPaletteNew("RGB");
+ im->palette = ImagingPaletteNew(IMAGING_MODE_RGB);
+ strcpy(im->band_names[0], "P");
- } else if (strcmp(mode, "PA") == 0) {
+ } else if (mode == IMAGING_MODE_PA) {
/* 8-bit palette with alpha */
im->bands = 2;
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;
- im->palette = ImagingPaletteNew("RGB");
+ im->palette = ImagingPaletteNew(IMAGING_MODE_RGB);
+ strcpy(im->band_names[0], "P");
+ strcpy(im->band_names[1], "X");
+ strcpy(im->band_names[2], "X");
+ strcpy(im->band_names[3], "A");
- } else if (strcmp(mode, "L") == 0) {
+ } else if (mode == IMAGING_MODE_L) {
/* 8-bit grayscale (luminance) images */
im->bands = im->pixelsize = 1;
im->linesize = xsize;
+ strcpy(im->band_names[0], "L");
- } else if (strcmp(mode, "LA") == 0) {
+ } else if (mode == IMAGING_MODE_LA) {
/* 8-bit grayscale (luminance) with alpha */
im->bands = 2;
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "L");
+ strcpy(im->band_names[1], "X");
+ strcpy(im->band_names[2], "X");
+ strcpy(im->band_names[3], "A");
- } else if (strcmp(mode, "La") == 0) {
+ } else if (mode == IMAGING_MODE_La) {
/* 8-bit grayscale (luminance) with premultiplied alpha */
im->bands = 2;
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "L");
+ strcpy(im->band_names[1], "X");
+ strcpy(im->band_names[2], "X");
+ strcpy(im->band_names[3], "a");
- } else if (strcmp(mode, "F") == 0) {
+ } else if (mode == IMAGING_MODE_F) {
/* 32-bit floating point images */
im->bands = 1;
im->pixelsize = 4;
im->linesize = xsize * 4;
im->type = IMAGING_TYPE_FLOAT32;
+ strcpy(im->arrow_band_format, "f");
+ strcpy(im->band_names[0], "F");
- } else if (strcmp(mode, "I") == 0) {
+ } else if (mode == IMAGING_MODE_I) {
/* 32-bit integer images */
im->bands = 1;
im->pixelsize = 4;
im->linesize = xsize * 4;
im->type = IMAGING_TYPE_INT32;
+ strcpy(im->arrow_band_format, "i");
+ strcpy(im->band_names[0], "I");
- } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 ||
- strcmp(mode, "I;16B") == 0 || strcmp(mode, "I;16N") == 0) {
+ } else if (isModeI16(mode)) {
/* EXPERIMENTAL */
/* 16-bit raw integer images */
im->bands = 1;
im->pixelsize = 2;
im->linesize = xsize * 2;
im->type = IMAGING_TYPE_SPECIAL;
+ strcpy(im->arrow_band_format, "s");
+ strcpy(im->band_names[0], "I");
- } else if (strcmp(mode, "RGB") == 0) {
+ } else if (mode == IMAGING_MODE_RGB) {
/* 24-bit true colour images */
im->bands = 3;
im->pixelsize = 4;
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "R");
+ strcpy(im->band_names[1], "G");
+ strcpy(im->band_names[2], "B");
+ strcpy(im->band_names[3], "X");
- } else if (strcmp(mode, "BGR;15") == 0) {
- /* EXPERIMENTAL */
- /* 15-bit reversed true colour */
- im->bands = 3;
- im->pixelsize = 2;
- im->linesize = (xsize * 2 + 3) & -4;
- im->type = IMAGING_TYPE_SPECIAL;
-
- } else if (strcmp(mode, "BGR;16") == 0) {
- /* EXPERIMENTAL */
- /* 16-bit reversed true colour */
- im->bands = 3;
- im->pixelsize = 2;
- im->linesize = (xsize * 2 + 3) & -4;
- im->type = IMAGING_TYPE_SPECIAL;
-
- } else if (strcmp(mode, "BGR;24") == 0) {
- /* EXPERIMENTAL */
- /* 24-bit reversed true colour */
- im->bands = 3;
- im->pixelsize = 3;
- im->linesize = (xsize * 3 + 3) & -4;
- im->type = IMAGING_TYPE_SPECIAL;
-
- } else if (strcmp(mode, "RGBX") == 0) {
+ } else if (mode == IMAGING_MODE_RGBX) {
/* 32-bit true colour images with padding */
im->bands = im->pixelsize = 4;
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "R");
+ strcpy(im->band_names[1], "G");
+ strcpy(im->band_names[2], "B");
+ strcpy(im->band_names[3], "X");
- } else if (strcmp(mode, "RGBA") == 0) {
+ } else if (mode == IMAGING_MODE_RGBA) {
/* 32-bit true colour images with alpha */
im->bands = im->pixelsize = 4;
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "R");
+ strcpy(im->band_names[1], "G");
+ strcpy(im->band_names[2], "B");
+ strcpy(im->band_names[3], "A");
- } else if (strcmp(mode, "RGBa") == 0) {
+ } else if (mode == IMAGING_MODE_RGBa) {
/* 32-bit true colour images with premultiplied alpha */
im->bands = im->pixelsize = 4;
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "R");
+ strcpy(im->band_names[1], "G");
+ strcpy(im->band_names[2], "B");
+ strcpy(im->band_names[3], "a");
- } else if (strcmp(mode, "CMYK") == 0) {
+ } else if (mode == IMAGING_MODE_CMYK) {
/* 32-bit colour separation */
im->bands = im->pixelsize = 4;
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "C");
+ strcpy(im->band_names[1], "M");
+ strcpy(im->band_names[2], "Y");
+ strcpy(im->band_names[3], "K");
- } else if (strcmp(mode, "YCbCr") == 0) {
+ } else if (mode == IMAGING_MODE_YCbCr) {
/* 24-bit video format */
im->bands = 3;
im->pixelsize = 4;
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "Y");
+ strcpy(im->band_names[1], "Cb");
+ strcpy(im->band_names[2], "Cr");
+ strcpy(im->band_names[3], "X");
- } else if (strcmp(mode, "LAB") == 0) {
+ } else if (mode == IMAGING_MODE_LAB) {
/* 24-bit color, luminance, + 2 color channels */
/* L is uint8, a,b are int8 */
im->bands = 3;
im->pixelsize = 4;
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "L");
+ strcpy(im->band_names[1], "a");
+ strcpy(im->band_names[2], "b");
+ strcpy(im->band_names[3], "X");
- } else if (strcmp(mode, "HSV") == 0) {
+ } else if (mode == IMAGING_MODE_HSV) {
/* 24-bit color, luminance, + 2 color channels */
/* L is uint8, a,b are int8 */
im->bands = 3;
im->pixelsize = 4;
im->linesize = xsize * 4;
+ strcpy(im->band_names[0], "H");
+ strcpy(im->band_names[1], "S");
+ strcpy(im->band_names[2], "V");
+ strcpy(im->band_names[3], "X");
} else {
free(im);
@@ -195,7 +224,7 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
}
/* Setup image descriptor */
- strcpy(im->mode, mode);
+ im->mode = mode;
/* Pointer array (allocate at least one line, to avoid MemoryError
exceptions on platforms where calloc(0, x) returns NULL) */
@@ -218,6 +247,7 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
break;
}
+ // UNDONE -- not accurate for arrow
MUTEX_LOCK(&ImagingDefaultArena.mutex);
ImagingDefaultArena.stats_new_count += 1;
MUTEX_UNLOCK(&ImagingDefaultArena.mutex);
@@ -226,7 +256,7 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
}
Imaging
-ImagingNewPrologue(const char *mode, int xsize, int ysize) {
+ImagingNewPrologue(const ModeID mode, int xsize, int ysize) {
return ImagingNewPrologueSubtype(
mode, xsize, ysize, sizeof(struct ImagingMemoryInstance)
);
@@ -238,8 +268,18 @@ ImagingDelete(Imaging im) {
return;
}
+ MUTEX_LOCK(&im->mutex);
+ im->refcount--;
+
+ if (im->refcount > 0) {
+ MUTEX_UNLOCK(&im->mutex);
+ return;
+ }
+ MUTEX_UNLOCK(&im->mutex);
+
if (im->palette) {
ImagingPaletteDelete(im->palette);
+ im->palette = NULL;
}
if (im->destroy) {
@@ -270,6 +310,7 @@ struct ImagingMemoryArena ImagingDefaultArena = {
0,
0,
0, // Stats
+ 0, // use_block_allocator
#ifdef Py_GIL_DISABLED
{0},
#endif
@@ -302,6 +343,11 @@ ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max) {
return 1;
}
+void
+ImagingMemorySetBlockAllocator(ImagingMemoryArena arena, int use_block_allocator) {
+ arena->use_block_allocator = use_block_allocator;
+}
+
void
ImagingMemoryClearCache(ImagingMemoryArena arena, int new_size) {
while (arena->blocks_cached > new_size) {
@@ -396,11 +442,13 @@ ImagingAllocateArray(Imaging im, ImagingMemoryArena arena, int dirty, int block_
if (lines_per_block == 0) {
lines_per_block = 1;
}
+ im->lines_per_block = lines_per_block;
blocks_count = (im->ysize + lines_per_block - 1) / lines_per_block;
// printf("NEW size: %dx%d, ls: %d, lpb: %d, blocks: %d\n",
// im->xsize, im->ysize, aligned_linesize, lines_per_block, blocks_count);
/* One extra pointer is always NULL */
+ im->blocks_count = blocks_count;
im->blocks = calloc(sizeof(*im->blocks), blocks_count + 1);
if (!im->blocks) {
return (Imaging)ImagingError_MemoryError();
@@ -487,12 +535,65 @@ ImagingAllocateBlock(Imaging im) {
return im;
}
+/* Borrowed Arrow Storage Type */
+/* --------------------------- */
+/* Don't allocate the image. */
+
+static void
+ImagingDestroyArrow(Imaging im) {
+ // Rely on the internal Python destructor for the array capsule.
+ if (im->arrow_array_capsule) {
+ Py_DECREF(im->arrow_array_capsule);
+ im->arrow_array_capsule = NULL;
+ }
+}
+
+Imaging
+ImagingBorrowArrow(
+ Imaging im,
+ struct ArrowArray *external_array,
+ int offset_width,
+ PyObject *arrow_capsule
+) {
+ // offset_width is the number of char* for a single offset from arrow
+ Py_ssize_t y, i;
+
+ char *borrowed_buffer = NULL;
+ struct ArrowArray *arr = external_array;
+
+ if (arr->n_children == 1) {
+ arr = arr->children[0];
+ }
+ if (arr->n_buffers == 2) {
+ // buffer 0 is the null list
+ // buffer 1 is the data
+ borrowed_buffer = (char *)arr->buffers[1] + (offset_width * arr->offset);
+ }
+
+ if (!borrowed_buffer) {
+ return (Imaging)ImagingError_ValueError(
+ "Arrow Array, exactly 2 buffers required"
+ );
+ }
+
+ for (y = i = 0; y < im->ysize; y++) {
+ im->image[y] = borrowed_buffer + i;
+ i += im->linesize;
+ }
+ im->read_only = 1;
+ Py_INCREF(arrow_capsule);
+ im->arrow_array_capsule = arrow_capsule;
+ im->destroy = ImagingDestroyArrow;
+
+ return im;
+}
+
/* --------------------------------------------------------------------
* Create a new, internally allocated, image.
*/
Imaging
-ImagingNewInternal(const char *mode, int xsize, int ysize, int dirty) {
+ImagingNewInternal(const ModeID mode, int xsize, int ysize, int dirty) {
Imaging im;
if (xsize < 0 || ysize < 0) {
@@ -513,7 +614,7 @@ ImagingNewInternal(const char *mode, int xsize, int ysize, int dirty) {
return im;
}
- ImagingError_Clear();
+ PyErr_Clear();
// Try to allocate the image once more with smallest possible block size
MUTEX_LOCK(&ImagingDefaultArena.mutex);
@@ -528,17 +629,23 @@ ImagingNewInternal(const char *mode, int xsize, int ysize, int dirty) {
}
Imaging
-ImagingNew(const char *mode, int xsize, int ysize) {
+ImagingNew(const ModeID mode, int xsize, int ysize) {
+ if (ImagingDefaultArena.use_block_allocator) {
+ return ImagingNewBlock(mode, xsize, ysize);
+ }
return ImagingNewInternal(mode, xsize, ysize, 0);
}
Imaging
-ImagingNewDirty(const char *mode, int xsize, int ysize) {
+ImagingNewDirty(const ModeID mode, int xsize, int ysize) {
+ if (ImagingDefaultArena.use_block_allocator) {
+ return ImagingNewBlock(mode, xsize, ysize);
+ }
return ImagingNewInternal(mode, xsize, ysize, 1);
}
Imaging
-ImagingNewBlock(const char *mode, int xsize, int ysize) {
+ImagingNewBlock(const ModeID mode, int xsize, int ysize) {
Imaging im;
if (xsize < 0 || ysize < 0) {
@@ -559,12 +666,86 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) {
}
Imaging
-ImagingNew2Dirty(const char *mode, Imaging imOut, Imaging imIn) {
+ImagingNewArrow(
+ const ModeID mode,
+ int xsize,
+ int ysize,
+ PyObject *schema_capsule,
+ PyObject *array_capsule
+) {
+ /* A borrowed arrow array */
+ Imaging im;
+ struct ArrowSchema *schema =
+ (struct ArrowSchema *)PyCapsule_GetPointer(schema_capsule, "arrow_schema");
+
+ struct ArrowArray *external_array =
+ (struct ArrowArray *)PyCapsule_GetPointer(array_capsule, "arrow_array");
+
+ if (xsize < 0 || ysize < 0) {
+ return (Imaging)ImagingError_ValueError("bad image size");
+ }
+
+ im = ImagingNewPrologue(mode, xsize, ysize);
+ if (!im) {
+ return NULL;
+ }
+
+ int64_t pixels = (int64_t)xsize * (int64_t)ysize;
+
+ // fmt:off // don't reformat this
+ // stored as a single array, one element per pixel, either single band
+ // or multiband, where each pixel is an I32.
+ if (((strcmp(schema->format, "I") == 0 // int32
+ && im->pixelsize == 4 // 4xchar* storage
+ && im->bands >= 2) // INT32 into any INT32 Storage mode
+ || // (()||()) &&
+ (strcmp(schema->format, im->arrow_band_format) == 0 // same mode
+ && im->bands == 1)) // Single band match
+ && pixels == external_array->length) {
+ // one arrow element per, and it matches a pixelsize*char
+ if (ImagingBorrowArrow(im, external_array, im->pixelsize, array_capsule)) {
+ return im;
+ }
+ }
+ // Stored as [[r,g,b,a],...]
+ if (strcmp(schema->format, "+w:4") == 0 // 4 up array
+ && im->pixelsize == 4 // storage as 32 bpc
+ && schema->n_children > 0 // make sure schema is well formed.
+ && schema->children // make sure schema is well formed
+ && strcmp(schema->children[0]->format, "C") == 0 // Expected format
+ && strcmp(im->arrow_band_format, "C") == 0 // Expected Format
+ && pixels == external_array->length // expected length
+ && external_array->n_children == 1 // array is well formed
+ && external_array->children // array is well formed
+ && 4 * pixels == external_array->children[0]->length) {
+ // 4 up element of char into pixelsize == 4
+ if (ImagingBorrowArrow(im, external_array, 1, array_capsule)) {
+ return im;
+ }
+ }
+ // Stored as [r,g,b,a,r,g,b,a,...]
+ if (strcmp(schema->format, "C") == 0 // uint8
+ && im->pixelsize == 4 // storage as 32 bpc
+ && schema->n_children == 0 // make sure schema is well formed.
+ && strcmp(im->arrow_band_format, "C") == 0 // expected format
+ && 4 * pixels == external_array->length) { // expected length
+ // single flat array, interleaved storage.
+ if (ImagingBorrowArrow(im, external_array, 1, array_capsule)) {
+ return im;
+ }
+ }
+ // fmt: on
+ ImagingDelete(im);
+ return NULL;
+}
+
+Imaging
+ImagingNew2Dirty(const ModeID mode, Imaging imOut, Imaging imIn) {
/* allocate or validate output image */
if (imOut) {
/* make sure images match */
- if (strcmp(imOut->mode, mode) != 0 || imOut->xsize != imIn->xsize ||
+ if (imOut->mode != mode || imOut->xsize != imIn->xsize ||
imOut->ysize != imIn->ysize) {
return ImagingError_Mismatch();
}
diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c
index e4da9162d..72e0d7b30 100644
--- a/src/libImaging/TiffDecode.c
+++ b/src/libImaging/TiffDecode.c
@@ -246,14 +246,26 @@ _pickUnpackers(
// We'll pick appropriate set of unpackers depending on planar_configuration
// It does not matter if data is RGB(A), CMYK or LUV really,
// we just copy it plane by plane
- unpackers[0] =
- ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "R;16N" : "R", NULL);
- unpackers[1] =
- ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "G;16N" : "G", NULL);
- unpackers[2] =
- ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "B;16N" : "B", NULL);
- unpackers[3] =
- ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "A;16N" : "A", NULL);
+ unpackers[0] = ImagingFindUnpacker(
+ IMAGING_MODE_RGBA,
+ bits_per_sample == 16 ? IMAGING_RAWMODE_R_16N : IMAGING_RAWMODE_R,
+ NULL
+ );
+ unpackers[1] = ImagingFindUnpacker(
+ IMAGING_MODE_RGBA,
+ bits_per_sample == 16 ? IMAGING_RAWMODE_G_16N : IMAGING_RAWMODE_G,
+ NULL
+ );
+ unpackers[2] = ImagingFindUnpacker(
+ IMAGING_MODE_RGBA,
+ bits_per_sample == 16 ? IMAGING_RAWMODE_B_16N : IMAGING_RAWMODE_B,
+ NULL
+ );
+ unpackers[3] = ImagingFindUnpacker(
+ IMAGING_MODE_RGBA,
+ bits_per_sample == 16 ? IMAGING_RAWMODE_A_16N : IMAGING_RAWMODE_A,
+ NULL
+ );
return im->bands;
} else {
@@ -299,6 +311,7 @@ _decodeAsRGBA(Imaging im, ImagingCodecState state, TIFF *tiff) {
return -1;
}
+ img.orientation = ORIENTATION_TOPLEFT;
img.req_orientation = ORIENTATION_TOPLEFT;
img.col_offset = 0;
@@ -556,7 +569,8 @@ _decodeStrip(
(tdata_t)state->buffer,
strip_size
) == -1) {
- TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))
+ TRACE(
+ ("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))
);
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
@@ -642,7 +656,7 @@ ImagingLibTiffDecode(
);
TRACE(
("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n",
- im->mode,
+ getModeData(im->mode)->name,
im->type,
im->bands,
im->xsize,
@@ -753,7 +767,7 @@ ImagingLibTiffDecode(
if (!state->errcode) {
// Check if raw mode was RGBa and it was stored on separate planes
// so we have to convert it to RGBA
- if (planes > 3 && strcmp(im->mode, "RGBA") == 0) {
+ if (planes > 3 && im->mode == IMAGING_MODE_RGBA) {
uint16_t extrasamples;
uint16_t *sampleinfo;
ImagingShuffler shuffle;
@@ -765,7 +779,9 @@ ImagingLibTiffDecode(
if (extrasamples >= 1 && (sampleinfo[0] == EXTRASAMPLE_UNSPECIFIED ||
sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA)) {
- shuffle = ImagingFindUnpacker("RGBA", "RGBa", NULL);
+ shuffle = ImagingFindUnpacker(
+ IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBa, NULL
+ );
for (y = state->yoff; y < state->ysize; y++) {
UINT8 *ptr = (UINT8 *)im->image[y + state->yoff] +
@@ -882,7 +898,6 @@ ImagingLibTiffMergeFieldInfo(
// Refer to libtiff docs (http://www.simplesystems.org/libtiff/addingtags.html)
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
uint32_t n;
- int status = 0;
// custom fields added with ImagingLibTiffMergeFieldInfo are only used for
// decoding, ignore readcount;
@@ -905,14 +920,7 @@ ImagingLibTiffMergeFieldInfo(
n = sizeof(info) / sizeof(info[0]);
- // Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7
-#if TIFFLIB_VERSION >= 20111221 && TIFFLIB_VERSION != 20120218 && \
- TIFFLIB_VERSION != 20120922
- status = TIFFMergeFieldInfo(clientstate->tiff, info, n);
-#else
- TIFFMergeFieldInfo(clientstate->tiff, info, n);
-#endif
- return status;
+ return TIFFMergeFieldInfo(clientstate->tiff, info, n);
}
int
@@ -928,6 +936,27 @@ ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...) {
return status;
}
+int
+ImagingLibTiffEncodeCleanup(ImagingCodecState state) {
+ TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
+ TIFF *tiff = clientstate->tiff;
+
+ if (!tiff) {
+ return 0;
+ }
+ // TIFFClose in libtiff calls tif_closeproc and TIFFCleanup
+ if (clientstate->fp) {
+ // Python will manage the closing of the file rather than libtiff
+ // So only call TIFFCleanup
+ TIFFCleanup(tiff);
+ } else {
+ // When tif_closeproc refers to our custom _tiffCloseProc though,
+ // that is fine, as it does not close the file
+ TIFFClose(tiff);
+ }
+ return 0;
+}
+
int
ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int bytes) {
/* One shot encoder. Encode everything to the tiff in the clientstate.
@@ -976,7 +1005,7 @@ ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int byt
);
TRACE(
("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n",
- im->mode,
+ getModeData(im->mode)->name,
im->type,
im->bands,
im->xsize,
@@ -1009,17 +1038,10 @@ ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int byt
TRACE(("Encode Error, row %d\n", state->y));
state->errcode = IMAGING_CODEC_BROKEN;
- // TIFFClose in libtiff calls tif_closeproc and TIFFCleanup
if (clientstate->fp) {
- // Python will manage the closing of the file rather than libtiff
- // So only call TIFFCleanup
TIFFCleanup(tiff);
+ clientstate->tiff = NULL;
} else {
- // When tif_closeproc refers to our custom _tiffCloseProc though,
- // that is fine, as it does not close the file
- TIFFClose(tiff);
- }
- if (!clientstate->fp) {
free(clientstate->data);
}
return -1;
@@ -1035,22 +1057,11 @@ ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int byt
TRACE(("Error flushing the tiff"));
// likely reason is memory.
state->errcode = IMAGING_CODEC_MEMORY;
- if (clientstate->fp) {
- TIFFCleanup(tiff);
- } else {
- TIFFClose(tiff);
- }
if (!clientstate->fp) {
free(clientstate->data);
}
return -1;
}
- TRACE(("Closing \n"));
- if (clientstate->fp) {
- TIFFCleanup(tiff);
- } else {
- TIFFClose(tiff);
- }
// reset the clientstate metadata to use it to read out the buffer.
clientstate->loc = 0;
clientstate->size = clientstate->eof; // redundant?
diff --git a/src/libImaging/TiffDecode.h b/src/libImaging/TiffDecode.h
index 22361210d..77808b543 100644
--- a/src/libImaging/TiffDecode.h
+++ b/src/libImaging/TiffDecode.h
@@ -40,6 +40,8 @@ ImagingLibTiffInit(ImagingCodecState state, int fp, uint32_t offset);
extern int
ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
extern int
+ImagingLibTiffEncodeCleanup(ImagingCodecState state);
+extern int
ImagingLibTiffMergeFieldInfo(
ImagingCodecState state, TIFFDataType field_type, int key, int is_var_length
);
diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c
index 9c3ee2665..203bcac2c 100644
--- a/src/libImaging/Unpack.c
+++ b/src/libImaging/Unpack.c
@@ -1284,12 +1284,6 @@ copy2(UINT8 *out, const UINT8 *in, int pixels) {
memcpy(out, in, pixels * 2);
}
-static void
-copy3(UINT8 *out, const UINT8 *in, int pixels) {
- /* BGR;24 */
- memcpy(out, in, pixels * 3);
-}
-
static void
copy4(UINT8 *out, const UINT8 *in, int pixels) {
/* RGBA, CMYK quadruples */
@@ -1548,12 +1542,11 @@ band316L(UINT8 *out, const UINT8 *in, int pixels) {
}
static struct {
- const char *mode;
- const char *rawmode;
+ const ModeID mode;
+ const RawModeID rawmode;
int bits;
ImagingShuffler unpack;
} unpackers[] = {
-
/* raw mode syntax is ";" where "bits" defaults
depending on mode (1 for "1", 8 for "P" and "L", etc), and
"flags" should be given in alphabetical order. if both bits
@@ -1566,303 +1559,295 @@ static struct {
/* exception: rawmodes "I" and "F" are always native endian byte order */
/* bilevel */
- {"1", "1", 1, unpack1},
- {"1", "1;I", 1, unpack1I},
- {"1", "1;R", 1, unpack1R},
- {"1", "1;IR", 1, unpack1IR},
- {"1", "1;8", 8, unpack18},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_1, 1, unpack1},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_1_I, 1, unpack1I},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_1_R, 1, unpack1R},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_1_IR, 1, unpack1IR},
+ {IMAGING_MODE_1, IMAGING_RAWMODE_1_8, 8, unpack18},
/* grayscale */
- {"L", "L;2", 2, unpackL2},
- {"L", "L;2I", 2, unpackL2I},
- {"L", "L;2R", 2, unpackL2R},
- {"L", "L;2IR", 2, unpackL2IR},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_2, 2, unpackL2},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_2I, 2, unpackL2I},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_2R, 2, unpackL2R},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_2IR, 2, unpackL2IR},
- {"L", "L;4", 4, unpackL4},
- {"L", "L;4I", 4, unpackL4I},
- {"L", "L;4R", 4, unpackL4R},
- {"L", "L;4IR", 4, unpackL4IR},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_4, 4, unpackL4},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_4I, 4, unpackL4I},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_4R, 4, unpackL4R},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_4IR, 4, unpackL4IR},
- {"L", "L", 8, copy1},
- {"L", "L;I", 8, unpackLI},
- {"L", "L;R", 8, unpackLR},
- {"L", "L;16", 16, unpackL16},
- {"L", "L;16B", 16, unpackL16B},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L, 8, copy1},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_I, 8, unpackLI},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_R, 8, unpackLR},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_16, 16, unpackL16},
+ {IMAGING_MODE_L, IMAGING_RAWMODE_L_16B, 16, unpackL16B},
/* grayscale w. alpha */
- {"LA", "LA", 16, unpackLA},
- {"LA", "LA;L", 16, unpackLAL},
+ {IMAGING_MODE_LA, IMAGING_RAWMODE_LA, 16, unpackLA},
+ {IMAGING_MODE_LA, IMAGING_RAWMODE_LA_L, 16, unpackLAL},
/* grayscale w. alpha premultiplied */
- {"La", "La", 16, unpackLA},
+ {IMAGING_MODE_La, IMAGING_RAWMODE_La, 16, unpackLA},
/* palette */
- {"P", "P;1", 1, unpackP1},
- {"P", "P;2", 2, unpackP2},
- {"P", "P;2L", 2, unpackP2L},
- {"P", "P;4", 4, unpackP4},
- {"P", "P;4L", 4, unpackP4L},
- {"P", "P", 8, copy1},
- {"P", "P;R", 8, unpackLR},
- {"P", "L", 8, copy1},
- {"P", "PX", 16, unpackL16B},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P_1, 1, unpackP1},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P_2, 2, unpackP2},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P_2L, 2, unpackP2L},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P_4, 4, unpackP4},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P_4L, 4, unpackP4L},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P, 8, copy1},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_P_R, 8, unpackLR},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_L, 8, copy1},
+ {IMAGING_MODE_P, IMAGING_RAWMODE_PX, 16, unpackL16B},
/* palette w. alpha */
- {"PA", "PA", 16, unpackLA},
- {"PA", "PA;L", 16, unpackLAL},
- {"PA", "LA", 16, unpackLA},
+ {IMAGING_MODE_PA, IMAGING_RAWMODE_PA, 16, unpackLA},
+ {IMAGING_MODE_PA, IMAGING_RAWMODE_PA_L, 16, unpackLAL},
+ {IMAGING_MODE_PA, IMAGING_RAWMODE_LA, 16, unpackLA},
/* true colour */
- {"RGB", "RGB", 24, ImagingUnpackRGB},
- {"RGB", "RGB;L", 24, unpackRGBL},
- {"RGB", "RGB;R", 24, unpackRGBR},
- {"RGB", "RGB;16L", 48, unpackRGB16L},
- {"RGB", "RGB;16B", 48, unpackRGB16B},
- {"RGB", "BGR", 24, ImagingUnpackBGR},
- {"RGB", "RGB;15", 16, ImagingUnpackRGB15},
- {"RGB", "BGR;15", 16, ImagingUnpackBGR15},
- {"RGB", "RGB;16", 16, ImagingUnpackRGB16},
- {"RGB", "BGR;16", 16, ImagingUnpackBGR16},
- {"RGB", "RGBX;16L", 64, unpackRGBA16L},
- {"RGB", "RGBX;16B", 64, unpackRGBA16B},
- {"RGB", "RGB;4B", 16, ImagingUnpackRGB4B},
- {"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */
- {"RGB", "RGBX", 32, copy4},
- {"RGB", "RGBX;L", 32, unpackRGBAL},
- {"RGB", "RGBXX", 40, copy4skip1},
- {"RGB", "RGBXXX", 48, copy4skip2},
- {"RGB", "RGBA;L", 32, unpackRGBAL},
- {"RGB", "RGBA;15", 16, ImagingUnpackRGBA15},
- {"RGB", "BGRX", 32, ImagingUnpackBGRX},
- {"RGB", "BGXR", 32, ImagingUnpackBGXR},
- {"RGB", "XRGB", 32, ImagingUnpackXRGB},
- {"RGB", "XBGR", 32, ImagingUnpackXBGR},
- {"RGB", "YCC;P", 24, ImagingUnpackYCC},
- {"RGB", "R", 8, band0},
- {"RGB", "G", 8, band1},
- {"RGB", "B", 8, band2},
- {"RGB", "R;16L", 16, band016L},
- {"RGB", "G;16L", 16, band116L},
- {"RGB", "B;16L", 16, band216L},
- {"RGB", "R;16B", 16, band016B},
- {"RGB", "G;16B", 16, band116B},
- {"RGB", "B;16B", 16, band216B},
- {"RGB", "CMYK", 32, cmyk2rgb},
-
- {"BGR;15", "BGR;15", 16, copy2},
- {"BGR;16", "BGR;16", 16, copy2},
- {"BGR;24", "BGR;24", 24, copy3},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB, 24, ImagingUnpackRGB},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_L, 24, unpackRGBL},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_R, 24, unpackRGBR},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_16L, 48, unpackRGB16L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_16B, 48, unpackRGB16B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_BGR, 24, ImagingUnpackBGR},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_15, 16, ImagingUnpackRGB15},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_BGR_15, 16, ImagingUnpackBGR15},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_16, 16, ImagingUnpackRGB16},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_BGR_16, 16, ImagingUnpackBGR16},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBX_16L, 64, unpackRGBA16L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBX_16B, 64, unpackRGBA16B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_4B, 16, ImagingUnpackRGB4B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_BGR_5, 16, ImagingUnpackBGR15}, /* compat */
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBX, 32, copy4},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBX_L, 32, unpackRGBAL},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBXX, 40, copy4skip1},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBXXX, 48, copy4skip2},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBA_L, 32, unpackRGBAL},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBA_15, 16, ImagingUnpackRGBA15},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_BGRX, 32, ImagingUnpackBGRX},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_BGXR, 32, ImagingUnpackBGXR},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_XRGB, 32, ImagingUnpackXRGB},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_XBGR, 32, ImagingUnpackXBGR},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_YCC_P, 24, ImagingUnpackYCC},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_R, 8, band0},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_G, 8, band1},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_B, 8, band2},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_R_16L, 16, band016L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_G_16L, 16, band116L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_B_16L, 16, band216L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_R_16B, 16, band016B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_G_16B, 16, band116B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_B_16B, 16, band216B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_CMYK, 32, cmyk2rgb},
/* true colour w. alpha */
- {"RGBA", "LA", 16, unpackRGBALA},
- {"RGBA", "LA;16B", 32, unpackRGBALA16B},
- {"RGBA", "RGBA", 32, copy4},
- {"RGBA", "RGBAX", 40, copy4skip1},
- {"RGBA", "RGBAXX", 48, copy4skip2},
- {"RGBA", "RGBa", 32, unpackRGBa},
- {"RGBA", "RGBaX", 40, unpackRGBaskip1},
- {"RGBA", "RGBaXX", 48, unpackRGBaskip2},
- {"RGBA", "RGBa;16L", 64, unpackRGBa16L},
- {"RGBA", "RGBa;16B", 64, unpackRGBa16B},
- {"RGBA", "BGR", 24, ImagingUnpackBGR},
- {"RGBA", "BGRa", 32, unpackBGRa},
- {"RGBA", "RGBA;I", 32, unpackRGBAI},
- {"RGBA", "RGBA;L", 32, unpackRGBAL},
- {"RGBA", "RGBA;15", 16, ImagingUnpackRGBA15},
- {"RGBA", "BGRA;15", 16, ImagingUnpackBGRA15},
- {"RGBA", "BGRA;15Z", 16, ImagingUnpackBGRA15Z},
- {"RGBA", "RGBA;4B", 16, ImagingUnpackRGBA4B},
- {"RGBA", "RGBA;16L", 64, unpackRGBA16L},
- {"RGBA", "RGBA;16B", 64, unpackRGBA16B},
- {"RGBA", "BGRA", 32, unpackBGRA},
- {"RGBA", "BGRA;16L", 64, unpackBGRA16L},
- {"RGBA", "BGRA;16B", 64, unpackBGRA16B},
- {"RGBA", "BGAR", 32, unpackBGAR},
- {"RGBA", "ARGB", 32, unpackARGB},
- {"RGBA", "ABGR", 32, unpackABGR},
- {"RGBA", "YCCA;P", 32, ImagingUnpackYCCA},
- {"RGBA", "R", 8, band0},
- {"RGBA", "G", 8, band1},
- {"RGBA", "B", 8, band2},
- {"RGBA", "A", 8, band3},
- {"RGBA", "R;16L", 16, band016L},
- {"RGBA", "G;16L", 16, band116L},
- {"RGBA", "B;16L", 16, band216L},
- {"RGBA", "A;16L", 16, band316L},
- {"RGBA", "R;16B", 16, band016B},
- {"RGBA", "G;16B", 16, band116B},
- {"RGBA", "B;16B", 16, band216B},
- {"RGBA", "A;16B", 16, band316B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_LA, 16, unpackRGBALA},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_LA_16B, 32, unpackRGBALA16B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA, 32, copy4},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBAX, 40, copy4skip1},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBAXX, 48, copy4skip2},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBa, 32, unpackRGBa},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBaX, 40, unpackRGBaskip1},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBaXX, 48, unpackRGBaskip2},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBa_16L, 64, unpackRGBa16L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBa_16B, 64, unpackRGBa16B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGR, 24, ImagingUnpackBGR},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGRa, 32, unpackBGRa},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA_I, 32, unpackRGBAI},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA_L, 32, unpackRGBAL},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA_15, 16, ImagingUnpackRGBA15},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGRA_15, 16, ImagingUnpackBGRA15},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGRA_15Z, 16, ImagingUnpackBGRA15Z},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA_4B, 16, ImagingUnpackRGBA4B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA_16L, 64, unpackRGBA16L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA_16B, 64, unpackRGBA16B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGRA, 32, unpackBGRA},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGRA_16L, 64, unpackBGRA16L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGRA_16B, 64, unpackBGRA16B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_BGAR, 32, unpackBGAR},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_ARGB, 32, unpackARGB},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_ABGR, 32, unpackABGR},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_YCCA_P, 32, ImagingUnpackYCCA},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_R, 8, band0},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_G, 8, band1},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_B, 8, band2},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_A, 8, band3},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_R_16L, 16, band016L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_G_16L, 16, band116L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_B_16L, 16, band216L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_A_16L, 16, band316L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_R_16B, 16, band016B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_G_16B, 16, band116B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_B_16B, 16, band216B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_A_16B, 16, band316B},
#ifdef WORDS_BIGENDIAN
- {"RGB", "RGB;16N", 48, unpackRGB16B},
- {"RGB", "RGBX;16N", 64, unpackRGBA16B},
- {"RGBA", "RGBa;16N", 64, unpackRGBa16B},
- {"RGBA", "RGBA;16N", 64, unpackRGBA16B},
- {"RGBX", "RGBX;16N", 64, unpackRGBA16B},
- {"RGB", "R;16N", 16, band016B},
- {"RGB", "G;16N", 16, band116B},
- {"RGB", "B;16N", 16, band216B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_16N, 48, unpackRGB16B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBX_16N, 64, unpackRGBA16B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBa_16N, 64, unpackRGBa16B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA_16N, 64, unpackRGBA16B},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBX_16N, 64, unpackRGBA16B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_R_16N, 16, band016B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_G_16N, 16, band116B},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_B_16N, 16, band216B},
- {"RGBA", "R;16N", 16, band016B},
- {"RGBA", "G;16N", 16, band116B},
- {"RGBA", "B;16N", 16, band216B},
- {"RGBA", "A;16N", 16, band316B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_R_16N, 16, band016B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_G_16N, 16, band116B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_B_16N, 16, band216B},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_A_16N, 16, band316B},
#else
- {"RGB", "RGB;16N", 48, unpackRGB16L},
- {"RGB", "RGBX;16N", 64, unpackRGBA16L},
- {"RGBA", "RGBa;16N", 64, unpackRGBa16L},
- {"RGBA", "RGBA;16N", 64, unpackRGBA16L},
- {"RGBX", "RGBX;16N", 64, unpackRGBA16L},
- {"RGB", "R;16N", 16, band016L},
- {"RGB", "G;16N", 16, band116L},
- {"RGB", "B;16N", 16, band216L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGB_16N, 48, unpackRGB16L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_RGBX_16N, 64, unpackRGBA16L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBa_16N, 64, unpackRGBa16L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_RGBA_16N, 64, unpackRGBA16L},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBX_16N, 64, unpackRGBA16L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_R_16N, 16, band016L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_G_16N, 16, band116L},
+ {IMAGING_MODE_RGB, IMAGING_RAWMODE_B_16N, 16, band216L},
- {"RGBA", "R;16N", 16, band016L},
- {"RGBA", "G;16N", 16, band116L},
- {"RGBA", "B;16N", 16, band216L},
- {"RGBA", "A;16N", 16, band316L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_R_16N, 16, band016L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_G_16N, 16, band116L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_B_16N, 16, band216L},
+ {IMAGING_MODE_RGBA, IMAGING_RAWMODE_A_16N, 16, band316L},
#endif
/* true colour w. alpha premultiplied */
- {"RGBa", "RGBa", 32, copy4},
- {"RGBa", "BGRa", 32, unpackBGRA},
- {"RGBa", "aRGB", 32, unpackARGB},
- {"RGBa", "aBGR", 32, unpackABGR},
+ {IMAGING_MODE_RGBa, IMAGING_RAWMODE_RGBa, 32, copy4},
+ {IMAGING_MODE_RGBa, IMAGING_RAWMODE_BGRa, 32, unpackBGRA},
+ {IMAGING_MODE_RGBa, IMAGING_RAWMODE_aRGB, 32, unpackARGB},
+ {IMAGING_MODE_RGBa, IMAGING_RAWMODE_aBGR, 32, unpackABGR},
/* true colour w. padding */
- {"RGBX", "RGB", 24, ImagingUnpackRGB},
- {"RGBX", "RGB;L", 24, unpackRGBL},
- {"RGBX", "RGB;16B", 48, unpackRGB16B},
- {"RGBX", "BGR", 24, ImagingUnpackBGR},
- {"RGBX", "RGB;15", 16, ImagingUnpackRGB15},
- {"RGBX", "BGR;15", 16, ImagingUnpackBGR15},
- {"RGBX", "RGB;4B", 16, ImagingUnpackRGB4B},
- {"RGBX", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */
- {"RGBX", "RGBX", 32, copy4},
- {"RGBX", "RGBXX", 40, copy4skip1},
- {"RGBX", "RGBXXX", 48, copy4skip2},
- {"RGBX", "RGBX;L", 32, unpackRGBAL},
- {"RGBX", "RGBX;16L", 64, unpackRGBA16L},
- {"RGBX", "RGBX;16B", 64, unpackRGBA16B},
- {"RGBX", "BGRX", 32, ImagingUnpackBGRX},
- {"RGBX", "XRGB", 32, ImagingUnpackXRGB},
- {"RGBX", "XBGR", 32, ImagingUnpackXBGR},
- {"RGBX", "YCC;P", 24, ImagingUnpackYCC},
- {"RGBX", "R", 8, band0},
- {"RGBX", "G", 8, band1},
- {"RGBX", "B", 8, band2},
- {"RGBX", "X", 8, band3},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGB, 24, ImagingUnpackRGB},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGB_L, 24, unpackRGBL},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGB_16B, 48, unpackRGB16B},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_BGR, 24, ImagingUnpackBGR},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGB_15, 16, ImagingUnpackRGB15},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_BGR_15, 16, ImagingUnpackBGR15},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGB_4B, 16, ImagingUnpackRGB4B},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_BGR_5, 16, ImagingUnpackBGR15}, /* compat */
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBX, 32, copy4},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBXX, 40, copy4skip1},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBXXX, 48, copy4skip2},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBX_L, 32, unpackRGBAL},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBX_16L, 64, unpackRGBA16L},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_RGBX_16B, 64, unpackRGBA16B},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_BGRX, 32, ImagingUnpackBGRX},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_XRGB, 32, ImagingUnpackXRGB},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_XBGR, 32, ImagingUnpackXBGR},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_YCC_P, 24, ImagingUnpackYCC},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_R, 8, band0},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_G, 8, band1},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_B, 8, band2},
+ {IMAGING_MODE_RGBX, IMAGING_RAWMODE_X, 8, band3},
/* colour separation */
- {"CMYK", "CMYK", 32, copy4},
- {"CMYK", "CMYKX", 40, copy4skip1},
- {"CMYK", "CMYKXX", 48, copy4skip2},
- {"CMYK", "CMYK;I", 32, unpackCMYKI},
- {"CMYK", "CMYK;L", 32, unpackRGBAL},
- {"CMYK", "CMYK;16L", 64, unpackRGBA16L},
- {"CMYK", "CMYK;16B", 64, unpackRGBA16B},
- {"CMYK", "C", 8, band0},
- {"CMYK", "M", 8, band1},
- {"CMYK", "Y", 8, band2},
- {"CMYK", "K", 8, band3},
- {"CMYK", "C;I", 8, band0I},
- {"CMYK", "M;I", 8, band1I},
- {"CMYK", "Y;I", 8, band2I},
- {"CMYK", "K;I", 8, band3I},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK, 32, copy4},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYKX, 40, copy4skip1},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYKXX, 48, copy4skip2},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK_I, 32, unpackCMYKI},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK_L, 32, unpackRGBAL},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK_16L, 64, unpackRGBA16L},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK_16B, 64, unpackRGBA16B},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_C, 8, band0},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_M, 8, band1},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_Y, 8, band2},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_K, 8, band3},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_C_I, 8, band0I},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_M_I, 8, band1I},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_Y_I, 8, band2I},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_K_I, 8, band3I},
#ifdef WORDS_BIGENDIAN
- {"CMYK", "CMYK;16N", 64, unpackRGBA16B},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK_16N, 64, unpackRGBA16B},
#else
- {"CMYK", "CMYK;16N", 64, unpackRGBA16L},
+ {IMAGING_MODE_CMYK, IMAGING_RAWMODE_CMYK_16N, 64, unpackRGBA16L},
#endif
/* video (YCbCr) */
- {"YCbCr", "YCbCr", 24, ImagingUnpackRGB},
- {"YCbCr", "YCbCr;L", 24, unpackRGBL},
- {"YCbCr", "YCbCrX", 32, copy4},
- {"YCbCr", "YCbCrK", 32, copy4},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_YCbCr, 24, ImagingUnpackRGB},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_YCbCr_L, 24, unpackRGBL},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_YCbCrX, 32, copy4},
+ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_YCbCrK, 32, copy4},
/* LAB Color */
- {"LAB", "LAB", 24, ImagingUnpackLAB},
- {"LAB", "L", 8, band0},
- {"LAB", "A", 8, band1},
- {"LAB", "B", 8, band2},
+ {IMAGING_MODE_LAB, IMAGING_RAWMODE_LAB, 24, ImagingUnpackLAB},
+ {IMAGING_MODE_LAB, IMAGING_RAWMODE_L, 8, band0},
+ {IMAGING_MODE_LAB, IMAGING_RAWMODE_A, 8, band1},
+ {IMAGING_MODE_LAB, IMAGING_RAWMODE_B, 8, band2},
/* HSV Color */
- {"HSV", "HSV", 24, ImagingUnpackRGB},
- {"HSV", "H", 8, band0},
- {"HSV", "S", 8, band1},
- {"HSV", "V", 8, band2},
+ {IMAGING_MODE_HSV, IMAGING_RAWMODE_HSV, 24, ImagingUnpackRGB},
+ {IMAGING_MODE_HSV, IMAGING_RAWMODE_H, 8, band0},
+ {IMAGING_MODE_HSV, IMAGING_RAWMODE_S, 8, band1},
+ {IMAGING_MODE_HSV, IMAGING_RAWMODE_V, 8, band2},
/* integer variations */
- {"I", "I", 32, copy4},
- {"I", "I;8", 8, unpackI8},
- {"I", "I;8S", 8, unpackI8S},
- {"I", "I;16", 16, unpackI16},
- {"I", "I;16S", 16, unpackI16S},
- {"I", "I;16B", 16, unpackI16B},
- {"I", "I;16BS", 16, unpackI16BS},
- {"I", "I;16N", 16, unpackI16N},
- {"I", "I;16NS", 16, unpackI16NS},
- {"I", "I;32", 32, unpackI32},
- {"I", "I;32S", 32, unpackI32S},
- {"I", "I;32B", 32, unpackI32B},
- {"I", "I;32BS", 32, unpackI32BS},
- {"I", "I;32N", 32, unpackI32N},
- {"I", "I;32NS", 32, unpackI32NS},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I, 32, copy4},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_8, 8, unpackI8},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_8S, 8, unpackI8S},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_16, 16, unpackI16},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_16S, 16, unpackI16S},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_16B, 16, unpackI16B},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_16BS, 16, unpackI16BS},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_16N, 16, unpackI16N},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_16NS, 16, unpackI16NS},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_32, 32, unpackI32},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_32S, 32, unpackI32S},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_32B, 32, unpackI32B},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_32BS, 32, unpackI32BS},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_32N, 32, unpackI32N},
+ {IMAGING_MODE_I, IMAGING_RAWMODE_I_32NS, 32, unpackI32NS},
/* floating point variations */
- {"F", "F", 32, copy4},
- {"F", "F;8", 8, unpackF8},
- {"F", "F;8S", 8, unpackF8S},
- {"F", "F;16", 16, unpackF16},
- {"F", "F;16S", 16, unpackF16S},
- {"F", "F;16B", 16, unpackF16B},
- {"F", "F;16BS", 16, unpackF16BS},
- {"F", "F;16N", 16, unpackF16N},
- {"F", "F;16NS", 16, unpackF16NS},
- {"F", "F;32", 32, unpackF32},
- {"F", "F;32S", 32, unpackF32S},
- {"F", "F;32B", 32, unpackF32B},
- {"F", "F;32BS", 32, unpackF32BS},
- {"F", "F;32N", 32, unpackF32N},
- {"F", "F;32NS", 32, unpackF32NS},
- {"F", "F;32F", 32, unpackF32F},
- {"F", "F;32BF", 32, unpackF32BF},
- {"F", "F;32NF", 32, unpackF32NF},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F, 32, copy4},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_8, 8, unpackF8},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_8S, 8, unpackF8S},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_16, 16, unpackF16},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_16S, 16, unpackF16S},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_16B, 16, unpackF16B},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_16BS, 16, unpackF16BS},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_16N, 16, unpackF16N},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_16NS, 16, unpackF16NS},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32, 32, unpackF32},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32S, 32, unpackF32S},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32B, 32, unpackF32B},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32BS, 32, unpackF32BS},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32N, 32, unpackF32N},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32NS, 32, unpackF32NS},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32F, 32, unpackF32F},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32BF, 32, unpackF32BF},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_32NF, 32, unpackF32NF},
#ifdef FLOAT64
- {"F", "F;64F", 64, unpackF64F},
- {"F", "F;64BF", 64, unpackF64BF},
- {"F", "F;64NF", 64, unpackF64NF},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_64F, 64, unpackF64F},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_64BF, 64, unpackF64BF},
+ {IMAGING_MODE_F, IMAGING_RAWMODE_F_64NF, 64, unpackF64NF},
#endif
/* storage modes */
- {"I;16", "I;16", 16, copy2},
- {"I;16B", "I;16B", 16, copy2},
- {"I;16L", "I;16L", 16, copy2},
- {"I;16N", "I;16N", 16, copy2},
+ {IMAGING_MODE_I_16, IMAGING_RAWMODE_I_16, 16, copy2},
+ {IMAGING_MODE_I_16B, IMAGING_RAWMODE_I_16B, 16, copy2},
+ {IMAGING_MODE_I_16L, IMAGING_RAWMODE_I_16L, 16, copy2},
+ {IMAGING_MODE_I_16N, IMAGING_RAWMODE_I_16N, 16, copy2},
- {"I;16", "I;16B", 16, unpackI16B_I16},
- {"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian.
- {"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian.
- {"I;16B", "I;16N", 16, unpackI16N_I16B},
+ {IMAGING_MODE_I_16, IMAGING_RAWMODE_I_16B, 16, unpackI16B_I16},
+ {IMAGING_MODE_I_16B, IMAGING_RAWMODE_I_16N, 16, unpackI16N_I16B},
+ {IMAGING_MODE_I_16, IMAGING_RAWMODE_I_16R, 16, unpackI16R_I16},
- {"I;16", "I;16R", 16, unpackI16R_I16},
+ // LibTiff native->image endian.
+ {IMAGING_MODE_I_16, IMAGING_RAWMODE_I_16N, 16, unpackI16N_I16},
+ {IMAGING_MODE_I_16L, IMAGING_RAWMODE_I_16N, 16, unpackI16N_I16},
- {"I;16", "I;12", 12, unpackI12_I16}, // 12 bit Tiffs stored in 16bits.
-
- {NULL} /* sentinel */
+ // 12 bit Tiffs stored in 16bits.
+ {IMAGING_MODE_I_16, IMAGING_RAWMODE_I_12, 12, unpackI12_I16}
};
ImagingShuffler
-ImagingFindUnpacker(const char *mode, const char *rawmode, int *bits_out) {
- int i;
-
- /* find a suitable pixel unpacker */
- for (i = 0; unpackers[i].rawmode; i++) {
- if (strcmp(unpackers[i].mode, mode) == 0 &&
- strcmp(unpackers[i].rawmode, rawmode) == 0) {
+ImagingFindUnpacker(const ModeID mode, const RawModeID rawmode, int *bits_out) {
+ for (size_t i = 0; i < sizeof(unpackers) / sizeof(*unpackers); i++) {
+ if (unpackers[i].mode == mode && unpackers[i].rawmode == rawmode) {
if (bits_out) {
*bits_out = unpackers[i].bits;
}
diff --git a/src/map.c b/src/map.c
index c66702981..6f66b0cc5 100644
--- a/src/map.c
+++ b/src/map.c
@@ -55,7 +55,7 @@ PyImaging_MapBuffer(PyObject *self, PyObject *args) {
PyObject *target;
Py_buffer view;
- char *mode;
+ char *mode_name;
char *codec;
Py_ssize_t offset;
int xsize, ysize;
@@ -70,7 +70,7 @@ PyImaging_MapBuffer(PyObject *self, PyObject *args) {
&ysize,
&codec,
&offset,
- &mode,
+ &mode_name,
&stride,
&ystep
)) {
@@ -82,10 +82,12 @@ PyImaging_MapBuffer(PyObject *self, PyObject *args) {
return NULL;
}
+ const ModeID mode = findModeID(mode_name);
+
if (stride <= 0) {
- if (!strcmp(mode, "L") || !strcmp(mode, "P")) {
+ if (mode == IMAGING_MODE_L || mode == IMAGING_MODE_P) {
stride = xsize;
- } else if (!strncmp(mode, "I;16", 4)) {
+ } else if (isModeI16(mode)) {
stride = xsize * 2;
} else {
stride = xsize * 4;
@@ -137,6 +139,7 @@ PyImaging_MapBuffer(PyObject *self, PyObject *args) {
}
}
+ im->read_only = view.readonly;
im->destroy = mapping_destroy_buffer;
Py_INCREF(target);
diff --git a/src/thirdparty/raqm/COPYING b/src/thirdparty/raqm/COPYING
index 97e2489b7..964318a8a 100644
--- a/src/thirdparty/raqm/COPYING
+++ b/src/thirdparty/raqm/COPYING
@@ -1,7 +1,7 @@
The MIT License (MIT)
Copyright © 2015 Information Technology Authority (ITA)
-Copyright © 2016-2023 Khaled Hosny
+Copyright © 2016-2025 Khaled Hosny
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/src/thirdparty/raqm/NEWS b/src/thirdparty/raqm/NEWS
index e8bf32e0b..fb432cffb 100644
--- a/src/thirdparty/raqm/NEWS
+++ b/src/thirdparty/raqm/NEWS
@@ -1,3 +1,19 @@
+Overview of changes leading to 0.10.3
+Tuesday, August 5, 2025
+====================================
+
+Fix raqm_set_text_utf8/utf16 reading beyond len for multibyte.
+
+Support building against SheenBidi 2.9.
+
+Fix deprecation warning with latest HarfBuzz.
+
+Overview of changes leading to 0.10.2
+Sunday, September 22, 2024
+====================================
+
+Fix Unicode codepoint conversion from UTF-16.
+
Overview of changes leading to 0.10.1
Wednesday, April 12, 2023
====================================
diff --git a/src/thirdparty/raqm/raqm-version.h b/src/thirdparty/raqm/raqm-version.h
index 62d2d2064..f2dd61cf6 100644
--- a/src/thirdparty/raqm/raqm-version.h
+++ b/src/thirdparty/raqm/raqm-version.h
@@ -33,9 +33,9 @@
#define RAQM_VERSION_MAJOR 0
#define RAQM_VERSION_MINOR 10
-#define RAQM_VERSION_MICRO 1
+#define RAQM_VERSION_MICRO 3
-#define RAQM_VERSION_STRING "0.10.1"
+#define RAQM_VERSION_STRING "0.10.3"
#define RAQM_VERSION_ATLEAST(major,minor,micro) \
((major)*10000+(minor)*100+(micro) <= \
diff --git a/src/thirdparty/raqm/raqm.c b/src/thirdparty/raqm/raqm.c
index 2b331e1af..9ecc5cac8 100644
--- a/src/thirdparty/raqm/raqm.c
+++ b/src/thirdparty/raqm/raqm.c
@@ -30,7 +30,11 @@
#include
#ifdef RAQM_SHEENBIDI
+#ifdef RAQM_SHEENBIDI_GT_2_9
+#include
+#else
#include
+#endif
#else
#ifdef HAVE_FRIBIDI_SYSTEM
#include
@@ -546,34 +550,32 @@ raqm_set_text (raqm_t *rq,
return true;
}
-static void *
-_raqm_get_utf8_codepoint (const void *str,
+static const char *
+_raqm_get_utf8_codepoint (const char *str,
uint32_t *out_codepoint)
{
- const char *s = (const char *)str;
-
- if (0xf0 == (0xf8 & s[0]))
+ if (0xf0 == (0xf8 & str[0]))
{
- *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) | ((0x3f & s[2]) << 6) | (0x3f & s[3]);
- s += 4;
+ *out_codepoint = ((0x07 & str[0]) << 18) | ((0x3f & str[1]) << 12) | ((0x3f & str[2]) << 6) | (0x3f & str[3]);
+ str += 4;
}
- else if (0xe0 == (0xf0 & s[0]))
+ else if (0xe0 == (0xf0 & str[0]))
{
- *out_codepoint = ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]);
- s += 3;
+ *out_codepoint = ((0x0f & str[0]) << 12) | ((0x3f & str[1]) << 6) | (0x3f & str[2]);
+ str += 3;
}
- else if (0xc0 == (0xe0 & s[0]))
+ else if (0xc0 == (0xe0 & str[0]))
{
- *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]);
- s += 2;
+ *out_codepoint = ((0x1f & str[0]) << 6) | (0x3f & str[1]);
+ str += 2;
}
else
{
- *out_codepoint = s[0];
- s += 1;
+ *out_codepoint = str[0];
+ str += 1;
}
- return (void *)s;
+ return str;
}
static size_t
@@ -585,42 +587,41 @@ _raqm_u8_to_u32 (const char *text, size_t len, uint32_t *unicode)
while ((*in_utf8 != '\0') && (in_len < len))
{
- in_utf8 = _raqm_get_utf8_codepoint (in_utf8, out_utf32);
+ const char *out_utf8 = _raqm_get_utf8_codepoint (in_utf8, out_utf32);
+ in_len += out_utf8 - in_utf8;
+ in_utf8 = out_utf8;
++out_utf32;
- ++in_len;
}
return (out_utf32 - unicode);
}
-static void *
-_raqm_get_utf16_codepoint (const void *str,
- uint32_t *out_codepoint)
+static const uint16_t *
+_raqm_get_utf16_codepoint (const uint16_t *str,
+ uint32_t *out_codepoint)
{
- const uint16_t *s = (const uint16_t *)str;
-
- if (s[0] > 0xD800 && s[0] < 0xDBFF)
+ if (str[0] >= 0xD800 && str[0] <= 0xDBFF)
{
- if (s[1] > 0xDC00 && s[1] < 0xDFFF)
+ if (str[1] >= 0xDC00 && str[1] <= 0xDFFF)
{
- uint32_t X = ((s[0] & ((1 << 6) -1)) << 10) | (s[1] & ((1 << 10) -1));
- uint32_t W = (s[0] >> 6) & ((1 << 5) - 1);
+ uint32_t X = ((str[0] & ((1 << 6) -1)) << 10) | (str[1] & ((1 << 10) -1));
+ uint32_t W = (str[0] >> 6) & ((1 << 5) - 1);
*out_codepoint = (W+1) << 16 | X;
- s += 2;
+ str += 2;
}
else
{
/* A single high surrogate, this is an error. */
- *out_codepoint = s[0];
- s += 1;
+ *out_codepoint = str[0];
+ str += 1;
}
}
else
{
- *out_codepoint = s[0];
- s += 1;
+ *out_codepoint = str[0];
+ str += 1;
}
- return (void *)s;
+ return str;
}
static size_t
@@ -632,9 +633,10 @@ _raqm_u16_to_u32 (const uint16_t *text, size_t len, uint32_t *unicode)
while ((*in_utf16 != '\0') && (in_len < len))
{
- in_utf16 = _raqm_get_utf16_codepoint (in_utf16, out_utf32);
+ const uint16_t *out_utf16 = _raqm_get_utf16_codepoint (in_utf16, out_utf32);
+ in_len += (out_utf16 - in_utf16);
+ in_utf16 = out_utf16;
++out_utf32;
- ++in_len;
}
return (out_utf32 - unicode);
@@ -1114,12 +1116,12 @@ _raqm_set_spacing (raqm_t *rq,
{
if (_raqm_allowed_grapheme_boundary (rq->text[i], rq->text[i+1]))
{
- /* CSS word seperators, word spacing is only applied on these.*/
+ /* CSS word separators, word spacing is only applied on these.*/
if (rq->text[i] == 0x0020 || /* Space */
rq->text[i] == 0x00A0 || /* No Break Space */
rq->text[i] == 0x1361 || /* Ethiopic Word Space */
- rq->text[i] == 0x10100 || /* Aegean Word Seperator Line */
- rq->text[i] == 0x10101 || /* Aegean Word Seperator Dot */
+ rq->text[i] == 0x10100 || /* Aegean Word Separator Line */
+ rq->text[i] == 0x10101 || /* Aegean Word Separator Dot */
rq->text[i] == 0x1039F || /* Ugaric Word Divider */
rq->text[i] == 0x1091F) /* Phoenician Word Separator */
{
@@ -2167,6 +2169,10 @@ _raqm_ft_transform (int *x,
*y = vector.y;
}
+#if !HB_VERSION_ATLEAST (10, 4, 0)
+# define hb_ft_font_get_ft_face hb_ft_font_get_face
+#endif
+
static bool
_raqm_shape (raqm_t *rq)
{
@@ -2199,7 +2205,7 @@ _raqm_shape (raqm_t *rq)
hb_glyph_position_t *pos;
unsigned int len;
- FT_Get_Transform (hb_ft_font_get_face (run->font), &matrix, NULL);
+ FT_Get_Transform (hb_ft_font_get_ft_face (run->font), &matrix, NULL);
pos = hb_buffer_get_glyph_positions (run->buffer, &len);
info = hb_buffer_get_glyph_infos (run->buffer, &len);
diff --git a/tox.ini b/tox.ini
index 4065245ee..d58fd67b6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,7 +3,7 @@ requires =
tox>=4.2
env_list =
lint
- py{py3, 313, 312, 311, 310, 39}
+ py{py3, 314, 313, 312, 311, 310}
[testenv]
deps =
@@ -29,7 +29,5 @@ commands =
skip_install = true
deps =
-r .ci/requirements-mypy.txt
-extras =
- typing
commands =
- mypy conftest.py selftest.py setup.py docs src winbuild Tests {posargs}
+ mypy conftest.py selftest.py setup.py checks docs src winbuild Tests {posargs}
diff --git a/wheels/dependency_licenses/AOM.txt b/wheels/dependency_licenses/AOM.txt
new file mode 100644
index 000000000..3a2e46c26
--- /dev/null
+++ b/wheels/dependency_licenses/AOM.txt
@@ -0,0 +1,26 @@
+Copyright (c) 2016, Alliance for Open Media. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/wheels/dependency_licenses/DAV1D.txt b/wheels/dependency_licenses/DAV1D.txt
new file mode 100644
index 000000000..875b138ec
--- /dev/null
+++ b/wheels/dependency_licenses/DAV1D.txt
@@ -0,0 +1,23 @@
+Copyright © 2018-2019, VideoLAN and dav1d authors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/wheels/dependency_licenses/FREETYPE2.txt b/wheels/dependency_licenses/FREETYPE2.txt
index 93efc6126..8f2345992 100644
--- a/wheels/dependency_licenses/FREETYPE2.txt
+++ b/wheels/dependency_licenses/FREETYPE2.txt
@@ -211,351 +211,6 @@ Legal Terms
--- end of FTL.TXT ---
---------------------------------------------------------------------------
-
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- , 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
-
---------------------------------------------------------------------------
-
The following license details are part of `src/bdf/README`:
```
diff --git a/wheels/dependency_licenses/LIBAVIF.txt b/wheels/dependency_licenses/LIBAVIF.txt
new file mode 100644
index 000000000..350eb9d15
--- /dev/null
+++ b/wheels/dependency_licenses/LIBAVIF.txt
@@ -0,0 +1,387 @@
+Copyright 2019 Joe Drago. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------------------------------------------------------------------------------
+
+Files: src/obu.c
+
+Copyright © 2018-2019, VideoLAN and dav1d authors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------------------------------------------------------------------------------
+
+Files: third_party/iccjpeg/*
+
+In plain English:
+
+1. We don't promise that this software works. (But if you find any bugs,
+ please let us know!)
+2. You can use this software for whatever you want. You don't have to pay us.
+3. You may not pretend that you wrote this software. If you use it in a
+ program, you must acknowledge somewhere in your documentation that
+ you've used the IJG code.
+
+In legalese:
+
+The authors make NO WARRANTY or representation, either express or implied,
+with respect to this software, its quality, accuracy, merchantability, or
+fitness for a particular purpose. This software is provided "AS IS", and you,
+its user, assume the entire risk as to its quality and accuracy.
+
+This software is copyright (C) 1991-2013, Thomas G. Lane, Guido Vollbeding.
+All Rights Reserved except as specified below.
+
+Permission is hereby granted to use, copy, modify, and distribute this
+software (or portions thereof) for any purpose, without fee, subject to these
+conditions:
+(1) If any part of the source code for this software is distributed, then this
+README file must be included, with this copyright and no-warranty notice
+unaltered; and any additions, deletions, or changes to the original files
+must be clearly indicated in accompanying documentation.
+(2) If only executable code is distributed, then the accompanying
+documentation must state that "this software is based in part on the work of
+the Independent JPEG Group".
+(3) Permission for use of this software is granted only if the user accepts
+full responsibility for any undesirable consequences; the authors accept
+NO LIABILITY for damages of any kind.
+
+These conditions apply to any software derived from or based on the IJG code,
+not just to the unmodified library. If you use our work, you ought to
+acknowledge us.
+
+Permission is NOT granted for the use of any IJG author's name or company name
+in advertising or publicity relating to this software or products derived from
+it. This software may be referred to only as "the Independent JPEG Group's
+software".
+
+We specifically permit and encourage the use of this software as the basis of
+commercial products, provided that all warranty or liability claims are
+assumed by the product vendor.
+
+
+The Unix configuration script "configure" was produced with GNU Autoconf.
+It is copyright by the Free Software Foundation but is freely distributable.
+The same holds for its supporting scripts (config.guess, config.sub,
+ltmain.sh). Another support script, install-sh, is copyright by X Consortium
+but is also freely distributable.
+
+The IJG distribution formerly included code to read and write GIF files.
+To avoid entanglement with the Unisys LZW patent, GIF reading support has
+been removed altogether, and the GIF writer has been simplified to produce
+"uncompressed GIFs". This technique does not use the LZW algorithm; the
+resulting GIF files are larger than usual, but are readable by all standard
+GIF decoders.
+
+We are required to state that
+ "The Graphics Interchange Format(c) is the Copyright property of
+ CompuServe Incorporated. GIF(sm) is a Service Mark property of
+ CompuServe Incorporated."
+
+------------------------------------------------------------------------------
+
+Files: contrib/gdk-pixbuf/*
+
+Copyright 2020 Emmanuel Gil Peyrot. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------------------------------------------------------------------------------
+
+Files: android_jni/gradlew*
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+------------------------------------------------------------------------------
+
+Files: third_party/libyuv/*
+
+Copyright 2011 The LibYuv Project Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name of Google nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/wheels/dependency_licenses/LIBYUV.txt b/wheels/dependency_licenses/LIBYUV.txt
new file mode 100644
index 000000000..c911747a6
--- /dev/null
+++ b/wheels/dependency_licenses/LIBYUV.txt
@@ -0,0 +1,29 @@
+Copyright 2011 The LibYuv Project Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name of Google nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/wheels/dependency_licenses/ZSTD.txt b/wheels/dependency_licenses/ZSTD.txt
new file mode 100644
index 000000000..75800288c
--- /dev/null
+++ b/wheels/dependency_licenses/ZSTD.txt
@@ -0,0 +1,30 @@
+BSD License
+
+For Zstandard software
+
+Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name Facebook, nor Meta, nor the names of its contributors may
+ be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/wheels/multibuild b/wheels/multibuild
index 42d761728..647393271 160000
--- a/wheels/multibuild
+++ b/wheels/multibuild
@@ -1 +1 @@
-Subproject commit 42d761728d141d8462cd9943f4329f12fe62b155
+Subproject commit 64739327166fcad1fa41ad9b23fa910fa244c84f
diff --git a/winbuild/README.md b/winbuild/README.md
index c474f12ce..db71f094e 100644
--- a/winbuild/README.md
+++ b/winbuild/README.md
@@ -11,13 +11,12 @@ For more extensive info, see the [Windows build instructions](build.rst).
* Requires Microsoft Visual Studio 2017 or newer with C++ component.
* Requires NASM for libjpeg-turbo, a required dependency when using this script.
* Requires CMake 3.15 or newer (available as Visual Studio component).
-* Tested on Windows Server 2022 with Visual Studio 2022 Enterprise and Windows Server
- 2019 with Visual Studio 2019 Enterprise (GitHub Actions).
+* Tested on Windows Server 2022 with Visual Studio 2022 Enterprise (GitHub Actions).
Here's an example script to build on Windows:
```
-set PYTHON=C:\Python39\bin
+set PYTHON=C:\Python310\bin
cd /D C:\Pillow\winbuild
%PYTHON%\python.exe build_prepare.py -v --depends=C:\pillow-depends
build\build_dep_all.cmd
@@ -25,6 +24,6 @@ cd ..
%PYTHON%\python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor .
path C:\Pillow\winbuild\build\bin;%PATH%
%PYTHON%\python.exe selftest.py
-%PYTHON%\python.exe -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests
+%PYTHON%\python.exe -m pytest -vv -x --cov PIL --cov Tests --cov-report term --cov-report xml Tests
%PYTHON%\python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor .
```
diff --git a/winbuild/build.rst b/winbuild/build.rst
index aae78ce12..23b26c422 100644
--- a/winbuild/build.rst
+++ b/winbuild/build.rst
@@ -61,6 +61,7 @@ Run ``build_prepare.py`` to configure the build::
--no-imagequant skip GPL-licensed optional dependency libimagequant
--no-fribidi, --no-raqm
skip LGPL-licensed optional dependency FriBiDi
+ --no-avif skip optional dependency libavif
Arguments can also be supplied using the environment variables PILLOW_BUILD,
PILLOW_DEPS, ARCHITECTURE. See winbuild\build.rst for more information.
@@ -114,7 +115,7 @@ Example
Here's an example script to build on Windows::
- set PYTHON=C:\Python39\bin
+ set PYTHON=C:\Python310\bin
cd /D C:\Pillow\winbuild
%PYTHON%\python.exe build_prepare.py -v --depends C:\pillow-depends
build\build_dep_all.cmd
@@ -123,5 +124,5 @@ Here's an example script to build on Windows::
%PYTHON%\python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor .
path C:\Pillow\winbuild\build\bin;%PATH%
%PYTHON%\python.exe selftest.py
- %PYTHON%\python.exe -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests
+ %PYTHON%\python.exe -m pytest -vv -x --cov PIL --cov Tests --cov-report term --cov-report xml Tests
%PYTHON%\python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor .
diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py
index ea3d99394..186a80cca 100644
--- a/winbuild/build_prepare.py
+++ b/winbuild/build_prepare.py
@@ -57,7 +57,10 @@ def cmd_nmake(
def cmds_cmake(
- target: str | tuple[str, ...] | list[str], *params: str, build_dir: str = "."
+ target: str | tuple[str, ...] | list[str],
+ *params: str,
+ build_dir: str = ".",
+ build_type: str = "Release",
) -> list[str]:
if not isinstance(target, str):
target = " ".join(target)
@@ -66,7 +69,7 @@ def cmds_cmake(
" ".join(
[
"{cmake}",
- "-DCMAKE_BUILD_TYPE=Release",
+ f"-DCMAKE_BUILD_TYPE={build_type}",
"-DCMAKE_VERBOSE_MAKEFILE=ON",
"-DCMAKE_RULE_MESSAGES:BOOL=OFF", # for NMake
"-DCMAKE_C_COMPILER=cl.exe", # for Ninja
@@ -111,18 +114,19 @@ ARCHITECTURES = {
V = {
"BROTLI": "1.1.0",
- "FREETYPE": "2.13.3",
+ "FREETYPE": "2.14.1",
"FRIBIDI": "1.0.16",
- "HARFBUZZ": "10.4.0",
- "JPEGTURBO": "3.1.0",
+ "HARFBUZZ": "12.1.0",
+ "JPEGTURBO": "3.1.2",
"LCMS2": "2.17",
- "LIBIMAGEQUANT": "4.3.4",
- "LIBPNG": "1.6.47",
- "LIBWEBP": "1.5.0",
- "OPENJPEG": "2.5.3",
- "TIFF": "4.7.0",
- "XZ": "5.6.4",
- "ZLIBNG": "2.2.4",
+ "LIBAVIF": "1.3.0",
+ "LIBIMAGEQUANT": "4.4.0",
+ "LIBPNG": "1.6.50",
+ "LIBWEBP": "1.6.0",
+ "OPENJPEG": "2.5.4",
+ "TIFF": "4.7.1",
+ "XZ": "5.8.1",
+ "ZLIBNG": "2.2.5",
}
V["LIBPNG_XY"] = "".join(V["LIBPNG"].split(".")[:2])
@@ -145,18 +149,17 @@ DEPS: dict[str, dict[str, Any]] = {
},
"build": [
*cmds_cmake(
- ("jpeg-static", "cjpeg-static", "djpeg-static"),
+ ("jpeg-static", "djpeg-static"),
"-DENABLE_SHARED:BOOL=FALSE",
"-DWITH_JPEG8:BOOL=TRUE",
"-DWITH_CRT_DLL:BOOL=TRUE",
),
cmd_copy("jpeg-static.lib", "libjpeg.lib"),
- cmd_copy("cjpeg-static.exe", "cjpeg.exe"),
cmd_copy("djpeg-static.exe", "djpeg.exe"),
],
"headers": ["jconfig.h", r"src\j*.h"],
"libs": ["libjpeg.lib"],
- "bins": ["cjpeg.exe", "djpeg.exe"],
+ "bins": ["djpeg.exe"],
},
"zlib": {
"url": f"https://github.com/zlib-ng/zlib-ng/archive/refs/tags/{V['ZLIBNG']}.tar.gz",
@@ -180,7 +183,11 @@ DEPS: dict[str, dict[str, Any]] = {
"filename": f"xz-{V['XZ']}.tar.gz",
"license": "COPYING",
"build": [
- *cmds_cmake("liblzma", "-DBUILD_SHARED_LIBS:BOOL=OFF"),
+ *cmds_cmake(
+ "liblzma",
+ "-DBUILD_SHARED_LIBS:BOOL=OFF"
+ + (" -DXZ_CLMUL_CRC:BOOL=OFF" if struct.calcsize("P") == 4 else ""),
+ ),
cmd_mkdir(r"{inc_dir}\lzma"),
cmd_copy(r"src\liblzma\api\lzma\*.h", r"{inc_dir}\lzma"),
],
@@ -221,12 +228,6 @@ DEPS: dict[str, dict[str, Any]] = {
# link against libwebp.lib
"#ifdef WEBP_SUPPORT": '#ifdef WEBP_SUPPORT\n#pragma comment(lib, "libwebp.lib")', # noqa: E501
},
- r"test\CMakeLists.txt": {
- "add_executable(test_write_read_tags ../placeholder.h)": "",
- "target_sources(test_write_read_tags PRIVATE test_write_read_tags.c)": "", # noqa: E501
- "target_link_libraries(test_write_read_tags PRIVATE tiff)": "",
- "list(APPEND simple_tests test_write_read_tags)": "",
- },
},
"build": [
*cmds_cmake(
@@ -347,8 +348,8 @@ DEPS: dict[str, dict[str, Any]] = {
"libs": [r"..\target\release\imagequant_sys.lib"],
},
"harfbuzz": {
- "url": f"https://github.com/harfbuzz/harfbuzz/archive/{V['HARFBUZZ']}.zip",
- "filename": f"harfbuzz-{V['HARFBUZZ']}.zip",
+ "url": f"https://github.com/harfbuzz/harfbuzz/releases/download/{V['HARFBUZZ']}/FILENAME",
+ "filename": f"harfbuzz-{V['HARFBUZZ']}.tar.xz",
"license": "COPYING",
"build": [
*cmds_cmake(
@@ -378,6 +379,29 @@ DEPS: dict[str, dict[str, Any]] = {
],
"bins": [r"*.dll"],
},
+ "libavif": {
+ "url": f"https://github.com/AOMediaCodec/libavif/archive/v{V['LIBAVIF']}.tar.gz",
+ "filename": f"libavif-{V['LIBAVIF']}.tar.gz",
+ "license": "LICENSE",
+ "build": [
+ "rustup update",
+ f"{sys.executable} -m pip install meson",
+ *cmds_cmake(
+ "avif_static",
+ "-DBUILD_SHARED_LIBS=OFF",
+ "-DAVIF_LIBSHARPYUV=LOCAL",
+ "-DAVIF_LIBYUV=LOCAL",
+ "-DAVIF_CODEC_AOM=LOCAL",
+ "-DCONFIG_AV1_HIGHBITDEPTH=0",
+ "-DAVIF_CODEC_AOM_DECODE=OFF",
+ "-DAVIF_CODEC_DAV1D=LOCAL",
+ "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON",
+ build_type="MinSizeRel",
+ ),
+ cmd_xcopy("include", "{inc_dir}"),
+ ],
+ "libs": ["avif.lib"],
+ },
}
@@ -491,8 +515,8 @@ def extract_dep(url: str, filename: str, prefs: dict[str, str]) -> None:
msg = "Attempted Path Traversal in Zip File"
raise RuntimeError(msg)
zf.extractall(sources_dir)
- elif filename.endswith((".tar.gz", ".tgz")):
- with tarfile.open(file, "r:gz") as tgz:
+ elif filename.endswith((".tar.gz", ".tar.xz")):
+ with tarfile.open(file, "r:xz" if filename.endswith(".xz") else "r:gz") as tgz:
for member in tgz.getnames():
member_abspath = os.path.abspath(os.path.join(sources_dir, member))
member_prefix = os.path.commonpath([sources_dir_abs, member_abspath])
@@ -683,6 +707,11 @@ def main() -> None:
action="store_true",
help="skip LGPL-licensed optional dependency FriBiDi",
)
+ parser.add_argument(
+ "--no-avif",
+ action="store_true",
+ help="skip optional dependency libavif",
+ )
args = parser.parse_args()
arch_prefs = ARCHITECTURES[args.architecture]
@@ -723,6 +752,8 @@ def main() -> None:
disabled += ["libimagequant"]
if args.no_fribidi:
disabled += ["fribidi"]
+ if args.no_avif or args.architecture == "ARM64":
+ disabled += ["libavif"]
prefs = {
"architecture": args.architecture,
@@ -746,7 +777,7 @@ def main() -> None:
for k, v in DEPS.items():
if "dir" not in v:
- v["dir"] = re.sub(r"\.(tar\.gz|zip)", "", v["filename"])
+ v["dir"] = re.sub(r"\.(tar\.gz|tar\.xz|zip)", "", v["filename"])
prefs[f"dir_{k}"] = os.path.join(sources_dir, v["dir"])
print()