Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/base/gsicc_create.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* This is the code that is used to convert the various PDF and PS CIE
18
   based color spaces to ICC profiles.  This enables the use of an
19
   external CMS that is ICC centric to be used for ALL color management.
20
21
   The following spaces are handled:
22
23
   From PDF
24
25
   % Input Spaces
26
27
   CalRGB      -->  ICC 1-D LUTS and Matrix
28
   CalGray     -->  ICC 1-D LUT
29
   LAB         -->  ICC MLUT with a 2x2 sized table
30
31
   From PS
32
33
   %% Input Spaces
34
35
   CIEBasedABC  --> ICC 1-D LUTs and Matrix
36
   CIEBasedA    --> ICC 1-D LUT
37
   CIEBasedDEF  --> 3-D MLUT plus 1-D LUTs
38
   CIEBasedDEFG --> 4-D MLUT pluse 1-D LUTs
39
40
   %% Output Spaces
41
42
   Type1 CRD -->  ICC will have MLUT if render table present.
43
44
   A few notes:
45
46
   Required Tags for ALL profiles include:
47
48
       profileDescriptionTag
49
       copyrightTag
50
       mediaWhatePointTag
51
       chromaticAdaptationTag (V4 -  when measurement data is for other than D50)
52
53
   For color input profiles:
54
55
       Required if N-component LUT-based:
56
57
          AToB0Tag   (NOTE ONE WAY! BtoA0Tag is optional. Not true for
58
                          display profiles.)
59
60
       Required if 3 component matrix based:
61
62
           redMatrixColumnTag
63
           greenMatrixColumnTag
64
           blueMatrixColumnTag
65
           redTRCTag
66
           greenTRCTag
67
           blueTRCTag
68
69
       Notes:
70
71
       3-component can include AToB0Tag.
72
       Only CIEXYZ encoding can be used with matrix/TRC models.
73
       If CIELAB encoding is to be used, we must use LUT-based.
74
75
    For Monochrome input:
76
77
       Required:
78
           grayTRCTag
79
80
       Optional
81
           AToB0Tag
82
83
    For Color Display Profiles:
84
85
        Required if N-Component LUT-Based
86
87
            AtoB0Tag
88
            BToA0Tag   (Note inverse required here).
89
90
        Required if 3 component matrix based display profiles
91
92
            redMatrixColumnTag
93
            greenMatrixColumnTag
94
            blueMatrixColumnTag
95
            redTRCTag
96
            greenTRCTag
97
            blueTRCTag
98
99
        Optional
100
101
            AtoB0Tag
102
            BToA0Tag   (Note inverse required here).
103
104
    For Monochrome Display Profiles
105
106
        Required
107
108
            grayTRCTag
109
110
        Optional
111
112
            AtoB0Tag
113
            BtoA0Tag
114
115
Note: All profile data must be encoded as big-endian
116
117
   */
118
119
#include "icc34.h"   /* Note this header is needed even if lcms is not
120
                            compiled as default CMS */
121
#include "string_.h"
122
#include "gsmemory.h"
123
#include "gx.h"
124
#include <gp.h>
125
126
#include "gxgstate.h"
127
#include "gstypes.h"
128
#include "gscspace.h"
129
#include "gscie.h"
130
#include "gsicc_create.h"
131
#include "gxarith.h"
132
#include "gsicc_manage.h"
133
#include "gsicc_cache.h"
134
#include "math_.h"
135
#include "gscolor2.h"
136
#include "gxcie.h"
137
138
static void
139
add_xyzdata(unsigned char *input_ptr, icS15Fixed16Number temp_XYZ[]);
140
141
#define SAVEICCPROFILE 0
142
6.80k
#define HEADER_SIZE 128
143
6.80k
#define TAG_SIZE 12
144
10.8k
#define XYZPT_SIZE 12
145
34.6k
#define DATATYPE_SIZE 8
146
0
#define CURVE_SIZE 512
147
0
#define IDENT_CURVE_SIZE 0
148
9.07k
#define NUMBER_COMMON_TAGS 2
149
4.53k
#define icMultiUnicodeText 0x6d6c7563           /* 'mluc' v4 text type */
150
0
#define icMultiFunctionAtoBType 0x6d414220      /* 'mAB ' v4 lutAtoBtype type */
151
4.53k
#define D50_X 0.9642f
152
4.53k
#define D50_Y 1.0f
153
4.53k
#define D50_Z 0.8249f
154
0
#define DEFAULT_TABLE_NSIZE 9
155
0
#define FORWARD_V2_TABLE_SIZE 9
156
0
#define BACKWARD_V2_TABLE_SIZE 33
157
0
#define DEFAULT_TABLE_GRAYSIZE 128
158
0
#define V2_COMMON_TAGS NUMBER_COMMON_TAGS + 1
159
160
typedef unsigned short u1Fixed15Number;
161
#if SAVEICCPROFILE
162
unsigned int icc_debug_index = 0;
163
#endif
164
165
typedef struct cielab_s {
166
    float lstar;
167
    float astar;
168
    float bstar;
169
} cielab_t;
170
171
static const char desc_name[] = "Ghostscript Internal Profile";
172
static const char copy_right[] = "Copyright Artifex Software 2009-2023";
173
174
typedef struct {
175
    icTagSignature      sig;            /* The tag signature */
176
    icUInt32Number      offset;         /* Start of tag relative to
177
                                         * start of header, Spec
178
                                         * Clause 5 */
179
    icUInt32Number      size;           /* Size in bytes */
180
    unsigned char       byte_padding;
181
} gsicc_tag;
182
/* In generating 2x2x2 approximations as well as cases
183
   where we will need to squash components together we
184
   will go to float and then to 16 bit tables, hence the
185
   float pointer.  Otherwise we will keep the data
186
   in the existing byte form that it is in the CIEDEF(G)
187
   tables of postscript */
188
typedef struct {
189
    unsigned short *data_short;
190
    unsigned char *data_byte;  /* Used for cases where we can
191
                                   use the table as is */
192
    int     clut_dims[4];
193
    int     clut_num_input;
194
    int     clut_num_output;
195
    int     clut_num_entries;   /* Number of entries */
196
    int     clut_word_width;    /* Word width of table, 1 or 2 */
197
} gsicc_clut;
198
199
typedef struct {
200
    float   *a_curves;
201
    gsicc_clut *clut;
202
    float   *m_curves;
203
    gs_matrix3 *matrix;
204
    float   *b_curves;
205
    int num_in;
206
    int num_out;
207
    gs_vector3 *white_point;
208
    gs_vector3 *black_point;
209
    float *cam;
210
} gsicc_lutatob;
211
212
static int
213
get_padding(int x)
214
21.8k
{
215
21.8k
    return (4 -x%4)%4;
216
21.8k
}
217
218
/* For some weird reason I cant link to the one in gscie.c */
219
static void
220
gsicc_matrix_init(register gs_matrix3 * mat)
221
0
{
222
0
    mat->is_identity =
223
0
        mat->cu.u == 1.0 && is_fzero2(mat->cu.v, mat->cu.w) &&
224
0
        mat->cv.v == 1.0 && is_fzero2(mat->cv.u, mat->cv.w) &&
225
0
        mat->cw.w == 1.0 && is_fzero2(mat->cw.u, mat->cw.v);
226
0
}
227
228
static void
229
gsicc_make_diag_matrix(gs_matrix3 *matrix, gs_vector3 * vec)
230
0
{
231
0
    matrix->cu.u = vec->u;
232
0
    matrix->cv.v = vec->v;
233
0
    matrix->cw.w = vec->w;
234
0
    matrix->cu.v = 0;
235
0
    matrix->cu.w = 0;
236
0
    matrix->cw.u = 0;
237
0
    matrix->cw.v = 0;
238
0
    matrix->cv.u = 0;
239
0
    matrix->cv.w = 0;
240
0
    matrix->is_identity = (vec->u == 1.0)&&(vec->v == 1.0)&&(vec->w == 1.0);
241
0
}
242
243
/* This function maps a gs matrix type to an ICC CLUT. This is required due to the
244
   multiple matrix and 1-D LUT forms for postscript management, which the ICC does not
245
   support (at least the older versions).  clut is allocated externally */
246
static void
247
gsicc_matrix3_to_mlut(gs_matrix3 *mat, unsigned short *clut)
248
0
{
249
    /* Step through the grid values */
250
0
    float grid_points[8][3]={{0,0,0},
251
0
                             {0,0,1},
252
0
                             {0,1,0},
253
0
                             {0,1,1},
254
0
                             {1,0,0},
255
0
                             {1,0,1},
256
0
                             {1,1,0},
257
0
                             {1,1,1}};
258
0
    int k;
259
0
    gs_vector3 input,output;
260
0
    unsigned short *curr_ptr = clut, value;
261
0
    float valueflt;
262
263
0
    for (k = 0; k < 8; k++) {
264
0
        input.u = grid_points[k][0];
265
0
        input.v = grid_points[k][1];
266
0
        input.w = grid_points[k][2];
267
0
        cie_mult3(&input, mat, &output);
268
0
        valueflt = output.u;
269
0
        if (valueflt < 0) valueflt = 0;
270
0
        if (valueflt > 1) valueflt = 1;
271
0
        value = (unsigned short) (valueflt*65535.0);
272
0
        *curr_ptr ++= value;
273
0
        valueflt = output.v;
274
0
        if (valueflt < 0) valueflt = 0;
275
0
        if (valueflt > 1) valueflt = 1;
276
0
        value = (unsigned short) (valueflt*65535.0);
277
0
        *curr_ptr ++= value;
278
0
        valueflt = output.w;
279
0
        if (valueflt < 0) valueflt = 0;
280
0
        if (valueflt > 1) valueflt = 1;
281
0
        value = (unsigned short) (valueflt*65535.0);
282
0
        *curr_ptr ++= value;
283
0
    }
284
0
}
285
286
static void
287
apply_adaption(float matrix[], float in[], float out[])
288
8.58k
{
289
8.58k
    out[0] = matrix[0] * in[0] + matrix[1] * in[1] + matrix[2] * in[2];
290
8.58k
    out[1] = matrix[3] * in[0] + matrix[4] * in[1] + matrix[5] * in[2];
291
8.58k
    out[2] = matrix[6] * in[0] + matrix[7] * in[1] + matrix[8] * in[2];
292
8.58k
}
293
294
/* This function mashes all the elements together into a single CLUT
295
   for the ICC profile.  This is an approach of last resort, but
296
   guaranteed to work. */
297
static int
298
gsicc_create_clut(const gs_color_space *pcs, gsicc_clut *clut, gs_range *ranges,
299
                  gs_vector3 *white_point, bool range_adjust, float cam[],
300
                  gs_memory_t *memory)
301
0
{
302
0
    gs_gstate *pgs;
303
0
    int code;
304
0
    int num_points = clut->clut_num_entries;
305
0
    int table_size = clut->clut_dims[0]; /* Same resolution in each direction*/
306
0
    int num_components = clut->clut_num_input;
307
0
    int j,i,index;
308
0
    float *input_samples[4], *fltptr;
309
0
    gs_range *curr_range;
310
0
    unsigned short *ptr_short;
311
0
    gs_client_color cc;
312
0
    frac xyz[3];
313
0
    float xyz_float[3];
314
0
    float temp;
315
0
    gs_color_space_index cs_index;
316
317
    /* This completes the joint cache inefficiently so that
318
       we can sample through it and get our table entries */
319
0
    code = gx_cie_to_xyz_alloc(&pgs, pcs, memory);
320
0
    if (code < 0)
321
0
        return gs_rethrow(code, "Allocation of cie to xyz transform failed");
322
0
    cs_index = gs_color_space_get_index(pcs);
323
324
    /* Create the sample indices across the input ranges
325
       for each color component.  When the concretization/remap occurs
326
       to be fed into this icc profile, we may will need to apply a linear
327
       map to the input if the range is something other than 0 to 1 */
328
0
    for (i = 0; i < num_components; i++) {
329
0
        input_samples[i] = (float*) gs_alloc_bytes(memory,
330
0
                                sizeof(float)*table_size,"gsicc_create_clut");
331
0
        if (input_samples[i] == NULL) {
332
0
            for (j = 0; j < i; j++) {
333
0
                gs_free_object(memory, input_samples[j], "gsicc_create_clut");
334
0
            }
335
0
            return gs_throw(gs_error_VMerror, "Allocation of input_sample arrays failed");
336
0
        }
337
0
        fltptr = input_samples[i];
338
0
        curr_range = &(ranges[i]);
339
0
        for (j = 0; j < table_size; j++ ) {
340
0
            *fltptr ++= ((float) j/ (float) (table_size-1)) *
341
0
                (curr_range->rmax - curr_range->rmin) + curr_range->rmin;
342
0
        }
343
0
    }
344
    /* Go through all the entries.
345
       Uniformly from min range to max range */
346
0
    ptr_short = clut->data_short;
347
0
    for (i = 0; i < num_points; i++) {
348
0
        switch (num_components) {
349
0
        case 1:
350
            /* Get the input vector value */
351
0
            fltptr = input_samples[0];
352
0
            index = i%table_size;
353
0
            cc.paint.values[0] = fltptr[index];
354
0
            break;
355
0
        case 3:
356
            /* The first channel varies least rapidly in the ICC table */
357
0
            fltptr = input_samples[2];
358
0
            index = i%table_size;
359
0
            cc.paint.values[2] = fltptr[index];
360
0
            fltptr = input_samples[1];
361
0
            index = (unsigned int) floor((float) i/(float) table_size)%table_size;
362
0
            cc.paint.values[1] = fltptr[index];
363
0
            fltptr = input_samples[0];
364
0
            index = (unsigned int) floor((float) i/(float) (table_size*
365
0
                                                        table_size))%table_size;
366
0
            cc.paint.values[0] = fltptr[index];
367
0
            break;
368
0
        case 4:
369
            /* The first channel varies least rapidly in the ICC table */
370
0
            fltptr = input_samples[3];
371
0
            index = i%table_size;
372
0
            cc.paint.values[3] = fltptr[index];
373
0
            fltptr = input_samples[2];
374
0
            index = (unsigned int) floor((float) i/(float) table_size)%table_size;
375
0
            cc.paint.values[2] = fltptr[index];
376
0
            fltptr = input_samples[1];
377
0
            index = (unsigned int) floor((float) i/(float) (table_size*
378
0
                                                        table_size))%table_size;
379
0
            cc.paint.values[1] = fltptr[index];
380
0
            fltptr = input_samples[0];
381
0
            index = (unsigned int) floor((float) i/(float) (table_size*
382
0
                                        table_size*table_size))%table_size;
383
0
            cc.paint.values[0] = fltptr[index];
384
0
            break;
385
0
        default:
386
0
            return_error(gs_error_rangecheck); /* Should never happen */
387
0
        }
388
        /* These special concretizations functions do not go through
389
           the ICC mapping like the procs associated with the color space */
390
0
        switch (cs_index) {
391
0
            case gs_color_space_index_CIEA:
392
0
                gx_psconcretize_CIEA(&cc, pcs, xyz, xyz_float, pgs);
393
                /* AR forces this case to always be achromatic.  We will
394
                   do the same even though it does not match the PS
395
                   specification */
396
                /* Use the resulting Y value to scale the D50 Illumination.
397
                   note that we scale to the whitepoint here.  Matrix out
398
                   handles mapping to CIE D50 */
399
0
                xyz_float[0] = white_point->u * xyz_float[1];
400
0
                xyz_float[2] = white_point->w * xyz_float[1];
401
0
                break;
402
0
            case gs_color_space_index_CIEABC:
403
0
                gx_psconcretize_CIEABC(&cc, pcs, xyz, xyz_float, pgs);
404
0
                break;
405
0
            case gs_color_space_index_CIEDEF:
406
0
                gx_psconcretize_CIEDEF(&cc, pcs, xyz, xyz_float, pgs);
407
0
                break;
408
0
            case gs_color_space_index_CIEDEFG:
409
0
               gx_psconcretize_CIEDEFG(&cc, pcs, xyz, xyz_float, pgs);
410
0
               break;
411
0
            default:
412
0
                return gs_throw(-1, "Invalid gs_color_space_index when creating ICC profile");
413
0
        }
414
        /* We need to map these values to D50 illuminant so that things work
415
           correctly with ICC profile */
416
        /* apply_adaption(cam, xyz_float, xyz_adapt); */
417
418
        /* Correct for range of ICC CIEXYZ table data */
419
0
        for (j = 0; j < 3; j++) {
420
0
            temp = xyz_float[j]/(1 + 32767.0/32768);
421
0
            if (temp < 0) temp = 0;
422
0
            if (temp > 1) temp = 1;
423
0
           *ptr_short ++= (unsigned int)(temp * 65535);
424
0
        }
425
0
    }
426
0
    gx_cie_to_xyz_free(pgs); /* Free the joint cache we created */
427
0
    for (i = 0; i < num_components; i++) {
428
0
        gs_free_object(memory, input_samples[i], "gsicc_create_clut");
429
0
    }
430
0
    return 0;
431
0
}
432
433
/* This function maps a gs vector type to an ICC CLUT.
434
   This is used in the CIEA type.  clut is allocated
435
   externally. We may need to replace this with a range value.
436
   For now we are mapping to an output between 0 and the vector */
437
static void
438
gsicc_vec_to_mlut(gs_vector3 *vec, unsigned short *clut)
439
0
{
440
0
    unsigned short *curr_ptr = clut;
441
0
    int temp;
442
443
0
    *curr_ptr ++= 0;
444
0
    *curr_ptr ++= 0;
445
0
    *curr_ptr ++= 0;
446
0
    temp = (int)(vec->u * 65535);
447
0
    if (temp > 65535) temp = 65535;
448
0
    if (temp < 0) temp = 0;
449
0
    *curr_ptr ++= temp;
450
0
    temp = (int)(vec->v * 65535);
451
0
    if (temp > 65535) temp = 65535;
452
0
    if (temp < 0) temp = 0;
453
0
    *curr_ptr ++= temp;
454
0
    temp = (int)(vec->w * 65535);
455
0
    if (temp > 65535) temp = 65535;
456
0
    if (temp < 0) temp = 0;
457
0
    *curr_ptr ++= temp;
458
0
}
459
460
#if SAVEICCPROFILE
461
/* Debug dump of internally created ICC profile for testing */
462
static void
463
save_profile(const gs_memory_t *mem, unsigned char *buffer, char filename[], int buffer_size)
464
{
465
    char full_file_name[50];
466
    gp_file *fid;
467
468
    gs_snprintf(full_file_name,sizeof(full_file_name),"%d)Profile_%s.icc",icc_debug_index,filename);
469
    fid = gp_fopen(mem, full_file_name,"wb");
470
    gp_fwrite(buffer,sizeof(unsigned char),buffer_size,fid);
471
    gp_fclose(fid);
472
    icc_debug_index++;
473
}
474
#endif
475
476
static void
477
write_bigendian_4bytes(unsigned char *curr_ptr,ulong input)
478
169k
{
479
169k
   *curr_ptr++ = (0xff & (input >> 24));
480
169k
   *curr_ptr++ = (0xff & (input >> 16));
481
169k
   *curr_ptr++ = (0xff & (input >> 8));
482
169k
   *curr_ptr++ = (0xff & input);
483
169k
}
484
485
static void
486
write_bigendian_2bytes(unsigned char *curr_ptr,ushort input)
487
15.5k
{
488
15.5k
   *curr_ptr++ = (0xff & (input >> 8));
489
15.5k
   *curr_ptr++ = (0xff & input);
490
15.5k
}
491
492
static void
493
setdatetime(icDateTimeNumber *datetime)
494
2.28k
{
495
2.28k
    datetime->day = 0;
496
2.28k
    datetime->hours = 0;
497
2.28k
    datetime->minutes = 0;
498
2.28k
    datetime->month = 0;
499
2.28k
    datetime->seconds = 0;
500
2.28k
    datetime->year = 0;
501
2.28k
}
502
503
static icS15Fixed16Number
504
double2XYZtype(float number_in)
505
39.3k
{
506
39.3k
    short s;
507
39.3k
    unsigned short m;
508
509
39.3k
    if (number_in < 0) {
510
2.84k
        number_in = 0;
511
#ifdef DEBUG
512
        gs_warn("Negative CIEXYZ in created ICC Profile");
513
#endif
514
2.84k
    }
515
516
39.3k
    s = (short) number_in;
517
39.3k
    m = (unsigned short) ((number_in - s) * 65536.0);
518
39.3k
    return (icS15Fixed16Number) ((s << 16) | m);
519
39.3k
}
520
521
static icS15Fixed16Number
522
double2icS15Fixed16Number(float number_in)
523
0
{
524
0
    short s;
525
0
    unsigned short m;
526
0
    icS15Fixed16Number temp;
527
0
    float number;
528
529
0
    if (number_in < 0) {
530
0
        number = -number_in;
531
0
        s = (short) number;
532
0
        m = (unsigned short) ((number - s) * 65536.0);
533
0
        temp = (icS15Fixed16Number) ((s << 16) | m);
534
0
        temp = -temp;
535
0
        return temp;
536
0
    } else {
537
0
        s = (short) number_in;
538
0
        m = (unsigned short) ((number_in - s) * 65536.0);
539
0
        return (icS15Fixed16Number) ((s << 16) | m);
540
0
    }
541
0
}
542
543
static unsigned short
544
float2u8Fixed8(float number_in)
545
6.47k
{
546
6.47k
    return (unsigned short) (number_in * 256);
547
6.47k
}
548
549
static
550
void init_common_tags(gsicc_tag tag_list[],int num_tags, int *last_tag)
551
2.26k
{
552
 /*    profileDescriptionTag
553
       copyrightTag  */
554
555
2.26k
    int curr_tag, temp_size;
556
557
2.26k
    if (*last_tag < 0)
558
2.26k
        curr_tag = 0;
559
0
    else
560
0
        curr_tag = (*last_tag)+1;
561
562
2.26k
    tag_list[curr_tag].offset = HEADER_SIZE+num_tags*TAG_SIZE + 4;
563
2.26k
    tag_list[curr_tag].sig = icSigProfileDescriptionTag;
564
    /* temp_size = DATATYPE_SIZE + 4 + strlen(desc_name) + 1 + 4 + 4 + 3 + 67; */
565
2.26k
    temp_size = 2*strlen(desc_name) + 28;
566
    /* +1 for NULL + 4 + 4 for unicode + 3 + 67 script code */
567
2.26k
    tag_list[curr_tag].byte_padding = get_padding(temp_size);
568
2.26k
    tag_list[curr_tag].size = temp_size + tag_list[curr_tag].byte_padding;
569
570
2.26k
    curr_tag++;
571
572
2.26k
    tag_list[curr_tag].offset = tag_list[curr_tag-1].offset +
573
2.26k
                                                    tag_list[curr_tag-1].size;
574
2.26k
    tag_list[curr_tag].sig = icSigCopyrightTag;
575
    /* temp_size = DATATYPE_SIZE + strlen(copy_right) + 1; */
576
2.26k
    temp_size = 2*strlen(copy_right) + 28;
577
2.26k
    tag_list[curr_tag].byte_padding = get_padding(temp_size);
578
2.26k
    tag_list[curr_tag].size = temp_size + tag_list[curr_tag].byte_padding;
579
2.26k
    *last_tag = curr_tag;
580
2.26k
}
581
582
/* Code to write out v4 text type which is a table of unicode text
583
   for different regions */
