color-icc-create.c 17 KB

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