Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ImageCms.py 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  1. # The Python Imaging Library.
  2. # $Id$
  3. # Optional color management support, based on Kevin Cazabon's PyCMS
  4. # library.
  5. # History:
  6. # 2009-03-08 fl Added to PIL.
  7. # Copyright (C) 2002-2003 Kevin Cazabon
  8. # Copyright (c) 2009 by Fredrik Lundh
  9. # Copyright (c) 2013 by Eric Soroos
  10. # See the README file for information on usage and redistribution. See
  11. # below for the original description.
  12. import sys
  13. from enum import IntEnum
  14. from PIL import Image
  15. from ._deprecate import deprecate
  16. try:
  17. from PIL import _imagingcms
  18. except ImportError as ex:
  19. # Allow error import for doc purposes, but error out when accessing
  20. # anything in core.
  21. from ._util import DeferredError
  22. _imagingcms = DeferredError(ex)
  23. DESCRIPTION = """
  24. pyCMS
  25. a Python / PIL interface to the littleCMS ICC Color Management System
  26. Copyright (C) 2002-2003 Kevin Cazabon
  27. kevin@cazabon.com
  28. https://www.cazabon.com
  29. pyCMS home page: https://www.cazabon.com/pyCMS
  30. littleCMS home page: https://www.littlecms.com
  31. (littleCMS is Copyright (C) 1998-2001 Marti Maria)
  32. Originally released under LGPL. Graciously donated to PIL in
  33. March 2009, for distribution under the standard PIL license
  34. The pyCMS.py module provides a "clean" interface between Python/PIL and
  35. pyCMSdll, taking care of some of the more complex handling of the direct
  36. pyCMSdll functions, as well as error-checking and making sure that all
  37. relevant data is kept together.
  38. While it is possible to call pyCMSdll functions directly, it's not highly
  39. recommended.
  40. Version History:
  41. 1.0.0 pil Oct 2013 Port to LCMS 2.
  42. 0.1.0 pil mod March 10, 2009
  43. Renamed display profile to proof profile. The proof
  44. profile is the profile of the device that is being
  45. simulated, not the profile of the device which is
  46. actually used to display/print the final simulation
  47. (that'd be the output profile) - also see LCMSAPI.txt
  48. input colorspace -> using 'renderingIntent' -> proof
  49. colorspace -> using 'proofRenderingIntent' -> output
  50. colorspace
  51. Added LCMS FLAGS support.
  52. Added FLAGS["SOFTPROOFING"] as default flag for
  53. buildProofTransform (otherwise the proof profile/intent
  54. would be ignored).
  55. 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms
  56. 0.0.2 alpha Jan 6, 2002
  57. Added try/except statements around type() checks of
  58. potential CObjects... Python won't let you use type()
  59. on them, and raises a TypeError (stupid, if you ask
  60. me!)
  61. Added buildProofTransformFromOpenProfiles() function.
  62. Additional fixes in DLL, see DLL code for details.
  63. 0.0.1 alpha first public release, Dec. 26, 2002
  64. Known to-do list with current version (of Python interface, not pyCMSdll):
  65. none
  66. """
  67. VERSION = "1.0.0 pil"
  68. # --------------------------------------------------------------------.
  69. core = _imagingcms
  70. #
  71. # intent/direction values
  72. class Intent(IntEnum):
  73. PERCEPTUAL = 0
  74. RELATIVE_COLORIMETRIC = 1
  75. SATURATION = 2
  76. ABSOLUTE_COLORIMETRIC = 3
  77. class Direction(IntEnum):
  78. INPUT = 0
  79. OUTPUT = 1
  80. PROOF = 2
  81. def __getattr__(name):
  82. for enum, prefix in {Intent: "INTENT_", Direction: "DIRECTION_"}.items():
  83. if name.startswith(prefix):
  84. name = name[len(prefix) :]
  85. if name in enum.__members__:
  86. deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
  87. return enum[name]
  88. msg = f"module '{__name__}' has no attribute '{name}'"
  89. raise AttributeError(msg)
  90. #
  91. # flags
  92. FLAGS = {
  93. "MATRIXINPUT": 1,
  94. "MATRIXOUTPUT": 2,
  95. "MATRIXONLY": (1 | 2),
  96. "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
  97. # Don't create prelinearization tables on precalculated transforms
  98. # (internal use):
  99. "NOPRELINEARIZATION": 16,
  100. "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
  101. "NOTCACHE": 64, # Inhibit 1-pixel cache
  102. "NOTPRECALC": 256,
  103. "NULLTRANSFORM": 512, # Don't transform anyway
  104. "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy
  105. "LOWRESPRECALC": 2048, # Use less memory to minimize resources
  106. "WHITEBLACKCOMPENSATION": 8192,
  107. "BLACKPOINTCOMPENSATION": 8192,
  108. "GAMUTCHECK": 4096, # Out of Gamut alarm
  109. "SOFTPROOFING": 16384, # Do softproofing
  110. "PRESERVEBLACK": 32768, # Black preservation
  111. "NODEFAULTRESOURCEDEF": 16777216, # CRD special
  112. "GRIDPOINTS": lambda n: (n & 0xFF) << 16, # Gridpoints
  113. }
  114. _MAX_FLAG = 0
  115. for flag in FLAGS.values():
  116. if isinstance(flag, int):
  117. _MAX_FLAG = _MAX_FLAG | flag
  118. # --------------------------------------------------------------------.
  119. # Experimental PIL-level API
  120. # --------------------------------------------------------------------.
  121. ##
  122. # Profile.
  123. class ImageCmsProfile:
  124. def __init__(self, profile):
  125. """
  126. :param profile: Either a string representing a filename,
  127. a file like object containing a profile or a
  128. low-level profile object
  129. """
  130. if isinstance(profile, str):
  131. if sys.platform == "win32":
  132. profile_bytes_path = profile.encode()
  133. try:
  134. profile_bytes_path.decode("ascii")
  135. except UnicodeDecodeError:
  136. with open(profile, "rb") as f:
  137. self._set(core.profile_frombytes(f.read()))
  138. return
  139. self._set(core.profile_open(profile), profile)
  140. elif hasattr(profile, "read"):
  141. self._set(core.profile_frombytes(profile.read()))
  142. elif isinstance(profile, _imagingcms.CmsProfile):
  143. self._set(profile)
  144. else:
  145. msg = "Invalid type for Profile"
  146. raise TypeError(msg)
  147. def _set(self, profile, filename=None):
  148. self.profile = profile
  149. self.filename = filename
  150. if profile:
  151. self.product_name = None # profile.product_name
  152. self.product_info = None # profile.product_info
  153. else:
  154. self.product_name = None
  155. self.product_info = None
  156. def tobytes(self):
  157. """
  158. Returns the profile in a format suitable for embedding in
  159. saved images.
  160. :returns: a bytes object containing the ICC profile.
  161. """
  162. return core.profile_tobytes(self.profile)
  163. class ImageCmsTransform(Image.ImagePointHandler):
  164. """
  165. Transform. This can be used with the procedural API, or with the standard
  166. :py:func:`~PIL.Image.Image.point` method.
  167. Will return the output profile in the ``output.info['icc_profile']``.
  168. """
  169. def __init__(
  170. self,
  171. input,
  172. output,
  173. input_mode,
  174. output_mode,
  175. intent=Intent.PERCEPTUAL,
  176. proof=None,
  177. proof_intent=Intent.ABSOLUTE_COLORIMETRIC,
  178. flags=0,
  179. ):
  180. if proof is None:
  181. self.transform = core.buildTransform(
  182. input.profile, output.profile, input_mode, output_mode, intent, flags
  183. )
  184. else:
  185. self.transform = core.buildProofTransform(
  186. input.profile,
  187. output.profile,
  188. proof.profile,
  189. input_mode,
  190. output_mode,
  191. intent,
  192. proof_intent,
  193. flags,
  194. )
  195. # Note: inputMode and outputMode are for pyCMS compatibility only
  196. self.input_mode = self.inputMode = input_mode
  197. self.output_mode = self.outputMode = output_mode
  198. self.output_profile = output
  199. def point(self, im):
  200. return self.apply(im)
  201. def apply(self, im, imOut=None):
  202. im.load()
  203. if imOut is None:
  204. imOut = Image.new(self.output_mode, im.size, None)
  205. self.transform.apply(im.im.id, imOut.im.id)
  206. imOut.info["icc_profile"] = self.output_profile.tobytes()
  207. return imOut
  208. def apply_in_place(self, im):
  209. im.load()
  210. if im.mode != self.output_mode:
  211. msg = "mode mismatch"
  212. raise ValueError(msg) # wrong output mode
  213. self.transform.apply(im.im.id, im.im.id)
  214. im.info["icc_profile"] = self.output_profile.tobytes()
  215. return im
  216. def get_display_profile(handle=None):
  217. """
  218. (experimental) Fetches the profile for the current display device.
  219. :returns: ``None`` if the profile is not known.
  220. """
  221. if sys.platform != "win32":
  222. return None
  223. from PIL import ImageWin
  224. if isinstance(handle, ImageWin.HDC):
  225. profile = core.get_display_profile_win32(handle, 1)
  226. else:
  227. profile = core.get_display_profile_win32(handle or 0)
  228. if profile is None:
  229. return None
  230. return ImageCmsProfile(profile)
  231. # --------------------------------------------------------------------.
  232. # pyCMS compatible layer
  233. # --------------------------------------------------------------------.
  234. class PyCMSError(Exception):
  235. """(pyCMS) Exception class.
  236. This is used for all errors in the pyCMS API."""
  237. pass
  238. def profileToProfile(
  239. im,
  240. inputProfile,
  241. outputProfile,
  242. renderingIntent=Intent.PERCEPTUAL,
  243. outputMode=None,
  244. inPlace=False,
  245. flags=0,
  246. ):
  247. """
  248. (pyCMS) Applies an ICC transformation to a given image, mapping from
  249. ``inputProfile`` to ``outputProfile``.
  250. If the input or output profiles specified are not valid filenames, a
  251. :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and
  252. ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised.
  253. If an error occurs during application of the profiles,
  254. a :exc:`PyCMSError` will be raised.
  255. If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS),
  256. a :exc:`PyCMSError` will be raised.
  257. This function applies an ICC transformation to im from ``inputProfile``'s
  258. color space to ``outputProfile``'s color space using the specified rendering
  259. intent to decide how to handle out-of-gamut colors.
  260. ``outputMode`` can be used to specify that a color mode conversion is to
  261. be done using these profiles, but the specified profiles must be able
  262. to handle that mode. I.e., if converting im from RGB to CMYK using
  263. profiles, the input profile must handle RGB data, and the output
  264. profile must handle CMYK data.
  265. :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...)
  266. or Image.open(...), etc.)
  267. :param inputProfile: String, as a valid filename path to the ICC input
  268. profile you wish to use for this image, or a profile object
  269. :param outputProfile: String, as a valid filename path to the ICC output
  270. profile you wish to use for this image, or a profile object
  271. :param renderingIntent: Integer (0-3) specifying the rendering intent you
  272. wish to use for the transform
  273. ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
  274. ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
  275. ImageCms.Intent.SATURATION = 2
  276. ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
  277. see the pyCMS documentation for details on rendering intents and what
  278. they do.
  279. :param outputMode: A valid PIL mode for the output image (i.e. "RGB",
  280. "CMYK", etc.). Note: if rendering the image "inPlace", outputMode
  281. MUST be the same mode as the input, or omitted completely. If
  282. omitted, the outputMode will be the same as the mode of the input
  283. image (im.mode)
  284. :param inPlace: Boolean. If ``True``, the original image is modified in-place,
  285. and ``None`` is returned. If ``False`` (default), a new
  286. :py:class:`~PIL.Image.Image` object is returned with the transform applied.
  287. :param flags: Integer (0-...) specifying additional flags
  288. :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on
  289. the value of ``inPlace``
  290. :exception PyCMSError:
  291. """
  292. if outputMode is None:
  293. outputMode = im.mode
  294. if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
  295. msg = "renderingIntent must be an integer between 0 and 3"
  296. raise PyCMSError(msg)
  297. if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
  298. msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
  299. raise PyCMSError(msg)
  300. try:
  301. if not isinstance(inputProfile, ImageCmsProfile):
  302. inputProfile = ImageCmsProfile(inputProfile)
  303. if not isinstance(outputProfile, ImageCmsProfile):
  304. outputProfile = ImageCmsProfile(outputProfile)
  305. transform = ImageCmsTransform(
  306. inputProfile,
  307. outputProfile,
  308. im.mode,
  309. outputMode,
  310. renderingIntent,
  311. flags=flags,
  312. )
  313. if inPlace:
  314. transform.apply_in_place(im)
  315. imOut = None
  316. else:
  317. imOut = transform.apply(im)
  318. except (OSError, TypeError, ValueError) as v:
  319. raise PyCMSError(v) from v
  320. return imOut
  321. def getOpenProfile(profileFilename):
  322. """
  323. (pyCMS) Opens an ICC profile file.
  324. The PyCMSProfile object can be passed back into pyCMS for use in creating
  325. transforms and such (as in ImageCms.buildTransformFromOpenProfiles()).
  326. If ``profileFilename`` is not a valid filename for an ICC profile,
  327. a :exc:`PyCMSError` will be raised.
  328. :param profileFilename: String, as a valid filename path to the ICC profile
  329. you wish to open, or a file-like object.
  330. :returns: A CmsProfile class object.
  331. :exception PyCMSError:
  332. """
  333. try:
  334. return ImageCmsProfile(profileFilename)
  335. except (OSError, TypeError, ValueError) as v:
  336. raise PyCMSError(v) from v
  337. def buildTransform(
  338. inputProfile,
  339. outputProfile,
  340. inMode,
  341. outMode,
  342. renderingIntent=Intent.PERCEPTUAL,
  343. flags=0,
  344. ):
  345. """
  346. (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
  347. ``outputProfile``. Use applyTransform to apply the transform to a given
  348. image.
  349. If the input or output profiles specified are not valid filenames, a
  350. :exc:`PyCMSError` will be raised. If an error occurs during creation
  351. of the transform, a :exc:`PyCMSError` will be raised.
  352. If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile``
  353. (or by pyCMS), a :exc:`PyCMSError` will be raised.
  354. This function builds and returns an ICC transform from the ``inputProfile``
  355. to the ``outputProfile`` using the ``renderingIntent`` to determine what to do
  356. with out-of-gamut colors. It will ONLY work for converting images that
  357. are in ``inMode`` to images that are in ``outMode`` color format (PIL mode,
  358. i.e. "RGB", "RGBA", "CMYK", etc.).
  359. Building the transform is a fair part of the overhead in
  360. ImageCms.profileToProfile(), so if you're planning on converting multiple
  361. images using the same input/output settings, this can save you time.
  362. Once you have a transform object, it can be used with
  363. ImageCms.applyProfile() to convert images without the need to re-compute
  364. the lookup table for the transform.
  365. The reason pyCMS returns a class object rather than a handle directly
  366. to the transform is that it needs to keep track of the PIL input/output
  367. modes that the transform is meant for. These attributes are stored in
  368. the ``inMode`` and ``outMode`` attributes of the object (which can be
  369. manually overridden if you really want to, but I don't know of any
  370. time that would be of use, or would even work).
  371. :param inputProfile: String, as a valid filename path to the ICC input
  372. profile you wish to use for this transform, or a profile object
  373. :param outputProfile: String, as a valid filename path to the ICC output
  374. profile you wish to use for this transform, or a profile object
  375. :param inMode: String, as a valid PIL mode that the appropriate profile
  376. also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
  377. :param outMode: String, as a valid PIL mode that the appropriate profile
  378. also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
  379. :param renderingIntent: Integer (0-3) specifying the rendering intent you
  380. wish to use for the transform
  381. ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
  382. ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
  383. ImageCms.Intent.SATURATION = 2
  384. ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
  385. see the pyCMS documentation for details on rendering intents and what
  386. they do.
  387. :param flags: Integer (0-...) specifying additional flags
  388. :returns: A CmsTransform class object.
  389. :exception PyCMSError:
  390. """
  391. if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
  392. msg = "renderingIntent must be an integer between 0 and 3"
  393. raise PyCMSError(msg)
  394. if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
  395. msg = "flags must be an integer between 0 and %s" + _MAX_FLAG
  396. raise PyCMSError(msg)
  397. try:
  398. if not isinstance(inputProfile, ImageCmsProfile):
  399. inputProfile = ImageCmsProfile(inputProfile)
  400. if not isinstance(outputProfile, ImageCmsProfile):
  401. outputProfile = ImageCmsProfile(outputProfile)
  402. return ImageCmsTransform(
  403. inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
  404. )
  405. except (OSError, TypeError, ValueError) as v:
  406. raise PyCMSError(v) from v
  407. def buildProofTransform(
  408. inputProfile,
  409. outputProfile,
  410. proofProfile,
  411. inMode,
  412. outMode,
  413. renderingIntent=Intent.PERCEPTUAL,
  414. proofRenderingIntent=Intent.ABSOLUTE_COLORIMETRIC,
  415. flags=FLAGS["SOFTPROOFING"],
  416. ):
  417. """
  418. (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
  419. ``outputProfile``, but tries to simulate the result that would be
  420. obtained on the ``proofProfile`` device.
  421. If the input, output, or proof profiles specified are not valid
  422. filenames, a :exc:`PyCMSError` will be raised.
  423. If an error occurs during creation of the transform,
  424. a :exc:`PyCMSError` will be raised.
  425. If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile``
  426. (or by pyCMS), a :exc:`PyCMSError` will be raised.
  427. This function builds and returns an ICC transform from the ``inputProfile``
  428. to the ``outputProfile``, but tries to simulate the result that would be
  429. obtained on the ``proofProfile`` device using ``renderingIntent`` and
  430. ``proofRenderingIntent`` to determine what to do with out-of-gamut
  431. colors. This is known as "soft-proofing". It will ONLY work for
  432. converting images that are in ``inMode`` to images that are in outMode
  433. color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.).
  434. Usage of the resulting transform object is exactly the same as with
  435. ImageCms.buildTransform().
  436. Proof profiling is generally used when using an output device to get a
  437. good idea of what the final printed/displayed image would look like on
  438. the ``proofProfile`` device when it's quicker and easier to use the
  439. output device for judging color. Generally, this means that the
  440. output device is a monitor, or a dye-sub printer (etc.), and the simulated
  441. device is something more expensive, complicated, or time consuming
  442. (making it difficult to make a real print for color judgement purposes).
  443. Soft-proofing basically functions by adjusting the colors on the
  444. output device to match the colors of the device being simulated. However,
  445. when the simulated device has a much wider gamut than the output
  446. device, you may obtain marginal results.
  447. :param inputProfile: String, as a valid filename path to the ICC input
  448. profile you wish to use for this transform, or a profile object
  449. :param outputProfile: String, as a valid filename path to the ICC output
  450. (monitor, usually) profile you wish to use for this transform, or a
  451. profile object
  452. :param proofProfile: String, as a valid filename path to the ICC proof
  453. profile you wish to use for this transform, or a profile object
  454. :param inMode: String, as a valid PIL mode that the appropriate profile
  455. also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
  456. :param outMode: String, as a valid PIL mode that the appropriate profile
  457. also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
  458. :param renderingIntent: Integer (0-3) specifying the rendering intent you
  459. wish to use for the input->proof (simulated) transform
  460. ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
  461. ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
  462. ImageCms.Intent.SATURATION = 2
  463. ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
  464. see the pyCMS documentation for details on rendering intents and what
  465. they do.
  466. :param proofRenderingIntent: Integer (0-3) specifying the rendering intent
  467. you wish to use for proof->output transform
  468. ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
  469. ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
  470. ImageCms.Intent.SATURATION = 2
  471. ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
  472. see the pyCMS documentation for details on rendering intents and what
  473. they do.
  474. :param flags: Integer (0-...) specifying additional flags
  475. :returns: A CmsTransform class object.
  476. :exception PyCMSError:
  477. """
  478. if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
  479. msg = "renderingIntent must be an integer between 0 and 3"
  480. raise PyCMSError(msg)
  481. if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
  482. msg = "flags must be an integer between 0 and %s" + _MAX_FLAG
  483. raise PyCMSError(msg)
  484. try:
  485. if not isinstance(inputProfile, ImageCmsProfile):
  486. inputProfile = ImageCmsProfile(inputProfile)
  487. if not isinstance(outputProfile, ImageCmsProfile):
  488. outputProfile = ImageCmsProfile(outputProfile)
  489. if not isinstance(proofProfile, ImageCmsProfile):
  490. proofProfile = ImageCmsProfile(proofProfile)
  491. return ImageCmsTransform(
  492. inputProfile,
  493. outputProfile,
  494. inMode,
  495. outMode,
  496. renderingIntent,
  497. proofProfile,
  498. proofRenderingIntent,
  499. flags,
  500. )
  501. except (OSError, TypeError, ValueError) as v:
  502. raise PyCMSError(v) from v
  503. buildTransformFromOpenProfiles = buildTransform
  504. buildProofTransformFromOpenProfiles = buildProofTransform
  505. def applyTransform(im, transform, inPlace=False):
  506. """
  507. (pyCMS) Applies a transform to a given image.
  508. If ``im.mode != transform.inMode``, a :exc:`PyCMSError` is raised.
  509. If ``inPlace`` is ``True`` and ``transform.inMode != transform.outMode``, a
  510. :exc:`PyCMSError` is raised.
  511. If ``im.mode``, ``transform.inMode`` or ``transform.outMode`` is not
  512. supported by pyCMSdll or the profiles you used for the transform, a
  513. :exc:`PyCMSError` is raised.
  514. If an error occurs while the transform is being applied,
  515. a :exc:`PyCMSError` is raised.
  516. This function applies a pre-calculated transform (from
  517. ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
  518. to an image. The transform can be used for multiple images, saving
  519. considerable calculation time if doing the same conversion multiple times.
  520. If you want to modify im in-place instead of receiving a new image as
  521. the return value, set ``inPlace`` to ``True``. This can only be done if
  522. ``transform.inMode`` and ``transform.outMode`` are the same, because we can't
  523. change the mode in-place (the buffer sizes for some modes are
  524. different). The default behavior is to return a new :py:class:`~PIL.Image.Image`
  525. object of the same dimensions in mode ``transform.outMode``.
  526. :param im: An :py:class:`~PIL.Image.Image` object, and im.mode must be the same
  527. as the ``inMode`` supported by the transform.
  528. :param transform: A valid CmsTransform class object
  529. :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is
  530. returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the
  531. transform applied is returned (and ``im`` is not changed). The default is
  532. ``False``.
  533. :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object,
  534. depending on the value of ``inPlace``. The profile will be returned in
  535. the image's ``info['icc_profile']``.
  536. :exception PyCMSError:
  537. """
  538. try:
  539. if inPlace:
  540. transform.apply_in_place(im)
  541. imOut = None
  542. else:
  543. imOut = transform.apply(im)
  544. except (TypeError, ValueError) as v:
  545. raise PyCMSError(v) from v
  546. return imOut
  547. def createProfile(colorSpace, colorTemp=-1):
  548. """
  549. (pyCMS) Creates a profile.
  550. If colorSpace not in ``["LAB", "XYZ", "sRGB"]``,
  551. a :exc:`PyCMSError` is raised.
  552. If using LAB and ``colorTemp`` is not a positive integer,
  553. a :exc:`PyCMSError` is raised.
  554. If an error occurs while creating the profile,
  555. a :exc:`PyCMSError` is raised.
  556. Use this function to create common profiles on-the-fly instead of
  557. having to supply a profile on disk and knowing the path to it. It
  558. returns a normal CmsProfile object that can be passed to
  559. ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
  560. to images.
  561. :param colorSpace: String, the color space of the profile you wish to
  562. create.
  563. Currently only "LAB", "XYZ", and "sRGB" are supported.
  564. :param colorTemp: Positive integer for the white point for the profile, in
  565. degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50
  566. illuminant if omitted (5000k). colorTemp is ONLY applied to LAB
  567. profiles, and is ignored for XYZ and sRGB.
  568. :returns: A CmsProfile class object
  569. :exception PyCMSError:
  570. """
  571. if colorSpace not in ["LAB", "XYZ", "sRGB"]:
  572. msg = (
  573. f"Color space not supported for on-the-fly profile creation ({colorSpace})"
  574. )
  575. raise PyCMSError(msg)
  576. if colorSpace == "LAB":
  577. try:
  578. colorTemp = float(colorTemp)
  579. except (TypeError, ValueError) as e:
  580. msg = f'Color temperature must be numeric, "{colorTemp}" not valid'
  581. raise PyCMSError(msg) from e
  582. try:
  583. return core.createProfile(colorSpace, colorTemp)
  584. except (TypeError, ValueError) as v:
  585. raise PyCMSError(v) from v
  586. def getProfileName(profile):
  587. """
  588. (pyCMS) Gets the internal product name for the given profile.
  589. If ``profile`` isn't a valid CmsProfile object or filename to a profile,
  590. a :exc:`PyCMSError` is raised If an error occurs while trying
  591. to obtain the name tag, a :exc:`PyCMSError` is raised.
  592. Use this function to obtain the INTERNAL name of the profile (stored
  593. in an ICC tag in the profile itself), usually the one used when the
  594. profile was originally created. Sometimes this tag also contains
  595. additional information supplied by the creator.
  596. :param profile: EITHER a valid CmsProfile object, OR a string of the
  597. filename of an ICC profile.
  598. :returns: A string containing the internal name of the profile as stored
  599. in an ICC tag.
  600. :exception PyCMSError:
  601. """
  602. try:
  603. # add an extra newline to preserve pyCMS compatibility
  604. if not isinstance(profile, ImageCmsProfile):
  605. profile = ImageCmsProfile(profile)
  606. # do it in python, not c.
  607. # // name was "%s - %s" (model, manufacturer) || Description ,
  608. # // but if the Model and Manufacturer were the same or the model
  609. # // was long, Just the model, in 1.x
  610. model = profile.profile.model
  611. manufacturer = profile.profile.manufacturer
  612. if not (model or manufacturer):
  613. return (profile.profile.profile_description or "") + "\n"
  614. if not manufacturer or len(model) > 30:
  615. return model + "\n"
  616. return f"{model} - {manufacturer}\n"
  617. except (AttributeError, OSError, TypeError, ValueError) as v:
  618. raise PyCMSError(v) from v
  619. def getProfileInfo(profile):
  620. """
  621. (pyCMS) Gets the internal product information for the given profile.
  622. If ``profile`` isn't a valid CmsProfile object or filename to a profile,
  623. a :exc:`PyCMSError` is raised.
  624. If an error occurs while trying to obtain the info tag,
  625. a :exc:`PyCMSError` is raised.
  626. Use this function to obtain the information stored in the profile's
  627. info tag. This often contains details about the profile, and how it
  628. was created, as supplied by the creator.
  629. :param profile: EITHER a valid CmsProfile object, OR a string of the
  630. filename of an ICC profile.
  631. :returns: A string containing the internal profile information stored in
  632. an ICC tag.
  633. :exception PyCMSError:
  634. """
  635. try:
  636. if not isinstance(profile, ImageCmsProfile):
  637. profile = ImageCmsProfile(profile)
  638. # add an extra newline to preserve pyCMS compatibility
  639. # Python, not C. the white point bits weren't working well,
  640. # so skipping.
  641. # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
  642. description = profile.profile.profile_description
  643. cpright = profile.profile.copyright
  644. arr = []
  645. for elt in (description, cpright):
  646. if elt:
  647. arr.append(elt)
  648. return "\r\n\r\n".join(arr) + "\r\n\r\n"
  649. except (AttributeError, OSError, TypeError, ValueError) as v:
  650. raise PyCMSError(v) from v
  651. def getProfileCopyright(profile):
  652. """
  653. (pyCMS) Gets the copyright for the given profile.
  654. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
  655. :exc:`PyCMSError` is raised.
  656. If an error occurs while trying to obtain the copyright tag,
  657. a :exc:`PyCMSError` is raised.
  658. Use this function to obtain the information stored in the profile's
  659. copyright tag.
  660. :param profile: EITHER a valid CmsProfile object, OR a string of the
  661. filename of an ICC profile.
  662. :returns: A string containing the internal profile information stored in
  663. an ICC tag.
  664. :exception PyCMSError:
  665. """
  666. try:
  667. # add an extra newline to preserve pyCMS compatibility
  668. if not isinstance(profile, ImageCmsProfile):
  669. profile = ImageCmsProfile(profile)
  670. return (profile.profile.copyright or "") + "\n"
  671. except (AttributeError, OSError, TypeError, ValueError) as v:
  672. raise PyCMSError(v) from v
  673. def getProfileManufacturer(profile):
  674. """
  675. (pyCMS) Gets the manufacturer for the given profile.
  676. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
  677. :exc:`PyCMSError` is raised.
  678. If an error occurs while trying to obtain the manufacturer tag, a
  679. :exc:`PyCMSError` is raised.
  680. Use this function to obtain the information stored in the profile's
  681. manufacturer tag.
  682. :param profile: EITHER a valid CmsProfile object, OR a string of the
  683. filename of an ICC profile.
  684. :returns: A string containing the internal profile information stored in
  685. an ICC tag.
  686. :exception PyCMSError:
  687. """
  688. try:
  689. # add an extra newline to preserve pyCMS compatibility
  690. if not isinstance(profile, ImageCmsProfile):
  691. profile = ImageCmsProfile(profile)
  692. return (profile.profile.manufacturer or "") + "\n"
  693. except (AttributeError, OSError, TypeError, ValueError) as v:
  694. raise PyCMSError(v) from v
  695. def getProfileModel(profile):
  696. """
  697. (pyCMS) Gets the model for the given profile.
  698. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
  699. :exc:`PyCMSError` is raised.
  700. If an error occurs while trying to obtain the model tag,
  701. a :exc:`PyCMSError` is raised.
  702. Use this function to obtain the information stored in the profile's
  703. model tag.
  704. :param profile: EITHER a valid CmsProfile object, OR a string of the
  705. filename of an ICC profile.
  706. :returns: A string containing the internal profile information stored in
  707. an ICC tag.
  708. :exception PyCMSError:
  709. """
  710. try:
  711. # add an extra newline to preserve pyCMS compatibility
  712. if not isinstance(profile, ImageCmsProfile):
  713. profile = ImageCmsProfile(profile)
  714. return (profile.profile.model or "") + "\n"
  715. except (AttributeError, OSError, TypeError, ValueError) as v:
  716. raise PyCMSError(v) from v
  717. def getProfileDescription(profile):
  718. """
  719. (pyCMS) Gets the description for the given profile.
  720. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
  721. :exc:`PyCMSError` is raised.
  722. If an error occurs while trying to obtain the description tag,
  723. a :exc:`PyCMSError` is raised.
  724. Use this function to obtain the information stored in the profile's
  725. description tag.
  726. :param profile: EITHER a valid CmsProfile object, OR a string of the
  727. filename of an ICC profile.
  728. :returns: A string containing the internal profile information stored in an
  729. ICC tag.
  730. :exception PyCMSError:
  731. """
  732. try:
  733. # add an extra newline to preserve pyCMS compatibility
  734. if not isinstance(profile, ImageCmsProfile):
  735. profile = ImageCmsProfile(profile)
  736. return (profile.profile.profile_description or "") + "\n"
  737. except (AttributeError, OSError, TypeError, ValueError) as v:
  738. raise PyCMSError(v) from v
  739. def getDefaultIntent(profile):
  740. """
  741. (pyCMS) Gets the default intent name for the given profile.
  742. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
  743. :exc:`PyCMSError` is raised.
  744. If an error occurs while trying to obtain the default intent, a
  745. :exc:`PyCMSError` is raised.
  746. Use this function to determine the default (and usually best optimized)
  747. rendering intent for this profile. Most profiles support multiple
  748. rendering intents, but are intended mostly for one type of conversion.
  749. If you wish to use a different intent than returned, use
  750. ImageCms.isIntentSupported() to verify it will work first.
  751. :param profile: EITHER a valid CmsProfile object, OR a string of the
  752. filename of an ICC profile.
  753. :returns: Integer 0-3 specifying the default rendering intent for this
  754. profile.
  755. ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
  756. ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
  757. ImageCms.Intent.SATURATION = 2
  758. ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
  759. see the pyCMS documentation for details on rendering intents and what
  760. they do.
  761. :exception PyCMSError:
  762. """
  763. try:
  764. if not isinstance(profile, ImageCmsProfile):
  765. profile = ImageCmsProfile(profile)
  766. return profile.profile.rendering_intent
  767. except (AttributeError, OSError, TypeError, ValueError) as v:
  768. raise PyCMSError(v) from v
  769. def isIntentSupported(profile, intent, direction):
  770. """
  771. (pyCMS) Checks if a given intent is supported.
  772. Use this function to verify that you can use your desired
  773. ``intent`` with ``profile``, and that ``profile`` can be used for the
  774. input/output/proof profile as you desire.
  775. Some profiles are created specifically for one "direction", can cannot
  776. be used for others. Some profiles can only be used for certain
  777. rendering intents, so it's best to either verify this before trying
  778. to create a transform with them (using this function), or catch the
  779. potential :exc:`PyCMSError` that will occur if they don't
  780. support the modes you select.
  781. :param profile: EITHER a valid CmsProfile object, OR a string of the
  782. filename of an ICC profile.
  783. :param intent: Integer (0-3) specifying the rendering intent you wish to
  784. use with this profile
  785. ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
  786. ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
  787. ImageCms.Intent.SATURATION = 2
  788. ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
  789. see the pyCMS documentation for details on rendering intents and what
  790. they do.
  791. :param direction: Integer specifying if the profile is to be used for
  792. input, output, or proof
  793. INPUT = 0 (or use ImageCms.Direction.INPUT)
  794. OUTPUT = 1 (or use ImageCms.Direction.OUTPUT)
  795. PROOF = 2 (or use ImageCms.Direction.PROOF)
  796. :returns: 1 if the intent/direction are supported, -1 if they are not.
  797. :exception PyCMSError:
  798. """
  799. try:
  800. if not isinstance(profile, ImageCmsProfile):
  801. profile = ImageCmsProfile(profile)
  802. # FIXME: I get different results for the same data w. different
  803. # compilers. Bug in LittleCMS or in the binding?
  804. if profile.profile.is_intent_supported(intent, direction):
  805. return 1
  806. else:
  807. return -1
  808. except (AttributeError, OSError, TypeError, ValueError) as v:
  809. raise PyCMSError(v) from v
  810. def versions():
  811. """
  812. (pyCMS) Fetches versions.
  813. """
  814. return VERSION, core.littlecms_version, sys.version.split()[0], Image.__version__