584
static void
585
add_v4_text_tag(unsigned char *buffer,const char text[], gsicc_tag tag_list[],
586
                int curr_tag)
587
4.53k
{
588
4.53k
    unsigned char *curr_ptr;
589
4.53k
    int k;
590
591
4.53k
    curr_ptr = buffer;
592
4.53k
    write_bigendian_4bytes(curr_ptr,icMultiUnicodeText);
593
4.53k
    curr_ptr += 4;
594
4.53k
    memset(curr_ptr,0,4);
595
4.53k
    curr_ptr += 4;
596
4.53k
    write_bigendian_4bytes(curr_ptr,1); /* Number of names */
597
4.53k
    curr_ptr += 4;
598
4.53k
    write_bigendian_4bytes(curr_ptr,12); /* Record size */
599
4.53k
    curr_ptr += 4;
600
4.53k
    write_bigendian_2bytes(curr_ptr,0x656e); /* ISO 639-1, en */
601
4.53k
    curr_ptr += 2;
602
4.53k
    write_bigendian_2bytes(curr_ptr,0x5553); /* ISO 3166, US */
603
4.53k
    curr_ptr += 2;
604
4.53k
    write_bigendian_4bytes(curr_ptr,2*strlen(text)); /* String length */
605
4.53k
    curr_ptr += 4;
606
4.53k
    write_bigendian_4bytes(curr_ptr,28); /* Offset to string */
607
4.53k
    curr_ptr += 4;
608
    /* String written as UTF-16BE. No NULL */
609
149k
    for (k = 0; k < strlen(text); k++) {
610
145k
        *curr_ptr ++= 0;
611
145k
        *curr_ptr ++= text[k];
612
145k
    }
613
4.53k
    memset(curr_ptr,0,tag_list[curr_tag].byte_padding);  /* padding */
614
4.53k
}
615
616
static void
617
add_desc_tag(unsigned char *buffer, const char text[], gsicc_tag tag_list[],
618
                int curr_tag)
619
0
{
620
0
    unsigned char *curr_ptr;
621
0
    int len = strlen(text) + 1;
622
0
    int k;
623
624
0
    curr_ptr = buffer;
625
0
    write_bigendian_4bytes(curr_ptr, icSigTextDescriptionType);
626
0
    curr_ptr += 4;
627
0
    memset(curr_ptr, 0, 4);
628
0
    curr_ptr += 4;
629
0
    write_bigendian_4bytes(curr_ptr, len);
630
0
    curr_ptr += 4;
631
0
    for (k = 0; k < strlen(text); k++) {
632
0
        *curr_ptr++ = text[k];
633
0
    }
634
0
    memset(curr_ptr, 0, 12 + 67 + 1);
635
0
    memset(curr_ptr, 0, tag_list[curr_tag].byte_padding);  /* padding */
636
0
}
637
638
static void
639
add_text_tag(unsigned char *buffer, const char text[], gsicc_tag tag_list[],
640
            int curr_tag)
641
0
{
642
0
    unsigned char *curr_ptr;
643
0
    int k;
644
645
0
    curr_ptr = buffer;
646
0
    write_bigendian_4bytes(curr_ptr, icSigTextType);
647
0
    curr_ptr += 4;
648
0
    memset(curr_ptr, 0, 4);
649
0
    curr_ptr += 4;
650
0
    for (k = 0; k < strlen(text); k++) {
651
0
        *curr_ptr++ = text[k];
652
0
    }
653
0
    memset(curr_ptr, 0, 1);
654
0
    memset(curr_ptr, 0, tag_list[curr_tag].byte_padding);  /* padding */
655
0
}
656
657
static void
658
add_common_tag_data(unsigned char *buffer,gsicc_tag tag_list[], int vers)
659
2.26k
{
660
2.26k
    unsigned char *curr_ptr;
661
2.26k
    curr_ptr = buffer;
662
663
2.26k
    if (vers == 4) {
664
2.26k
        add_v4_text_tag(curr_ptr, desc_name, tag_list, 0);
665
2.26k
        curr_ptr += tag_list[0].size;
666
2.26k
        add_v4_text_tag(curr_ptr, copy_right, tag_list, 1);
667
2.26k
    } else {
668
0
        add_desc_tag(curr_ptr, desc_name, tag_list, 0);
669
0
        curr_ptr += tag_list[0].size;
670
0
        add_text_tag(curr_ptr, copy_right, tag_list, 1);
671
0
    }
672
2.26k
}
673
674
static
675
void  init_tag(gsicc_tag tag_list[], int *last_tag, icTagSignature tagsig,
676
               int datasize)
677
17.3k
{
678
    /* This should never be called first. Common tags should be taken care of */
679
680
17.3k
    int curr_tag = (*last_tag)+1;
681
682
17.3k
    tag_list[curr_tag].offset = tag_list[curr_tag-1].offset +
683
17.3k
                                                    tag_list[curr_tag-1].size;
684
17.3k
    tag_list[curr_tag].sig = tagsig;
685
17.3k
    tag_list[curr_tag].byte_padding = get_padding(DATATYPE_SIZE + datasize);
686
17.3k
    tag_list[curr_tag].size = DATATYPE_SIZE + datasize +
687
17.3k
                                            tag_list[curr_tag].byte_padding;
688
17.3k
    *last_tag = curr_tag;
689
17.3k
}
690
691
static void
692
setheader_common(icHeader *header, int vers)
693
2.28k
{
694
    /* This needs to all be predefined for a simple copy. MJV todo */
695
2.28k
    header->cmmId = 0;
696
2.28k
    if (vers == 4)
697
2.26k
        header->version = 0x04200000;
698
13
    else
699
13
        header->version = 0x02200000;
700
2.28k
    setdatetime(&(header->date));
701
2.28k
    header->magic = icMagicNumber;
702
2.28k
    header->platform = icSigMacintosh;
703
2.28k
    header->flags = 0;
704
2.28k
    header->manufacturer = 0;
705
2.28k
    header->model = 0;
706
2.28k
    header->attributes[0] = 0;
707
2.28k
    header->attributes[1] = 0;
708
2.28k
    header->renderingIntent = 3;
709
2.28k
    header->illuminant.X = double2XYZtype((float) 0.9642);
710
2.28k
    header->illuminant.Y = double2XYZtype((float) 1.0);
711
2.28k
    header->illuminant.Z = double2XYZtype((float) 0.8249);
712
2.28k
    header->creator = 0;
713
    /* Version 4 includes a profile id, field which is an md5 sum */
714
2.28k
    memset(header->reserved,0,44);
715
2.28k
}
716
717
static void
718
copy_header(unsigned char *buffer,icHeader *header)
719
2.26k
{
720
2.26k
    unsigned char *curr_ptr;
721
722
2.26k
    curr_ptr = buffer;
723
2.26k
    write_bigendian_4bytes(curr_ptr,header->size);
724
2.26k
    curr_ptr += 4;
725
2.26k
    memset(curr_ptr,0,4);
726
2.26k
    curr_ptr += 4;
727
2.26k
    write_bigendian_4bytes(curr_ptr,header->version);
728
2.26k
    curr_ptr += 4;
729
2.26k
    write_bigendian_4bytes(curr_ptr,header->deviceClass);
730
2.26k
    curr_ptr += 4;
731
2.26k
    write_bigendian_4bytes(curr_ptr,header->colorSpace);
732
2.26k
    curr_ptr += 4;
733
2.26k
    write_bigendian_4bytes(curr_ptr,header->pcs);
734
2.26k
    curr_ptr += 4;
735
736
    /* Date and time */
737
2.26k
    memset(curr_ptr,0,12);
738
2.26k
    curr_ptr += 12;
739
2.26k
    write_bigendian_4bytes(curr_ptr,header->magic);
740
2.26k
    curr_ptr += 4;
741
2.26k
    write_bigendian_4bytes(curr_ptr,header->platform);
742
2.26k
    curr_ptr += 4;
743
2.26k
    memset(curr_ptr,0,24);
744
2.26k
    curr_ptr += 24;
745
2.26k
    write_bigendian_4bytes(curr_ptr,header->illuminant.X);
746
2.26k
    curr_ptr += 4;
747
2.26k
    write_bigendian_4bytes(curr_ptr,header->illuminant.Y);
748
2.26k
    curr_ptr += 4;
749
2.26k
    write_bigendian_4bytes(curr_ptr,header->illuminant.Z);
750
2.26k
    curr_ptr += 4;
751
2.26k
    memset(curr_ptr,0,48);
752
2.26k
}
753
754
static void
755
copy_tagtable(unsigned char *buffer,gsicc_tag *tag_list, ulong num_tags)
756
2.26k
{
757
2.26k
    unsigned int k;
758
2.26k
    unsigned char *curr_ptr;
759
760
2.26k
    curr_ptr = buffer;
761
2.26k
    write_bigendian_4bytes(curr_ptr,num_tags);
762
2.26k
    curr_ptr += 4;
763
24.1k
    for (k = 0; k < num_tags; k++) {
764
21.8k
        write_bigendian_4bytes(curr_ptr,tag_list[k].sig);
765
21.8k
        curr_ptr += 4;
766
21.8k
        write_bigendian_4bytes(curr_ptr,tag_list[k].offset);
767
21.8k
        curr_ptr += 4;
768
21.8k
        write_bigendian_4bytes(curr_ptr,tag_list[k].size);
769
21.8k
        curr_ptr += 4;
770
21.8k
    }
771
2.26k
}
772
773
static void
774
get_D50(icS15Fixed16Number XYZ[])
775
2.26k
{
776
2.26k
    XYZ[0] = double2XYZtype(D50_X);
777
2.26k
    XYZ[1] = double2XYZtype(D50_Y);
778
2.26k
    XYZ[2] = double2XYZtype(D50_Z);
779
2.26k
}
780
781
static void
782
get_XYZ(icS15Fixed16Number XYZ[], gs_vector3 *vector)
783
0
{
784
0
    XYZ[0] = double2XYZtype(vector->u);
785
0
    XYZ[1] = double2XYZtype(vector->v);
786
0
    XYZ[2] = double2XYZtype(vector->w);
787
0
}
788
789
static void
790
get_XYZ_doubletr(icS15Fixed16Number XYZ[], float *vector)
791
8.58k
{
792
8.58k
    XYZ[0] = double2XYZtype(vector[0]);
793
8.58k
    XYZ[1] = double2XYZtype(vector[1]);
794
8.58k
    XYZ[2] = double2XYZtype(vector[2]);
795
8.58k
}
796
797
static void
798
scale_matrix(float *matrix_input,float scale_factor)
799
0
{
800
0
    int k;
801
802
0
    for (k = 0; k < 9; k++) {
803
0
        matrix_input[k] = matrix_input[k]/2.0;
804
0
    }
805
0
}
806
807
static void
808
add_gammadata(unsigned char *input_ptr, unsigned short gamma,
809
              icTagTypeSignature curveType)
810
6.47k
{
811
6.47k
    unsigned char *curr_ptr;
812
813
6.47k
    curr_ptr = input_ptr;
814
6.47k
    write_bigendian_4bytes(curr_ptr,curveType);
815
6.47k
    curr_ptr += 4;
816
6.47k
    memset(curr_ptr,0,4);
817
6.47k
    curr_ptr += 4;
818
819
    /* one entry for gamma */
820
6.47k
    write_bigendian_4bytes(curr_ptr, 1);
821
6.47k
    curr_ptr += 4;
822
823
    /* The encode (8frac8) gamma, with padding */
824
6.47k
    write_bigendian_2bytes(curr_ptr, gamma);
825
6.47k
    curr_ptr += 2;
826
827
    /* pad two bytes */
828
6.47k
    memset(curr_ptr,0,2);
829
6.47k
}
830
831
static void
832
add_xyzdata(unsigned char *input_ptr, icS15Fixed16Number temp_XYZ[])
833
10.8k
{
834
10.8k
    int j;
835
10.8k
    unsigned char *curr_ptr;
836
837
10.8k
    curr_ptr = input_ptr;
838
10.8k
    write_bigendian_4bytes(curr_ptr,icSigXYZType);
839
10.8k
    curr_ptr += 4;
840
10.8k
    memset(curr_ptr,0,4);
841
10.8k
    curr_ptr += 4;
842
43.4k
    for (j = 0; j < 3; j++) {
843
32.5k
        write_bigendian_4bytes(curr_ptr, temp_XYZ[j]);
844
32.5k
        curr_ptr += 4;
845
32.5k
    }
846
10.8k
}
847
848
/* If abc matrix is identity the abc and lmn curves can be mashed together  */
849
static void
850
merge_abc_lmn_curves(gx_cie_vector_cache *DecodeABC_caches,
851
                     gx_cie_scalar_cache *DecodeLMN)
852
0
{
853
854
0
}
855
856
static void
857
add_matrixwithbias(unsigned char *input_ptr, float *float_ptr_in, bool has_bias)
858
0
{
859
0
    unsigned char *curr_ptr;
860
0
    float *float_ptr = float_ptr_in;
861
0
    int k;
862
863
    /* GS Matrix is coming in with data arranged in row ordered form */
864
0
    curr_ptr = input_ptr;
865
0
    for (k = 0; k < 9; k++ ){
866
0
        write_bigendian_4bytes(curr_ptr, double2icS15Fixed16Number(*float_ptr));
867
0
        curr_ptr += 4;
868
0
        float_ptr++;
869
0
    }
870
0
    if (has_bias){
871
0
        memset(curr_ptr,0,4*3);
872
0
    }
873
0
}
874
875
static void
876
matrixmult(float leftmatrix[], int nlrow, int nlcol,
877
           float rightmatrix[], int nrrow, int nrcol, float result[])
878
9.07k
{
879
9.07k
    float *curr_row;
880
9.07k
    int k,l,j,ncols,nrows;
881
9.07k
    float sum;
882
883
9.07k
    nrows = nlrow;
884
9.07k
    ncols = nrcol;
885
9.07k
    if (nlcol == nrrow) {
886
36.2k
        for (k = 0; k < nrows; k++) {
887
27.2k
            curr_row = &(leftmatrix[k*nlcol]);
888
81.6k
            for (l = 0; l < ncols; l++) {
889
54.4k
                sum = 0.0;
890
217k
                for (j = 0; j < nlcol; j++) {
891
163k
                    sum = sum + curr_row[j] * rightmatrix[j*nrcol+l];
892
163k
                }
893
54.4k
                result[k*ncols+l] = sum;
894
54.4k
            }
895
27.2k
        }
896
9.07k
    }
897
9.07k
}
898
899
static void
900
gsicc_create_copy_matrix3(float *src, float *des)
901
0
{
902
0
    memcpy(des,src,9*sizeof(float));
903
0
}
904
905
static void
906
gsicc_create_compute_cam( gs_vector3 *white_src, gs_vector3 *white_des,
907
                                float *cam)
908
2.26k
{
909
2.26k
    float cat02matrix[] = {0.7328f, 0.4296f, -0.1624f,
910
2.26k
                            -0.7036f, 1.6975f, 0.0061f,
911
2.26k
                             0.003f, 0.0136f, 0.9834f};
912
2.26k
    float cat02matrixinv[] = {1.0961f, -0.2789f, 0.1827f,
913
2.26k
                              0.4544f, 0.4735f, 0.0721f,
914
2.26k
                             -0.0096f, -0.0057f, 1.0153f};
915
2.26k
    float vonkries_diag[9];
916
2.26k
    float temp_matrix[9];
917
2.26k
    float lms_wp_src[3], lms_wp_des[3];
918
2.26k
    int k;
919
920
2.26k
    matrixmult(cat02matrix,3,3,&(white_src->u),3,1,&(lms_wp_src[0]));
921
2.26k
    matrixmult(cat02matrix,3,3,&(white_des->u),3,1,&(lms_wp_des[0]));
922
2.26k
    memset(&(vonkries_diag[0]),0,sizeof(float)*9);
923
924
9.07k
    for (k = 0; k < 3; k++) {
925
6.80k
        if (lms_wp_src[k] > 0 ) {
926
6.80k
            vonkries_diag[k*3+k] = lms_wp_des[k]/lms_wp_src[k];
927
6.80k
        } else {
928
2
            vonkries_diag[k*3+k] = 1;
929
2
        }
930
6.80k
    }
931
2.26k
    matrixmult(&(vonkries_diag[0]), 3, 3, &(cat02matrix[0]), 3, 3,
932
2.26k
                &(temp_matrix[0]));
933
2.26k
    matrixmult(&(cat02matrixinv[0]), 3, 3, &(temp_matrix[0]), 3, 3, &(cam[0]));
934
2.26k
}
935
936
static int
937
gsicc_compute_cam(gsicc_lutatob *icc_luta2bparts, gs_memory_t *memory)
938
0
{
939
0
    gs_vector3 d50;
940
941
0
    d50.u = D50_X;
942
0
    d50.v = D50_Y;
943
0
    d50.w = D50_Z;
944
945
    /* Calculate the chromatic adaptation matrix */
946
0
    icc_luta2bparts->cam = (float*) gs_alloc_bytes(memory,
947
0
                                        9 * sizeof(float), "gsicc_compute_cam");
948
0
    if (icc_luta2bparts->cam == NULL) {
949
0
        return gs_throw(gs_error_VMerror, "Allocation of ICC cam failed");
950
0
    }
951
0
    gsicc_create_compute_cam(icc_luta2bparts->white_point, &(d50), icc_luta2bparts->cam);
952
0
    return 0;
953
0
}
954
955
/* Compute the CAT02 transformation to get us from the Cal White
956
   point to the D50 white point.  We could pack this in a chad tag
957
   and let the CMM worry about applying but it is safer if we just
958
   take care of it ourselves by mapping the primaries.  This is what is
959
   also done for the table based data */
960
static float*
961
gsicc_get_cat02_cam(float *curr_wp, gs_memory_t *memory)
962
2.26k
{
963
2.26k
    gs_vector3 d50;
964
2.26k
    gs_vector3 wp;
965
2.26k
    float *cam;
966
967
2.26k
    wp.u = curr_wp[0];
968
2.26k
    wp.v = curr_wp[1];
969
2.26k
    wp.w = curr_wp[2];
970
971
2.26k
    d50.u = D50_X;
972
2.26k
    d50.v = D50_Y;
973
2.26k
    d50.w = D50_Z;
974
975
2.26k
    cam = (float*)gs_alloc_bytes(memory, 9 * sizeof(float), "gsicc_get_cat02_cam");
976
2.26k
    if (cam == NULL) {
977
0
        gs_throw(gs_error_VMerror, "Allocation of cat02 matrix failed");
978
0
        return NULL;
979
0
    }
980
2.26k
    gsicc_create_compute_cam(&wp, &(d50), cam);
981
982
2.26k
    return cam;
983
2.26k
}
984
985
static void
986
add_ident_curves(unsigned char *input_ptr,int number_of_curves)
987
0
{
988
0
    unsigned char *curr_ptr;
989
0
    int k;
990
991
0
    curr_ptr = input_ptr;
992
0
    for (k = 0; k < number_of_curves; k++) {
993
       /* Signature */
994
0
        write_bigendian_4bytes(curr_ptr,icSigCurveType);
995
0
        curr_ptr += 4;
996
        /* Reserved */
997
0
        memset(curr_ptr,0,4);
998
0
        curr_ptr += 4;
999
        /* Count */
1000
0
        write_bigendian_4bytes(curr_ptr, 0);
1001
0
        curr_ptr += 4;
1002
0
    }
1003
0
}
1004
1005
static void
1006
add_clutAtoB(unsigned char *input_ptr, gsicc_clut *clut)
1007
0
{
1008
0
    unsigned char *curr_ptr = input_ptr;
1009
0
    int k;
1010
0
    int num_channels_in = clut->clut_num_input;
1011
0
    int number_samples = clut->clut_num_entries;
1012
1013
    /* First write out the dimensions for each channel */
1014
0
    for (k = 0; k < num_channels_in; k++) {
1015
0
        memset(curr_ptr, clut->clut_dims[k], 1);
1016
0
        curr_ptr++;
1017
0
    }
1018
    /* Set the remainder of the dimenensions */
1019
0
    memset(curr_ptr, 0, 16-num_channels_in);
1020
0
    curr_ptr += (16-num_channels_in);
1021
    /* word size */
1022
0
    memset(curr_ptr, clut->clut_word_width, 1);
1023
0
    curr_ptr++;
1024
    /* padding */
1025
0
    memset(curr_ptr, 0, 3);
1026
0
    curr_ptr += 3;
1027
0
    if (clut->data_byte != NULL) {
1028
        /* A byte table */
1029
0
        memcpy(curr_ptr,clut->data_byte,number_samples*3);
1030
0
    } else {
1031
        /* A float table */
1032
0
        for ( k = 0; k < number_samples*3; k++ ) {
1033
0
            write_bigendian_2bytes(curr_ptr,clut->data_short[k]);
1034
0
            curr_ptr += 2;
1035
0
        }
1036
0
    }
1037
0
}
1038
1039
static void
1040
add_curve(unsigned char *input_ptr, float *curve_data, int num_samples)
1041
0
{
1042
0
    unsigned char *curr_ptr;
1043
0
    unsigned short value;
1044
0
    int k;
1045
1046
   /* Signature */
1047
0
    curr_ptr = input_ptr;
1048
0
    write_bigendian_4bytes(curr_ptr,icSigCurveType);
1049
0
    curr_ptr += 4;
1050
    /* Reserved */
1051
0
    memset(curr_ptr,0,4);
1052
0
    curr_ptr += 4;
1053
    /* Count */
1054
0
    write_bigendian_4bytes(curr_ptr, num_samples);
1055
0
    curr_ptr += 4;
1056
    /* Now the data uInt16 Number 0 to 65535.  For now assume input is 0 to 1.
1057
            Need to fix this.  MJV */
1058
0
    for (k = 0; k < num_samples; k++) {
1059
0
        if (curve_data[k] < 0) curve_data[k] = 0;
1060
0
        if (curve_data[k] > 1) curve_data[k] = 1;
1061
0
        value = (unsigned int) (curve_data[k]*65535.0);
1062
0
        write_bigendian_2bytes(curr_ptr,value);
1063
0
        curr_ptr+=2;
1064
0
    }
1065
0
}
1066
1067
/* See comments before add_lutAtoBtype about allowable forms, which will
1068
    explain much of these size calculations */
