mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 16:07:30 +03:00 
			
		
		
		
	Merge pull request #7913 from nulano/types-cms2
This commit is contained in:
		
						commit
						6c55ab22d7
					
				|  | @ -4,13 +4,14 @@ import datetime | |||
| import os | ||||
| import re | ||||
| import shutil | ||||
| import sys | ||||
| from io import BytesIO | ||||
| from pathlib import Path | ||||
| from typing import Any | ||||
| 
 | ||||
| import pytest | ||||
| 
 | ||||
| from PIL import Image, ImageMode, features | ||||
| from PIL import Image, ImageMode, ImageWin, features | ||||
| 
 | ||||
| from .helper import ( | ||||
|     assert_image, | ||||
|  | @ -18,6 +19,7 @@ from .helper import ( | |||
|     assert_image_similar, | ||||
|     assert_image_similar_tofile, | ||||
|     hopper, | ||||
|     is_pypy, | ||||
| ) | ||||
| 
 | ||||
| try: | ||||
|  | @ -213,6 +215,10 @@ def test_display_profile() -> None: | |||
|     # try fetching the profile for the current display device | ||||
|     ImageCms.get_display_profile() | ||||
| 
 | ||||
|     if sys.platform == "win32": | ||||
|         ImageCms.get_display_profile(ImageWin.HDC(0)) | ||||
|         ImageCms.get_display_profile(ImageWin.HWND(0)) | ||||
| 
 | ||||
| 
 | ||||
| def test_lab_color_profile() -> None: | ||||
|     ImageCms.createProfile("LAB", 5000) | ||||
|  | @ -496,16 +502,34 @@ def test_non_ascii_path(tmp_path: Path) -> None: | |||
| 
 | ||||
| 
 | ||||
| def test_profile_typesafety() -> None: | ||||
|     """Profile init type safety | ||||
| 
 | ||||
|     prepatch, these would segfault, postpatch they should emit a typeerror | ||||
|     """ | ||||
| 
 | ||||
|     # does not segfault | ||||
|     with pytest.raises(TypeError, match="Invalid type for Profile"): | ||||
|         ImageCms.ImageCmsProfile(0).tobytes() | ||||
|     with pytest.raises(TypeError, match="Invalid type for Profile"): | ||||
|         ImageCms.ImageCmsProfile(1).tobytes() | ||||
| 
 | ||||
|     # also check core function | ||||
|     with pytest.raises(TypeError): | ||||
|         ImageCms.core.profile_tobytes(0) | ||||
|     with pytest.raises(TypeError): | ||||
|         ImageCms.core.profile_tobytes(1) | ||||
| 
 | ||||
|     if not is_pypy(): | ||||
|         # core profile should not be directly instantiable | ||||
|         with pytest.raises(TypeError): | ||||
|             ImageCms.core.CmsProfile() | ||||
|         with pytest.raises(TypeError): | ||||
|             ImageCms.core.CmsProfile(0) | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.skipif(is_pypy(), reason="fails on PyPy") | ||||
| def test_transform_typesafety() -> None: | ||||
|     # core transform should not be directly instantiable | ||||
|     with pytest.raises(TypeError): | ||||
|         ImageCms.core.CmsProfile() | ||||
|     with pytest.raises(TypeError): | ||||
|         ImageCms.core.CmsProfile(0) | ||||
| 
 | ||||
| 
 | ||||
| def assert_aux_channel_preserved( | ||||
|     mode: str, transform_in_place: bool, preserved_channel: str | ||||
|  |  | |||
|  | @ -121,7 +121,12 @@ nitpicky = True | |||
| # generating warnings in “nitpicky mode”. Note that type should include the domain name | ||||
| # if present. Example entries would be ('py:func', 'int') or | ||||
| # ('envvar', 'LD_LIBRARY_PATH'). | ||||
| # nitpick_ignore = [] | ||||
| nitpick_ignore = [ | ||||
|     # Sphinx does not understand typing.Literal[-1] | ||||
|     # Will be fixed in a future version. | ||||
|     # https://github.com/sphinx-doc/sphinx/pull/11904 | ||||
|     ("py:obj", "typing.Literal[-1, 1]"), | ||||
| ] | ||||
| 
 | ||||
| 
 | ||||
| # -- Options for HTML output ---------------------------------------------- | ||||
|  |  | |||
|  | @ -73,7 +73,7 @@ can be easily displayed in a chromaticity diagram, for example). | |||
|     :canonical: PIL._imagingcms.CmsProfile | ||||
| 
 | ||||
|     .. py:attribute:: creation_date | ||||
|         :type: Optional[datetime.datetime] | ||||
|         :type: datetime.datetime | None | ||||
| 
 | ||||
|         Date and time this profile was first created (see 7.2.1 of ICC.1:2010). | ||||
| 
 | ||||
|  | @ -156,58 +156,58 @@ can be easily displayed in a chromaticity diagram, for example). | |||
|         not been calculated (see 7.2.18 of ICC.1:2010). | ||||
| 
 | ||||
