Pillow

Форк
0
/
_imagingcms.c 
1571 строка · 43.9 Кб
1
/*
2
 * pyCMS
3
 * a Python / PIL interface to the littleCMS ICC Color Management System
4
 * Copyright (C) 2002-2003 Kevin Cazabon
5
 * kevin@cazabon.com
6
 * https://www.cazabon.com
7
 * Adapted/reworked for PIL by Fredrik Lundh
8
 * Copyright (c) 2009 Fredrik Lundh
9
 * Updated to LCMS2
10
 * Copyright (c) 2013 Eric Soroos
11
 *
12
 * pyCMS home page:  https://www.cazabon.com/pyCMS
13
 * littleCMS home page:  https://www.littlecms.com
14
 * (littleCMS is Copyright (C) 1998-2001 Marti Maria)
15
 *
16
 * Originally released under LGPL.  Graciously donated to PIL in
17
 * March 2009, for distribution under the standard PIL license
18
 */
19

20
#define COPYRIGHTINFO \
21
    "\
22
pyCMS\n\
23
a Python / PIL interface to the littleCMS ICC Color Management System\n\
24
Copyright (C) 2002-2003 Kevin Cazabon\n\
25
kevin@cazabon.com\n\
26
https://www.cazabon.com\n\
27
"
28

29
#define PY_SSIZE_T_CLEAN
30
#include "Python.h"  // Include before wchar.h so _GNU_SOURCE is set
31
#include "wchar.h"
32
#include "datetime.h"
33

34
#include "lcms2.h"
35
#include "libImaging/Imaging.h"
36

37
#define PYCMSVERSION "1.0.0 pil"
38

39
/* version history */
40

41
/*
42
  1.0.0 pil Integrating littleCMS2
43
  0.1.0 pil integration & refactoring
44
  0.0.2 alpha:  Minor updates, added interfaces to littleCMS features, Jan 6, 2003
45
  - fixed some memory holes in how transforms/profiles were created and passed back to
46
  Python due to improper destructor setup for PyCObjects
47
  - added buildProofTransformFromOpenProfiles() function
48
  - eliminated some code redundancy, centralizing several common tasks with internal
49
  functions
50

51
  0.0.1 alpha:  First public release Dec 26, 2002
52

53
*/
54

55
/* known to-do list:
56

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

60
   Add support for creating custom RGB profiles on the fly
61
   Add support for checking presence of a specific tag in a profile
62
   Add support for other littleCMS features as required
63

64
*/
65

66
/*
67
  INTENT_PERCEPTUAL                 0
68
  INTENT_RELATIVE_COLORIMETRIC      1
69
  INTENT_SATURATION                 2
70
  INTENT_ABSOLUTE_COLORIMETRIC      3
71
*/
72

73
/* -------------------------------------------------------------------- */
74
/* wrapper classes */
75

76
/* a profile represents the ICC characteristics for a specific device */
77

78
typedef struct {
79
    PyObject_HEAD cmsHPROFILE profile;
80
} CmsProfileObject;
81

82
static PyTypeObject CmsProfile_Type;
83

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

86
static PyObject *
87
cms_profile_new(cmsHPROFILE profile) {
88
    CmsProfileObject *self;
89

90
    self = PyObject_New(CmsProfileObject, &CmsProfile_Type);
91
    if (!self) {
92
        return NULL;
93
    }
94

95
    self->profile = profile;
96

97
    return (PyObject *)self;
98
}
99

100
static PyObject *
101
cms_profile_open(PyObject *self, PyObject *args) {
102
    cmsHPROFILE hProfile;
103

104
    char *sProfile;
105
    if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile)) {
106
        return NULL;
107
    }
108

109
    hProfile = cmsOpenProfileFromFile(sProfile, "r");
110
    if (!hProfile) {
111
        PyErr_SetString(PyExc_OSError, "cannot open profile file");
112
        return NULL;
113
    }
114

115
    return cms_profile_new(hProfile);
116
}
117

118
static PyObject *
119
cms_profile_frombytes(PyObject *self, PyObject *args) {
120
    cmsHPROFILE hProfile;
121

122
    char *pProfile;
123
    Py_ssize_t nProfile;
124
    if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile)) {
125
        return NULL;
126
    }
127

128
    hProfile = cmsOpenProfileFromMem(pProfile, nProfile);
129
    if (!hProfile) {
130
        PyErr_SetString(PyExc_OSError, "cannot open profile from string");
131
        return NULL;
132
    }
133

134
    return cms_profile_new(hProfile);
135
}
136

137
static PyObject *
138
cms_profile_tobytes(PyObject *self, PyObject *args) {
139
    char *pProfile = NULL;
140
    cmsUInt32Number nProfile;
141
    PyObject *CmsProfile;
142

143
    cmsHPROFILE *profile;
144

145
    PyObject *ret;
146
    if (!PyArg_ParseTuple(args, "O!", &CmsProfile_Type, &CmsProfile)) {
147
        return NULL;
148
    }
149

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

152
    if (!cmsSaveProfileToMem(profile, pProfile, &nProfile)) {
153
        PyErr_SetString(PyExc_OSError, "Could not determine profile size");
154
        return NULL;
155
    }
156

157
    pProfile = (char *)malloc(nProfile);
158
    if (!pProfile) {
159
        PyErr_SetString(PyExc_OSError, "Out of Memory");
160
        return NULL;
161
    }
162

163
    if (!cmsSaveProfileToMem(profile, pProfile, &nProfile)) {
164
        PyErr_SetString(PyExc_OSError, "Could not get profile");
165
        free(pProfile);
166
        return NULL;
167
    }
168

169
    ret = PyBytes_FromStringAndSize(pProfile, (Py_ssize_t)nProfile);
170

171
    free(pProfile);
172
    return ret;
173
}
174

175
static void
176
cms_profile_dealloc(CmsProfileObject *self) {
177
    (void)cmsCloseProfile(self->profile);
178
    PyObject_Del(self);
179
}
180

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

183
typedef struct {
184
    PyObject_HEAD cmsHTRANSFORM transform;
185
} CmsTransformObject;
186

187
static PyTypeObject CmsTransform_Type;
188

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

191
static PyObject *
192
cms_transform_new(cmsHTRANSFORM transform) {
193
    CmsTransformObject *self;
194

195
    self = PyObject_New(CmsTransformObject, &CmsTransform_Type);
196
    if (!self) {
197
        return NULL;
198
    }
199

200
    self->transform = transform;
201

202
    return (PyObject *)self;
203
}
204

205
static void
206
cms_transform_dealloc(CmsTransformObject *self) {
207
    cmsDeleteTransform(self->transform);
208
    PyObject_Del(self);
209
}
210

211
/* -------------------------------------------------------------------- */
212
/* internal functions */
213