1069
static int
1070
getsize_lutAtoBtype(gsicc_lutatob *lutatobparts)
1071
0
{
1072
0
    int data_offset, mlut_size;
1073
0
    int numout = lutatobparts->num_out;
1074
0
    int numin = lutatobparts->num_in;
1075
0
    int pad_bytes;
1076
1077
0
    data_offset = 32;
1078
    /* B curves always present */
1079
0
    if (lutatobparts->b_curves != NULL) {
1080
0
        data_offset += (numout*(CURVE_SIZE*2+12));
1081
0
    } else {
1082
0
        data_offset += (numout*(IDENT_CURVE_SIZE*2+12));
1083
0
    }
1084
    /* M curves present if Matrix is present */
1085
0
    if (lutatobparts->matrix != NULL ) {
1086
0
        data_offset += (12*4);
1087
        /* M curves */
1088
0
        if (lutatobparts->m_curves != NULL) {
1089
0
            data_offset += (numout*(CURVE_SIZE*2+12));
1090
0
        } else {
1091
0
            data_offset += (numout*(IDENT_CURVE_SIZE*2+12));
1092
0
        }
1093
0
    }
1094
    /* A curves present if clut is present */
1095
0
    if (lutatobparts->clut != NULL) {
1096
        /* We may need to pad the clut to make sure we are on a 4 byte boundary */
1097
0
        mlut_size = lutatobparts->clut->clut_num_entries *
1098
0
                            lutatobparts->clut->clut_word_width * 3;
1099
0
        pad_bytes = (4 - mlut_size%4)%4;
1100
0
        data_offset += (mlut_size + pad_bytes + 20);
1101
0
        if (lutatobparts->a_curves != NULL) {
1102
0
            data_offset += (numin*(CURVE_SIZE*2+12));
1103
0
        } else {
1104
0
            data_offset += (numin*(IDENT_CURVE_SIZE*2+12));
1105
0
        }
1106
0
    }
1107
0
    return data_offset;
1108
0
}
1109
1110
/* Note:  ICC V4 fomat allows ONLY these forms
1111
B
1112
M - Matrix - B
1113
A - CLUT - B
1114
A - CLUT - M - Matrix - B
1115
Other forms are created by making some of these items identity.  In other words
1116
the B curves must always be included.  If CLUT is present, A curves must be present.
1117
Also, if Matrix is present M curves must be present.  A curves cannot be
1118
present if CLUT is not present. */
1119
static void
1120
add_lutAtoBtype(unsigned char *input_ptr, gsicc_lutatob *lutatobparts)
1121
0
{
1122
/* We need to figure out all the offsets to the various objects based upon
1123
    which ones are actually present */
1124
0
    unsigned char *curr_ptr;
1125
0
    long mlut_size = 0;     /* silence compiler warning */
1126
0
    int data_offset;
1127
0
    int k;
1128
0
    int numout = lutatobparts->num_out;
1129
0
    int numin = lutatobparts->num_in;
1130
0
    int pad_bytes = 0;
1131
1132
    /* Signature */
1133
0
    curr_ptr = input_ptr;
1134
0
    write_bigendian_4bytes(curr_ptr,icMultiFunctionAtoBType);
1135
0
    curr_ptr += 4;
1136
    /* Reserved */
1137
0
    memset(curr_ptr,0,4);
1138
0
    curr_ptr += 4;
1139
    /* Padded sizes */
1140
0
    *curr_ptr++ = numin;
1141
0
    *curr_ptr++ = numout;
1142
0
    memset(curr_ptr,0,2);
1143
0
    curr_ptr += 2;
1144
    /* Note if data offset is zero, element is not present */
1145
    /* offset to B curves (last curves) */
1146
0
    data_offset = 32;
1147
0
    if (lutatobparts->b_curves == NULL) {
1148
        /* identity curve must be present */
1149
0
        write_bigendian_4bytes(curr_ptr,data_offset);
1150
0
        data_offset += (numout*(IDENT_CURVE_SIZE*2+12));
1151
0
    } else {
1152
0
        write_bigendian_4bytes(curr_ptr,data_offset);
1153
0
        data_offset += (numout*(CURVE_SIZE*2+12));
1154
0
    }
1155
0
    curr_ptr += 4;
1156
    /* offset to matrix and M curves */
1157
0
    if (lutatobparts->matrix == NULL) {
1158
0
        memset(curr_ptr,0,4);  /* Matrix */
1159
0
        curr_ptr += 4;
1160
0
        memset(curr_ptr,0,4);  /* M curves */
1161
0
    } else {
1162
0
        write_bigendian_4bytes(curr_ptr,data_offset);
1163
0
        data_offset += (12*4);
1164
0
        curr_ptr += 4;
1165
        /* offset to M curves (Matrix curves -- only come with matrix) */
1166
0
        if (lutatobparts->m_curves == NULL) {
1167
            /* identity curve must be present */
1168
0
            write_bigendian_4bytes(curr_ptr,data_offset);
1169
0
            data_offset += (numout*(IDENT_CURVE_SIZE*2+12));
1170
0
        } else {
1171
0
            write_bigendian_4bytes(curr_ptr,data_offset);
1172
0
            data_offset += (numout*(CURVE_SIZE*2+12));
1173
0
        }
1174
0
    }
1175
0
    curr_ptr += 4;
1176
    /* offset to CLUT and A curves */
1177
0
    if (lutatobparts->clut == NULL) {
1178
0
        memset(curr_ptr,0,4); /* CLUT */
1179
0
        curr_ptr += 4;
1180
0
        memset(curr_ptr,0,4); /* A curves */
1181
0
    } else {
1182
0
        write_bigendian_4bytes(curr_ptr,data_offset);
1183
0
        mlut_size = (long)lutatobparts->clut->clut_num_entries *
1184
0
                          lutatobparts->clut->clut_word_width * 3;
1185
0
        pad_bytes = (4 - mlut_size%4)%4;
1186
0
        data_offset += (mlut_size + pad_bytes + 20);
1187
0
        curr_ptr += 4;
1188
        /* offset to A curves (first curves) */
1189
0
        if (lutatobparts->a_curves == NULL || lutatobparts->clut == NULL) {
1190
            /* identity curve must be present */
1191
0
            write_bigendian_4bytes(curr_ptr,data_offset);
1192
0
            data_offset += (numin*(IDENT_CURVE_SIZE*2+12));
1193
0
        } else {
1194
0
            write_bigendian_4bytes(curr_ptr,data_offset);
1195
0
            data_offset += (numin*(CURVE_SIZE*2+12));
1196
0
        }
1197
0
    }
1198
0
    curr_ptr += 4;
1199
    /* Header is completed */
1200
    /* Now write out the various parts (i.e. curves, matrix and clut) */
1201
    /* First the B curves */
1202
0
    if (lutatobparts->b_curves != NULL) {
1203
0
        for (k = 0; k < numout; k++) {
1204
0
            add_curve(curr_ptr, (lutatobparts->b_curves)+k*CURVE_SIZE, CURVE_SIZE);
1205
0
            curr_ptr += (12 + CURVE_SIZE*2);
1206
0
        }
1207
0
    } else {
1208
0
        add_ident_curves(curr_ptr,numout);
1209
0
        curr_ptr += numout*(12 + IDENT_CURVE_SIZE*2);
1210
0
    }
1211
    /* Then the matrix */
1212
0
    if (lutatobparts->matrix != NULL) {
1213
0
        add_matrixwithbias(curr_ptr,(float*) lutatobparts->matrix,true);
1214
0
        curr_ptr += (12*4);
1215
        /* M curves */
1216
0
        if (lutatobparts->m_curves != NULL) {
1217
0
            for (k = 0; k < numout; k++) {
1218
0
                add_curve(curr_ptr, (lutatobparts->m_curves)+k*CURVE_SIZE, CURVE_SIZE);
1219
0
                curr_ptr += (12 + CURVE_SIZE*2);
1220
0
            }
1221
0
        } else {
1222
0
            add_ident_curves(curr_ptr,numout);
1223
0
            curr_ptr += numout*(12 + IDENT_CURVE_SIZE*2);
1224
0
        }
1225
0
    }
1226
    /* Then the clut */
1227
0
    if (lutatobparts->clut != NULL) {
1228
0
        add_clutAtoB(curr_ptr, lutatobparts->clut);
1229
0
        curr_ptr += (20 + mlut_size);
1230
0
        memset(curr_ptr,0,pad_bytes); /* 4 byte boundary */
1231
0
        curr_ptr += pad_bytes;
1232
        /* The A curves */
1233
0
        if (lutatobparts->a_curves != NULL) {
1234
0
            for (k = 0; k < numin; k++) {
1235
0
                add_curve(curr_ptr, (lutatobparts->a_curves)+k*CURVE_SIZE,
1236
0
                            CURVE_SIZE);
1237
0
                curr_ptr += (12 + CURVE_SIZE*2);
1238
0
            }
1239
0
        } else {
1240
0
            add_ident_curves(curr_ptr,numin);
1241
0
            curr_ptr += numin*(12 + IDENT_CURVE_SIZE*2);
1242
0
        }
1243
1244
0
    }
1245
0
}
1246
1247
/* This creates an ICC profile from the PDF calGray and calRGB definitions */
1248
cmm_profile_t*
1249
gsicc_create_from_cal(float *white, float *black, float *gamma, float *matrix,
1250
                      gs_memory_t *memory, int num_colors)
1251
2.26k
{
1252
2.26k
    icProfile iccprofile;
1253
2.26k
    icHeader  *header = &(iccprofile.header);
1254
2.26k
    int profile_size,k;
1255
2.26k
    int num_tags;
1256
2.26k
    gsicc_tag *tag_list;
1257
2.26k
    unsigned short encode_gamma;
1258
2.26k
    unsigned char *curr_ptr;
1259
2.26k
    int last_tag;
1260
2.26k
    icS15Fixed16Number temp_XYZ[3];
1261
2.26k
    int tag_location;
1262
2.26k
    icTagSignature TRC_Tags[3] = {icSigRedTRCTag, icSigGreenTRCTag,
1263
2.26k
                                  icSigBlueTRCTag};
1264
2.26k
    int trc_tag_size;
1265
2.26k
    unsigned char *buffer;
1266
2.26k
    cmm_profile_t *result;
1267
2.26k
    float *cat02;
1268
2.26k
    float black_adapt[3];
1269
1270
    /* Fill in the common stuff */
1271
2.26k
    setheader_common(header, 4);
1272
2.26k
    header->pcs = icSigXYZData;
1273
2.26k
    profile_size = HEADER_SIZE;
1274
2.26k
    header->deviceClass = icSigInputClass;
1275
2.26k
    if (num_colors == 3) {
1276
2.10k
        header->colorSpace = icSigRgbData;
1277
2.10k
        num_tags = 10;  /* common (2) + rXYZ,gXYZ,bXYZ,rTRC,gTRC,bTRC,bkpt,wtpt */
1278
2.10k
    } else if (num_colors == 1) {
1279
163
        header->colorSpace = icSigGrayData;
1280
163
        num_tags = 5;  /* common (2) + GrayTRC,bkpt,wtpt */
1281
163
        TRC_Tags[0] = icSigGrayTRCTag;
1282
163
    } else {
1283
0
        return NULL;
1284
0
    }
1285
2.26k
    tag_list = (gsicc_tag*) gs_alloc_bytes(memory,
1286
2.26k
                    sizeof(gsicc_tag)*num_tags,"gsicc_create_from_cal");
1287
2.26k
    if (tag_list == NULL)
1288
0
        return NULL;
1289
    /* Let us precompute the sizes of everything and all our offsets */
1290
2.26k
    profile_size += TAG_SIZE*num_tags;
1291
2.26k
    profile_size += 4; /* number of tags.... */
1292
2.26k
    last_tag = -1;
1293
2.26k
    init_common_tags(tag_list, num_tags, &last_tag);
1294
2.26k
    if (num_colors == 3) {
1295
2.10k
        init_tag(tag_list, &last_tag, icSigRedColorantTag, XYZPT_SIZE);
1296
2.10k
        init_tag(tag_list, &last_tag, icSigGreenColorantTag, XYZPT_SIZE);
1297
2.10k
        init_tag(tag_list, &last_tag, icSigBlueColorantTag, XYZPT_SIZE);
1298
2.10k
    }
1299
2.26k
    init_tag(tag_list, &last_tag, icSigMediaWhitePointTag, XYZPT_SIZE);
1300
2.26k
    init_tag(tag_list, &last_tag, icSigMediaBlackPointTag, XYZPT_SIZE);
1301
    /* 4 for count, 2 for gamma, Extra 2 bytes for 4 byte alignment requirement */
1302
2.26k
    trc_tag_size = 8;
1303
8.74k
    for (k = 0; k < num_colors; k++) {
1304
6.47k
        init_tag(tag_list, &last_tag, TRC_Tags[k], trc_tag_size);
1305
6.47k
    }
1306
24.1k
    for(k = 0; k < num_tags; k++) {
1307
21.8k
        profile_size += tag_list[k].size;
1308
21.8k
    }
1309
    /* Now we can go ahead and fill our buffer with the data.  Profile
1310
       buffer data is in non-gc memory */
1311
2.26k
    buffer = gs_alloc_bytes(memory->non_gc_memory,
1312
2.26k
                            profile_size, "gsicc_create_from_cal");
1313
2.26k
    if (buffer == NULL) {
1314
0
        gs_free_object(memory, tag_list, "gsicc_create_from_cal");
1315
0
        return NULL;
1316
0
    }
1317
2.26k
    curr_ptr = buffer;
1318
    /* The header */
1319
2.26k
    header->size = profile_size;
1320
2.26k
    copy_header(curr_ptr,header);
1321
2.26k
    curr_ptr += HEADER_SIZE;
1322
    /* Tag table */
1323
2.26k
    copy_tagtable(curr_ptr,tag_list,num_tags);
1324
2.26k
    curr_ptr += TAG_SIZE*num_tags;
1325
2.26k
    curr_ptr += 4;
1326
    /* Now the data.  Must be in same order as we created the tag table */
1327
    /* First the common tags */
1328
2.26k
    add_common_tag_data(curr_ptr, tag_list, 4);
1329
6.80k
    for (k = 0; k< NUMBER_COMMON_TAGS; k++) {
1330
4.53k
        curr_ptr += tag_list[k].size;
1331
4.53k
    }
1332
2.26k
    tag_location = NUMBER_COMMON_TAGS;
1333
1334
    /* Get the cat02 matrix */
1335
2.26k
    cat02 = gsicc_get_cat02_cam(white, memory);
1336
2.26k
    if (cat02 == NULL)
1337
0
    {
1338
0
        gs_rethrow(gs_error_VMerror, "Creation of cat02 matrix / ICC profile failed");
1339
0
        return NULL;
1340
0
    }
1341
1342
    /* The matrix */
1343
2.26k
    if (num_colors == 3) {
1344
8.42k
        for ( k = 0; k < 3; k++ ) {
1345
6.31k
            float primary[3];
1346
            /* Apply the cat02 matrix to the primaries */
1347
6.31k
            apply_adaption(cat02, &(matrix[k * 3]), &(primary[0]));
1348
6.31k
            get_XYZ_doubletr(temp_XYZ, &(primary[0]));
1349
6.31k
            add_xyzdata(curr_ptr, temp_XYZ);
1350
6.31k
            curr_ptr += tag_list[tag_location].size;
1351
6.31k
            tag_location++;
1352
6.31k
        }
1353
2.10k
    }
1354
    /* White and black points.  WP is D50 */
1355
2.26k
    get_D50(temp_XYZ);
1356
2.26k
    add_xyzdata(curr_ptr,temp_XYZ);
1357
2.26k
    curr_ptr += tag_list[tag_location].size;
1358
2.26k
    tag_location++;
1359
    /* Black point.  Apply cat02*/
1360
2.26k
    apply_adaption(cat02, black, &(black_adapt[0]));
1361
2.26k
    get_XYZ_doubletr(temp_XYZ, &(black_adapt[0]));
1362
2.26k
    add_xyzdata(curr_ptr,temp_XYZ);
1363
2.26k
    curr_ptr += tag_list[tag_location].size;
1364
2.26k
    tag_location++;
1365
    /* Now the gamma values */
1366
8.74k
    for (k = 0; k < num_colors; k++) {
1367
6.47k
        encode_gamma = float2u8Fixed8(gamma[k]);
1368
6.47k
        add_gammadata(curr_ptr, encode_gamma, icSigCurveType);
1369
6.47k
        curr_ptr += tag_list[tag_location].size;
1370
6.47k
        tag_location++;
1371
6.47k
    }
1372
2.26k
    result = gsicc_profile_new(NULL, memory, NULL, 0);
1373
2.26k
    if (result == NULL)
1374
0
    {
1375
0
        gs_throw(gs_error_VMerror, "Creation of ICC profile failed");
1376
0
        return NULL;
1377
0
    }
1378
2.26k
    result->buffer = buffer;
1379
2.26k
    result->buffer_size = profile_size;
1380
2.26k
    result->num_comps = num_colors;
1381
2.26k
    if (num_colors == 3) {
1382
2.10k
        result->data_cs = gsRGB;
1383
2.10k
        result->default_match = CAL_RGB;
1384
2.10k
    } else {
1385
163
        result->data_cs = gsGRAY;
1386
163
        result->default_match = CAL_GRAY;
1387
163
    }
1388
    /* Set the hash code  */
1389
2.26k
    gsicc_get_icc_buff_hash(buffer, &(result->hashcode), result->buffer_size);
1390
2.26k
    result->hash_is_valid = true;
1391
    /* Free up the tag list */
1392
2.26k
    gs_free_object(memory, tag_list, "gsicc_create_from_cal");
1393
2.26k
    gs_free_object(memory, cat02, "gsicc_create_from_cal");
1394
1395
#if SAVEICCPROFILE
1396
    /* Dump the buffer to a file for testing if its a valid ICC profile */
1397
    if (num_colors == 3)
1398
        save_profile(memory,buffer,"from_calRGB",profile_size);
1399
    else
1400
        save_profile(memory,buffer,"from_calGray",profile_size);
1401
#endif
1402
2.26k
    return result;
1403
2.26k
}
1404
1405
static void
1406
gsicc_create_free_luta2bpart(gs_memory_t *memory, gsicc_lutatob *icc_luta2bparts)
1407
0
{
1408
    /* Note that white_point, black_point and matrix are not allocated but
1409
       are on the local stack */
1410
0
    gs_free_object(memory, icc_luta2bparts->a_curves,
1411
0
                    "gsicc_create_free_luta2bpart");
1412
0
    gs_free_object(memory, icc_luta2bparts->b_curves,
1413
0
                    "gsicc_create_free_luta2bpart");
1414
0
    gs_free_object(memory, icc_luta2bparts->m_curves,
1415
0
                    "gsicc_create_free_luta2bpart");
1416
0
    gs_free_object(memory, icc_luta2bparts->cam,
1417
0
                    "gsicc_create_free_luta2bpart");
1418
0
    if (icc_luta2bparts->clut) {
1419
        /* Note, data_byte is handled externally.  We do not free that member here */
1420
0
        gs_free_object(memory, icc_luta2bparts->clut->data_short,
1421
0
                        "gsicc_create_free_luta2bpart");
1422
0
        gs_free_object(memory, icc_luta2bparts->clut,
1423
0
                        "gsicc_create_free_luta2bpart");
1424
0
    }
1425
0
}
1426
1427
static void
1428
gsicc_create_init_luta2bpart(gsicc_lutatob *icc_luta2bparts)
1429
0
{
1430
0
    icc_luta2bparts->a_curves = NULL;
1431
0
    icc_luta2bparts->b_curves = NULL;
1432
0
    icc_luta2bparts->clut = NULL;
1433
0
    icc_luta2bparts->m_curves = NULL;
1434
0
    icc_luta2bparts->cam = NULL;
1435
0
    icc_luta2bparts->matrix = NULL;
1436
0
    icc_luta2bparts->white_point = NULL;
1437
0
    icc_luta2bparts->black_point = NULL;
1438
0
    icc_luta2bparts->num_in = 0;
1439
0
    icc_luta2bparts->num_out = 0;
1440
0
}
1441
1442
static void
1443
gsicc_create_initialize_clut(gsicc_clut *clut)
1444
0
{
1445
0
    int k;
1446
1447
0
    clut->clut_num_entries = clut->clut_dims[0];
1448
0
    for (k = 1; k < clut->clut_num_input; k++) {
1449
0
        clut->clut_num_entries *= clut->clut_dims[k];
1450
0
    }
1451
0
    clut->data_byte =  NULL;
1452
0
    clut->data_short = NULL;
1453
0
}
1454
1455
/* A common form used for most of the PS CIE color spaces */
1456
static int
1457
create_lutAtoBprofile(unsigned char **pp_buffer_in, icHeader *header,
1458
                      gsicc_lutatob *lutatobparts, bool yonly, bool mashedLUT,
1459
                      gs_memory_t *memory)