|     .. py:attribute:: copyright | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         The text copyright information for the profile (see 9.2.21 of ICC.1:2010). | ||||
| 
 | ||||
|     .. py:attribute:: manufacturer | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         The (English) display string for the device manufacturer (see | ||||
|         9.2.22 of ICC.1:2010). | ||||
| 
 | ||||
|     .. py:attribute:: model | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         The (English) display string for the device model of the device | ||||
|         for which this profile is created (see 9.2.23 of ICC.1:2010). | ||||
| 
 | ||||
|     .. py:attribute:: profile_description | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         The (English) display string for the profile description (see | ||||
|         9.2.41 of ICC.1:2010). | ||||
| 
 | ||||
|     .. py:attribute:: target | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         The name of the registered characterization data set, or the | ||||
|         measurement data for a characterization target (see 9.2.14 of | ||||
|         ICC.1:2010). | ||||
| 
 | ||||
|     .. py:attribute:: red_colorant | ||||
|         :type: Optional[tuple[tuple[float]]] | ||||
|         :type: tuple[tuple[float, float, float], tuple[float, float, float]] | None | ||||
| 
 | ||||
|         The first column in the matrix used in matrix/TRC transforms (see 9.2.44 of ICC.1:2010). | ||||
| 
 | ||||
|         The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. | ||||
| 
 | ||||
|     .. py:attribute:: green_colorant | ||||
|         :type: Optional[tuple[tuple[float]]] | ||||
|         :type: tuple[tuple[float, float, float], tuple[float, float, float]] | None | ||||
| 
 | ||||
|         The second column in the matrix used in matrix/TRC transforms (see 9.2.30 of ICC.1:2010). | ||||
| 
 | ||||
|         The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. | ||||
| 
 | ||||
|     .. py:attribute:: blue_colorant | ||||
|         :type: Optional[tuple[tuple[float]]] | ||||
|         :type: tuple[tuple[float, float, float], tuple[float, float, float]] | None | ||||
| 
 | ||||
|         The third column in the matrix used in matrix/TRC transforms (see 9.2.4 of ICC.1:2010). | ||||
| 
 | ||||
|         The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. | ||||
| 
 | ||||
|     .. py:attribute:: luminance | ||||
|         :type: Optional[tuple[tuple[float]]] | ||||
|         :type: tuple[tuple[float, float, float], tuple[float, float, float]] | None | ||||
| 
 | ||||
|         The absolute luminance of emissive devices in candelas per square | ||||
|         metre as described by the Y channel (see 9.2.32 of ICC.1:2010). | ||||
|  | @ -215,7 +215,7 @@ can be easily displayed in a chromaticity diagram, for example). | |||
|         The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. | ||||
| 
 | ||||
|     .. py:attribute:: chromaticity | ||||
|         :type: Optional[tuple[tuple[float]]] | ||||
|         :type: tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]] | None | ||||
| 
 | ||||
|         The data of the phosphor/colorant chromaticity set used (red, | ||||
|         green and blue channels, see 9.2.16 of ICC.1:2010). | ||||
|  | @ -223,7 +223,7 @@ can be easily displayed in a chromaticity diagram, for example). | |||
|         The value is in the format ``((x, y, Y), (x, y, Y), (x, y, Y))``, if available. | ||||
| 
 | ||||
|     .. py:attribute:: chromatic_adaption | ||||
|         :type: tuple[tuple[float]] | ||||
|         :type: tuple[tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]], tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]]] | None | ||||
| 
 | ||||
|         The chromatic adaption matrix converts a color measured using the | ||||
|         actual illumination conditions and relative to the actual adopted | ||||
|  | @ -249,34 +249,34 @@ can be easily displayed in a chromaticity diagram, for example). | |||
|         9.2.19 of ICC.1:2010). | ||||
| 
 | ||||
|     .. py:attribute:: colorimetric_intent | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         4-character string (padded with whitespace) identifying the image | ||||
|         state of PCS colorimetry produced using the colorimetric intent | ||||
|         transforms (see 9.2.20 of ICC.1:2010 for details). | ||||
| 
 | ||||
|     .. py:attribute:: perceptual_rendering_intent_gamut | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         4-character string (padded with whitespace) identifying the (one) | ||||
|         standard reference medium gamut (see 9.2.37 of ICC.1:2010 for | ||||
|         details). | ||||
| 
 | ||||
|     .. py:attribute:: saturation_rendering_intent_gamut | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         4-character string (padded with whitespace) identifying the (one) | ||||
|         standard reference medium gamut (see 9.2.37 of ICC.1:2010 for | ||||
|         details). | ||||
| 
 | ||||
|     .. py:attribute:: technology | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         4-character string (padded with whitespace) identifying the device | ||||
|         technology (see 9.2.47 of ICC.1:2010 for details). | ||||
| 
 | ||||
|     .. py:attribute:: media_black_point | ||||
|         :type: Optional[tuple[tuple[float]]] | ||||
|         :type: tuple[tuple[float, float, float], tuple[float, float, float]] | None | ||||
| 
 | ||||
