Skip to content

Commit

Permalink
Merge pull request #7676 from nulano/lcms2-flags
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk authored Jan 6, 2024
2 parents f162d65 + d9ec2fd commit fcaed26
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 16 deletions.
10 changes: 10 additions & 0 deletions Tests/test_imagecms.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ def test_sanity():
hopper().point(t)


def test_flags():
assert ImageCms.Flags.NONE == 0
assert ImageCms.Flags.GRIDPOINTS(0) == ImageCms.Flags.NONE
assert ImageCms.Flags.GRIDPOINTS(256) == ImageCms.Flags.NONE

assert ImageCms.Flags.GRIDPOINTS(255) == (255 << 16)
assert ImageCms.Flags.GRIDPOINTS(-1) == ImageCms.Flags.GRIDPOINTS(255)
assert ImageCms.Flags.GRIDPOINTS(511) == ImageCms.Flags.GRIDPOINTS(255)


def test_name():
skip_missing()
# get profile information for file
Expand Down
4 changes: 2 additions & 2 deletions docs/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,8 @@ ImageCms.CmsProfile attributes
.. deprecated:: 3.2.0
.. versionremoved:: 8.0.0

Some attributes in :py:class:`PIL.ImageCms.CmsProfile` have been removed. From 6.0.0,
they issued a :py:exc:`DeprecationWarning`:
Some attributes in :py:class:`PIL.ImageCms.core.CmsProfile` have been removed.
From 6.0.0, they issued a :py:exc:`DeprecationWarning`:

======================== ===================================================
Removed Use instead
Expand Down
26 changes: 25 additions & 1 deletion docs/reference/ImageCms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,31 @@ The :py:mod:`~PIL.ImageCms` module provides color profile management
support using the LittleCMS2 color management engine, based on Kevin
Cazabon's PyCMS library.

.. autoclass:: ImageCmsProfile
:members:
:special-members: __init__
.. autoclass:: ImageCmsTransform
:members:
:undoc-members:
:show-inheritance:
.. autoexception:: PyCMSError

Constants
---------

.. autoclass:: Intent
:members:
:member-order: bysource
:undoc-members:
.. autoclass:: Direction
:members:
:member-order: bysource
:undoc-members:
.. autoclass:: Flags
:members:
:member-order: bysource
:undoc-members:

Functions
---------

Expand All @@ -37,13 +59,15 @@ CmsProfile
----------

The ICC color profiles are wrapped in an instance of the class
:py:class:`CmsProfile`. The specification ICC.1:2010 contains more
:py:class:`~core.CmsProfile`. The specification ICC.1:2010 contains more
information about the meaning of the values in ICC profiles.

For convenience, all XYZ-values are also given as xyY-values (so they
can be easily displayed in a chromaticity diagram, for example).

.. py:currentmodule:: PIL.ImageCms.core
.. py:class:: CmsProfile
:canonical: PIL._imagingcms.CmsProfile

.. py:attribute:: creation_date
:type: Optional[datetime.datetime]
Expand Down
2 changes: 1 addition & 1 deletion docs/releasenotes/8.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Image.fromstring, im.fromstring and im.tostring
ImageCms.CmsProfile attributes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Some attributes in :py:class:`PIL.ImageCms.CmsProfile` have been removed:
Some attributes in :py:class:`PIL.ImageCms.core.CmsProfile` have been removed:

======================== ===================================================
Removed Use instead
Expand Down
84 changes: 72 additions & 12 deletions src/PIL/ImageCms.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
# below for the original description.
from __future__ import annotations

import operator
import sys
from enum import IntEnum
from enum import IntEnum, IntFlag
from functools import reduce

from . import Image

Expand Down Expand Up @@ -119,6 +121,69 @@ class Direction(IntEnum):
#
# flags


class Flags(IntFlag):
"""Flags and documentation are taken from ``lcms2.h``."""