1460
0
{
1461
0
    int num_tags = 5;  /* common (2), AToB0Tag,bkpt, wtpt */
1462
0
    int k;
1463
0
    gsicc_tag *tag_list;
1464
0
    int profile_size, last_tag, tag_location, tag_size;
1465
0
    unsigned char *buffer,*curr_ptr;
1466
0
    icS15Fixed16Number temp_XYZ[3];
1467
0
    gs_vector3 d50;
1468
0
    float *cam;
1469
0
    gs_matrix3 temp_matrix;
1470
0
    float lmn_vector[3],d50_cieA[3];
1471
1472
0
    profile_size = HEADER_SIZE;
1473
0
    tag_list = (gsicc_tag*) gs_alloc_bytes(memory, sizeof(gsicc_tag)*num_tags,
1474
0
                                            "create_lutAtoBprofile");
1475
0
    if (tag_list == NULL)
1476
0
        return gs_throw(gs_error_VMerror, "Allocation of ICC tag list failed");
1477
1478
    /* Let us precompute the sizes of everything and all our offsets */
1479
0
    profile_size += TAG_SIZE*num_tags;
1480
0
    profile_size += 4; /* number of tags.... */
1481
0
    last_tag = -1;
1482
0
    init_common_tags(tag_list, num_tags, &last_tag);
1483
0
    init_tag(tag_list, &last_tag, icSigMediaWhitePointTag, XYZPT_SIZE);
1484
0
    init_tag(tag_list, &last_tag, icSigMediaBlackPointTag, XYZPT_SIZE);
1485
1486
    /* Get the tag size of the A2B0 with the lutAtoBType */
1487
    /* Compensate for init_tag() adding DATATYPE_SIZE */
1488
0
    tag_size = getsize_lutAtoBtype(lutatobparts) - DATATYPE_SIZE;
1489
0
    init_tag(tag_list, &last_tag, icSigAToB0Tag, tag_size);
1490
    /* Add all the tag sizes to get the new profile size */
1491
0
    for(k = 0; k < num_tags; k++) {
1492
0
        profile_size += tag_list[k].size;
1493
0
    }
1494
    /* End of tag table information */
1495
    /* Now we can go ahead and fill our buffer with the data.  Profile
1496
       is in non-gc memory */
1497
0
    buffer = gs_alloc_bytes(memory->non_gc_memory, profile_size,
1498
0
                            "create_lutAtoBprofile");
1499
0
    if (buffer == NULL) {
1500
0
        gs_free_object(memory, tag_list, "create_lutAtoBprofile");
1501
0
        return gs_throw(gs_error_VMerror, "Allocation of ICC buffer failed");
1502
0
    }
1503
0
    curr_ptr = buffer;
1504
    /* The header */
1505
0
    header->size = profile_size;
1506
0
    copy_header(curr_ptr,header);
1507
0
    curr_ptr += HEADER_SIZE;
1508
    /* Tag table */
1509
0
    copy_tagtable(curr_ptr, tag_list, num_tags);
1510
0
    curr_ptr += TAG_SIZE * num_tags;
1511
0
    curr_ptr += 4;
1512
    /* Now the data.  Must be in same order as we created the tag table */
1513
    /* First the common tags */
1514
0
    add_common_tag_data(curr_ptr, tag_list, 4);
1515
0
    for (k = 0; k< NUMBER_COMMON_TAGS; k++) {
1516
0
        curr_ptr += tag_list[k].size;
1517
0
    }
1518
0
    tag_location = NUMBER_COMMON_TAGS;
1519
    /* Here we take care of chromatic adapatation.  Compute the
1520
       matrix.  We will need to hit the data with the matrix and
1521
       store it in the profile. */
1522
0
    d50.u = D50_X;
1523
0
    d50.v = D50_Y;
1524
0
    d50.w = D50_Z;
1525
0
    cam = (float*) gs_alloc_bytes(memory, 9 * sizeof(float), "create_lutAtoBprofile");
1526
0
    if (cam == NULL) {
1527
0
        gs_free_object(memory, tag_list, "create_lutAtoBprofile");
1528
0
        gs_free_object(memory->non_gc_memory, buffer, "create_lutAtoBprofile");
1529
0
        return gs_throw(gs_error_VMerror, "Allocation of ICC cam failed");
1530
0
    }
1531
0
    gsicc_create_compute_cam(lutatobparts->white_point, &(d50), cam);
1532
0
    gs_free_object(memory, lutatobparts->cam, "create_lutAtoBprofile");
1533
0
    lutatobparts->cam = cam;
1534
0
    get_D50(temp_XYZ); /* See Appendix D6 in spec */
1535
0
    add_xyzdata(curr_ptr, temp_XYZ);
1536
0
    curr_ptr += tag_list[tag_location].size;
1537
0
    tag_location++;
1538
0
    get_XYZ(temp_XYZ, lutatobparts->black_point);
1539
0
    add_xyzdata(curr_ptr, temp_XYZ);
1540
0
    curr_ptr += tag_list[tag_location].size;
1541
0
    tag_location++;
1542
    /* Multiply the matrix in the AtoB object by the cam so that the data
1543
       is in D50 */
1544
0
    if (lutatobparts->matrix == NULL) {
1545
0
        gsicc_create_copy_matrix3(cam, (float*) &temp_matrix);
1546
0
        lutatobparts->matrix = &temp_matrix;
1547
0
    } else {
1548
0
        if (yonly) {
1549
            /* Used for CIEBaseA case.  Studies of CIEBasedA spaces
1550
               and AR rendering of these reveals that they only look
1551
               at the product sum of the MatrixA and the 2nd column of
1552
               the LM Matrix (if there is one).  This is used as a Y
1553
               decode value from which to map between the black point
1554
               and the white point.  The black point is actually ignored
1555
               and a black point of 0 is used. Essentialy we have
1556
               weighted versions of D50 in each column of the matrix
1557
               which ensures we stay on the achromatic axis */
1558
0
            lmn_vector[0] = lutatobparts->matrix->cv.u;
1559
0
            lmn_vector[1] = lutatobparts->matrix->cv.v;
1560
0
            lmn_vector[2] = lutatobparts->matrix->cv.w;
1561
0
            if (mashedLUT) {
1562
                /* Table data already scaled */
1563
0
                d50_cieA[0] = D50_X;
1564
0
                d50_cieA[1] = D50_Y;
1565
0
                d50_cieA[2] = D50_Z;
1566
0
            } else {
1567
                /* Need to do final scaling to ICC CIEXYZ range */
1568
0
                d50_cieA[0] = (float)(D50_X / (1.0 + (32767.0/32768.0)));
1569
0
                d50_cieA[1] = (float)(D50_Y / (1.0 + (32767.0/32768.0)));
1570
0
                d50_cieA[2] = (float)(D50_Z / (1.0 + (32767.0/32768.0)));
1571
0
            }
1572
0
            matrixmult(&(d50_cieA[0]), 3, 1, &(lmn_vector[0]), 1, 3,
1573
0
                        &(lutatobparts->matrix->cu.u));
1574
0
        } else {
1575
0
            matrixmult(cam, 3, 3, &(lutatobparts->matrix->cu.u), 3, 3,
1576
0
                    &(temp_matrix.cu.u));
1577
0
            lutatobparts->matrix = &temp_matrix;
1578
0
        }
1579
0
    }
1580
    /* Now the AToB0Tag Data. Here this will include the M curves, the matrix
1581
       and the B curves. We may need to do some adustements with respect
1582
       to encode and decode.  For now assume all is between 0 and 1. */
1583
0
    add_lutAtoBtype(curr_ptr, lutatobparts);
1584
0
    *pp_buffer_in = buffer;
1585
0
    gs_free_object(memory, tag_list, "create_lutAtoBprofile");
1586
0
    return 0;
1587
0
}
1588
1589
/* Shared code between all the PS types whereby we mash together all the
1590
   components into a single CLUT.  Not preferable in general but necessary
1591
   when the PS components do not map easily into the ICC forms */
1592
static int
1593
gsicc_create_mashed_clut(gsicc_lutatob *icc_luta2bparts,
1594
                         icHeader *header, gx_color_lookup_table *Table,
1595
                         const gs_color_space *pcs, gs_range *ranges,
1596
                         unsigned char **pp_buffer_in, int *profile_size_out,
1597
                         bool range_adjust, gs_memory_t* memory)
1598
0
{
1599
0
    int k;
1600
0
    int code;
1601
0
    gsicc_clut *clut;
1602
0
    gs_matrix3 ident_matrix;
1603
0
    gs_vector3 ones_vec;
1604
1605
   /* A table is going to be mashed form of all the transform */
1606
    /* Allocate space for the clut */
1607
0
    clut = (gsicc_clut*) gs_alloc_bytes(memory, sizeof(gsicc_clut),
1608
0
                                "gsicc_create_mashed_clut");
1609
0
    if (clut == NULL)
1610
0
        return gs_throw(gs_error_VMerror, "Allocation of ICC clut failed");
1611
0
    icc_luta2bparts->clut = clut;
1612
0
    if ( icc_luta2bparts->num_in == 1 ) {
1613
        /* Use a larger sample for 1-D input */
1614
0
        clut->clut_dims[0] = DEFAULT_TABLE_GRAYSIZE;
1615
0
    } else {
1616
0
        for (k = 0; k < icc_luta2bparts->num_in; k++) {
1617
0
            if (Table != NULL && Table->dims[k] > DEFAULT_TABLE_NSIZE ) {
1618
                /* If it has a table use the existing table size if
1619
                   it is larger than our default size */
1620
0
                clut->clut_dims[k] = Table->dims[k];
1621
0
            } else {
1622
                /* If not, then use a default size */
1623
0
                clut->clut_dims[k] = DEFAULT_TABLE_NSIZE;
1624
0
            }
1625
0
        }
1626
0
    }
1627
0
    clut->clut_num_input = icc_luta2bparts->num_in;
1628
0
    clut->clut_num_output = 3;  /* CIEXYZ */
1629
0
    clut->clut_word_width = 2;  /* 16 bit */
1630
0
    gsicc_create_initialize_clut(clut);
1631
    /* Allocate space for the table data */
1632
0
    clut->data_short = (unsigned short*) gs_alloc_bytes(memory,
1633
0
        clut->clut_num_entries*3*sizeof(unsigned short),"gsicc_create_mashed_clut");
1634
0
    if (clut->data_short == NULL) {
1635
0
        gs_free_object(memory, clut, "gsicc_create_mashed_clut");
1636
0
        return gs_throw(gs_error_VMerror, "Allocation of ICC clut short data failed");
1637
0
    }
1638
    /* Create the table */
1639
0
    code = gsicc_create_clut(pcs, clut, ranges, icc_luta2bparts->white_point,
1640
0
                             range_adjust, icc_luta2bparts->cam, memory);
1641
0
    if (code < 0) {
1642
0
        gs_free_object(memory, clut, "gsicc_create_mashed_clut");
1643
0
        return gs_rethrow(code, "Creation of ICC clut failed");
1644
0
    }
1645
    /* Initialize other parts. Also make sure acurves are reset since
1646
       they have been mashed into the table. */
1647
0
    gs_free_object(memory, icc_luta2bparts->a_curves, "gsicc_create_mashed_clut");
1648
0
    icc_luta2bparts->a_curves = NULL;
1649
0
    icc_luta2bparts->b_curves = NULL;
1650
0
    icc_luta2bparts->m_curves = NULL;
1651
0
    ones_vec.u = 1;
1652
0
    ones_vec.v = 1;
1653
0
    ones_vec.w = 1;
1654
0
    gsicc_make_diag_matrix(&ident_matrix,&ones_vec);
1655
0
    icc_luta2bparts->matrix = &ident_matrix;
1656
    /* Now create the profile */
1657
0
    if (icc_luta2bparts->num_in == 1 ) {
1658
0
        code = create_lutAtoBprofile(pp_buffer_in, header, icc_luta2bparts, true,
1659
0
                                     true, memory);
1660
0
    } else {
1661
0
        code = create_lutAtoBprofile(pp_buffer_in, header, icc_luta2bparts, false,
1662
0
                                     true, memory);
1663
0
    }
1664
0
    return code;
1665
0
}
1666
1667
/* Shared code by ABC, DEF and DEFG compaction of ABC/LMN parts.  This is used
1668
   when either MatrixABC is identity, LMN Decode is identity or MatrixLMN
1669
   is identity.  This allows us to map into the ICC form and not have to mash
1670
   into a full CLUT */
1671
static int
1672
gsicc_create_abc_merge(gsicc_lutatob *atob_parts, gs_matrix3 *matrixLMN,
1673
                       gs_matrix3 *matrixABC, bool has_abc_procs,
1674
                       bool has_lmn_procs, gx_cie_vector_cache *abc_caches,
1675
                       gx_cie_scalar_cache *lmn_caches, gs_memory_t *memory)
1676
0
{
1677
0
    gs_matrix3 temp_matrix;
1678
0
    gs_matrix3 *matrix_ptr;
1679
0
    float *curr_pos;
1680
1681
    /* Determine the matrix that we will be using */
1682
0
    if (!(matrixLMN->is_identity) && !(matrixABC->is_identity)){
1683
        /* Use the product of the ABC and LMN matrices, since lmn_procs identity.
1684
           Product must be LMN_Matrix*ABC_Matrix */
1685
0
        cie_matrix_mult3(matrixLMN, matrixABC, &temp_matrix);
1686
0
        cie_matrix_transpose3(&temp_matrix, atob_parts->matrix);
1687
0
    } else {
1688
        /* Either ABC matrix or LMN matrix is identity */
1689
0
        if (matrixABC->is_identity) {
1690
0
            matrix_ptr = matrixLMN;
1691
0
        } else {
1692
0
            matrix_ptr = matrixABC;
1693
0
        }
1694
0
        cie_matrix_transpose3(matrix_ptr, atob_parts->matrix);
1695
0
    }
1696
    /* Merge the curves */
1697
0
    if (has_abc_procs && has_lmn_procs && matrixABC->is_identity) {
1698
        /* Merge the curves into the abc curves. no b curves */
1699
0
        merge_abc_lmn_curves(abc_caches, lmn_caches);
1700
0
        has_lmn_procs = false;
1701
0
    }
1702
    /* Figure out what curves get mapped to where.  The only time we will use the b
1703
       curves is if matrixABC is not the identity and we have lmn procs */
1704
0
    if ( !(matrixABC->is_identity) && has_lmn_procs) {
1705
        /* A matrix followed by a curve */
1706
0
        atob_parts->b_curves = (float*) gs_alloc_bytes(memory,
1707
0
                            3*CURVE_SIZE*sizeof(float),"gsicc_create_abc_merge");
1708
0
        if (atob_parts->b_curves == NULL)
1709
0
            return gs_throw(gs_error_VMerror, "Allocation of ICC b curves failed");
1710
0
        curr_pos = atob_parts->b_curves;
1711
0
        memcpy(curr_pos,&(lmn_caches[0].floats.values[0]),CURVE_SIZE*sizeof(float));
1712
0
        curr_pos += CURVE_SIZE;
1713
0
        memcpy(curr_pos,&(lmn_caches[1].floats.values[0]),CURVE_SIZE*sizeof(float));
1714
0
        curr_pos += CURVE_SIZE;
1715
0
        memcpy(curr_pos,&(lmn_caches[2].floats.values[0]),CURVE_SIZE*sizeof(float));
1716
0
        if (has_abc_procs) {
1717
            /* Also a curve before the matrix */
1718
0
            atob_parts->m_curves = (float*) gs_alloc_bytes(memory,
1719
0
                            3*CURVE_SIZE*sizeof(float),"gsicc_create_abc_merge");
1720
0
            if (atob_parts->m_curves == NULL) {
1721
0
                gs_free_object(memory, atob_parts->b_curves, "gsicc_create_abc_merge");
1722
0
                return gs_throw(gs_error_VMerror, "Allocation of ICC m curves failed");
1723
0
            }
1724
0
            curr_pos = atob_parts->m_curves;
1725
0
            memcpy(curr_pos,&(abc_caches[0].floats.values[0]),CURVE_SIZE*sizeof(float));
1726
0
            curr_pos += CURVE_SIZE;
1727
0
            memcpy(curr_pos,&(abc_caches[1].floats.values[0]),CURVE_SIZE*sizeof(float));
1728
0
            curr_pos += CURVE_SIZE;
1729
0
            memcpy(curr_pos,&(abc_caches[2].floats.values[0]),CURVE_SIZE*sizeof(float));
1730
0
        }
1731
0
    } else {
1732
        /* Only one set of curves before a matrix.  Need to check this to make sure
1733
           there is not an issue here and we have has_abc_procs true and
1734
           has_lmn_procs true */
1735
0
        if (has_abc_procs) {
1736
0
            atob_parts->m_curves = (float*) gs_alloc_bytes(memory,
1737
0
                            3*CURVE_SIZE*sizeof(float),"gsicc_create_abc_merge");
1738
0
            if (atob_parts->m_curves == NULL)
1739
0
                return gs_throw(gs_error_VMerror, "Allocation of ICC m curves failed");
1740
0
            curr_pos = atob_parts->m_curves;
1741
0
            memcpy(curr_pos,&(abc_caches[0].floats.values[0]),CURVE_SIZE*sizeof(float));
1742
0
            curr_pos += CURVE_SIZE;
1743
0
            memcpy(curr_pos,&(abc_caches[1].floats.values[0]),CURVE_SIZE*sizeof(float));
1744
0
            curr_pos += CURVE_SIZE;
1745
0
            memcpy(curr_pos,&(abc_caches[2].floats.values[0]),CURVE_SIZE*sizeof(float));
1746
0
        }
1747
0
        if (has_lmn_procs) {
1748
0
            atob_parts->m_curves = (float*) gs_alloc_bytes(memory,
1749
0
                                3*CURVE_SIZE*sizeof(float),"gsicc_create_abc_merge");
1750
0
            if (atob_parts->m_curves == NULL)
1751
0
                return gs_throw(gs_error_VMerror, "Allocation of ICC m curves failed");
1752
0
            curr_pos = atob_parts->m_curves;
1753
0
            memcpy(curr_pos,&(lmn_caches[0].floats.values[0]),CURVE_SIZE*sizeof(float));
1754
0
            curr_pos += CURVE_SIZE;
1755
0
            memcpy(curr_pos,&(lmn_caches[1].floats.values[0]),CURVE_SIZE*sizeof(float));
1756
0
            curr_pos += CURVE_SIZE;
1757
0
            memcpy(curr_pos,&(lmn_caches[2].floats.values[0]),CURVE_SIZE*sizeof(float));
1758
0
        }
1759
0
    }
1760
    /* Note that if the b_curves are null and we have a matrix we need to scale
1761
       the matrix values by 2. Otherwise an input value of 50% gray, which is
1762
       32767 would get mapped to 32767 by the matrix.  This will be interpreted
1763
       as a max XYZ value (s15.16) when it is eventually mapped to u16.16 due
1764
       to the mapping of X=Y by the identity table.  If there are b_curves
1765
       these have an output that is 16 bit. */
1766
0
    if (atob_parts->b_curves == NULL) {
1767
0
        scale_matrix((float*) atob_parts->matrix, 2.0);
1768
0
    }
1769
0
    return 0;
1770
0
}
1771
1772
/* The ABC color space is modeled using the V4 lutAtoBType which has the
1773
   flexibility to model  the various parameters.  Simplified versions are used
1774
   it possible when certain parameters in the ABC color space definition are
1775
   the identity. */
1776
int
1777
gsicc_create_fromabc(const gs_color_space *pcs, unsigned char **pp_buffer_in,
1778
                     int *profile_size_out, gs_memory_t *memory,
1779
                     gx_cie_vector_cache *abc_caches,
1780
                     gx_cie_scalar_cache *lmn_caches, bool *islab)