|         This tag specifies the media black point and is used for | ||||
|         generating absolute colorimetry. | ||||
|  | @ -287,19 +287,19 @@ can be easily displayed in a chromaticity diagram, for example). | |||
|         The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. | ||||
| 
 | ||||
|     .. py:attribute:: media_white_point_temperature | ||||
|         :type: Optional[float] | ||||
|         :type: float | None | ||||
| 
 | ||||
|         Calculates the white point temperature (see the LCMS documentation | ||||
|         for more information). | ||||
| 
 | ||||
|     .. py:attribute:: viewing_condition | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         The (English) display string for the viewing conditions (see | ||||
|         9.2.48 of ICC.1:2010). | ||||
| 
 | ||||
|     .. py:attribute:: screening_description | ||||
|         :type: Optional[str] | ||||
|         :type: str | None | ||||
| 
 | ||||
|         The (English) display string for the screening conditions. | ||||
| 
 | ||||
|  | @ -307,21 +307,21 @@ can be easily displayed in a chromaticity diagram, for example). | |||
|         version 4. | ||||
| 
 | ||||
|     .. py:attribute:: red_primary | ||||
|         :type: Optional[tuple[tuple[float]]] | ||||
|         :type: tuple[tuple[float, float, float], tuple[float, float, float]] | None | ||||
| 
 | ||||
|         The XYZ-transformed of the RGB primary color red (1, 0, 0). | ||||
| 
 | ||||
|         The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. | ||||
| 
 | ||||
|     .. py:attribute:: green_primary | ||||
|         :type: Optional[tuple[tuple[float]]] | ||||
|         :type: tuple[tuple[float, float, float], tuple[float, float, float]] | None | ||||
| 
 | ||||
|         The XYZ-transformed of the RGB primary color green (0, 1, 0). | ||||
| 
 | ||||
|         The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. | ||||
| 
 | ||||
|     .. py:attribute:: blue_primary | ||||
|         :type: Optional[tuple[tuple[float]]] | ||||
|         :type: tuple[tuple[float, float, float], tuple[float, float, float]] | None | ||||
| 
 | ||||
|         The XYZ-transformed of the RGB primary color blue (0, 0, 1). | ||||
| 
 | ||||
|  | @ -334,7 +334,7 @@ can be easily displayed in a chromaticity diagram, for example). | |||
|         documentation on LCMS). | ||||
| 
 | ||||
|     .. py:attribute:: clut | ||||
|         :type: dict[tuple[bool]] | ||||
|         :type: dict[int, tuple[bool, bool, bool]] | None | ||||
| 
 | ||||
|         Returns a dictionary of all supported intents and directions for | ||||
|         the CLUT model. | ||||
|  | @ -353,7 +353,7 @@ can be easily displayed in a chromaticity diagram, for example). | |||
|         that intent is supported for that direction. | ||||
| 
 | ||||
|     .. py:attribute:: intent_supported | ||||
|         :type: dict[tuple[bool]] | ||||
|         :type: dict[int, tuple[bool, bool, bool]] | None | ||||
| 
 | ||||
|         Returns a dictionary of all supported intents and directions. | ||||
| 
 | ||||
|  | @ -372,7 +372,7 @@ can be easily displayed in a chromaticity diagram, for example). | |||
| 
 | ||||
|     There is one function defined on the class: | ||||
| 
 | ||||
|     .. py:method:: is_intent_supported(intent, direction) | ||||
|     .. py:method:: is_intent_supported(intent: int, direction: int, /) | ||||
| 
 | ||||
|         Returns if the intent is supported for the given direction. | ||||
| 
 | ||||
|  |  | |||
|  | @ -117,6 +117,7 @@ ignore = [ | |||
|   "E221", # Multiple spaces before operator | ||||
|   "E226", # Missing whitespace around arithmetic operator | ||||
|   "E241", # Multiple spaces after ',' | ||||
|   "PYI026", # flake8-pyi: typing.TypeAlias added in Python 3.10 | ||||
|   "PYI034", # flake8-pyi: typing.Self added in Python 3.11 | ||||
| ] | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,19 +23,20 @@ import operator | |||
| import sys | ||||
| from enum import IntEnum, IntFlag | ||||
| from functools import reduce | ||||
| from typing import Any | ||||
| from typing import Any, Literal, SupportsFloat, SupportsInt, Union | ||||
| 
 | ||||
| from . import Image, __version__ | ||||
| from ._deprecate import deprecate | ||||
| from ._typing import SupportsRead | ||||
| 
 | ||||
| try: | ||||
|     from . import _imagingcms | ||||
|     from . import _imagingcms as core | ||||
| except ImportError as ex: | ||||
|     # Allow error import for doc purposes, but error out when accessing | ||||
|     # anything in core. | ||||
|     from ._util import DeferredError | ||||
| 
 | ||||
|     _imagingcms = DeferredError.new(ex) | ||||
|     core = DeferredError.new(ex) | ||||
| 
 | ||||
