Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

squarecapadmin / Pillow   python

Repository URL to install this package:

/ src / _imagingcms.c

/*
 * pyCMS
 * a Python / PIL interface to the littleCMS ICC Color Management System
 * Copyright (C) 2002-2003 Kevin Cazabon
 * kevin@cazabon.com
 * http://www.cazabon.com
 * Adapted/reworked for PIL by Fredrik Lundh
 * Copyright (c) 2009 Fredrik Lundh
 * Updated to LCMS2
 * Copyright (c) 2013 Eric Soroos
 *
 * pyCMS home page:  http://www.cazabon.com/pyCMS
 * littleCMS home page:  http://www.littlecms.com
 * (littleCMS is Copyright (C) 1998-2001 Marti Maria)
 *
 * Originally released under LGPL.  Graciously donated to PIL in
 * March 2009, for distribution under the standard PIL license
 */

#define COPYRIGHTINFO "\
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\
http://www.cazabon.com\n\
"

#include "Python.h" // Include before wchar.h so _GNU_SOURCE is set
#include "wchar.h"
#include "datetime.h"

#include "lcms2.h"
#include "Imaging.h"
#include "py3.h"

#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
  due to improper destructor setup for PyCObjects
  - added buildProofTransformFromOpenProfiles() function
  - eliminated some code redundancy, centralizing several common tasks with internal functions

  0.0.1 alpha:  First public release Dec 26, 2002

*/

/* known to-do list with current version:

   Verify that PILmode->littleCMStype conversion in findLCMStype is correct for all
   PIL modes (it probably isn't for the more obscure ones)

   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 {
    PyObject_HEAD
    cmsHPROFILE profile;
} CmsProfileObject;

static PyTypeObject CmsProfile_Type;

#define CmsProfile_Check(op) (Py_TYPE(op) == &CmsProfile_Type)

static PyObject*
cms_profile_new(cmsHPROFILE profile)
{
    CmsProfileObject* self;

    self = PyObject_New(CmsProfileObject, &CmsProfile_Type);
    if (!self)
        return NULL;

    self->profile = profile;

    return (PyObject*) self;
}

static PyObject*
cms_profile_open(PyObject* self, PyObject* args)
{
    cmsHPROFILE hProfile;

    char* sProfile;
    if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile))
        return NULL;

    hProfile = cmsOpenProfileFromFile(sProfile, "r");
    if (!hProfile) {
        PyErr_SetString(PyExc_IOError, "cannot open profile file");
        return NULL;
    }

    return cms_profile_new(hProfile);
}

static PyObject*
cms_profile_fromstring(PyObject* self, PyObject* args)
{
    cmsHPROFILE hProfile;

    char* pProfile;
    int nProfile;
#if PY_VERSION_HEX >= 0x03000000
    if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile))
        return NULL;
#else
    if (!PyArg_ParseTuple(args, "s#:profile_fromstring", &pProfile, &nProfile))
        return NULL;
#endif

    hProfile = cmsOpenProfileFromMem(pProfile, nProfile);
    if (!hProfile) {
        PyErr_SetString(PyExc_IOError, "cannot open profile from string");
        return NULL;
    }

    return cms_profile_new(hProfile);
}

static PyObject*
cms_profile_tobytes(PyObject* self, PyObject* args)
{
    char *pProfile =NULL;
    cmsUInt32Number nProfile;
    PyObject* CmsProfile;

    cmsHPROFILE *profile;

    PyObject* ret;
    if (!PyArg_ParseTuple(args, "O", &CmsProfile)){
        return NULL;
    }

    profile = ((CmsProfileObject*)CmsProfile)->profile;

    if (!cmsSaveProfileToMem(profile, pProfile, &nProfile)) {
        PyErr_SetString(PyExc_IOError, "Could not determine profile size");
        return NULL;
    }

    pProfile = (char*)malloc(nProfile);
    if (!pProfile) {
        PyErr_SetString(PyExc_IOError, "Out of Memory");
        return NULL;
    }

    if (!cmsSaveProfileToMem(profile, pProfile, &nProfile)) {
        PyErr_SetString(PyExc_IOError, "Could not get profile");
        free(pProfile);
        return NULL;
    }

#if PY_VERSION_HEX >= 0x03000000
    ret = PyBytes_FromStringAndSize(pProfile, (Py_ssize_t)nProfile);
#else
    ret = PyString_FromStringAndSize(pProfile, (Py_ssize_t)nProfile);
#endif

    free(pProfile);
    return ret;
}

static void
cms_profile_dealloc(CmsProfileObject* self)
{
    (void) cmsCloseProfile(self->profile);
    PyObject_Del(self);
}

/* a transform represents the mapping between two profiles */