1781
0
{
1782
0
    icProfile iccprofile;
1783
0
    icHeader  *header = &(iccprofile.header);
1784
#if SAVEICCPROFILE
1785
    int debug_catch = 1;
1786
#endif
1787
0
    int k;
1788
0
    gs_matrix3 matrix_input_trans;
1789
0
    gsicc_lutatob icc_luta2bparts;
1790
0
    float *curr_pos;
1791
0
    bool has_abc_procs = !((abc_caches->floats.params.is_identity &&
1792
0
                         (abc_caches)[1].floats.params.is_identity &&
1793
0
                         (abc_caches)[2].floats.params.is_identity));
1794
0
    bool has_lmn_procs = !((lmn_caches->floats.params.is_identity &&
1795
0
                         (lmn_caches)[1].floats.params.is_identity &&
1796
0
                         (lmn_caches)[2].floats.params.is_identity));
1797
0
    gs_cie_abc *pcie = pcs->params.abc;
1798
0
    bool input_range_ok;
1799
0
    int code;
1800
1801
0
    gsicc_create_init_luta2bpart(&icc_luta2bparts);
1802
0
    gsicc_matrix_init(&(pcie->common.MatrixLMN));  /* Need this set now */
1803
0
    gsicc_matrix_init(&(pcie->MatrixABC));          /* Need this set now */
1804
    /* Fill in the common stuff */
1805
0
    setheader_common(header, 4);
1806
1807
    /* We will use an input type class which keeps us from having to
1808
       create an inverse.  We will keep the data a generic 3 color.
1809
       Since we are doing PS color management the PCS is XYZ */
1810
0
    header->colorSpace = icSigRgbData;
1811
0
    header->deviceClass = icSigInputClass;
1812
0
    header->pcs = icSigXYZData;
1813
0
    icc_luta2bparts.num_in = 3;
1814
0
    icc_luta2bparts.num_out = 3;
1815
0
    icc_luta2bparts.white_point = &(pcie->common.points.WhitePoint);
1816
0
    icc_luta2bparts.black_point = &(pcie->common.points.BlackPoint);
1817
1818
    /* Calculate the chromatic adaptation matrix */
1819
0
    code = gsicc_compute_cam(&icc_luta2bparts, memory);
1820
0
    if (code < 0) {
1821
0
        return gs_rethrow(code, "Create ICC from CIEABC failed");
1822
0
    }
1823
1824
    /* Detect if the space is CIELAB. We don't have access to pgs here though */
1825
    /* *islab = cie_is_lab(pcie); This is not working yet */
1826
0
    *islab = false;
1827
1828
    /* Check what combination we have with respect to the various
1829
       LMN and ABC parameters. Depending upon the situation we
1830
       may be able to use a standard 3 channel input profile type. If we
1831
       do not have the LMN decode we can mash together the ABC and LMN
1832
       matrix. Also, if ABC is identity we can mash the ABC and LMN
1833
       decode procs.  If we have an ABC matrix, LMN procs and an LMN
1834
       matrix we will need to create a small (2x2x2) CLUT for the ICC format. */
1835
0
    input_range_ok = check_range(&(pcie->RangeABC.ranges[0]), 3);
1836
0
    if (!input_range_ok) {
1837
        /* We have a range problem at input */
1838
0
        code = gsicc_create_mashed_clut(&icc_luta2bparts, header, NULL, pcs,
1839
0
                                 &(pcie->RangeABC.ranges[0]), pp_buffer_in,
1840
0
                                 profile_size_out, true, memory);
1841
0
        if (code < 0)
1842
0
            return gs_rethrow(code, "Failed in ICC creation from ABC mashed. CLUT");
1843
0
    } else {
1844
0
        if (pcie->MatrixABC.is_identity || !has_lmn_procs ||
1845
0
                            pcie->common.MatrixLMN.is_identity) {
1846
            /* The merging of these parts into the curves/matrix/curves of the
1847
               lutAtoBtype portion can be used by abc, def and defg */
1848
0
            icc_luta2bparts.matrix = &matrix_input_trans;
1849
0
            code = gsicc_create_abc_merge(&(icc_luta2bparts), &(pcie->common.MatrixLMN),
1850
0
                                    &(pcie->MatrixABC), has_abc_procs,
1851
0
                                    has_lmn_procs, pcie->caches.DecodeABC.caches,
1852
0
                                    pcie->common.caches.DecodeLMN, memory);
1853
0
            if (code < 0)
1854
0
                return gs_rethrow(code, "Failed in ICC creation from ABC. Merge");
1855
0
            icc_luta2bparts.clut =  NULL;
1856
            /* Create the profile.  This is for the common generic form we will use
1857
               for almost everything. */
1858
0
            code = create_lutAtoBprofile(pp_buffer_in, header, &icc_luta2bparts, false,
1859
0
                                         false, memory);
1860
0
            if (code < 0)
1861
0
                return gs_rethrow(code, "Failed in ICC creation from ABC. Profile");
1862
0
        } else {
1863
            /* This will be a bit more complex as we have an ABC matrix, LMN decode
1864
               and an LMN matrix.  We will need to create an MLUT to handle this properly.
1865
               Any ABC decode will be handled as the A curves.  ABC matrix will be the
1866
               MLUT, LMN decode will be the M curves.  LMN matrix will be the Matrix
1867
               and b curves will be identity. */
1868
0
            if (has_abc_procs) {
1869
0
                icc_luta2bparts.a_curves = (float*) gs_alloc_bytes(memory,
1870
0
                                3*CURVE_SIZE*sizeof(float),"gsicc_create_fromabc");
1871
0
                if (icc_luta2bparts.a_curves == NULL)
1872
0
                    return gs_throw(gs_error_VMerror, "Allocation of ICC a curves failed");
1873
1874
0
                curr_pos = icc_luta2bparts.a_curves;
1875
0
                memcpy(curr_pos,&(pcie->caches.DecodeABC.caches->floats.values[0]),
1876
0
                                CURVE_SIZE*sizeof(float));
1877
0
                curr_pos += CURVE_SIZE;
1878
0
                memcpy(curr_pos,&((pcie->caches.DecodeABC.caches[1]).floats.values[0]),
1879
0
                                CURVE_SIZE*sizeof(float));
1880
0
                curr_pos += CURVE_SIZE;
1881
0
                memcpy(curr_pos,&((pcie->caches.DecodeABC.caches[2]).floats.values[0]),
1882
0
                                CURVE_SIZE*sizeof(float));
1883
0
            }
1884
0
            if (has_lmn_procs) {
1885
0
                icc_luta2bparts.m_curves = (float*) gs_alloc_bytes(memory,
1886
0
                                3*CURVE_SIZE*sizeof(float),"gsicc_create_fromabc");
1887
0
                if (icc_luta2bparts.m_curves == NULL) {
1888
0
                    gs_free_object(memory, icc_luta2bparts.a_curves,
1889
0
                                   "gsicc_create_fromabc");
1890
0
                    return gs_throw(gs_error_VMerror, "Allocation of ICC m curves failed");
1891
0
                }
1892
0
                curr_pos = icc_luta2bparts.m_curves;
1893
0
                memcpy(curr_pos,&(pcie->common.caches.DecodeLMN->floats.values[0]),
1894
0
                                CURVE_SIZE*sizeof(float));
1895
0
                curr_pos += CURVE_SIZE;
1896
0
                memcpy(curr_pos,&((pcie->common.caches.DecodeLMN[1]).floats.values[0]),
1897
0
                                CURVE_SIZE*sizeof(float));
1898
0
                curr_pos += CURVE_SIZE;
1899
0
                memcpy(curr_pos,&((pcie->common.caches.DecodeLMN[2]).floats.values[0]),
1900
0
                                CURVE_SIZE*sizeof(float));
1901
0
            }
1902
            /* Convert ABC matrix to 2x2x2 MLUT type */
1903
0
            icc_luta2bparts.clut = (gsicc_clut*) gs_alloc_bytes(memory,
1904
0
                                        sizeof(gsicc_clut),"gsicc_create_fromabc");
1905
0
            if (icc_luta2bparts.clut == NULL) {
1906
0
                gs_free_object(memory, icc_luta2bparts.a_curves,
1907
0
                               "gsicc_create_fromabc");
1908
0
                gs_free_object(memory, icc_luta2bparts.m_curves,
1909
0
                               "gsicc_create_fromabc");
1910
0
                return gs_throw(gs_error_VMerror, "Allocation of ICC clut failed");
1911
0
            }
1912
0
            for (k = 0; k < 3; k++) {
1913
0
                icc_luta2bparts.clut->clut_dims[k] = 2;
1914
0
            }
1915
0
            icc_luta2bparts.clut->clut_num_input = 3;
1916
0
            icc_luta2bparts.clut->clut_num_output = 3;
1917
0
            icc_luta2bparts.clut->clut_word_width = 2;
1918
0
            gsicc_create_initialize_clut(icc_luta2bparts.clut);
1919
            /* 8 grid points, 3 outputs */
1920
0
            icc_luta2bparts.clut->data_short =
1921
0
                            (unsigned short*) gs_alloc_bytes(memory,
1922
0
                            8*3*sizeof(short),"gsicc_create_fromabc");
1923
0
            if (icc_luta2bparts.clut->data_short == NULL) {
1924
0
                gs_free_object(memory, icc_luta2bparts.a_curves,
1925
0
                               "gsicc_create_fromabc");
1926
0
                gs_free_object(memory, icc_luta2bparts.m_curves,
1927
0
                               "gsicc_create_fromabc");
1928
0
                gs_free_object(memory, icc_luta2bparts.clut,
1929
0
                               "gsicc_create_fromabc");
1930
0
                return gs_throw(gs_error_VMerror, "Allocation of ICC clut data failed");
1931
0
            }
1932
0
            gsicc_matrix3_to_mlut(&(pcie->MatrixABC), icc_luta2bparts.clut->data_short);
1933
            /* LMN Matrix */
1934
0
            cie_matrix_transpose3(&(pcie->common.MatrixLMN), &matrix_input_trans);
1935
0
            icc_luta2bparts.matrix = &matrix_input_trans;
1936
            /* Create the profile */
1937
0
            code = create_lutAtoBprofile(pp_buffer_in, header, &icc_luta2bparts,
1938
0
                                         false, false, memory);
1939
0
            if (code < 0)
1940
0
                return code;
1941
0
        }
1942
0
    }
1943
0
    gsicc_create_free_luta2bpart(memory, &icc_luta2bparts);
1944
0
    *profile_size_out = header->size;
1945
#if SAVEICCPROFILE
1946
    /* Dump the buffer to a file for testing if its a valid ICC profile */
1947
    if(debug_catch)
1948
        save_profile(memory,*pp_buffer_in,"fromabc",header->size);
1949
#endif
1950
0
    return 0;
1951
0
}
1952
1953
int
1954
gsicc_create_froma(const gs_color_space *pcs, unsigned char **pp_buffer_in,
1955
                   int *profile_size_out, gs_memory_t *memory,
1956
                   gx_cie_vector_cache *a_cache, gx_cie_scalar_cache *lmn_caches)
1957
0
{
1958
0
    icProfile iccprofile;
1959
0
    icHeader  *header = &(iccprofile.header);
1960
#if SAVEICCPROFILE
1961
    int debug_catch = 1;
1962
#endif
1963
0
    gs_matrix3 matrix_input;
1964
0
    float *curr_pos;
1965
0
    bool has_a_proc = !(a_cache->floats.params.is_identity);
1966
0
    bool has_lmn_procs = !(lmn_caches->floats.params.is_identity &&
1967
0
                         (lmn_caches)[1].floats.params.is_identity &&
1968
0
                         (lmn_caches)[2].floats.params.is_identity);
1969
0
    gsicc_lutatob icc_luta2bparts;
1970
0
    bool common_range_ok;
1971
0
    gs_cie_a *pcie = pcs->params.a;
1972
0
    bool input_range_ok;
1973
0
    int code;
1974
1975
0
    gsicc_create_init_luta2bpart(&icc_luta2bparts);
1976
    /* Fill in the common stuff */
1977
0
    setheader_common(header, 4);
1978
    /* We will use an input type class which keeps us from having to
1979
       create an inverse.  We will keep the data a generic 3 color.
1980
       Since we are doing PS color management the PCS is XYZ */
1981
0
    header->colorSpace = icSigGrayData;
1982
0
    header->deviceClass = icSigInputClass;
1983
0
    header->pcs = icSigXYZData;
1984
0
    icc_luta2bparts.num_out = 3;
1985
0
    icc_luta2bparts.num_in = 1;
1986
0
    icc_luta2bparts.white_point = &(pcie->common.points.WhitePoint);
1987
0
    icc_luta2bparts.black_point = &(pcie->common.points.BlackPoint);
1988
1989
0
    code = gsicc_compute_cam(&icc_luta2bparts, memory);
1990
0
    if (code < 0) {
1991
0
        return gs_rethrow(code, "Create from CIEA failed");
1992
0
    }
1993
1994
    /* Check the range values.  If the internal ranges are outside of
1995
       0 to 1 then we will need to sample as a full CLUT.  The input
1996
       range can be different, but we we will correct for this.  Finally
1997
       we need to worry about enforcing the achromatic constraint for the
1998
       CLUT if we are creating the entire thing. */
1999
0
    common_range_ok = check_range(&(pcie->common.RangeLMN.ranges[0]),3);
2000
0
    if (!common_range_ok) {
2001
0
        input_range_ok = check_range(&(pcie->RangeA),1);
2002
0
        code = gsicc_create_mashed_clut(&icc_luta2bparts, header, NULL, pcs,
2003
0
                                 &(pcie->RangeA), pp_buffer_in, profile_size_out,
2004
0
                                 !input_range_ok, memory);
2005
0
        if (code < 0)
2006
0
            return gs_rethrow(code, "Failed to create ICC mashed CLUT");
2007
0
    } else {
2008
        /* We do not need to create a massive CLUT.  Try to maintain
2009
           the objects as best we can */
2010
        /* Since we are going from 1 gray input to 3 XYZ values, we will need
2011
           to include the MLUT for the 1 to 3 conversion applied by the matrix A.
2012
           Depending upon the other parameters we may have simpiler forms, but this
2013
           is required even when Matrix A is the identity. */
2014
0
        if (has_a_proc) {
2015
0
            icc_luta2bparts.a_curves = (float*) gs_alloc_bytes(memory,
2016
0
                CURVE_SIZE*sizeof(float),"gsicc_create_froma");
2017
0
            if (icc_luta2bparts.a_curves == NULL)
2018
0
                return gs_throw(gs_error_VMerror, "Allocation of ICC a curves failed");
2019
0
            memcpy(icc_luta2bparts.a_curves,
2020
0
                    &(pcie->caches.DecodeA.floats.values[0]),
2021
0
                    CURVE_SIZE*sizeof(float));
2022
0
        }
2023
0
        if (has_lmn_procs) {
2024
0
            icc_luta2bparts.m_curves = (float*) gs_alloc_bytes(memory,
2025
0
                3*CURVE_SIZE*sizeof(float),"gsicc_create_froma");
2026
0
            if (icc_luta2bparts.m_curves == NULL) {
2027
0
                gs_free_object(memory, icc_luta2bparts.a_curves, "gsicc_create_froma");
2028
0
                return gs_throw(gs_error_VMerror, "Allocation of ICC m curves failed");
2029
0
            }
2030
0
            curr_pos = icc_luta2bparts.m_curves;
2031
0
            memcpy(curr_pos,&(pcie->common.caches.DecodeLMN->floats.values[0]),
2032
0
                        CURVE_SIZE*sizeof(float));
2033
0
            curr_pos += CURVE_SIZE;
2034
0
            memcpy(curr_pos,&((pcie->common.caches.DecodeLMN[1]).floats.values[0]),
2035
0
                        CURVE_SIZE*sizeof(float));
2036
0
            curr_pos += CURVE_SIZE;
2037
0
            memcpy(curr_pos,&((pcie->common.caches.DecodeLMN[2]).floats.values[0]),
2038
0
                        CURVE_SIZE*sizeof(float));
2039
0
        }
2040
        /* Convert diagonal A matrix to 2x1 MLUT type */
2041
0
        icc_luta2bparts.clut = (gsicc_clut*) gs_alloc_bytes(memory,
2042
0
            sizeof(gsicc_clut),"gsicc_create_froma"); /* 2 grid points 3 outputs */
2043
0
        if (icc_luta2bparts.clut == NULL) {
2044
0
            gs_free_object(memory, icc_luta2bparts.a_curves, "gsicc_create_froma");
2045
0
            gs_free_object(memory, icc_luta2bparts.m_curves, "gsicc_create_froma");
2046
0
            return gs_throw(gs_error_VMerror, "Allocation of ICC clut failed");
2047
0
        }
2048
0
        icc_luta2bparts.clut->clut_dims[0] = 2;
2049
0
        icc_luta2bparts.clut->clut_num_input = 1;
2050
0
        icc_luta2bparts.clut->clut_num_output = 3;
2051
0
        icc_luta2bparts.clut->clut_word_width = 2;
2052
0
        gsicc_create_initialize_clut(icc_luta2bparts.clut);
2053
        /* 2 grid points 3 outputs */
2054
0
        icc_luta2bparts.clut->data_short = (unsigned short*)
2055
0
                    gs_alloc_bytes(memory, 2 * 3 * sizeof(short),
2056
0
                   "gsicc_create_froma");
2057
0
        if (icc_luta2bparts.clut->data_short == NULL) {
2058
0
            gs_free_object(memory, icc_luta2bparts.a_curves, "gsicc_create_froma");
2059
0
            gs_free_object(memory, icc_luta2bparts.m_curves, "gsicc_create_froma");
2060
0
            gs_free_object(memory, icc_luta2bparts.clut, "gsicc_create_froma");
2061
0
            return gs_throw(gs_error_VMerror, "Allocation of ICC clut data failed");
2062
0
        }
2063
        /*  Studies of CIEBasedA spaces
2064
            and AR rendering of these reveals that they only look
2065
            at the product sum of the MatrixA and the 2nd column of
2066
            the LM Matrix (if there is one).  This is used as a Y
2067
            decode value from which to map between the black point
2068
            and the white point.  The black point is actually ignored
2069
            and a black point of 0 is used. */
2070
0
        gsicc_vec_to_mlut(&(pcie->MatrixA), icc_luta2bparts.clut->data_short);
2071
0
        cie_matrix_transpose3(&(pcie->common.MatrixLMN), &matrix_input);
2072
        /* Encoding to ICC range happens in create_lutAtoBprofile */
2073
0
        icc_luta2bparts.matrix = &matrix_input;
2074
0
        icc_luta2bparts.num_in = 1;
2075
0
        icc_luta2bparts.num_out = 3;
2076
        /* Create the profile */
2077
        /* Note Adobe only looks at the Y value for CIEBasedA spaces.
2078
           we will do the same */
2079
0
        code = create_lutAtoBprofile(pp_buffer_in, header, &icc_luta2bparts, true,
2080
0
                                     false, memory);
2081
0
        if (code < 0)
2082
0
            return gs_rethrow(code, "Failed to create ICC AtoB Profile");
2083
0
    }
2084
0
    *profile_size_out = header->size;
2085
0
    gsicc_create_free_luta2bpart(memory, &icc_luta2bparts);
2086
#if SAVEICCPROFILE
2087
    /* Dump the buffer to a file for testing if its a valid ICC profile */
2088
    if(debug_catch)
2089
        save_profile(memory,*pp_buffer_in,"froma",header->size);
2090
#endif
2091
0
    return 0;
2092
0
}
2093
2094
/* Common code shared by def and defg generation */
2095
static int
2096
gsicc_create_defg_common(gs_cie_abc *pcie, gsicc_lutatob *icc_luta2bparts,
2097
                         bool has_lmn_procs, bool has_abc_procs,
2098
                         icHeader *header, gx_color_lookup_table *Table,
2099
                         const gs_color_space *pcs, gs_range *ranges,
2100
                         unsigned char **pp_buffer_in, int *profile_size_out,
2101
                         gs_memory_t* memory)
