Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PIL/ImageCms.py: 34%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# The Python Imaging Library.
2# $Id$
4# Optional color management support, based on Kevin Cazabon's PyCMS
5# library.
7# Originally released under LGPL. Graciously donated to PIL in
8# March 2009, for distribution under the standard PIL license
10# History:
12# 2009-03-08 fl Added to PIL.
14# Copyright (C) 2002-2003 Kevin Cazabon
15# Copyright (c) 2009 by Fredrik Lundh
16# Copyright (c) 2013 by Eric Soroos
18# See the README file for information on usage and redistribution. See
19# below for the original description.
20from __future__ import annotations
22import operator
23import sys
24from enum import IntEnum, IntFlag
25from functools import reduce
26from typing import Any, Literal, SupportsFloat, SupportsInt, Union
28from . import Image
29from ._deprecate import deprecate
30from ._typing import SupportsRead
32try:
33 from . import _imagingcms as core
35 _CmsProfileCompatible = Union[
36 str, SupportsRead[bytes], core.CmsProfile, "ImageCmsProfile"
37 ]
38except ImportError as ex:
39 # Allow error import for doc purposes, but error out when accessing
40 # anything in core.
41 from ._util import DeferredError
43 core = DeferredError.new(ex)
45_DESCRIPTION = """
46pyCMS
48 a Python / PIL interface to the littleCMS ICC Color Management System
49 Copyright (C) 2002-2003 Kevin Cazabon
50 kevin@cazabon.com
51 https://www.cazabon.com
53 pyCMS home page: https://www.cazabon.com/pyCMS
54 littleCMS home page: https://www.littlecms.com
55 (littleCMS is Copyright (C) 1998-2001 Marti Maria)
57 Originally released under LGPL. Graciously donated to PIL in
58 March 2009, for distribution under the standard PIL license
60 The pyCMS.py module provides a "clean" interface between Python/PIL and
61 pyCMSdll, taking care of some of the more complex handling of the direct
62 pyCMSdll functions, as well as error-checking and making sure that all
63 relevant data is kept together.
65 While it is possible to call pyCMSdll functions directly, it's not highly
66 recommended.
68 Version History:
70 1.0.0 pil Oct 2013 Port to LCMS 2.
72 0.1.0 pil mod March 10, 2009
74 Renamed display profile to proof profile. The proof
75 profile is the profile of the device that is being
76 simulated, not the profile of the device which is
77 actually used to display/print the final simulation
78 (that'd be the output profile) - also see LCMSAPI.txt
79 input colorspace -> using 'renderingIntent' -> proof
80 colorspace -> using 'proofRenderingIntent' -> output
81 colorspace
83 Added LCMS FLAGS support.
84 Added FLAGS["SOFTPROOFING"] as default flag for
85 buildProofTransform (otherwise the proof profile/intent
86 would be ignored).
88 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms
90 0.0.2 alpha Jan 6, 2002
92 Added try/except statements around type() checks of
93 potential CObjects... Python won't let you use type()
94 on them, and raises a TypeError (stupid, if you ask
95 me!)
97 Added buildProofTransformFromOpenProfiles() function.
98 Additional fixes in DLL, see DLL code for details.
100 0.0.1 alpha first public release, Dec. 26, 2002
102 Known to-do list with current version (of Python interface, not pyCMSdll):
104 none
106"""
108_VERSION = "1.0.0 pil"
111# --------------------------------------------------------------------.
114#
115# intent/direction values
118class Intent(IntEnum):
119 PERCEPTUAL = 0
120 RELATIVE_COLORIMETRIC = 1
121 SATURATION = 2
122 ABSOLUTE_COLORIMETRIC = 3
125class Direction(IntEnum):
126 INPUT = 0
127 OUTPUT = 1
128 PROOF = 2
131#
132# flags
135class Flags(IntFlag):
136 """Flags and documentation are taken from ``lcms2.h``."""
138 NONE = 0
139 NOCACHE = 0x0040
140 """Inhibit 1-pixel cache"""
141 NOOPTIMIZE = 0x0100
142 """Inhibit optimizations"""
143 NULLTRANSFORM = 0x0200
144 """Don't transform anyway"""
145 GAMUTCHECK = 0x1000
146 """Out of Gamut alarm"""
147 SOFTPROOFING = 0x4000
148 """Do softproofing"""
149 BLACKPOINTCOMPENSATION = 0x2000
150 NOWHITEONWHITEFIXUP = 0x0004
151 """Don't fix scum dot"""
152 HIGHRESPRECALC = 0x0400
153 """Use more memory to give better accuracy"""
154 LOWRESPRECALC = 0x0800
155 """Use less memory to minimize resources"""
156 # this should be 8BITS_DEVICELINK, but that is not a valid name in Python:
157 USE_8BITS_DEVICELINK = 0x0008
158 """Create 8 bits devicelinks"""
159 GUESSDEVICECLASS = 0x0020
160 """Guess device class (for ``transform2devicelink``)"""
161 KEEP_SEQUENCE = 0x0080
162 """Keep profile sequence for devicelink creation"""
163 FORCE_CLUT = 0x0002
164 """Force CLUT optimization"""
165 CLUT_POST_LINEARIZATION = 0x0001
166 """create postlinearization tables if possible"""
167 CLUT_PRE_LINEARIZATION = 0x0010
168 """create prelinearization tables if possible"""
169 NONEGATIVES = 0x8000
170 """Prevent negative numbers in floating point transforms"""
171 COPY_ALPHA = 0x04000000
172 """Alpha channels are copied on ``cmsDoTransform()``"""
173 NODEFAULTRESOURCEDEF = 0x01000000
175 _GRIDPOINTS_1 = 1 << 16
176 _GRIDPOINTS_2 = 2 << 16
177 _GRIDPOINTS_4 = 4 << 16
178 _GRIDPOINTS_8 = 8 << 16
179 _GRIDPOINTS_16 = 16 << 16
180 _GRIDPOINTS_32 = 32 << 16
181 _GRIDPOINTS_64 = 64 << 16
182 _GRIDPOINTS_128 = 128 << 16
184 @staticmethod
185 def GRIDPOINTS(n: int) -> Flags:
186 """
187 Fine-tune control over number of gridpoints
189 :param n: :py:class:`int` in range ``0 <= n <= 255``
190 """
191 return Flags.NONE | ((n & 0xFF) << 16)
194_MAX_FLAG = reduce(operator.or_, Flags)
197_FLAGS = {
198 "MATRIXINPUT": 1,
199 "MATRIXOUTPUT": 2,
200 "MATRIXONLY": (1 | 2),
201 "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
202 # Don't create prelinearization tables on precalculated transforms
203 # (internal use):
204 "NOPRELINEARIZATION": 16,
205 "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
206 "NOTCACHE": 64, # Inhibit 1-pixel cache
207 "NOTPRECALC": 256,
208 "NULLTRANSFORM": 512, # Don't transform anyway
209 "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy
210 "LOWRESPRECALC": 2048, # Use less memory to minimize resources
211 "WHITEBLACKCOMPENSATION": 8192,
212 "BLACKPOINTCOMPENSATION": 8192,
213 "GAMUTCHECK": 4096, # Out of Gamut alarm
214 "SOFTPROOFING": 16384, # Do softproofing
215 "PRESERVEBLACK": 32768, # Black preservation
216 "NODEFAULTRESOURCEDEF": 16777216, # CRD special
217 "GRIDPOINTS": lambda n: (n & 0xFF) << 16, # Gridpoints
218}
221# --------------------------------------------------------------------.
222# Experimental PIL-level API
223# --------------------------------------------------------------------.
225##
226# Profile.
229class ImageCmsProfile:
230 def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None:
231 """
232 :param profile: Either a string representing a filename,
233 a file like object containing a profile or a
234 low-level profile object
236 """
237 self.filename: str | None = None
239 if isinstance(profile, str):
240 if sys.platform == "win32":
241 profile_bytes_path = profile.encode()
242 try:
243 profile_bytes_path.decode("ascii")
244 except UnicodeDecodeError:
245 with open(profile, "rb") as f:
246 self.profile = core.profile_frombytes(f.read())
247 return
248 self.filename = profile
249 self.profile = core.profile_open(profile)
250 elif hasattr(profile, "read"):
251 self.profile = core.profile_frombytes(profile.read())
252 elif isinstance(profile, core.CmsProfile):
253 self.profile = profile
254 else:
255 msg = "Invalid type for Profile" # type: ignore[unreachable]
256 raise TypeError(msg)
258 def __getattr__(self, name: str) -> Any:
259 if name in ("product_name", "product_info"):
260 deprecate(f"ImageCms.ImageCmsProfile.{name}", 13)
261 return None
262 msg = f"'{self.__class__.__name__}' object has no attribute '{name}'"
263 raise AttributeError(msg)
265 def tobytes(self) -> bytes:
266 """
267 Returns the profile in a format suitable for embedding in
268 saved images.
270 :returns: a bytes object containing the ICC profile.
271 """
273 return core.profile_tobytes(self.profile)
276class ImageCmsTransform(Image.ImagePointHandler):
277 """
278 Transform. This can be used with the procedural API, or with the standard
279 :py:func:`~PIL.Image.Image.point` method.
281 Will return the output profile in the ``output.info['icc_profile']``.
282 """
284 def __init__(
285 self,
286 input: ImageCmsProfile,
287 output: ImageCmsProfile,
288 input_mode: str,
289 output_mode: str,
290 intent: Intent = Intent.PERCEPTUAL,
291 proof: ImageCmsProfile | None = None,
292 proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC,
293 flags: Flags = Flags.NONE,
294 ):
295 if proof is None:
296 self.transform = core.buildTransform(
297 input.profile, output.profile, input_mode, output_mode, intent, flags
298 )
299 else:
300 self.transform = core.buildProofTransform(
301 input.profile,
302 output.profile,
303 proof.profile,
304 input_mode,
305 output_mode,
306 intent,
307 proof_intent,
308 flags,
309 )
310 # Note: inputMode and outputMode are for pyCMS compatibility only
311 self.input_mode = self.inputMode = input_mode
312 self.output_mode = self.outputMode = output_mode
314 self.output_profile = output
316 def point(self, im: Image.Image) -> Image.Image:
317 return self.apply(im)
319 def apply(self, im: Image.Image, imOut: Image.Image | None = None) -> Image.Image:
320 if imOut is None:
321 imOut = Image.new(self.output_mode, im.size, None)
322 self.transform.apply(im.getim(), imOut.getim())
323 imOut.info["icc_profile"] = self.output_profile.tobytes()
324 return imOut
326 def apply_in_place(self, im: Image.Image) -> Image.Image:
327 if im.mode != self.output_mode:
328 msg = "mode mismatch"
329 raise ValueError(msg) # wrong output mode
330 self.transform.apply(im.getim(), im.getim())
331 im.info["icc_profile"] = self.output_profile.tobytes()
332 return im
335def get_display_profile(handle: SupportsInt | None = None) -> ImageCmsProfile | None:
336 """
337 (experimental) Fetches the profile for the current display device.
339 :returns: ``None`` if the profile is not known.
340 """
342 if sys.platform != "win32":
343 return None
345 from . import ImageWin # type: ignore[unused-ignore, unreachable]
347 if isinstance(handle, ImageWin.HDC):
348 profile = core.get_display_profile_win32(int(handle), 1)
349 else:
350 profile = core.get_display_profile_win32(int(handle or 0))
351 if profile is None:
352 return None
353 return ImageCmsProfile(profile)
356# --------------------------------------------------------------------.
357# pyCMS compatible layer
358# --------------------------------------------------------------------.
361class PyCMSError(Exception):
362 """(pyCMS) Exception class.
363 This is used for all errors in the pyCMS API."""
365 pass
368def profileToProfile(
369 im: Image.Image,
370 inputProfile: _CmsProfileCompatible,
371 outputProfile: _CmsProfileCompatible,
372 renderingIntent: Intent = Intent.PERCEPTUAL,
373 outputMode: str | None = None,
374 inPlace: bool = False,
375 flags: Flags = Flags.NONE,
376) -> Image.Image | None:
377 """
378 (pyCMS) Applies an ICC transformation to a given image, mapping from
379 ``inputProfile`` to ``outputProfile``.
381 If the input or output profiles specified are not valid filenames, a
382 :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and
383 ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised.
384 If an error occurs during application of the profiles,
385 a :exc:`PyCMSError` will be raised.
386 If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS),
387 a :exc:`PyCMSError` will be raised.
389 This function applies an ICC transformation to im from ``inputProfile``'s
390 color space to ``outputProfile``'s color space using the specified rendering
391 intent to decide how to handle out-of-gamut colors.
393 ``outputMode`` can be used to specify that a color mode conversion is to
394 be done using these profiles, but the specified profiles must be able
395 to handle that mode. I.e., if converting im from RGB to CMYK using
396 profiles, the input profile must handle RGB data, and the output
397 profile must handle CMYK data.
399 :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...)
400 or Image.open(...), etc.)
401 :param inputProfile: String, as a valid filename path to the ICC input
402 profile you wish to use for this image, or a profile object
403 :param outputProfile: String, as a valid filename path to the ICC output
404 profile you wish to use for this image, or a profile object
405 :param renderingIntent: Integer (0-3) specifying the rendering intent you
406 wish to use for the transform
408 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
409 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
410 ImageCms.Intent.SATURATION = 2
411 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
413 see the pyCMS documentation for details on rendering intents and what
414 they do.
415 :param outputMode: A valid PIL mode for the output image (i.e. "RGB",
416 "CMYK", etc.). Note: if rendering the image "inPlace", outputMode
417 MUST be the same mode as the input, or omitted completely. If
418 omitted, the outputMode will be the same as the mode of the input
419 image (im.mode)
420 :param inPlace: Boolean. If ``True``, the original image is modified in-place,
421 and ``None`` is returned. If ``False`` (default), a new
422 :py:class:`~PIL.Image.Image` object is returned with the transform applied.
423 :param flags: Integer (0-...) specifying additional flags
424 :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on
425 the value of ``inPlace``
426 :exception PyCMSError:
427 """
429 if outputMode is None:
430 outputMode = im.mode
432 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
433 msg = "renderingIntent must be an integer between 0 and 3"
434 raise PyCMSError(msg)
436 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
437 msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
438 raise PyCMSError(msg)
440 try:
441 if not isinstance(inputProfile, ImageCmsProfile):
442 inputProfile = ImageCmsProfile(inputProfile)
443 if not isinstance(outputProfile, ImageCmsProfile):
444 outputProfile = ImageCmsProfile(outputProfile)
445 transform = ImageCmsTransform(
446 inputProfile,
447 outputProfile,
448 im.mode,
449 outputMode,
450 renderingIntent,
451 flags=flags,
452 )
453 if inPlace:
454 transform.apply_in_place(im)
455 imOut = None
456 else:
457 imOut = transform.apply(im)
458 except (OSError, TypeError, ValueError) as v:
459 raise PyCMSError(v) from v
461 return imOut
464def getOpenProfile(
465 profileFilename: str | SupportsRead[bytes] | core.CmsProfile,
466) -> ImageCmsProfile:
467 """
468 (pyCMS) Opens an ICC profile file.
470 The PyCMSProfile object can be passed back into pyCMS for use in creating
471 transforms and such (as in ImageCms.buildTransformFromOpenProfiles()).
473 If ``profileFilename`` is not a valid filename for an ICC profile,
474 a :exc:`PyCMSError` will be raised.
476 :param profileFilename: String, as a valid filename path to the ICC profile
477 you wish to open, or a file-like object.
478 :returns: A CmsProfile class object.
479 :exception PyCMSError:
480 """
482 try:
483 return ImageCmsProfile(profileFilename)
484 except (OSError, TypeError, ValueError) as v:
485 raise PyCMSError(v) from v
488def buildTransform(
489 inputProfile: _CmsProfileCompatible,
490 outputProfile: _CmsProfileCompatible,
491 inMode: str,
492 outMode: str,
493 renderingIntent: Intent = Intent.PERCEPTUAL,
494 flags: Flags = Flags.NONE,
495) -> ImageCmsTransform:
496 """
497 (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
498 ``outputProfile``. Use applyTransform to apply the transform to a given
499 image.
501 If the input or output profiles specified are not valid filenames, a
502 :exc:`PyCMSError` will be raised. If an error occurs during creation
503 of the transform, a :exc:`PyCMSError` will be raised.
505 If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile``
506 (or by pyCMS), a :exc:`PyCMSError` will be raised.
508 This function builds and returns an ICC transform from the ``inputProfile``
509 to the ``outputProfile`` using the ``renderingIntent`` to determine what to do
510 with out-of-gamut colors. It will ONLY work for converting images that
511 are in ``inMode`` to images that are in ``outMode`` color format (PIL mode,
512 i.e. "RGB", "RGBA", "CMYK", etc.).
514 Building the transform is a fair part of the overhead in
515 ImageCms.profileToProfile(), so if you're planning on converting multiple
516 images using the same input/output settings, this can save you time.
517 Once you have a transform object, it can be used with
518 ImageCms.applyProfile() to convert images without the need to re-compute
519 the lookup table for the transform.
521 The reason pyCMS returns a class object rather than a handle directly
522 to the transform is that it needs to keep track of the PIL input/output
523 modes that the transform is meant for. These attributes are stored in
524 the ``inMode`` and ``outMode`` attributes of the object (which can be
525 manually overridden if you really want to, but I don't know of any
526 time that would be of use, or would even work).
528 :param inputProfile: String, as a valid filename path to the ICC input
529 profile you wish to use for this transform, or a profile object
530 :param outputProfile: String, as a valid filename path to the ICC output
531 profile you wish to use for this transform, or a profile object
532 :param inMode: String, as a valid PIL mode that the appropriate profile
533 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
534 :param outMode: String, as a valid PIL mode that the appropriate profile
535 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
536 :param renderingIntent: Integer (0-3) specifying the rendering intent you
537 wish to use for the transform
539 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
540 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
541 ImageCms.Intent.SATURATION = 2
542 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
544 see the pyCMS documentation for details on rendering intents and what
545 they do.
546 :param flags: Integer (0-...) specifying additional flags
547 :returns: A CmsTransform class object.
548 :exception PyCMSError:
549 """
551 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
552 msg = "renderingIntent must be an integer between 0 and 3"
553 raise PyCMSError(msg)
555 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
556 msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
557 raise PyCMSError(msg)
559 try:
560 if not isinstance(inputProfile, ImageCmsProfile):
561 inputProfile = ImageCmsProfile(inputProfile)
562 if not isinstance(outputProfile, ImageCmsProfile):
563 outputProfile = ImageCmsProfile(outputProfile)
564 return ImageCmsTransform(
565 inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
566 )
567 except (OSError, TypeError, ValueError) as v:
568 raise PyCMSError(v) from v
571def buildProofTransform(
572 inputProfile: _CmsProfileCompatible,
573 outputProfile: _CmsProfileCompatible,
574 proofProfile: _CmsProfileCompatible,
575 inMode: str,
576 outMode: str,
577 renderingIntent: Intent = Intent.PERCEPTUAL,
578 proofRenderingIntent: Intent = Intent.ABSOLUTE_COLORIMETRIC,
579 flags: Flags = Flags.SOFTPROOFING,
580) -> ImageCmsTransform:
581 """
582 (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
583 ``outputProfile``, but tries to simulate the result that would be
584 obtained on the ``proofProfile`` device.
586 If the input, output, or proof profiles specified are not valid
587 filenames, a :exc:`PyCMSError` will be raised.
589 If an error occurs during creation of the transform,
590 a :exc:`PyCMSError` will be raised.
592 If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile``
593 (or by pyCMS), a :exc:`PyCMSError` will be raised.
595 This function builds and returns an ICC transform from the ``inputProfile``
596 to the ``outputProfile``, but tries to simulate the result that would be
597 obtained on the ``proofProfile`` device using ``renderingIntent`` and
598 ``proofRenderingIntent`` to determine what to do with out-of-gamut
599 colors. This is known as "soft-proofing". It will ONLY work for
600 converting images that are in ``inMode`` to images that are in outMode
601 color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.).
603 Usage of the resulting transform object is exactly the same as with
604 ImageCms.buildTransform().
606 Proof profiling is generally used when using an output device to get a
607 good idea of what the final printed/displayed image would look like on
608 the ``proofProfile`` device when it's quicker and easier to use the
609 output device for judging color. Generally, this means that the
610 output device is a monitor, or a dye-sub printer (etc.), and the simulated
611 device is something more expensive, complicated, or time consuming
612 (making it difficult to make a real print for color judgement purposes).
614 Soft-proofing basically functions by adjusting the colors on the
615 output device to match the colors of the device being simulated. However,
616 when the simulated device has a much wider gamut than the output
617 device, you may obtain marginal results.
619 :param inputProfile: String, as a valid filename path to the ICC input
620 profile you wish to use for this transform, or a profile object
621 :param outputProfile: String, as a valid filename path to the ICC output
622 (monitor, usually) profile you wish to use for this transform, or a
623 profile object
624 :param proofProfile: String, as a valid filename path to the ICC proof
625 profile you wish to use for this transform, or a profile object
626 :param inMode: String, as a valid PIL mode that the appropriate profile
627 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
628 :param outMode: String, as a valid PIL mode that the appropriate profile
629 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
630 :param renderingIntent: Integer (0-3) specifying the rendering intent you
631 wish to use for the input->proof (simulated) transform
633 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
634 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
635 ImageCms.Intent.SATURATION = 2
636 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
638 see the pyCMS documentation for details on rendering intents and what
639 they do.
640 :param proofRenderingIntent: Integer (0-3) specifying the rendering intent
641 you wish to use for proof->output transform
643 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
644 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
645 ImageCms.Intent.SATURATION = 2
646 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
648 see the pyCMS documentation for details on rendering intents and what
649 they do.
650 :param flags: Integer (0-...) specifying additional flags
651 :returns: A CmsTransform class object.
652 :exception PyCMSError:
653 """
655 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
656 msg = "renderingIntent must be an integer between 0 and 3"
657 raise PyCMSError(msg)
659 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
660 msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
661 raise PyCMSError(msg)
663 try:
664 if not isinstance(inputProfile, ImageCmsProfile):
665 inputProfile = ImageCmsProfile(inputProfile)
666 if not isinstance(outputProfile, ImageCmsProfile):
667 outputProfile = ImageCmsProfile(outputProfile)
668 if not isinstance(proofProfile, ImageCmsProfile):
669 proofProfile = ImageCmsProfile(proofProfile)
670 return ImageCmsTransform(
671 inputProfile,
672 outputProfile,
673 inMode,
674 outMode,
675 renderingIntent,
676 proofProfile,
677 proofRenderingIntent,
678 flags,
679 )
680 except (OSError, TypeError, ValueError) as v:
681 raise PyCMSError(v) from v
684buildTransformFromOpenProfiles = buildTransform
685buildProofTransformFromOpenProfiles = buildProofTransform
688def applyTransform(
689 im: Image.Image, transform: ImageCmsTransform, inPlace: bool = False
690) -> Image.Image | None:
691 """
692 (pyCMS) Applies a transform to a given image.
694 If ``im.mode != transform.input_mode``, a :exc:`PyCMSError` is raised.
696 If ``inPlace`` is ``True`` and ``transform.input_mode != transform.output_mode``, a
697 :exc:`PyCMSError` is raised.
699 If ``im.mode``, ``transform.input_mode`` or ``transform.output_mode`` is not
700 supported by pyCMSdll or the profiles you used for the transform, a
701 :exc:`PyCMSError` is raised.
703 If an error occurs while the transform is being applied,
704 a :exc:`PyCMSError` is raised.
706 This function applies a pre-calculated transform (from
707 ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
708 to an image. The transform can be used for multiple images, saving
709 considerable calculation time if doing the same conversion multiple times.
711 If you want to modify im in-place instead of receiving a new image as
712 the return value, set ``inPlace`` to ``True``. This can only be done if
713 ``transform.input_mode`` and ``transform.output_mode`` are the same, because we
714 can't change the mode in-place (the buffer sizes for some modes are
715 different). The default behavior is to return a new :py:class:`~PIL.Image.Image`
716 object of the same dimensions in mode ``transform.output_mode``.
718 :param im: An :py:class:`~PIL.Image.Image` object, and ``im.mode`` must be the same
719 as the ``input_mode`` supported by the transform.
720 :param transform: A valid CmsTransform class object
721 :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is
722 returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the
723 transform applied is returned (and ``im`` is not changed). The default is
724 ``False``.
725 :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object,
726 depending on the value of ``inPlace``. The profile will be returned in
727 the image's ``info['icc_profile']``.
728 :exception PyCMSError:
729 """
731 try:
732 if inPlace:
733 transform.apply_in_place(im)
734 imOut = None
735 else:
736 imOut = transform.apply(im)
737 except (TypeError, ValueError) as v:
738 raise PyCMSError(v) from v
740 return imOut
743def createProfile(
744 colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = 0
745) -> core.CmsProfile:
746 """
747 (pyCMS) Creates a profile.
749 If colorSpace not in ``["LAB", "XYZ", "sRGB"]``,
750 a :exc:`PyCMSError` is raised.
752 If using LAB and ``colorTemp`` is not a positive integer,
753 a :exc:`PyCMSError` is raised.
755 If an error occurs while creating the profile,
756 a :exc:`PyCMSError` is raised.
758 Use this function to create common profiles on-the-fly instead of
759 having to supply a profile on disk and knowing the path to it. It
760 returns a normal CmsProfile object that can be passed to
761 ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
762 to images.
764 :param colorSpace: String, the color space of the profile you wish to
765 create.
766 Currently only "LAB", "XYZ", and "sRGB" are supported.
767 :param colorTemp: Positive number for the white point for the profile, in
768 degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50
769 illuminant if omitted (5000k). colorTemp is ONLY applied to LAB
770 profiles, and is ignored for XYZ and sRGB.
771 :returns: A CmsProfile class object
772 :exception PyCMSError:
773 """
775 if colorSpace not in ["LAB", "XYZ", "sRGB"]:
776 msg = (
777 f"Color space not supported for on-the-fly profile creation ({colorSpace})"
778 )
779 raise PyCMSError(msg)
781 if colorSpace == "LAB":
782 try:
783 colorTemp = float(colorTemp)
784 except (TypeError, ValueError) as e:
785 msg = f'Color temperature must be numeric, "{colorTemp}" not valid'
786 raise PyCMSError(msg) from e
788 try:
789 return core.createProfile(colorSpace, colorTemp)
790 except (TypeError, ValueError) as v:
791 raise PyCMSError(v) from v
794def getProfileName(profile: _CmsProfileCompatible) -> str:
795 """
797 (pyCMS) Gets the internal product name for the given profile.
799 If ``profile`` isn't a valid CmsProfile object or filename to a profile,
800 a :exc:`PyCMSError` is raised If an error occurs while trying
801 to obtain the name tag, a :exc:`PyCMSError` is raised.
803 Use this function to obtain the INTERNAL name of the profile (stored
804 in an ICC tag in the profile itself), usually the one used when the
805 profile was originally created. Sometimes this tag also contains
806 additional information supplied by the creator.
808 :param profile: EITHER a valid CmsProfile object, OR a string of the
809 filename of an ICC profile.
810 :returns: A string containing the internal name of the profile as stored
811 in an ICC tag.
812 :exception PyCMSError:
813 """
815 try:
816 # add an extra newline to preserve pyCMS compatibility
817 if not isinstance(profile, ImageCmsProfile):
818 profile = ImageCmsProfile(profile)
819 # do it in python, not c.
820 # // name was "%s - %s" (model, manufacturer) || Description ,
821 # // but if the Model and Manufacturer were the same or the model
822 # // was long, Just the model, in 1.x
823 model = profile.profile.model
824 manufacturer = profile.profile.manufacturer
826 if not (model or manufacturer):
827 return (profile.profile.profile_description or "") + "\n"
828 if not manufacturer or (model and len(model) > 30):
829 return f"{model}\n"
830 return f"{model} - {manufacturer}\n"
832 except (AttributeError, OSError, TypeError, ValueError) as v:
833 raise PyCMSError(v) from v
836def getProfileInfo(profile: _CmsProfileCompatible) -> str:
837 """
838 (pyCMS) Gets the internal product information for the given profile.
840 If ``profile`` isn't a valid CmsProfile object or filename to a profile,
841 a :exc:`PyCMSError` is raised.
843 If an error occurs while trying to obtain the info tag,
844 a :exc:`PyCMSError` is raised.
846 Use this function to obtain the information stored in the profile's
847 info tag. This often contains details about the profile, and how it
848 was created, as supplied by the creator.
850 :param profile: EITHER a valid CmsProfile object, OR a string of the
851 filename of an ICC profile.
852 :returns: A string containing the internal profile information stored in
853 an ICC tag.
854 :exception PyCMSError:
855 """
857 try:
858 if not isinstance(profile, ImageCmsProfile):
859 profile = ImageCmsProfile(profile)
860 # add an extra newline to preserve pyCMS compatibility
861 # Python, not C. the white point bits weren't working well,
862 # so skipping.
863 # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
864 description = profile.profile.profile_description
865 cpright = profile.profile.copyright
866 elements = [element for element in (description, cpright) if element]
867 return "\r\n\r\n".join(elements) + "\r\n\r\n"
869 except (AttributeError, OSError, TypeError, ValueError) as v:
870 raise PyCMSError(v) from v
873def getProfileCopyright(profile: _CmsProfileCompatible) -> str:
874 """
875 (pyCMS) Gets the copyright for the given profile.
877 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
878 :exc:`PyCMSError` is raised.
880 If an error occurs while trying to obtain the copyright tag,
881 a :exc:`PyCMSError` is raised.
883 Use this function to obtain the information stored in the profile's
884 copyright tag.
886 :param profile: EITHER a valid CmsProfile object, OR a string of the
887 filename of an ICC profile.
888 :returns: A string containing the internal profile information stored in
889 an ICC tag.
890 :exception PyCMSError:
891 """
892 try:
893 # add an extra newline to preserve pyCMS compatibility
894 if not isinstance(profile, ImageCmsProfile):
895 profile = ImageCmsProfile(profile)
896 return (profile.profile.copyright or "") + "\n"
897 except (AttributeError, OSError, TypeError, ValueError) as v:
898 raise PyCMSError(v) from v
901def getProfileManufacturer(profile: _CmsProfileCompatible) -> str:
902 """
903 (pyCMS) Gets the manufacturer for the given profile.
905 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
906 :exc:`PyCMSError` is raised.
908 If an error occurs while trying to obtain the manufacturer tag, a
909 :exc:`PyCMSError` is raised.
911 Use this function to obtain the information stored in the profile's
912 manufacturer tag.
914 :param profile: EITHER a valid CmsProfile object, OR a string of the
915 filename of an ICC profile.
916 :returns: A string containing the internal profile information stored in
917 an ICC tag.
918 :exception PyCMSError:
919 """
920 try:
921 # add an extra newline to preserve pyCMS compatibility
922 if not isinstance(profile, ImageCmsProfile):
923 profile = ImageCmsProfile(profile)
924 return (profile.profile.manufacturer or "") + "\n"
925 except (AttributeError, OSError, TypeError, ValueError) as v:
926 raise PyCMSError(v) from v
929def getProfileModel(profile: _CmsProfileCompatible) -> str:
930 """
931 (pyCMS) Gets the model for the given profile.
933 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
934 :exc:`PyCMSError` is raised.
936 If an error occurs while trying to obtain the model tag,
937 a :exc:`PyCMSError` is raised.
939 Use this function to obtain the information stored in the profile's
940 model tag.
942 :param profile: EITHER a valid CmsProfile object, OR a string of the
943 filename of an ICC profile.
944 :returns: A string containing the internal profile information stored in
945 an ICC tag.
946 :exception PyCMSError:
947 """
949 try:
950 # add an extra newline to preserve pyCMS compatibility
951 if not isinstance(profile, ImageCmsProfile):
952 profile = ImageCmsProfile(profile)
953 return (profile.profile.model or "") + "\n"
954 except (AttributeError, OSError, TypeError, ValueError) as v:
955 raise PyCMSError(v) from v
958def getProfileDescription(profile: _CmsProfileCompatible) -> str:
959 """
960 (pyCMS) Gets the description for the given profile.
962 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
963 :exc:`PyCMSError` is raised.
965 If an error occurs while trying to obtain the description tag,
966 a :exc:`PyCMSError` is raised.
968 Use this function to obtain the information stored in the profile's
969 description tag.
971 :param profile: EITHER a valid CmsProfile object, OR a string of the
972 filename of an ICC profile.
973 :returns: A string containing the internal profile information stored in an
974 ICC tag.
975 :exception PyCMSError:
976 """
978 try:
979 # add an extra newline to preserve pyCMS compatibility
980 if not isinstance(profile, ImageCmsProfile):
981 profile = ImageCmsProfile(profile)
982 return (profile.profile.profile_description or "") + "\n"
983 except (AttributeError, OSError, TypeError, ValueError) as v:
984 raise PyCMSError(v) from v
987def getDefaultIntent(profile: _CmsProfileCompatible) -> int:
988 """
989 (pyCMS) Gets the default intent name for the given profile.
991 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
992 :exc:`PyCMSError` is raised.
994 If an error occurs while trying to obtain the default intent, a
995 :exc:`PyCMSError` is raised.
997 Use this function to determine the default (and usually best optimized)
998 rendering intent for this profile. Most profiles support multiple
999 rendering intents, but are intended mostly for one type of conversion.
1000 If you wish to use a different intent than returned, use
1001 ImageCms.isIntentSupported() to verify it will work first.
1003 :param profile: EITHER a valid CmsProfile object, OR a string of the
1004 filename of an ICC profile.
1005 :returns: Integer 0-3 specifying the default rendering intent for this
1006 profile.
1008 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
1009 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
1010 ImageCms.Intent.SATURATION = 2
1011 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
1013 see the pyCMS documentation for details on rendering intents and what
1014 they do.
1015 :exception PyCMSError:
1016 """
1018 try:
1019 if not isinstance(profile, ImageCmsProfile):
1020 profile = ImageCmsProfile(profile)
1021 return profile.profile.rendering_intent
1022 except (AttributeError, OSError, TypeError, ValueError) as v:
1023 raise PyCMSError(v) from v
1026def isIntentSupported(
1027 profile: _CmsProfileCompatible, intent: Intent, direction: Direction
1028) -> Literal[-1, 1]:
1029 """
1030 (pyCMS) Checks if a given intent is supported.
1032 Use this function to verify that you can use your desired
1033 ``intent`` with ``profile``, and that ``profile`` can be used for the
1034 input/output/proof profile as you desire.
1036 Some profiles are created specifically for one "direction", can cannot
1037 be used for others. Some profiles can only be used for certain
1038 rendering intents, so it's best to either verify this before trying
1039 to create a transform with them (using this function), or catch the
1040 potential :exc:`PyCMSError` that will occur if they don't
1041 support the modes you select.
1043 :param profile: EITHER a valid CmsProfile object, OR a string of the
1044 filename of an ICC profile.
1045 :param intent: Integer (0-3) specifying the rendering intent you wish to
1046 use with this profile
1048 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
1049 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
1050 ImageCms.Intent.SATURATION = 2
1051 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
1053 see the pyCMS documentation for details on rendering intents and what
1054 they do.
1055 :param direction: Integer specifying if the profile is to be used for
1056 input, output, or proof
1058 INPUT = 0 (or use ImageCms.Direction.INPUT)
1059 OUTPUT = 1 (or use ImageCms.Direction.OUTPUT)
1060 PROOF = 2 (or use ImageCms.Direction.PROOF)
1062 :returns: 1 if the intent/direction are supported, -1 if they are not.
1063 :exception PyCMSError:
1064 """
1066 try:
1067 if not isinstance(profile, ImageCmsProfile):
1068 profile = ImageCmsProfile(profile)
1069 # FIXME: I get different results for the same data w. different
1070 # compilers. Bug in LittleCMS or in the binding?
1071 if profile.profile.is_intent_supported(intent, direction):
1072 return 1
1073 else:
1074 return -1
1075 except (AttributeError, OSError, TypeError, ValueError) as v:
1076 raise PyCMSError(v) from v