NONE = 0
NOCACHE = 0x0040
"""Inhibit 1-pixel cache"""
NOOPTIMIZE = 0x0100
"""Inhibit optimizations"""
NULLTRANSFORM = 0x0200
"""Don't transform anyway"""
GAMUTCHECK = 0x1000
"""Out of Gamut alarm"""
SOFTPROOFING = 0x4000
"""Do softproofing"""
BLACKPOINTCOMPENSATION = 0x2000
NOWHITEONWHITEFIXUP = 0x0004
"""Don't fix scum dot"""
HIGHRESPRECALC = 0x0400
"""Use more memory to give better accuracy"""
LOWRESPRECALC = 0x0800
"""Use less memory to minimize resources"""
# this should be 8BITS_DEVICELINK, but that is not a valid name in Python:
USE_8BITS_DEVICELINK = 0x0008
"""Create 8 bits devicelinks"""
GUESSDEVICECLASS = 0x0020
"""Guess device class (for ``transform2devicelink``)"""
KEEP_SEQUENCE = 0x0080
"""Keep profile sequence for devicelink creation"""
FORCE_CLUT = 0x0002
"""Force CLUT optimization"""
CLUT_POST_LINEARIZATION = 0x0001
"""create postlinearization tables if possible"""
CLUT_PRE_LINEARIZATION = 0x0010
"""create prelinearization tables if possible"""
NONEGATIVES = 0x8000
"""Prevent negative numbers in floating point transforms"""
COPY_ALPHA = 0x04000000
"""Alpha channels are copied on ``cmsDoTransform()``"""
NODEFAULTRESOURCEDEF = 0x01000000

_GRIDPOINTS_1 = 1 << 16
_GRIDPOINTS_2 = 2 << 16
_GRIDPOINTS_4 = 4 << 16
_GRIDPOINTS_8 = 8 << 16
_GRIDPOINTS_16 = 16 << 16
_GRIDPOINTS_32 = 32 << 16
_GRIDPOINTS_64 = 64 << 16
_GRIDPOINTS_128 = 128 << 16

@staticmethod
def GRIDPOINTS(n: int) -> Flags:
"""
Fine-tune control over number of gridpoints
:param n: :py:class:`int` in range ``0 <= n <= 255``
"""
return Flags.NONE | ((n & 0xFF) << 16)


_MAX_FLAG = reduce(operator.or_, Flags)


FLAGS = {
"MATRIXINPUT": 1,
"MATRIXOUTPUT": 2,
Expand All @@ -142,11 +207,6 @@ class Direction(IntEnum):
"GRIDPOINTS": lambda n: (n & 0xFF) << 16, # Gridpoints
}

_MAX_FLAG = 0
for flag in FLAGS.values():
if isinstance(flag, int):
_MAX_FLAG = _MAX_FLAG | flag


# --------------------------------------------------------------------.
# Experimental PIL-level API
Expand Down Expand Up @@ -218,7 +278,7 @@ def __init__(
intent=Intent.PERCEPTUAL,
proof=None,
proof_intent=Intent.ABSOLUTE_COLORIMETRIC,
flags=0,
flags=Flags.NONE,
):
if proof is None:
self.transform = core.buildTransform(
Expand Down Expand Up @@ -303,7 +363,7 @@ def profileToProfile(
renderingIntent=Intent.PERCEPTUAL,
outputMode=None,
inPlace=False,
flags=0,
flags=Flags.NONE,
):
"""
(pyCMS) Applies an ICC transformation to a given image, mapping from
Expand Down Expand Up @@ -420,7 +480,7 @@ def buildTransform(
inMode,
outMode,
renderingIntent=Intent.PERCEPTUAL,
flags=0,
flags=Flags.NONE,
):
"""
(pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
Expand Down Expand Up @@ -482,7 +542,7 @@ def buildTransform(
raise PyCMSError(msg)

if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
msg = "flags must be an integer between 0 and %s" + _MAX_FLAG
msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
raise PyCMSError(msg)

try:
Expand All @@ -505,7 +565,7 @@ def buildProofTransform(
outMode,
renderingIntent=Intent.PERCEPTUAL,
proofRenderingIntent=Intent.ABSOLUTE_COLORIMETRIC,
flags=FLAGS["SOFTPROOFING"],
flags=Flags.SOFTPROOFING,
):
"""
(pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
Expand Down Expand Up @@ -586,7 +646,7 @@ def buildProofTransform(
raise PyCMSError(msg)

if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
msg = "flags must be an integer between 0 and %s" + _MAX_FLAG
msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
raise PyCMSError(msg)

try:
Expand Down

0 comments on commit fcaed26

Please sign in to comment.