2102
0
{
2103
0
    gs_matrix3 matrix_input_trans;
2104
0
    int k;
2105
0
    bool input_range_ok;
2106
0
    int code;
2107
2108
0
    gsicc_matrix_init(&(pcie->common.MatrixLMN));  /* Need this set now */
2109
0
    gsicc_matrix_init(&(pcie->MatrixABC));          /* Need this set now */
2110
0
    setheader_common(header, 4);
2111
2112
    /* We will use an input type class which keeps us from having to
2113
       create an inverse.  We will keep the data a generic 3 color.
2114
       Since we are doing PS color management the PCS is XYZ */
2115
0
    header->deviceClass = icSigInputClass;
2116
0
    header->pcs = icSigXYZData;
2117
0
    icc_luta2bparts->num_out = 3;
2118
0
    icc_luta2bparts->white_point = &(pcie->common.points.WhitePoint);
2119
0
    icc_luta2bparts->black_point = &(pcie->common.points.BlackPoint);
2120
2121
    /* Calculate the chromatic adaptation matrix */
2122
0
    code = gsicc_compute_cam(icc_luta2bparts, memory);
2123
0
    if (code < 0) {
2124
0
        return gs_rethrow(code, "Create ICC from CIEABC failed");
2125
0
    }
2126
2127
    /* question now is, can we keep the table as it is, or do we need to merge
2128
     some of the def(g) parts.  Some merging or operators into the table must occur
2129
     if we have MatrixABC, LMN Decode and Matrix LMN, otherwise we can encode
2130
     the table directly and squash the rest into the curves matrix curve portion
2131
     of the ICC form */
2132
0
    if ( (!(pcie->MatrixABC.is_identity) && has_lmn_procs &&
2133
0
                   !(pcie->common.MatrixLMN.is_identity)) || 1 ) {
2134
        /* Table must take over some of the other elements. We are going to
2135
           go to a 16 bit table in this case.  For now, we are going to
2136
           mash all the elements in the table.  We may want to revisit this later. */
2137
        /* We must complete the defg or def decode function such that it is within
2138
           the HIJ(K) range AND is scaled to index into the CLUT properly */
2139
0
        if (gs_color_space_get_index(pcs) == gs_color_space_index_CIEDEF) {
2140
0
            input_range_ok = check_range(&(pcs->params.def->RangeDEF.ranges[0]),3);
2141
0
        } else {
2142
0
            input_range_ok = check_range(&(pcs->params.defg->RangeDEFG.ranges[0]),4);
2143
0
        }
2144
0
        code = gsicc_create_mashed_clut(icc_luta2bparts, header, Table,
2145
0
                            pcs, ranges, pp_buffer_in, profile_size_out,
2146
0
                            !input_range_ok, memory);
2147
0
        if (code < 0)
2148
0
            return gs_rethrow(code, "Failed to create ICC clut");
2149
0
    } else {
2150
        /* Table can stay as is. Handle the ABC/LMN portions via the curves
2151
           matrix curves operation */
2152
0
        icc_luta2bparts->matrix = &matrix_input_trans;
2153
0
        code = gsicc_create_abc_merge(icc_luta2bparts, &(pcie->common.MatrixLMN),
2154
0
                                &(pcie->MatrixABC), has_abc_procs,
2155
0
                                has_lmn_procs, pcie->caches.DecodeABC.caches,
2156
0
                                pcie->common.caches.DecodeLMN, memory);
2157
0
        if (code < 0)
2158
0
            return gs_rethrow(code, "Failed to create ICC abc merge");
2159
2160
        /* Get the table data */
2161
0
        icc_luta2bparts->clut = (gsicc_clut*) gs_alloc_bytes(memory,
2162
0
                            sizeof(gsicc_clut),"gsicc_create_defg_common");
2163
0
        if (icc_luta2bparts->clut == NULL)
2164
0
            return gs_throw(gs_error_VMerror, "Allocation of ICC clut failed");
2165
2166
0
        for (k = 0; k < icc_luta2bparts->num_in; k++) {
2167
0
            icc_luta2bparts->clut->clut_dims[k] = Table->dims[k];
2168
0
        }
2169
0
        icc_luta2bparts->clut->clut_num_input = icc_luta2bparts->num_in;
2170
0
        icc_luta2bparts->clut->clut_num_output = 3;
2171
0
        icc_luta2bparts->clut->clut_word_width = 1;
2172
0
        gsicc_create_initialize_clut(icc_luta2bparts->clut);
2173
        /* Get the PS table data directly */
2174
0
        icc_luta2bparts->clut->data_byte = (byte*) Table->table->data;
2175
        /* Create the profile. */
2176
0
        code = create_lutAtoBprofile(pp_buffer_in, header, icc_luta2bparts, false,
2177
0
                                     false, memory);
2178
0
        if (code < 0)
2179
0
            return gs_rethrow(code, "Failed to create ICC lutAtoB");
2180
0
    }
2181
0
    gsicc_create_free_luta2bpart(memory, icc_luta2bparts);
2182
0
    *profile_size_out = header->size;
2183
0
    return 0;
2184
0
}
2185
2186
/* If we have an ABC matrix, a DecodeLMN and an LMN matrix we have to mash
2187
   together the table, Decode ABC (if present) and ABC matrix. */
2188
int
2189
gsicc_create_fromdefg(const gs_color_space *pcs, unsigned char **pp_buffer_in,
2190
                      int *profile_size_out, gs_memory_t *memory,
2191
                      gx_cie_vector_cache *abc_caches,
2192
                      gx_cie_scalar_cache *lmn_caches,
2193
                      gx_cie_scalar_cache *defg_caches)
2194
0
{
2195
0
    gs_cie_defg *pcie = pcs->params.defg;
2196
0
    gsicc_lutatob icc_luta2bparts;
2197
0
    icProfile iccprofile;
2198
0
    icHeader  *header = &(iccprofile.header);
2199
#if SAVEICCPROFILE
2200
    int debug_catch = 1;
2201
#endif
2202
0
    float *curr_pos;
2203
0
    bool has_abc_procs = !((abc_caches->floats.params.is_identity &&
2204
0
                         (abc_caches)[1].floats.params.is_identity &&
2205
0
                         (abc_caches)[2].floats.params.is_identity));
2206
0
    bool has_lmn_procs = !((lmn_caches->floats.params.is_identity &&
2207
0
                         (lmn_caches)[1].floats.params.is_identity &&
2208
0
                         (lmn_caches)[2].floats.params.is_identity));
2209
0
    bool has_defg_procs = !((defg_caches->floats.params.is_identity &&
2210
0
                         (defg_caches)[1].floats.params.is_identity &&
2211
0
                         (defg_caches)[2].floats.params.is_identity &&
2212
0
                         (defg_caches)[3].floats.params.is_identity));
2213
0
    int code;
2214
2215
    /* Fill in the uncommon stuff */
2216
0
    gsicc_create_init_luta2bpart(&icc_luta2bparts);
2217
0
    header->colorSpace = icSigCmykData;
2218
0
    icc_luta2bparts.num_in = 4;
2219
2220
    /* The a curves stored as def procs */
2221
0
    if (has_defg_procs) {
2222
0
        icc_luta2bparts.a_curves = (float*) gs_alloc_bytes(memory,
2223
0
            4*CURVE_SIZE*sizeof(float),"gsicc_create_fromdefg");
2224
0
        if (icc_luta2bparts.a_curves == NULL)
2225
0
            return gs_throw(gs_error_VMerror, "Allocation of ICC a curves failed");
2226
0
        curr_pos = icc_luta2bparts.a_curves;
2227
0
        memcpy(curr_pos,&(pcie->caches_defg.DecodeDEFG->floats.values[0]),
2228
0
                CURVE_SIZE*sizeof(float));
2229
0
        curr_pos += CURVE_SIZE;
2230
0
        memcpy(curr_pos,&((pcie->caches_defg.DecodeDEFG[1]).floats.values[0]),
2231
0
                CURVE_SIZE*sizeof(float));
2232
0
        curr_pos += CURVE_SIZE;
2233
0
        memcpy(curr_pos,&((pcie->caches_defg.DecodeDEFG[2]).floats.values[0]),
2234
0
                CURVE_SIZE*sizeof(float));
2235
0
        curr_pos += CURVE_SIZE;
2236
0
        memcpy(curr_pos,&((pcie->caches_defg.DecodeDEFG[3]).floats.values[0]),
2237
0
                CURVE_SIZE*sizeof(float));
2238
0
    }
2239
    /* Note the recast.  Should be OK since we only access common stuff in there */
2240
0
    code = gsicc_create_defg_common((gs_cie_abc*) pcie, &icc_luta2bparts,
2241
0
                                    has_lmn_procs, has_abc_procs,
2242
0
                                    header, &(pcie->Table), pcs,
2243
0
                                    &(pcie->RangeDEFG.ranges[0]),
2244
0
                                    pp_buffer_in, profile_size_out, memory);
2245
#if SAVEICCPROFILE
2246
    /* Dump the buffer to a file for testing if its a valid ICC profile */
2247
    if(debug_catch)
2248
        save_profile(memory,*pp_buffer_in,"fromdefg",header->size);
2249
#endif
2250
0
    return code;
2251
0
}
2252
2253
int
2254
gsicc_create_fromdef(const gs_color_space *pcs, unsigned char **pp_buffer_in,
2255
                     int *profile_size_out, gs_memory_t *memory,
2256
                     gx_cie_vector_cache *abc_caches,
2257
                     gx_cie_scalar_cache *lmn_caches,
2258
                     gx_cie_scalar_cache *def_caches)
2259
0
{
2260
0
    gs_cie_def *pcie = pcs->params.def;
2261
0
    gsicc_lutatob icc_luta2bparts;
2262
0
    icProfile iccprofile;
2263
0
    icHeader  *header = &(iccprofile.header);
2264
#if SAVEICCPROFILE
2265
    int debug_catch = 1;
2266
#endif
2267
0
    float *curr_pos;
2268
0
    bool has_abc_procs = !((abc_caches->floats.params.is_identity &&
2269
0
                         (abc_caches)[1].floats.params.is_identity &&
2270
0
                         (abc_caches)[2].floats.params.is_identity));
2271
0
    bool has_lmn_procs = !((lmn_caches->floats.params.is_identity &&
2272
0
                         (lmn_caches)[1].floats.params.is_identity &&
2273
0
                         (lmn_caches)[2].floats.params.is_identity));
2274
0
    bool has_def_procs = !((def_caches->floats.params.is_identity &&
2275
0
                         (def_caches)[1].floats.params.is_identity &&
2276
0
                         (def_caches)[2].floats.params.is_identity));
2277
0
    int code;
2278
2279
0
    gsicc_create_init_luta2bpart(&icc_luta2bparts);
2280
2281
0
    header->colorSpace = icSigRgbData;
2282
0
    icc_luta2bparts.num_in = 3;
2283
2284
    /* The a curves stored as def procs */
2285
0
    if (has_def_procs) {
2286
0
        icc_luta2bparts.a_curves = (float*) gs_alloc_bytes(memory,
2287
0
                        3*CURVE_SIZE*sizeof(float),"gsicc_create_fromdef");
2288
0
        if (icc_luta2bparts.a_curves == NULL)
2289
0
            return gs_throw(gs_error_VMerror, "Allocation of ICC a curves failed");
2290
0
        curr_pos = icc_luta2bparts.a_curves;
2291
0
        memcpy(curr_pos,&(pcie->caches_def.DecodeDEF->floats.values[0]),
2292
0
                CURVE_SIZE*sizeof(float));
2293
0
        curr_pos += CURVE_SIZE;
2294
0
        memcpy(curr_pos,&((pcie->caches_def.DecodeDEF[1]).floats.values[0]),
2295
0
                CURVE_SIZE*sizeof(float));
2296
0
        curr_pos += CURVE_SIZE;
2297
0
        memcpy(curr_pos,&((pcie->caches_def.DecodeDEF[2]).floats.values[0]),
2298
0
                CURVE_SIZE*sizeof(float));
2299
0
    }
2300
0
    code = gsicc_create_defg_common((gs_cie_abc*) pcie, &icc_luta2bparts,
2301
0
                                    has_lmn_procs, has_abc_procs, header,
2302
0
                                    &(pcie->Table), pcs, &(pcie->RangeDEF.ranges[0]),
2303
0
                                    pp_buffer_in, profile_size_out, memory);
2304
#if SAVEICCPROFILE
2305
    /* Dump the buffer to a file for testing if its a valid ICC profile */
2306
    if(debug_catch)
2307
        save_profile(memory,*pp_buffer_in,"fromdef",header->size);
2308
#endif
2309
0
    return code;
2310
0
}
2311
2312
void
2313
gsicc_create_fromcrd(unsigned char *buffer, gs_memory_t *memory)
2314
0
{
2315
0
    icProfile iccprofile;
2316
0
    icHeader  *header = &(iccprofile.header);
2317
2318
0
    setheader_common(header, 4);
2319
0
}
2320
2321
/* V2 creation from current profile */
2322
2323
0
#define TRC_V2_SIZE 256
2324
2325
static void
2326
init_common_tagsv2(gsicc_tag tag_list[], int num_tags, int *last_tag)
2327
0
{
2328
    /*    profileDescriptionTag copyrightTag  */
2329
0
    int curr_tag, temp_size;
2330
2331
0
    if (*last_tag < 0)
2332
0
        curr_tag = 0;
2333
0
    else
2334
0
        curr_tag = (*last_tag) + 1;
2335
2336
0
    tag_list[curr_tag].offset = HEADER_SIZE + num_tags * TAG_SIZE + 4;
2337
0
    tag_list[curr_tag].sig = icSigProfileDescriptionTag;
2338
0
    temp_size = DATATYPE_SIZE + 4 + strlen(desc_name) + 1 + 12 + 67;
2339
0
    tag_list[curr_tag].byte_padding = get_padding(temp_size);
2340
0
    tag_list[curr_tag].size = temp_size + tag_list[curr_tag].byte_padding;
2341
2342
0
    curr_tag++;
2343
2344
0
    tag_list[curr_tag].offset = tag_list[curr_tag - 1].offset +
2345
0
        tag_list[curr_tag - 1].size;
2346
0
    tag_list[curr_tag].sig = icSigCopyrightTag;
2347
0
    temp_size = DATATYPE_SIZE + strlen(copy_right) + 1;
2348
0
    tag_list[curr_tag].byte_padding = get_padding(temp_size);
2349
0
    tag_list[curr_tag].size = temp_size + tag_list[curr_tag].byte_padding;
2350
0
    *last_tag = curr_tag;
2351
0
}
2352
2353
static int
2354
getsize_lut16Type(int tablesize, int num_inputs, int num_outputs)
2355
0
{
2356
0
    int clutsize;
2357
2358
    /* Header (-8 as we already include the type later)
2359
       plus linear curves (2 points each of 2 bytes) */
2360
0
    int size = 52 - 8 + 4 * num_inputs + 4 * num_outputs;
2361
0
    clutsize = (int) pow(tablesize, num_inputs) * num_outputs * 2;
2362
0
    return size + clutsize;
2363
0
}
2364
2365
static int
2366
getsize_lut8Type(int tablesize, int num_inputs, int num_outputs)
2367
0
{
2368
0
    int clutsize;
2369
2370
    /* Header (-8 as we already include the type later)
2371
       plus linear curves (2 points each of 2 bytes) */
2372
0
    int size = 48 - 8 + 256 * num_inputs + 256 * num_outputs;
2373
0
    clutsize = (int)pow(tablesize, num_inputs) * num_outputs;
2374
0
    return size + clutsize;
2375
0
}
2376
2377
2378
static byte*
2379
write_v2_common_data(byte *buffer, int profile_size, icHeader *header,
2380
    gsicc_tag *tag_list, int num_tags, byte *mediawhitept)
2381
0
{
2382
0
    byte *curr_ptr = buffer;
2383
0
    int k;
2384
2385
    /* The header */
2386
0
    header->size = profile_size;
2387
0
    copy_header(curr_ptr, header);
2388
0
    curr_ptr += HEADER_SIZE;
2389
2390
    /* Tag table */
2391
0
    copy_tagtable(curr_ptr, tag_list, num_tags);
2392
0
    curr_ptr += TAG_SIZE*num_tags;
2393
0
    curr_ptr += 4;
2394
2395
    /* Common tags */
2396
0
    add_common_tag_data(curr_ptr, tag_list, 2);
2397
0
    for (k = 0; k< NUMBER_COMMON_TAGS; k++) {
2398
0
        curr_ptr += tag_list[k].size;
2399
0
    }
2400
2401
    /* Media white point. Get from current profile */
2402
0
    write_bigendian_4bytes(curr_ptr, icSigXYZType);
2403
0
    curr_ptr += 4;
2404
0
    memset(curr_ptr, 0, 4);
2405
0
    curr_ptr += 4;
2406
0
    memcpy(curr_ptr, mediawhitept, 12);
2407
0
    curr_ptr += 12;
2408
2409
0
    return curr_ptr;
2410
0
}
2411
2412
static gsicc_link_t*
2413
get_link(const gs_gstate *pgs, cmm_profile_t *src_profile,
2414
    cmm_profile_t *des_profile, gsicc_rendering_intents_t intent)
2415
0
{
2416
0
    gsicc_rendering_param_t rendering_params;
2417
2418
    /* Now the main colorants. Get them and the TRC data from using the link
2419
    between the source profile and the CIEXYZ profile */
2420
0
    rendering_params.black_point_comp = gsBLACKPTCOMP_OFF;
2421
0
    rendering_params.override_icc = false;
2422
0
    rendering_params.preserve_black = gsBLACKPRESERVE_OFF;
2423
0
    rendering_params.rendering_intent = intent;
2424
0
    rendering_params.cmm = gsCMM_DEFAULT;
2425
0
    return gsicc_get_link_profile(pgs, NULL, src_profile, des_profile,
2426
0
        &rendering_params, pgs->memory, false);
2427
0
}
2428
2429
static void
2430
get_colorant(int index, gsicc_link_t *link, icS15Fixed16Number XYZ_Data[])
2431
0
{
2432
0
    unsigned short des[3], src[3];
2433
0
    int k;
2434
2435
0
    src[0] = 0;
2436
0
    src[1] = 0;
2437
0
    src[2] = 0;
2438
0
    src[index] = 65535;
2439
0
    (link->procs.map_color)(NULL, link, &src, &des, 2);
2440
0
    for (k = 0; k < 3; k++) {
2441
0
        XYZ_Data[k] = double2XYZtype((float)des[k] / 65535.0);
2442
0
    }
2443
0
}
2444
2445
static void
2446
get_trc(int index, gsicc_link_t *link, float **htrc, int trc_size)
2447
0
{
2448
0
    unsigned short des[3], src[3];
2449
0
    float max;
2450
0
    float *ptrc = *htrc;
2451
0
    int k;
2452
2453
0
    src[0] = 0;
2454
0
    src[1] = 0;
2455
0
    src[2] = 0;
2456
2457
    /* First get the max value for Y on the range */
2458
0
    src[index] = 65535;
2459
0
    (link->procs.map_color)(NULL, link, &src, &des, 2);
2460
0
    max = des[1];
2461
2462
0
    for (k = 0; k < trc_size; k++) {
2463
0
        src[index] = (unsigned short)((double)k * (double)65535 / (double)(trc_size - 1));
2464
0
        (link->procs.map_color)(NULL, link, &src, &des, 2);
2465
        /* Use Y */
2466
0
        ptrc[k] = (float)des[1] / max;
2467
0
    }
2468
0
}
2469
2470
static void
2471
clean_lut(gsicc_clut *clut, gs_memory_t *memory)
2472
0
{
2473
0
    if (clut->clut_word_width == 2)
2474
0
        gs_free_object(memory, clut->data_short, "clean_lut");
2475
0
    else
2476
0
        gs_free_object(memory, clut->data_byte, "clean_lut");
2477
0
}
2478
2479
/* This is used for the A2B0 type table and B2A0. */
2480
static int
2481
create_clut_v2(gsicc_clut *clut, gsicc_link_t *link, int num_in,
2482
        int num_out, int table_size, gs_memory_t *memory, int bitdepth)
2483
0
{
2484
0
    unsigned short *input_samples, *indexptr;
2485
0
    unsigned short *ptr_short;
2486
0
    byte *ptr_byte;
2487
0
    int num_points, index;
2488
0
    unsigned short input[4], output[4];
2489
0
    int kk, j, i;
2490
2491
0
    clut->clut_num_input = num_in;
2492
0
    clut->clut_num_output = num_out;
2493
0
    clut->clut_word_width = bitdepth;
2494
0
    for (kk = 0; kk < num_in; kk++)
2495
0
        clut->clut_dims[kk] = table_size;
2496
0
    clut->clut_num_entries = (int) pow(table_size, num_in);
2497
0
    num_points = clut->clut_num_entries;
2498
0
    if (bitdepth == 2) {
2499
0
        clut->data_byte = NULL;
2500
0
        clut->data_short = (unsigned short*)gs_alloc_bytes(memory,
2501
0
                              (size_t)clut->clut_num_entries * num_out *
2502
0
                                                 sizeof(unsigned short),
2503
0
                              "create_clut_v2");
2504
0
        if (clut->data_short == NULL)
2505
0
            return -1;
2506
0
    } else {
2507
0
        clut->data_short = NULL;
2508
0
        clut->data_byte = (byte*)gs_alloc_bytes(memory,
2509
0
                               (size_t)clut->clut_num_entries * num_out,
2510
0
                               "create_clut_v2");
2511
0
        if (clut->data_byte == NULL)
2512
0
            return -1;
2513
0
    }
2514
2515
    /* Create the sample indices */
2516
0
    input_samples = (unsigned short*) gs_alloc_bytes(memory,
2517
0
        sizeof(unsigned short)*table_size, "create_clut_v2");
2518
0
    if (input_samples == NULL) {
2519
0
        return -1;
2520
0
    }
2521
0
    indexptr = input_samples;
2522
0
    for (j = 0; j < table_size; j++)
2523
0
        *indexptr++ = (unsigned short)(((double)j / (double)(table_size - 1)) * 65535.0);
2524
2525
    /* Now populate the table. Index 1 goes the slowest (e.g. R) */
2526
0
    ptr_short = clut->data_short;
2527
0
    ptr_byte = clut->data_byte;
2528
0
    for (i = 0; i < num_points; i++) {
2529
0
        if (num_in == 1) {
2530
0
            index = i%table_size;
2531
0
            input[0] = input_samples[index];
2532
0
        }
2533
0
        if (num_in == 3) {
2534
0
            index = i%table_size;
2535
0
            input[2] = input_samples[index];
2536
0
            index = (unsigned int)floor((float)i / (float)table_size) % table_size;
2537
0
            input[1] = input_samples[index];
2538
0
            index = (unsigned int)floor((float)i / (float)(table_size*
2539
0
                table_size)) % table_size;
2540
0
            input[0] = input_samples[index];
2541
0
        }
2542
0
        if (num_in == 4) {
2543
0
            index = i%table_size;
2544
0
            input[3] = input_samples[index];
2545
0
            index = (unsigned int)floor((float)i / (float)table_size) % table_size;
2546
0
            input[2] = input_samples[index];
2547
0
            index = (unsigned int)floor((float)i / (float)(table_size*
2548
0
                table_size)) % table_size;
2549
0
            input[1] = input_samples[index];
2550
0
            index = (unsigned int)floor((float)i / (float)(table_size*
2551
0
                table_size*table_size)) % table_size;
2552
0
            input[0] = input_samples[index];
2553
0
        }
2554
0
        if (link == NULL) {
2555
            /* gamut table case */
2556
0
            for (j = 0; j < num_out; j++) {
2557
0
                if (bitdepth == 2)
2558
0
                    *ptr_short++ = 1;
2559
0
                else
2560
0
                    *ptr_byte++ = 1;
2561
0
            }
2562
0
        } else {
2563
0
            double temp;
2564
0
            (link->procs.map_color)(NULL, link, input, output, 2);
2565
2566
            /* Note.  We are using 16 bit for the forward table
2567
               (colorant to lab) and 8 bit for the backward table
2568
               (lab to colorant).  A larger table is used for the backward
2569
               table to reduce quantization */
2570
2571
0
            if (bitdepth == 2) {
2572
                /* Output is LAB 16 bit */
2573
                /* Apply offset of 128 on a and b */
2574
0
                output[1] = output[1] - 128;
2575
0
                output[2] = output[2] - 128;
2576
                /* Scale L to range 0 to 0xFF00 */
2577
0
                temp = (double)output[0] / 65535.0;
2578
0
                temp = temp * 65280.0;
2579
0
                output[0] = (unsigned short)temp;
2580
0
                for (j = 0; j < num_out; j++)
2581
0
                    *ptr_short++ = output[j];
2582
0
            } else {
2583
                /* Output is colorant and 8 bit */
2584
0
                for (j = 0; j < num_out; j++) {
2585
0
                    double temp = (double)output[j] * 255.0 / 65535.0;
2586
0
                    *ptr_byte++ = (byte) temp;
2587
0
                }
2588
0
            }
2589
0
        }
2590
0
    }
2591
0
    gs_free_object(memory, input_samples, "create_clut_v2");
2592
0
    return 0;
2593
0
}
2594
2595
2596
/* Here we write out the lut16Type or lut8Type data V2. Curves are always linear,
2597
   matrix is the identity.  Table data is unique and could be a forward
2598
   or inverse table */
