Pillow/src/_imagingcms.c

1564 lines
44 KiB
C
Raw Normal View History

/*
2010-07-31 06:52:47 +04:00
* pyCMS
* a Python / PIL interface to the littleCMS ICC Color Management System
* Copyright (C) 2002-2003 Kevin Cazabon
* kevin@cazabon.com
2021-10-27 14:51:54 +03:00
* https://www.cazabon.com
2010-07-31 06:52:47 +04:00
* Adapted/reworked for PIL by Fredrik Lundh
* Copyright (c) 2009 Fredrik Lundh
2014-07-15 09:00:29 +04:00
* Updated to LCMS2
* Copyright (c) 2013 Eric Soroos
*
2021-10-27 14:51:54 +03:00
* pyCMS home page: https://www.cazabon.com/pyCMS
* littleCMS home page: https://www.littlecms.com
2010-07-31 06:52:47 +04:00
* (littleCMS is Copyright (C) 1998-2001 Marti Maria)
*
2010-07-31 06:52:47 +04:00
* Originally released under LGPL. Graciously donated to PIL in
* March 2009, for distribution under the standard PIL license
*/
2021-01-03 06:17:51 +03:00
#define COPYRIGHTINFO \
"\
2010-07-31 06:52:47 +04:00
pyCMS\n\
a Python / PIL interface to the littleCMS ICC Color Management System\n\
Copyright (C) 2002-2003 Kevin Cazabon\n\
kevin@cazabon.com\n\
2021-10-27 14:51:54 +03:00
https://www.cazabon.com\n\
2010-07-31 06:52:47 +04:00
"
2019-03-26 05:50:57 +03:00
#define PY_SSIZE_T_CLEAN
2021-01-03 06:17:51 +03:00
#include "Python.h" // Include before wchar.h so _GNU_SOURCE is set
#include "wchar.h"
#include "datetime.h"
#include "lcms2.h"
#include "libImaging/Imaging.h"
2010-07-31 06:52:47 +04:00
#define PYCMSVERSION "1.0.0 pil"
2010-07-31 06:52:47 +04:00
/* version history */
/*
1.0.0 pil Integrating littleCMS2
2010-07-31 06:52:47 +04:00
0.1.0 pil integration & refactoring
0.0.2 alpha: Minor updates, added interfaces to littleCMS features, Jan 6, 2003
2021-01-03 06:17:51 +03:00
- fixed some memory holes in how transforms/profiles were created and passed back to
Python due to improper destructor setup for PyCObjects
2010-07-31 06:52:47 +04:00
- added buildProofTransformFromOpenProfiles() function
2021-01-03 06:17:51 +03:00
- eliminated some code redundancy, centralizing several common tasks with internal
functions
2010-07-31 06:52:47 +04:00
0.0.1 alpha: First public release Dec 26, 2002
*/
/* known to-do list with current version:
2015-10-11 13:24:35 +03:00
Verify that PILmode->littleCMStype conversion in findLCMStype is correct for all
PIL modes (it probably isn't for the more obscure ones)
2010-07-31 06:52:47 +04:00
Add support for creating custom RGB profiles on the fly
Add support for checking presence of a specific tag in a profile
Add support for other littleCMS features as required
*/
/*
INTENT_PERCEPTUAL 0
INTENT_RELATIVE_COLORIMETRIC 1
INTENT_SATURATION 2
INTENT_ABSOLUTE_COLORIMETRIC 3
*/
/* -------------------------------------------------------------------- */
/* wrapper classes */
/* a profile represents the ICC characteristics for a specific device */
typedef struct {
2021-01-03 06:17:51 +03:00
PyObject_HEAD cmsHPROFILE profile;
2010-07-31 06:52:47 +04:00
} CmsProfileObject;
static PyTypeObject CmsProfile_Type;
2010-07-31 06:52:47 +04:00
#define CmsProfile_Check(op) (Py_TYPE(op) == &CmsProfile_Type)
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_new(cmsHPROFILE profile) {
CmsProfileObject *self;
2010-07-31 06:52:47 +04:00
self = PyObject_New(CmsProfileObject, &CmsProfile_Type);
2020-05-10 12:56:36 +03:00
if (!self) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
self->profile = profile;
2021-01-03 06:17:51 +03:00
return (PyObject *)self;
2010-07-31 06:52:47 +04:00
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_open(PyObject *self, PyObject *args) {
2010-07-31 06:52:47 +04:00
cmsHPROFILE hProfile;
2021-01-03 06:17:51 +03:00
char *sProfile;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile)) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
hProfile = cmsOpenProfileFromFile(sProfile, "r");
if (!hProfile) {
PyErr_SetString(PyExc_OSError, "cannot open profile file");
2010-07-31 06:52:47 +04:00
return NULL;
}
return cms_profile_new(hProfile);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_frombytes(PyObject *self, PyObject *args) {
2010-07-31 06:52:47 +04:00
cmsHPROFILE hProfile;
2021-01-03 06:17:51 +03:00
char *pProfile;
2019-03-26 05:50:57 +03:00
Py_ssize_t nProfile;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile)) {
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
hProfile = cmsOpenProfileFromMem(pProfile, nProfile);
if (!hProfile) {
PyErr_SetString(PyExc_OSError, "cannot open profile from string");
return NULL;
}
2010-07-31 06:52:47 +04:00
return cms_profile_new(hProfile);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_tobytes(PyObject *self, PyObject *args) {
char *pProfile = NULL;
cmsUInt32Number nProfile;
2021-01-03 06:17:51 +03:00
PyObject *CmsProfile;
cmsHPROFILE *profile;
2021-01-03 06:17:51 +03:00
PyObject *ret;
if (!PyArg_ParseTuple(args, "O", &CmsProfile)) {
return NULL;
}
2021-01-03 06:17:51 +03:00
profile = ((CmsProfileObject *)CmsProfile)->profile;
if (!cmsSaveProfileToMem(profile, pProfile, &nProfile)) {
PyErr_SetString(PyExc_OSError, "Could not determine profile size");
return NULL;
}
2021-01-03 06:17:51 +03:00
pProfile = (char *)malloc(nProfile);
if (!pProfile) {
PyErr_SetString(PyExc_OSError, "Out of Memory");
return NULL;
}
if (!cmsSaveProfileToMem(profile, pProfile, &nProfile)) {
PyErr_SetString(PyExc_OSError, "Could not get profile");
free(pProfile);
return NULL;
}
ret = PyBytes_FromStringAndSize(pProfile, (Py_ssize_t)nProfile);
free(pProfile);
return ret;
}
2010-07-31 06:52:47 +04:00
static void
2021-01-03 06:17:51 +03:00
cms_profile_dealloc(CmsProfileObject *self) {
(void)cmsCloseProfile(self->profile);
2010-07-31 06:52:47 +04:00
PyObject_Del(self);
}
/* a transform represents the mapping between two profiles */
typedef struct {
2021-01-03 06:17:51 +03:00
PyObject_HEAD char mode_in[8];
2010-07-31 06:52:47 +04:00
char mode_out[8];
cmsHTRANSFORM transform;
} CmsTransformObject;
static PyTypeObject CmsTransform_Type;
2010-07-31 06:52:47 +04:00
#define CmsTransform_Check(op) (Py_TYPE(op) == &CmsTransform_Type)
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
static PyObject *
cms_transform_new(cmsHTRANSFORM transform, char *mode_in, char *mode_out) {
CmsTransformObject *self;
2010-07-31 06:52:47 +04:00
self = PyObject_New(CmsTransformObject, &CmsTransform_Type);
2020-05-10 12:56:36 +03:00
if (!self) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
self->transform = transform;
strcpy(self->mode_in, mode_in);
strcpy(self->mode_out, mode_out);
2021-01-03 06:17:51 +03:00
return (PyObject *)self;
2010-07-31 06:52:47 +04:00
}
static void
2021-01-03 06:17:51 +03:00
cms_transform_dealloc(CmsTransformObject *self) {
2010-07-31 06:52:47 +04:00
cmsDeleteTransform(self->transform);
PyObject_Del(self);
}
/* -------------------------------------------------------------------- */
/* internal functions */
2013-10-02 09:07:35 +04:00
static cmsUInt32Number
2021-01-03 06:17:51 +03:00
findLCMStype(char *PILmode) {
2010-07-31 06:52:47 +04:00
if (strcmp(PILmode, "RGB") == 0) {
return TYPE_RGBA_8;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "RGBA") == 0) {
2010-07-31 06:52:47 +04:00
return TYPE_RGBA_8;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "RGBX") == 0) {
2010-07-31 06:52:47 +04:00
return TYPE_RGBA_8;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "RGBA;16B") == 0) {
2010-07-31 06:52:47 +04:00
return TYPE_RGBA_16;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "CMYK") == 0) {
2010-07-31 06:52:47 +04:00
return TYPE_CMYK_8;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "L") == 0) {
2010-07-31 06:52:47 +04:00
return TYPE_GRAY_8;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "L;16") == 0) {
2010-07-31 06:52:47 +04:00
return TYPE_GRAY_16;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "L;16B") == 0) {
2010-07-31 06:52:47 +04:00
return TYPE_GRAY_16_SE;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "YCCA") == 0) {
2010-07-31 06:52:47 +04:00
return TYPE_YCbCr_8;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "YCC") == 0) {
2010-07-31 06:52:47 +04:00
return TYPE_YCbCr_8;
2021-01-03 06:17:51 +03:00
} else if (strcmp(PILmode, "LAB") == 0) {
2015-06-02 15:50:22 +03:00
// LabX equivalent like ALab, but not reversed -- no #define in lcms2
2021-01-03 06:17:51 +03:00
return (COLORSPACE_SH(PT_LabV2) | CHANNELS_SH(3) | BYTES_SH(1) | EXTRA_SH(1));
2013-10-11 10:27:34 +04:00
}
2010-07-31 06:52:47 +04:00
else {
/* take a wild guess... but you probably should fail instead. */
return TYPE_GRAY_8; /* so there's no buffer overrun... */
}
}
#define Cms_Min(a, b) ((a) < (b) ? (a) : (b))
static int
2021-01-03 06:17:51 +03:00
pyCMSgetAuxChannelChannel(cmsUInt32Number format, int auxChannelNdx) {
int numColors = T_CHANNELS(format);
int numExtras = T_EXTRA(format);
if (T_SWAPFIRST(format) && T_DOSWAP(format)) {
// reverse order, before anything but last extra is shifted last
2020-05-10 12:56:36 +03:00
if (auxChannelNdx == numExtras - 1) {
return numColors + numExtras - 1;
2020-05-10 12:56:36 +03:00
} else {
return numExtras - 2 - auxChannelNdx;
2020-05-10 12:56:36 +03:00
}
2021-01-03 06:17:51 +03:00
} else if (T_SWAPFIRST(format)) {
// in order, after color channels, but last extra is shifted to first
2020-05-10 12:56:36 +03:00
if (auxChannelNdx == numExtras - 1) {
return 0;
2020-05-10 12:56:36 +03:00
} else {
return numColors + 1 + auxChannelNdx;
2020-05-10 12:56:36 +03:00
}
2021-01-03 06:17:51 +03:00
} else if (T_DOSWAP(format)) {
// reverse order, before anything
return numExtras - 1 - auxChannelNdx;
2021-01-03 06:17:51 +03:00
} else {
// in order, after color channels
return numColors + auxChannelNdx;
}
}
static void
2021-01-03 06:17:51 +03:00
pyCMScopyAux(cmsHTRANSFORM hTransform, Imaging imDst, const Imaging imSrc) {
cmsUInt32Number dstLCMSFormat;
cmsUInt32Number srcLCMSFormat;
int numSrcExtras;
int numDstExtras;
int numExtras;
int ySize;
int xSize;
int channelSize;
int srcChunkSize;
int dstChunkSize;
int e;
// trivially copied
2020-05-10 12:56:36 +03:00
if (imDst == imSrc) {
return;
2020-05-10 12:56:36 +03:00
}
dstLCMSFormat = cmsGetTransformOutputFormat(hTransform);
srcLCMSFormat = cmsGetTransformInputFormat(hTransform);
// currently, all Pillow formats are chunky formats, but check it anyway
2020-05-10 12:56:36 +03:00
if (T_PLANAR(dstLCMSFormat) || T_PLANAR(srcLCMSFormat)) {
return;
2020-05-10 12:56:36 +03:00
}
// copy only if channel format is identical, except OPTIMIZED is ignored as it
// does not affect the aux channel
2021-01-03 06:17:51 +03:00
if (T_FLOAT(dstLCMSFormat) != T_FLOAT(srcLCMSFormat) ||
T_FLAVOR(dstLCMSFormat) != T_FLAVOR(srcLCMSFormat) ||
T_ENDIAN16(dstLCMSFormat) != T_ENDIAN16(srcLCMSFormat) ||
T_BYTES(dstLCMSFormat) != T_BYTES(srcLCMSFormat)) {
return;
2020-05-10 12:56:36 +03:00
}
numSrcExtras = T_EXTRA(srcLCMSFormat);
numDstExtras = T_EXTRA(dstLCMSFormat);
numExtras = Cms_Min(numSrcExtras, numDstExtras);
ySize = Cms_Min(imSrc->ysize, imDst->ysize);
xSize = Cms_Min(imSrc->xsize, imDst->xsize);
channelSize = T_BYTES(dstLCMSFormat);
srcChunkSize = (T_CHANNELS(srcLCMSFormat) + T_EXTRA(srcLCMSFormat)) * channelSize;
dstChunkSize = (T_CHANNELS(dstLCMSFormat) + T_EXTRA(dstLCMSFormat)) * channelSize;
for (e = 0; e < numExtras; ++e) {
int y;
int dstChannel = pyCMSgetAuxChannelChannel(dstLCMSFormat, e);
int srcChannel = pyCMSgetAuxChannelChannel(srcLCMSFormat, e);
for (y = 0; y < ySize; y++) {
int x;
2021-01-03 06:17:51 +03:00
char *pDstExtras = imDst->image[y] + dstChannel * channelSize;
const char *pSrcExtras = imSrc->image[y] + srcChannel * channelSize;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xSize; x++) {
2021-01-03 06:17:51 +03:00
memcpy(
pDstExtras + x * dstChunkSize,
pSrcExtras + x * srcChunkSize,
channelSize);
2020-05-10 12:56:36 +03:00
}
}
}
}
2010-07-31 06:52:47 +04:00
static int
2021-01-03 06:17:51 +03:00
pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform) {
2010-07-31 06:52:47 +04:00
int i;
2020-05-10 12:56:36 +03:00
if (im->xsize > imOut->xsize || im->ysize > imOut->ysize) {
2010-07-31 06:52:47 +04:00
return -1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
Py_BEGIN_ALLOW_THREADS
2021-01-03 06:17:51 +03:00
// transform color channels only
for (i = 0; i < im->ysize; i++) {
2010-07-31 06:52:47 +04:00
cmsDoTransform(hTransform, im->image[i], imOut->image[i], im->xsize);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
// lcms by default does nothing to the auxiliary channels leaving those
// unchanged. To do "the right thing" here, i.e. maintain identical results
// with and without inPlace, we replicate those channels to the output.
//
// As of lcms 2.8, a new cmsFLAGS_COPY_ALPHA flag is introduced which would
// do the same thing automagically. Unfortunately, lcms2.8 is not yet widely
// enough available on all platforms, so we polyfill it here for now.
pyCMScopyAux(hTransform, imOut, im);
2010-07-31 06:52:47 +04:00
Py_END_ALLOW_THREADS
2021-01-03 06:17:51 +03:00
return 0;
2010-07-31 06:52:47 +04:00
}
static cmsHTRANSFORM
2021-01-03 06:17:51 +03:00
_buildTransform(
cmsHPROFILE hInputProfile,
cmsHPROFILE hOutputProfile,
char *sInMode,
char *sOutMode,
int iRenderingIntent,
cmsUInt32Number cmsFLAGS) {
2010-07-31 06:52:47 +04:00
cmsHTRANSFORM hTransform;
Py_BEGIN_ALLOW_THREADS
2021-01-03 06:17:51 +03:00
/* create the transform */
hTransform = cmsCreateTransform(
hInputProfile,
findLCMStype(sInMode),
hOutputProfile,
findLCMStype(sOutMode),
iRenderingIntent,
cmsFLAGS);
2010-07-31 06:52:47 +04:00
Py_END_ALLOW_THREADS
2021-01-03 06:17:51 +03:00
if (!hTransform) {
2010-07-31 06:52:47 +04:00
PyErr_SetString(PyExc_ValueError, "cannot build transform");
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return hTransform; /* if NULL, an exception is set */
}
static cmsHTRANSFORM
2021-01-03 06:17:51 +03:00
_buildProofTransform(
cmsHPROFILE hInputProfile,
cmsHPROFILE hOutputProfile,
cmsHPROFILE hProofProfile,
char *sInMode,
char *sOutMode,
int iRenderingIntent,
int iProofIntent,
cmsUInt32Number cmsFLAGS) {
2010-07-31 06:52:47 +04:00
cmsHTRANSFORM hTransform;
Py_BEGIN_ALLOW_THREADS
2021-01-03 06:17:51 +03:00
/* create the transform */
hTransform = cmsCreateProofingTransform(
hInputProfile,
findLCMStype(sInMode),
hOutputProfile,
findLCMStype(sOutMode),
hProofProfile,
iRenderingIntent,
iProofIntent,
cmsFLAGS);
2010-07-31 06:52:47 +04:00
Py_END_ALLOW_THREADS
2021-01-03 06:17:51 +03:00
if (!hTransform) {
2010-07-31 06:52:47 +04:00
PyErr_SetString(PyExc_ValueError, "cannot build proof transform");
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return hTransform; /* if NULL, an exception is set */
}
/* -------------------------------------------------------------------- */
/* Python callable functions */
static PyObject *
buildTransform(PyObject *self, PyObject *args) {
CmsProfileObject *pInputProfile;
CmsProfileObject *pOutputProfile;
char *sInMode;
char *sOutMode;
int iRenderingIntent = 0;
int cmsFLAGS = 0;
cmsHTRANSFORM transform = NULL;
2021-01-03 06:17:51 +03:00
if (!PyArg_ParseTuple(
args,
"O!O!ss|ii:buildTransform",
&CmsProfile_Type,
&pInputProfile,
&CmsProfile_Type,
&pOutputProfile,
&sInMode,
&sOutMode,
&iRenderingIntent,
&cmsFLAGS)) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
transform = _buildTransform(
pInputProfile->profile,
pOutputProfile->profile,
sInMode,
sOutMode,
iRenderingIntent,
cmsFLAGS);
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (!transform) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return cms_transform_new(transform, sInMode, sOutMode);
}
static PyObject *
2021-01-03 06:17:51 +03:00
buildProofTransform(PyObject *self, PyObject *args) {
2010-07-31 06:52:47 +04:00
CmsProfileObject *pInputProfile;
CmsProfileObject *pOutputProfile;
CmsProfileObject *pProofProfile;
char *sInMode;
char *sOutMode;
int iRenderingIntent = 0;
int iProofIntent = 0;
int cmsFLAGS = 0;
cmsHTRANSFORM transform = NULL;
2021-01-03 06:17:51 +03:00
if (!PyArg_ParseTuple(
args,
"O!O!O!ss|iii:buildProofTransform",
&CmsProfile_Type,
&pInputProfile,
&CmsProfile_Type,
&pOutputProfile,
&CmsProfile_Type,
&pProofProfile,
&sInMode,
&sOutMode,
&iRenderingIntent,
&iProofIntent,
&cmsFLAGS)) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
transform = _buildProofTransform(
pInputProfile->profile,
pOutputProfile->profile,
pProofProfile->profile,
sInMode,
sOutMode,
iRenderingIntent,
iProofIntent,
cmsFLAGS);
2020-05-10 12:56:36 +03:00
if (!transform) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return cms_transform_new(transform, sInMode, sOutMode);
}
static PyObject *
2021-01-03 06:17:51 +03:00
cms_transform_apply(CmsTransformObject *self, PyObject *args) {
2013-02-14 06:36:09 +04:00
Py_ssize_t idIn;
Py_ssize_t idOut;
2010-07-31 06:52:47 +04:00
Imaging im;
Imaging imOut;
int result;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "nn:apply", &idIn, &idOut)) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
im = (Imaging)idIn;
imOut = (Imaging)idOut;
2010-07-31 06:52:47 +04:00
result = pyCMSdoTransform(im, imOut, self->transform);
return Py_BuildValue("i", result);
}
/* -------------------------------------------------------------------- */
/* Python-Callable On-The-Fly profile creation functions */
static PyObject *
2021-01-03 06:17:51 +03:00
createProfile(PyObject *self, PyObject *args) {
2010-07-31 06:52:47 +04:00
char *sColorSpace;
cmsHPROFILE hProfile;
2013-10-02 09:16:59 +04:00
cmsFloat64Number dColorTemp = 0.0;
cmsCIExyY whitePoint;
2013-10-02 09:16:59 +04:00
cmsBool result;
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "s|d:createProfile", &sColorSpace, &dColorTemp)) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
if (strcmp(sColorSpace, "LAB") == 0) {
2013-10-02 09:16:59 +04:00
if (dColorTemp > 0.0) {
result = cmsWhitePointFromTemp(&whitePoint, dColorTemp);
2010-07-31 06:52:47 +04:00
if (!result) {
2021-01-03 06:17:51 +03:00
PyErr_SetString(
PyExc_ValueError,
"ERROR: Could not calculate white point from color temperature "
"provided, must be float in degrees Kelvin");
2010-07-31 06:52:47 +04:00
return NULL;
}
hProfile = cmsCreateLab2Profile(&whitePoint);
2013-10-02 09:16:59 +04:00
} else {
2013-10-02 09:22:56 +04:00
hProfile = cmsCreateLab2Profile(NULL);
2013-10-02 09:16:59 +04:00
}
2021-01-03 06:17:51 +03:00
} else if (strcmp(sColorSpace, "XYZ") == 0) {
2010-07-31 06:52:47 +04:00
hProfile = cmsCreateXYZProfile();
2021-01-03 06:17:51 +03:00
} else if (strcmp(sColorSpace, "sRGB") == 0) {
2010-07-31 06:52:47 +04:00
hProfile = cmsCreate_sRGBProfile();
2021-01-03 06:17:51 +03:00
} else {
2010-07-31 06:52:47 +04:00
hProfile = NULL;
2013-10-02 09:16:59 +04:00
}
2010-07-31 06:52:47 +04:00
if (!hProfile) {
PyErr_SetString(PyExc_ValueError, "failed to create requested color space");
return NULL;
}
return cms_profile_new(hProfile);
}
/* -------------------------------------------------------------------- */
/* profile methods */
static PyObject *
2021-01-03 06:17:51 +03:00
cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) {
2013-10-02 09:24:41 +04:00
cmsBool result;
2010-07-31 06:52:47 +04:00
int intent;
int direction;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "ii:is_intent_supported", &intent, &direction)) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
result = cmsIsIntentSupported(self->profile, intent, direction);
2021-01-03 06:17:51 +03:00
/* printf("cmsIsIntentSupported(%p, %d, %d) => %d\n", self->profile, intent,
* direction, result); */
2010-07-31 06:52:47 +04:00
2019-09-26 15:12:28 +03:00
return PyLong_FromLong(result != 0);
2010-07-31 06:52:47 +04:00
}
#ifdef _WIN32
2017-10-02 09:11:39 +03:00
#ifdef _WIN64
#define F_HANDLE "K"
#else
#define F_HANDLE "k"
#endif
2010-07-31 06:52:47 +04:00
static PyObject *
2021-01-03 06:17:51 +03:00
cms_get_display_profile_win32(PyObject *self, PyObject *args) {
2010-07-31 06:52:47 +04:00
char filename[MAX_PATH];
2013-10-02 09:07:35 +04:00
cmsUInt32Number filename_size;
2010-07-31 06:52:47 +04:00
BOOL ok;
2017-10-02 09:11:39 +03:00
HANDLE handle = 0;
2010-07-31 06:52:47 +04:00
int is_dc = 0;
2021-01-03 06:17:51 +03:00
if (!PyArg_ParseTuple(
args, "|" F_HANDLE "i:get_display_profile", &handle, &is_dc)) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
filename_size = sizeof(filename);
if (is_dc) {
2021-01-03 06:17:51 +03:00
ok = GetICMProfile((HDC)handle, &filename_size, filename);
2010-07-31 06:52:47 +04:00
} else {
2021-01-03 06:17:51 +03:00
HDC dc = GetDC((HWND)handle);
2010-07-31 06:52:47 +04:00
ok = GetICMProfile(dc, &filename_size, filename);
2021-01-03 06:17:51 +03:00
ReleaseDC((HWND)handle, dc);
2010-07-31 06:52:47 +04:00
}
2020-05-10 12:56:36 +03:00
if (ok) {
2021-01-03 06:17:51 +03:00
return PyUnicode_FromStringAndSize(filename, filename_size - 1);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
Py_INCREF(Py_None);
return Py_None;
}
#endif
/* -------------------------------------------------------------------- */
/* Helper functions. */
2021-01-03 06:17:51 +03:00
static PyObject *
_profile_read_mlu(CmsProfileObject *self, cmsTagSignature info) {
PyObject *uni;
char *lc = "en";
char *cc = cmsNoCountry;
cmsMLU *mlu;
cmsUInt32Number len;
wchar_t *buf;
if (!cmsIsTag(self->profile, info)) {
Py_INCREF(Py_None);
return Py_None;
}
mlu = cmsReadTag(self->profile, info);
if (!mlu) {
Py_INCREF(Py_None);
return Py_None;
}
len = cmsMLUgetWide(mlu, lc, cc, NULL, 0);
if (len == 0) {
Py_INCREF(Py_None);
return Py_None;
}
buf = malloc(len);
if (!buf) {
PyErr_SetString(PyExc_OSError, "Out of Memory");
return NULL;
}
/* Just in case the next call fails. */
buf[0] = '\0';
cmsMLUgetWide(mlu, lc, cc, buf, len);
// buf contains additional junk after \0
uni = PyUnicode_FromWideChar(buf, wcslen(buf));
free(buf);
return uni;
}
2021-01-03 06:17:51 +03:00
static PyObject *
_profile_read_int_as_string(cmsUInt32Number nr) {
PyObject *ret;
char buf[5];
2021-01-03 06:17:51 +03:00
buf[0] = (char)((nr >> 24) & 0xff);
buf[1] = (char)((nr >> 16) & 0xff);
buf[2] = (char)((nr >> 8) & 0xff);
buf[3] = (char)(nr & 0xff);
buf[4] = 0;
ret = PyUnicode_DecodeASCII(buf, 4, NULL);
return ret;
}
2021-01-03 06:17:51 +03:00
static PyObject *
_profile_read_signature(CmsProfileObject *self, cmsTagSignature info) {
unsigned int *sig;
if (!cmsIsTag(self->profile, info)) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
sig = (unsigned int *)cmsReadTag(self->profile, info);
if (!sig) {
Py_INCREF(Py_None);
return Py_None;
}
return _profile_read_int_as_string(*sig);
}
2021-01-03 06:17:51 +03:00
static PyObject *
_xyz_py(cmsCIEXYZ *XYZ) {
cmsCIExyY xyY;
cmsXYZ2xyY(&xyY, XYZ);
2021-01-03 06:17:51 +03:00
return Py_BuildValue(
"((d,d,d),(d,d,d))", XYZ->X, XYZ->Y, XYZ->Z, xyY.x, xyY.y, xyY.Y);
}
2021-01-03 06:17:51 +03:00
static PyObject *
_xyz3_py(cmsCIEXYZ *XYZ) {
cmsCIExyY xyY[3];
cmsXYZ2xyY(&xyY[0], &XYZ[0]);
cmsXYZ2xyY(&xyY[1], &XYZ[1]);
cmsXYZ2xyY(&xyY[2], &XYZ[2]);
2021-01-03 06:17:51 +03:00
return Py_BuildValue(
"(((d,d,d),(d,d,d),(d,d,d)),((d,d,d),(d,d,d),(d,d,d)))",
XYZ[0].X,
XYZ[0].Y,
XYZ[0].Z,
XYZ[1].X,
XYZ[1].Y,
XYZ[1].Z,
XYZ[2].X,
XYZ[2].Y,
XYZ[2].Z,
xyY[0].x,
xyY[0].y,
xyY[0].Y,
xyY[1].x,
xyY[1].y,
xyY[1].Y,
xyY[2].x,
xyY[2].y,
xyY[2].Y);
}
2021-01-03 06:17:51 +03:00
static PyObject *
_profile_read_ciexyz(CmsProfileObject *self, cmsTagSignature info, int multi) {
cmsCIEXYZ *XYZ;
if (!cmsIsTag(self->profile, info)) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
XYZ = (cmsCIEXYZ *)cmsReadTag(self->profile, info);
if (!XYZ) {
Py_INCREF(Py_None);
return Py_None;
}
2020-05-10 12:56:36 +03:00
if (multi) {
return _xyz3_py(XYZ);
2020-05-10 12:56:36 +03:00
} else {
return _xyz_py(XYZ);
2020-05-10 12:56:36 +03:00
}
}
2021-01-03 06:17:51 +03:00
static PyObject *
_profile_read_ciexyy_triple(CmsProfileObject *self, cmsTagSignature info) {
cmsCIExyYTRIPLE *triple;
if (!cmsIsTag(self->profile, info)) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
triple = (cmsCIExyYTRIPLE *)cmsReadTag(self->profile, info);
if (!triple) {
Py_INCREF(Py_None);
return Py_None;
}
/* Note: lcms does all the heavy lifting and error checking (nr of
channels == 3). */
2021-01-03 06:17:51 +03:00
return Py_BuildValue(
"((d,d,d),(d,d,d),(d,d,d)),",
triple->Red.x,
triple->Red.y,
triple->Red.Y,
triple->Green.x,
triple->Green.y,
triple->Green.Y,
triple->Blue.x,
triple->Blue.y,
triple->Blue.Y);
}
2021-01-03 06:17:51 +03:00
static PyObject *
_profile_read_named_color_list(CmsProfileObject *self, cmsTagSignature info) {
cmsNAMEDCOLORLIST *ncl;
int i, n;
char name[cmsMAX_PATH];
2021-01-03 06:17:51 +03:00
PyObject *result;
if (!cmsIsTag(self->profile, info)) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
ncl = (cmsNAMEDCOLORLIST *)cmsReadTag(self->profile, info);
if (ncl == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
n = cmsNamedColorCount(ncl);
result = PyList_New(n);
if (!result) {
Py_INCREF(Py_None);
return Py_None;
}
for (i = 0; i < n; i++) {
2021-01-03 06:17:51 +03:00
PyObject *str;
cmsNamedColorInfo(ncl, i, name, NULL, NULL, NULL, NULL);
2019-01-28 09:39:18 +03:00
str = PyUnicode_FromString(name);
if (str == NULL) {
Py_DECREF(result);
Py_INCREF(Py_None);
return Py_None;
}
PyList_SET_ITEM(result, i, str);
}
return result;
}
2021-01-03 06:17:51 +03:00
static cmsBool
_calculate_rgb_primaries(CmsProfileObject *self, cmsCIEXYZTRIPLE *result) {
double input[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
cmsHPROFILE hXYZ;
cmsHTRANSFORM hTransform;
2017-02-14 12:27:02 +03:00
/* https://littlecms2.blogspot.com/2009/07/less-is-more.html */
2016-08-14 09:44:56 +03:00
// double array of RGB values with max on each identity
hXYZ = cmsCreateXYZProfile();
2020-05-10 12:56:36 +03:00
if (hXYZ == NULL) {
return 0;
2020-05-10 12:56:36 +03:00
}
// transform from our profile to XYZ using doubles for highest precision
2021-01-03 06:17:51 +03:00
hTransform = cmsCreateTransform(
self->profile,
TYPE_RGB_DBL,
hXYZ,
TYPE_XYZ_DBL,
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
cmsCloseProfile(hXYZ);
2020-05-10 12:56:36 +03:00
if (hTransform == NULL) {
return 0;
2020-05-10 12:56:36 +03:00
}
2021-01-03 06:17:51 +03:00
cmsDoTransform(hTransform, (void *)input, result, 3);
cmsDeleteTransform(hTransform);
return 1;
}
2021-01-03 06:17:51 +03:00
static cmsBool
_check_intent(
int clut,
cmsHPROFILE hProfile,
cmsUInt32Number Intent,
cmsUInt32Number UsedDirection) {
if (clut) {
return cmsIsCLUT(hProfile, Intent, UsedDirection);
2021-01-03 06:17:51 +03:00
} else {
return cmsIsIntentSupported(hProfile, Intent, UsedDirection);
}
}
#define INTENTS 200
2021-01-03 06:17:51 +03:00
static PyObject *
_is_intent_supported(CmsProfileObject *self, int clut) {
PyObject *result;
int n;
int i;
cmsUInt32Number intent_ids[INTENTS];
char *intent_descs[INTENTS];
result = PyDict_New();
if (result == NULL) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
n = cmsGetSupportedIntents(INTENTS, intent_ids, intent_descs);
for (i = 0; i < n; i++) {
2021-01-03 06:17:51 +03:00
int intent = (int)intent_ids[i];
PyObject *id;
PyObject *entry;
/* Only valid for ICC Intents (otherwise we read invalid memory in lcms
* cmsio1.c). */
if (!(intent == INTENT_PERCEPTUAL || intent == INTENT_RELATIVE_COLORIMETRIC ||
intent == INTENT_SATURATION || intent == INTENT_ABSOLUTE_COLORIMETRIC)) {
2019-01-28 09:39:18 +03:00
continue;
2020-05-10 12:56:36 +03:00
}
2019-01-28 09:39:18 +03:00
2021-01-03 06:17:51 +03:00
id = PyLong_FromLong((long)intent);
entry = Py_BuildValue(
"(OOO)",
_check_intent(clut, self->profile, intent, LCMS_USED_AS_INPUT) ? Py_True
: Py_False,
_check_intent(clut, self->profile, intent, LCMS_USED_AS_OUTPUT) ? Py_True
: Py_False,
_check_intent(clut, self->profile, intent, LCMS_USED_AS_PROOF) ? Py_True
: Py_False);
2019-01-28 09:39:18 +03:00
if (id == NULL || entry == NULL) {
Py_XDECREF(id);
Py_XDECREF(entry);
Py_XDECREF(result);
Py_INCREF(Py_None);
return Py_None;
}
PyDict_SetItem(result, id, entry);
2023-03-11 10:32:43 +03:00
Py_DECREF(id);
Py_DECREF(entry);
}
return result;
}
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
/* Python interface setup */
static PyMethodDef pyCMSdll_methods[] = {
2021-05-11 13:16:44 +03:00
{"profile_open", cms_profile_open, METH_VARARGS},
{"profile_frombytes", cms_profile_frombytes, METH_VARARGS},
2021-05-11 13:16:44 +03:00
{"profile_tobytes", cms_profile_tobytes, METH_VARARGS},
2010-07-31 06:52:47 +04:00
/* profile and transform functions */
2021-05-11 13:16:44 +03:00
{"buildTransform", buildTransform, METH_VARARGS},
{"buildProofTransform", buildProofTransform, METH_VARARGS},
{"createProfile", createProfile, METH_VARARGS},
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
/* platform specific tools */
#ifdef _WIN32
2021-05-11 13:16:44 +03:00
{"get_display_profile_win32", cms_get_display_profile_win32, METH_VARARGS},
2010-07-31 06:52:47 +04:00
#endif
2021-01-03 06:17:51 +03:00
{NULL, NULL}};
2010-07-31 06:52:47 +04:00
static struct PyMethodDef cms_profile_methods[] = {
2021-05-11 13:16:44 +03:00
{"is_intent_supported", (PyCFunction)cms_profile_is_intent_supported, METH_VARARGS},
2010-07-31 06:52:47 +04:00
{NULL, NULL} /* sentinel */
};
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_rendering_intent(CmsProfileObject *self, void *closure) {
2019-09-26 15:12:28 +03:00
return PyLong_FromLong(cmsGetHeaderRenderingIntent(self->profile));
}
/* New-style unicode interfaces. */
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_copyright(CmsProfileObject *self, void *closure) {
return _profile_read_mlu(self, cmsSigCopyrightTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_target(CmsProfileObject *self, void *closure) {
return _profile_read_mlu(self, cmsSigCharTargetTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_manufacturer(CmsProfileObject *self, void *closure) {
return _profile_read_mlu(self, cmsSigDeviceMfgDescTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_model(CmsProfileObject *self, void *closure) {
return _profile_read_mlu(self, cmsSigDeviceModelDescTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_profile_description(CmsProfileObject *self, void *closure) {
return _profile_read_mlu(self, cmsSigProfileDescriptionTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_screening_description(CmsProfileObject *self, void *closure) {
return _profile_read_mlu(self, cmsSigScreeningDescTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_viewing_condition(CmsProfileObject *self, void *closure) {
return _profile_read_mlu(self, cmsSigViewingCondDescTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_creation_date(CmsProfileObject *self, void *closure) {
cmsBool result;
struct tm ct;
result = cmsGetHeaderCreationDateTime(self->profile, &ct);
2021-01-03 06:17:51 +03:00
if (!result) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
return PyDateTime_FromDateAndTime(
1900 + ct.tm_year, ct.tm_mon, ct.tm_mday, ct.tm_hour, ct.tm_min, ct.tm_sec, 0);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_version(CmsProfileObject *self, void *closure) {
cmsFloat64Number version = cmsGetProfileVersion(self->profile);
return PyFloat_FromDouble(version);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_icc_version(CmsProfileObject *self, void *closure) {
return PyLong_FromLong((long)cmsGetEncodedICCversion(self->profile));
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_attributes(CmsProfileObject *self, void *closure) {
cmsUInt64Number attr;
cmsGetHeaderAttributes(self->profile, &attr);
/* This works just as well on Windows (LLP64), 32-bit Linux
(ILP32) and 64-bit Linux (LP64) systems. */
2021-01-03 06:17:51 +03:00
return PyLong_FromUnsignedLongLong((unsigned long long)attr);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_header_flags(CmsProfileObject *self, void *closure) {
cmsUInt32Number flags = cmsGetHeaderFlags(self->profile);
2019-09-26 15:12:28 +03:00
return PyLong_FromLong(flags);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_header_manufacturer(CmsProfileObject *self, void *closure) {
return _profile_read_int_as_string(cmsGetHeaderManufacturer(self->profile));
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_header_model(CmsProfileObject *self, void *closure) {
return _profile_read_int_as_string(cmsGetHeaderModel(self->profile));
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_device_class(CmsProfileObject *self, void *closure) {
return _profile_read_int_as_string(cmsGetDeviceClass(self->profile));
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_connection_space(CmsProfileObject *self, void *closure) {
return _profile_read_int_as_string(cmsGetPCS(self->profile));
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_xcolor_space(CmsProfileObject *self, void *closure) {
return _profile_read_int_as_string(cmsGetColorSpace(self->profile));
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_profile_id(CmsProfileObject *self, void *closure) {
cmsUInt8Number id[16];
cmsGetHeaderProfileID(self->profile, id);
2021-01-03 06:17:51 +03:00
return PyBytes_FromStringAndSize((char *)id, 16);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_is_matrix_shaper(CmsProfileObject *self, void *closure) {
return PyBool_FromLong((long)cmsIsMatrixShaper(self->profile));
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_technology(CmsProfileObject *self, void *closure) {
return _profile_read_signature(self, cmsSigTechnologyTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_colorimetric_intent(CmsProfileObject *self, void *closure) {
return _profile_read_signature(self, cmsSigColorimetricIntentImageStateTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_perceptual_rendering_intent_gamut(
CmsProfileObject *self, void *closure) {
return _profile_read_signature(self, cmsSigPerceptualRenderingIntentGamutTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_saturation_rendering_intent_gamut(
CmsProfileObject *self, void *closure) {
return _profile_read_signature(self, cmsSigSaturationRenderingIntentGamutTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_red_colorant(CmsProfileObject *self, void *closure) {
if (!cmsIsMatrixShaper(self->profile)) {
Py_INCREF(Py_None);
return Py_None;
}
return _profile_read_ciexyz(self, cmsSigRedColorantTag, 0);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_green_colorant(CmsProfileObject *self, void *closure) {
if (!cmsIsMatrixShaper(self->profile)) {
Py_INCREF(Py_None);
return Py_None;
}
return _profile_read_ciexyz(self, cmsSigGreenColorantTag, 0);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_blue_colorant(CmsProfileObject *self, void *closure) {
if (!cmsIsMatrixShaper(self->profile)) {
Py_INCREF(Py_None);
return Py_None;
}
return _profile_read_ciexyz(self, cmsSigBlueColorantTag, 0);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_media_white_point_temperature(
CmsProfileObject *self, void *closure) {
cmsCIEXYZ *XYZ;
cmsCIExyY xyY;
cmsFloat64Number tempK;
cmsTagSignature info = cmsSigMediaWhitePointTag;
cmsBool result;
if (!cmsIsTag(self->profile, info)) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
XYZ = (cmsCIEXYZ *)cmsReadTag(self->profile, info);
if (XYZ == NULL || XYZ->X == 0) {
Py_INCREF(Py_None);
return Py_None;
}
cmsXYZ2xyY(&xyY, XYZ);
result = cmsTempFromWhitePoint(&tempK, &xyY);
if (!result) {
Py_INCREF(Py_None);
return Py_None;
}
return PyFloat_FromDouble(tempK);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_media_white_point(CmsProfileObject *self, void *closure) {
return _profile_read_ciexyz(self, cmsSigMediaWhitePointTag, 0);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_media_black_point(CmsProfileObject *self, void *closure) {
return _profile_read_ciexyz(self, cmsSigMediaBlackPointTag, 0);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_luminance(CmsProfileObject *self, void *closure) {
return _profile_read_ciexyz(self, cmsSigLuminanceTag, 0);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_chromatic_adaptation(CmsProfileObject *self, void *closure) {
return _profile_read_ciexyz(self, cmsSigChromaticAdaptationTag, 1);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_chromaticity(CmsProfileObject *self, void *closure) {
return _profile_read_ciexyy_triple(self, cmsSigChromaticityTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_red_primary(CmsProfileObject *self, void *closure) {
cmsBool result = 0;
cmsCIEXYZTRIPLE primaries;
2020-05-10 12:56:36 +03:00
if (cmsIsMatrixShaper(self->profile)) {
result = _calculate_rgb_primaries(self, &primaries);
2020-05-10 12:56:36 +03:00
}
2021-01-03 06:17:51 +03:00
if (!result) {
Py_INCREF(Py_None);
return Py_None;
}
return _xyz_py(&primaries.Red);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_green_primary(CmsProfileObject *self, void *closure) {
cmsBool result = 0;
cmsCIEXYZTRIPLE primaries;
2020-05-10 12:56:36 +03:00
if (cmsIsMatrixShaper(self->profile)) {
result = _calculate_rgb_primaries(self, &primaries);
2020-05-10 12:56:36 +03:00
}
2021-01-03 06:17:51 +03:00
if (!result) {
Py_INCREF(Py_None);
2019-01-28 09:39:18 +03:00
return Py_None;
}
return _xyz_py(&primaries.Green);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_blue_primary(CmsProfileObject *self, void *closure) {
cmsBool result = 0;
cmsCIEXYZTRIPLE primaries;
2020-05-10 12:56:36 +03:00
if (cmsIsMatrixShaper(self->profile)) {
result = _calculate_rgb_primaries(self, &primaries);
2020-05-10 12:56:36 +03:00
}
2021-01-03 06:17:51 +03:00
if (!result) {
Py_INCREF(Py_None);
2019-01-28 09:39:18 +03:00
return Py_None;
}
return _xyz_py(&primaries.Blue);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_colorant_table(CmsProfileObject *self, void *closure) {
return _profile_read_named_color_list(self, cmsSigColorantTableTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_colorant_table_out(CmsProfileObject *self, void *closure) {
return _profile_read_named_color_list(self, cmsSigColorantTableOutTag);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_is_intent_supported(CmsProfileObject *self, void *closure) {
return _is_intent_supported(self, 0);
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_is_clut(CmsProfileObject *self, void *closure) {
return _is_intent_supported(self, 1);
}
2021-01-03 06:17:51 +03:00
static const char *
_illu_map(int i) {
switch (i) {
case 0:
return "unknown";
case 1:
return "D50";
case 2:
return "D65";
case 3:
return "D93";
case 4:
return "F2";
case 5:
return "D55";
case 6:
return "A";
case 7:
return "E";
case 8:
return "F8";
default:
return NULL;
}
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_icc_measurement_condition(CmsProfileObject *self, void *closure) {
cmsICCMeasurementConditions *mc;
cmsTagSignature info = cmsSigMeasurementTag;
const char *geo;
if (!cmsIsTag(self->profile, info)) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
mc = (cmsICCMeasurementConditions *)cmsReadTag(self->profile, info);
if (!mc) {
Py_INCREF(Py_None);
return Py_None;
}
2020-05-10 12:56:36 +03:00
if (mc->Geometry == 1) {
geo = "45/0, 0/45";
2020-05-10 12:56:36 +03:00
} else if (mc->Geometry == 2) {
geo = "0d, d/0";
2020-05-10 12:56:36 +03:00
} else {
geo = "unknown";
2020-05-10 12:56:36 +03:00
}
2021-01-03 06:17:51 +03:00
return Py_BuildValue(
"{s:i,s:(ddd),s:s,s:d,s:s}",
"observer",
mc->Observer,
"backing",
mc->Backing.X,
mc->Backing.Y,
mc->Backing.Z,
"geo",
geo,
"flare",
mc->Flare,
"illuminant_type",
_illu_map(mc->IlluminantType));
}
2021-01-03 06:17:51 +03:00
static PyObject *
cms_profile_getattr_icc_viewing_condition(CmsProfileObject *self, void *closure) {
cmsICCViewingConditions *vc;
cmsTagSignature info = cmsSigViewingConditionsTag;
if (!cmsIsTag(self->profile, info)) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
vc = (cmsICCViewingConditions *)cmsReadTag(self->profile, info);
if (!vc) {
Py_INCREF(Py_None);
return Py_None;
}
2021-01-03 06:17:51 +03:00
return Py_BuildValue(
"{s:(ddd),s:(ddd),s:s}",
"illuminant",
vc->IlluminantXYZ.X,
vc->IlluminantXYZ.Y,
vc->IlluminantXYZ.Z,
"surround",
vc->SurroundXYZ.X,
vc->SurroundXYZ.Y,
vc->SurroundXYZ.Z,
"illuminant_type",
_illu_map(vc->IlluminantType));
}
static struct PyGetSetDef cms_profile_getsetters[] = {
/* New style interfaces. */
2021-01-03 06:17:51 +03:00
{"rendering_intent", (getter)cms_profile_getattr_rendering_intent},
{"creation_date", (getter)cms_profile_getattr_creation_date},
{"copyright", (getter)cms_profile_getattr_copyright},
{"target", (getter)cms_profile_getattr_target},
{"manufacturer", (getter)cms_profile_getattr_manufacturer},
{"model", (getter)cms_profile_getattr_model},
{"profile_description", (getter)cms_profile_getattr_profile_description},
{"screening_description", (getter)cms_profile_getattr_screening_description},
{"viewing_condition", (getter)cms_profile_getattr_viewing_condition},
{"version", (getter)cms_profile_getattr_version},
{"icc_version", (getter)cms_profile_getattr_icc_version},
{"attributes", (getter)cms_profile_getattr_attributes},
{"header_flags", (getter)cms_profile_getattr_header_flags},
{"header_manufacturer", (getter)cms_profile_getattr_header_manufacturer},
{"header_model", (getter)cms_profile_getattr_header_model},
{"device_class", (getter)cms_profile_getattr_device_class},
{"connection_space", (getter)cms_profile_getattr_connection_space},
{"xcolor_space", (getter)cms_profile_getattr_xcolor_space},
{"profile_id", (getter)cms_profile_getattr_profile_id},
{"is_matrix_shaper", (getter)cms_profile_getattr_is_matrix_shaper},
{"technology", (getter)cms_profile_getattr_technology},
{"colorimetric_intent", (getter)cms_profile_getattr_colorimetric_intent},
{"perceptual_rendering_intent_gamut",
(getter)cms_profile_getattr_perceptual_rendering_intent_gamut},
{"saturation_rendering_intent_gamut",
(getter)cms_profile_getattr_saturation_rendering_intent_gamut},
{"red_colorant", (getter)cms_profile_getattr_red_colorant},
{"green_colorant", (getter)cms_profile_getattr_green_colorant},
{"blue_colorant", (getter)cms_profile_getattr_blue_colorant},
{"red_primary", (getter)cms_profile_getattr_red_primary},
{"green_primary", (getter)cms_profile_getattr_green_primary},
{"blue_primary", (getter)cms_profile_getattr_blue_primary},
{"media_white_point_temperature",
(getter)cms_profile_getattr_media_white_point_temperature},
{"media_white_point", (getter)cms_profile_getattr_media_white_point},
{"media_black_point", (getter)cms_profile_getattr_media_black_point},
{"luminance", (getter)cms_profile_getattr_luminance},
{"chromatic_adaptation", (getter)cms_profile_getattr_chromatic_adaptation},
{"chromaticity", (getter)cms_profile_getattr_chromaticity},
{"colorant_table", (getter)cms_profile_getattr_colorant_table},
{"colorant_table_out", (getter)cms_profile_getattr_colorant_table_out},
{"intent_supported", (getter)cms_profile_getattr_is_intent_supported},
{"clut", (getter)cms_profile_getattr_is_clut},
{"icc_measurement_condition",
(getter)cms_profile_getattr_icc_measurement_condition},
{"icc_viewing_condition", (getter)cms_profile_getattr_icc_viewing_condition},
{NULL}};
static PyTypeObject CmsProfile_Type = {
2021-01-03 06:17:51 +03:00
PyVarObject_HEAD_INIT(NULL, 0) "PIL._imagingcms.CmsProfile", /*tp_name */
sizeof(CmsProfileObject),
0, /*tp_basicsize, tp_itemsize */
2010-07-31 06:52:47 +04:00
/* methods */
2021-01-03 06:17:51 +03:00
(destructor)cms_profile_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number */
0, /*tp_as_sequence */
0, /*tp_as_mapping */
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
cms_profile_methods, /*tp_methods*/
0, /*tp_members*/
cms_profile_getsetters, /*tp_getset*/
2010-07-31 06:52:47 +04:00
};
static struct PyMethodDef cms_transform_methods[] = {
2021-01-03 06:17:51 +03:00
{"apply", (PyCFunction)cms_transform_apply, 1}, {NULL, NULL} /* sentinel */
2010-07-31 06:52:47 +04:00
};
2021-01-03 06:17:51 +03:00
static PyObject *
cms_transform_getattr_inputMode(CmsTransformObject *self, void *closure) {
return PyUnicode_FromString(self->mode_in);
}
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
static PyObject *
cms_transform_getattr_outputMode(CmsTransformObject *self, void *closure) {
return PyUnicode_FromString(self->mode_out);
2010-07-31 06:52:47 +04:00
}
static struct PyGetSetDef cms_transform_getsetters[] = {
2021-01-03 06:17:51 +03:00
{"inputMode", (getter)cms_transform_getattr_inputMode},
{"outputMode", (getter)cms_transform_getattr_outputMode},
{NULL}};
static PyTypeObject CmsTransform_Type = {
2021-01-03 06:17:51 +03:00
PyVarObject_HEAD_INIT(NULL, 0) "CmsTransform",
sizeof(CmsTransformObject),
0,
2010-07-31 06:52:47 +04:00
/* methods */
2021-01-03 06:17:51 +03:00
(destructor)cms_transform_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number */
0, /*tp_as_sequence */
0, /*tp_as_mapping */
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
cms_transform_methods, /*tp_methods*/
0, /*tp_members*/
cms_transform_getsetters, /*tp_getset*/
2010-07-31 06:52:47 +04:00
};
static int
2021-01-03 06:17:51 +03:00
setup_module(PyObject *m) {
2010-07-31 06:52:47 +04:00
PyObject *d;
PyObject *v;
int vn;
2010-07-31 06:52:47 +04:00
CmsProfile_Type.tp_new = PyType_GenericNew;
/* Ready object types */
PyType_Ready(&CmsProfile_Type);
PyType_Ready(&CmsTransform_Type);
2010-07-31 06:52:47 +04:00
Py_INCREF(&CmsProfile_Type);
PyModule_AddObject(m, "CmsProfile", (PyObject *)&CmsProfile_Type);
2010-07-31 06:52:47 +04:00
d = PyModule_GetDict(m);
/* this check is also in PIL.features.pilinfo() */
#if LCMS_VERSION < 2070
vn = LCMS_VERSION;
#else
vn = cmsGetEncodedCMMversion();
#endif
if (vn % 10) {
v = PyUnicode_FromFormat("%d.%d.%d", vn / 1000, (vn / 10) % 100, vn % 10);
} else {
v = PyUnicode_FromFormat("%d.%d", vn / 1000, (vn / 10) % 100);
}
2023-03-22 00:44:52 +03:00
PyDict_SetItemString(d, "littlecms_version", v ? v : Py_None);
2023-03-20 09:34:34 +03:00
Py_XDECREF(v);
return 0;
}
PyMODINIT_FUNC
PyInit__imagingcms(void) {
2021-01-03 06:17:51 +03:00
PyObject *m;
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
2021-01-03 06:17:51 +03:00
"_imagingcms", /* m_name */
NULL, /* m_doc */
-1, /* m_size */
pyCMSdll_methods, /* m_methods */
};
m = PyModule_Create(&module_def);
2020-05-10 12:56:36 +03:00
if (setup_module(m) < 0) {
return NULL;
2020-05-10 12:56:36 +03:00
}
2017-02-14 12:27:02 +03:00
PyDateTime_IMPORT;
return m;
2010-07-31 06:52:47 +04:00
}