| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941 |
- // Copyright (C) 2004-2025 Artifex Software, Inc.
- //
- // This file is part of MuPDF.
- //
- // MuPDF is free software: you can redistribute it and/or modify it under the
- // terms of the GNU Affero General Public License as published by the Free
- // Software Foundation, either version 3 of the License, or (at your option)
- // any later version.
- //
- // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
- // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
- // details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
- //
- // Alternative licensing terms are available from the licensor.
- // For commercial licensing, see <https://www.artifex.com/> or contact
- // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
- // CA 94129, USA, for further information.
- #include "mupdf/fitz.h"
- #include "color-imp.h"
- #include <assert.h>
- #include <math.h>
- #include <string.h>
- #if FZ_ENABLE_ICC
- #include "icc/gray.icc.h"
- #include "icc/rgb.icc.h"
- #include "icc/cmyk.icc.h"
- #include "icc/lab.icc.h"
- void fz_new_colorspace_context(fz_context *ctx)
- {
- fz_colorspace_context *cct;
- fz_buffer *gray = NULL;
- fz_buffer *rgb = NULL;
- fz_buffer *cmyk = NULL;
- fz_buffer *lab = NULL;
- fz_var(gray);
- fz_var(rgb);
- fz_var(cmyk);
- fz_var(lab);
- cct = ctx->colorspace = fz_malloc_struct(ctx, fz_colorspace_context);
- cct->ctx_refs = 1;
- fz_new_icc_context(ctx);
- ctx->icc_enabled = 1;
- fz_try(ctx)
- {
- gray = fz_new_buffer_from_shared_data(ctx, resources_icc_gray_icc, resources_icc_gray_icc_len);
- rgb = fz_new_buffer_from_shared_data(ctx, resources_icc_rgb_icc, resources_icc_rgb_icc_len);
- cmyk = fz_new_buffer_from_shared_data(ctx, resources_icc_cmyk_icc, resources_icc_cmyk_icc_len);
- lab = fz_new_buffer_from_shared_data(ctx, resources_icc_lab_icc, resources_icc_lab_icc_len);
- cct->gray = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_GRAY, FZ_COLORSPACE_IS_DEVICE, "DeviceGray", gray);
- cct->rgb = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_RGB, FZ_COLORSPACE_IS_DEVICE, "DeviceRGB", rgb);
- cct->bgr = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_BGR, FZ_COLORSPACE_IS_DEVICE, "DeviceBGR", rgb);
- cct->cmyk = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_CMYK, FZ_COLORSPACE_IS_DEVICE, "DeviceCMYK", cmyk);
- cct->lab = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_LAB, FZ_COLORSPACE_IS_DEVICE, "Lab", lab);
- }
- fz_always(ctx)
- {
- fz_drop_buffer(ctx, gray);
- fz_drop_buffer(ctx, rgb);
- fz_drop_buffer(ctx, cmyk);
- fz_drop_buffer(ctx, lab);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- }
- void fz_enable_icc(fz_context *ctx)
- {
- ctx->icc_enabled = 1;
- }
- void fz_disable_icc(fz_context *ctx)
- {
- ctx->icc_enabled = 0;
- }
- #else
- void fz_new_colorspace_context(fz_context *ctx)
- {
- fz_colorspace_context *cct;
- cct = ctx->colorspace = fz_malloc_struct(ctx, fz_colorspace_context);
- cct->ctx_refs = 1;
- cct->gray = fz_new_colorspace(ctx, FZ_COLORSPACE_GRAY, FZ_COLORSPACE_IS_DEVICE, 1, "DeviceGray");
- cct->rgb = fz_new_colorspace(ctx, FZ_COLORSPACE_RGB, FZ_COLORSPACE_IS_DEVICE, 3, "DeviceRGB");
- cct->bgr = fz_new_colorspace(ctx, FZ_COLORSPACE_BGR, FZ_COLORSPACE_IS_DEVICE, 3, "DeviceBGR");
- cct->cmyk = fz_new_colorspace(ctx, FZ_COLORSPACE_CMYK, FZ_COLORSPACE_IS_DEVICE, 4, "DeviceCMYK");
- cct->lab = fz_new_colorspace(ctx, FZ_COLORSPACE_LAB, FZ_COLORSPACE_IS_DEVICE, 3, "Lab");
- }
- void fz_enable_icc(fz_context *ctx)
- {
- fz_warn(ctx, "ICC support is not available");
- }
- void fz_disable_icc(fz_context *ctx)
- {
- }
- #endif
- fz_colorspace_context *fz_keep_colorspace_context(fz_context *ctx)
- {
- fz_keep_imp(ctx, ctx->colorspace, &ctx->colorspace->ctx_refs);
- return ctx->colorspace;
- }
- void fz_drop_colorspace_context(fz_context *ctx)
- {
- if (fz_drop_imp(ctx, ctx->colorspace, &ctx->colorspace->ctx_refs))
- {
- fz_drop_colorspace(ctx, ctx->colorspace->gray);
- fz_drop_colorspace(ctx, ctx->colorspace->rgb);
- fz_drop_colorspace(ctx, ctx->colorspace->bgr);
- fz_drop_colorspace(ctx, ctx->colorspace->cmyk);
- fz_drop_colorspace(ctx, ctx->colorspace->lab);
- #if FZ_ENABLE_ICC
- fz_drop_icc_context(ctx);
- #endif
- fz_free(ctx, ctx->colorspace);
- ctx->colorspace = NULL;
- }
- }
- fz_colorspace *fz_device_gray(fz_context *ctx)
- {
- return ctx->colorspace->gray;
- }
- fz_colorspace *fz_device_rgb(fz_context *ctx)
- {
- return ctx->colorspace->rgb;
- }
- fz_colorspace *fz_device_bgr(fz_context *ctx)
- {
- return ctx->colorspace->bgr;
- }
- fz_colorspace *fz_device_cmyk(fz_context *ctx)
- {
- return ctx->colorspace->cmyk;
- }
- fz_colorspace *fz_device_lab(fz_context *ctx)
- {
- return ctx->colorspace->lab;
- }
- /* Same order as needed by LCMS */
- static const char *fz_intent_names[] =
- {
- "Perceptual",
- "RelativeColorimetric",
- "Saturation",
- "AbsoluteColorimetric",
- };
- int fz_lookup_rendering_intent(const char *name)
- {
- int i;
- for (i = 0; i < (int)nelem(fz_intent_names); i++)
- if (!strcmp(name, fz_intent_names[i]))
- return i;
- return FZ_RI_RELATIVE_COLORIMETRIC;
- }
- const char *fz_rendering_intent_name(int ri)
- {
- if (ri >= 0 && ri < (int)nelem(fz_intent_names))
- return fz_intent_names[ri];
- return "RelativeColorimetric";
- }
- /* Colorspace feature tests */
- const char *fz_colorspace_name(fz_context *ctx, fz_colorspace *cs)
- {
- return cs ? cs->name : "None";
- }
- enum fz_colorspace_type fz_colorspace_type(fz_context *ctx, fz_colorspace *cs)
- {
- return cs ? cs->type : FZ_COLORSPACE_NONE;
- }
- int fz_colorspace_n(fz_context *ctx, fz_colorspace *cs)
- {
- return cs ? cs->n : 0;
- }
- int fz_colorspace_is_gray(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && cs->type == FZ_COLORSPACE_GRAY;
- }
- int fz_colorspace_is_rgb(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && cs->type == FZ_COLORSPACE_RGB;
- }
- int fz_colorspace_is_cmyk(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && cs->type == FZ_COLORSPACE_CMYK;
- }
- int fz_colorspace_is_lab(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && cs->type == FZ_COLORSPACE_LAB;
- }
- int fz_colorspace_is_indexed(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && (cs->type == FZ_COLORSPACE_INDEXED);
- }
- int fz_colorspace_is_device_n(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && (cs->type == FZ_COLORSPACE_SEPARATION);
- }
- int fz_colorspace_is_subtractive(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && (cs->type == FZ_COLORSPACE_CMYK || cs->type == FZ_COLORSPACE_SEPARATION);
- }
- int fz_colorspace_is_device(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && (cs->flags & FZ_COLORSPACE_IS_DEVICE);
- }
- int fz_colorspace_is_icc(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && (cs->flags & FZ_COLORSPACE_IS_ICC);
- }
- int fz_colorspace_is_lab_icc(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && (cs->type == FZ_COLORSPACE_LAB) && (cs->flags & FZ_COLORSPACE_IS_ICC);
- }
- int fz_colorspace_is_device_gray(fz_context *ctx, fz_colorspace *cs)
- {
- return fz_colorspace_is_device(ctx, cs) && fz_colorspace_is_gray(ctx, cs);
- }
- int fz_colorspace_is_device_cmyk(fz_context *ctx, fz_colorspace *cs)
- {
- return fz_colorspace_is_device(ctx, cs) && fz_colorspace_is_cmyk(ctx, cs);
- }
- int fz_colorspace_device_n_has_only_cmyk(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && ((cs->flags & FZ_COLORSPACE_HAS_CMYK_AND_SPOTS) == FZ_COLORSPACE_HAS_CMYK);
- }
- int fz_colorspace_device_n_has_cmyk(fz_context *ctx, fz_colorspace *cs)
- {
- return cs && (cs->flags & FZ_COLORSPACE_HAS_CMYK);
- }
- int fz_is_valid_blend_colorspace(fz_context *ctx, fz_colorspace *cs)
- {
- return cs == NULL ||
- cs->type == FZ_COLORSPACE_GRAY ||
- cs->type == FZ_COLORSPACE_RGB ||
- cs->type == FZ_COLORSPACE_CMYK;
- }
- fz_colorspace *fz_base_colorspace(fz_context *ctx, fz_colorspace *cs)
- {
- if (cs == NULL)
- return NULL;
- if (cs->type == FZ_COLORSPACE_INDEXED)
- return cs->u.indexed.base;
- return cs;
- }
- fz_colorspace *
- fz_keep_colorspace(fz_context *ctx, fz_colorspace *cs)
- {
- return fz_keep_key_storable(ctx, &cs->key_storable);
- }
- void
- fz_drop_colorspace(fz_context *ctx, fz_colorspace *cs)
- {
- fz_drop_key_storable(ctx, &cs->key_storable);
- }
- fz_colorspace *
- fz_keep_colorspace_store_key(fz_context *ctx, fz_colorspace *cs)
- {
- return fz_keep_key_storable_key(ctx, &cs->key_storable);
- }
- void
- fz_drop_colorspace_store_key(fz_context *ctx, fz_colorspace *cs)
- {
- fz_drop_key_storable_key(ctx, &cs->key_storable);
- }
- void
- fz_drop_colorspace_imp(fz_context *ctx, fz_storable *cs_)
- {
- fz_colorspace *cs = (fz_colorspace *)cs_;
- int i;
- if (cs->type == FZ_COLORSPACE_INDEXED)
- {
- fz_drop_colorspace(ctx, cs->u.indexed.base);
- fz_free(ctx, cs->u.indexed.lookup);
- }
- if (cs->type == FZ_COLORSPACE_SEPARATION)
- {
- fz_drop_colorspace(ctx, cs->u.separation.base);
- cs->u.separation.drop(ctx, cs->u.separation.tint);
- for (i = 0; i < FZ_MAX_COLORS; i++)
- fz_free(ctx, cs->u.separation.colorant[i]);
- }
- #if FZ_ENABLE_ICC
- if (cs->flags & FZ_COLORSPACE_IS_ICC)
- {
- fz_drop_icc_profile(ctx, cs->u.icc.profile);
- fz_drop_buffer(ctx, cs->u.icc.buffer);
- }
- #endif
- fz_free(ctx, cs->name);
- fz_free(ctx, cs);
- }
- fz_colorspace *
- fz_new_colorspace(fz_context *ctx, enum fz_colorspace_type type, int flags, int n, const char *name)
- {
- fz_colorspace *cs = fz_malloc_struct(ctx, fz_colorspace);
- FZ_INIT_KEY_STORABLE(cs, 1, fz_drop_colorspace_imp);
- if (n > FZ_MAX_COLORS)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "too many color components (%d > %d)", n, FZ_MAX_COLORS);
- if (n < 1)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "too few color components (%d < 1)", n);
- fz_try(ctx)
- {
- cs->type = type;
- cs->flags = flags;
- cs->n = n;
- cs->name = Memento_label(fz_strdup(ctx, name ? name : "UNKNOWN"), "cs_name");
- }
- fz_catch(ctx)
- {
- fz_free(ctx, cs);
- fz_rethrow(ctx);
- }
- return cs;
- }
- fz_colorspace *
- fz_new_indexed_colorspace(fz_context *ctx, fz_colorspace *base, int high, unsigned char *lookup)
- {
- fz_colorspace *cs;
- char name[100];
- if (high < 0 || high > 255)
- fz_throw(ctx, FZ_ERROR_SYNTAX, "invalid maximum value in indexed colorspace");
- fz_snprintf(name, sizeof name, "Indexed(%d,%s)", high, base->name);
- cs = fz_new_colorspace(ctx, FZ_COLORSPACE_INDEXED, 0, 1, name);
- cs->u.indexed.base = fz_keep_colorspace(ctx, base);
- cs->u.indexed.high = high;
- cs->u.indexed.lookup = lookup;
- return cs;
- }
- fz_colorspace *
- fz_new_icc_colorspace(fz_context *ctx, enum fz_colorspace_type type, int flags, const char *name, fz_buffer *buf)
- {
- #if FZ_ENABLE_ICC
- fz_icc_profile *profile = NULL;
- fz_colorspace *cs = NULL;
- unsigned char *data;
- char name_buf[100];
- size_t size;
- int n;
- fz_var(profile);
- fz_var(cs);
- fz_var(type);
- fz_try(ctx)
- {
- size = fz_buffer_storage(ctx, buf, &data);
- profile = fz_new_icc_profile(ctx, data, size);
- n = fz_icc_profile_components(ctx, profile);
- switch (type)
- {
- default:
- fz_throw(ctx, FZ_ERROR_SYNTAX, "invalid colorspace type for ICC profile");
- case FZ_COLORSPACE_NONE:
- switch (n)
- {
- default:
- fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile has unexpected number of channels: %d", n);
- case 1:
- type = FZ_COLORSPACE_GRAY;
- break;
- case 3:
- if (fz_icc_profile_is_lab(ctx, profile))
- type = FZ_COLORSPACE_LAB;
- else
- type = FZ_COLORSPACE_RGB;
- break;
- case 4:
- type = FZ_COLORSPACE_CMYK;
- break;
- }
- break;
- case FZ_COLORSPACE_GRAY:
- if (n != 1)
- fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not Gray", n);
- break;
- case FZ_COLORSPACE_RGB:
- case FZ_COLORSPACE_BGR:
- if (n != 3 || fz_icc_profile_is_lab(ctx, profile))
- fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not RGB", n);
- break;
- case FZ_COLORSPACE_LAB:
- if (n != 3 || !fz_icc_profile_is_lab(ctx, profile))
- fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not Lab", n);
- break;
- case FZ_COLORSPACE_CMYK:
- if (n != 4)
- fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not CMYK", n);
- break;
- }
- if (!name)
- {
- char cmm_name[100];
- fz_icc_profile_name(ctx, profile, cmm_name, sizeof cmm_name);
- switch (type)
- {
- default: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(%d,%s)", n, cmm_name); break;
- case FZ_COLORSPACE_GRAY: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(Gray,%s)", cmm_name); break;
- case FZ_COLORSPACE_RGB: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(RGB,%s)", cmm_name); break;
- case FZ_COLORSPACE_BGR: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(BGR,%s)", cmm_name); break;
- case FZ_COLORSPACE_CMYK: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(CMYK,%s)", cmm_name); break;
- case FZ_COLORSPACE_LAB: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(Lab,%s)", cmm_name); break;
- }
- name = name_buf;
- }
- cs = fz_new_colorspace(ctx, type, flags | FZ_COLORSPACE_IS_ICC, n, name);
- cs->u.icc.buffer = fz_keep_buffer(ctx, buf);
- cs->u.icc.profile = profile;
- fz_md5_buffer(ctx, buf, cs->u.icc.md5);
- }
- fz_catch(ctx)
- {
- fz_drop_icc_profile(ctx, profile);
- fz_drop_colorspace(ctx, cs);
- fz_rethrow(ctx);
- }
- return cs;
- #else
- switch (type)
- {
- default: fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown colorspace type");
- case FZ_COLORSPACE_GRAY: return fz_keep_colorspace(ctx, fz_device_gray(ctx));
- case FZ_COLORSPACE_RGB: return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
- case FZ_COLORSPACE_BGR: return fz_keep_colorspace(ctx, fz_device_bgr(ctx));
- case FZ_COLORSPACE_CMYK: return fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
- case FZ_COLORSPACE_LAB: return fz_keep_colorspace(ctx, fz_device_lab(ctx));
- }
- #endif
- }
- fz_colorspace *fz_new_cal_gray_colorspace(fz_context *ctx, float wp[3], float bp[3], float gamma)
- {
- #if FZ_ENABLE_ICC
- fz_buffer *buf = fz_new_icc_data_from_cal(ctx, wp, bp, &gamma, NULL, 1);
- fz_colorspace *cs;
- fz_try(ctx)
- cs = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_GRAY, 0, "CalGray", buf);
- fz_always(ctx)
- fz_drop_buffer(ctx, buf);
- fz_catch(ctx)
- fz_rethrow(ctx);
- return cs;
- #else
- return fz_keep_colorspace(ctx, fz_device_gray(ctx));
- #endif
- }
- fz_colorspace *fz_new_cal_rgb_colorspace(fz_context *ctx, float wp[3], float bp[3], float gamma[3], float matrix[9])
- {
- #if FZ_ENABLE_ICC
- fz_buffer *buf = fz_new_icc_data_from_cal(ctx, wp, bp, gamma, matrix, 3);
- fz_colorspace *cs;
- fz_try(ctx)
- cs = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_RGB, 0, "CalRGB", buf);
- fz_always(ctx)
- fz_drop_buffer(ctx, buf);
- fz_catch(ctx)
- fz_rethrow(ctx);
- return cs;
- #else
- return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
- #endif
- }
- void fz_colorspace_name_colorant(fz_context *ctx, fz_colorspace *cs, int i, const char *name)
- {
- if (i < 0 || i >= cs->n)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Attempt to name out of range colorant");
- if (cs->type != FZ_COLORSPACE_SEPARATION)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Attempt to name colorant for non-separation colorspace");
- fz_free(ctx, cs->u.separation.colorant[i]);
- cs->u.separation.colorant[i] = NULL;
- cs->u.separation.colorant[i] = fz_strdup(ctx, name);
- if (!strcmp(name, "Cyan") || !strcmp(name, "Magenta") || !strcmp(name, "Yellow") || !strcmp(name, "Black"))
- cs->flags |= FZ_COLORSPACE_HAS_CMYK;
- else
- cs->flags |= FZ_COLORSPACE_HAS_SPOTS;
- }
- const char *fz_colorspace_colorant(fz_context *ctx, fz_colorspace *cs, int i)
- {
- if (!cs || i < 0 || i >= cs->n)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Colorant out of range");
- switch (cs->type)
- {
- case FZ_COLORSPACE_NONE:
- return "None";
- case FZ_COLORSPACE_GRAY:
- return "Gray";
- case FZ_COLORSPACE_RGB:
- if (i == 0) return "Red";
- if (i == 1) return "Green";
- if (i == 2) return "Blue";
- break;
- case FZ_COLORSPACE_BGR:
- if (i == 0) return "Blue";
- if (i == 1) return "Green";
- if (i == 2) return "Red";
- break;
- case FZ_COLORSPACE_CMYK:
- if (i == 0) return "Cyan";
- if (i == 1) return "Magenta";
- if (i == 2) return "Yellow";
- if (i == 3) return "Black";
- break;
- case FZ_COLORSPACE_LAB:
- if (i == 0) return "L*";
- if (i == 1) return "a*";
- if (i == 2) return "b*";
- break;
- case FZ_COLORSPACE_INDEXED:
- return "Index";
- case FZ_COLORSPACE_SEPARATION:
- return cs->u.separation.colorant[i];
- }
- return "None";
- }
- void
- fz_clamp_color(fz_context *ctx, fz_colorspace *cs, const float *in, float *out)
- {
- if (cs->type == FZ_COLORSPACE_LAB)
- {
- out[0] = fz_clamp(in[0], 0, 100);
- out[1] = fz_clamp(in[1], -128, 127);
- out[2] = fz_clamp(in[2], -128, 127);
- }
- else if (cs->type == FZ_COLORSPACE_INDEXED)
- {
- /* round color index to integer before rescaling to hival */
- out[0] = fz_clamp((int)(in[0]+0.5), 0, cs->u.indexed.high) / 255.0f;
- }
- else
- {
- int i, n = cs->n;
- for (i = 0; i < n; ++i)
- out[i] = fz_clamp(in[i], 0, 1);
- }
- }
- const fz_color_params fz_default_color_params = { FZ_RI_RELATIVE_COLORIMETRIC, 1, 0, 0 };
- fz_default_colorspaces *fz_new_default_colorspaces(fz_context *ctx)
- {
- fz_default_colorspaces *default_cs = fz_malloc_struct(ctx, fz_default_colorspaces);
- default_cs->refs = 1;
- default_cs->gray = fz_keep_colorspace(ctx, fz_device_gray(ctx));
- default_cs->rgb = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
- default_cs->cmyk = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
- default_cs->oi = NULL;
- return default_cs;
- }
- fz_default_colorspaces *fz_clone_default_colorspaces(fz_context *ctx, fz_default_colorspaces *base)
- {
- fz_default_colorspaces *default_cs = fz_malloc_struct(ctx, fz_default_colorspaces);
- default_cs->refs = 1;
- if (base)
- {
- default_cs->gray = fz_keep_colorspace(ctx, base->gray);
- default_cs->rgb = fz_keep_colorspace(ctx, base->rgb);
- default_cs->cmyk = fz_keep_colorspace(ctx, base->cmyk);
- default_cs->oi = fz_keep_colorspace(ctx, base->oi);
- }
- return default_cs;
- }
- fz_default_colorspaces *fz_keep_default_colorspaces(fz_context *ctx, fz_default_colorspaces *default_cs)
- {
- return fz_keep_imp(ctx, default_cs, &default_cs->refs);
- }
- void
- fz_drop_default_colorspaces(fz_context *ctx, fz_default_colorspaces *default_cs)
- {
- if (fz_drop_imp(ctx, default_cs, &default_cs->refs))
- {
- fz_drop_colorspace(ctx, default_cs->gray);
- fz_drop_colorspace(ctx, default_cs->rgb);
- fz_drop_colorspace(ctx, default_cs->cmyk);
- fz_drop_colorspace(ctx, default_cs->oi);
- fz_free(ctx, default_cs);
- }
- }
- fz_colorspace *fz_default_gray(fz_context *ctx, const fz_default_colorspaces *default_cs)
- {
- return (default_cs && default_cs->gray) ? default_cs->gray : fz_device_gray(ctx);
- }
- fz_colorspace *fz_default_rgb(fz_context *ctx, const fz_default_colorspaces *default_cs)
- {
- return (default_cs && default_cs->rgb) ? default_cs->rgb : fz_device_rgb(ctx);
- }
- fz_colorspace *fz_default_cmyk(fz_context *ctx, const fz_default_colorspaces *default_cs)
- {
- return (default_cs && default_cs->cmyk) ? default_cs->cmyk : fz_device_cmyk(ctx);
- }
- fz_colorspace *fz_default_output_intent(fz_context *ctx, const fz_default_colorspaces *default_cs)
- {
- return default_cs ? default_cs->oi : NULL;
- }
- void fz_set_default_gray(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
- {
- if (cs->type == FZ_COLORSPACE_GRAY && cs->n == 1)
- {
- fz_drop_colorspace(ctx, default_cs->gray);
- default_cs->gray = fz_keep_colorspace(ctx, cs);
- }
- }
- void fz_set_default_rgb(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
- {
- if (cs->type == FZ_COLORSPACE_RGB && cs->n == 3)
- {
- fz_drop_colorspace(ctx, default_cs->rgb);
- default_cs->rgb = fz_keep_colorspace(ctx, cs);
- }
- }
- void fz_set_default_cmyk(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
- {
- if (cs->type == FZ_COLORSPACE_CMYK && cs->n == 4)
- {
- fz_drop_colorspace(ctx, default_cs->cmyk);
- default_cs->cmyk = fz_keep_colorspace(ctx, cs);
- }
- }
- void fz_set_default_output_intent(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
- {
- fz_drop_colorspace(ctx, default_cs->oi);
- default_cs->oi = NULL;
- /* FIXME: Why do we set DefaultXXX along with the output intent?! */
- switch (cs->type)
- {
- default:
- fz_warn(ctx, "Ignoring incompatible output intent: %s.", cs->name);
- break;
- case FZ_COLORSPACE_GRAY:
- default_cs->oi = fz_keep_colorspace(ctx, cs);
- if (default_cs->gray == fz_device_gray(ctx))
- fz_set_default_gray(ctx, default_cs, cs);
- break;
- case FZ_COLORSPACE_RGB:
- default_cs->oi = fz_keep_colorspace(ctx, cs);
- if (default_cs->rgb == fz_device_rgb(ctx))
- fz_set_default_rgb(ctx, default_cs, cs);
- break;
- case FZ_COLORSPACE_CMYK:
- default_cs->oi = fz_keep_colorspace(ctx, cs);
- if (default_cs->cmyk == fz_device_cmyk(ctx))
- fz_set_default_cmyk(ctx, default_cs, cs);
- break;
- }
- }
- /* Link cache */
- #if FZ_ENABLE_ICC
- typedef struct {
- int refs;
- unsigned char src_md5[16];
- unsigned char dst_md5[16];
- fz_color_params rend;
- unsigned char src_extras;
- unsigned char dst_extras;
- unsigned char copy_spots;
- unsigned char format;
- unsigned char proof;
- unsigned char bgr;
- } fz_link_key;
- static void *
- fz_keep_link_key(fz_context *ctx, void *key_)
- {
- fz_link_key *key = (fz_link_key *)key_;
- return fz_keep_imp(ctx, key, &key->refs);
- }
- static void
- fz_drop_link_key(fz_context *ctx, void *key_)
- {
- fz_link_key *key = (fz_link_key *)key_;
- if (fz_drop_imp(ctx, key, &key->refs))
- fz_free(ctx, key);
- }
- static int
- fz_cmp_link_key(fz_context *ctx, void *k0_, void *k1_)
- {
- fz_link_key *k0 = (fz_link_key *)k0_;
- fz_link_key *k1 = (fz_link_key *)k1_;
- return
- memcmp(k0->src_md5, k1->src_md5, 16) == 0 &&
- memcmp(k0->dst_md5, k1->dst_md5, 16) == 0 &&
- k0->src_extras == k1->src_extras &&
- k0->dst_extras == k1->dst_extras &&
- k0->rend.bp == k1->rend.bp &&
- k0->rend.ri == k1->rend.ri &&
- k0->copy_spots == k1->copy_spots &&
- k0->format == k1->format &&
- k0->proof == k1->proof &&
- k0->bgr == k1->bgr;
- }
- static void
- fz_format_link_key(fz_context *ctx, char *s, size_t n, void *key_)
- {
- static const char *hex = "0123456789abcdef";
- fz_link_key *key = (fz_link_key *)key_;
- char sm[33], dm[33];
- int i;
- for (i = 0; i < 16; ++i)
- {
- sm[i*2+0] = hex[key->src_md5[i]>>4];
- sm[i*2+1] = hex[key->src_md5[i]&15];
- dm[i*2+0] = hex[key->dst_md5[i]>>4];
- dm[i*2+1] = hex[key->dst_md5[i]&15];
- }
- sm[32] = 0;
- dm[32] = 0;
- fz_snprintf(s, n, "(link src_md5=%s dst_md5=%s)", sm, dm);
- }
- static int
- fz_make_hash_link_key(fz_context *ctx, fz_store_hash *hash, void *key_)
- {
- fz_link_key *key = (fz_link_key *)key_;
- memcpy(hash->u.link.dst_md5, key->dst_md5, 16);
- memcpy(hash->u.link.src_md5, key->src_md5, 16);
- hash->u.link.ri = key->rend.ri;
- hash->u.link.bp = key->rend.bp;
- hash->u.link.src_extras = key->src_extras;
- hash->u.link.dst_extras = key->dst_extras;
- hash->u.link.format = key->format;
- hash->u.link.proof = key->proof;
- hash->u.link.copy_spots = key->copy_spots;
- hash->u.link.bgr = key->bgr;
- return 1;
- }
- static fz_store_type fz_link_store_type =
- {
- "fz_icc_link",
- fz_make_hash_link_key,
- fz_keep_link_key,
- fz_drop_link_key,
- fz_cmp_link_key,
- fz_format_link_key,
- NULL
- };
- fz_icc_link *
- fz_find_icc_link(fz_context *ctx,
- fz_colorspace *src, int src_extras,
- fz_colorspace *dst, int dst_extras,
- fz_colorspace *prf,
- fz_color_params rend,
- int format,
- int copy_spots,
- int premult)
- {
- fz_icc_link *link, *old_link;
- fz_link_key key, *new_key;
- fz_var(link);
- /* Check the storable to see if we have a copy. */
- key.refs = 1;
- memcpy(&key.src_md5, src->u.icc.md5, 16);
- memcpy(&key.dst_md5, dst->u.icc.md5, 16);
- key.rend = rend;
- key.src_extras = src_extras;
- key.dst_extras = dst_extras;
- key.copy_spots = copy_spots;
- key.format = (format & 1) | (premult*2);
- key.proof = (prf != NULL);
- key.bgr = (dst->type == FZ_COLORSPACE_BGR);
- link = fz_find_item(ctx, fz_drop_icc_link_imp, &key, &fz_link_store_type);
- if (!link)
- {
- new_key = fz_malloc_struct(ctx, fz_link_key);
- memcpy(new_key, &key, sizeof (fz_link_key));
- fz_try(ctx)
- {
- link = fz_new_icc_link(ctx, src, src_extras, dst, dst_extras, prf, rend, format, copy_spots, premult);
- old_link = fz_store_item(ctx, new_key, link, 1000, &fz_link_store_type);
- if (old_link)
- {
- /* Found one while adding! Perhaps from another thread? */
- fz_drop_icc_link(ctx, link);
- link = old_link;
- }
- }
- fz_always(ctx)
- {
- fz_drop_link_key(ctx, new_key);
- }
- fz_catch(ctx)
- {
- fz_drop_icc_link(ctx, link);
- fz_rethrow(ctx);
- }
- }
- return link;
- }
- #endif
- /* Color conversions */
- static void indexed_via_base(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst)
- {
- fz_colorspace *ss = cc->ss_via;
- const unsigned char *lookup = ss->u.indexed.lookup;
- int high = ss->u.indexed.high;
- int n = ss->u.indexed.base->n;
- float base[4];
- int i, k;
- i = src[0] * 255;
- i = fz_clampi(i, 0, high);
- if (ss->u.indexed.base->type == FZ_COLORSPACE_LAB)
- {
- base[0] = lookup[i * 3 + 0] * 100 / 255.0f;
- base[1] = lookup[i * 3 + 1] - 128;
- base[2] = lookup[i * 3 + 2] - 128;
- }
- else
- {
- for (k = 0; k < n; ++k)
- base[k] = lookup[i * n + k] / 255.0f;
- }
- cc->convert_via(ctx, cc, base, dst);
- }
- static void separation_via_base(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst)
- {
- fz_colorspace *ss = cc->ss_via;
- float base[4];
- ss->u.separation.eval(ctx, ss->u.separation.tint, src, ss->n, base, ss->u.separation.base->n);
- cc->convert_via(ctx, cc, base, dst);
- }
- static void indexed_via_separation_via_base(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst)
- {
- fz_colorspace *ss = cc->ss_via;
- fz_colorspace *ssep = cc->ss_via->u.indexed.base;
- const unsigned char *lookup = ss->u.indexed.lookup;
- int high = ss->u.indexed.high;
- int n = ss->u.indexed.base->n;
- float base[4], mid[FZ_MAX_COLORS];
- int i, k;
- /* First map through the index. */
- i = src[0] * 255;
- i = fz_clampi(i, 0, high);
- for (k = 0; k < n; ++k)
- mid[k] = lookup[i * n + k] / 255.0f;
- /* Then map through the separation. */
- ssep->u.separation.eval(ctx, ssep->u.separation.tint, mid, ssep->n, base, ssep->u.separation.base->n);
- /* Then convert in the base. */
- cc->convert_via(ctx, cc, base, dst);
- }
- static void
- 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)
- {
- if (ss->type == FZ_COLORSPACE_INDEXED)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "base colorspace must not be indexed");
- if (ss->type == FZ_COLORSPACE_SEPARATION)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "base colorspace must not be separation");
- #if FZ_ENABLE_ICC
- if (ctx->icc_enabled)
- {
- /* Handle identity case. */
- if (ss == ds || (!memcmp(ss->u.icc.md5, ds->u.icc.md5, 16)))
- {
- cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
- return;
- }
- /* Handle DeviceGray to CMYK as K only. See note in Section 6.3 of PDF spec 1.7. */
- if (ss->type == FZ_COLORSPACE_GRAY && (ss->flags & FZ_COLORSPACE_IS_DEVICE))
- {
- if (ds->type == FZ_COLORSPACE_CMYK)
- {
- cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
- return;
- }
- }
- fz_try(ctx)
- {
- cc->link = fz_find_icc_link(ctx, ss, 0, ds, 0, is, params, 1, 0, 0);
- cc->convert = fz_icc_transform_color;
- }
- fz_catch(ctx)
- {
- fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
- fz_report_error(ctx);
- fz_warn(ctx, "cannot create ICC link, falling back to fast color conversion");
- cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
- }
- }
- else
- {
- cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
- }
- #else
- cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
- #endif
- }
- void
- 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)
- {
- cc->ds = ds;
- cc->dseps = NULL;
- cc->dst_n = ds->n;
- #if FZ_ENABLE_ICC
- cc->link = NULL;
- #endif
- if (ds->type == FZ_COLORSPACE_INDEXED)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot convert into Indexed colorspace.");
- if (ds->type == FZ_COLORSPACE_SEPARATION)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot convert into Separation colorspace.");
- if (ss->type == FZ_COLORSPACE_INDEXED)
- {
- if (ss->u.indexed.base->type == FZ_COLORSPACE_SEPARATION)
- {
- cc->ss = ss->u.indexed.base->u.separation.base;
- cc->ss_via = ss;
- fz_init_process_color_converter(ctx, cc, cc->ss, ds, is, params);
- cc->convert_via = cc->convert;
- cc->convert = indexed_via_separation_via_base;
- }
- else
- {
- cc->ss = ss->u.indexed.base;
- cc->ss_via = ss;
- fz_init_process_color_converter(ctx, cc, cc->ss, ds, is, params);
- cc->convert_via = cc->convert;
- cc->convert = indexed_via_base;
- }
- }
- else if (ss->type == FZ_COLORSPACE_SEPARATION)
- {
- if (dseps &&
- fz_init_separation_copy_color_converter(ctx, cc, ss, ds, dseps, is, params))
- {
- /* We can just copy separations from ss to dseps */
- cc->dseps = dseps;
- cc->dst_n += fz_count_separations(ctx, dseps);
- }
- else
- {
- cc->ss = ss->u.separation.base;
- cc->ss_via = ss;
- fz_init_process_color_converter(ctx, cc, cc->ss, ds, is, params);
- cc->convert_via = cc->convert;
- cc->convert = separation_via_base;
- }
- }
- else
- {
- cc->ss = ss;
- fz_init_process_color_converter(ctx, cc, ss, ds, is, params);
- }
- }
- void
- fz_drop_color_converter(fz_context *ctx, fz_color_converter *cc)
- {
- #if FZ_ENABLE_ICC
- if (cc->link)
- {
- fz_drop_icc_link(ctx, cc->link);
- cc->link = NULL;
- }
- #endif
- }
- void
- fz_convert_color(fz_context *ctx, fz_colorspace *ss, const float *sv, fz_colorspace *ds, float *dv, fz_colorspace *is, fz_color_params params)
- {
- fz_color_converter cc;
- fz_find_color_converter(ctx, &cc, ss, ds, NULL, is, params);
- cc.convert(ctx, &cc, sv, dv);
- fz_drop_color_converter(ctx, &cc);
- }
- /* Cached color converter using hash table. */
- typedef struct fz_cached_color_converter
- {
- fz_color_converter base;
- fz_hash_table *hash;
- } fz_cached_color_converter;
- static void fz_cached_color_convert(fz_context *ctx, fz_color_converter *cc_, const float *ss, float *ds)
- {
- fz_cached_color_converter *cc = cc_->opaque;
- if (cc->hash)
- {
- float *val = fz_hash_find(ctx, cc->hash, ss);
- int n = cc->base.dst_n * sizeof(float);
- if (val)
- {
- memcpy(ds, val, n);
- return;
- }
- cc->base.convert(ctx, &cc->base, ss, ds);
- val = Memento_label(fz_malloc_array(ctx, cc->base.dst_n, float), "cached_color_convert");
- memcpy(val, ds, n);
- fz_try(ctx)
- fz_hash_insert(ctx, cc->hash, ss, val);
- fz_catch(ctx)
- {
- fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
- fz_report_error(ctx);
- fz_free(ctx, val);
- }
- }
- else
- {
- cc->base.convert(ctx, &cc->base, ss, ds);
- }
- }
- 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)
- {
- int n = ss->n;
- fz_cached_color_converter *cached = fz_malloc_struct(ctx, fz_cached_color_converter);
- cc->opaque = cached;
- cc->convert = fz_cached_color_convert;
- cc->ss = ss;
- cc->ds = ds;
- #if FZ_ENABLE_ICC
- cc->link = NULL;
- #endif
- fz_try(ctx)
- {
- fz_find_color_converter(ctx, &cached->base, ss, ds, dseps, is, params);
- if (n * sizeof(float) <= FZ_HASH_TABLE_KEY_LENGTH)
- cached->hash = fz_new_hash_table(ctx, 256, n * sizeof(float), -1, fz_free);
- else
- fz_warn(ctx, "colorspace has too many components to be cached");
- }
- fz_catch(ctx)
- {
- fz_drop_color_converter(ctx, &cached->base);
- fz_drop_hash_table(ctx, cached->hash);
- fz_free(ctx, cached);
- cc->opaque = NULL;
- fz_rethrow(ctx);
- }
- }
- void fz_fin_cached_color_converter(fz_context *ctx, fz_color_converter *cc_)
- {
- fz_cached_color_converter *cc;
- if (cc_ == NULL)
- return;
- cc = cc_->opaque;
- if (cc == NULL)
- return;
- cc_->opaque = NULL;
- fz_drop_hash_table(ctx, cc->hash);
- fz_drop_color_converter(ctx, &cc->base);
- fz_free(ctx, cc);
- }
- /* Pixmap color conversion */
- static inline void
- 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)
- {
- float srcv[FZ_MAX_COLORS];
- float dstv[FZ_MAX_COLORS];
- size_t w = src->w;
- int h = src->h;
- fz_colorspace *src_cs = src->colorspace;
- fz_colorspace *dst_cs = dst->colorspace;
- unsigned char *s = src->samples;
- unsigned char *d = dst->samples;
- int src_n = spots ? src->n : 3+sa;
- int dst_c = dst->n - (spots ? dst->s : 0) - da;
- int dst_n = dst->n;
- fz_color_converter cc;
- int alpha = 255;
- ptrdiff_t d_line_inc = dst->stride - w * dst->n;
- ptrdiff_t s_line_inc = src->stride - w * src->n;
- int k;
- fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
- while (h--)
- {
- size_t ww = w;
- while (ww--)
- {
- if (sa)
- {
- alpha = s[4];
- srcv[0] = fz_div255(s[0], alpha) / 255.0f * 100;
- srcv[1] = fz_div255(s[1], alpha) - 128;
- srcv[2] = fz_div255(s[2], alpha) - 128;
- }
- else
- {
- srcv[0] = s[0] / 255.0f * 100;
- srcv[1] = s[1] - 128;
- srcv[2] = s[2] - 128;
- }
- s += src_n;
- cc.convert(ctx, &cc, srcv, dstv);
- if (da)
- {
- for (k = 0; k < dst_c; k++)
- *d++ = fz_mul255(dstv[k] * 255, alpha);
- /* Just fill in spots as empty */
- if (spots)
- for (; k < dst_n; k++)
- *d++ = 0;
- *d++ = alpha;
- }
- else
- {
- for (k = 0; k < dst_c; k++)
- *d++ = dstv[k] * 255;
- if (spots)
- for (; k < dst_n; k++)
- *d++ = 0;
- }
- }
- d += d_line_inc;
- s += s_line_inc;
- }
- fz_drop_color_converter(ctx, &cc);
- }
- static void convert_lab(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_convert_lab(ctx, src, dst, is, params, 0, 0, 0);
- }
- static void convert_lab_sa(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_convert_lab(ctx, src, dst, is, params, 1, 0, 0);
- }
- static void convert_lab_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_convert_lab(ctx, src, dst, is, params, 0, 1, 0);
- }
- static void convert_lab_sa_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_convert_lab(ctx, src, dst, is, params, 1, 1, 0);
- }
- static void convert_lab_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_convert_lab(ctx, src, dst, is, params, 0, 0, 1);
- }
- static void convert_lab_sa_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_convert_lab(ctx, src, dst, is, params, 1, 0, 1);
- }
- static void convert_lab_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_convert_lab(ctx, src, dst, is, params, 0, 1, 1);
- }
- static void convert_lab_sa_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_convert_lab(ctx, src, dst, is, params, 1, 1, 1);
- }
- static inline void
- 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)
- {
- float srcv[FZ_MAX_COLORS];
- float dstv[FZ_MAX_COLORS];
- size_t w = src->w;
- int h = src->h;
- fz_colorspace *src_cs = src->colorspace;
- fz_colorspace *dst_cs = dst->colorspace;
- unsigned char *s = src->samples;
- unsigned char *d = dst->samples;
- int src_c = src->n - (spots ? src->s : 0) - sa;
- int src_n = src->n;
- int dst_c = dst->n - (spots ? dst->s : 0) - da;
- int dst_n = dst->n;
- fz_color_converter cc;
- int alpha = 255;
- ptrdiff_t d_line_inc = dst->stride - w * dst->n;
- ptrdiff_t s_line_inc = src->stride - w * src->n;
- int k;
- fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
- while (h--)
- {
- size_t ww = w;
- while (ww--)
- {
- if (sa)
- {
- alpha = s[src_n];
- for (k = 0; k < src_c; k++)
- srcv[k] = fz_div255(s[k], alpha) / 255.0f;
- }
- else
- {
- for (k = 0; k < src_c; k++)
- srcv[k] = s[k] / 255.0f;
- }
- s += src_n;
- cc.convert(ctx, &cc, srcv, dstv);
- if (da)
- {
- for (k = 0; k < dst_c; k++)
- *d++ = fz_mul255(dstv[k] * 255, alpha);
- if (spots)
- for (; k < dst_n; k++)
- *d++ = 0;
- *d++ = alpha;
- }
- else
- {
- for (k = 0; k < dst_c; k++)
- *d++ = dstv[k] * 255;
- if (spots)
- for (; k < dst_n; k++)
- *d++ = 0;
- }
- }
- d += d_line_inc;
- s += s_line_inc;
- }
- fz_drop_color_converter(ctx, &cc);
- }
- static void brute_force(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_brute_force(ctx, src, dst, is, params, 0, 0, 0);
- }
- static void brute_force_sa(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_brute_force(ctx, src, dst, is, params, 1, 0, 0);
- }
- static void brute_force_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_brute_force(ctx, src, dst, is, params, 0, 1, 0);
- }
- static void brute_force_sa_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_brute_force(ctx, src, dst, is, params, 1, 1, 0);
- }
- static void brute_force_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_brute_force(ctx, src, dst, is, params, 0, 0, 1);
- }
- static void brute_force_sa_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_brute_force(ctx, src, dst, is, params, 1, 0, 1);
- }
- static void brute_force_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_brute_force(ctx, src, dst, is, params, 0, 1, 1);
- }
- static void brute_force_sa_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- template_brute_force(ctx, src, dst, is, params, 1, 1, 1);
- }
- static void
- lookup_1d(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- float srcv[FZ_MAX_COLORS];
- float dstv[FZ_MAX_COLORS];
- size_t w = src->w;
- int h = src->h;
- fz_colorspace *src_cs = src->colorspace;
- fz_colorspace *dst_cs = dst->colorspace;
- unsigned char *s = src->samples;
- unsigned char *d = dst->samples;
- int sa = src->alpha;
- int da = dst->alpha;
- int dst_s = dst->s;
- int dst_n = dst->n;
- int dst_c = dst_n - dst_s - da;
- fz_color_converter cc;
- int alpha = 255;
- ptrdiff_t d_line_inc = dst->stride - w * dst->n;
- ptrdiff_t s_line_inc = src->stride - w * src->n;
- int i, k;
- unsigned char lookup[FZ_MAX_COLORS * 256];
- fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
- for (i = 0; i < 256; i++)
- {
- srcv[0] = i / 255.0f;
- cc.convert(ctx, &cc, srcv, dstv);
- for (k = 0; k < dst_c; k++)
- lookup[i * dst_c + k] = dstv[k] * 255;
- }
- fz_drop_color_converter(ctx, &cc);
- while (h--)
- {
- size_t ww = w;
- while (ww--)
- {
- if (sa)
- {
- alpha = s[1];
- i = fz_div255(s[0], alpha);
- s += 2;
- }
- else
- {
- i = *s++;
- }
- if (da)
- {
- for (k = 0; k < dst_c; k++)
- *d++ = fz_mul255(lookup[i * dst_c + k], alpha);
- for (; k < dst_n; k++)
- *d++ = 0;
- *d++ = alpha;
- }
- else
- {
- for (k = 0; k < dst_c; k++)
- *d++ = lookup[i * dst_c + k];
- for (; k < dst_n; k++)
- *d++ = 0;
- }
- }
- d += d_line_inc;
- s += s_line_inc;
- }
- }
- static void
- memoize_nospots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- float srcv[FZ_MAX_COLORS];
- float dstv[FZ_MAX_COLORS];
- size_t w = src->w;
- int h = src->h;
- fz_colorspace *src_cs = src->colorspace;
- fz_colorspace *dst_cs = dst->colorspace;
- unsigned char *s = src->samples;
- unsigned char *d = dst->samples;
- int sa = src->alpha;
- int src_s = src->s;
- int src_n = src->n;
- int src_c = src_n - src_s - sa;
- int da = dst->alpha;
- int dst_s = dst->s;
- int dst_n = dst->n;
- int dst_c = dst_n - dst_s - da;
- ptrdiff_t d_line_inc = dst->stride - w * dst->n;
- ptrdiff_t s_line_inc = src->stride - w * src->n;
- int k;
- fz_hash_table *lookup;
- unsigned char *color;
- unsigned char dummy = s[0] ^ 255;
- unsigned char *sold = &dummy;
- unsigned char *dold;
- fz_color_converter cc;
- int alpha = 255;
- lookup = fz_new_hash_table(ctx, 509, src_n, -1, NULL);
- fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
- fz_try(ctx)
- {
- while (h--)
- {
- size_t ww = w;
- while (ww--)
- {
- if (*s == *sold && memcmp(sold, s, src_n) == 0)
- {
- sold = s;
- memcpy(d, dold, dst_n);
- }
- else
- {
- sold = s;
- dold = d;
- color = fz_hash_find(ctx, lookup, s);
- if (color)
- {
- memcpy(d, color, dst_n);
- }
- else
- {
- if (sa)
- {
- alpha = s[src_c];
- for (k = 0; k < src_c; k++)
- srcv[k] = fz_div255(s[k], alpha) / 255.0f;
- }
- else
- {
- for (k = 0; k < src_c; k++)
- srcv[k] = s[k] / 255.0f;
- }
- cc.convert(ctx, &cc, srcv, dstv);
- if (da)
- {
- for (k = 0; k < dst_c; k++)
- d[k] = fz_mul255(dstv[k] * 255, alpha);
- d[k] = alpha;
- }
- else
- {
- for (k = 0; k < dst_c; k++)
- d[k] = dstv[k] * 255;
- }
- fz_hash_insert(ctx, lookup, s, d);
- }
- }
- s += src_n;
- d += dst_n;
- }
- d += d_line_inc;
- s += s_line_inc;
- }
- }
- fz_always(ctx)
- {
- fz_drop_color_converter(ctx, &cc);
- fz_drop_hash_table(ctx, lookup);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- static void
- memoize_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
- {
- float srcv[FZ_MAX_COLORS];
- float dstv[FZ_MAX_COLORS];
- size_t w = src->w;
- int h = src->h;
- fz_colorspace *src_cs = src->colorspace;
- fz_colorspace *dst_cs = dst->colorspace;
- unsigned char *s = src->samples;
- unsigned char *d = dst->samples;
- int sa = src->alpha;
- int src_s = src->s;
- int src_n = src->n;
- int src_c = src_n - src_s - sa;
- int src_m = src_c + sa;
- int da = dst->alpha;
- int dst_s = dst->s;
- int dst_n = dst->n;
- int dst_c = dst_n - dst_s - da;
- ptrdiff_t d_line_inc = dst->stride - w * dst->n;
- ptrdiff_t s_line_inc = src->stride - w * src->n;
- int k;
- fz_hash_table *lookup;
- unsigned char *color;
- unsigned char sold[FZ_MAX_COLORS];
- unsigned char dold[FZ_MAX_COLORS];
- fz_color_converter cc;
- int alpha = 255;
- sold[0] = s[0] ^ 255;
- lookup = fz_new_hash_table(ctx, 509, src_m, -1, NULL);
- fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
- fz_try(ctx)
- {
- while (h--)
- {
- size_t ww = w;
- while (ww--)
- {
- if (*s == *sold && memcmp(sold, s, src_m) == 0)
- {
- memcpy(d, dold, dst_c);
- if (dst_s)
- memset(d + dst_c, 0, dst_s);
- if (da)
- d[dst_n-1] = sold[src_m];
- }
- else
- {
- memcpy(sold, s, src_m);
- if (sa)
- sold[src_m] = s[src_n-1];
- color = fz_hash_find(ctx, lookup, sold);
- if (color)
- {
- memcpy(d, color, dst_n);
- }
- else
- {
- if (sa)
- {
- alpha = s[src_c];
- for (k = 0; k < src_c; k++)
- srcv[k] = fz_div255(s[k], alpha) / 255.0f;
- }
- else
- {
- for (k = 0; k < src_c; k++)
- srcv[k] = s[k] / 255.0f;
- }
- cc.convert(ctx, &cc, srcv, dstv);
- if (da)
- {
- for (k = 0; k < dst_c; k++)
- d[k] = fz_mul255(dstv[k] * 255, alpha);
- if (dst_s)
- memset(d + dst_c, 0, dst_s);
- dold[dst_c] = d[dst_n-1] = alpha;
- }
- else
- {
- for (k = 0; k < dst_c; k++)
- d[k] = dstv[k] * 255;
- if (dst_s)
- memset(d + dst_c, 0, dst_s);
- }
- memcpy(dold, d, dst_c);
- fz_hash_insert(ctx, lookup, sold, dold);
- }
- }
- s += src_n;
- d += dst_n;
- }
- d += d_line_inc;
- s += s_line_inc;
- }
- }
- fz_always(ctx)
- {
- fz_drop_color_converter(ctx, &cc);
- fz_drop_hash_table(ctx, lookup);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- void
- 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)
- {
- int sa = src->alpha;
- int src_s = src->s;
- int src_c = src->n - src_s - sa;
- int da = dst->alpha;
- int dst_s = dst->s;
- size_t w = src->w;
- int h = src->h;
- ptrdiff_t d_line_inc = dst->stride - w * dst->n;
- ptrdiff_t s_line_inc = src->stride - w * src->n;
- fz_colorspace *ss = src->colorspace;
- if ((int)w < 0 || h < 0)
- return;
- assert(src->w == dst->w && src->h == dst->h);
- if (d_line_inc == 0 && s_line_inc == 0)
- {
- w *= h;
- h = 1;
- }
- if (src_s != 0 || dst_s != 0)
- {
- fz_warn(ctx, "Spots dropped during pixmap conversion");
- }
- /* Special case for Lab colorspace (scaling of components to float) */
- if (ss->type == FZ_COLORSPACE_LAB)
- {
- if (src_s == 0 && dst_s == 0)
- {
- if (sa)
- {
- if (da)
- convert_lab_sa_da(ctx, src, dst, is, params);
- else
- convert_lab_sa(ctx, src, dst, is, params);
- }
- else
- {
- if (da)
- convert_lab_da(ctx, src, dst, is, params);
- else
- convert_lab(ctx, src, dst, is, params);
- }
- }
- else
- {
- if (sa)
- {
- if (da)
- convert_lab_sa_da_spots(ctx, src, dst, is, params);
- else
- convert_lab_sa_spots(ctx, src, dst, is, params);
- }
- else
- {
- if (da)
- convert_lab_da_spots(ctx, src, dst, is, params);
- else
- convert_lab_spots(ctx, src, dst, is, params);
- }
- }
- }
- /* Brute-force for small images */
- else if (w*h < 256)
- {
- if (src_s == 0 && dst_s == 0)
- {
- if (sa)
- {
- if (da)
- brute_force_sa_da(ctx, src, dst, is, params);
- else
- brute_force_sa(ctx, src, dst, is, params);
- }
- else
- {
- if (da)
- brute_force_da(ctx, src, dst, is, params);
- else
- brute_force(ctx, src, dst, is, params);
- }
- }
- else
- {
- if (sa)
- {
- if (da)
- brute_force_sa_da_spots(ctx, src, dst, is, params);
- else
- brute_force_sa_spots(ctx, src, dst, is, params);
- }
- else
- {
- if (da)
- brute_force_da_spots(ctx, src, dst, is, params);
- else
- brute_force_spots(ctx, src, dst, is, params);
- }
- }
- }
- /* 1-d lookup table for single channel colorspaces */
- else if (src_c == 1)
- {
- lookup_1d(ctx, src, dst, is, params);
- }
- /* Memoize colors using a hash table for the general case */
- else
- {
- if (src_s == 0 && dst_s == 0)
- memoize_nospots(ctx, src, dst, is, params);
- else
- memoize_spots(ctx, src, dst, is, params);
- }
- }
- void
- fz_convert_pixmap_samples(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst,
- fz_colorspace *prf,
- const fz_default_colorspaces *default_cs,
- fz_color_params params,
- int copy_spots)
- {
- #if FZ_ENABLE_ICC
- fz_colorspace *ss = src->colorspace;
- fz_colorspace *ds = dst->colorspace;
- fz_pixmap *base_idx = NULL;
- fz_pixmap *base_sep = NULL;
- fz_icc_link *link = NULL;
- fz_var(link);
- fz_var(base_idx);
- fz_var(base_sep);
- if (!ds)
- {
- fz_fast_any_to_alpha(ctx, src, dst, copy_spots);
- return;
- }
- fz_try(ctx)
- {
- /* Treat any alpha-only pixmap as being device gray here. */
- if (!ss)
- ss = fz_device_gray(ctx);
- /* Convert indexed into base colorspace. */
- if (ss->type == FZ_COLORSPACE_INDEXED)
- {
- src = base_idx = fz_convert_indexed_pixmap_to_base(ctx, src);
- ss = src->colorspace;
- }
- /* Convert separation into base colorspace. */
- if (ss->type == FZ_COLORSPACE_SEPARATION)
- {
- src = base_sep = fz_convert_separation_pixmap_to_base(ctx, src);
- ss = src->colorspace;
- }
- /* Substitute Device colorspace with page Default colorspace: */
- if (ss->flags & FZ_COLORSPACE_IS_DEVICE)
- {
- switch (ss->type)
- {
- default: break;
- case FZ_COLORSPACE_GRAY: ss = fz_default_gray(ctx, default_cs); break;
- case FZ_COLORSPACE_RGB: ss = fz_default_rgb(ctx, default_cs); break;
- case FZ_COLORSPACE_CMYK: ss = fz_default_cmyk(ctx, default_cs); break;
- }
- }
- if (!ctx->icc_enabled)
- {
- fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
- }
- /* Handle identity case. */
- else if (ss == ds || (!memcmp(ss->u.icc.md5, ds->u.icc.md5, 16)))
- {
- fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
- }
- /* Handle DeviceGray to CMYK as K only. See note in Section 6.3 of PDF spec 1.7. */
- else if ((ss->flags & FZ_COLORSPACE_IS_DEVICE) &&
- (ss->type == FZ_COLORSPACE_GRAY) &&
- (ds->type == FZ_COLORSPACE_CMYK))
- {
- fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
- }
- /* Use slow conversion path for indexed. */
- else if (ss->type == FZ_COLORSPACE_INDEXED)
- {
- fz_convert_slow_pixmap_samples(ctx, src, dst, prf, params, copy_spots);
- }
- /* Use slow conversion path for separation. */
- else if (ss->type == FZ_COLORSPACE_SEPARATION)
- {
- fz_convert_slow_pixmap_samples(ctx, src, dst, prf, params, copy_spots);
- }
- else
- {
- fz_try(ctx)
- {
- int sx = src->s + src->alpha;
- int dx = dst->s + dst->alpha;
- /* If there are no spots to copy, we might as well copy spots! */
- int effectively_copying_spots = copy_spots || (src->s == 0 && dst->s == 0);
- /* If we have alpha, we're preserving spots and we have the same number
- * of 'extra' (non process, spots+alpha) channels (i.e. sx == dx), then
- * we get lcms2 to do the premultiplication handling for us. If not,
- * fz_icc_transform_pixmap will have to do it by steam. */
- int premult = src->alpha && (sx == dx) && effectively_copying_spots;
- link = fz_find_icc_link(ctx, ss, sx, ds, dx, prf, params, 0, effectively_copying_spots, premult);
- fz_icc_transform_pixmap(ctx, link, src, dst, effectively_copying_spots);
- }
- fz_catch(ctx)
- {
- fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
- fz_report_error(ctx);
- fz_warn(ctx, "falling back to fast color conversion");
- fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
- }
- }
- }
- fz_always(ctx)
- {
- fz_drop_icc_link(ctx, link);
- fz_drop_pixmap(ctx, base_sep);
- fz_drop_pixmap(ctx, base_idx);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- #else
- fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
- #endif
- }
- void fz_colorspace_digest(fz_context *ctx, fz_colorspace *cs, unsigned char digest[16])
- {
- #if FZ_ENABLE_ICC
- if (!fz_colorspace_is_icc(ctx, cs))
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "must have icc profile for colorspace digest");
- memcpy(digest, cs->u.icc.md5, 16);
- #else
- fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "ICC support disabled");
- #endif
- }
|