2599
static byte*
2600
add_lutType(byte *input_ptr, gsicc_clut *lut)
2601
0
{
2602
0
    byte *curr_ptr;
2603
0
    unsigned char numout = lut->clut_num_output;
2604
0
    unsigned char numin = lut->clut_num_input;
2605
0
    unsigned char tablesize = lut->clut_dims[0];
2606
0
    float ident[9] = { 1.0, 0, 0, 0, 1.0, 0, 0, 0, 1.0 };
2607
0
    int clut_size = lut->clut_num_entries * numout, k, j;
2608
2609
    /* Signature */
2610
0
    curr_ptr = input_ptr;
2611
0
    if (lut->clut_word_width == 2)
2612
0
        write_bigendian_4bytes(curr_ptr, icSigLut16Type);
2613
0
    else
2614
0
        write_bigendian_4bytes(curr_ptr, icSigLut8Type);
2615
0
    curr_ptr += 4;
2616
    /* Reserved */
2617
0
    memset(curr_ptr, 0, 4);
2618
0
    curr_ptr += 4;
2619
    /* Sizes padded */
2620
0
    *curr_ptr++ = numin;
2621
0
    *curr_ptr++ = numout;
2622
0
    *curr_ptr++ = tablesize;
2623
0
    *curr_ptr++ = 0;
2624
2625
    /* Now the identity matrix */
2626
0
    add_matrixwithbias(curr_ptr, &(ident[0]), false);
2627
0
    curr_ptr += (9 * 4);
2628
2629
    /* Input TRC are linear.  16 bit can have 2 points. 8 bit need 256 */
2630
0
    if (lut->clut_word_width == 2) {
2631
        /* Sizes */
2632
0
        write_bigendian_2bytes(curr_ptr, 2);
2633
0
        curr_ptr += 2;
2634
0
        write_bigendian_2bytes(curr_ptr, 2);
2635
0
        curr_ptr += 2;
2636
2637
        /* Input table data. Linear. */
2638
0
        for (k = 0; k < numin; k++) {
2639
0
            write_bigendian_2bytes(curr_ptr, 0);
2640
0
            curr_ptr += 2;
2641
0
            write_bigendian_2bytes(curr_ptr, 65535);
2642
0
            curr_ptr += 2;
2643
0
        }
2644
0
    } else {
2645
        /* Input table data. Linear. */
2646
0
        for (k = 0; k < numin; k++)
2647
0
            for (j = 0; j < 256; j++)
2648
0
                *curr_ptr++ = j;
2649
0
    }
2650
2651
    /* The CLUT. Write out each entry. */
2652
0
    if (lut->clut_word_width == 2) {
2653
0
        for (k = 0; k < clut_size; k++) {
2654
0
            write_bigendian_2bytes(curr_ptr, lut->data_short[k]);
2655
0
            curr_ptr += 2;
2656
0
        }
2657
0
    } else {
2658
0
        for (k = 0; k < clut_size; k++)
2659
0
            *curr_ptr++ = lut->data_byte[k];
2660
0
    }
2661
2662
    /* Output table data. Linear. */
2663
0
    if (lut->clut_word_width == 2) {
2664
0
        for (k = 0; k < numout; k++) {
2665
0
            write_bigendian_2bytes(curr_ptr, 0);
2666
0
            curr_ptr += 2;
2667
0
            write_bigendian_2bytes(curr_ptr, 65535);
2668
0
            curr_ptr += 2;
2669
0
        }
2670
0
    } else {
2671
0
        for (k = 0; k < numout; k++)
2672
0
            for (j = 0; j < 256; j++)
2673
0
                *curr_ptr++ = j;
2674
0
    }
2675
0
    return curr_ptr;
2676
0
}
2677
2678
static int
2679
create_write_table_intent(const gs_gstate *pgs, gsicc_rendering_intents_t intent,
2680
        cmm_profile_t *src_profile, cmm_profile_t *des_profile, byte *curr_ptr,
2681
        int table_size, int bit_depth, int padding)
2682
0
{
2683
0
    gsicc_link_t *link;
2684
0
    int code;
2685
0
    gsicc_clut clut;
2686
2687
0
    link = get_link(pgs, src_profile, des_profile, intent);
2688
0
    if (link == NULL)
2689
0
        return_error(gs_error_undefined);
2690
0
    code = create_clut_v2(&clut, link, src_profile->num_comps,
2691
0
        des_profile->num_comps, table_size, pgs->memory, bit_depth);
2692
0
    if (code < 0)
2693
0
        return code;
2694
0
    curr_ptr = add_lutType(curr_ptr, &clut);
2695
0
    memset(curr_ptr, 0, padding);
2696
0
    clean_lut(&clut, pgs->memory);
2697
0
    gsicc_release_link(link);
2698
0
    return 0;
2699
0
}
2700
2701
static void
2702
gsicc_create_v2input(const gs_gstate *pgs, icHeader *header, cmm_profile_t *src_profile,
2703
                byte *mediawhitept, cmm_profile_t *lab_profile)
2704
0
{
2705
    /* Need to create the forward table only (Gray, RGB, CMYK to LAB) */
2706
0
    int num_tags = 4; /* 2 common + white + A2B0 */
2707
0
    int profile_size = HEADER_SIZE;
2708
0
    gsicc_tag *tag_list;
2709
0
    gs_memory_t *memory = src_profile->memory;
2710
0
    int last_tag = -1;
2711
0
    byte *buffer, *curr_ptr;
2712
0
    gsicc_link_t *link;
2713
0
    int tag_size;
2714
0
    gsicc_clut clut;
2715
0
    int code, k;
2716
2717
    /* Profile description tag, copyright tag white point and grayTRC */
2718
0
    tag_list = (gsicc_tag*)gs_alloc_bytes(memory,
2719
0
        sizeof(gsicc_tag)*num_tags, "gsicc_create_v2input");
2720
0
    if (tag_list == NULL)
2721
0
        return;
2722
    /* Let us precompute the sizes of everything and all our offsets */
2723
0
    profile_size += TAG_SIZE * num_tags;
2724
0
    profile_size += 4; /* number of tags.... */
2725
2726
    /* Common tags */
2727
0
    init_common_tagsv2(tag_list, num_tags, &last_tag);
2728
0
    init_tag(tag_list, &last_tag, icSigMediaWhitePointTag, XYZPT_SIZE);
2729
2730
    /* Get the tag size of the A2B0 with the lut16Type */
2731
0
    tag_size = getsize_lut16Type(FORWARD_V2_TABLE_SIZE, src_profile->num_comps, 3);
2732
0
    init_tag(tag_list, &last_tag, icSigAToB0Tag, tag_size);
2733
2734
    /* Now get the profile size */
2735
0
    for (k = 0; k < num_tags; k++) {
2736
0
        profile_size += tag_list[k].size;
2737
0
    }
2738
2739
    /* Allocate buffer */
2740
0
    buffer = gs_alloc_bytes(memory, profile_size, "gsicc_create_v2input");
2741
0
    if (buffer == NULL) {
2742
0
        gs_free_object(memory, tag_list, "gsicc_create_v2input");
2743
0
        return;
2744
0
    }
2745
2746
    /* Write out data */
2747
0
    curr_ptr = write_v2_common_data(buffer, profile_size, header, tag_list,
2748
0
        num_tags, mediawhitept);
2749
2750
    /* Now the A2B0 Tag */
2751
0
    link = get_link(pgs, src_profile, lab_profile, gsPERCEPTUAL);
2752
0
    if (link == NULL) {
2753
0
        gs_free_object(memory, tag_list, "gsicc_create_v2input");
2754
0
        gs_free_object(memory, buffer, "gsicc_create_v2input");
2755
0
        return;
2756
0
    }
2757
2758
    /* First create the data */
2759
0
    code = create_clut_v2(&clut, link, src_profile->num_comps, 3,
2760
0
        FORWARD_V2_TABLE_SIZE, pgs->memory, 2);
2761
0
    if (code < 0) {
2762
0
        gs_free_object(memory, tag_list, "gsicc_create_v2input");
2763
0
        gs_free_object(memory, buffer, "gsicc_create_v2input");
2764
0
        return;
2765
0
    }
2766
2767
    /* Now write it out */
2768
0
    curr_ptr = add_lutType(curr_ptr, &clut);
2769
0
    memset(curr_ptr, 0, tag_list[last_tag].byte_padding);  /* padding */
2770
2771
    /* Clean up */
2772
0
    gsicc_release_link(link);
2773
0
    clean_lut(&clut, pgs->memory);
2774
0
    gs_free_object(memory, tag_list, "gsicc_create_v2input");
2775
    /* Save the v2 data */
2776
0
    src_profile->v2_data = buffer;
2777
0
    src_profile->v2_size = profile_size;
2778
2779
#if SAVEICCPROFILE
2780
    /* Dump the buffer to a file for testing if its a valid ICC profile */
2781
    save_profile(memory,buffer, "V2InputType", profile_size);
2782
#endif
2783
0
}
2784
2785
static void
2786
gsicc_create_v2output(const gs_gstate *pgs, icHeader *header, cmm_profile_t *src_profile,
2787
                byte *mediawhitept, cmm_profile_t *lab_profile)
2788
0
{
2789
    /* Need to create forward and backward table (Gray, RGB, CMYK to LAB and back)
2790
       and need to do this for all the intents */
2791
0
    int num_tags = 10; /* 2 common + white + A2B0 + B2A0 + A2B1 + B2A1 + A2B2 + B2A2 + gamut */
2792
0
    int profile_size = HEADER_SIZE;
2793
0
    gsicc_tag *tag_list;
2794
0
    gs_memory_t *memory = src_profile->memory;
2795
0
    int last_tag = -1;
2796
0
    byte *buffer, *curr_ptr;
2797
0
    int tag_location;
2798
0
    int tag_size;
2799
0
    gsicc_clut gamutlut;
2800
0
    int code, k;
2801
2802
    /* Profile description tag, copyright tag white point and grayTRC */
2803
0
    tag_list = (gsicc_tag*)gs_alloc_bytes(memory,
2804
0
        sizeof(gsicc_tag)*num_tags, "gsicc_create_v2output");
2805
0
    if (tag_list == NULL)
2806
0
        return;
2807
    /* Let us precompute the sizes of everything and all our offsets */
2808
0
    profile_size += TAG_SIZE * num_tags;
2809
0
    profile_size += 4; /* number of tags.... */
2810
2811
    /* Common tags */
2812
0
    init_common_tagsv2(tag_list, num_tags, &last_tag);
2813
0
    init_tag(tag_list, &last_tag, icSigMediaWhitePointTag, XYZPT_SIZE);
2814
2815
    /* Get the tag size of the cluts with the lut16Type */
2816
    /* Perceptual */
2817
0
    tag_size = getsize_lut16Type(FORWARD_V2_TABLE_SIZE, src_profile->num_comps, 3);
2818
0
    init_tag(tag_list, &last_tag, icSigAToB0Tag, tag_size);
2819
0
    tag_size = getsize_lut8Type(BACKWARD_V2_TABLE_SIZE, 3, src_profile->num_comps);
2820
0
    init_tag(tag_list, &last_tag, icSigBToA0Tag, tag_size);
2821
2822
    /* Relative Colorimetric */
2823
0
    tag_size = getsize_lut16Type(FORWARD_V2_TABLE_SIZE, src_profile->num_comps, 3);
2824
0
    init_tag(tag_list, &last_tag, icSigAToB1Tag, tag_size);
2825
0
    tag_size = getsize_lut8Type(BACKWARD_V2_TABLE_SIZE, 3, src_profile->num_comps);
2826
0
    init_tag(tag_list, &last_tag, icSigBToA1Tag, tag_size);
2827
2828
    /* Saturation */
2829
0
    tag_size = getsize_lut16Type(FORWARD_V2_TABLE_SIZE, src_profile->num_comps, 3);
2830
0
    init_tag(tag_list, &last_tag, icSigAToB2Tag, tag_size);
2831
0
    tag_size = getsize_lut8Type(BACKWARD_V2_TABLE_SIZE, 3, src_profile->num_comps);
2832
0
    init_tag(tag_list, &last_tag, icSigBToA2Tag, tag_size);
2833
2834
    /* And finally the Gamut Tag.  Since we can't determine gamut here this
2835
       is essentially a required place holder. Make it small */
2836
0
    tag_size = getsize_lut8Type(2, src_profile->num_comps, 1);
2837
0
    init_tag(tag_list, &last_tag, icSigGamutTag, tag_size);
2838
2839
    /* Now get the profile size */
2840
0
    for (k = 0; k < num_tags; k++) {
2841
0
        profile_size += tag_list[k].size;
2842
0
    }
2843
2844
    /* Allocate buffer */
2845
0
    buffer = gs_alloc_bytes(memory, profile_size, "gsicc_create_v2output");
2846
0
    if (buffer == NULL) {
2847
0
        gs_free_object(memory, tag_list, "gsicc_create_v2output");
2848
0
        return;
2849
0
    }
2850
2851
    /* Write out data */
2852
0
    curr_ptr = write_v2_common_data(buffer, profile_size, header, tag_list,
2853
0
        num_tags, mediawhitept);
2854
0
    tag_location = V2_COMMON_TAGS;
2855
2856
    /* A2B0 */
2857
0
    if (create_write_table_intent(pgs, gsPERCEPTUAL, src_profile, lab_profile,
2858
0
        curr_ptr, FORWARD_V2_TABLE_SIZE, 2,
2859
0
        tag_list[tag_location].byte_padding) < 0) {
2860
0
        gs_free_object(memory, tag_list, "gsicc_create_v2output");
2861
0
        return;
2862
0
    }
2863
0
    curr_ptr += tag_list[tag_location].size;
2864
0
    tag_location++;
2865
2866
    /* B2A0 */
2867
0
    if (create_write_table_intent(pgs, gsPERCEPTUAL, lab_profile, src_profile,
2868
0
        curr_ptr, BACKWARD_V2_TABLE_SIZE, 1,
2869
0
        tag_list[tag_location].byte_padding) < 0) {
2870
0
        gs_free_object(memory, tag_list, "gsicc_create_v2output");
2871
0
        return;
2872
0
    }
2873
0
    curr_ptr += tag_list[tag_location].size;
2874
0
    tag_location++;
2875
2876
    /* A2B1 */
2877
0
    if (create_write_table_intent(pgs, gsRELATIVECOLORIMETRIC, src_profile,
2878
0
        lab_profile, curr_ptr, FORWARD_V2_TABLE_SIZE, 2,
2879
0
        tag_list[tag_location].byte_padding) < 0) {
2880
0
        gs_free_object(memory, tag_list, "gsicc_create_v2output");
2881
0
        return;
2882
0
    }
2883
0
    curr_ptr += tag_list[tag_location].size;
2884
0
    tag_location++;
2885
2886
    /* B2A1 */
2887
0
    if (create_write_table_intent(pgs, gsRELATIVECOLORIMETRIC, lab_profile,
2888
0
        src_profile, curr_ptr, BACKWARD_V2_TABLE_SIZE, 1,
2889
0
        tag_list[tag_location].byte_padding) < 0) {
2890
0
        gs_free_object(memory, tag_list, "gsicc_create_v2output");
2891
0
        return;
2892
0
    }
2893
0
    curr_ptr += tag_list[tag_location].size;
2894
0
    tag_location++;
2895
2896
    /* A2B2 */
2897
0
    if (create_write_table_intent(pgs, gsSATURATION, src_profile, lab_profile,
2898
0
        curr_ptr, FORWARD_V2_TABLE_SIZE, 2,
2899
0
        tag_list[tag_location].byte_padding) < 0) {
2900
0
        gs_free_object(memory, tag_list, "gsicc_create_v2output");
2901
0
        return;
2902
0
    }
2903
0
    curr_ptr += tag_list[tag_location].size;
2904
0
    tag_location++;
2905
2906
    /* B2A2 */
2907
0
    if (create_write_table_intent(pgs, gsSATURATION, lab_profile, src_profile,
2908
0
        curr_ptr, BACKWARD_V2_TABLE_SIZE, 1,
2909
0
        tag_list[tag_location].byte_padding) < 0) {
2910
0
        gs_free_object(memory, tag_list, "gsicc_create_v2output");
2911
0
        return;
2912
0
    }
2913
0
    curr_ptr += tag_list[tag_location].size;
2914
0
    tag_location++;
2915
2916
    /* Gamut tag, which is bogus */
2917
0
    code = create_clut_v2(&gamutlut, NULL, src_profile->num_comps, 1, 2, pgs->memory, 1);
2918
0
    if (code < 0) {
2919
0
        gs_free_object(memory, tag_list, "gsicc_create_v2output");
2920
0
        return;
2921
0
    }
2922
2923
    /* Now write it out */
2924
0
    curr_ptr = add_lutType(curr_ptr, &gamutlut);
2925
0
    memset(curr_ptr, 0, tag_list[tag_location].byte_padding);
2926
2927
    /* Done */
2928
0
    gs_free_object(memory, tag_list, "gsicc_create_v2output");
2929
0
    clean_lut(&gamutlut, pgs->memory);
2930
2931
    /* Save the v2 data */
2932
0
    src_profile->v2_data = buffer;
2933
0
    src_profile->v2_size = profile_size;
2934
2935
#if SAVEICCPROFILE
2936
    /* Dump the buffer to a file for testing if its a valid ICC profile */
2937
    save_profile(memory,buffer, "V2OutputType", profile_size);
2938
#endif
2939
0
}
2940
2941
static void
2942
gsicc_create_v2displaygray(const gs_gstate *pgs, icHeader *header, cmm_profile_t *src_profile,
2943
            byte *mediawhitept, cmm_profile_t *xyz_profile)
2944
0
{
2945
0
    int num_tags = 4;
2946
0
    int profile_size = HEADER_SIZE;
2947
0
    gsicc_tag *tag_list;
2948
0
    gs_memory_t *memory = src_profile->memory;
2949
0
    int last_tag = -1;
2950
    /* 4 for name, 4 reserved, 4 for number entries, 2*num_entries */
2951
0
    int trc_tag_size = 12 + 2 * TRC_V2_SIZE;
2952
0
    byte *buffer, *curr_ptr;
2953
0
    unsigned short des[3], src;
2954
0
    float *trc;
2955
0
    int tag_location;
2956
0
    gsicc_link_t *link;
2957
0
    float max;
2958
0
    int k;
2959
2960
    /* Profile description tag, copyright tag white point and grayTRC */
2961
0
    tag_list = (gsicc_tag*)gs_alloc_bytes(memory,
2962
0
        sizeof(gsicc_tag)*num_tags, "gsicc_createv2display_gray");
2963
0
    if (tag_list == NULL)
2964
0
        return;
2965
    /* Let us precompute the sizes of everything and all our offsets */
2966
0
    profile_size += TAG_SIZE * num_tags;
2967
0
    profile_size += 4; /* number of tags.... */
2968
2969
    /* Common tags */
2970
0
    init_common_tagsv2(tag_list, num_tags, &last_tag);
2971
0
    init_tag(tag_list, &last_tag, icSigMediaWhitePointTag, XYZPT_SIZE);
2972
0
    init_tag(tag_list, &last_tag, icSigGrayTRCTag, trc_tag_size);
2973
2974
    /* Now get the profile size */
2975
0
    for (k = 0; k < num_tags; k++) {
2976
0
        profile_size += tag_list[k].size;
2977
0
    }
2978
    /* Allocate buffer */
2979
0
    buffer = gs_alloc_bytes(memory, profile_size, "gsicc_createv2display_gray");
2980
0
    if (buffer == NULL) {
2981
0
        gs_free_object(memory, tag_list, "gsicc_createv2display_gray");
2982
0
        return;
2983
0
    }
2984
2985
    /* Start writing out data to buffer */
2986
0
    curr_ptr = write_v2_common_data(buffer, profile_size, header, tag_list,
2987
0
        num_tags, mediawhitept);
2988
0
    tag_location = V2_COMMON_TAGS;
2989
2990
    /* Now the TRC. First collect the curve data and then write it out */
2991
    /* Get the link between our gray profile and XYZ profile */
2992
0
    link = get_link(pgs, src_profile, xyz_profile, gsPERCEPTUAL);
2993
0
    if (link == NULL) {
2994
0
        gs_free_object(memory, tag_list, "gsicc_createv2display_gray");
2995
0
        gs_free_object(memory, buffer, "gsicc_createv2display_gray");
2996
0
        return;
2997
0
    }
2998
2999
    /* First get the max value for Y on the range */
3000
0
    src = 65535;
3001
0
    (link->procs.map_color)(NULL, link, &src, &(des[0]), 2);
3002
0
    max = des[1];
3003
3004
0
    trc = (float*) gs_alloc_bytes(memory, TRC_V2_SIZE * sizeof(float), "gsicc_createv2display_gray");
3005
0
    if (trc == NULL) {
3006
0
        gsicc_release_link(link);
3007
0
        gs_free_object(memory, tag_list, "gsicc_createv2display_gray");
3008
0
        gs_free_object(memory, buffer, "gsicc_createv2display_gray");
3009
0
        return;
3010
0
    }
3011
0
    for (k = 0; k < TRC_V2_SIZE; k++) {
3012
0
        src = (unsigned short)((double)k * (double)65535 / (double)(TRC_V2_SIZE - 1));
3013
0
        (link->procs.map_color)(NULL, link, &src, &(des[0]), 2);
3014
0
        trc[k] = (float)des[1] / max;
3015
0
    }
3016
0
    add_curve(curr_ptr, trc, TRC_V2_SIZE);
3017
0
    curr_ptr += tag_list[tag_location].size;
3018
3019
    /* Clean up */
3020
0
    gsicc_release_link(link);
3021
0
    gs_free_object(memory, tag_list, "gsicc_createv2display_gray");
3022
0
    gs_free_object(memory, trc, "gsicc_createv2display_gray");
3023
    /* Save the v2 data */
3024
0
    src_profile->v2_data = buffer;
3025
0
    src_profile->v2_size = profile_size;
3026
3027
#if SAVEICCPROFILE
3028
    /* Dump the buffer to a file for testing if its a valid ICC profile */
3029
    save_profile(memory, buffer, "V2FromGray", profile_size);
3030
#endif
3031
0
}
3032
3033
static void
3034
gsicc_create_v2displayrgb(const gs_gstate *pgs, icHeader *header, cmm_profile_t *src_profile,
3035
        byte *mediawhitept, cmm_profile_t *xyz_profile)