| _DESCRIPTION = """ | ||||
| pyCMS | ||||
|  | @ -119,7 +120,6 @@ def __getattr__(name: str) -> Any: | |||
| 
 | ||||
| # --------------------------------------------------------------------. | ||||
| 
 | ||||
| core = _imagingcms | ||||
| 
 | ||||
| # | ||||
| # intent/direction values | ||||
|  | @ -237,7 +237,7 @@ _FLAGS = { | |||
| 
 | ||||
| 
 | ||||
| class ImageCmsProfile: | ||||
|     def __init__(self, profile): | ||||
|     def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None: | ||||
|         """ | ||||
|         :param profile: Either a string representing a filename, | ||||
|             a file like object containing a profile or a | ||||
|  | @ -257,19 +257,19 @@ class ImageCmsProfile: | |||
|             self._set(core.profile_open(profile), profile) | ||||
|         elif hasattr(profile, "read"): | ||||
|             self._set(core.profile_frombytes(profile.read())) | ||||
|         elif isinstance(profile, _imagingcms.CmsProfile): | ||||
|         elif isinstance(profile, core.CmsProfile): | ||||
|             self._set(profile) | ||||
|         else: | ||||
|             msg = "Invalid type for Profile" | ||||
|             msg = "Invalid type for Profile"  # type: ignore[unreachable] | ||||
|             raise TypeError(msg) | ||||
| 
 | ||||
|     def _set(self, profile, filename=None): | ||||
|     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 tobytes(self): | ||||
|     def tobytes(self) -> bytes: | ||||
|         """ | ||||
|         Returns the profile in a format suitable for embedding in | ||||
|         saved images. | ||||
|  | @ -290,14 +290,14 @@ class ImageCmsTransform(Image.ImagePointHandler): | |||
| 
 | ||||
|     def __init__( | ||||
|         self, | ||||
|         input, | ||||
|         output, | ||||
|         input_mode, | ||||
|         output_mode, | ||||
|         intent=Intent.PERCEPTUAL, | ||||
|         proof=None, | ||||
|         proof_intent=Intent.ABSOLUTE_COLORIMETRIC, | ||||
|         flags=Flags.NONE, | ||||
|         input: ImageCmsProfile, | ||||
|         output: ImageCmsProfile, | ||||
|         input_mode: str, | ||||
|         output_mode: str, | ||||
|         intent: Intent = Intent.PERCEPTUAL, | ||||
|         proof: ImageCmsProfile | None = None, | ||||
|         proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC, | ||||
|         flags: Flags = Flags.NONE, | ||||
|     ): | ||||
|         if proof is None: | ||||
|             self.transform = core.buildTransform( | ||||
|  | @ -320,10 +320,10 @@ class ImageCmsTransform(Image.ImagePointHandler): | |||
| 
 | ||||
|         self.output_profile = output | ||||
| 
 | ||||
|     def point(self, im): | ||||
|     def point(self, im: Image.Image) -> Image.Image: | ||||
|         return self.apply(im) | ||||
| 
 | ||||
|     def apply(self, im, imOut=None): | ||||
|     def apply(self, im: Image.Image, imOut: Image.Image | None = None) -> Image.Image: | ||||
|         im.load() | ||||
|         if imOut is None: | ||||
|             imOut = Image.new(self.output_mode, im.size, None) | ||||
|  | @ -331,7 +331,7 @@ class ImageCmsTransform(Image.ImagePointHandler): | |||
|         imOut.info["icc_profile"] = self.output_profile.tobytes() | ||||
|         return imOut | ||||
| 
 | ||||
|     def apply_in_place(self, im): | ||||
|     def apply_in_place(self, im: Image.Image) -> Image.Image: | ||||
|         im.load() | ||||
|         if im.mode != self.output_mode: | ||||
|             msg = "mode mismatch" | ||||
|  | @ -341,7 +341,7 @@ class ImageCmsTransform(Image.ImagePointHandler): | |||
|         return im | ||||
| 
 | ||||
| 
 | ||||
| def get_display_profile(handle=None): | ||||
| def get_display_profile(handle: SupportsInt | None = None) -> ImageCmsProfile | None: | ||||
|     """ | ||||
|     (experimental) Fetches the profile for the current display device. | ||||
| 
 | ||||
|  | @ -351,12 +351,12 @@ def get_display_profile(handle=None): | |||
|     if sys.platform != "win32": | ||||
|         return None | ||||
| 
 | ||||
|     from . import ImageWin | ||||
|     from . import ImageWin  # type: ignore[unused-ignore, unreachable] | ||||
| 
 | ||||
|     if isinstance(handle, ImageWin.HDC): | ||||
|         profile = core.get_display_profile_win32(handle, 1) | ||||
|         profile = core.get_display_profile_win32(int(handle), 1) | ||||
|     else: | ||||
|         profile = core.get_display_profile_win32(handle or 0) | ||||
|         profile = core.get_display_profile_win32(int(handle or 0)) | ||||
|     if profile is None: | ||||
|         return None | ||||
|     return ImageCmsProfile(profile) | ||||
|  | @ -366,6 +366,10 @@ def get_display_profile(handle=None): | |||
| # pyCMS compatible layer | ||||
| # --------------------------------------------------------------------. | ||||
| 
 | ||||
