Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
