Skip to content

Commit 9099142

Browse files
committed
add LCMS2 flags to ImageCms
1 parent 40a3f91 commit 9099142

2 files changed

Lines changed: 61 additions & 12 deletions

File tree

Tests/test_imagecms.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ def test_sanity():
9090
hopper().point(t)
9191

9292

93+
def test_flags():
94+
assert ImageCms.Flags.NONE == 0
95+
assert ImageCms.Flags.GRIDPOINTS(0) == ImageCms.Flags.NONE
96+
assert ImageCms.Flags.GRIDPOINTS(256) == ImageCms.Flags.NONE
97+
98+
assert ImageCms.Flags.GRIDPOINTS(255) == (255 << 16)
99+
assert ImageCms.Flags.GRIDPOINTS(-1) == ImageCms.Flags.GRIDPOINTS(255)
100+
assert ImageCms.Flags.GRIDPOINTS(511) == ImageCms.Flags.GRIDPOINTS(255)
101+
102+
93103
def test_name():
94104
skip_missing()
95105
# get profile information for file

src/PIL/ImageCms.py

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
# below for the original description.
1717
from __future__ import annotations
1818

19+
import operator
1920
import sys
20-
from enum import IntEnum
21+
from enum import IntEnum, IntFlag
22+
from functools import reduce
2123

2224
from . import Image
2325

@@ -119,6 +121,48 @@ class Direction(IntEnum):
119121
#
120122
# flags
121123

124+
125+
class Flags(IntFlag):
126+
# These are taken from lcms2.h (including comments)
127+
NONE = 0
128+
NOCACHE = 0x0040 # Inhibit 1-pixel cache
129+
NOOPTIMIZE = 0x0100 # Inhibit optimizations
130+
NULLTRANSFORM = 0x0200 # Don't transform anyway
131+
GAMUTCHECK = 0x1000 # Out of Gamut alarm
132+
SOFTPROOFING = 0x4000 # Do softproofing
133+
BLACKPOINTCOMPENSATION = 0x2000
134+
NOWHITEONWHITEFIXUP = 0x0004 # Don't fix scum dot
135+
HIGHRESPRECALC = 0x0400 # Use more memory to give better accuracy
136+
LOWRESPRECALC = 0x0800 # Use less memory to minimize resources
137+
# this should be 8BITS_DEVICELINK, but that is not a valid name in Python:
138+
USE_8BITS_DEVICELINK = 0x0008 # Create 8 bits devicelinks
139+
GUESSDEVICECLASS = 0x0020 # Guess device class (for transform2devicelink)
140+
KEEP_SEQUENCE = 0x0080 # Keep profile sequence for devicelink creation
141+
FORCE_CLUT = 0x0002 # Force CLUT optimization
142+
CLUT_POST_LINEARIZATION = 0x0001 # create postlinearization tables if possible
143+
CLUT_PRE_LINEARIZATION = 0x0010 # create prelinearization tables if possible
144+
NONEGATIVES = 0x8000 # Prevent negative numbers in floating point transforms
145+
COPY_ALPHA = 0x04000000 # Alpha channels are copied on cmsDoTransform()
146+
NODEFAULTRESOURCEDEF = 0x01000000
147+
148+
_GRIDPOINTS_1 = 1 << 16
149+
_GRIDPOINTS_2 = 2 << 16
150+
_GRIDPOINTS_4 = 4 << 16
151+
_GRIDPOINTS_8 = 8 << 16
152+
_GRIDPOINTS_16 = 16 << 16
153+
_GRIDPOINTS_32 = 32 << 16
154+
_GRIDPOINTS_64 = 64 << 16
155+
_GRIDPOINTS_128 = 128 << 16
156+
157+
@staticmethod
158+
def GRIDPOINTS(n: int) -> Flags:
159+
# Fine-tune control over number of gridpoints
160+
return Flags.NONE | ((n & 0xFF) << 16)
161+
162+
163+
_MAX_FLAG = reduce(operator.or_, Flags)
164+
165+
122166
FLAGS = {
123167
"MATRIXINPUT": 1,
124168
"MATRIXOUTPUT": 2,
@@ -142,11 +186,6 @@ class Direction(IntEnum):
142186
"GRIDPOINTS": lambda n: (n & 0xFF) << 16, # Gridpoints
143187
}
144188

145-
_MAX_FLAG = 0
146-
for flag in FLAGS.values():
147-
if isinstance(flag, int):
148-
_MAX_FLAG = _MAX_FLAG | flag
149-
150189

151190
# --------------------------------------------------------------------.
152191
# Experimental PIL-level API
@@ -218,7 +257,7 @@ def __init__(
218257
intent=Intent.PERCEPTUAL,
219258
proof=None,
220259
proof_intent=Intent.ABSOLUTE_COLORIMETRIC,
221-
flags=0,
260+
flags=Flags.NONE,
222261
):
223262
if proof is None:
224263
self.transform = core.buildTransform(
@@ -303,7 +342,7 @@ def profileToProfile(
303342
renderingIntent=Intent.PERCEPTUAL,
304343
outputMode=None,
305344
inPlace=False,
306-
flags=0,
345+
flags=Flags.NONE,
307346
):
308347
"""
309348
(pyCMS) Applies an ICC transformation to a given image, mapping from
@@ -420,7 +459,7 @@ def buildTransform(
420459
inMode,
421460
outMode,
422461
renderingIntent=Intent.PERCEPTUAL,
423-
flags=0,
462+
flags=Flags.NONE,
424463
):
425464
"""
426465
(pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
@@ -482,7 +521,7 @@ def buildTransform(
482521
raise PyCMSError(msg)
483522

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

488527
try:
@@ -505,7 +544,7 @@ def buildProofTransform(
505544
outMode,
506545
renderingIntent=Intent.PERCEPTUAL,
507546
proofRenderingIntent=Intent.ABSOLUTE_COLORIMETRIC,
508-
flags=FLAGS["SOFTPROOFING"],
547+
flags=Flags.SOFTPROOFING,
509548
):
510549
"""
511550
(pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
@@ -586,7 +625,7 @@ def buildProofTransform(
586625
raise PyCMSError(msg)
587626

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

592631
try:

0 commit comments

Comments
 (0)