Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/fitz/colorspace.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 // Copyright (C) 2004-2025 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "mupdf/fitz.h" | |
| 24 | |
| 25 #include "color-imp.h" | |
| 26 | |
| 27 #include <assert.h> | |
| 28 #include <math.h> | |
| 29 #include <string.h> | |
| 30 | |
| 31 #if FZ_ENABLE_ICC | |
| 32 | |
| 33 #include "icc/gray.icc.h" | |
| 34 #include "icc/rgb.icc.h" | |
| 35 #include "icc/cmyk.icc.h" | |
| 36 #include "icc/lab.icc.h" | |
| 37 | |
| 38 void fz_new_colorspace_context(fz_context *ctx) | |
| 39 { | |
| 40 fz_colorspace_context *cct; | |
| 41 | |
| 42 fz_buffer *gray = NULL; | |
| 43 fz_buffer *rgb = NULL; | |
| 44 fz_buffer *cmyk = NULL; | |
| 45 fz_buffer *lab = NULL; | |
| 46 | |
| 47 fz_var(gray); | |
| 48 fz_var(rgb); | |
| 49 fz_var(cmyk); | |
| 50 fz_var(lab); | |
| 51 | |
| 52 cct = ctx->colorspace = fz_malloc_struct(ctx, fz_colorspace_context); | |
| 53 cct->ctx_refs = 1; | |
| 54 | |
| 55 fz_new_icc_context(ctx); | |
| 56 | |
| 57 ctx->icc_enabled = 1; | |
| 58 | |
| 59 fz_try(ctx) | |
| 60 { | |
| 61 gray = fz_new_buffer_from_shared_data(ctx, resources_icc_gray_icc, resources_icc_gray_icc_len); | |
| 62 rgb = fz_new_buffer_from_shared_data(ctx, resources_icc_rgb_icc, resources_icc_rgb_icc_len); | |
| 63 cmyk = fz_new_buffer_from_shared_data(ctx, resources_icc_cmyk_icc, resources_icc_cmyk_icc_len); | |
| 64 lab = fz_new_buffer_from_shared_data(ctx, resources_icc_lab_icc, resources_icc_lab_icc_len); | |
| 65 cct->gray = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_GRAY, FZ_COLORSPACE_IS_DEVICE, "DeviceGray", gray); | |
| 66 cct->rgb = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_RGB, FZ_COLORSPACE_IS_DEVICE, "DeviceRGB", rgb); | |
| 67 cct->bgr = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_BGR, FZ_COLORSPACE_IS_DEVICE, "DeviceBGR", rgb); | |
| 68 cct->cmyk = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_CMYK, FZ_COLORSPACE_IS_DEVICE, "DeviceCMYK", cmyk); | |
| 69 cct->lab = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_LAB, FZ_COLORSPACE_IS_DEVICE, "Lab", lab); | |
| 70 } | |
| 71 fz_always(ctx) | |
| 72 { | |
| 73 fz_drop_buffer(ctx, gray); | |
| 74 fz_drop_buffer(ctx, rgb); | |
| 75 fz_drop_buffer(ctx, cmyk); | |
| 76 fz_drop_buffer(ctx, lab); | |
| 77 } | |
| 78 fz_catch(ctx) | |
| 79 { | |
| 80 fz_rethrow(ctx); | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 void fz_enable_icc(fz_context *ctx) | |
| 85 { | |
| 86 ctx->icc_enabled = 1; | |
| 87 } | |
| 88 | |
| 89 void fz_disable_icc(fz_context *ctx) | |
| 90 { | |
| 91 ctx->icc_enabled = 0; | |
| 92 } | |
| 93 | |
| 94 #else | |
| 95 | |
| 96 void fz_new_colorspace_context(fz_context *ctx) | |
| 97 { | |
| 98 fz_colorspace_context *cct; | |
| 99 | |
| 100 cct = ctx->colorspace = fz_malloc_struct(ctx, fz_colorspace_context); | |
| 101 cct->ctx_refs = 1; | |
| 102 | |
| 103 cct->gray = fz_new_colorspace(ctx, FZ_COLORSPACE_GRAY, FZ_COLORSPACE_IS_DEVICE, 1, "DeviceGray"); | |
| 104 cct->rgb = fz_new_colorspace(ctx, FZ_COLORSPACE_RGB, FZ_COLORSPACE_IS_DEVICE, 3, "DeviceRGB"); | |
| 105 cct->bgr = fz_new_colorspace(ctx, FZ_COLORSPACE_BGR, FZ_COLORSPACE_IS_DEVICE, 3, "DeviceBGR"); | |
| 106 cct->cmyk = fz_new_colorspace(ctx, FZ_COLORSPACE_CMYK, FZ_COLORSPACE_IS_DEVICE, 4, "DeviceCMYK"); | |
| 107 cct->lab = fz_new_colorspace(ctx, FZ_COLORSPACE_LAB, FZ_COLORSPACE_IS_DEVICE, 3, "Lab"); | |
| 108 } | |
| 109 | |
| 110 void fz_enable_icc(fz_context *ctx) | |
| 111 { | |
| 112 fz_warn(ctx, "ICC support is not available"); | |
| 113 } | |
| 114 | |
| 115 void fz_disable_icc(fz_context *ctx) | |
| 116 { | |
| 117 } | |
| 118 | |
| 119 #endif | |
| 120 | |
| 121 fz_colorspace_context *fz_keep_colorspace_context(fz_context *ctx) | |
| 122 { | |
| 123 fz_keep_imp(ctx, ctx->colorspace, &ctx->colorspace->ctx_refs); | |
| 124 return ctx->colorspace; | |
| 125 } | |
| 126 | |
| 127 void fz_drop_colorspace_context(fz_context *ctx) | |
| 128 { | |
| 129 if (fz_drop_imp(ctx, ctx->colorspace, &ctx->colorspace->ctx_refs)) | |
| 130 { | |
| 131 fz_drop_colorspace(ctx, ctx->colorspace->gray); | |
| 132 fz_drop_colorspace(ctx, ctx->colorspace->rgb); | |
| 133 fz_drop_colorspace(ctx, ctx->colorspace->bgr); | |
| 134 fz_drop_colorspace(ctx, ctx->colorspace->cmyk); | |
| 135 fz_drop_colorspace(ctx, ctx->colorspace->lab); | |
| 136 #if FZ_ENABLE_ICC | |
| 137 fz_drop_icc_context(ctx); | |
| 138 #endif | |
| 139 fz_free(ctx, ctx->colorspace); | |
| 140 ctx->colorspace = NULL; | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 fz_colorspace *fz_device_gray(fz_context *ctx) | |
| 145 { | |
| 146 return ctx->colorspace->gray; | |
| 147 } | |
| 148 | |
| 149 fz_colorspace *fz_device_rgb(fz_context *ctx) | |
| 150 { | |
| 151 return ctx->colorspace->rgb; | |
| 152 } | |
| 153 | |
| 154 fz_colorspace *fz_device_bgr(fz_context *ctx) | |
| 155 { | |
| 156 return ctx->colorspace->bgr; | |
| 157 } | |
| 158 | |
| 159 fz_colorspace *fz_device_cmyk(fz_context *ctx) | |
| 160 { | |
| 161 return ctx->colorspace->cmyk; | |
| 162 } | |
| 163 | |
| 164 fz_colorspace *fz_device_lab(fz_context *ctx) | |
| 165 { | |
| 166 return ctx->colorspace->lab; | |
| 167 } | |
| 168 | |
| 169 /* Same order as needed by LCMS */ | |
| 170 static const char *fz_intent_names[] = | |
| 171 { | |
| 172 "Perceptual", | |
| 173 "RelativeColorimetric", | |
| 174 "Saturation", | |
| 175 "AbsoluteColorimetric", | |
| 176 }; | |
| 177 | |
| 178 int fz_lookup_rendering_intent(const char *name) | |
| 179 { | |
| 180 int i; | |
| 181 for (i = 0; i < (int)nelem(fz_intent_names); i++) | |
| 182 if (!strcmp(name, fz_intent_names[i])) | |
| 183 return i; | |
| 184 return FZ_RI_RELATIVE_COLORIMETRIC; | |
| 185 } | |
| 186 | |
| 187 const char *fz_rendering_intent_name(int ri) | |
| 188 { | |
| 189 if (ri >= 0 && ri < (int)nelem(fz_intent_names)) | |
| 190 return fz_intent_names[ri]; | |
| 191 return "RelativeColorimetric"; | |
| 192 } | |
| 193 | |
| 194 /* Colorspace feature tests */ | |
| 195 | |
| 196 const char *fz_colorspace_name(fz_context *ctx, fz_colorspace *cs) | |
| 197 { | |
| 198 return cs ? cs->name : "None"; | |
| 199 } | |
| 200 | |
| 201 enum fz_colorspace_type fz_colorspace_type(fz_context *ctx, fz_colorspace *cs) | |
| 202 { | |
| 203 return cs ? cs->type : FZ_COLORSPACE_NONE; | |
| 204 } | |
| 205 | |
| 206 int fz_colorspace_n(fz_context *ctx, fz_colorspace *cs) | |
| 207 { | |
| 208 return cs ? cs->n : 0; | |
| 209 } | |
| 210 | |
| 211 int fz_colorspace_is_gray(fz_context *ctx, fz_colorspace *cs) | |
| 212 { | |
| 213 return cs && cs->type == FZ_COLORSPACE_GRAY; | |
| 214 } | |
| 215 | |
| 216 int fz_colorspace_is_rgb(fz_context *ctx, fz_colorspace *cs) | |
| 217 { | |
| 218 return cs && cs->type == FZ_COLORSPACE_RGB; | |
| 219 } | |
| 220 | |
| 221 int fz_colorspace_is_cmyk(fz_context *ctx, fz_colorspace *cs) | |
| 222 { | |
| 223 return cs && cs->type == FZ_COLORSPACE_CMYK; | |
| 224 } | |
| 225 | |
| 226 int fz_colorspace_is_lab(fz_context *ctx, fz_colorspace *cs) | |
| 227 { | |
| 228 return cs && cs->type == FZ_COLORSPACE_LAB; | |
| 229 } | |
| 230 | |
| 231 int fz_colorspace_is_indexed(fz_context *ctx, fz_colorspace *cs) | |
| 232 { | |
| 233 return cs && (cs->type == FZ_COLORSPACE_INDEXED); | |
| 234 } | |
| 235 | |
| 236 int fz_colorspace_is_device_n(fz_context *ctx, fz_colorspace *cs) | |
| 237 { | |
| 238 return cs && (cs->type == FZ_COLORSPACE_SEPARATION); | |
| 239 } | |
| 240 | |
| 241 int fz_colorspace_is_subtractive(fz_context *ctx, fz_colorspace *cs) | |
| 242 { | |
| 243 return cs && (cs->type == FZ_COLORSPACE_CMYK || cs->type == FZ_COLORSPACE_SEPARATION); | |
| 244 } | |
| 245 | |
| 246 int fz_colorspace_is_device(fz_context *ctx, fz_colorspace *cs) | |
| 247 { | |
| 248 return cs && (cs->flags & FZ_COLORSPACE_IS_DEVICE); | |
| 249 } | |
| 250 | |
| 251 int fz_colorspace_is_icc(fz_context *ctx, fz_colorspace *cs) | |
| 252 { | |
| 253 return cs && (cs->flags & FZ_COLORSPACE_IS_ICC); | |
| 254 } | |
| 255 | |
| 256 int fz_colorspace_is_lab_icc(fz_context *ctx, fz_colorspace *cs) | |
| 257 { | |
| 258 return cs && (cs->type == FZ_COLORSPACE_LAB) && (cs->flags & FZ_COLORSPACE_IS_ICC); | |
| 259 } | |
| 260 | |
| 261 int fz_colorspace_is_device_gray(fz_context *ctx, fz_colorspace *cs) | |
| 262 { | |
| 263 return fz_colorspace_is_device(ctx, cs) && fz_colorspace_is_gray(ctx, cs); | |
| 264 } | |
| 265 | |
| 266 int fz_colorspace_is_device_cmyk(fz_context *ctx, fz_colorspace *cs) | |
| 267 { | |
| 268 return fz_colorspace_is_device(ctx, cs) && fz_colorspace_is_cmyk(ctx, cs); | |
| 269 } | |
| 270 | |
| 271 int fz_colorspace_device_n_has_only_cmyk(fz_context *ctx, fz_colorspace *cs) | |
| 272 { | |
| 273 return cs && ((cs->flags & FZ_COLORSPACE_HAS_CMYK_AND_SPOTS) == FZ_COLORSPACE_HAS_CMYK); | |
| 274 } | |
| 275 | |
| 276 int fz_colorspace_device_n_has_cmyk(fz_context *ctx, fz_colorspace *cs) | |
| 277 { | |
| 278 return cs && (cs->flags & FZ_COLORSPACE_HAS_CMYK); | |
| 279 } | |
| 280 | |
| 281 int fz_is_valid_blend_colorspace(fz_context *ctx, fz_colorspace *cs) | |
| 282 { | |
| 283 return cs == NULL || | |
| 284 cs->type == FZ_COLORSPACE_GRAY || | |
| 285 cs->type == FZ_COLORSPACE_RGB || | |
| 286 cs->type == FZ_COLORSPACE_CMYK; | |
| 287 } | |
| 288 | |
| 289 fz_colorspace *fz_base_colorspace(fz_context *ctx, fz_colorspace *cs) | |
| 290 { | |
| 291 if (cs == NULL) | |
| 292 return NULL; | |
| 293 if (cs->type == FZ_COLORSPACE_INDEXED) | |
| 294 return cs->u.indexed.base; | |
| 295 return cs; | |
| 296 } | |
| 297 | |
| 298 fz_colorspace * | |
| 299 fz_keep_colorspace(fz_context *ctx, fz_colorspace *cs) | |
| 300 { | |
| 301 return fz_keep_key_storable(ctx, &cs->key_storable); | |
| 302 } | |
| 303 | |
| 304 void | |
| 305 fz_drop_colorspace(fz_context *ctx, fz_colorspace *cs) | |
| 306 { | |
| 307 fz_drop_key_storable(ctx, &cs->key_storable); | |
| 308 } | |
| 309 | |
| 310 fz_colorspace * | |
| 311 fz_keep_colorspace_store_key(fz_context *ctx, fz_colorspace *cs) | |
| 312 { | |
| 313 return fz_keep_key_storable_key(ctx, &cs->key_storable); | |
| 314 } | |
| 315 | |
| 316 void | |
| 317 fz_drop_colorspace_store_key(fz_context *ctx, fz_colorspace *cs) | |
| 318 { | |
| 319 fz_drop_key_storable_key(ctx, &cs->key_storable); | |
| 320 } | |
| 321 | |
| 322 void | |
| 323 fz_drop_colorspace_imp(fz_context *ctx, fz_storable *cs_) | |
| 324 { | |
| 325 fz_colorspace *cs = (fz_colorspace *)cs_; | |
| 326 int i; | |
| 327 | |
| 328 if (cs->type == FZ_COLORSPACE_INDEXED) | |
| 329 { | |
| 330 fz_drop_colorspace(ctx, cs->u.indexed.base); | |
| 331 fz_free(ctx, cs->u.indexed.lookup); | |
| 332 } | |
| 333 if (cs->type == FZ_COLORSPACE_SEPARATION) | |
| 334 { | |
| 335 fz_drop_colorspace(ctx, cs->u.separation.base); | |
| 336 cs->u.separation.drop(ctx, cs->u.separation.tint); | |
| 337 for (i = 0; i < FZ_MAX_COLORS; i++) | |
| 338 fz_free(ctx, cs->u.separation.colorant[i]); | |
| 339 } | |
| 340 #if FZ_ENABLE_ICC | |
| 341 if (cs->flags & FZ_COLORSPACE_IS_ICC) | |
| 342 { | |
| 343 fz_drop_icc_profile(ctx, cs->u.icc.profile); | |
| 344 fz_drop_buffer(ctx, cs->u.icc.buffer); | |
| 345 } | |
| 346 #endif | |
| 347 | |
| 348 fz_free(ctx, cs->name); | |
| 349 fz_free(ctx, cs); | |
| 350 } | |
| 351 | |
| 352 fz_colorspace * | |
| 353 fz_new_colorspace(fz_context *ctx, enum fz_colorspace_type type, int flags, int n, const char *name) | |
| 354 { | |
| 355 fz_colorspace *cs = fz_malloc_struct(ctx, fz_colorspace); | |
| 356 FZ_INIT_KEY_STORABLE(cs, 1, fz_drop_colorspace_imp); | |
| 357 | |
| 358 if (n > FZ_MAX_COLORS) | |
| 359 fz_throw(ctx, FZ_ERROR_ARGUMENT, "too many color components (%d > %d)", n, FZ_MAX_COLORS); | |
| 360 if (n < 1) | |
| 361 fz_throw(ctx, FZ_ERROR_ARGUMENT, "too few color components (%d < 1)", n); | |
| 362 | |
| 363 fz_try(ctx) | |
| 364 { | |
| 365 cs->type = type; | |
| 366 cs->flags = flags; | |
| 367 cs->n = n; | |
| 368 cs->name = Memento_label(fz_strdup(ctx, name ? name : "UNKNOWN"), "cs_name"); | |
| 369 } | |
| 370 fz_catch(ctx) | |
| 371 { | |
| 372 fz_free(ctx, cs); | |
| 373 fz_rethrow(ctx); | |
| 374 } | |
| 375 | |
| 376 return cs; | |
| 377 } | |
| 378 | |
| 379 fz_colorspace * | |
| 380 fz_new_indexed_colorspace(fz_context *ctx, fz_colorspace *base, int high, unsigned char *lookup) | |
| 381 { | |
| 382 fz_colorspace *cs; | |
| 383 char name[100]; | |
| 384 if (high < 0 || high > 255) | |
| 385 fz_throw(ctx, FZ_ERROR_SYNTAX, "invalid maximum value in indexed colorspace"); | |
| 386 fz_snprintf(name, sizeof name, "Indexed(%d,%s)", high, base->name); | |
| 387 cs = fz_new_colorspace(ctx, FZ_COLORSPACE_INDEXED, 0, 1, name); | |
| 388 cs->u.indexed.base = fz_keep_colorspace(ctx, base); | |
| 389 cs->u.indexed.high = high; | |
| 390 cs->u.indexed.lookup = lookup; | |
| 391 return cs; | |
| 392 } | |
| 393 | |
| 394 fz_colorspace * | |
| 395 fz_new_icc_colorspace(fz_context *ctx, enum fz_colorspace_type type, int flags, const char *name, fz_buffer *buf) | |
| 396 { | |
| 397 #if FZ_ENABLE_ICC | |
| 398 fz_icc_profile *profile = NULL; | |
| 399 fz_colorspace *cs = NULL; | |
| 400 unsigned char *data; | |
| 401 char name_buf[100]; | |
| 402 size_t size; | |
| 403 int n; | |
| 404 | |
| 405 fz_var(profile); | |
| 406 fz_var(cs); | |
| 407 fz_var(type); | |
| 408 | |
| 409 fz_try(ctx) | |
| 410 { | |
| 411 size = fz_buffer_storage(ctx, buf, &data); | |
| 412 profile = fz_new_icc_profile(ctx, data, size); | |
| 413 n = fz_icc_profile_components(ctx, profile); | |
| 414 switch (type) | |
| 415 { | |
| 416 default: | |
| 417 fz_throw(ctx, FZ_ERROR_SYNTAX, "invalid colorspace type for ICC profile"); | |
| 418 case FZ_COLORSPACE_NONE: | |
| 419 switch (n) | |
| 420 { | |
| 421 default: | |
| 422 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile has unexpected number of channels: %d", n); | |
| 423 case 1: | |
| 424 type = FZ_COLORSPACE_GRAY; | |
| 425 break; | |
| 426 case 3: | |
| 427 if (fz_icc_profile_is_lab(ctx, profile)) | |
| 428 type = FZ_COLORSPACE_LAB; | |
| 429 else | |
| 430 type = FZ_COLORSPACE_RGB; | |
| 431 break; | |
| 432 case 4: | |
| 433 type = FZ_COLORSPACE_CMYK; | |
| 434 break; | |
| 435 } | |
| 436 break; | |
| 437 case FZ_COLORSPACE_GRAY: | |
| 438 if (n != 1) | |
| 439 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not Gray", n); | |
| 440 break; | |
| 441 case FZ_COLORSPACE_RGB: | |
| 442 case FZ_COLORSPACE_BGR: | |
| 443 if (n != 3 || fz_icc_profile_is_lab(ctx, profile)) | |
| 444 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not RGB", n); | |
| 445 break; | |
| 446 case FZ_COLORSPACE_LAB: | |
| 447 if (n != 3 || !fz_icc_profile_is_lab(ctx, profile)) | |
| 448 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not Lab", n); | |
| 449 break; | |
| 450 case FZ_COLORSPACE_CMYK: | |
| 451 if (n != 4) | |
| 452 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not CMYK", n); | |
| 453 break; | |
| 454 } | |
| 455 | |
| 456 if (!name) | |
| 457 { | |
| 458 char cmm_name[100]; | |
| 459 fz_icc_profile_name(ctx, profile, cmm_name, sizeof cmm_name); | |
| 460 switch (type) | |
| 461 { | |
| 462 default: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(%d,%s)", n, cmm_name); break; | |
| 463 case FZ_COLORSPACE_GRAY: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(Gray,%s)", cmm_name); break; | |
| 464 case FZ_COLORSPACE_RGB: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(RGB,%s)", cmm_name); break; | |
| 465 case FZ_COLORSPACE_BGR: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(BGR,%s)", cmm_name); break; | |
| 466 case FZ_COLORSPACE_CMYK: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(CMYK,%s)", cmm_name); break; | |
| 467 case FZ_COLORSPACE_LAB: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(Lab,%s)", cmm_name); break; | |
| 468 } | |
| 469 name = name_buf; | |
| 470 } | |
| 471 | |
| 472 cs = fz_new_colorspace(ctx, type, flags | FZ_COLORSPACE_IS_ICC, n, name); | |
| 473 cs->u.icc.buffer = fz_keep_buffer(ctx, buf); | |
| 474 cs->u.icc.profile = profile; | |
| 475 fz_md5_buffer(ctx, buf, cs->u.icc.md5); | |
| 476 } | |
| 477 fz_catch(ctx) | |
| 478 { | |
| 479 fz_drop_icc_profile(ctx, profile); | |
| 480 fz_drop_colorspace(ctx, cs); | |
| 481 fz_rethrow(ctx); | |
| 482 } | |
| 483 return cs; | |
| 484 #else | |
| 485 switch (type) | |
| 486 { | |
| 487 default: fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown colorspace type"); | |
| 488 case FZ_COLORSPACE_GRAY: return fz_keep_colorspace(ctx, fz_device_gray(ctx)); | |
| 489 case FZ_COLORSPACE_RGB: return fz_keep_colorspace(ctx, fz_device_rgb(ctx)); | |
| 490 case FZ_COLORSPACE_BGR: return fz_keep_colorspace(ctx, fz_device_bgr(ctx)); | |
| 491 case FZ_COLORSPACE_CMYK: return fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); | |
| 492 case FZ_COLORSPACE_LAB: return fz_keep_colorspace(ctx, fz_device_lab(ctx)); | |
| 493 } | |
| 494 #endif | |
| 495 } | |
| 496 | |
| 497 fz_colorspace *fz_new_cal_gray_colorspace(fz_context *ctx, float wp[3], float bp[3], float gamma) | |
| 498 { | |
| 499 #if FZ_ENABLE_ICC | |
| 500 fz_buffer *buf = fz_new_icc_data_from_cal(ctx, wp, bp, &gamma, NULL, 1); | |
| 501 fz_colorspace *cs; | |
| 502 fz_try(ctx) | |
| 503 cs = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_GRAY, 0, "CalGray", buf); | |
| 504 fz_always(ctx) | |
| 505 fz_drop_buffer(ctx, buf); | |
| 506 fz_catch(ctx) | |
| 507 fz_rethrow(ctx); | |
| 508 return cs; | |
| 509 #else | |
| 510 return fz_keep_colorspace(ctx, fz_device_gray(ctx)); | |
| 511 #endif | |
| 512 } | |
| 513 | |
| 514 fz_colorspace *fz_new_cal_rgb_colorspace(fz_context *ctx, float wp[3], float bp[3], float gamma[3], float matrix[9]) | |
| 515 { | |
| 516 #if FZ_ENABLE_ICC | |
| 517 fz_buffer *buf = fz_new_icc_data_from_cal(ctx, wp, bp, gamma, matrix, 3); | |
| 518 fz_colorspace *cs; | |
| 519 fz_try(ctx) | |
| 520 cs = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_RGB, 0, "CalRGB", buf); | |
| 521 fz_always(ctx) | |
| 522 fz_drop_buffer(ctx, buf); | |
| 523 fz_catch(ctx) | |
| 524 fz_rethrow(ctx); | |
| 525 return cs; | |
| 526 #else | |
| 527 return fz_keep_colorspace(ctx, fz_device_rgb(ctx)); | |
| 528 #endif | |
| 529 } | |
| 530 | |
| 531 void fz_colorspace_name_colorant(fz_context *ctx, fz_colorspace *cs, int i, const char *name) | |
| 532 { | |
| 533 if (i < 0 || i >= cs->n) | |
| 534 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Attempt to name out of range colorant"); | |
| 535 if (cs->type != FZ_COLORSPACE_SEPARATION) | |
| 536 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Attempt to name colorant for non-separation colorspace"); | |
| 537 | |
| 538 fz_free(ctx, cs->u.separation.colorant[i]); | |
| 539 cs->u.separation.colorant[i] = NULL; | |
| 540 cs->u.separation.colorant[i] = fz_strdup(ctx, name); | |
| 541 | |
| 542 if (!strcmp(name, "Cyan") || !strcmp(name, "Magenta") || !strcmp(name, "Yellow") || !strcmp(name, "Black")) | |
| 543 cs->flags |= FZ_COLORSPACE_HAS_CMYK; | |
| 544 else | |
| 545 cs->flags |= FZ_COLORSPACE_HAS_SPOTS; | |
| 546 } | |
| 547 | |
| 548 const char *fz_colorspace_colorant(fz_context *ctx, fz_colorspace *cs, int i) | |
| 549 { | |
| 550 if (!cs || i < 0 || i >= cs->n) | |
| 551 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Colorant out of range"); | |
| 552 switch (cs->type) | |
| 553 { | |
| 554 case FZ_COLORSPACE_NONE: | |
| 555 return "None"; | |
| 556 case FZ_COLORSPACE_GRAY: | |
| 557 return "Gray"; | |
| 558 case FZ_COLORSPACE_RGB: | |
| 559 if (i == 0) return "Red"; | |
| 560 if (i == 1) return "Green"; | |
| 561 if (i == 2) return "Blue"; | |
| 562 break; | |
| 563 case FZ_COLORSPACE_BGR: | |
| 564 if (i == 0) return "Blue"; | |
| 565 if (i == 1) return "Green"; | |
| 566 if (i == 2) return "Red"; | |
| 567 break; | |
| 568 case FZ_COLORSPACE_CMYK: | |
| 569 if (i == 0) return "Cyan"; | |
| 570 if (i == 1) return "Magenta"; | |
| 571 if (i == 2) return "Yellow"; | |
| 572 if (i == 3) return "Black"; | |
| 573 break; | |
| 574 case FZ_COLORSPACE_LAB: | |
| 575 if (i == 0) return "L*"; | |
| 576 if (i == 1) return "a*"; | |
| 577 if (i == 2) return "b*"; | |
| 578 break; | |
| 579 case FZ_COLORSPACE_INDEXED: | |
| 580 return "Index"; | |
| 581 case FZ_COLORSPACE_SEPARATION: | |
| 582 return cs->u.separation.colorant[i]; | |
| 583 } | |
| 584 return "None"; | |
| 585 } | |
| 586 | |
| 587 void | |
| 588 fz_clamp_color(fz_context *ctx, fz_colorspace *cs, const float *in, float *out) | |
| 589 { | |
| 590 if (cs->type == FZ_COLORSPACE_LAB) | |
| 591 { | |
| 592 out[0] = fz_clamp(in[0], 0, 100); | |
| 593 out[1] = fz_clamp(in[1], -128, 127); | |
| 594 out[2] = fz_clamp(in[2], -128, 127); | |
| 595 } | |
| 596 else if (cs->type == FZ_COLORSPACE_INDEXED) | |
| 597 { | |
| 598 /* round color index to integer before rescaling to hival */ | |
| 599 out[0] = fz_clamp((int)(in[0]+0.5), 0, cs->u.indexed.high) / 255.0f; | |
| 600 } | |
| 601 else | |
| 602 { | |
| 603 int i, n = cs->n; | |
| 604 for (i = 0; i < n; ++i) | |
| 605 out[i] = fz_clamp(in[i], 0, 1); | |
| 606 } | |
| 607 } | |
| 608 | |
| 609 const fz_color_params fz_default_color_params = { FZ_RI_RELATIVE_COLORIMETRIC, 1, 0, 0 }; | |
| 610 | |
| 611 fz_default_colorspaces *fz_new_default_colorspaces(fz_context *ctx) | |
| 612 { | |
| 613 fz_default_colorspaces *default_cs = fz_malloc_struct(ctx, fz_default_colorspaces); | |
| 614 default_cs->refs = 1; | |
| 615 default_cs->gray = fz_keep_colorspace(ctx, fz_device_gray(ctx)); | |
| 616 default_cs->rgb = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); | |
| 617 default_cs->cmyk = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); | |
| 618 default_cs->oi = NULL; | |
| 619 return default_cs; | |
| 620 } | |
| 621 | |
| 622 fz_default_colorspaces *fz_clone_default_colorspaces(fz_context *ctx, fz_default_colorspaces *base) | |
| 623 { | |
| 624 fz_default_colorspaces *default_cs = fz_malloc_struct(ctx, fz_default_colorspaces); | |
| 625 default_cs->refs = 1; | |
| 626 if (base) | |
| 627 { | |
| 628 default_cs->gray = fz_keep_colorspace(ctx, base->gray); | |
| 629 default_cs->rgb = fz_keep_colorspace(ctx, base->rgb); | |
| 630 default_cs->cmyk = fz_keep_colorspace(ctx, base->cmyk); | |
| 631 default_cs->oi = fz_keep_colorspace(ctx, base->oi); | |
| 632 } | |
| 633 return default_cs; | |
| 634 } | |
| 635 | |
| 636 fz_default_colorspaces *fz_keep_default_colorspaces(fz_context *ctx, fz_default_colorspaces *default_cs) | |
| 637 { | |
| 638 return fz_keep_imp(ctx, default_cs, &default_cs->refs); | |
| 639 } | |
| 640 | |
| 641 void | |
| 642 fz_drop_default_colorspaces(fz_context *ctx, fz_default_colorspaces *default_cs) | |
| 643 { | |
| 644 if (fz_drop_imp(ctx, default_cs, &default_cs->refs)) | |
| 645 { | |
| 646 fz_drop_colorspace(ctx, default_cs->gray); | |
| 647 fz_drop_colorspace(ctx, default_cs->rgb); | |
| 648 fz_drop_colorspace(ctx, default_cs->cmyk); | |
| 649 fz_drop_colorspace(ctx, default_cs->oi); | |
| 650 fz_free(ctx, default_cs); | |
| 651 } | |
| 652 } | |
| 653 | |
| 654 fz_colorspace *fz_default_gray(fz_context *ctx, const fz_default_colorspaces *default_cs) | |
| 655 { | |
| 656 return (default_cs && default_cs->gray) ? default_cs->gray : fz_device_gray(ctx); | |
| 657 } | |
| 658 | |
| 659 fz_colorspace *fz_default_rgb(fz_context *ctx, const fz_default_colorspaces *default_cs) | |
| 660 { | |
| 661 return (default_cs && default_cs->rgb) ? default_cs->rgb : fz_device_rgb(ctx); | |
| 662 } | |
| 663 | |
| 664 fz_colorspace *fz_default_cmyk(fz_context *ctx, const fz_default_colorspaces *default_cs) | |
| 665 { | |
| 666 return (default_cs && default_cs->cmyk) ? default_cs->cmyk : fz_device_cmyk(ctx); | |
| 667 } | |
| 668 | |
| 669 fz_colorspace *fz_default_output_intent(fz_context *ctx, const fz_default_colorspaces *default_cs) | |
| 670 { | |
| 671 return default_cs ? default_cs->oi : NULL; | |
| 672 } | |
| 673 | |
| 674 void fz_set_default_gray(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs) | |
| 675 { | |
| 676 if (cs->type == FZ_COLORSPACE_GRAY && cs->n == 1) | |
| 677 { | |
| 678 fz_drop_colorspace(ctx, default_cs->gray); | |
| 679 default_cs->gray = fz_keep_colorspace(ctx, cs); | |
| 680 } | |
| 681 } | |
| 682 | |
| 683 void fz_set_default_rgb(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs) | |
| 684 { | |
| 685 if (cs->type == FZ_COLORSPACE_RGB && cs->n == 3) | |
| 686 { | |
| 687 fz_drop_colorspace(ctx, default_cs->rgb); | |
| 688 default_cs->rgb = fz_keep_colorspace(ctx, cs); | |
| 689 } | |
| 690 } | |
| 691 | |
| 692 void fz_set_default_cmyk(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs) | |
| 693 { | |
| 694 if (cs->type == FZ_COLORSPACE_CMYK && cs->n == 4) | |
| 695 { | |
| 696 fz_drop_colorspace(ctx, default_cs->cmyk); | |
| 697 default_cs->cmyk = fz_keep_colorspace(ctx, cs); | |
| 698 } | |
| 699 } | |
| 700 | |
| 701 void fz_set_default_output_intent(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs) | |
| 702 { | |
| 703 fz_drop_colorspace(ctx, default_cs->oi); | |
| 704 default_cs->oi = NULL; | |
| 705 | |
| 706 /* FIXME: Why do we set DefaultXXX along with the output intent?! */ | |
| 707 switch (cs->type) | |
| 708 { | |
| 709 default: | |
| 710 fz_warn(ctx, "Ignoring incompatible output intent: %s.", cs->name); | |
| 711 break; | |
| 712 case FZ_COLORSPACE_GRAY: | |
| 713 default_cs->oi = fz_keep_colorspace(ctx, cs); | |
| 714 if (default_cs->gray == fz_device_gray(ctx)) | |
| 715 fz_set_default_gray(ctx, default_cs, cs); | |
| 716 break; | |
| 717 case FZ_COLORSPACE_RGB: | |
| 718 default_cs->oi = fz_keep_colorspace(ctx, cs); | |
| 719 if (default_cs->rgb == fz_device_rgb(ctx)) | |
| 720 fz_set_default_rgb(ctx, default_cs, cs); | |
| 721 break; | |
| 722 case FZ_COLORSPACE_CMYK: | |
| 723 default_cs->oi = fz_keep_colorspace(ctx, cs); | |
| 724 if (default_cs->cmyk == fz_device_cmyk(ctx)) | |
| 725 fz_set_default_cmyk(ctx, default_cs, cs); | |
| 726 break; | |
| 727 } | |
| 728 } | |
| 729 | |
| 730 /* Link cache */ | |
| 731 | |
| 732 #if FZ_ENABLE_ICC | |
| 733 | |
| 734 typedef struct { | |
| 735 int refs; | |
| 736 unsigned char src_md5[16]; | |
| 737 unsigned char dst_md5[16]; | |
| 738 fz_color_params rend; | |
| 739 unsigned char src_extras; | |
| 740 unsigned char dst_extras; | |
| 741 unsigned char copy_spots; | |
| 742 unsigned char format; | |
| 743 unsigned char proof; | |
| 744 unsigned char bgr; | |
| 745 } fz_link_key; | |
| 746 | |
| 747 static void * | |
| 748 fz_keep_link_key(fz_context *ctx, void *key_) | |
| 749 { | |
| 750 fz_link_key *key = (fz_link_key *)key_; | |
| 751 return fz_keep_imp(ctx, key, &key->refs); | |
| 752 } | |
| 753 | |
| 754 static void | |
| 755 fz_drop_link_key(fz_context *ctx, void *key_) | |
| 756 { | |
| 757 fz_link_key *key = (fz_link_key *)key_; | |
| 758 if (fz_drop_imp(ctx, key, &key->refs)) | |
| 759 fz_free(ctx, key); | |
| 760 } | |
| 761 | |
| 762 static int | |
| 763 fz_cmp_link_key(fz_context *ctx, void *k0_, void *k1_) | |
| 764 { | |
| 765 fz_link_key *k0 = (fz_link_key *)k0_; | |
| 766 fz_link_key *k1 = (fz_link_key *)k1_; | |
| 767 return | |
| 768 memcmp(k0->src_md5, k1->src_md5, 16) == 0 && | |
| 769 memcmp(k0->dst_md5, k1->dst_md5, 16) == 0 && | |
| 770 k0->src_extras == k1->src_extras && | |
| 771 k0->dst_extras == k1->dst_extras && | |
| 772 k0->rend.bp == k1->rend.bp && | |
| 773 k0->rend.ri == k1->rend.ri && | |
| 774 k0->copy_spots == k1->copy_spots && | |
| 775 k0->format == k1->format && | |
| 776 k0->proof == k1->proof && | |
| 777 k0->bgr == k1->bgr; | |
| 778 } | |
| 779 | |
| 780 static void | |
| 781 fz_format_link_key(fz_context *ctx, char *s, size_t n, void *key_) | |
| 782 { | |
| 783 static const char *hex = "0123456789abcdef"; | |
| 784 fz_link_key *key = (fz_link_key *)key_; | |
| 785 char sm[33], dm[33]; | |
| 786 int i; | |
| 787 for (i = 0; i < 16; ++i) | |
| 788 { | |
| 789 sm[i*2+0] = hex[key->src_md5[i]>>4]; | |
| 790 sm[i*2+1] = hex[key->src_md5[i]&15]; | |
| 791 dm[i*2+0] = hex[key->dst_md5[i]>>4]; | |
| 792 dm[i*2+1] = hex[key->dst_md5[i]&15]; | |
| 793 } | |
| 794 sm[32] = 0; | |
| 795 dm[32] = 0; | |
| 796 fz_snprintf(s, n, "(link src_md5=%s dst_md5=%s)", sm, dm); | |
| 797 } | |
| 798 | |
| 799 static int | |
| 800 fz_make_hash_link_key(fz_context *ctx, fz_store_hash *hash, void *key_) | |
| 801 { | |
| 802 fz_link_key *key = (fz_link_key *)key_; | |
| 803 memcpy(hash->u.link.dst_md5, key->dst_md5, 16); | |
| 804 memcpy(hash->u.link.src_md5, key->src_md5, 16); | |
| 805 hash->u.link.ri = key->rend.ri; | |
| 806 hash->u.link.bp = key->rend.bp; | |
| 807 hash->u.link.src_extras = key->src_extras; | |
| 808 hash->u.link.dst_extras = key->dst_extras; | |
| 809 hash->u.link.format = key->format; | |
| 810 hash->u.link.proof = key->proof; | |
| 811 hash->u.link.copy_spots = key->copy_spots; | |
| 812 hash->u.link.bgr = key->bgr; | |
| 813 return 1; | |
| 814 } | |
| 815 | |
| 816 static fz_store_type fz_link_store_type = | |
| 817 { | |
| 818 "fz_icc_link", | |
| 819 fz_make_hash_link_key, | |
| 820 fz_keep_link_key, | |
| 821 fz_drop_link_key, | |
| 822 fz_cmp_link_key, | |
| 823 fz_format_link_key, | |
| 824 NULL | |
| 825 }; | |
| 826 | |
| 827 fz_icc_link * | |
| 828 fz_find_icc_link(fz_context *ctx, | |
| 829 fz_colorspace *src, int src_extras, | |
| 830 fz_colorspace *dst, int dst_extras, | |
| 831 fz_colorspace *prf, | |
| 832 fz_color_params rend, | |
| 833 int format, | |
| 834 int copy_spots, | |
| 835 int premult) | |
| 836 { | |
| 837 fz_icc_link *link, *old_link; | |
| 838 fz_link_key key, *new_key; | |
| 839 | |
| 840 fz_var(link); | |
| 841 | |
| 842 /* Check the storable to see if we have a copy. */ | |
| 843 key.refs = 1; | |
| 844 memcpy(&key.src_md5, src->u.icc.md5, 16); | |
| 845 memcpy(&key.dst_md5, dst->u.icc.md5, 16); | |
| 846 key.rend = rend; | |
| 847 key.src_extras = src_extras; | |
| 848 key.dst_extras = dst_extras; | |
| 849 key.copy_spots = copy_spots; | |
| 850 key.format = (format & 1) | (premult*2); | |
| 851 key.proof = (prf != NULL); | |
| 852 key.bgr = (dst->type == FZ_COLORSPACE_BGR); | |
| 853 | |
| 854 link = fz_find_item(ctx, fz_drop_icc_link_imp, &key, &fz_link_store_type); | |
| 855 if (!link) | |
| 856 { | |
| 857 new_key = fz_malloc_struct(ctx, fz_link_key); | |
| 858 memcpy(new_key, &key, sizeof (fz_link_key)); | |
| 859 fz_try(ctx) | |
| 860 { | |
| 861 link = fz_new_icc_link(ctx, src, src_extras, dst, dst_extras, prf, rend, format, copy_spots, premult); | |
| 862 old_link = fz_store_item(ctx, new_key, link, 1000, &fz_link_store_type); | |
| 863 if (old_link) | |
| 864 { | |
| 865 /* Found one while adding! Perhaps from another thread? */ | |
| 866 fz_drop_icc_link(ctx, link); | |
| 867 link = old_link; | |
| 868 } | |
| 869 } | |
| 870 fz_always(ctx) | |
| 871 { | |
| 872 fz_drop_link_key(ctx, new_key); | |
| 873 } | |
| 874 fz_catch(ctx) | |
| 875 { | |
| 876 fz_drop_icc_link(ctx, link); | |
| 877 fz_rethrow(ctx); | |
| 878 } | |
| 879 } | |
| 880 return link; | |
| 881 } | |
| 882 | |
| 883 #endif | |
| 884 | |
| 885 /* Color conversions */ | |
| 886 | |
| 887 static void indexed_via_base(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst) | |
| 888 { | |
| 889 fz_colorspace *ss = cc->ss_via; | |
| 890 const unsigned char *lookup = ss->u.indexed.lookup; | |
| 891 int high = ss->u.indexed.high; | |
| 892 int n = ss->u.indexed.base->n; | |
| 893 float base[4]; | |
| 894 int i, k; | |
| 895 | |
| 896 i = src[0] * 255; | |
| 897 i = fz_clampi(i, 0, high); | |
| 898 if (ss->u.indexed.base->type == FZ_COLORSPACE_LAB) | |
| 899 { | |
| 900 base[0] = lookup[i * 3 + 0] * 100 / 255.0f; | |
| 901 base[1] = lookup[i * 3 + 1] - 128; | |
| 902 base[2] = lookup[i * 3 + 2] - 128; | |
| 903 } | |
| 904 else | |
| 905 { | |
| 906 for (k = 0; k < n; ++k) | |
| 907 base[k] = lookup[i * n + k] / 255.0f; | |
| 908 } | |
| 909 | |
| 910 cc->convert_via(ctx, cc, base, dst); | |
| 911 } | |
| 912 | |
| 913 static void separation_via_base(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst) | |
| 914 { | |
| 915 fz_colorspace *ss = cc->ss_via; | |
| 916 float base[4]; | |
| 917 ss->u.separation.eval(ctx, ss->u.separation.tint, src, ss->n, base, ss->u.separation.base->n); | |
| 918 cc->convert_via(ctx, cc, base, dst); | |
| 919 } | |
| 920 | |
| 921 static void indexed_via_separation_via_base(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst) | |
| 922 { | |
| 923 fz_colorspace *ss = cc->ss_via; | |
| 924 fz_colorspace *ssep = cc->ss_via->u.indexed.base; | |
| 925 const unsigned char *lookup = ss->u.indexed.lookup; | |
| 926 int high = ss->u.indexed.high; | |
| 927 int n = ss->u.indexed.base->n; | |
| 928 float base[4], mid[FZ_MAX_COLORS]; | |
| 929 int i, k; | |
| 930 | |
| 931 /* First map through the index. */ | |
| 932 i = src[0] * 255; | |
| 933 i = fz_clampi(i, 0, high); | |
| 934 for (k = 0; k < n; ++k) | |
| 935 mid[k] = lookup[i * n + k] / 255.0f; | |
| 936 | |
| 937 /* Then map through the separation. */ | |
| 938 ssep->u.separation.eval(ctx, ssep->u.separation.tint, mid, ssep->n, base, ssep->u.separation.base->n); | |
| 939 | |
| 940 /* Then convert in the base. */ | |
| 941 cc->convert_via(ctx, cc, base, dst); | |
| 942 } | |
| 943 | |
| 944 static void | |
| 945 fz_init_process_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *ss, fz_colorspace *ds, fz_colorspace *is, fz_color_params params) | |
| 946 { | |
| 947 if (ss->type == FZ_COLORSPACE_INDEXED) | |
| 948 fz_throw(ctx, FZ_ERROR_ARGUMENT, "base colorspace must not be indexed"); | |
| 949 if (ss->type == FZ_COLORSPACE_SEPARATION) | |
| 950 fz_throw(ctx, FZ_ERROR_ARGUMENT, "base colorspace must not be separation"); | |
| 951 | |
| 952 #if FZ_ENABLE_ICC | |
| 953 if (ctx->icc_enabled) | |
| 954 { | |
| 955 /* Handle identity case. */ | |
| 956 if (ss == ds || (!memcmp(ss->u.icc.md5, ds->u.icc.md5, 16))) | |
| 957 { | |
| 958 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds); | |
| 959 return; | |
| 960 } | |
| 961 | |
| 962 /* Handle DeviceGray to CMYK as K only. See note in Section 6.3 of PDF spec 1.7. */ | |
| 963 if (ss->type == FZ_COLORSPACE_GRAY && (ss->flags & FZ_COLORSPACE_IS_DEVICE)) | |
| 964 { | |
| 965 if (ds->type == FZ_COLORSPACE_CMYK) | |
| 966 { | |
| 967 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds); | |
| 968 return; | |
| 969 } | |
| 970 } | |
| 971 | |
| 972 fz_try(ctx) | |
| 973 { | |
| 974 cc->link = fz_find_icc_link(ctx, ss, 0, ds, 0, is, params, 1, 0, 0); | |
| 975 cc->convert = fz_icc_transform_color; | |
| 976 } | |
| 977 fz_catch(ctx) | |
| 978 { | |
| 979 fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); | |
| 980 fz_report_error(ctx); | |
| 981 fz_warn(ctx, "cannot create ICC link, falling back to fast color conversion"); | |
| 982 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds); | |
| 983 } | |
| 984 } | |
| 985 else | |
| 986 { | |
| 987 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds); | |
| 988 } | |
| 989 #else | |
| 990 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds); | |
| 991 #endif | |
| 992 } | |
| 993 | |
| 994 void | |
| 995 fz_find_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *ss, fz_colorspace *ds, fz_separations *dseps, fz_colorspace *is, fz_color_params params) | |
| 996 { | |
| 997 cc->ds = ds; | |
| 998 cc->dseps = NULL; | |
| 999 cc->dst_n = ds->n; | |
| 1000 #if FZ_ENABLE_ICC | |
| 1001 cc->link = NULL; | |
| 1002 #endif | |
| 1003 | |
| 1004 if (ds->type == FZ_COLORSPACE_INDEXED) | |
| 1005 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot convert into Indexed colorspace."); | |
| 1006 if (ds->type == FZ_COLORSPACE_SEPARATION) | |
| 1007 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot convert into Separation colorspace."); | |
| 1008 | |
| 1009 if (ss->type == FZ_COLORSPACE_INDEXED) | |
| 1010 { | |
| 1011 if (ss->u.indexed.base->type == FZ_COLORSPACE_SEPARATION) | |
| 1012 { | |
| 1013 cc->ss = ss->u.indexed.base->u.separation.base; | |
| 1014 cc->ss_via = ss; | |
| 1015 fz_init_process_color_converter(ctx, cc, cc->ss, ds, is, params); | |
| 1016 cc->convert_via = cc->convert; | |
| 1017 cc->convert = indexed_via_separation_via_base; | |
| 1018 } | |
| 1019 else | |
| 1020 { | |
| 1021 cc->ss = ss->u.indexed.base; | |
| 1022 cc->ss_via = ss; | |
| 1023 fz_init_process_color_converter(ctx, cc, cc->ss, ds, is, params); | |
| 1024 cc->convert_via = cc->convert; | |
| 1025 cc->convert = indexed_via_base; | |
| 1026 } | |
| 1027 } | |
| 1028 else if (ss->type == FZ_COLORSPACE_SEPARATION) | |
| 1029 { | |
| 1030 if (dseps && | |
| 1031 fz_init_separation_copy_color_converter(ctx, cc, ss, ds, dseps, is, params)) | |
| 1032 { | |
| 1033 /* We can just copy separations from ss to dseps */ | |
| 1034 cc->dseps = dseps; | |
| 1035 cc->dst_n += fz_count_separations(ctx, dseps); | |
| 1036 } | |
| 1037 else | |
| 1038 { | |
| 1039 cc->ss = ss->u.separation.base; | |
| 1040 cc->ss_via = ss; | |
| 1041 fz_init_process_color_converter(ctx, cc, cc->ss, ds, is, params); | |
| 1042 cc->convert_via = cc->convert; | |
| 1043 cc->convert = separation_via_base; | |
| 1044 } | |
| 1045 } | |
| 1046 else | |
| 1047 { | |
| 1048 cc->ss = ss; | |
| 1049 fz_init_process_color_converter(ctx, cc, ss, ds, is, params); | |
| 1050 } | |
| 1051 } | |
| 1052 | |
| 1053 void | |
| 1054 fz_drop_color_converter(fz_context *ctx, fz_color_converter *cc) | |
| 1055 { | |
| 1056 #if FZ_ENABLE_ICC | |
| 1057 if (cc->link) | |
| 1058 { | |
| 1059 fz_drop_icc_link(ctx, cc->link); | |
| 1060 cc->link = NULL; | |
| 1061 } | |
| 1062 #endif | |
| 1063 } | |
| 1064 | |
| 1065 void | |
| 1066 fz_convert_color(fz_context *ctx, fz_colorspace *ss, const float *sv, fz_colorspace *ds, float *dv, fz_colorspace *is, fz_color_params params) | |
| 1067 { | |
| 1068 fz_color_converter cc; | |
| 1069 fz_find_color_converter(ctx, &cc, ss, ds, NULL, is, params); | |
| 1070 cc.convert(ctx, &cc, sv, dv); | |
| 1071 fz_drop_color_converter(ctx, &cc); | |
| 1072 } | |
| 1073 | |
| 1074 /* Cached color converter using hash table. */ | |
| 1075 | |
| 1076 typedef struct fz_cached_color_converter | |
| 1077 { | |
| 1078 fz_color_converter base; | |
| 1079 fz_hash_table *hash; | |
| 1080 } fz_cached_color_converter; | |
| 1081 | |
| 1082 static void fz_cached_color_convert(fz_context *ctx, fz_color_converter *cc_, const float *ss, float *ds) | |
| 1083 { | |
| 1084 fz_cached_color_converter *cc = cc_->opaque; | |
| 1085 if (cc->hash) | |
| 1086 { | |
| 1087 float *val = fz_hash_find(ctx, cc->hash, ss); | |
| 1088 int n = cc->base.dst_n * sizeof(float); | |
| 1089 | |
| 1090 if (val) | |
| 1091 { | |
| 1092 memcpy(ds, val, n); | |
| 1093 return; | |
| 1094 } | |
| 1095 | |
| 1096 cc->base.convert(ctx, &cc->base, ss, ds); | |
| 1097 | |
| 1098 val = Memento_label(fz_malloc_array(ctx, cc->base.dst_n, float), "cached_color_convert"); | |
| 1099 memcpy(val, ds, n); | |
| 1100 fz_try(ctx) | |
| 1101 fz_hash_insert(ctx, cc->hash, ss, val); | |
| 1102 fz_catch(ctx) | |
| 1103 { | |
| 1104 fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); | |
| 1105 fz_report_error(ctx); | |
| 1106 fz_free(ctx, val); | |
| 1107 } | |
| 1108 } | |
| 1109 else | |
| 1110 { | |
| 1111 cc->base.convert(ctx, &cc->base, ss, ds); | |
| 1112 } | |
| 1113 } | |
| 1114 | |
| 1115 void fz_init_cached_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *ss, fz_colorspace *ds, fz_separations *dseps, fz_colorspace *is, fz_color_params params) | |
| 1116 { | |
| 1117 int n = ss->n; | |
| 1118 fz_cached_color_converter *cached = fz_malloc_struct(ctx, fz_cached_color_converter); | |
| 1119 | |
| 1120 cc->opaque = cached; | |
| 1121 cc->convert = fz_cached_color_convert; | |
| 1122 cc->ss = ss; | |
| 1123 cc->ds = ds; | |
| 1124 #if FZ_ENABLE_ICC | |
| 1125 cc->link = NULL; | |
| 1126 #endif | |
| 1127 | |
| 1128 fz_try(ctx) | |
| 1129 { | |
| 1130 fz_find_color_converter(ctx, &cached->base, ss, ds, dseps, is, params); | |
| 1131 if (n * sizeof(float) <= FZ_HASH_TABLE_KEY_LENGTH) | |
| 1132 cached->hash = fz_new_hash_table(ctx, 256, n * sizeof(float), -1, fz_free); | |
| 1133 else | |
| 1134 fz_warn(ctx, "colorspace has too many components to be cached"); | |
| 1135 } | |
| 1136 fz_catch(ctx) | |
| 1137 { | |
| 1138 fz_drop_color_converter(ctx, &cached->base); | |
| 1139 fz_drop_hash_table(ctx, cached->hash); | |
| 1140 fz_free(ctx, cached); | |
| 1141 cc->opaque = NULL; | |
| 1142 fz_rethrow(ctx); | |
| 1143 } | |
| 1144 } | |
| 1145 | |
| 1146 void fz_fin_cached_color_converter(fz_context *ctx, fz_color_converter *cc_) | |
| 1147 { | |
| 1148 fz_cached_color_converter *cc; | |
| 1149 if (cc_ == NULL) | |
| 1150 return; | |
| 1151 cc = cc_->opaque; | |
| 1152 if (cc == NULL) | |
| 1153 return; | |
| 1154 cc_->opaque = NULL; | |
| 1155 fz_drop_hash_table(ctx, cc->hash); | |
| 1156 fz_drop_color_converter(ctx, &cc->base); | |
| 1157 fz_free(ctx, cc); | |
| 1158 } | |
| 1159 | |
| 1160 /* Pixmap color conversion */ | |
| 1161 | |
| 1162 static inline void | |
| 1163 template_convert_lab(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params, int sa, int da, int spots) | |
| 1164 { | |
| 1165 float srcv[FZ_MAX_COLORS]; | |
| 1166 float dstv[FZ_MAX_COLORS]; | |
| 1167 size_t w = src->w; | |
| 1168 int h = src->h; | |
| 1169 fz_colorspace *src_cs = src->colorspace; | |
| 1170 fz_colorspace *dst_cs = dst->colorspace; | |
| 1171 | |
| 1172 unsigned char *s = src->samples; | |
| 1173 unsigned char *d = dst->samples; | |
| 1174 | |
| 1175 int src_n = spots ? src->n : 3+sa; | |
| 1176 int dst_c = dst->n - (spots ? dst->s : 0) - da; | |
| 1177 int dst_n = dst->n; | |
| 1178 | |
| 1179 fz_color_converter cc; | |
| 1180 int alpha = 255; | |
| 1181 ptrdiff_t d_line_inc = dst->stride - w * dst->n; | |
| 1182 ptrdiff_t s_line_inc = src->stride - w * src->n; | |
| 1183 | |
| 1184 int k; | |
| 1185 | |
| 1186 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params); | |
| 1187 while (h--) | |
| 1188 { | |
| 1189 size_t ww = w; | |
| 1190 while (ww--) | |
| 1191 { | |
| 1192 if (sa) | |
| 1193 { | |
| 1194 alpha = s[4]; | |
| 1195 srcv[0] = fz_div255(s[0], alpha) / 255.0f * 100; | |
| 1196 srcv[1] = fz_div255(s[1], alpha) - 128; | |
| 1197 srcv[2] = fz_div255(s[2], alpha) - 128; | |
| 1198 } | |
| 1199 else | |
| 1200 { | |
| 1201 srcv[0] = s[0] / 255.0f * 100; | |
| 1202 srcv[1] = s[1] - 128; | |
| 1203 srcv[2] = s[2] - 128; | |
| 1204 } | |
| 1205 s += src_n; | |
| 1206 | |
| 1207 cc.convert(ctx, &cc, srcv, dstv); | |
| 1208 | |
| 1209 if (da) | |
| 1210 { | |
| 1211 for (k = 0; k < dst_c; k++) | |
| 1212 *d++ = fz_mul255(dstv[k] * 255, alpha); | |
| 1213 /* Just fill in spots as empty */ | |
| 1214 if (spots) | |
| 1215 for (; k < dst_n; k++) | |
| 1216 *d++ = 0; | |
| 1217 *d++ = alpha; | |
| 1218 } | |
| 1219 else | |
| 1220 { | |
| 1221 for (k = 0; k < dst_c; k++) | |
| 1222 *d++ = dstv[k] * 255; | |
| 1223 if (spots) | |
| 1224 for (; k < dst_n; k++) | |
| 1225 *d++ = 0; | |
| 1226 } | |
| 1227 } | |
| 1228 d += d_line_inc; | |
| 1229 s += s_line_inc; | |
| 1230 } | |
| 1231 fz_drop_color_converter(ctx, &cc); | |
| 1232 } | |
| 1233 | |
| 1234 static void convert_lab(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1235 { | |
| 1236 template_convert_lab(ctx, src, dst, is, params, 0, 0, 0); | |
| 1237 } | |
| 1238 | |
| 1239 static void convert_lab_sa(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1240 { | |
| 1241 template_convert_lab(ctx, src, dst, is, params, 1, 0, 0); | |
| 1242 } | |
| 1243 | |
| 1244 static void convert_lab_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1245 { | |
| 1246 template_convert_lab(ctx, src, dst, is, params, 0, 1, 0); | |
| 1247 } | |
| 1248 | |
| 1249 static void convert_lab_sa_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1250 { | |
| 1251 template_convert_lab(ctx, src, dst, is, params, 1, 1, 0); | |
| 1252 } | |
| 1253 | |
| 1254 static void convert_lab_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1255 { | |
| 1256 template_convert_lab(ctx, src, dst, is, params, 0, 0, 1); | |
| 1257 } | |
| 1258 | |
| 1259 static void convert_lab_sa_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1260 { | |
| 1261 template_convert_lab(ctx, src, dst, is, params, 1, 0, 1); | |
| 1262 } | |
| 1263 | |
| 1264 static void convert_lab_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1265 { | |
| 1266 template_convert_lab(ctx, src, dst, is, params, 0, 1, 1); | |
| 1267 } | |
| 1268 | |
| 1269 static void convert_lab_sa_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1270 { | |
| 1271 template_convert_lab(ctx, src, dst, is, params, 1, 1, 1); | |
| 1272 } | |
| 1273 | |
| 1274 static inline void | |
| 1275 template_brute_force(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params, int sa, int da, int spots) | |
| 1276 { | |
| 1277 float srcv[FZ_MAX_COLORS]; | |
| 1278 float dstv[FZ_MAX_COLORS]; | |
| 1279 size_t w = src->w; | |
| 1280 int h = src->h; | |
| 1281 fz_colorspace *src_cs = src->colorspace; | |
| 1282 fz_colorspace *dst_cs = dst->colorspace; | |
| 1283 | |
| 1284 unsigned char *s = src->samples; | |
| 1285 unsigned char *d = dst->samples; | |
| 1286 | |
| 1287 int src_c = src->n - (spots ? src->s : 0) - sa; | |
| 1288 int src_n = src->n; | |
| 1289 int dst_c = dst->n - (spots ? dst->s : 0) - da; | |
| 1290 int dst_n = dst->n; | |
| 1291 | |
| 1292 fz_color_converter cc; | |
| 1293 int alpha = 255; | |
| 1294 ptrdiff_t d_line_inc = dst->stride - w * dst->n; | |
| 1295 ptrdiff_t s_line_inc = src->stride - w * src->n; | |
| 1296 | |
| 1297 int k; | |
| 1298 | |
| 1299 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params); | |
| 1300 while (h--) | |
| 1301 { | |
| 1302 size_t ww = w; | |
| 1303 while (ww--) | |
| 1304 { | |
| 1305 if (sa) | |
| 1306 { | |
| 1307 alpha = s[src_n]; | |
| 1308 for (k = 0; k < src_c; k++) | |
| 1309 srcv[k] = fz_div255(s[k], alpha) / 255.0f; | |
| 1310 } | |
| 1311 else | |
| 1312 { | |
| 1313 for (k = 0; k < src_c; k++) | |
| 1314 srcv[k] = s[k] / 255.0f; | |
| 1315 } | |
| 1316 s += src_n; | |
| 1317 | |
| 1318 cc.convert(ctx, &cc, srcv, dstv); | |
| 1319 | |
| 1320 if (da) | |
| 1321 { | |
| 1322 for (k = 0; k < dst_c; k++) | |
| 1323 *d++ = fz_mul255(dstv[k] * 255, alpha); | |
| 1324 if (spots) | |
| 1325 for (; k < dst_n; k++) | |
| 1326 *d++ = 0; | |
| 1327 *d++ = alpha; | |
| 1328 } | |
| 1329 else | |
| 1330 { | |
| 1331 for (k = 0; k < dst_c; k++) | |
| 1332 *d++ = dstv[k] * 255; | |
| 1333 if (spots) | |
| 1334 for (; k < dst_n; k++) | |
| 1335 *d++ = 0; | |
| 1336 } | |
| 1337 } | |
| 1338 d += d_line_inc; | |
| 1339 s += s_line_inc; | |
| 1340 } | |
| 1341 fz_drop_color_converter(ctx, &cc); | |
| 1342 } | |
| 1343 | |
| 1344 static void brute_force(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1345 { | |
| 1346 template_brute_force(ctx, src, dst, is, params, 0, 0, 0); | |
| 1347 } | |
| 1348 | |
| 1349 static void brute_force_sa(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1350 { | |
| 1351 template_brute_force(ctx, src, dst, is, params, 1, 0, 0); | |
| 1352 } | |
| 1353 | |
| 1354 static void brute_force_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1355 { | |
| 1356 template_brute_force(ctx, src, dst, is, params, 0, 1, 0); | |
| 1357 } | |
| 1358 | |
| 1359 static void brute_force_sa_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1360 { | |
| 1361 template_brute_force(ctx, src, dst, is, params, 1, 1, 0); | |
| 1362 } | |
| 1363 | |
| 1364 static void brute_force_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1365 { | |
| 1366 template_brute_force(ctx, src, dst, is, params, 0, 0, 1); | |
| 1367 } | |
| 1368 | |
| 1369 static void brute_force_sa_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1370 { | |
| 1371 template_brute_force(ctx, src, dst, is, params, 1, 0, 1); | |
| 1372 } | |
| 1373 | |
| 1374 static void brute_force_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1375 { | |
| 1376 template_brute_force(ctx, src, dst, is, params, 0, 1, 1); | |
| 1377 } | |
| 1378 | |
| 1379 static void brute_force_sa_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1380 { | |
| 1381 template_brute_force(ctx, src, dst, is, params, 1, 1, 1); | |
| 1382 } | |
| 1383 | |
| 1384 static void | |
| 1385 lookup_1d(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1386 { | |
| 1387 float srcv[FZ_MAX_COLORS]; | |
| 1388 float dstv[FZ_MAX_COLORS]; | |
| 1389 size_t w = src->w; | |
| 1390 int h = src->h; | |
| 1391 fz_colorspace *src_cs = src->colorspace; | |
| 1392 fz_colorspace *dst_cs = dst->colorspace; | |
| 1393 | |
| 1394 unsigned char *s = src->samples; | |
| 1395 unsigned char *d = dst->samples; | |
| 1396 | |
| 1397 int sa = src->alpha; | |
| 1398 int da = dst->alpha; | |
| 1399 int dst_s = dst->s; | |
| 1400 int dst_n = dst->n; | |
| 1401 int dst_c = dst_n - dst_s - da; | |
| 1402 | |
| 1403 fz_color_converter cc; | |
| 1404 int alpha = 255; | |
| 1405 ptrdiff_t d_line_inc = dst->stride - w * dst->n; | |
| 1406 ptrdiff_t s_line_inc = src->stride - w * src->n; | |
| 1407 | |
| 1408 int i, k; | |
| 1409 | |
| 1410 unsigned char lookup[FZ_MAX_COLORS * 256]; | |
| 1411 | |
| 1412 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params); | |
| 1413 for (i = 0; i < 256; i++) | |
| 1414 { | |
| 1415 srcv[0] = i / 255.0f; | |
| 1416 cc.convert(ctx, &cc, srcv, dstv); | |
| 1417 for (k = 0; k < dst_c; k++) | |
| 1418 lookup[i * dst_c + k] = dstv[k] * 255; | |
| 1419 } | |
| 1420 fz_drop_color_converter(ctx, &cc); | |
| 1421 | |
| 1422 while (h--) | |
| 1423 { | |
| 1424 size_t ww = w; | |
| 1425 while (ww--) | |
| 1426 { | |
| 1427 if (sa) | |
| 1428 { | |
| 1429 alpha = s[1]; | |
| 1430 i = fz_div255(s[0], alpha); | |
| 1431 s += 2; | |
| 1432 } | |
| 1433 else | |
| 1434 { | |
| 1435 i = *s++; | |
| 1436 } | |
| 1437 | |
| 1438 if (da) | |
| 1439 { | |
| 1440 for (k = 0; k < dst_c; k++) | |
| 1441 *d++ = fz_mul255(lookup[i * dst_c + k], alpha); | |
| 1442 for (; k < dst_n; k++) | |
| 1443 *d++ = 0; | |
| 1444 *d++ = alpha; | |
| 1445 } | |
| 1446 else | |
| 1447 { | |
| 1448 for (k = 0; k < dst_c; k++) | |
| 1449 *d++ = lookup[i * dst_c + k]; | |
| 1450 for (; k < dst_n; k++) | |
| 1451 *d++ = 0; | |
| 1452 } | |
| 1453 } | |
| 1454 d += d_line_inc; | |
| 1455 s += s_line_inc; | |
| 1456 } | |
| 1457 } | |
| 1458 | |
| 1459 static void | |
| 1460 memoize_nospots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1461 { | |
| 1462 float srcv[FZ_MAX_COLORS]; | |
| 1463 float dstv[FZ_MAX_COLORS]; | |
| 1464 size_t w = src->w; | |
| 1465 int h = src->h; | |
| 1466 fz_colorspace *src_cs = src->colorspace; | |
| 1467 fz_colorspace *dst_cs = dst->colorspace; | |
| 1468 | |
| 1469 unsigned char *s = src->samples; | |
| 1470 unsigned char *d = dst->samples; | |
| 1471 | |
| 1472 int sa = src->alpha; | |
| 1473 int src_s = src->s; | |
| 1474 int src_n = src->n; | |
| 1475 int src_c = src_n - src_s - sa; | |
| 1476 int da = dst->alpha; | |
| 1477 int dst_s = dst->s; | |
| 1478 int dst_n = dst->n; | |
| 1479 int dst_c = dst_n - dst_s - da; | |
| 1480 | |
| 1481 ptrdiff_t d_line_inc = dst->stride - w * dst->n; | |
| 1482 ptrdiff_t s_line_inc = src->stride - w * src->n; | |
| 1483 | |
| 1484 int k; | |
| 1485 | |
| 1486 fz_hash_table *lookup; | |
| 1487 unsigned char *color; | |
| 1488 unsigned char dummy = s[0] ^ 255; | |
| 1489 unsigned char *sold = &dummy; | |
| 1490 unsigned char *dold; | |
| 1491 fz_color_converter cc; | |
| 1492 int alpha = 255; | |
| 1493 | |
| 1494 lookup = fz_new_hash_table(ctx, 509, src_n, -1, NULL); | |
| 1495 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params); | |
| 1496 | |
| 1497 fz_try(ctx) | |
| 1498 { | |
| 1499 while (h--) | |
| 1500 { | |
| 1501 size_t ww = w; | |
| 1502 while (ww--) | |
| 1503 { | |
| 1504 if (*s == *sold && memcmp(sold, s, src_n) == 0) | |
| 1505 { | |
| 1506 sold = s; | |
| 1507 memcpy(d, dold, dst_n); | |
| 1508 } | |
| 1509 else | |
| 1510 { | |
| 1511 sold = s; | |
| 1512 dold = d; | |
| 1513 color = fz_hash_find(ctx, lookup, s); | |
| 1514 if (color) | |
| 1515 { | |
| 1516 memcpy(d, color, dst_n); | |
| 1517 } | |
| 1518 else | |
| 1519 { | |
| 1520 if (sa) | |
| 1521 { | |
| 1522 alpha = s[src_c]; | |
| 1523 for (k = 0; k < src_c; k++) | |
| 1524 srcv[k] = fz_div255(s[k], alpha) / 255.0f; | |
| 1525 } | |
| 1526 else | |
| 1527 { | |
| 1528 for (k = 0; k < src_c; k++) | |
| 1529 srcv[k] = s[k] / 255.0f; | |
| 1530 } | |
| 1531 | |
| 1532 cc.convert(ctx, &cc, srcv, dstv); | |
| 1533 | |
| 1534 if (da) | |
| 1535 { | |
| 1536 for (k = 0; k < dst_c; k++) | |
| 1537 d[k] = fz_mul255(dstv[k] * 255, alpha); | |
| 1538 d[k] = alpha; | |
| 1539 } | |
| 1540 else | |
| 1541 { | |
| 1542 for (k = 0; k < dst_c; k++) | |
| 1543 d[k] = dstv[k] * 255; | |
| 1544 } | |
| 1545 | |
| 1546 fz_hash_insert(ctx, lookup, s, d); | |
| 1547 } | |
| 1548 } | |
| 1549 s += src_n; | |
| 1550 d += dst_n; | |
| 1551 } | |
| 1552 d += d_line_inc; | |
| 1553 s += s_line_inc; | |
| 1554 } | |
| 1555 } | |
| 1556 fz_always(ctx) | |
| 1557 { | |
| 1558 fz_drop_color_converter(ctx, &cc); | |
| 1559 fz_drop_hash_table(ctx, lookup); | |
| 1560 } | |
| 1561 fz_catch(ctx) | |
| 1562 fz_rethrow(ctx); | |
| 1563 } | |
| 1564 | |
| 1565 static void | |
| 1566 memoize_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params) | |
| 1567 { | |
| 1568 float srcv[FZ_MAX_COLORS]; | |
| 1569 float dstv[FZ_MAX_COLORS]; | |
| 1570 size_t w = src->w; | |
| 1571 int h = src->h; | |
| 1572 fz_colorspace *src_cs = src->colorspace; | |
| 1573 fz_colorspace *dst_cs = dst->colorspace; | |
| 1574 | |
| 1575 unsigned char *s = src->samples; | |
| 1576 unsigned char *d = dst->samples; | |
| 1577 | |
| 1578 int sa = src->alpha; | |
| 1579 int src_s = src->s; | |
| 1580 int src_n = src->n; | |
| 1581 int src_c = src_n - src_s - sa; | |
| 1582 int src_m = src_c + sa; | |
| 1583 int da = dst->alpha; | |
| 1584 int dst_s = dst->s; | |
| 1585 int dst_n = dst->n; | |
| 1586 int dst_c = dst_n - dst_s - da; | |
| 1587 | |
| 1588 ptrdiff_t d_line_inc = dst->stride - w * dst->n; | |
| 1589 ptrdiff_t s_line_inc = src->stride - w * src->n; | |
| 1590 | |
| 1591 int k; | |
| 1592 | |
| 1593 fz_hash_table *lookup; | |
| 1594 unsigned char *color; | |
| 1595 unsigned char sold[FZ_MAX_COLORS]; | |
| 1596 unsigned char dold[FZ_MAX_COLORS]; | |
| 1597 fz_color_converter cc; | |
| 1598 int alpha = 255; | |
| 1599 | |
| 1600 sold[0] = s[0] ^ 255; | |
| 1601 | |
| 1602 lookup = fz_new_hash_table(ctx, 509, src_m, -1, NULL); | |
| 1603 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params); | |
| 1604 | |
| 1605 fz_try(ctx) | |
| 1606 { | |
| 1607 while (h--) | |
| 1608 { | |
| 1609 size_t ww = w; | |
| 1610 while (ww--) | |
| 1611 { | |
| 1612 if (*s == *sold && memcmp(sold, s, src_m) == 0) | |
| 1613 { | |
| 1614 memcpy(d, dold, dst_c); | |
| 1615 if (dst_s) | |
| 1616 memset(d + dst_c, 0, dst_s); | |
| 1617 if (da) | |
| 1618 d[dst_n-1] = sold[src_m]; | |
| 1619 } | |
| 1620 else | |
| 1621 { | |
| 1622 memcpy(sold, s, src_m); | |
| 1623 if (sa) | |
| 1624 sold[src_m] = s[src_n-1]; | |
| 1625 color = fz_hash_find(ctx, lookup, sold); | |
| 1626 if (color) | |
| 1627 { | |
| 1628 memcpy(d, color, dst_n); | |
| 1629 } | |
| 1630 else | |
| 1631 { | |
| 1632 if (sa) | |
| 1633 { | |
| 1634 alpha = s[src_c]; | |
| 1635 for (k = 0; k < src_c; k++) | |
| 1636 srcv[k] = fz_div255(s[k], alpha) / 255.0f; | |
| 1637 } | |
| 1638 else | |
| 1639 { | |
| 1640 for (k = 0; k < src_c; k++) | |
| 1641 srcv[k] = s[k] / 255.0f; | |
| 1642 } | |
| 1643 | |
| 1644 cc.convert(ctx, &cc, srcv, dstv); | |
| 1645 | |
| 1646 if (da) | |
| 1647 { | |
| 1648 for (k = 0; k < dst_c; k++) | |
| 1649 d[k] = fz_mul255(dstv[k] * 255, alpha); | |
| 1650 if (dst_s) | |
| 1651 memset(d + dst_c, 0, dst_s); | |
| 1652 dold[dst_c] = d[dst_n-1] = alpha; | |
| 1653 } | |
| 1654 else | |
| 1655 { | |
| 1656 for (k = 0; k < dst_c; k++) | |
| 1657 d[k] = dstv[k] * 255; | |
| 1658 if (dst_s) | |
| 1659 memset(d + dst_c, 0, dst_s); | |
| 1660 } | |
| 1661 memcpy(dold, d, dst_c); | |
| 1662 | |
| 1663 fz_hash_insert(ctx, lookup, sold, dold); | |
| 1664 } | |
| 1665 } | |
| 1666 s += src_n; | |
| 1667 d += dst_n; | |
| 1668 } | |
| 1669 d += d_line_inc; | |
| 1670 s += s_line_inc; | |
| 1671 } | |
| 1672 } | |
| 1673 fz_always(ctx) | |
| 1674 { | |
| 1675 fz_drop_color_converter(ctx, &cc); | |
| 1676 fz_drop_hash_table(ctx, lookup); | |
| 1677 } | |
| 1678 fz_catch(ctx) | |
| 1679 fz_rethrow(ctx); | |
| 1680 } | |
| 1681 | |
| 1682 void | |
| 1683 fz_convert_slow_pixmap_samples(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params, int copy_spots) | |
| 1684 { | |
| 1685 int sa = src->alpha; | |
| 1686 int src_s = src->s; | |
| 1687 int src_c = src->n - src_s - sa; | |
| 1688 int da = dst->alpha; | |
| 1689 int dst_s = dst->s; | |
| 1690 size_t w = src->w; | |
| 1691 int h = src->h; | |
| 1692 ptrdiff_t d_line_inc = dst->stride - w * dst->n; | |
| 1693 ptrdiff_t s_line_inc = src->stride - w * src->n; | |
| 1694 | |
| 1695 fz_colorspace *ss = src->colorspace; | |
| 1696 | |
| 1697 if ((int)w < 0 || h < 0) | |
| 1698 return; | |
| 1699 | |
| 1700 assert(src->w == dst->w && src->h == dst->h); | |
| 1701 | |
| 1702 if (d_line_inc == 0 && s_line_inc == 0) | |
| 1703 { | |
| 1704 w *= h; | |
| 1705 h = 1; | |
| 1706 } | |
| 1707 | |
| 1708 if (src_s != 0 || dst_s != 0) | |
| 1709 { | |
| 1710 fz_warn(ctx, "Spots dropped during pixmap conversion"); | |
| 1711 } | |
| 1712 | |
| 1713 /* Special case for Lab colorspace (scaling of components to float) */ | |
| 1714 if (ss->type == FZ_COLORSPACE_LAB) | |
| 1715 { | |
| 1716 if (src_s == 0 && dst_s == 0) | |
| 1717 { | |
| 1718 if (sa) | |
| 1719 { | |
| 1720 if (da) | |
| 1721 convert_lab_sa_da(ctx, src, dst, is, params); | |
| 1722 else | |
| 1723 convert_lab_sa(ctx, src, dst, is, params); | |
| 1724 } | |
| 1725 else | |
| 1726 { | |
| 1727 if (da) | |
| 1728 convert_lab_da(ctx, src, dst, is, params); | |
| 1729 else | |
| 1730 convert_lab(ctx, src, dst, is, params); | |
| 1731 } | |
| 1732 } | |
| 1733 else | |
| 1734 { | |
| 1735 if (sa) | |
| 1736 { | |
| 1737 if (da) | |
| 1738 convert_lab_sa_da_spots(ctx, src, dst, is, params); | |
| 1739 else | |
| 1740 convert_lab_sa_spots(ctx, src, dst, is, params); | |
| 1741 } | |
| 1742 else | |
| 1743 { | |
| 1744 if (da) | |
| 1745 convert_lab_da_spots(ctx, src, dst, is, params); | |
| 1746 else | |
| 1747 convert_lab_spots(ctx, src, dst, is, params); | |
| 1748 } | |
| 1749 } | |
| 1750 } | |
| 1751 | |
| 1752 /* Brute-force for small images */ | |
| 1753 else if (w*h < 256) | |
| 1754 { | |
| 1755 if (src_s == 0 && dst_s == 0) | |
| 1756 { | |
| 1757 if (sa) | |
| 1758 { | |
| 1759 if (da) | |
| 1760 brute_force_sa_da(ctx, src, dst, is, params); | |
| 1761 else | |
| 1762 brute_force_sa(ctx, src, dst, is, params); | |
| 1763 } | |
| 1764 else | |
| 1765 { | |
| 1766 if (da) | |
| 1767 brute_force_da(ctx, src, dst, is, params); | |
| 1768 else | |
| 1769 brute_force(ctx, src, dst, is, params); | |
| 1770 } | |
| 1771 } | |
| 1772 else | |
| 1773 { | |
| 1774 if (sa) | |
| 1775 { | |
| 1776 if (da) | |
| 1777 brute_force_sa_da_spots(ctx, src, dst, is, params); | |
| 1778 else | |
| 1779 brute_force_sa_spots(ctx, src, dst, is, params); | |
| 1780 } | |
| 1781 else | |
| 1782 { | |
| 1783 if (da) | |
| 1784 brute_force_da_spots(ctx, src, dst, is, params); | |
| 1785 else | |
| 1786 brute_force_spots(ctx, src, dst, is, params); | |
| 1787 } | |
| 1788 } | |
| 1789 } | |
| 1790 | |
| 1791 /* 1-d lookup table for single channel colorspaces */ | |
| 1792 else if (src_c == 1) | |
| 1793 { | |
| 1794 lookup_1d(ctx, src, dst, is, params); | |
| 1795 } | |
| 1796 | |
| 1797 /* Memoize colors using a hash table for the general case */ | |
| 1798 else | |
| 1799 { | |
| 1800 if (src_s == 0 && dst_s == 0) | |
| 1801 memoize_nospots(ctx, src, dst, is, params); | |
| 1802 else | |
| 1803 memoize_spots(ctx, src, dst, is, params); | |
| 1804 } | |
| 1805 } | |
| 1806 | |
| 1807 void | |
| 1808 fz_convert_pixmap_samples(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, | |
| 1809 fz_colorspace *prf, | |
| 1810 const fz_default_colorspaces *default_cs, | |
| 1811 fz_color_params params, | |
| 1812 int copy_spots) | |
| 1813 { | |
| 1814 #if FZ_ENABLE_ICC | |
| 1815 fz_colorspace *ss = src->colorspace; | |
| 1816 fz_colorspace *ds = dst->colorspace; | |
| 1817 fz_pixmap *base_idx = NULL; | |
| 1818 fz_pixmap *base_sep = NULL; | |
| 1819 fz_icc_link *link = NULL; | |
| 1820 | |
| 1821 fz_var(link); | |
| 1822 fz_var(base_idx); | |
| 1823 fz_var(base_sep); | |
| 1824 | |
| 1825 if (!ds) | |
| 1826 { | |
| 1827 fz_fast_any_to_alpha(ctx, src, dst, copy_spots); | |
| 1828 return; | |
| 1829 } | |
| 1830 | |
| 1831 fz_try(ctx) | |
| 1832 { | |
| 1833 /* Treat any alpha-only pixmap as being device gray here. */ | |
| 1834 if (!ss) | |
| 1835 ss = fz_device_gray(ctx); | |
| 1836 | |
| 1837 /* Convert indexed into base colorspace. */ | |
| 1838 if (ss->type == FZ_COLORSPACE_INDEXED) | |
| 1839 { | |
| 1840 src = base_idx = fz_convert_indexed_pixmap_to_base(ctx, src); | |
| 1841 ss = src->colorspace; | |
| 1842 } | |
| 1843 | |
| 1844 /* Convert separation into base colorspace. */ | |
| 1845 if (ss->type == FZ_COLORSPACE_SEPARATION) | |
| 1846 { | |
| 1847 src = base_sep = fz_convert_separation_pixmap_to_base(ctx, src); | |
| 1848 ss = src->colorspace; | |
| 1849 } | |
| 1850 | |
| 1851 /* Substitute Device colorspace with page Default colorspace: */ | |
| 1852 if (ss->flags & FZ_COLORSPACE_IS_DEVICE) | |
| 1853 { | |
| 1854 switch (ss->type) | |
| 1855 { | |
| 1856 default: break; | |
| 1857 case FZ_COLORSPACE_GRAY: ss = fz_default_gray(ctx, default_cs); break; | |
| 1858 case FZ_COLORSPACE_RGB: ss = fz_default_rgb(ctx, default_cs); break; | |
| 1859 case FZ_COLORSPACE_CMYK: ss = fz_default_cmyk(ctx, default_cs); break; | |
| 1860 } | |
| 1861 } | |
| 1862 | |
| 1863 if (!ctx->icc_enabled) | |
| 1864 { | |
| 1865 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots); | |
| 1866 } | |
| 1867 | |
| 1868 /* Handle identity case. */ | |
| 1869 else if (ss == ds || (!memcmp(ss->u.icc.md5, ds->u.icc.md5, 16))) | |
| 1870 { | |
| 1871 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots); | |
| 1872 } | |
| 1873 | |
| 1874 /* Handle DeviceGray to CMYK as K only. See note in Section 6.3 of PDF spec 1.7. */ | |
| 1875 else if ((ss->flags & FZ_COLORSPACE_IS_DEVICE) && | |
| 1876 (ss->type == FZ_COLORSPACE_GRAY) && | |
| 1877 (ds->type == FZ_COLORSPACE_CMYK)) | |
| 1878 { | |
| 1879 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots); | |
| 1880 } | |
| 1881 | |
| 1882 /* Use slow conversion path for indexed. */ | |
| 1883 else if (ss->type == FZ_COLORSPACE_INDEXED) | |
| 1884 { | |
| 1885 fz_convert_slow_pixmap_samples(ctx, src, dst, prf, params, copy_spots); | |
| 1886 } | |
| 1887 | |
| 1888 /* Use slow conversion path for separation. */ | |
| 1889 else if (ss->type == FZ_COLORSPACE_SEPARATION) | |
| 1890 { | |
| 1891 fz_convert_slow_pixmap_samples(ctx, src, dst, prf, params, copy_spots); | |
| 1892 } | |
| 1893 | |
| 1894 else | |
| 1895 { | |
| 1896 fz_try(ctx) | |
| 1897 { | |
| 1898 int sx = src->s + src->alpha; | |
| 1899 int dx = dst->s + dst->alpha; | |
| 1900 /* If there are no spots to copy, we might as well copy spots! */ | |
| 1901 int effectively_copying_spots = copy_spots || (src->s == 0 && dst->s == 0); | |
| 1902 /* If we have alpha, we're preserving spots and we have the same number | |
| 1903 * of 'extra' (non process, spots+alpha) channels (i.e. sx == dx), then | |
| 1904 * we get lcms2 to do the premultiplication handling for us. If not, | |
| 1905 * fz_icc_transform_pixmap will have to do it by steam. */ | |
| 1906 int premult = src->alpha && (sx == dx) && effectively_copying_spots; | |
| 1907 link = fz_find_icc_link(ctx, ss, sx, ds, dx, prf, params, 0, effectively_copying_spots, premult); | |
| 1908 fz_icc_transform_pixmap(ctx, link, src, dst, effectively_copying_spots); | |
| 1909 } | |
| 1910 fz_catch(ctx) | |
| 1911 { | |
| 1912 fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); | |
| 1913 fz_report_error(ctx); | |
| 1914 fz_warn(ctx, "falling back to fast color conversion"); | |
| 1915 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots); | |
| 1916 } | |
| 1917 } | |
| 1918 } | |
| 1919 fz_always(ctx) | |
| 1920 { | |
| 1921 fz_drop_icc_link(ctx, link); | |
| 1922 fz_drop_pixmap(ctx, base_sep); | |
| 1923 fz_drop_pixmap(ctx, base_idx); | |
| 1924 } | |
| 1925 fz_catch(ctx) | |
| 1926 fz_rethrow(ctx); | |
| 1927 #else | |
| 1928 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots); | |
| 1929 #endif | |
| 1930 } | |
| 1931 | |
| 1932 void fz_colorspace_digest(fz_context *ctx, fz_colorspace *cs, unsigned char digest[16]) | |
| 1933 { | |
| 1934 #if FZ_ENABLE_ICC | |
| 1935 if (!fz_colorspace_is_icc(ctx, cs)) | |
| 1936 fz_throw(ctx, FZ_ERROR_ARGUMENT, "must have icc profile for colorspace digest"); | |
| 1937 memcpy(digest, cs->u.icc.md5, 16); | |
| 1938 #else | |
| 1939 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "ICC support disabled"); | |
| 1940 #endif | |
| 1941 } |
