comparison mupdf-source/source/fitz/color-icc-create.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 #include "mupdf/fitz.h"
2 #include "icc34.h"
3
4 #include <string.h>
5
6 #define SAVEICCPROFILE 0
7 #define ICC_HEADER_SIZE 128
8 #define ICC_TAG_SIZE 12
9 #define ICC_NUMBER_COMMON_TAGS 2
10 #define ICC_XYZPT_SIZE 12
11 #define ICC_DATATYPE_SIZE 8
12 #define D50_X 0.9642f
13 #define D50_Y 1.0f
14 #define D50_Z 0.8249f
15 static const char copy_right[] = "Copyright Artifex Software 2020";
16 #if SAVEICCPROFILE
17 unsigned int icc_debug_index = 0;
18 #endif
19
20 typedef struct
21 {
22 icTagSignature sig;
23 icUInt32Number offset;
24 icUInt32Number size;
25 unsigned char byte_padding;
26 } fz_icc_tag;
27
28 #if SAVEICCPROFILE
29 static void
30 save_profile(fz_context *ctx, fz_buffer *buf, const char *name)
31 {
32 char full_file_name[50];
33 fz_snprintf(full_file_name, sizeof full_file_name, "profile%d-%s.icc", icc_debug_index, name);
34 fz_save_buffer(ctx, buf, full_file_name);
35 icc_debug_index++;
36 }
37 #endif
38
39 static void
40 fz_append_byte_n(fz_context *ctx, fz_buffer *buf, int c, int n)
41 {
42 int k;
43 for (k = 0; k < n; k++)
44 fz_append_byte(ctx, buf, c);
45 }
46
47 static int
48 get_padding(int x)
49 {
50 return (4 - x % 4) % 4;
51 }
52
53 static void
54 setdatetime(fz_context *ctx, icDateTimeNumber *datetime)
55 {
56 datetime->day = 0;
57 datetime->hours = 0;
58 datetime->minutes = 0;
59 datetime->month = 0;
60 datetime->seconds = 0;
61 datetime->year = 0;
62 }
63
64 static void
65 add_gammadata(fz_context *ctx, fz_buffer *buf, unsigned short gamma, icTagTypeSignature curveType)
66 {
67 fz_append_int32_be(ctx, buf, curveType);
68 fz_append_byte_n(ctx, buf, 0, 4);
69
70 /* one entry for gamma */
71 fz_append_int32_be(ctx, buf, 1);
72
73 /* The encode (8frac8) gamma, with padding */
74 fz_append_int16_be(ctx, buf, gamma);
75
76 /* pad two bytes */
77 fz_append_byte_n(ctx, buf, 0, 2);
78 }
79
80 static unsigned short
81 float2u8Fixed8(fz_context *ctx, float number_in)
82 {
83 return (unsigned short)(number_in * 256);
84 }
85
86 static void
87 add_xyzdata(fz_context *ctx, fz_buffer *buf, icS15Fixed16Number temp_XYZ[])
88 {
89 int j;
90
91 fz_append_int32_be(ctx, buf, icSigXYZType);
92 fz_append_byte_n(ctx, buf, 0, 4);
93
94 for (j = 0; j < 3; j++)
95 fz_append_int32_be(ctx, buf, temp_XYZ[j]);
96 }
97
98 static icS15Fixed16Number
99 double2XYZtype(fz_context *ctx, float number_in)
100 {
101 short s;
102 unsigned short m;
103
104 if (number_in < 0)
105 number_in = 0;
106 s = (short)number_in;
107 m = (unsigned short)((number_in - s) * 65536);
108 return (icS15Fixed16Number) ((s << 16) | m);
109 }
110
111 static void
112 get_D50(fz_context *ctx, icS15Fixed16Number XYZ[])
113 {
114 XYZ[0] = double2XYZtype(ctx, D50_X);
115 XYZ[1] = double2XYZtype(ctx, D50_Y);
116 XYZ[2] = double2XYZtype(ctx, D50_Z);
117 }
118
119 static void
120 get_XYZ_doubletr(fz_context *ctx, icS15Fixed16Number XYZ[], float vector[])
121 {
122 XYZ[0] = double2XYZtype(ctx, vector[0]);
123 XYZ[1] = double2XYZtype(ctx, vector[1]);
124 XYZ[2] = double2XYZtype(ctx, vector[2]);
125 }
126
127 static void
128 add_desc_tag(fz_context *ctx, fz_buffer *buf, const char text[], fz_icc_tag tag_list[], int curr_tag)
129 {
130 size_t len = strlen(text);
131
132 fz_append_int32_be(ctx, buf, icSigTextDescriptionType);
133 fz_append_byte_n(ctx, buf, 0, 4);
134 fz_append_int32_be(ctx, buf, (int)len + 1);
135 fz_append_string(ctx, buf, text);
136 /* 1 + 4 + 4 + 2 + 1 + 67 */
137 fz_append_byte_n(ctx, buf, 0, 79);
138 fz_append_byte_n(ctx, buf, 0, tag_list[curr_tag].byte_padding);
139 }
140
141 static void
142 add_text_tag(fz_context *ctx, fz_buffer *buf, const char text[], fz_icc_tag tag_list[], int curr_tag)
143 {
144 fz_append_int32_be(ctx, buf, icSigTextType);
145 fz_append_byte_n(ctx, buf, 0, 4);
146 fz_append_string(ctx, buf, text);
147 fz_append_byte(ctx, buf, 0);
148 fz_append_byte_n(ctx, buf, 0, tag_list[curr_tag].byte_padding);
149 }
150
151 static void
152 add_common_tag_data(fz_context *ctx, fz_buffer *buf, fz_icc_tag tag_list[], const char *desc_name)
153 {
154 add_desc_tag(ctx, buf, desc_name, tag_list, 0);
155 add_text_tag(ctx, buf, copy_right, tag_list, 1);
156 }
157
158 static void
159 init_common_tags(fz_context *ctx, fz_icc_tag tag_list[], int num_tags, int *last_tag, const char *desc_name)
160 {
161 int curr_tag, temp_size;
162
163 if (*last_tag < 0)
164 curr_tag = 0;
165 else
166 curr_tag = (*last_tag) + 1;
167
168 tag_list[curr_tag].offset = ICC_HEADER_SIZE + num_tags * ICC_TAG_SIZE + 4;
169 tag_list[curr_tag].sig = icSigProfileDescriptionTag;
170
171 /* temp_size = DATATYPE_SIZE + 4 (zeros) + 4 (len) + strlen(desc_name) + 1 (null) + 4 + 4 + 2 + 1 + 67 + bytepad; */
172 temp_size = (int)strlen(desc_name) + 91;
173
174 tag_list[curr_tag].byte_padding = get_padding(temp_size);
175 tag_list[curr_tag].size = temp_size + tag_list[curr_tag].byte_padding;
176 curr_tag++;
177 tag_list[curr_tag].offset = tag_list[curr_tag - 1].offset + tag_list[curr_tag - 1].size;
178 tag_list[curr_tag].sig = icSigCopyrightTag;
179
180 /* temp_size = DATATYPE_SIZE + 4 (zeros) + strlen(copy_right) + 1 (null); */
181 temp_size = (int)strlen(copy_right) + 9;
182 tag_list[curr_tag].byte_padding = get_padding(temp_size);
183 tag_list[curr_tag].size = temp_size + tag_list[curr_tag].byte_padding;
184 *last_tag = curr_tag;
185 }
186
187 static void
188 copy_header(fz_context *ctx, fz_buffer *buffer, icHeader *header)
189 {
190 fz_append_int32_be(ctx, buffer, header->size);
191 fz_append_byte_n(ctx, buffer, 0, 4);
192 fz_append_int32_be(ctx, buffer, header->version);
193 fz_append_int32_be(ctx, buffer, header->deviceClass);
194 fz_append_int32_be(ctx, buffer, header->colorSpace);
195 fz_append_int32_be(ctx, buffer, header->pcs);
196 fz_append_byte_n(ctx, buffer, 0, 12);
197 fz_append_int32_be(ctx, buffer, header->magic);
198 fz_append_int32_be(ctx, buffer, header->platform);
199 fz_append_byte_n(ctx, buffer, 0, 24);
200 fz_append_int32_be(ctx, buffer, header->illuminant.X);
201 fz_append_int32_be(ctx, buffer, header->illuminant.Y);
202 fz_append_int32_be(ctx, buffer, header->illuminant.Z);
203 fz_append_byte_n(ctx, buffer, 0, 48);
204 }
205
206 static void
207 setheader_common(fz_context *ctx, icHeader *header)
208 {
209 header->cmmId = 0;
210 header->version = 0x02200000;
211 setdatetime(ctx, &(header->date));
212 header->magic = icMagicNumber;
213 header->platform = icSigMacintosh;
214 header->flags = 0;
215 header->manufacturer = 0;
216 header->model = 0;
217 header->attributes[0] = 0;
218 header->attributes[1] = 0;
219 header->renderingIntent = 3;
220 header->illuminant.X = double2XYZtype(ctx, (float) 0.9642);
221 header->illuminant.Y = double2XYZtype(ctx, (float) 1.0);
222 header->illuminant.Z = double2XYZtype(ctx, (float) 0.8249);
223 header->creator = 0;
224 memset(header->reserved, 0, 44);
225 }
226
227 static void
228 copy_tagtable(fz_context *ctx, fz_buffer *buf, fz_icc_tag *tag_list, int num_tags)
229 {
230 int k;
231
232 fz_append_int32_be(ctx, buf, num_tags);
233 for (k = 0; k < num_tags; k++)
234 {
235 fz_append_int32_be(ctx, buf, tag_list[k].sig);
236 fz_append_int32_be(ctx, buf, tag_list[k].offset);
237 fz_append_int32_be(ctx, buf, tag_list[k].size);
238 }
239 }
240
241 static void
242 init_tag(fz_context *ctx, fz_icc_tag tag_list[], int *last_tag, icTagSignature tagsig, int datasize)
243 {
244 int curr_tag = (*last_tag) + 1;
245
246 tag_list[curr_tag].offset = tag_list[curr_tag - 1].offset + tag_list[curr_tag - 1].size;
247 tag_list[curr_tag].sig = tagsig;
248 tag_list[curr_tag].byte_padding = get_padding(ICC_DATATYPE_SIZE + datasize);
249 tag_list[curr_tag].size = ICC_DATATYPE_SIZE + datasize + tag_list[curr_tag].byte_padding;
250 *last_tag = curr_tag;
251 }
252
253 static void
254 matrixmult(fz_context *ctx, float leftmatrix[], int nlrow, int nlcol, float rightmatrix[], int nrrow, int nrcol, float result[])
255 {
256 float *curr_row;
257 int k, l, j, ncols, nrows;
258 float sum;
259
260 nrows = nlrow;
261 ncols = nrcol;
262 if (nlcol == nrrow)
263 {
264 for (k = 0; k < nrows; k++)
265 {
266 curr_row = &(leftmatrix[k*nlcol]);
267 for (l = 0; l < ncols; l++)
268 {
269 sum = 0.0;
270 for (j = 0; j < nlcol; j++)
271 sum = sum + curr_row[j] * rightmatrix[j*nrcol + l];
272 result[k*ncols + l] = sum;
273 }
274 }
275 }
276 }
277
278 static void
279 apply_adaption(fz_context *ctx, float matrix[], float in[], float out[])
280 {
281 out[0] = matrix[0] * in[0] + matrix[1] * in[1] + matrix[2] * in[2];
282 out[1] = matrix[3] * in[0] + matrix[4] * in[1] + matrix[5] * in[2];
283 out[2] = matrix[6] * in[0] + matrix[7] * in[1] + matrix[8] * in[2];
284 }
285
286 /*
287 Compute the CAT02 transformation to get us from the Cal White point to the
288 D50 white point
289 */
290 static void
291 gsicc_create_compute_cam(fz_context *ctx, float white_src[], float *cam)
292 {
293 float cat02matrix[] = { 0.7328f, 0.4296f, -0.1624f, -0.7036f, 1.6975f, 0.0061f, 0.003f, 0.0136f, 0.9834f };
294 float cat02matrixinv[] = { 1.0961f, -0.2789f, 0.1827f, 0.4544f, 0.4735f, 0.0721f, -0.0096f, -0.0057f, 1.0153f };
295 float vonkries_diag[9];
296 float temp_matrix[9];
297 float lms_wp_src[3], lms_wp_des[3];
298 int k;
299 float d50[3] = { D50_X, D50_Y, D50_Z };
300
301 matrixmult(ctx, cat02matrix, 3, 3, white_src, 3, 1, lms_wp_src);
302 matrixmult(ctx, cat02matrix, 3, 3, d50, 3, 1, lms_wp_des);
303 memset(&(vonkries_diag[0]), 0, sizeof(float) * 9);
304
305 for (k = 0; k < 3; k++)
306 {
307 if (lms_wp_src[k] > 0)
308 vonkries_diag[k * 3 + k] = lms_wp_des[k] / lms_wp_src[k];
309 else
310 vonkries_diag[k * 3 + k] = 1;
311 }
312 matrixmult(ctx, &(vonkries_diag[0]), 3, 3, cat02matrix, 3, 3, temp_matrix);
313 matrixmult(ctx, &(cat02matrixinv[0]), 3, 3, temp_matrix, 3, 3, cam);
314 }
315
316 /* The algorithm used by the following few routines comes from
317 * LCMS2. We use this for converting the XYZ primaries + whitepoint
318 * supplied to fz_new_icc_data_from_cal into the correct form for
319 * writing into the profile.
320 *
321 * Prior to this code, we were, in some cases (such as with
322 * the color data from pal8v4.bmp) ending up with a profile where
323 * the whitepoint did not match properly.
324 */
325
326 // Determinant lower than that are assumed zero (used on matrix invert)
327 #define MATRIX_DET_TOLERANCE 0.0001
328
329 static int
330 matrix_invert(double *out, const double *in)
331 {
332 double det, c0, c1, c2, absdet;
333
334 c0 = in[4]*in[8] - in[5]*in[7];
335 c1 = -in[3]*in[8] + in[5]*in[6];
336 c2 = in[3]*in[7] - in[4]*in[6];
337
338 det = in[0]*c0 + in[1]*c1 + in[2]*c2;
339 absdet = det;
340 if (absdet < 0)
341 absdet = -absdet;
342 if (absdet < MATRIX_DET_TOLERANCE)
343 return 1; // singular matrix; can't invert
344
345 out[0] = c0/det;
346 out[1] = (in[2]*in[7] - in[1]*in[8])/det;
347 out[2] = (in[1]*in[5] - in[2]*in[4])/det;
348 out[3] = c1/det;
349 out[4] = (in[0]*in[8] - in[2]*in[6])/det;
350 out[5] = (in[2]*in[3] - in[0]*in[5])/det;
351 out[6] = c2/det;
352 out[7] = (in[1]*in[6] - in[0]*in[7])/det;
353 out[8] = (in[0]*in[4] - in[1]*in[3])/det;
354
355 return 0;
356 }
357
358 static void
359 transform_vector(double *out, const double *mat, const double *v)
360 {
361 out[0] = mat[0]*v[0] + mat[1]*v[1] + mat[2]*v[2];
362 out[1] = mat[3]*v[0] + mat[4]*v[1] + mat[5]*v[2];
363 out[2] = mat[6]*v[0] + mat[7]*v[1] + mat[8]*v[2];
364 }
365
366 static void
367 matrix_compose(double *out, const double *a, const double *b)
368 {
369 out[0] = a[0]*b[0] + a[1]*b[3] + a[2]*b[6];
370 out[1] = a[0]*b[1] + a[1]*b[4] + a[2]*b[7];
371 out[2] = a[0]*b[2] + a[1]*b[5] + a[2]*b[8];
372 out[3] = a[3]*b[0] + a[4]*b[3] + a[5]*b[6];
373 out[4] = a[3]*b[1] + a[4]*b[4] + a[5]*b[7];
374 out[5] = a[3]*b[2] + a[4]*b[5] + a[5]*b[8];
375 out[6] = a[6]*b[0] + a[7]*b[3] + a[8]*b[6];
376 out[7] = a[6]*b[1] + a[7]*b[4] + a[8]*b[7];
377 out[8] = a[6]*b[2] + a[7]*b[5] + a[8]*b[8];
378 }
379
380 static int
381 adaptation_matrix(double *out, const double *fromXYZ, const double *toXYZ)
382 {
383 // Bradford matrix
384 static const double LamRigg[9] = {
385 0.8951, 0.2664, -0.1614,
386 -0.7502, 1.7135, 0.0367,
387 0.0389, -0.0685, 1.0296
388 };
389 double chad_inv[9];
390 double fromRGB[3];
391 double toRGB[3];
392 double cone[9];
393 double tmp[9];
394
395 /* This should never fail, because it's the same operation
396 * every time! Could save the work here... */
397 if (matrix_invert(chad_inv, LamRigg))
398 return 1;
399
400 transform_vector(fromRGB, LamRigg, fromXYZ);
401 transform_vector(toRGB, LamRigg, toXYZ);
402
403 // Build matrix
404 cone[0] = toRGB[0]/fromRGB[0];
405 cone[1] = 0.0;
406 cone[2] = 0.0;
407 cone[3] = 0.0;
408 cone[4] = toRGB[1]/fromRGB[1];
409 cone[5] = 0.0;
410 cone[6] = 0.0;
411 cone[7] = 0.0;
412 cone[8] = toRGB[2]/fromRGB[2];
413
414 // Normalize
415 matrix_compose(tmp, cone, LamRigg);
416 matrix_compose(out, chad_inv, tmp);
417
418 return 0;
419 }
420
421 static int
422 build_rgb2XYZ_transfer_matrix(double *out, double *xyYwhite, const double *xyYprimaries)
423 {
424 double whitepoint[3], coef[3];
425 double xn, yn;
426 double xr, yr;
427 double xg, yg;
428 double xb, yb;
429 double primaries[9];
430 double result[9];
431 double mat[9];
432 double bradford[9];
433 static double d50XYZ[3] = { 0.9642, 1.0, 0.8249 };
434
435 xn = xyYwhite[0];
436 yn = xyYwhite[1];
437 xr = xyYprimaries[0];
438 yr = xyYprimaries[1];
439 xg = xyYprimaries[3];
440 yg = xyYprimaries[4];
441 xb = xyYprimaries[6];
442 yb = xyYprimaries[7];
443
444 // Build Primaries matrix
445 primaries[0] = xr;
446 primaries[1] = xg;
447 primaries[2] = xb;
448 primaries[3] = yr;
449 primaries[4] = yg;
450 primaries[5] = yb;
451 primaries[6] = (1-xr-yr);
452 primaries[7] = (1-xg-yg);
453 primaries[8] = (1-xb-yb);
454
455 // Result = Primaries ^ (-1) inverse matrix
456 if (matrix_invert(&result[0], &primaries[0]))
457 return 1;
458
459 /* Convert whitepoint from xyY to XYZ. Isn't this where
460 * we came in? I think we've effectively normalised during
461 * this process. */
462 whitepoint[0] = xn/yn;
463 whitepoint[1] = 1;
464 whitepoint[2] = (1-xn-yn)/yn;
465
466 // Across inverse primaries ...
467 transform_vector(coef, result, whitepoint);
468
469 // Give us the Coefs, then I build transformation matrix
470 mat[0] = coef[0]*xr;
471 mat[1] = coef[1]*xg;
472 mat[2] = coef[2]*xb;
473 mat[3] = coef[0]*yr;
474 mat[4] = coef[1]*yg;
475 mat[5] = coef[2]*yb;
476 mat[6] = coef[0]*(1.0-xr-yr);
477 mat[7] = coef[1]*(1.0-xg-yg);
478 mat[8] = coef[2]*(1.0-xb-yb);
479
480 if (adaptation_matrix(bradford, whitepoint, d50XYZ))
481 return 1;
482
483 matrix_compose(out, bradford, mat);
484
485 return 0;
486 }
487
488
489 fz_buffer *
490 fz_new_icc_data_from_cal(fz_context *ctx,
491 float wp[3],
492 float bp[3],
493 float *gamma,
494 float matrix[9],
495 int n)
496 {
497 fz_icc_tag *tag_list;
498 icProfile iccprofile;
499 icHeader *header = &(iccprofile.header);
500 fz_buffer *profile = NULL;
501 size_t profile_size;
502 int k;
503 int num_tags;
504 unsigned short encode_gamma;
505 int last_tag;
506 icS15Fixed16Number temp_XYZ[3];
507 icTagSignature TRC_Tags[3] = { icSigRedTRCTag, icSigGreenTRCTag, icSigBlueTRCTag };
508 int trc_tag_size;
509 float cat02[9];
510 float black_adapt[3];
511 const char *desc_name;
512
513 /* common */
514 setheader_common(ctx, header);
515 header->pcs = icSigXYZData;
516 profile_size = ICC_HEADER_SIZE;
517 header->deviceClass = icSigInputClass;
518
519 if (n == 3)
520 {
521 desc_name = "CalRGB";
522 header->colorSpace = icSigRgbData;
523 num_tags = 10; /* common (2) + rXYZ, gXYZ, bXYZ, rTRC, gTRC, bTRC, bkpt, wtpt */
524 }
525 else
526 {
527 desc_name = "CalGray";
528 header->colorSpace = icSigGrayData;
529 num_tags = 5; /* common (2) + GrayTRC, bkpt, wtpt */
530 TRC_Tags[0] = icSigGrayTRCTag;
531 }
532
533 tag_list = Memento_label(fz_malloc(ctx, sizeof(fz_icc_tag) * num_tags), "icc_tag_list");
534
535 /* precompute sizes and offsets */
536 profile_size += ICC_TAG_SIZE * num_tags;
537 profile_size += 4; /* number of tags.... */
538 last_tag = -1;
539 init_common_tags(ctx, tag_list, num_tags, &last_tag, desc_name);
540 if (n == 3)
541 {
542 init_tag(ctx, tag_list, &last_tag, icSigRedColorantTag, ICC_XYZPT_SIZE);
543 init_tag(ctx, tag_list, &last_tag, icSigGreenColorantTag, ICC_XYZPT_SIZE);
544 init_tag(ctx, tag_list, &last_tag, icSigBlueColorantTag, ICC_XYZPT_SIZE);
545 }
546 init_tag(ctx, tag_list, &last_tag, icSigMediaWhitePointTag, ICC_XYZPT_SIZE);
547 init_tag(ctx, tag_list, &last_tag, icSigMediaBlackPointTag, ICC_XYZPT_SIZE);
548
549 /* 4 for count, 2 for gamma, Extra 2 bytes for 4 byte alignment requirement */
550 trc_tag_size = 8;
551 for (k = 0; k < n; k++)
552 init_tag(ctx, tag_list, &last_tag, TRC_Tags[k], trc_tag_size);
553 for (k = 0; k < num_tags; k++)
554 profile_size += tag_list[k].size;
555
556 fz_var(profile);
557
558 /* Allocate buffer */
559 fz_try(ctx)
560 {
561 profile = fz_new_buffer(ctx, profile_size);
562
563 /* Header */
564 header->size = (icUInt32Number)profile_size;
565 copy_header(ctx, profile, header);
566
567 /* Tag table */
568 copy_tagtable(ctx, profile, tag_list, num_tags);
569
570 /* Common tags */
571 add_common_tag_data(ctx, profile, tag_list, desc_name);
572
573 /* Get the cat02 matrix */
574 gsicc_create_compute_cam(ctx, wp, cat02);
575
576 /* The matrix */
577 if (n == 3)
578 {
579 /* Convert whitepoint from XYZ to xyY */
580 double xyz = wp[0] + wp[1] + wp[2];
581 double whitexyY[3] = { wp[0] / xyz, wp[1] / xyz, 1.0 };
582 /* Convert primaries from XYZ to xyY */
583 double matrix012 = matrix[0] + matrix[1] + matrix[2];
584 double matrix345 = matrix[3] + matrix[4] + matrix[5];
585 double matrix678 = matrix[6] + matrix[7] + matrix[8];
586 double primariesxyY[9] = {
587 matrix[0] / matrix012,
588 matrix[1] / matrix012,
589 matrix[1],
590 matrix[3] / matrix345,
591 matrix[4] / matrix345,
592 matrix[5],
593 matrix[6] / matrix678,
594 matrix[7] / matrix678,
595 matrix[8]
596 };
597 double primaries[9];
598
599 if (build_rgb2XYZ_transfer_matrix(primaries, whitexyY, primariesxyY))
600 fz_throw(ctx, FZ_ERROR_ARGUMENT, "CalRGB profile creation failed; bad values");
601
602 for (k = 0; k < 3; k++)
603 {
604 float primary[3] = { primaries[k+0], primaries[k+3], primaries[k+6] };
605 get_XYZ_doubletr(ctx, temp_XYZ, primary);
606 add_xyzdata(ctx, profile, temp_XYZ);
607 }
608 }
609
610 /* White and black points. WP is D50 */
611 get_D50(ctx, temp_XYZ);
612 add_xyzdata(ctx, profile, temp_XYZ);
613
614 /* Black point. Apply cat02*/
615 apply_adaption(ctx, cat02, bp, &(black_adapt[0]));
616 get_XYZ_doubletr(ctx, temp_XYZ, &(black_adapt[0]));
617 add_xyzdata(ctx, profile, temp_XYZ);
618
619 /* Gamma */
620 for (k = 0; k < n; k++)
621 {
622 encode_gamma = float2u8Fixed8(ctx, gamma[k]);
623 add_gammadata(ctx, profile, encode_gamma, icSigCurveType);
624 }
625 }
626 fz_always(ctx)
627 fz_free(ctx, tag_list);
628 fz_catch(ctx)
629 {
630 fz_drop_buffer(ctx, profile);
631 fz_rethrow(ctx);
632 }
633
634 #if SAVEICCPROFILE
635 if (n == 3)
636 save_profile(ctx, profile, "calRGB");
637 else
638 save_profile(ctx, profile, "calGray");
639 #endif
640 return profile;
641 }