3036
0
{
3037
0
    int num_tags = 9;
3038
0
    int profile_size = HEADER_SIZE;
3039
0
    gsicc_tag *tag_list;
3040
0
    gs_memory_t *memory = src_profile->memory;
3041
0
    int last_tag = -1;
3042
    /* 4 for name, 4 reserved, 4 for number entries, 2*num_entries */
3043
0
    int trc_tag_size = 12 + 2 * TRC_V2_SIZE;
3044
0
    byte *buffer, *curr_ptr;
3045
0
    float *trc;
3046
0
    int tag_location;
3047
0
    icS15Fixed16Number XYZ_Data[3];
3048
0
    gsicc_link_t *link;
3049
0
    int k;
3050
3051
    /* Profile description tag, copyright tag white point RGB colorants and
3052
       RGB TRCs */
3053
0
    tag_list = (gsicc_tag*)gs_alloc_bytes(memory,
3054
0
        sizeof(gsicc_tag)*num_tags, "gsicc_create_v2displayrgb");
3055
0
    if (tag_list == NULL)
3056
0
        return;
3057
    /* Let us precompute the sizes of everything and all our offsets */
3058
0
    profile_size += TAG_SIZE * num_tags;
3059
0
    profile_size += 4; /* number of tags.... */
3060
3061
    /* Common tags + white point + RGB colorants + RGB TRCs */
3062
0
    init_common_tagsv2(tag_list, num_tags, &last_tag);
3063
0
    init_tag(tag_list, &last_tag, icSigMediaWhitePointTag, XYZPT_SIZE);
3064
0
    init_tag(tag_list, &last_tag, icSigRedColorantTag, XYZPT_SIZE);
3065
0
    init_tag(tag_list, &last_tag, icSigGreenColorantTag, XYZPT_SIZE);
3066
0
    init_tag(tag_list, &last_tag, icSigBlueColorantTag, XYZPT_SIZE);
3067
0
    init_tag(tag_list, &last_tag, icSigRedTRCTag, trc_tag_size);
3068
0
    init_tag(tag_list, &last_tag, icSigGreenTRCTag, trc_tag_size);
3069
0
    init_tag(tag_list, &last_tag, icSigBlueTRCTag, trc_tag_size);
3070
3071
    /* Now get the profile size */
3072
0
    for (k = 0; k < num_tags; k++) {
3073
0
        profile_size += tag_list[k].size;
3074
0
    }
3075
3076
    /* Allocate buffer */
3077
0
    buffer = gs_alloc_bytes(memory, profile_size, "gsicc_create_v2displayrgb");
3078
0
    if (buffer == NULL) {
3079
0
        gs_free_object(memory, tag_list, "gsicc_create_v2displayrgb");
3080
0
        return;
3081
0
    }
3082
3083
    /* Start writing out data to buffer */
3084
0
    curr_ptr = write_v2_common_data(buffer, profile_size, header, tag_list,
3085
0
        num_tags, mediawhitept);
3086
0
    tag_location = V2_COMMON_TAGS;
3087
3088
    /* Now the main colorants. Get them and the TRC data from using the link
3089
        between the source profile and the CIEXYZ profile */
3090
0
    link = get_link(pgs, src_profile, xyz_profile, gsPERCEPTUAL);
3091
0
    if (link == NULL) {
3092
0
        gs_free_object(memory, tag_list, "gsicc_create_v2displayrgb");
3093
0
        gs_free_object(memory, buffer, "gsicc_create_v2displayrgb");
3094
0
        return;
3095
0
    }
3096
3097
    /* Get the Red, Green and Blue colorants */
3098
0
    for (k = 0; k < 3; k++) {
3099
0
        get_colorant(k, link, XYZ_Data);
3100
0
        add_xyzdata(curr_ptr, XYZ_Data);
3101
0
        curr_ptr += tag_list[tag_location].size;
3102
0
        tag_location++;
3103
0
    }
3104
3105
    /* Now the TRCs */
3106
0
    trc = (float*) gs_alloc_bytes(memory, TRC_V2_SIZE * sizeof(float), "gsicc_create_v2displayrgb");
3107
0
    if (trc == NULL) {
3108
0
        gsicc_release_link(link);
3109
0
        gs_free_object(memory, tag_list, "gsicc_create_v2displayrgb");
3110
0
        gs_free_object(memory, buffer, "gsicc_create_v2displayrgb");
3111
0
        return;
3112
0
    }
3113
3114
0
    for (k = 0; k < 3; k++) {
3115
0
        get_trc(k, link, &trc, TRC_V2_SIZE);
3116
0
        add_curve(curr_ptr, trc, TRC_V2_SIZE);
3117
0
        curr_ptr += tag_list[tag_location].size;
3118
0
    }
3119
3120
    /* Clean up */
3121
0
    gsicc_release_link(link);
3122
0
    gs_free_object(memory, tag_list, "gsicc_create_v2displayrgb");
3123
0
    gs_free_object(memory, trc, "gsicc_create_v2displayrgb");
3124
    /* Save the v2 data */
3125
0
    src_profile->v2_data = buffer;
3126
0
    src_profile->v2_size = profile_size;
3127
3128
#if SAVEICCPROFILE
3129
    /* Dump the buffer to a file for testing if its a valid ICC profile */
3130
    save_profile(memory,buffer, "V2FromRGB", profile_size);
3131
#endif
3132
0
}
3133
3134
static void
3135
gsicc_create_v2display(const gs_gstate *pgs, icHeader *header, cmm_profile_t *src_profile,
3136
                    byte *mediawhitept, cmm_profile_t *xyz_profile)
3137
0
{
3138
    /* Need to create matrix with the TRCs.  Have to worry about gray
3139
       and RGB cases. */
3140
0
    if (header->colorSpace == icSigGrayData)
3141
0
        gsicc_create_v2displaygray(pgs, header, src_profile, mediawhitept, xyz_profile);
3142
0
    else
3143
0
        gsicc_create_v2displayrgb(pgs, header, src_profile, mediawhitept, xyz_profile);
3144
0
}
3145
3146
static int
3147
readint32(byte *buff)
3148
92
{
3149
92
    int out = 0;
3150
92
    byte *ptr = buff;
3151
92
    int k;
3152
3153
460
    for (k = 0; k < 4; k++) {
3154
368
        int temp = ptr[k];
3155
368
        int shift = (3 - k) * 8;
3156
368
        out += temp << shift;
3157
368
    }
3158
92
    return out;
3159
92
}
3160
3161
/* Create special profile for going to/from CIEXYZ color space.  We will use
3162
   this with lcms and the current profile to construct the structures in
3163
   a new V2 profile */
3164
static int
3165
get_xyzprofile(cmm_profile_t *xyz_profile)
3166
0
{
3167
0
    icProfile iccprofile;
3168
0
    icHeader *header = &(iccprofile.header);
3169
0
    int num_tags = 9;  /* common (2) + rXYZ,gXYZ,bXYZ,rTRC,gTRC,bTRC,wtpt */
3170
0
    int profile_size = HEADER_SIZE;
3171
0
    gsicc_tag *tag_list;
3172
0
    int last_tag = -1;
3173
    /* 4 for name, 4 reserved, 4 for number entries. 0 entries implies linear */
3174
0
    int trc_tag_size = 12;
3175
0
    byte *buffer, *curr_ptr, *tempptr;
3176
0
    int tag_location;
3177
0
    gs_memory_t *memory = xyz_profile->memory;
3178
0
    icS15Fixed16Number temp_XYZ[3];
3179
0
    byte mediawhitept[12];
3180
0
    icS15Fixed16Number one, zero;
3181
0
    int k, j;
3182
0
    int code;
3183
3184
    /* Fill in the common stuff */
3185
0
    setheader_common(header, 2);
3186
    /* If we have to create a table we will do it in XYZ.  If it is a matrix,
3187
    it is still XYZ */
3188
0
    header->pcs = icSigXYZData;
3189
0
    header->colorSpace = icSigRgbData;
3190
0
    header->deviceClass = icSigDisplayClass;
3191
3192
    /* Profile description tag, copyright tag white point and grayTRC */
3193
0
    tag_list = (gsicc_tag*)gs_alloc_bytes(memory,
3194
0
        sizeof(gsicc_tag) * num_tags, "get_xyzprofile");
3195
0
    if (tag_list == NULL)
3196
0
        return -1;
3197
    /* Let us precompute the sizes of everything and all our offsets */
3198
0
    profile_size += TAG_SIZE * num_tags;
3199
0
    profile_size += 4; /* number of tags.... */
3200
3201
    /* Common tags + white point + RGB colorants + RGB TRCs */
3202
0
    init_common_tagsv2(tag_list, num_tags, &last_tag);
3203
0
    init_tag(tag_list, &last_tag, icSigMediaWhitePointTag, XYZPT_SIZE);
3204
0
    init_tag(tag_list, &last_tag, icSigRedColorantTag, XYZPT_SIZE);
3205
0
    init_tag(tag_list, &last_tag, icSigGreenColorantTag, XYZPT_SIZE);
3206
0
    init_tag(tag_list, &last_tag, icSigBlueColorantTag, XYZPT_SIZE);
3207
0
    init_tag(tag_list, &last_tag, icSigRedTRCTag, trc_tag_size);
3208
0
    init_tag(tag_list, &last_tag, icSigGreenTRCTag, trc_tag_size);
3209
0
    init_tag(tag_list, &last_tag, icSigBlueTRCTag, trc_tag_size);
3210
3211
    /* Now get the profile size */
3212
0
    for (k = 0; k < num_tags; k++) {
3213
0
        profile_size += tag_list[k].size;
3214
0
    }
3215
3216
    /* Allocate buffer */
3217
0
    buffer = gs_alloc_bytes(memory, profile_size, "get_xyzprofile");
3218
0
    if (buffer == NULL) {
3219
0
        gs_free_object(memory, tag_list, "get_xyzprofile");
3220
0
        return -1;
3221
0
    }
3222
3223
    /* Media white point for this profile is D50 */
3224
0
    get_D50(temp_XYZ); /* See Appendix D6 in spec */
3225
0
    tempptr = mediawhitept;
3226
0
    for (j = 0; j < 3; j++) {
3227
0
        write_bigendian_4bytes(tempptr, temp_XYZ[j]);
3228
0
        tempptr += 4;
3229
0
    }
3230
3231
    /* Start writing out data to buffer */
3232
0
    curr_ptr = write_v2_common_data(buffer, profile_size, header, tag_list,
3233
0
        num_tags, mediawhitept);
3234
0
    tag_location = V2_COMMON_TAGS;
3235
    /* Now lets add the Red Green and Blue colorant information */
3236
0
    one = double2XYZtype(1);
3237
0
    zero = double2XYZtype(0);
3238
3239
0
    temp_XYZ[0] = one;
3240
0
    temp_XYZ[1] = zero;
3241
0
    temp_XYZ[2] = zero;
3242
0
    add_xyzdata(curr_ptr, temp_XYZ);
3243
0
    curr_ptr += tag_list[tag_location].size;
3244
0
    tag_location++;
3245
3246
0
    temp_XYZ[0] = zero;
3247
0
    temp_XYZ[1] = one;
3248
0
    add_xyzdata(curr_ptr, temp_XYZ);
3249
0
    curr_ptr += tag_list[tag_location].size;
3250
0
    tag_location++;
3251
3252
0
    temp_XYZ[1] = zero;
3253
0
    temp_XYZ[2] = one;
3254
0
    add_xyzdata(curr_ptr, temp_XYZ);
3255
0
    curr_ptr += tag_list[tag_location].size;
3256
0
    tag_location++;
3257
3258
    /* And now the TRCs */
3259
0
    add_curve(curr_ptr, NULL, 0);
3260
0
    curr_ptr += tag_list[tag_location].size;
3261
0
    tag_location++;
3262
0
    add_curve(curr_ptr, NULL, 0);
3263
0
    curr_ptr += tag_list[tag_location].size;
3264
0
    tag_location++;
3265
0
    add_curve(curr_ptr, NULL, 0);
3266
3267
    /* Done */
3268
0
    gs_free_object(memory, tag_list, "get_xyzprofile");
3269
0
    xyz_profile->buffer = buffer;
3270
0
    xyz_profile->buffer_size = profile_size;
3271
0
    code = gsicc_init_profile_info(xyz_profile);
3272
#if SAVEICCPROFILE
3273
    /* Dump the buffer to a file for testing if its a valid ICC profile */
3274
    save_profile(memory,buffer, "XYZProfile", profile_size);
3275
#endif
3276
0
    return code;
3277
0
}
3278
3279
static bool
3280
get_mediawp(cmm_profile_t *src_profile, byte *mediawhitept)
3281
13
{
3282
13
    byte *buffer = &(src_profile->buffer[128]);
3283
13
    int num_tags = readint32(buffer);
3284
13
    int tag_signature = -1;
3285
13
    int offset;
3286
13
    int k;
3287
13
    int buffer_left = src_profile->buffer_size;
3288
3289
13
    if (buffer_left < 128)
3290
0
        return false;
3291
13
    buffer_left -= 128;
3292
3293
13
    if (buffer_left < 4)
3294
0
        return false;
3295
3296
13
    buffer += 4;
3297
13
    buffer_left -= 4;
3298
3299
    /* Get to the tag table */
3300
66
    for (k = 0; k < num_tags; k++) {
3301
66
        if (buffer_left < 12)
3302
0
            return false;
3303
3304
66
        tag_signature = readint32(buffer);
3305
66
        if (tag_signature == icSigMediaWhitePointTag)
3306
13
            break;
3307
53
        buffer += 12;
3308
53
        buffer_left -= 12;
3309
53
    }
3310
13
    if (tag_signature != icSigMediaWhitePointTag)
3311
0
        return false;
3312
3313
13
    if (buffer_left < 4)
3314
0
        return false;
3315
3316
13
    buffer += 4;
3317
13
    buffer_left -= 4;
3318
3319
13
    offset = readint32(buffer);
3320
3321
13
    if (buffer_left < offset + 8)
3322
13
        return false;
3323
3324
0
    buffer = &(src_profile->buffer[offset + 8]);  /* Add offset of 8 for XYZ tag and padding */
3325
0
    buffer_left = src_profile->buffer_size - (offset + 8);
3326
3327
    /* Data is already in the proper format. Just get the bytes */
3328
0
    if (buffer_left < 12)
3329
0
        return false;
3330
3331
0
    memcpy(mediawhitept, buffer, 12);
3332
0
    return true;
3333
0
}
3334
3335
static void
3336
gsicc_create_v2(const gs_gstate *pgs, cmm_profile_t *src_profile)
3337
13
{
3338
13
    icProfile iccprofile;
3339
13
    icHeader *header = &(iccprofile.header);
3340
13
    byte mediawhitept[12];
3341
13
    cmm_profile_t *xyz_profile;
3342
3343
13
    if (src_profile->v2_data != NULL)
3344
0
        return;
3345
3346
    /* Fill in the common stuff */
3347
13
    setheader_common(header, 2);
3348
3349
    /* Get the data_cs of current profile */
3350
13
    switch (src_profile->data_cs) {
3351
1
        case gsGRAY:
3352
1
            header->colorSpace = icSigGrayData;
3353
1
        break;
3354
12
        case gsRGB:
3355
12
            header->colorSpace = icSigRgbData;
3356
12
            break;
3357
0
        case gsCMYK:
3358
0
            header->colorSpace = icSigCmykData;
3359
0
            break;
3360
0
        default:
3361
#ifdef DEBUG
3362
            gs_warn("Failed in creating V2 ICC profile");
3363
#endif
3364
0
            return;
3365
0
            break;
3366
13
    }
3367
3368
    /* Use the deviceClass from the source profile. */
3369
13
    header->deviceClass = gsicc_get_device_class(src_profile);
3370
3371
    /* Unfortunately we have to get the media white point also. lcms wrapped
3372
       up the method internally when it went to release 2 so we will do our
3373
       own*/
3374
13
    if (!get_mediawp(src_profile, &(mediawhitept[0]))) {
3375
#ifdef DEBUG
3376
        gs_warn("Failed in creating V2 ICC profile");
3377
#endif
3378
13
        return;
3379
13
    }
3380
3381
    /* Also, we will want to create an XYZ ICC profile that we can use for
3382
       creating our data with lcms.  If already created, this profile is
3383
       stored in the manager */
3384
0
    if (pgs->icc_manager->xyz_profile != NULL) {
3385
0
        xyz_profile = pgs->icc_manager->xyz_profile;
3386
0
    } else {
3387
0
        xyz_profile = gsicc_profile_new(NULL, pgs->memory, NULL, 0);
3388
0
        if (xyz_profile == NULL) {
3389
#ifdef DEBUG
3390
            gs_warn("Failed in creating V2 ICC profile");
3391
#endif
3392
0
            return;
3393
0
        }
3394
0
        if (get_xyzprofile(xyz_profile) != 0) {
3395
#ifdef DEBUG
3396
            gs_warn("Failed in creating V2 ICC profile");
3397
#endif
3398
0
            return;
3399
0
        }
3400
0
        pgs->icc_manager->xyz_profile = xyz_profile;
3401
0
    }
3402
3403
    /* The type of stuff that we need to create */
3404
0
    switch (header->deviceClass) {
3405
0
        case icSigInputClass:
3406
0
            header->pcs = icSigLabData;
3407
0
            gsicc_create_v2input(pgs, header, src_profile, mediawhitept,
3408
0
                pgs->icc_manager->lab_profile);
3409
0
            break;
3410
0
        case icSigDisplayClass:
3411
0
            header->pcs = icSigXYZData;
3412
0
            gsicc_create_v2display(pgs, header, src_profile, mediawhitept,
3413
0
                xyz_profile);
3414
0
            break;
3415
0
        case icSigOutputClass:
3416
0
            header->pcs = icSigLabData;
3417
0
            gsicc_create_v2output(pgs, header, src_profile, mediawhitept,
3418
0
                pgs->icc_manager->lab_profile);
3419
0
            break;
3420
0
        default:
3421
#ifdef DEBUG
3422
            gs_warn("Failed in creating V2 ICC profile");
3423
#endif
3424
0
            return;
3425
0
            break;
3426
0
    }
3427
0
    return;
3428
0
}
3429
3430
/* While someone could create something that was not valid for now we will
3431
   just trust the version information in the header.  Allow anything with
3432
   major version 2 */
3433
static bool
3434
gsicc_create_isv2(cmm_profile_t *profile)
3435
1.47k
{
3436
1.47k
    if (profile->vers == ICCVERS_UNKNOWN) {
3437
1.47k
        int majorvers = profile->buffer[8];
3438
3439
1.47k
        if (majorvers == 2) {
3440
1.46k
            profile->vers = ICCVERS_2;
3441
1.46k
            return true;
3442
1.46k
        } else {
3443
13
            profile->vers = ICCVERS_NOT2;
3444
13
            return false;
3445
13
        }
3446
1.47k
    }
3447
0
    if (profile->vers == ICCVERS_2)
3448
0
        return true;
3449
0
    else
3450
0
        return false;
3451
0
}
3452
3453
byte*
3454
gsicc_create_getv2buffer(const gs_gstate *pgs, cmm_profile_t *srcprofile,
3455
                        int *size)
3456
1.47k
{
3457
1.47k
    if (gsicc_create_isv2(srcprofile)) {
3458
1.46k
        *size = srcprofile->buffer_size;
3459
1.46k
        return srcprofile->buffer;
3460
1.46k
    }
3461
3462
13
    if (srcprofile->profile_handle == NULL)
3463
0
        srcprofile->profile_handle =
3464
0
        gsicc_get_profile_handle_buffer(srcprofile->buffer,
3465
0
        srcprofile->buffer_size, pgs->memory);
3466
3467
    /* Need to create v2 profile */
3468
13
    gsicc_create_v2(pgs, srcprofile);
3469
3470
13
    *size = srcprofile->v2_size;
3471
13
    return srcprofile->v2_data;
3472
1.47k
}