214
static cmsUInt32Number
215
findLCMStype(char *PILmode) {
216
    if (strcmp(PILmode, "RGB") == 0 || strcmp(PILmode, "RGBA") == 0 ||
217
        strcmp(PILmode, "RGBX") == 0) {
218
        return TYPE_RGBA_8;
219
    }
220
    if (strcmp(PILmode, "RGBA;16B") == 0) {
221
        return TYPE_RGBA_16;
222
    }
223
    if (strcmp(PILmode, "CMYK") == 0) {
224
        return TYPE_CMYK_8;
225
    }
226
    if (strcmp(PILmode, "I;16") == 0 || strcmp(PILmode, "I;16L") == 0 ||
227
        strcmp(PILmode, "L;16") == 0) {
228
        return TYPE_GRAY_16;
229
    }
230
    if (strcmp(PILmode, "I;16B") == 0 || strcmp(PILmode, "L;16B") == 0) {
231
        return TYPE_GRAY_16_SE;
232
    }
233
    if (strcmp(PILmode, "YCbCr") == 0 || strcmp(PILmode, "YCCA") == 0 ||
234
        strcmp(PILmode, "YCC") == 0) {
235
        return TYPE_YCbCr_8;
236
    }
237
    if (strcmp(PILmode, "LAB") == 0) {
238
        // LabX equivalent like ALab, but not reversed -- no #define in lcms2
239
        return (COLORSPACE_SH(PT_LabV2) | CHANNELS_SH(3) | BYTES_SH(1) | EXTRA_SH(1));
240
    }
241
    /* presume "1" or "L" by default */
242
    return TYPE_GRAY_8;
243
}
244

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

247
static int
248
pyCMSgetAuxChannelChannel(cmsUInt32Number format, int auxChannelNdx) {
249
    int numColors = T_CHANNELS(format);
250
    int numExtras = T_EXTRA(format);
251

252
    if (T_SWAPFIRST(format) && T_DOSWAP(format)) {
253
        // reverse order, before anything but last extra is shifted last
254
        if (auxChannelNdx == numExtras - 1) {
255
            return numColors + numExtras - 1;
256
        } else {
257
            return numExtras - 2 - auxChannelNdx;
258
        }
259
    } else if (T_SWAPFIRST(format)) {
260
        // in order, after color channels, but last extra is shifted to first
261
        if (auxChannelNdx == numExtras - 1) {
262
            return 0;
263
        } else {
264
            return numColors + 1 + auxChannelNdx;
265
        }
266
    } else if (T_DOSWAP(format)) {
267
        // reverse order, before anything
268
        return numExtras - 1 - auxChannelNdx;
269
    } else {
270
        // in order, after color channels
271
        return numColors + auxChannelNdx;
272
    }
273
}
274

275
static void
276
pyCMScopyAux(cmsHTRANSFORM hTransform, Imaging imDst, const Imaging imSrc) {
277
    cmsUInt32Number dstLCMSFormat;
278
    cmsUInt32Number srcLCMSFormat;
279
    int numSrcExtras;
280
    int numDstExtras;
281
    int numExtras;
282
    int ySize;
283
    int xSize;
284
    int channelSize;
285
    int srcChunkSize;
286
    int dstChunkSize;
287
    int e;
288

289
    // trivially copied
290
    if (imDst == imSrc) {
291
        return;
292
    }
293

294
    dstLCMSFormat = cmsGetTransformOutputFormat(hTransform);
295
    srcLCMSFormat = cmsGetTransformInputFormat(hTransform);
296

297
    // currently, all Pillow formats are chunky formats, but check it anyway
298
    if (T_PLANAR(dstLCMSFormat) || T_PLANAR(srcLCMSFormat)) {
299
        return;
300
    }
301

302
    // copy only if channel format is identical, except OPTIMIZED is ignored as it
303
    // does not affect the aux channel
304
    if (T_FLOAT(dstLCMSFormat) != T_FLOAT(srcLCMSFormat) ||
305
        T_FLAVOR(dstLCMSFormat) != T_FLAVOR(srcLCMSFormat) ||
306
        T_ENDIAN16(dstLCMSFormat) != T_ENDIAN16(srcLCMSFormat) ||
307
        T_BYTES(dstLCMSFormat) != T_BYTES(srcLCMSFormat)) {
308
        return;
309
    }
310

311
    numSrcExtras = T_EXTRA(srcLCMSFormat);
312
    numDstExtras = T_EXTRA(dstLCMSFormat);
313
    numExtras = Cms_Min(numSrcExtras, numDstExtras);
314
    ySize = Cms_Min(imSrc->ysize, imDst->ysize);
315
    xSize = Cms_Min(imSrc->xsize, imDst->xsize);
316
    channelSize = T_BYTES(dstLCMSFormat);
317
    srcChunkSize = (T_CHANNELS(srcLCMSFormat) + T_EXTRA(srcLCMSFormat)) * channelSize;
318
    dstChunkSize = (T_CHANNELS(dstLCMSFormat) + T_EXTRA(dstLCMSFormat)) * channelSize;
319

320
    for (e = 0; e < numExtras; ++e) {
321
        int y;
322
        int dstChannel = pyCMSgetAuxChannelChannel(dstLCMSFormat, e);
323
        int srcChannel = pyCMSgetAuxChannelChannel(srcLCMSFormat, e);
324

325
        for (y = 0; y < ySize; y++) {
326
            int x;
327
            char *pDstExtras = imDst->image[y] + dstChannel * channelSize;
328
            const char *pSrcExtras = imSrc->image[y] + srcChannel * channelSize;
329

330
            for (x = 0; x < xSize; x++) {
331
                memcpy(
332
                    pDstExtras + x * dstChunkSize,
333
                    pSrcExtras + x * srcChunkSize,
334
                    channelSize
335
                );
336
            }
337
        }
338
    }
339
}
340

341
static int
342
pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform) {
343
    int i;
344

345
    if (im->xsize > imOut->xsize || im->ysize > imOut->ysize) {
346
        return -1;
347
    }
348

349
    Py_BEGIN_ALLOW_THREADS
350

351
        // transform color channels only
352
        for (i = 0; i < im->ysize; i++) {
353
        cmsDoTransform(hTransform, im->image[i], imOut->image[i], im->xsize);
354
    }
355

356
    // lcms by default does nothing to the auxiliary channels leaving those
357
    // unchanged. To do "the right thing" here, i.e. maintain identical results
358
    // with and without inPlace, we replicate those channels to the output.
359
    //
360
    // As of lcms 2.8, a new cmsFLAGS_COPY_ALPHA flag is introduced which would
361
    // do the same thing automagically. Unfortunately, lcms2.8 is not yet widely
362
    // enough available on all platforms, so we polyfill it here for now.
363
    pyCMScopyAux(hTransform, imOut, im);
364

365
    Py_END_ALLOW_THREADS
366

367
        return 0;
368
}
369

370
static cmsHTRANSFORM
371
_buildTransform(
372
    cmsHPROFILE hInputProfile,
373
    cmsHPROFILE hOutputProfile,
374
    char *sInMode,
375
    char *sOutMode,
376
    int iRenderingIntent,
377
    cmsUInt32Number cmsFLAGS
378
) {
379
    cmsHTRANSFORM hTransform;
380

381
    Py_BEGIN_ALLOW_THREADS
382

383
        /* create the transform */
384
        hTransform = cmsCreateTransform(
385
            hInputProfile,
386
            findLCMStype(sInMode),
387
            hOutputProfile,
388
            findLCMStype(sOutMode),
389
            iRenderingIntent,
390
            cmsFLAGS
391
        );
392

393
    Py_END_ALLOW_THREADS;
394

395
    if (!hTransform) {
396
        PyErr_SetString(PyExc_ValueError, "cannot build transform");
397
    }
398

399
    return hTransform; /* if NULL, an exception is set */
400
}
401