typedef struct {
    PyObject_HEAD
    char mode_in[8];
    char mode_out[8];
    cmsHTRANSFORM transform;
} CmsTransformObject;

static PyTypeObject CmsTransform_Type;

#define CmsTransform_Check(op) (Py_TYPE(op) == &CmsTransform_Type)

static PyObject*
cms_transform_new(cmsHTRANSFORM transform, char* mode_in, char* mode_out)
{
    CmsTransformObject* self;

    self = PyObject_New(CmsTransformObject, &CmsTransform_Type);
    if (!self)
        return NULL;

    self->transform = transform;

    strcpy(self->mode_in, mode_in);
    strcpy(self->mode_out, mode_out);

    return (PyObject*) self;
}

static void
cms_transform_dealloc(CmsTransformObject* self)
{
    cmsDeleteTransform(self->transform);
    PyObject_Del(self);
}

/* -------------------------------------------------------------------- */
/* internal functions */

static const char*
findICmode(cmsColorSpaceSignature cs)
{
    switch (cs) {
    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 cmsUInt32Number
findLCMStype(char* PILmode)
{
    if (strcmp(PILmode, "RGB") == 0) {
        return TYPE_RGBA_8;
    }
    else if (strcmp(PILmode, "RGBA") == 0) {
        return TYPE_RGBA_8;
    }
    else if (strcmp(PILmode, "RGBX") == 0) {
        return TYPE_RGBA_8;
    }
    else if (strcmp(PILmode, "RGBA;16B") == 0) {
        return TYPE_RGBA_16;
    }
    else if (strcmp(PILmode, "CMYK") == 0) {
        return TYPE_CMYK_8;
    }
    else if (strcmp(PILmode, "L") == 0) {
        return TYPE_GRAY_8;
    }
    else if (strcmp(PILmode, "L;16") == 0) {
        return TYPE_GRAY_16;
    }
    else if (strcmp(PILmode, "L;16B") == 0) {
        return TYPE_GRAY_16_SE;
    }
    else if (strcmp(PILmode, "YCCA") == 0) {
        return TYPE_YCbCr_8;
    }
    else if (strcmp(PILmode, "YCC") == 0) {
        return TYPE_YCbCr_8;
    }
    else if (strcmp(PILmode, "LAB") == 0) {
        // LabX equivalent 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. */
        return TYPE_GRAY_8; /* so there's no buffer overrun... */
    }
}

#define Cms_Min(a, b) ((a) < (b) ? (a) : (b))

static int
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
        if (auxChannelNdx == numExtras - 1)
            return numColors + numExtras - 1;
        else
            return numExtras - 2 - auxChannelNdx;
    }
    else if (T_SWAPFIRST(format)) {
        // in order, after color channels, but last extra is shifted to first
        if (auxChannelNdx == numExtras - 1)
            return 0;
        else
            return numColors + 1 + auxChannelNdx;
    }
    else if (T_DOSWAP(format)) {
        // reverse order, before anything
        return numExtras - 1 - auxChannelNdx;
    }
    else {
        // in order, after color channels
        return numColors + auxChannelNdx;
    }
}

static void
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
    if (imDst == imSrc)
        return;

    dstLCMSFormat = cmsGetTransformOutputFormat(hTransform);
Loading ...