mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
commit
55a0792815
|
@ -6,7 +6,7 @@ python:
|
|||
- 3.2
|
||||
- 3.3
|
||||
|
||||
install: "sudo apt-get -qq install libfreetype6-dev liblcms1-dev libwebp-dev"
|
||||
install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev"
|
||||
|
||||
script:
|
||||
- python setup.py clean
|
||||
|
|
|
@ -200,6 +200,7 @@ _MODEINFO = {
|
|||
"RGBA": ("RGB", "L", ("R", "G", "B", "A")),
|
||||
"CMYK": ("RGB", "L", ("C", "M", "Y", "K")),
|
||||
"YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")),
|
||||
"LAB": ("RGB", "L", ("L", "A", "B")),
|
||||
|
||||
# Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and
|
||||
# BGR;24. Use these modes only if you know exactly what you're
|
||||
|
@ -224,6 +225,7 @@ _MODE_CONV = {
|
|||
"RGBA": ('|u1', 4),
|
||||
"CMYK": ('|u1', 4),
|
||||
"YCbCr": ('|u1', 3),
|
||||
"LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1
|
||||
"I;16": ('=u2', None),
|
||||
"I;16B": ('>u2', None),
|
||||
"I;16L": ('<u2', None),
|
||||
|
|
152
PIL/ImageCms.py
152
PIL/ImageCms.py
|
@ -42,6 +42,8 @@ pyCMS
|
|||
|
||||
Version History:
|
||||
|
||||
1.0.0 pil Oct 2013 Port to LCMS 2.
|
||||
|
||||
0.1.0 pil mod March 10, 2009
|
||||
|
||||
Renamed display profile to proof profile. The proof
|
||||
|
@ -77,7 +79,7 @@ pyCMS
|
|||
|
||||
"""
|
||||
|
||||
VERSION = "0.1.0 pil"
|
||||
VERSION = "1.0.0 pil"
|
||||
|
||||
# --------------------------------------------------------------------.
|
||||
|
||||
|
@ -151,8 +153,8 @@ class ImageCmsProfile:
|
|||
self.profile = profile
|
||||
self.filename = filename
|
||||
if profile:
|
||||
self.product_name = profile.product_name
|
||||
self.product_info = profile.product_info
|
||||
self.product_name = None #profile.product_name
|
||||
self.product_info = None #profile.product_info
|
||||
else:
|
||||
self.product_name = None
|
||||
self.product_info = None
|
||||
|
@ -564,10 +566,10 @@ def createProfile(colorSpace, colorTemp=-1):
|
|||
raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace)
|
||||
|
||||
if colorSpace == "LAB":
|
||||
if isinstance(colorTemp, float):
|
||||
colorTemp = int(colorTemp + 0.5)
|
||||
if not isinstance(colorTemp, int):
|
||||
raise PyCMSError("Color temperature must be a positive integer, \"%s\" not valid" % colorTemp)
|
||||
try:
|
||||
colorTemp = float(colorTemp)
|
||||
except:
|
||||
raise PyCMSError("Color temperature must be numeric, \"%s\" not valid" % colorTemp)
|
||||
|
||||
try:
|
||||
return core.createProfile(colorSpace, colorTemp)
|
||||
|
@ -597,7 +599,19 @@ def getProfileName(profile):
|
|||
# add an extra newline to preserve pyCMS compatibility
|
||||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.product_name + "\n"
|
||||
# do it in python, not c.
|
||||
# // name was "%s - %s" (model, manufacturer) || Description ,
|
||||
# // but if the Model and Manufacturer were the same or the model
|
||||
# // was long, Just the model, in 1.x
|
||||
model = profile.profile.product_model
|
||||
manufacturer = profile.profile.product_manufacturer
|
||||
|
||||
if not (model or manufacturer):
|
||||
return profile.profile.product_description+"\n"
|
||||
if not manufacturer or len(model) > 30:
|
||||
return model + "\n"
|
||||
return "%s - %s\n" % (model, manufacturer)
|
||||
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
@ -625,10 +639,130 @@ def getProfileInfo(profile):
|
|||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
# add an extra newline to preserve pyCMS compatibility
|
||||
return profile.product_info + "\n"
|
||||
# Python, not C. the white point bits weren't working well, so skipping.
|
||||
# // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
|
||||
description = profile.profile.product_description
|
||||
cpright = profile.profile.product_copyright
|
||||
arr = []
|
||||
for elt in (description, cpright):
|
||||
if elt:
|
||||
arr.append(elt)
|
||||
return "\r\n\r\n".join(arr)+"\r\n\r\n"
|
||||
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
||||
##
|
||||
# (pyCMS) Gets the copyright for the given profile.
|
||||
#
|
||||
# If profile isn't a valid CmsProfile object or filename to a profile,
|
||||
# a PyCMSError is raised.
|
||||
#
|
||||
# If an error occurs while trying to obtain the copyright tag, a PyCMSError
|
||||
# is raised
|
||||
#
|
||||
# Use this function to obtain the information stored in the profile's
|
||||
# copyright tag.
|
||||
#
|
||||
# @param profile EITHER a valid CmsProfile object, OR a string of the filename
|
||||
# of an ICC profile.
|
||||
# @return A string containing the internal profile information stored in an ICC
|
||||
# tag.
|
||||
# @exception PyCMSError
|
||||
|
||||
def getProfileCopyright(profile):
|
||||
try:
|
||||
# add an extra newline to preserve pyCMS compatibility
|
||||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.product_copyright + "\n"
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
##
|
||||
# (pyCMS) Gets the manufacturer for the given profile.
|
||||
#
|
||||
# If profile isn't a valid CmsProfile object or filename to a profile,
|
||||
# a PyCMSError is raised.
|
||||
#
|
||||
# If an error occurs while trying to obtain the manufacturer tag, a PyCMSError
|
||||
# is raised
|
||||
#
|
||||
# Use this function to obtain the information stored in the profile's
|
||||
# manufacturer tag.
|
||||
#
|
||||
# @param profile EITHER a valid CmsProfile object, OR a string of the filename
|
||||
# of an ICC profile.
|
||||
# @return A string containing the internal profile information stored in an ICC
|
||||
# tag.
|
||||
# @exception PyCMSError
|
||||
|
||||
def getProfileManufacturer(profile):
|
||||
try:
|
||||
# add an extra newline to preserve pyCMS compatibility
|
||||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.product_manufacturer + "\n"
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
##
|
||||
# (pyCMS) Gets the model for the given profile.
|
||||
#
|
||||
# If profile isn't a valid CmsProfile object or filename to a profile,
|
||||
# a PyCMSError is raised.
|
||||
#
|
||||
# If an error occurs while trying to obtain the model tag, a PyCMSError
|
||||
# is raised
|
||||
#
|
||||
# Use this function to obtain the information stored in the profile's
|
||||
# model tag.
|
||||
#
|
||||
# @param profile EITHER a valid CmsProfile object, OR a string of the filename
|
||||
# of an ICC profile.
|
||||
# @return A string containing the internal profile information stored in an ICC
|
||||
# tag.
|
||||
# @exception PyCMSError
|
||||
|
||||
def getProfileModel(profile):
|
||||
try:
|
||||
# add an extra newline to preserve pyCMS compatibility
|
||||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.product_model + "\n"
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
##
|
||||
# (pyCMS) Gets the description for the given profile.
|
||||
#
|
||||
# If profile isn't a valid CmsProfile object or filename to a profile,
|
||||
# a PyCMSError is raised.
|
||||
#
|
||||
# If an error occurs while trying to obtain the description tag, a PyCMSError
|
||||
# is raised
|
||||
#
|
||||
# Use this function to obtain the information stored in the profile's
|
||||
# description tag.
|
||||
#
|
||||
# @param profile EITHER a valid CmsProfile object, OR a string of the filename
|
||||
# of an ICC profile.
|
||||
# @return A string containing the internal profile information stored in an ICC
|
||||
# tag.
|
||||
# @exception PyCMSError
|
||||
|
||||
def getProfileDescription(profile):
|
||||
try:
|
||||
# add an extra newline to preserve pyCMS compatibility
|
||||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.product_description + "\n"
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# (pyCMS) Gets the default intent name for the given profile.
|
||||
#
|
||||
|
|
BIN
Tests/images/lab-green.tif
Normal file
BIN
Tests/images/lab-green.tif
Normal file
Binary file not shown.
BIN
Tests/images/lab-red.tif
Normal file
BIN
Tests/images/lab-red.tif
Normal file
Binary file not shown.
BIN
Tests/images/lab.tif
Normal file
BIN
Tests/images/lab.tif
Normal file
Binary file not shown.
BIN
Tests/images/lena.Lab.tif
Normal file
BIN
Tests/images/lena.Lab.tif
Normal file
Binary file not shown.
41
Tests/test_format_lab.py
Normal file
41
Tests/test_format_lab.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
from tester import *
|
||||
|
||||
from PIL import Image
|
||||
|
||||
def test_white():
|
||||
i = Image.open('Tests/images/lab.tif')
|
||||
|
||||
bits = i.load()
|
||||
|
||||
assert_equal(i.mode, 'LAB')
|
||||
|
||||
assert_equal(i.getbands(), ('L','A', 'B'))
|
||||
|
||||
k = i.getpixel((0,0))
|
||||
assert_equal(k, (255,128,128))
|
||||
|
||||
L = i.getdata(0)
|
||||
a = i.getdata(1)
|
||||
b = i.getdata(2)
|
||||
|
||||
assert_equal(list(L), [255]*100)
|
||||
assert_equal(list(a), [128]*100)
|
||||
assert_equal(list(b), [128]*100)
|
||||
|
||||
|
||||
def test_green():
|
||||
# l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS
|
||||
# == RGB: 0, 152, 117
|
||||
i = Image.open('Tests/images/lab-green.tif')
|
||||
|
||||
k = i.getpixel((0,0))
|
||||
assert_equal(k, (128,28,128))
|
||||
|
||||
|
||||
def test_red():
|
||||
# l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS
|
||||
# == RGB: 255, 0, 124
|
||||
i = Image.open('Tests/images/lab-red.tif')
|
||||
|
||||
k = i.getpixel((0,0))
|
||||
assert_equal(k, (128,228,128))
|
|
@ -14,7 +14,7 @@ def test_sanity():
|
|||
# this mostly follows the cms_test outline.
|
||||
|
||||
v = ImageCms.versions() # should return four strings
|
||||
assert_equal(v[0], '0.1.0 pil')
|
||||
assert_equal(v[0], '1.0.0 pil')
|
||||
assert_equal(list(map(type, v)), [str, str, str, str])
|
||||
|
||||
# internal version number
|
||||
|
@ -39,44 +39,121 @@ def test_sanity():
|
|||
i = ImageCms.applyTransform(lena(), t)
|
||||
assert_image(i, "RGB", (128, 128))
|
||||
|
||||
# test PointTransform convenience API
|
||||
im = lena().point(t)
|
||||
|
||||
def test_name():
|
||||
# get profile information for file
|
||||
assert_equal(ImageCms.getProfileName(SRGB).strip(),
|
||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||
def x_test_info():
|
||||
assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(),
|
||||
['sRGB IEC61966-2.1', '',
|
||||
'Copyright (c) 1998 Hewlett-Packard Company', '',
|
||||
'WhitePoint : D65 (daylight)', '',
|
||||
'Tests/icc/sRGB.icm'])
|
||||
|
||||
def test_intent():
|
||||
assert_equal(ImageCms.getDefaultIntent(SRGB), 0)
|
||||
assert_equal(ImageCms.isIntentSupported(
|
||||
SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
ImageCms.DIRECTION_INPUT), 1)
|
||||
|
||||
def test_profile_object():
|
||||
# same, using profile object
|
||||
p = ImageCms.createProfile("sRGB")
|
||||
assert_equal(ImageCms.getProfileName(p).strip(),
|
||||
'sRGB built-in - (lcms internal)')
|
||||
assert_equal(ImageCms.getProfileInfo(p).splitlines(),
|
||||
['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', ''])
|
||||
# assert_equal(ImageCms.getProfileName(p).strip(),
|
||||
# 'sRGB built-in - (lcms internal)')
|
||||
# assert_equal(ImageCms.getProfileInfo(p).splitlines(),
|
||||
# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', ''])
|
||||
assert_equal(ImageCms.getDefaultIntent(p), 0)
|
||||
assert_equal(ImageCms.isIntentSupported(
|
||||
p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
ImageCms.DIRECTION_INPUT), 1)
|
||||
|
||||
def test_extensions():
|
||||
# extensions
|
||||
i = Image.open("Tests/images/rgb.jpg")
|
||||
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
|
||||
assert_equal(ImageCms.getProfileName(p).strip(),
|
||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||
|
||||
def test_exceptions():
|
||||
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar"))
|
||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB"))
|
||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None))
|
||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None))
|
||||
|
||||
# test PointTransform convenience API
|
||||
im = lena().point(t)
|
||||
|
||||
def test_display_profile():
|
||||
# try fetching the profile for the current display device
|
||||
assert_no_exception(lambda: ImageCms.get_display_profile())
|
||||
|
||||
|
||||
def test_lab_color_profile():
|
||||
pLab = ImageCms.createProfile("LAB", 5000)
|
||||
pLab = ImageCms.createProfile("LAB", 6500)
|
||||
|
||||
def test_simple_lab():
|
||||
i = Image.new('RGB', (10,10), (128,128,128))
|
||||
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
||||
|
||||
i_lab = ImageCms.applyTransform(i, t)
|
||||
|
||||
|
||||
assert_equal(i_lab.mode, 'LAB')
|
||||
|
||||
k = i_lab.getpixel((0,0))
|
||||
assert_equal(k, (137,128,128)) # not a linear luminance map. so L != 128
|
||||
|
||||
L = i_lab.getdata(0)
|
||||
a = i_lab.getdata(1)
|
||||
b = i_lab.getdata(2)
|
||||
|
||||
assert_equal(list(L), [137]*100)
|
||||
assert_equal(list(a), [128]*100)
|
||||
assert_equal(list(b), [128]*100)
|
||||
|
||||
|
||||
def test_lab_color():
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
||||
# need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType,
|
||||
# and have that mapping work back to a PIL mode. (likely RGB)
|
||||
i = ImageCms.applyTransform(lena(), t)
|
||||
assert_image(i, "LAB", (128, 128))
|
||||
|
||||
# i.save('temp.lab.tif') # visually verified vs PS.
|
||||
|
||||
target = Image.open('Tests/images/lena.Lab.tif')
|
||||
|
||||
assert_image_similar(i, target, 30)
|
||||
|
||||
def test_lab_srgb():
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB")
|
||||
|
||||
img = Image.open('Tests/images/lena.Lab.tif')
|
||||
|
||||
img_srgb = ImageCms.applyTransform(img, t)
|
||||
|
||||
# img_srgb.save('temp.srgb.tif') # visually verified vs ps.
|
||||
|
||||
assert_image_similar(lena(), img_srgb, 30)
|
||||
|
||||
def test_lab_roundtrip():
|
||||
# check to see if we're at least internally consistent.
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
||||
|
||||
t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB")
|
||||
|
||||
i = ImageCms.applyTransform(lena(), t)
|
||||
out = ImageCms.applyTransform(i, t2)
|
||||
|
||||
assert_image_similar(lena(), out, 2)
|
||||
|
||||
|
||||
|
|
151
_imagingcms.c
151
_imagingcms.c
|
@ -24,24 +24,21 @@ http://www.cazabon.com\n\
|
|||
"
|
||||
|
||||
#include "Python.h"
|
||||
#include "lcms.h"
|
||||
#include "lcms2.h"
|
||||
#include "Imaging.h"
|
||||
#include "py3.h"
|
||||
|
||||
#if LCMS_VERSION < 117
|
||||
#define LCMSBOOL BOOL
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windef.h>
|
||||
#include <wingdi.h>
|
||||
#endif
|
||||
|
||||
#define PYCMSVERSION "0.1.0 pil"
|
||||
#define PYCMSVERSION "1.0.0 pil"
|
||||
|
||||
/* version history */
|
||||
|
||||
/*
|
||||
1.0.0 pil Integrating littleCMS2
|
||||
0.1.0 pil integration & refactoring
|
||||
0.0.2 alpha: Minor updates, added interfaces to littleCMS features, Jan 6, 2003
|
||||
- fixed some memory holes in how transforms/profiles were created and passed back to Python
|
||||
|
@ -107,8 +104,6 @@ cms_profile_open(PyObject* self, PyObject* args)
|
|||
if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile))
|
||||
return NULL;
|
||||
|
||||
cmsErrorAction(LCMS_ERROR_IGNORE);
|
||||
|
||||
hProfile = cmsOpenProfileFromFile(sProfile, "r");
|
||||
if (!hProfile) {
|
||||
PyErr_SetString(PyExc_IOError, "cannot open profile file");
|
||||
|
@ -133,8 +128,6 @@ cms_profile_fromstring(PyObject* self, PyObject* args)
|
|||
return NULL;
|
||||
#endif
|
||||
|
||||
cmsErrorAction(LCMS_ERROR_IGNORE);
|
||||
|
||||
hProfile = cmsOpenProfileFromMem(pProfile, nProfile);
|
||||
if (!hProfile) {
|
||||
PyErr_SetString(PyExc_IOError, "cannot open profile from string");
|
||||
|
@ -192,25 +185,25 @@ cms_transform_dealloc(CmsTransformObject* self)
|
|||
/* internal functions */
|
||||
|
||||
static const char*
|
||||
findICmode(icColorSpaceSignature cs)
|
||||
findICmode(cmsColorSpaceSignature cs)
|
||||
{
|
||||
switch (cs) {
|
||||
case icSigXYZData: return "XYZ";
|
||||
case icSigLabData: return "LAB";
|
||||
case icSigLuvData: return "LUV";
|
||||
case icSigYCbCrData: return "YCbCr";
|
||||
case icSigYxyData: return "YXY";
|
||||
case icSigRgbData: return "RGB";
|
||||
case icSigGrayData: return "L";
|
||||
case icSigHsvData: return "HSV";
|
||||
case icSigHlsData: return "HLS";
|
||||
case icSigCmykData: return "CMYK";
|
||||
case icSigCmyData: return "CMY";
|
||||
case cmsSigXYZData: return "XYZ";
|
||||
case cmsSigLabData: return "LAB";
|
||||
case cmsSigLuvData: return "LUV";
|
||||
case cmsSigYCbCrData: return "YCbCr";
|
||||
case cmsSigYxyData: return "YXY";
|
||||
case cmsSigRgbData: return "RGB";
|
||||
case cmsSigGrayData: return "L";
|
||||
case cmsSigHsvData: return "HSV";
|
||||
case cmsSigHlsData: return "HLS";
|
||||
case cmsSigCmykData: return "CMYK";
|
||||
case cmsSigCmyData: return "CMY";
|
||||
default: return ""; /* other TBA */
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD
|
||||
static cmsUInt32Number
|
||||
findLCMStype(char* PILmode)
|
||||
{
|
||||
if (strcmp(PILmode, "RGB") == 0) {
|
||||
|
@ -243,6 +236,10 @@ findLCMStype(char* PILmode)
|
|||
else if (strcmp(PILmode, "YCC") == 0) {
|
||||
return TYPE_YCbCr_8;
|
||||
}
|
||||
else if (strcmp(PILmode, "LAB") == 0) {
|
||||
// LabX equvalent like ALab, but not reversed -- no #define in lcms2
|
||||
return (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1));
|
||||
}
|
||||
|
||||
else {
|
||||
/* take a wild guess... but you probably should fail instead. */
|
||||
|
@ -269,12 +266,10 @@ pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform)
|
|||
}
|
||||
|
||||
static cmsHTRANSFORM
|
||||
_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, DWORD cmsFLAGS)
|
||||
_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, cmsUInt32Number cmsFLAGS)
|
||||
{
|
||||
cmsHTRANSFORM hTransform;
|
||||
|
||||
cmsErrorAction(LCMS_ERROR_IGNORE);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
|
||||
/* create the transform */
|
||||
|
@ -293,12 +288,10 @@ _buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sIn
|
|||
}
|
||||
|
||||
static cmsHTRANSFORM
|
||||
_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, DWORD cmsFLAGS)
|
||||
_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, cmsUInt32Number cmsFLAGS)
|
||||
{
|
||||
cmsHTRANSFORM hTransform;
|
||||
|
||||
cmsErrorAction(LCMS_ERROR_IGNORE);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
|
||||
/* create the transform */
|
||||
|
@ -336,8 +329,6 @@ buildTransform(PyObject *self, PyObject *args) {
|
|||
if (!PyArg_ParseTuple(args, "O!O!ss|ii:buildTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &sInMode, &sOutMode, &iRenderingIntent, &cmsFLAGS))
|
||||
return NULL;
|
||||
|
||||
cmsErrorAction(LCMS_ERROR_IGNORE);
|
||||
|
||||
transform = _buildTransform(pInputProfile->profile, pOutputProfile->profile, sInMode, sOutMode, iRenderingIntent, cmsFLAGS);
|
||||
|
||||
if (!transform)
|
||||
|
@ -363,8 +354,6 @@ buildProofTransform(PyObject *self, PyObject *args)
|
|||
if (!PyArg_ParseTuple(args, "O!O!O!ss|iii:buildProofTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &CmsProfile_Type, &pProofProfile, &sInMode, &sOutMode, &iRenderingIntent, &iProofIntent, &cmsFLAGS))
|
||||
return NULL;
|
||||
|
||||
cmsErrorAction(LCMS_ERROR_IGNORE);
|
||||
|
||||
transform = _buildProofTransform(pInputProfile->profile, pOutputProfile->profile, pProofProfile->profile, sInMode, sOutMode, iRenderingIntent, iProofIntent, cmsFLAGS);
|
||||
|
||||
if (!transform)
|
||||
|
@ -390,8 +379,6 @@ cms_transform_apply(CmsTransformObject *self, PyObject *args)
|
|||
im = (Imaging) idIn;
|
||||
imOut = (Imaging) idOut;
|
||||
|
||||
cmsErrorAction(LCMS_ERROR_IGNORE);
|
||||
|
||||
result = pyCMSdoTransform(im, imOut, self->transform);
|
||||
|
||||
return Py_BuildValue("i", result);
|
||||
|
@ -405,32 +392,34 @@ createProfile(PyObject *self, PyObject *args)
|
|||
{
|
||||
char *sColorSpace;
|
||||
cmsHPROFILE hProfile;
|
||||
int iColorTemp = 0;
|
||||
LPcmsCIExyY whitePoint = NULL;
|
||||
LCMSBOOL result;
|
||||
cmsFloat64Number dColorTemp = 0.0;
|
||||
cmsCIExyY whitePoint;
|
||||
cmsBool result;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s|i:createProfile", &sColorSpace, &iColorTemp))
|
||||
if (!PyArg_ParseTuple(args, "s|d:createProfile", &sColorSpace, &dColorTemp))
|
||||
return NULL;
|
||||
|
||||
cmsErrorAction(LCMS_ERROR_IGNORE);
|
||||
|
||||
if (strcmp(sColorSpace, "LAB") == 0) {
|
||||
if (iColorTemp > 0) {
|
||||
result = cmsWhitePointFromTemp(iColorTemp, whitePoint);
|
||||
if (dColorTemp > 0.0) {
|
||||
result = cmsWhitePointFromTemp(&whitePoint, dColorTemp);
|
||||
if (!result) {
|
||||
PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be integer in degrees Kelvin");
|
||||
PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be float in degrees Kelvin");
|
||||
return NULL;
|
||||
}
|
||||
hProfile = cmsCreateLabProfile(whitePoint);
|
||||
} else
|
||||
hProfile = cmsCreateLabProfile(NULL);
|
||||
hProfile = cmsCreateLab2Profile(&whitePoint);
|
||||
} else {
|
||||
hProfile = cmsCreateLab2Profile(NULL);
|
||||
}
|
||||
}
|
||||
else if (strcmp(sColorSpace, "XYZ") == 0)
|
||||
else if (strcmp(sColorSpace, "XYZ") == 0) {
|
||||
hProfile = cmsCreateXYZProfile();
|
||||
else if (strcmp(sColorSpace, "sRGB") == 0)
|
||||
}
|
||||
else if (strcmp(sColorSpace, "sRGB") == 0) {
|
||||
hProfile = cmsCreate_sRGBProfile();
|
||||
else
|
||||
}
|
||||
else {
|
||||
hProfile = NULL;
|
||||
}
|
||||
|
||||
if (!hProfile) {
|
||||
PyErr_SetString(PyExc_ValueError, "failed to create requested color space");
|
||||
|
@ -446,7 +435,7 @@ createProfile(PyObject *self, PyObject *args)
|
|||
static PyObject *
|
||||
cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args)
|
||||
{
|
||||
LCMSBOOL result;
|
||||
cmsBool result;
|
||||
|
||||
int intent;
|
||||
int direction;
|
||||
|
@ -465,7 +454,7 @@ static PyObject *
|
|||
cms_get_display_profile_win32(PyObject* self, PyObject* args)
|
||||
{
|
||||
char filename[MAX_PATH];
|
||||
DWORD filename_size;
|
||||
cmsUInt32Number filename_size;
|
||||
BOOL ok;
|
||||
|
||||
int handle = 0;
|
||||
|
@ -519,27 +508,63 @@ static struct PyMethodDef cms_profile_methods[] = {
|
|||
};
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_product_name(CmsProfileObject* self, void* closure)
|
||||
_profile_getattr(CmsProfileObject* self, cmsInfoType field)
|
||||
{
|
||||
return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile));
|
||||
// UNDONE -- check that I'm getting the right fields on these.
|
||||
// return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile));
|
||||
//wchar_t buf[256]; -- UNDONE need wchar_t for unicode version.
|
||||
char buf[256];
|
||||
cmsUInt32Number written;
|
||||
written = cmsGetProfileInfoASCII(self->profile,
|
||||
field,
|
||||
"en",
|
||||
"us",
|
||||
buf,
|
||||
256);
|
||||
if (written) {
|
||||
return PyUnicode_FromString(buf);
|
||||
}
|
||||
// UNDONE suppressing error here by sending back blank string.
|
||||
return PyUnicode_FromString("");
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
return PyUnicode_DecodeFSDefault(cmsTakeProductDesc(self->profile));
|
||||
{
|
||||
// description was Description != 'Copyright' || or "%s - %s" (manufacturer, model) in 1.x
|
||||
return _profile_getattr(self, cmsInfoDescription);
|
||||
}
|
||||
|
||||
/* use these four for the individual fields.
|
||||
*/
|
||||
static PyObject*
|
||||
cms_profile_getattr_product_description(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
return _profile_getattr(self, cmsInfoDescription);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_product_info(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
return PyUnicode_DecodeFSDefault(cmsTakeProductInfo(self->profile));
|
||||
cms_profile_getattr_product_model(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
return _profile_getattr(self, cmsInfoModel);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_product_manufacturer(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
return _profile_getattr(self, cmsInfoManufacturer);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_product_copyright(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
return _profile_getattr(self, cmsInfoCopyright);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
return PyInt_FromLong(cmsTakeRenderingIntent(self->profile));
|
||||
return PyInt_FromLong(cmsGetHeaderRenderingIntent(self->profile));
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
@ -556,9 +581,11 @@ cms_profile_getattr_color_space(CmsProfileObject* self, void* closure)
|
|||
|
||||
/* FIXME: add more properties (creation_datetime etc) */
|
||||
static struct PyGetSetDef cms_profile_getsetters[] = {
|
||||
{ "product_name", (getter) cms_profile_getattr_product_name },
|
||||
{ "product_desc", (getter) cms_profile_getattr_product_desc },
|
||||
{ "product_info", (getter) cms_profile_getattr_product_info },
|
||||
{ "product_description", (getter) cms_profile_getattr_product_description },
|
||||
{ "product_manufacturer", (getter) cms_profile_getattr_product_manufacturer },
|
||||
{ "product_model", (getter) cms_profile_getattr_product_model },
|
||||
{ "product_copyright", (getter) cms_profile_getattr_product_copyright },
|
||||
{ "rendering_intent", (getter) cms_profile_getattr_rendering_intent },
|
||||
{ "pcs", (getter) cms_profile_getattr_pcs },
|
||||
{ "color_space", (getter) cms_profile_getattr_color_space },
|
||||
|
|
|
@ -237,6 +237,7 @@ ImagingAccessInit()
|
|||
ADD("RGBX", line_32, get_pixel_32, put_pixel_32);
|
||||
ADD("CMYK", line_32, get_pixel_32, put_pixel_32);
|
||||
ADD("YCbCr", line_32, get_pixel_32, put_pixel_32);
|
||||
ADD("LAB", line_32, get_pixel_32, put_pixel_32);
|
||||
}
|
||||
|
||||
ImagingAccess
|
||||
|
|
|
@ -40,6 +40,7 @@ extern "C" {
|
|||
* RGBA 4 R, G, B, A
|
||||
* CMYK 4 C, M, Y, K
|
||||
* YCbCr 4 Y, Cb, Cr, -
|
||||
* Lab 4 L, a, b, -
|
||||
*
|
||||
* experimental modes (incomplete):
|
||||
* LA 4 L, -, -, A
|
||||
|
|
|
@ -372,6 +372,19 @@ packI32S(UINT8* out, const UINT8* in, int pixels)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImagingPackLAB(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
int i;
|
||||
/* LAB triplets */
|
||||
for (i = 0; i < pixels; i++) {
|
||||
out[0] = in[0];
|
||||
out[1] = in[1] ^ 128; /* signed in outside world */
|
||||
out[2] = in[2] ^ 128;
|
||||
out += 3; in += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy1(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
|
@ -526,6 +539,12 @@ static struct {
|
|||
{"YCbCr", "Cb", 8, band1},
|
||||
{"YCbCr", "Cr", 8, band2},
|
||||
|
||||
/* LAB Color */
|
||||
{"LAB", "LAB", 24, ImagingPackLAB},
|
||||
{"LAB", "L", 8, band0},
|
||||
{"LAB", "A", 8, band1},
|
||||
{"LAB", "B", 8, band2},
|
||||
|
||||
/* integer */
|
||||
{"I", "I", 32, copy4},
|
||||
{"I", "I;16B", 16, packI16B},
|
||||
|
|
|
@ -178,9 +178,16 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize,
|
|||
im->pixelsize = 4;
|
||||
im->linesize = xsize * 4;
|
||||
|
||||
} else if (strcmp(mode, "LAB") == 0) {
|
||||
/* 24-bit color, luminance, + 2 color channels */
|
||||
/* L is uint8, a,b are int8 */
|
||||
im->bands = 3;
|
||||
im->pixelsize = 4;
|
||||
im->linesize = xsize * 4;
|
||||
|
||||
} else {
|
||||
free(im);
|
||||
return (Imaging) ImagingError_ValueError("unrecognized mode");
|
||||
return (Imaging) ImagingError_ValueError("unrecognized mode");
|
||||
}
|
||||
|
||||
/* Setup image descriptor */
|
||||
|
|
|
@ -750,6 +750,31 @@ unpackCMYKI(UINT8* out, const UINT8* in, int pixels)
|
|||
}
|
||||
}
|
||||
|
||||
/* Unpack to "LAB" image */
|
||||
/* There are two representations of LAB images for whatever precision:
|
||||
L: Uint (in PS, it's 0-100)
|
||||
A: Int (in ps, -128 .. 128, or elsewhere 0..255, with 128 as middle.
|
||||
Channels in PS display a 0 value as middle grey,
|
||||
LCMS appears to use 128 as the 0 value for these channels)
|
||||
B: Int (as above)
|
||||
|
||||
Since we don't have any signed ints, we're going with the shifted versions
|
||||
internally, and we'll unshift for saving and whatnot.
|
||||
*/
|
||||
void
|
||||
ImagingUnpackLAB(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
int i;
|
||||
/* LAB triplets */
|
||||
for (i = 0; i < pixels; i++) {
|
||||
out[0] = in[0];
|
||||
out[1] = in[1] ^ 128; /* signed in outside world */
|
||||
out[2] = in[2] ^ 128;
|
||||
out[3] = 255;
|
||||
out += 4; in += 3;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy1(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
|
@ -764,6 +789,13 @@ copy2(UINT8* out, const UINT8* in, int pixels)
|
|||
memcpy(out, in, pixels*2);
|
||||
}
|
||||
|
||||
static void
|
||||
copy3(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
/* LAB triples, 24bit */
|
||||
memcpy(out, in, 3 * pixels);
|
||||
}
|
||||
|
||||
static void
|
||||
copy4(UINT8* out, const UINT8* in, int pixels)
|
||||
{
|
||||
|
@ -1054,6 +1086,12 @@ static struct {
|
|||
{"YCbCr", "YCbCrX", 32, copy4},
|
||||
{"YCbCr", "YCbCrK", 32, copy4},
|
||||
|
||||
/* LAB Color */
|
||||
{"LAB", "LAB", 24, ImagingUnpackLAB},
|
||||
{"LAB", "L", 8, band0},
|
||||
{"LAB", "A", 8, band1},
|
||||
{"LAB", "B", 8, band2},
|
||||
|
||||
/* integer variations */
|
||||
{"I", "I", 32, copy4},
|
||||
{"I", "I;8", 8, unpackI8},
|
||||
|
|
|
@ -195,7 +195,7 @@ if __name__ == "__main__":
|
|||
check_codec("ZLIB (PNG/ZIP)", "zip")
|
||||
check_codec("G4 TIFF", "group4")
|
||||
check_module("FREETYPE2", "PIL._imagingft")
|
||||
check_module("LITTLECMS", "PIL._imagingcms")
|
||||
check_module("LITTLECMS2", "PIL._imagingcms")
|
||||
check_module("WEBP", "PIL._webp")
|
||||
try:
|
||||
from PIL import _webp
|
||||
|
|
8
setup.py
8
setup.py
|
@ -342,8 +342,8 @@ class pil_build_ext(build_ext):
|
|||
_add_directory(self.compiler.include_dirs, dir, 0)
|
||||
|
||||
if feature.want('lcms'):
|
||||
if _find_include_file(self, "lcms.h"):
|
||||
if _find_library_file(self, "lcms"):
|
||||
if _find_include_file(self, "lcms2.h"):
|
||||
if _find_library_file(self, "lcms2"):
|
||||
feature.lcms = "lcms"
|
||||
|
||||
if _tkinter and _find_include_file(self, "tk.h"):
|
||||
|
@ -426,7 +426,7 @@ class pil_build_ext(build_ext):
|
|||
if sys.platform == "win32":
|
||||
extra.extend(["user32", "gdi32"])
|
||||
exts.append(Extension(
|
||||
"PIL._imagingcms", ["_imagingcms.c"], libraries=["lcms"] + extra))
|
||||
"PIL._imagingcms", ["_imagingcms.c"], libraries=["lcms2"] + extra))
|
||||
|
||||
if os.path.isfile("_webp.c") and feature.webp:
|
||||
libs = ["webp"]
|
||||
|
@ -502,7 +502,7 @@ class pil_build_ext(build_ext):
|
|||
(feature.zlib, "ZLIB (PNG/ZIP)"),
|
||||
(feature.tiff, "TIFF G3/G4 (experimental)"),
|
||||
(feature.freetype, "FREETYPE2"),
|
||||
(feature.lcms, "LITTLECMS"),
|
||||
(feature.lcms, "LITTLECMS2"),
|
||||
(feature.webp, "WEBP"),
|
||||
(feature.webpmux, "WEBPMUX"), ]
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user