| _CmsProfileCompatible = Union[ | ||||
|     str, SupportsRead[bytes], core.CmsProfile, ImageCmsProfile | ||||
| ] | ||||
| 
 | ||||
| 
 | ||||
| class PyCMSError(Exception): | ||||
|     """(pyCMS) Exception class. | ||||
|  | @ -375,14 +379,14 @@ class PyCMSError(Exception): | |||
| 
 | ||||
| 
 | ||||
| def profileToProfile( | ||||
|     im, | ||||
|     inputProfile, | ||||
|     outputProfile, | ||||
|     renderingIntent=Intent.PERCEPTUAL, | ||||
|     outputMode=None, | ||||
|     inPlace=False, | ||||
|     flags=Flags.NONE, | ||||
| ): | ||||
|     im: Image.Image, | ||||
|     inputProfile: _CmsProfileCompatible, | ||||
|     outputProfile: _CmsProfileCompatible, | ||||
|     renderingIntent: Intent = Intent.PERCEPTUAL, | ||||
|     outputMode: str | None = None, | ||||
|     inPlace: bool = False, | ||||
|     flags: Flags = Flags.NONE, | ||||
| ) -> Image.Image | None: | ||||
|     """ | ||||
|     (pyCMS) Applies an ICC transformation to a given image, mapping from | ||||
|     ``inputProfile`` to ``outputProfile``. | ||||
|  | @ -470,7 +474,9 @@ def profileToProfile( | |||
|     return imOut | ||||
| 
 | ||||
| 
 | ||||
| def getOpenProfile(profileFilename): | ||||
| def getOpenProfile( | ||||
|     profileFilename: str | SupportsRead[bytes] | core.CmsProfile, | ||||
| ) -> ImageCmsProfile: | ||||
|     """ | ||||
|     (pyCMS) Opens an ICC profile file. | ||||
| 
 | ||||
|  | @ -493,13 +499,13 @@ def getOpenProfile(profileFilename): | |||
| 
 | ||||
| 
 | ||||
| def buildTransform( | ||||
|     inputProfile, | ||||
|     outputProfile, | ||||
|     inMode, | ||||
|     outMode, | ||||
|     renderingIntent=Intent.PERCEPTUAL, | ||||
|     flags=Flags.NONE, | ||||
| ): | ||||
|     inputProfile: _CmsProfileCompatible, | ||||
|     outputProfile: _CmsProfileCompatible, | ||||
|     inMode: str, | ||||
|     outMode: str, | ||||
|     renderingIntent: Intent = Intent.PERCEPTUAL, | ||||
|     flags: Flags = Flags.NONE, | ||||
| ) -> ImageCmsTransform: | ||||
|     """ | ||||
|     (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the | ||||
|     ``outputProfile``. Use applyTransform to apply the transform to a given | ||||
|  | @ -576,15 +582,15 @@ def buildTransform( | |||
| 
 | ||||
| 
 | ||||
| def buildProofTransform( | ||||
|     inputProfile, | ||||
|     outputProfile, | ||||
|     proofProfile, | ||||
|     inMode, | ||||
|     outMode, | ||||
|     renderingIntent=Intent.PERCEPTUAL, | ||||
|     proofRenderingIntent=Intent.ABSOLUTE_COLORIMETRIC, | ||||
|     flags=Flags.SOFTPROOFING, | ||||
| ): | ||||
|     inputProfile: _CmsProfileCompatible, | ||||
|     outputProfile: _CmsProfileCompatible, | ||||
|     proofProfile: _CmsProfileCompatible, | ||||
|     inMode: str, | ||||
|     outMode: str, | ||||
|     renderingIntent: Intent = Intent.PERCEPTUAL, | ||||
|     proofRenderingIntent: Intent = Intent.ABSOLUTE_COLORIMETRIC, | ||||
|     flags: Flags = Flags.SOFTPROOFING, | ||||
| ) -> ImageCmsTransform: | ||||
|     """ | ||||
|     (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the | ||||
|     ``outputProfile``, but tries to simulate the result that would be | ||||
|  | @ -692,7 +698,9 @@ buildTransformFromOpenProfiles = buildTransform | |||
| buildProofTransformFromOpenProfiles = buildProofTransform | ||||
| 
 | ||||
| 
 | ||||
| def applyTransform(im, transform, inPlace=False): | ||||
| def applyTransform( | ||||
|     im: Image.Image, transform: ImageCmsTransform, inPlace: bool = False | ||||
| ) -> Image.Image | None: | ||||
|     """ | ||||
|     (pyCMS) Applies a transform to a given image. | ||||
| 
 | ||||
|  | @ -745,7 +753,9 @@ def applyTransform(im, transform, inPlace=False): | |||
|     return imOut | ||||
| 
 | ||||
| 
 | ||||
| def createProfile(colorSpace, colorTemp=-1): | ||||
| def createProfile( | ||||
|     colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = -1 | ||||
| ) -> core.CmsProfile: | ||||
|     """ | ||||
|     (pyCMS) Creates a profile. | ||||
| 
 | ||||
|  | @ -794,7 +804,7 @@ def createProfile(colorSpace, colorTemp=-1): | |||
|         raise PyCMSError(v) from v | ||||
| 
 | ||||
| 
 | ||||
| def getProfileName(profile): | ||||
| def getProfileName(profile: _CmsProfileCompatible) -> str: | ||||
|     """ | ||||
| 
 | ||||
|     (pyCMS) Gets the internal product name for the given profile. | ||||
|  | @ -828,15 +838,15 @@ def getProfileName(profile): | |||
| 
 | ||||
|         if not (model or manufacturer): | ||||
|             return (profile.profile.profile_description or "") + "\n" | ||||
|         if not manufacturer or len(model) > 30: | ||||
|             return model + "\n" | ||||
|         if not manufacturer or len(model) > 30:  # type: ignore[arg-type] | ||||
|             return model + "\n"  # type: ignore[operator] | ||||
|         return f"{model} - {manufacturer}\n" | ||||
| 
 | ||||
|     except (AttributeError, OSError, TypeError, ValueError) as v: | ||||
|         raise PyCMSError(v) from v | ||||
| 
 | ||||
| 
 | ||||
| def getProfileInfo(profile): | ||||
| def getProfileInfo(profile: _CmsProfileCompatible) -> str: | ||||
|     """ | ||||
|     (pyCMS) Gets the internal product information for the given profile. | ||||
| 
 | ||||
|  | @ -873,7 +883,7 @@ def getProfileInfo(profile): | |||
|         raise PyCMSError(v) from v | ||||
| 
 | ||||
| 
 | ||||
| def getProfileCopyright(profile): | ||||
| def getProfileCopyright(profile: _CmsProfileCompatible) -> str: | ||||
|     """ | ||||
|     (pyCMS) Gets the copyright for the given profile. | ||||
| 
 | ||||
|  | @ -901,7 +911,7 @@ def getProfileCopyright(profile): | |||
|         raise PyCMSError(v) from v | ||||
| 
 | ||||
| 
 | ||||
| def getProfileManufacturer(profile): | ||||
| def getProfileManufacturer(profile: _CmsProfileCompatible) -> str: | ||||
|     """ | ||||
|     (pyCMS) Gets the manufacturer for the given profile. | ||||
| 
 | ||||
|  | @ -929,7 +939,7 @@ def getProfileManufacturer(profile): | |||
|         raise PyCMSError(v) from v | ||||
| 
 | ||||
| 
 | ||||
| def getProfileModel(profile): | ||||
| def getProfileModel(profile: _CmsProfileCompatible) -> str: | ||||
|     """ | ||||
|     (pyCMS) Gets the model for the given profile. | ||||
| 
 | ||||
|  | @ -958,7 +968,7 @@ def getProfileModel(profile): | |||
|         raise PyCMSError(v) from v | ||||
| 
 | ||||
| 
 | ||||
| def getProfileDescription(profile): | ||||
| def getProfileDescription(profile: _CmsProfileCompatible) -> str: | ||||
|     """ | ||||
|     (pyCMS) Gets the description for the given profile. | ||||
| 
 | ||||
|  | @ -987,7 +997,7 @@ def getProfileDescription(profile): | |||
|         raise PyCMSError(v) from v | ||||
| 
 | ||||
| 
 | ||||
| def getDefaultIntent(profile): | ||||
| def getDefaultIntent(profile: _CmsProfileCompatible) -> int: | ||||
|     """ | ||||
|     (pyCMS) Gets the default intent name for the given profile. | ||||
| 
 | ||||
|  | @ -1026,7 +1036,9 @@ def getDefaultIntent(profile): | |||
|         raise PyCMSError(v) from v | ||||
| 
 | ||||
| 
 | ||||
| def isIntentSupported(profile, intent, direction): | ||||
| def isIntentSupported( | ||||
|     profile: _CmsProfileCompatible, intent: Intent, direction: Direction | ||||
| ) -> Literal[-1, 1]: | ||||
|     """ | ||||
|     (pyCMS) Checks if a given intent is supported. | ||||
| 
 | ||||
|  | @ -1077,7 +1089,7 @@ def isIntentSupported(profile, intent, direction): | |||
|         raise PyCMSError(v) from v | ||||
| 
 | ||||
| 
 | ||||
| def versions(): | ||||
| def versions() -> tuple[str, str, str, str]: | ||||
|     """ | ||||
|     (pyCMS) Fetches versions. | ||||
|     """ | ||||
|  |  | |||
|  | @ -1,3 +1,145 @@ | |||
| from typing import Any | ||||
| import datetime | ||||
| import sys | ||||
| from typing import Literal, SupportsFloat, TypedDict | ||||
| 
 | ||||
| def __getattr__(name: str) -> Any: ... | ||||
| littlecms_version: str | ||||
| 
 | ||||
| _Tuple3f = tuple[float, float, float] | ||||
| _Tuple2x3f = tuple[_Tuple3f, _Tuple3f] | ||||
| _Tuple3x3f = tuple[_Tuple3f, _Tuple3f, _Tuple3f] | ||||
| 
 | ||||
| class _IccMeasurementCondition(TypedDict): | ||||
|     observer: int | ||||
|     backing: _Tuple3f | ||||
|     geo: str | ||||
|     flare: float | ||||
|     illuminant_type: str | ||||
| 
 | ||||
| class _IccViewingCondition(TypedDict): | ||||
|     illuminant: _Tuple3f | ||||
|     surround: _Tuple3f | ||||
|     illuminant_type: str | ||||
| 
 | ||||
| class CmsProfile: | ||||
|     @property | ||||
|     def rendering_intent(self) -> int: ... | ||||
|     @property | ||||
|     def creation_date(self) -> datetime.datetime | None: ... | ||||
|     @property | ||||
|     def copyright(self) -> str | None: ... | ||||
|     @property | ||||
|     def target(self) -> str | None: ... | ||||
|     @property | ||||
|     def manufacturer(self) -> str | None: ... | ||||
|     @property | ||||
|     def model(self) -> str | None: ... | ||||
|     @property | ||||
|     def profile_description(self) -> str | None: ... | ||||
|     @property | ||||
|     def screening_description(self) -> str | None: ... | ||||
|     @property | ||||
|     def viewing_condition(self) -> str | None: ... | ||||
|     @property | ||||
|     def version(self) -> float: ... | ||||
|     @property | ||||
|     def icc_version(self) -> int: ... | ||||
|     @property | ||||
|     def attributes(self) -> int: ... | ||||
|     @property | ||||
|     def header_flags(self) -> int: ... | ||||
|     @property | ||||
|     def header_manufacturer(self) -> str: ... | ||||
|     @property | ||||
|     def header_model(self) -> str: ... | ||||
|     @property | ||||
|     def device_class(self) -> str: ... | ||||
|     @property | ||||
|     def connection_space(self) -> str: ... | ||||
|     @property | ||||
|     def xcolor_space(self) -> str: ... | ||||
|     @property | ||||
|     def profile_id(self) -> bytes: ... | ||||
|     @property | ||||
|     def is_matrix_shaper(self) -> bool: ... | ||||
|     @property | ||||
|     def technology(self) -> str | None: ... | ||||
|     @property | ||||
|     def colorimetric_intent(self) -> str | None: ... | ||||
|     @property | ||||
|     def perceptual_rendering_intent_gamut(self) -> str | None: ... | ||||
|     @property | ||||
|     def saturation_rendering_intent_gamut(self) -> str | None: ... | ||||
|     @property | ||||
|     def red_colorant(self) -> _Tuple2x3f | None: ... | ||||
|     @property | ||||
|     def green_colorant(self) -> _Tuple2x3f | None: ... | ||||
|     @property | ||||
|     def blue_colorant(self) -> _Tuple2x3f | None: ... | ||||
|     @property | ||||
|     def red_primary(self) -> _Tuple2x3f | None: ... | ||||
|     @property | ||||
|     def green_primary(self) -> _Tuple2x3f | None: ... | ||||
|     @property | ||||
|     def blue_primary(self) -> _Tuple2x3f | None: ... | ||||
|     @property | ||||
|     def media_white_point_temperature(self) -> float | None: ... | ||||
|     @property | ||||
|     def media_white_point(self) -> _Tuple2x3f | None: ... | ||||
|     @property | ||||
|     def media_black_point(self) -> _Tuple2x3f | None: ... | ||||
|     @property | ||||
|     def luminance(self) -> _Tuple2x3f | None: ... | ||||
|     @property | ||||
|     def chromatic_adaptation(self) -> tuple[_Tuple3x3f, _Tuple3x3f] | None: ... | ||||
|     @property | ||||
|     def chromaticity(self) -> _Tuple3x3f | None: ... | ||||
|     @property | ||||
|     def colorant_table(self) -> list[str] | None: ... | ||||
|     @property | ||||
|     def colorant_table_out(self) -> list[str] | None: ... | ||||
|     @property | ||||
|     def intent_supported(self) -> dict[int, tuple[bool, bool, bool]] | None: ... | ||||
|     @property | ||||
|     def clut(self) -> dict[int, tuple[bool, bool, bool]] | None: ... | ||||
|     @property | ||||
|     def icc_measurement_condition(self) -> _IccMeasurementCondition | None: ... | ||||
|     @property | ||||
|     def icc_viewing_condition(self) -> _IccViewingCondition | None: ... | ||||
|     def is_intent_supported(self, intent: int, direction: int, /) -> int: ... | ||||
| 
 | ||||
| class CmsTransform: | ||||
|     @property | ||||
|     def inputMode(self) -> str: ... | ||||
|     @property | ||||
|     def outputMode(self) -> str: ... | ||||
|     def apply(self, id_in: int, id_out: int) -> int: ... | ||||
| 
 | ||||
| def profile_open(profile: str, /) -> CmsProfile: ... | ||||
| def profile_frombytes(profile: bytes, /) -> CmsProfile: ... | ||||
| def profile_tobytes(profile: CmsProfile, /) -> bytes: ... | ||||
| def buildTransform( | ||||
|     input_profile: CmsProfile, | ||||
|     output_profile: CmsProfile, | ||||
|     in_mode: str, | ||||
|     out_mode: str, | ||||
|     rendering_intent: int = 0, | ||||
|     cms_flags: int = 0, | ||||
|     /, | ||||
| ) -> CmsTransform: ... | ||||
| def buildProofTransform( | ||||
|     input_profile: CmsProfile, | ||||
|     output_profile: CmsProfile, | ||||
|     proof_profile: CmsProfile, | ||||
|     in_mode: str, | ||||
|     out_mode: str, | ||||
|     rendering_intent: int = 0, | ||||
|     proof_intent: int = 0, | ||||
|     cms_flags: int = 0, | ||||
|     /, | ||||
| ) -> CmsTransform: ... | ||||
| def createProfile( | ||||
|     color_space: Literal["LAB", "XYZ", "sRGB"], color_temp: SupportsFloat = 0.0, / | ||||
| ) -> CmsProfile: ... | ||||
| 
 | ||||
| if sys.platform == "win32": | ||||
|     def get_display_profile_win32(handle: int = 0, is_dc: int = 0, /) -> str | None: ... | ||||
|  |  | |||
|  | @ -143,7 +143,7 @@ cms_profile_tobytes(PyObject *self, PyObject *args) { | |||
|     cmsHPROFILE *profile; | ||||
| 
 | ||||
|     PyObject *ret; | ||||
|     if (!PyArg_ParseTuple(args, "O", &CmsProfile)) { | ||||
|     if (!PyArg_ParseTuple(args, "O!", &CmsProfile_Type, &CmsProfile)) { | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -1421,9 +1421,9 @@ static struct PyGetSetDef cms_profile_getsetters[] = { | |||
|     {NULL}}; | ||||
| 
 | ||||
| static PyTypeObject CmsProfile_Type = { | ||||
|     PyVarObject_HEAD_INIT(NULL, 0) "PIL._imagingcms.CmsProfile", /*tp_name*/ | ||||
|     sizeof(CmsProfileObject),                                    /*tp_basicsize*/ | ||||
|     0,                                                           /*tp_itemsize*/ | ||||
|     PyVarObject_HEAD_INIT(NULL, 0) "PIL.ImageCms.core.CmsProfile", /*tp_name*/ | ||||
|     sizeof(CmsProfileObject),                                      /*tp_basicsize*/ | ||||
|     0,                                                             /*tp_itemsize*/ | ||||
|     /* methods */ | ||||
|     (destructor)cms_profile_dealloc, /*tp_dealloc*/ | ||||
|     0,                               /*tp_vectorcall_offset*/ | ||||
|  | @ -1473,9 +1473,9 @@ static struct PyGetSetDef cms_transform_getsetters[] = { | |||
|     {NULL}}; | ||||
| 
 | ||||
| static PyTypeObject CmsTransform_Type = { | ||||
|     PyVarObject_HEAD_INIT(NULL, 0) "CmsTransform", /*tp_name*/ | ||||
|     sizeof(CmsTransformObject),                    /*tp_basicsize*/ | ||||
|     0,                                             /*tp_itemsize*/ | ||||
|     PyVarObject_HEAD_INIT(NULL, 0) "PIL.ImageCms.core.CmsTransform", /*tp_name*/ | ||||
|     sizeof(CmsTransformObject),                                      /*tp_basicsize*/ | ||||
|     0,                                                               /*tp_itemsize*/ | ||||
|     /* methods */ | ||||
|     (destructor)cms_transform_dealloc, /*tp_dealloc*/ | ||||
|     0,                                 /*tp_vectorcall_offset*/ | ||||
|  | @ -1511,8 +1511,6 @@ setup_module(PyObject *m) { | |||
|     PyObject *v; | ||||
|     int vn; | ||||
| 
 | ||||
|     CmsProfile_Type.tp_new = PyType_GenericNew; | ||||
| 
 | ||||
|     /* Ready object types */ | ||||
|     PyType_Ready(&CmsProfile_Type); | ||||
|     PyType_Ready(&CmsTransform_Type); | ||||
|  | @ -1520,6 +1518,9 @@ setup_module(PyObject *m) { | |||
|     Py_INCREF(&CmsProfile_Type); | ||||
|     PyModule_AddObject(m, "CmsProfile", (PyObject *)&CmsProfile_Type); | ||||
| 
 | ||||
|     Py_INCREF(&CmsTransform_Type); | ||||
|     PyModule_AddObject(m, "CmsTransform", (PyObject *)&CmsTransform_Type); | ||||
| 
 | ||||
|     d = PyModule_GetDict(m); | ||||
| 
 | ||||
|     /* this check is also in PIL.features.pilinfo() */ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user