402
static cmsHTRANSFORM
403
_buildProofTransform(
404
    cmsHPROFILE hInputProfile,
405
    cmsHPROFILE hOutputProfile,
406
    cmsHPROFILE hProofProfile,
407
    char *sInMode,
408
    char *sOutMode,
409
    int iRenderingIntent,
410
    int iProofIntent,
411
    cmsUInt32Number cmsFLAGS
412
) {
413
    cmsHTRANSFORM hTransform;
414

415
    Py_BEGIN_ALLOW_THREADS
416

417
        /* create the transform */
418
        hTransform = cmsCreateProofingTransform(
419
            hInputProfile,
420
            findLCMStype(sInMode),
421
            hOutputProfile,
422
            findLCMStype(sOutMode),
423
            hProofProfile,
424
            iRenderingIntent,
425
            iProofIntent,
426
            cmsFLAGS
427
        );
428

429
    Py_END_ALLOW_THREADS;
430

431
    if (!hTransform) {
432
        PyErr_SetString(PyExc_ValueError, "cannot build proof transform");
433
    }
434

435
    return hTransform; /* if NULL, an exception is set */
436
}
437

438
/* -------------------------------------------------------------------- */
439
/* Python callable functions */
440

441
static PyObject *
442
buildTransform(PyObject *self, PyObject *args) {
443
    CmsProfileObject *pInputProfile;
444
    CmsProfileObject *pOutputProfile;
445
    char *sInMode;
446
    char *sOutMode;
447
    int iRenderingIntent = 0;
448
    int cmsFLAGS = 0;
449

450
    cmsHTRANSFORM transform = NULL;
451

452
    if (!PyArg_ParseTuple(
453
            args,
454
            "O!O!ss|ii:buildTransform",
455
            &CmsProfile_Type,
456
            &pInputProfile,
457
            &CmsProfile_Type,
458
            &pOutputProfile,
459
            &sInMode,
460
            &sOutMode,
461
            &iRenderingIntent,
462
            &cmsFLAGS
463
        )) {
464
        return NULL;
465
    }
466

467
    transform = _buildTransform(
468
        pInputProfile->profile,
469
        pOutputProfile->profile,
470
        sInMode,
471
        sOutMode,
472
        iRenderingIntent,
473
        cmsFLAGS
474
    );
475

476
    if (!transform) {
477
        return NULL;
478
    }
479

480
    return cms_transform_new(transform);
481
}
482

483
static PyObject *
484
buildProofTransform(PyObject *self, PyObject *args) {
485
    CmsProfileObject *pInputProfile;
486
    CmsProfileObject *pOutputProfile;
487
    CmsProfileObject *pProofProfile;
488
    char *sInMode;
489
    char *sOutMode;
490
    int iRenderingIntent = 0;
491
    int iProofIntent = 0;
492
    int cmsFLAGS = 0;
493

494
    cmsHTRANSFORM transform = NULL;
495

496
    if (!PyArg_ParseTuple(
497
            args,
498
            "O!O!O!ss|iii:buildProofTransform",
499
            &CmsProfile_Type,
500
            &pInputProfile,
501
            &CmsProfile_Type,
502
            &pOutputProfile,
503
            &CmsProfile_Type,
504
            &pProofProfile,
505
            &sInMode,
506
            &sOutMode,
507
            &iRenderingIntent,
508
            &iProofIntent,
509
            &cmsFLAGS
510
        )) {
511
        return NULL;
512
    }
513

514
    transform = _buildProofTransform(
515
        pInputProfile->profile,
516
        pOutputProfile->profile,
517
        pProofProfile->profile,
518
        sInMode,
519
        sOutMode,
520
        iRenderingIntent,
521
        iProofIntent,
522
        cmsFLAGS
523
    );
524

525
    if (!transform) {
526
        return NULL;
527
    }
528

529
    return cms_transform_new(transform);
530
}
531

532
static PyObject *
533
cms_transform_apply(CmsTransformObject *self, PyObject *args) {
534
    Py_ssize_t idIn;
535
    Py_ssize_t idOut;
536
    Imaging im;
537
    Imaging imOut;
538

539
    int result;
540

541
    if (!PyArg_ParseTuple(args, "nn:apply", &idIn, &idOut)) {
542
        return NULL;
543
    }
544

545
    im = (Imaging)idIn;
546
    imOut = (Imaging)idOut;
547

548
    result = pyCMSdoTransform(im, imOut, self->transform);
549

550
    return Py_BuildValue("i", result);
551
}
552

553
/* -------------------------------------------------------------------- */
554
/* Python-Callable On-The-Fly profile creation functions */
555

556
static PyObject *
557
createProfile(PyObject *self, PyObject *args) {
558
    char *sColorSpace;
559
    cmsHPROFILE hProfile;
560
    cmsFloat64Number dColorTemp = 0.0;
561
    cmsCIExyY whitePoint;
562
    cmsBool result;
563

564
    if (!PyArg_ParseTuple(args, "s|d:createProfile", &sColorSpace, &dColorTemp)) {
565
        return NULL;
566
    }
567

568
    if (strcmp(sColorSpace, "LAB") == 0) {
569
        if (dColorTemp > 0.0) {
570
            result = cmsWhitePointFromTemp(&whitePoint, dColorTemp);
571
            if (!result) {
572
                PyErr_SetString(
573
                    PyExc_ValueError,
574
                    "ERROR: Could not calculate white point from color temperature "
575
                    "provided, must be float in degrees Kelvin"
576
                );
577
                return NULL;
578
            }
579
            hProfile = cmsCreateLab2Profile(&whitePoint);
580
        } else {
581
            hProfile = cmsCreateLab2Profile(NULL);
582
        }
583
    } else if (strcmp(sColorSpace, "XYZ") == 0) {
584
        hProfile = cmsCreateXYZProfile();
585
    } else if (strcmp(sColorSpace, "sRGB") == 0) {
586
        hProfile = cmsCreate_sRGBProfile();
587
    } else {
588
        hProfile = NULL;
589
    }
590

591
    if (!hProfile) {
592
        PyErr_SetString(PyExc_ValueError, "failed to create requested color space");
593
        return NULL;
594
    }
595

596
    return cms_profile_new(hProfile);
597
}
598

599
/* -------------------------------------------------------------------- */
600
/* profile methods */
601

602
static PyObject *
603
cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) {
604
    cmsBool result;
605

606
    int intent;
607
    int direction;
608
    if (!PyArg_ParseTuple(args, "ii:is_intent_supported", &intent, &direction)) {
609
        return NULL;
610
    }
611

612
    result = cmsIsIntentSupported(self->profile, intent, direction);
613

614
    /* printf("cmsIsIntentSupported(%p, %d, %d) => %d\n", self->profile, intent,
615
     * direction, result); */
616

617
    return PyLong_FromLong(result != 0);
618
}
619

620
#ifdef _WIN32
621

622
#ifdef _WIN64
623
#define F_HANDLE "K"
624
#else
625
#define F_HANDLE "k"
626
#endif
627

628
static PyObject *
629
cms_get_display_profile_win32(PyObject *self, PyObject *args) {
630
    char filename[MAX_PATH];
631
    DWORD filename_size;
632
    BOOL ok;
633

634
    HANDLE handle = 0;
635
    int is_dc = 0;
636
    if (!PyArg_ParseTuple(
637
            args, "|" F_HANDLE "i:get_display_profile", &handle, &is_dc
638
        )) {
639
        return NULL;
640
    }
641

642
    filename_size = sizeof(filename);
643

644
    if (is_dc) {
645
        ok = GetICMProfile((HDC)handle, &filename_size, filename);
646
    } else {
647
        HDC dc = GetDC((HWND)handle);
648
        ok = GetICMProfile(dc, &filename_size, filename);
649
        ReleaseDC((HWND)handle, dc);
650
    }
651

652
    if (ok) {
653
        return PyUnicode_FromStringAndSize(filename, filename_size - 1);
654
    }
655

656
    Py_INCREF(Py_None);
657
    return Py_None;
658
}
659
#endif
660

