mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-24 00:46:16 +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