Merge pull request #7913 from nulano/types-cms2

This commit is contained in:
Hugo van Kemenade 2024-03-31 15:34:03 +03:00 committed by GitHub
commit 6c55ab22d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 291 additions and 106 deletions

View File

@ -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

View File

@ -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 ----------------------------------------------

View File

@ -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.

View File

@ -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
]

View File

@ -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.
"""

View File

@ -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: ...

View File

@ -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() */