661
/* -------------------------------------------------------------------- */
662
/* Helper functions.  */
663

664
static PyObject *
665
_profile_read_mlu(CmsProfileObject *self, cmsTagSignature info) {
666
    PyObject *uni;
667
    char *lc = "en";
668
    char *cc = cmsNoCountry;
669
    cmsMLU *mlu;
670
    cmsUInt32Number len;
671
    wchar_t *buf;
672

673
    if (!cmsIsTag(self->profile, info)) {
674
        Py_INCREF(Py_None);
675
        return Py_None;
676
    }
677

678
    mlu = cmsReadTag(self->profile, info);
679
    if (!mlu) {
680
        Py_INCREF(Py_None);
681
        return Py_None;
682
    }
683

684
    len = cmsMLUgetWide(mlu, lc, cc, NULL, 0);
685
    if (len == 0) {
686
        Py_INCREF(Py_None);
687
        return Py_None;
688
    }
689

690
    buf = malloc(len);
691
    if (!buf) {
692
        PyErr_SetString(PyExc_OSError, "Out of Memory");
693
        return NULL;
694
    }
695
    /* Just in case the next call fails.  */
696
    buf[0] = '\0';
697

698
    cmsMLUgetWide(mlu, lc, cc, buf, len);
699
    // buf contains additional junk after \0
700
    uni = PyUnicode_FromWideChar(buf, wcslen(buf));
701
    free(buf);
702

703
    return uni;
704
}
705

706
static PyObject *
707
_profile_read_int_as_string(cmsUInt32Number nr) {
708
    PyObject *ret;
709
    char buf[5];
710
    buf[0] = (char)((nr >> 24) & 0xff);
711
    buf[1] = (char)((nr >> 16) & 0xff);
712
    buf[2] = (char)((nr >> 8) & 0xff);
713
    buf[3] = (char)(nr & 0xff);
714
    buf[4] = 0;
715

716
    ret = PyUnicode_DecodeASCII(buf, 4, NULL);
717
    return ret;
718
}
719

720
static PyObject *
721
_profile_read_signature(CmsProfileObject *self, cmsTagSignature info) {
722
    unsigned int *sig;
723

724
    if (!cmsIsTag(self->profile, info)) {
725
        Py_INCREF(Py_None);
726
        return Py_None;
727
    }
728

729
    sig = (unsigned int *)cmsReadTag(self->profile, info);
730
    if (!sig) {
731
        Py_INCREF(Py_None);
732
        return Py_None;
733
    }
734

735
    return _profile_read_int_as_string(*sig);
736
}
737

738
static PyObject *
739
_xyz_py(cmsCIEXYZ *XYZ) {
740
    cmsCIExyY xyY;
741
    cmsXYZ2xyY(&xyY, XYZ);
742
    return Py_BuildValue(
743
        "((d,d,d),(d,d,d))", XYZ->X, XYZ->Y, XYZ->Z, xyY.x, xyY.y, xyY.Y
744
    );
745
}
746

747
static PyObject *
748
_xyz3_py(cmsCIEXYZ *XYZ) {
749
    cmsCIExyY xyY[3];
750
    cmsXYZ2xyY(&xyY[0], &XYZ[0]);
751
    cmsXYZ2xyY(&xyY[1], &XYZ[1]);
752
    cmsXYZ2xyY(&xyY[2], &XYZ[2]);
753

754
    return Py_BuildValue(
755
        "(((d,d,d),(d,d,d),(d,d,d)),((d,d,d),(d,d,d),(d,d,d)))",
756
        XYZ[0].X,
757
        XYZ[0].Y,
758
        XYZ[0].Z,
759
        XYZ[1].X,
760
        XYZ[1].Y,
761
        XYZ[1].Z,
762
        XYZ[2].X,
763
        XYZ[2].Y,
764
        XYZ[2].Z,
765
        xyY[0].x,
766
        xyY[0].y,
767
        xyY[0].Y,
768
        xyY[1].x,
769
        xyY[1].y,
770
        xyY[1].Y,
771
        xyY[2].x,
772
        xyY[2].y,
773
        xyY[2].Y
774
    );
775
}
776

777
static PyObject *
778
_profile_read_ciexyz(CmsProfileObject *self, cmsTagSignature info, int multi) {
779
    cmsCIEXYZ *XYZ;
780

781
    if (!cmsIsTag(self->profile, info)) {
782
        Py_INCREF(Py_None);
783
        return Py_None;
784
    }
785

786
    XYZ = (cmsCIEXYZ *)cmsReadTag(self->profile, info);
787
    if (!XYZ) {
788
        Py_INCREF(Py_None);
789
        return Py_None;
790
    }
791
    if (multi) {
792
        return _xyz3_py(XYZ);
793
    } else {
794
        return _xyz_py(XYZ);
795
    }
796
}
797

798
static PyObject *
799
_profile_read_ciexyy_triple(CmsProfileObject *self, cmsTagSignature info) {
800
    cmsCIExyYTRIPLE *triple;
801

802
    if (!cmsIsTag(self->profile, info)) {
803
        Py_INCREF(Py_None);
804
        return Py_None;
805
    }
806

807
    triple = (cmsCIExyYTRIPLE *)cmsReadTag(self->profile, info);
808
    if (!triple) {
809
        Py_INCREF(Py_None);
810
        return Py_None;
811
    }
812

813
    /* Note: lcms does all the heavy lifting and error checking (nr of
814
       channels == 3).  */
815
    return Py_BuildValue(
816
        "((d,d,d),(d,d,d),(d,d,d)),",
817
        triple->Red.x,
818
        triple->Red.y,
819
        triple->Red.Y,
820
        triple->Green.x,
821
        triple->Green.y,
822
        triple->Green.Y,
823
        triple->Blue.x,
824
        triple->Blue.y,
825
        triple->Blue.Y
826
    );
827
}
828

829
static PyObject *
830
_profile_read_named_color_list(CmsProfileObject *self, cmsTagSignature info) {
831
    cmsNAMEDCOLORLIST *ncl;
832
    int i, n;
833
    char name[cmsMAX_PATH];
834
    PyObject *result;
835

836
    if (!cmsIsTag(self->profile, info)) {
837
        Py_INCREF(Py_None);
838
        return Py_None;
839
    }
840

841
    ncl = (cmsNAMEDCOLORLIST *)cmsReadTag(self->profile, info);
842
    if (ncl == NULL) {
843
        Py_INCREF(Py_None);
844
        return Py_None;
845
    }
846

847
    n = cmsNamedColorCount(ncl);
848
    result = PyList_New(n);
849
    if (!result) {
850
        Py_INCREF(Py_None);
851
        return Py_None;
852
    }
853

854
    for (i = 0; i < n; i++) {
855
        PyObject *str;
856
        cmsNamedColorInfo(ncl, i, name, NULL, NULL, NULL, NULL);
857
        str = PyUnicode_FromString(name);
858
        if (str == NULL) {
859
            Py_DECREF(result);
860
            Py_INCREF(Py_None);
861
            return Py_None;
862
        }
863
        PyList_SET_ITEM(result, i, str);
864
    }
865

866
    return result;
867
}
868

869
static cmsBool
870
_calculate_rgb_primaries(CmsProfileObject *self, cmsCIEXYZTRIPLE *result) {
871
    double input[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
872
    cmsHPROFILE hXYZ;
873
    cmsHTRANSFORM hTransform;
874

875
    /* https://littlecms2.blogspot.com/2009/07/less-is-more.html */
876

877
    // double array of RGB values with max on each identity
878
    hXYZ = cmsCreateXYZProfile();
879
    if (hXYZ == NULL) {
880
        return 0;
881
    }
882

883
    // transform from our profile to XYZ using doubles for highest precision
884
    hTransform = cmsCreateTransform(
885
        self->profile,
886
        TYPE_RGB_DBL,
887
        hXYZ,
888
        TYPE_XYZ_DBL,
889
        INTENT_RELATIVE_COLORIMETRIC,
890
        cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE
891
    );
892
    cmsCloseProfile(hXYZ);
893
    if (hTransform == NULL) {
894
        return 0;
895
    }
896

897
    cmsDoTransform(hTransform, (void *)input, result, 3);
898
    cmsDeleteTransform(hTransform);
899
    return 1;
900
}
901

902
static cmsBool
903
_check_intent(
904
    int clut,
905
    cmsHPROFILE hProfile,
906
    cmsUInt32Number Intent,
907
    cmsUInt32Number UsedDirection
908
) {
909
    if (clut) {
910
        return cmsIsCLUT(hProfile, Intent, UsedDirection);
911
    } else {
912
        return cmsIsIntentSupported(hProfile, Intent, UsedDirection);
913
    }
914
}
915

916
#define INTENTS 200
917

918
static PyObject *
919
_is_intent_supported(CmsProfileObject *self, int clut) {
920
    PyObject *result;
921
    int n;
922
    int i;
923
    cmsUInt32Number intent_ids[INTENTS];
924
    char *intent_descs[INTENTS];
925

926
    result = PyDict_New();
927
    if (result == NULL) {
928
        Py_INCREF(Py_None);
929
        return Py_None;
930
    }
931

932
    n = cmsGetSupportedIntents(INTENTS, intent_ids, intent_descs);
933
    for (i = 0; i < n; i++) {
934
        int intent = (int)intent_ids[i];
935
        PyObject *id;
936
        PyObject *entry;
937

938
        /* Only valid for ICC Intents (otherwise we read invalid memory in lcms
939
         * cmsio1.c). */
940
        if (!(intent == INTENT_PERCEPTUAL || intent == INTENT_RELATIVE_COLORIMETRIC ||
941
              intent == INTENT_SATURATION || intent == INTENT_ABSOLUTE_COLORIMETRIC)) {
942
            continue;
943
        }
944

945
        id = PyLong_FromLong((long)intent);
946
        entry = Py_BuildValue(
947
            "(OOO)",
948
            _check_intent(clut, self->profile, intent, LCMS_USED_AS_INPUT) ? Py_True
949
                                                                           : Py_False,
950
            _check_intent(clut, self->profile, intent, LCMS_USED_AS_OUTPUT) ? Py_True
951
                                                                            : Py_False,
952
            _check_intent(clut, self->profile, intent, LCMS_USED_AS_PROOF) ? Py_True
953
                                                                           : Py_False
954
        );
955
        if (id == NULL || entry == NULL) {
956
            Py_XDECREF(id);
957
            Py_XDECREF(entry);
958
            Py_XDECREF(result);
959
            Py_INCREF(Py_None);
960
            return Py_None;
961
        }
962
        PyDict_SetItem(result, id, entry);
963
        Py_DECREF(id);
964
        Py_DECREF(entry);
965
    }
966
    return result;
967
}
968

969
/* -------------------------------------------------------------------- */
970
/* Python interface setup */
971

972
static PyMethodDef pyCMSdll_methods[] = {
973

974
    {"profile_open", cms_profile_open, METH_VARARGS},
975
    {"profile_frombytes", cms_profile_frombytes, METH_VARARGS},
976
    {"profile_tobytes", cms_profile_tobytes, METH_VARARGS},
977

978
    /* profile and transform functions */
979
    {"buildTransform", buildTransform, METH_VARARGS},
980
    {"buildProofTransform", buildProofTransform, METH_VARARGS},
981
    {"createProfile", createProfile, METH_VARARGS},
982

983
/* platform specific tools */
984
#ifdef _WIN32
985
    {"get_display_profile_win32", cms_get_display_profile_win32, METH_VARARGS},
986
#endif
987

988
    {NULL, NULL}
989
};
990

991
static struct PyMethodDef cms_profile_methods[] = {
992
    {"is_intent_supported", (PyCFunction)cms_profile_is_intent_supported, METH_VARARGS},
993
    {NULL, NULL} /* sentinel */
994
};
995

996
static PyObject *
997
cms_profile_getattr_rendering_intent(CmsProfileObject *self, void *closure) {
998
    return PyLong_FromLong(cmsGetHeaderRenderingIntent(self->profile));
999
}
1000

1001
/* New-style unicode interfaces.  */
1002
static PyObject *
1003
cms_profile_getattr_copyright(CmsProfileObject *self, void *closure) {
1004
    return _profile_read_mlu(self, cmsSigCopyrightTag);
1005
}
1006

1007
static PyObject *
1008
cms_profile_getattr_target(CmsProfileObject *self, void *closure) {
1009
    return _profile_read_mlu(self, cmsSigCharTargetTag);
1010
}
1011

1012
static PyObject *
1013
cms_profile_getattr_manufacturer(CmsProfileObject *self, void *closure) {
1014
    return _profile_read_mlu(self, cmsSigDeviceMfgDescTag);
1015
}
1016

1017
static PyObject *
1018
cms_profile_getattr_model(CmsProfileObject *self, void *closure) {
1019
    return _profile_read_mlu(self, cmsSigDeviceModelDescTag);
1020
}
1021

1022
static PyObject *
1023
cms_profile_getattr_profile_description(CmsProfileObject *self, void *closure) {
1024
    return _profile_read_mlu(self, cmsSigProfileDescriptionTag);
1025
}
1026

1027
static PyObject *
1028
cms_profile_getattr_screening_description(CmsProfileObject *self, void *closure) {
1029
    return _profile_read_mlu(self, cmsSigScreeningDescTag);
1030
}
1031

1032
static PyObject *
1033
cms_profile_getattr_viewing_condition(CmsProfileObject *self, void *closure) {
1034
    return _profile_read_mlu(self, cmsSigViewingCondDescTag);
1035
}
1036

1037
static PyObject *
1038
cms_profile_getattr_creation_date(CmsProfileObject *self, void *closure) {
1039
    cmsBool result;
1040
    struct tm ct;
1041

1042
    result = cmsGetHeaderCreationDateTime(self->profile, &ct);
1043
    if (!result) {
1044
        Py_INCREF(Py_None);
1045
        return Py_None;
1046
    }
1047

1048
    return PyDateTime_FromDateAndTime(
1049
        1900 + ct.tm_year, ct.tm_mon, ct.tm_mday, ct.tm_hour, ct.tm_min, ct.tm_sec, 0
1050
    );
1051
}
1052

1053
static PyObject *
1054
cms_profile_getattr_version(CmsProfileObject *self, void *closure) {
1055
    cmsFloat64Number version = cmsGetProfileVersion(self->profile);
1056
    return PyFloat_FromDouble(version);
1057
}
1058

1059
static PyObject *
1060
cms_profile_getattr_icc_version(CmsProfileObject *self, void *closure) {
1061
    return PyLong_FromLong((long)cmsGetEncodedICCversion(self->profile));
1062
}
1063

1064
static PyObject *
1065
cms_profile_getattr_attributes(CmsProfileObject *self, void *closure) {
1066
    cmsUInt64Number attr;
1067
    cmsGetHeaderAttributes(self->profile, &attr);
1068
    /* This works just as well on Windows (LLP64), 32-bit Linux
1069
       (ILP32) and 64-bit Linux (LP64) systems.  */
1070
    return PyLong_FromUnsignedLongLong((unsigned long long)attr);
1071
}
1072

1073
static PyObject *
1074
cms_profile_getattr_header_flags(CmsProfileObject *self, void *closure) {
1075
    cmsUInt32Number flags = cmsGetHeaderFlags(self->profile);
1076
    return PyLong_FromLong(flags);
1077
}
1078

1079
static PyObject *
1080
cms_profile_getattr_header_manufacturer(CmsProfileObject *self, void *closure) {
1081
    return _profile_read_int_as_string(cmsGetHeaderManufacturer(self->profile));
1082
}
1083

1084
static PyObject *
1085
cms_profile_getattr_header_model(CmsProfileObject *self, void *closure) {
1086
    return _profile_read_int_as_string(cmsGetHeaderModel(self->profile));
1087
}
1088

1089
static PyObject *
1090
cms_profile_getattr_device_class(CmsProfileObject *self, void *closure) {
1091
    return _profile_read_int_as_string(cmsGetDeviceClass(self->profile));
1092
}
1093

1094
static PyObject *
1095
cms_profile_getattr_connection_space(CmsProfileObject *self, void *closure) {
1096
    return _profile_read_int_as_string(cmsGetPCS(self->profile));
1097
}
1098

1099
static PyObject *
1100
cms_profile_getattr_xcolor_space(CmsProfileObject *self, void *closure) {
1101
    return _profile_read_int_as_string(cmsGetColorSpace(self->profile));
1102
}
1103

1104
static PyObject *
1105
cms_profile_getattr_profile_id(CmsProfileObject *self, void *closure) {
1106
    cmsUInt8Number id[16];
1107
    cmsGetHeaderProfileID(self->profile, id);
1108
    return PyBytes_FromStringAndSize((char *)id, 16);
1109
}
1110

1111
static PyObject *
1112
cms_profile_getattr_is_matrix_shaper(CmsProfileObject *self, void *closure) {
1113
    return PyBool_FromLong((long)cmsIsMatrixShaper(self->profile));
1114
}
1115

1116
static PyObject *
1117
cms_profile_getattr_technology(CmsProfileObject *self, void *closure) {
1118
    return _profile_read_signature(self, cmsSigTechnologyTag);
1119
}
1120

1121
static PyObject *
1122
cms_profile_getattr_colorimetric_intent(CmsProfileObject *self, void *closure) {
1123
    return _profile_read_signature(self, cmsSigColorimetricIntentImageStateTag);
1124
}
1125

1126
static PyObject *
1127
cms_profile_getattr_perceptual_rendering_intent_gamut(
1128
    CmsProfileObject *self, void *closure
1129
) {
1130
    return _profile_read_signature(self, cmsSigPerceptualRenderingIntentGamutTag);
1131
}
1132

1133
static PyObject *
1134
cms_profile_getattr_saturation_rendering_intent_gamut(
1135
    CmsProfileObject *self, void *closure
1136
) {
1137
    return _profile_read_signature(self, cmsSigSaturationRenderingIntentGamutTag);
1138
}
1139

1140
static PyObject *
1141
cms_profile_getattr_red_colorant(CmsProfileObject *self, void *closure) {
1142
    if (!cmsIsMatrixShaper(self->profile)) {
1143
        Py_INCREF(Py_None);
1144
        return Py_None;
1145
    }
1146
    return _profile_read_ciexyz(self, cmsSigRedColorantTag, 0);
1147
}
1148

1149
static PyObject *
1150
cms_profile_getattr_green_colorant(CmsProfileObject *self, void *closure) {
1151
    if (!cmsIsMatrixShaper(self->profile)) {
1152
        Py_INCREF(Py_None);
1153
        return Py_None;
1154
    }
1155
    return _profile_read_ciexyz(self, cmsSigGreenColorantTag, 0);
1156
}
1157

1158
static PyObject *
1159
cms_profile_getattr_blue_colorant(CmsProfileObject *self, void *closure) {
1160
    if (!cmsIsMatrixShaper(self->profile)) {
1161
        Py_INCREF(Py_None);
1162
        return Py_None;
1163
    }
1164
    return _profile_read_ciexyz(self, cmsSigBlueColorantTag, 0);
1165
}
1166

1167
static PyObject *
1168
cms_profile_getattr_media_white_point_temperature(
1169
    CmsProfileObject *self, void *closure
1170
) {
1171
    cmsCIEXYZ *XYZ;
1172
    cmsCIExyY xyY;
1173
    cmsFloat64Number tempK;
1174
    cmsTagSignature info = cmsSigMediaWhitePointTag;
1175
    cmsBool result;
1176

1177
    if (!cmsIsTag(self->profile, info)) {
1178
        Py_INCREF(Py_None);
1179
        return Py_None;
1180
    }
1181

1182
    XYZ = (cmsCIEXYZ *)cmsReadTag(self->profile, info);
1183
    if (XYZ == NULL || XYZ->X == 0) {
1184
        Py_INCREF(Py_None);
1185
        return Py_None;
1186
    }
1187

1188
    cmsXYZ2xyY(&xyY, XYZ);
1189
    result = cmsTempFromWhitePoint(&tempK, &xyY);
1190
    if (!result) {
1191
        Py_INCREF(Py_None);
1192
        return Py_None;
1193
    }
1194
    return PyFloat_FromDouble(tempK);
1195
}
1196

1197
static PyObject *
1198
cms_profile_getattr_media_white_point(CmsProfileObject *self, void *closure) {
1199
    return _profile_read_ciexyz(self, cmsSigMediaWhitePointTag, 0);
1200
}
1201

1202
static PyObject *
1203
cms_profile_getattr_media_black_point(CmsProfileObject *self, void *closure) {
1204
    return _profile_read_ciexyz(self, cmsSigMediaBlackPointTag, 0);
1205
}
1206

1207
static PyObject *
1208
cms_profile_getattr_luminance(CmsProfileObject *self, void *closure) {
1209
    return _profile_read_ciexyz(self, cmsSigLuminanceTag, 0);
1210
}
1211

1212
static PyObject *
1213
cms_profile_getattr_chromatic_adaptation(CmsProfileObject *self, void *closure) {
1214
    return _profile_read_ciexyz(self, cmsSigChromaticAdaptationTag, 1);
1215
}
1216

1217
static PyObject *
1218
cms_profile_getattr_chromaticity(CmsProfileObject *self, void *closure) {
1219
    return _profile_read_ciexyy_triple(self, cmsSigChromaticityTag);
1220
}
1221

1222
static PyObject *
1223
cms_profile_getattr_red_primary(CmsProfileObject *self, void *closure) {
1224
    cmsBool result = 0;
1225
    cmsCIEXYZTRIPLE primaries;
1226

1227
    if (cmsIsMatrixShaper(self->profile)) {
1228
        result = _calculate_rgb_primaries(self, &primaries);
1229
    }
1230
    if (!result) {
1231
        Py_INCREF(Py_None);
1232
        return Py_None;
1233
    }
1234

1235
    return _xyz_py(&primaries.Red);
1236
}
1237

1238
static PyObject *
1239
cms_profile_getattr_green_primary(CmsProfileObject *self, void *closure) {
1240
    cmsBool result = 0;
1241
    cmsCIEXYZTRIPLE primaries;
1242

1243
    if (cmsIsMatrixShaper(self->profile)) {
1244
        result = _calculate_rgb_primaries(self, &primaries);
1245
    }
1246
    if (!result) {
1247
        Py_INCREF(Py_None);
1248
        return Py_None;
1249
    }
1250

1251
    return _xyz_py(&primaries.Green);
1252
}
1253

1254
static PyObject *
1255
cms_profile_getattr_blue_primary(CmsProfileObject *self, void *closure) {
1256
    cmsBool result = 0;
1257
    cmsCIEXYZTRIPLE primaries;
1258

1259
    if (cmsIsMatrixShaper(self->profile)) {
1260
        result = _calculate_rgb_primaries(self, &primaries);
1261
    }
1262
    if (!result) {
1263
        Py_INCREF(Py_None);
1264
        return Py_None;
1265
    }
1266

1267
    return _xyz_py(&primaries.Blue);
1268
}
1269

1270
static PyObject *
1271
cms_profile_getattr_colorant_table(CmsProfileObject *self, void *closure) {
1272
    return _profile_read_named_color_list(self, cmsSigColorantTableTag);
1273
}
1274

1275
static PyObject *
1276
cms_profile_getattr_colorant_table_out(CmsProfileObject *self, void *closure) {
1277
    return _profile_read_named_color_list(self, cmsSigColorantTableOutTag);
1278
}
1279

1280
static PyObject *
1281
cms_profile_getattr_is_intent_supported(CmsProfileObject *self, void *closure) {
1282
    return _is_intent_supported(self, 0);
1283
}
1284

1285
static PyObject *
1286
cms_profile_getattr_is_clut(CmsProfileObject *self, void *closure) {
1287
    return _is_intent_supported(self, 1);
1288
}
1289

1290
static const char *
1291
_illu_map(int i) {
1292
    switch (i) {
1293
        case 0:
1294
            return "unknown";
1295
        case 1:
1296
            return "D50";
1297
        case 2:
1298
            return "D65";
1299
        case 3:
1300
            return "D93";
1301
        case 4:
1302
            return "F2";
1303
        case 5:
1304
            return "D55";
1305
        case 6:
1306
            return "A";
1307
        case 7:
1308
            return "E";
1309
        case 8:
1310
            return "F8";
1311
        default:
1312
            return NULL;
1313
    }
1314
}
1315

1316
static PyObject *
1317
cms_profile_getattr_icc_measurement_condition(CmsProfileObject *self, void *closure) {
1318
    cmsICCMeasurementConditions *mc;
1319
    cmsTagSignature info = cmsSigMeasurementTag;
1320
    const char *geo;
1321

1322
    if (!cmsIsTag(self->profile, info)) {
1323
        Py_INCREF(Py_None);
1324
        return Py_None;
1325
    }
1326

1327
    mc = (cmsICCMeasurementConditions *)cmsReadTag(self->profile, info);
1328
    if (!mc) {
1329
        Py_INCREF(Py_None);
1330
        return Py_None;
1331
    }
1332

1333
    if (mc->Geometry == 1) {
1334
        geo = "45/0, 0/45";
1335
    } else if (mc->Geometry == 2) {
1336
        geo = "0d, d/0";
1337
    } else {
1338
        geo = "unknown";
1339
    }
1340

1341
    return Py_BuildValue(
1342
        "{s:i,s:(ddd),s:s,s:d,s:s}",
1343
        "observer",
1344
        mc->Observer,
1345
        "backing",
1346
        mc->Backing.X,
1347
        mc->Backing.Y,
1348
        mc->Backing.Z,
1349
        "geo",
1350
        geo,
1351
        "flare",
1352
        mc->Flare,
1353
        "illuminant_type",
1354
        _illu_map(mc->IlluminantType)
1355
    );
1356
}
1357

1358
static PyObject *
1359
cms_profile_getattr_icc_viewing_condition(CmsProfileObject *self, void *closure) {
1360
    cmsICCViewingConditions *vc;
1361
    cmsTagSignature info = cmsSigViewingConditionsTag;
1362

1363
    if (!cmsIsTag(self->profile, info)) {
1364
        Py_INCREF(Py_None);
1365
        return Py_None;
1366
    }
1367

1368
    vc = (cmsICCViewingConditions *)cmsReadTag(self->profile, info);
1369
    if (!vc) {
1370
        Py_INCREF(Py_None);
1371
        return Py_None;
1372
    }
1373

1374
    return Py_BuildValue(
1375
        "{s:(ddd),s:(ddd),s:s}",
1376
        "illuminant",
1377
        vc->IlluminantXYZ.X,
1378
        vc->IlluminantXYZ.Y,
1379
        vc->IlluminantXYZ.Z,
1380
        "surround",
1381
        vc->SurroundXYZ.X,
1382
        vc->SurroundXYZ.Y,
1383
        vc->SurroundXYZ.Z,
1384
        "illuminant_type",
1385
        _illu_map(vc->IlluminantType)
1386
    );
1387
}
1388

1389
static struct PyGetSetDef cms_profile_getsetters[] = {
1390
    /* New style interfaces.  */
1391
    {"rendering_intent", (getter)cms_profile_getattr_rendering_intent},
1392
    {"creation_date", (getter)cms_profile_getattr_creation_date},
1393
    {"copyright", (getter)cms_profile_getattr_copyright},
1394
    {"target", (getter)cms_profile_getattr_target},
1395
    {"manufacturer", (getter)cms_profile_getattr_manufacturer},
1396
    {"model", (getter)cms_profile_getattr_model},
1397
    {"profile_description", (getter)cms_profile_getattr_profile_description},
1398
    {"screening_description", (getter)cms_profile_getattr_screening_description},
1399
    {"viewing_condition", (getter)cms_profile_getattr_viewing_condition},
1400
    {"version", (getter)cms_profile_getattr_version},
1401
    {"icc_version", (getter)cms_profile_getattr_icc_version},
1402
    {"attributes", (getter)cms_profile_getattr_attributes},
1403
    {"header_flags", (getter)cms_profile_getattr_header_flags},
1404
    {"header_manufacturer", (getter)cms_profile_getattr_header_manufacturer},
1405
    {"header_model", (getter)cms_profile_getattr_header_model},
1406
    {"device_class", (getter)cms_profile_getattr_device_class},
1407
    {"connection_space", (getter)cms_profile_getattr_connection_space},
1408
    {"xcolor_space", (getter)cms_profile_getattr_xcolor_space},
1409
    {"profile_id", (getter)cms_profile_getattr_profile_id},
1410
    {"is_matrix_shaper", (getter)cms_profile_getattr_is_matrix_shaper},
1411
    {"technology", (getter)cms_profile_getattr_technology},
1412
    {"colorimetric_intent", (getter)cms_profile_getattr_colorimetric_intent},
1413
    {"perceptual_rendering_intent_gamut",
1414
     (getter)cms_profile_getattr_perceptual_rendering_intent_gamut},
1415
    {"saturation_rendering_intent_gamut",
1416
     (getter)cms_profile_getattr_saturation_rendering_intent_gamut},
1417
    {"red_colorant", (getter)cms_profile_getattr_red_colorant},
1418
    {"green_colorant", (getter)cms_profile_getattr_green_colorant},
1419
    {"blue_colorant", (getter)cms_profile_getattr_blue_colorant},
1420
    {"red_primary", (getter)cms_profile_getattr_red_primary},
1421
    {"green_primary", (getter)cms_profile_getattr_green_primary},
1422
    {"blue_primary", (getter)cms_profile_getattr_blue_primary},
1423
    {"media_white_point_temperature",
1424
     (getter)cms_profile_getattr_media_white_point_temperature},
1425
    {"media_white_point", (getter)cms_profile_getattr_media_white_point},
1426
    {"media_black_point", (getter)cms_profile_getattr_media_black_point},
1427
    {"luminance", (getter)cms_profile_getattr_luminance},
1428
    {"chromatic_adaptation", (getter)cms_profile_getattr_chromatic_adaptation},
1429
    {"chromaticity", (getter)cms_profile_getattr_chromaticity},
1430
    {"colorant_table", (getter)cms_profile_getattr_colorant_table},
1431
    {"colorant_table_out", (getter)cms_profile_getattr_colorant_table_out},
1432
    {"intent_supported", (getter)cms_profile_getattr_is_intent_supported},
1433
    {"clut", (getter)cms_profile_getattr_is_clut},
1434
    {"icc_measurement_condition", (getter)cms_profile_getattr_icc_measurement_condition
1435
    },
1436
    {"icc_viewing_condition", (getter)cms_profile_getattr_icc_viewing_condition},
1437

1438
    {NULL}
1439
};
1440

1441
static PyTypeObject CmsProfile_Type = {
1442
    PyVarObject_HEAD_INIT(NULL, 0) "PIL.ImageCms.core.CmsProfile", /*tp_name*/
1443
    sizeof(CmsProfileObject),                                      /*tp_basicsize*/
1444
    0,                                                             /*tp_itemsize*/
1445
    /* methods */
1446
    (destructor)cms_profile_dealloc, /*tp_dealloc*/
1447
    0,                               /*tp_vectorcall_offset*/
1448
    0,                               /*tp_getattr*/
1449
    0,                               /*tp_setattr*/
1450
    0,                               /*tp_as_async*/
1451
    0,                               /*tp_repr*/
1452
    0,                               /*tp_as_number*/
1453
    0,                               /*tp_as_sequence*/
1454
    0,                               /*tp_as_mapping*/
1455
    0,                               /*tp_hash*/
1456
    0,                               /*tp_call*/
1457
    0,                               /*tp_str*/
1458
    0,                               /*tp_getattro*/
1459
    0,                               /*tp_setattro*/
1460
    0,                               /*tp_as_buffer*/
1461
    Py_TPFLAGS_DEFAULT,              /*tp_flags*/
1462
    0,                               /*tp_doc*/
1463
    0,                               /*tp_traverse*/
1464
    0,                               /*tp_clear*/
1465
    0,                               /*tp_richcompare*/
1466
    0,                               /*tp_weaklistoffset*/
1467
    0,                               /*tp_iter*/
1468
    0,                               /*tp_iternext*/
1469
    cms_profile_methods,             /*tp_methods*/
1470
    0,                               /*tp_members*/
1471
    cms_profile_getsetters,          /*tp_getset*/
1472
};
1473

1474
static struct PyMethodDef cms_transform_methods[] = {
1475
    {"apply", (PyCFunction)cms_transform_apply, 1}, {NULL, NULL} /* sentinel */
1476
};
1477

1478
static PyTypeObject CmsTransform_Type = {
1479
    PyVarObject_HEAD_INIT(NULL, 0) "PIL.ImageCms.core.CmsTransform", /*tp_name*/
1480
    sizeof(CmsTransformObject),                                      /*tp_basicsize*/
1481
    0,                                                               /*tp_itemsize*/
1482
    /* methods */
1483
    (destructor)cms_transform_dealloc, /*tp_dealloc*/
1484
    0,                                 /*tp_vectorcall_offset*/
1485
    0,                                 /*tp_getattr*/
1486
    0,                                 /*tp_setattr*/
1487
    0,                                 /*tp_as_async*/
1488
    0,                                 /*tp_repr*/
1489
    0,                                 /*tp_as_number*/
1490
    0,                                 /*tp_as_sequence*/
1491
    0,                                 /*tp_as_mapping*/
1492
    0,                                 /*tp_hash*/
1493
    0,                                 /*tp_call*/
1494
    0,                                 /*tp_str*/
1495
    0,                                 /*tp_getattro*/
1496
    0,                                 /*tp_setattro*/
1497
    0,                                 /*tp_as_buffer*/
1498
    Py_TPFLAGS_DEFAULT,                /*tp_flags*/
1499
    0,                                 /*tp_doc*/
1500
    0,                                 /*tp_traverse*/
1501
    0,                                 /*tp_clear*/
1502
    0,                                 /*tp_richcompare*/
1503
    0,                                 /*tp_weaklistoffset*/
1504
    0,                                 /*tp_iter*/
1505
    0,                                 /*tp_iternext*/
1506
    cms_transform_methods,             /*tp_methods*/
1507
    0,                                 /*tp_members*/
1508
    0,                                 /*tp_getset*/
1509
};
1510

1511
static int
1512
setup_module(PyObject *m) {
1513
    PyObject *d;
1514
    PyObject *v;
1515
    int vn;
1516

1517
    /* Ready object types */
1518
    PyType_Ready(&CmsProfile_Type);
1519
    PyType_Ready(&CmsTransform_Type);
1520

1521
    Py_INCREF(&CmsProfile_Type);
1522
    PyModule_AddObject(m, "CmsProfile", (PyObject *)&CmsProfile_Type);
1523

1524
    Py_INCREF(&CmsTransform_Type);
1525
    PyModule_AddObject(m, "CmsTransform", (PyObject *)&CmsTransform_Type);
1526

1527
    d = PyModule_GetDict(m);
1528

1529
    /* this check is also in PIL.features.pilinfo() */
1530
#if LCMS_VERSION < 2070
1531
    vn = LCMS_VERSION;
1532
#else
1533
    vn = cmsGetEncodedCMMversion();
1534
#endif
1535
    if (vn % 10) {
1536
        v = PyUnicode_FromFormat("%d.%d.%d", vn / 1000, (vn / 10) % 100, vn % 10);
1537
    } else {
1538
        v = PyUnicode_FromFormat("%d.%d", vn / 1000, (vn / 10) % 100);
1539
    }
1540
    PyDict_SetItemString(d, "littlecms_version", v ? v : Py_None);
1541
    Py_XDECREF(v);
1542

1543
    return 0;
1544
}
1545

1546
PyMODINIT_FUNC
1547
PyInit__imagingcms(void) {
1548
    PyObject *m;
1549

1550
    static PyModuleDef module_def = {
1551
        PyModuleDef_HEAD_INIT,
1552
        "_imagingcms",    /* m_name */
1553
        NULL,             /* m_doc */
1554
        -1,               /* m_size */
1555
        pyCMSdll_methods, /* m_methods */
1556
    };
1557

1558
    m = PyModule_Create(&module_def);
1559

1560
    if (setup_module(m) < 0) {
1561
        return NULL;
1562
    }
1563

1564
    PyDateTime_IMPORT;
1565

1566
#ifdef Py_GIL_DISABLED
1567
    PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
1568
#endif
1569

1570
    return m;
1571
}
1572

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.