color-lcms.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. // Copyright (C) 2004-2024 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. #include "mupdf/fitz.h"
  23. #include "color-imp.h"
  24. #include <string.h>
  25. #if FZ_ENABLE_ICC
  26. #ifndef LCMS_USE_FLOAT
  27. #define LCMS_USE_FLOAT 0
  28. #endif
  29. #ifdef HAVE_LCMS2MT
  30. #define GLOINIT cmsContext glo = ctx->colorspace->icc_instance;
  31. #define GLO glo,
  32. #include "lcms2mt.h"
  33. #include "lcms2mt_plugin.h"
  34. #else
  35. #define GLOINIT
  36. #define GLO
  37. #include "lcms2.h"
  38. #endif
  39. static void fz_premultiply_row(fz_context *ctx, int n, int c, int w, unsigned char *s)
  40. {
  41. unsigned char a;
  42. int k;
  43. int n1 = n-1;
  44. for (; w > 0; w--)
  45. {
  46. a = s[n1];
  47. if (a == 0)
  48. memset(s, 0, c);
  49. else if (a != 255)
  50. for (k = 0; k < c; k++)
  51. s[k] = fz_mul255(s[k], a);
  52. s += n;
  53. }
  54. }
  55. static void fz_premultiply_row_0or1(fz_context *ctx, int n, int c, int w, unsigned char *s)
  56. {
  57. unsigned char a;
  58. int n1 = n-1;
  59. for (; w > 0; w--)
  60. {
  61. a = s[n1];
  62. if (a == 0)
  63. memset(s, 0, c);
  64. s += n;
  65. }
  66. }
  67. /* Returns 0 for all the alphas being 0, 1 for them being 0 or 255, 2 otherwise. */
  68. static int fz_unmultiply_row(fz_context *ctx, int n, int c, int w, unsigned char *s, const unsigned char *in)
  69. {
  70. int a, inva;
  71. int k;
  72. int n1 = n-1;
  73. for (; w > 0; w--)
  74. {
  75. a = in[n1];
  76. if (a != 0)
  77. goto nonzero;
  78. for (k = 0; k < c; k++)
  79. s[k] = 0;
  80. for (;k < n1; k++)
  81. s[k] = in[k];
  82. s[n1] = 0;
  83. s += n;
  84. in += n;
  85. }
  86. return 0;
  87. for (; w > 0; w--)
  88. {
  89. a = in[n1];
  90. nonzero:
  91. if (a != 0 && a != 255)
  92. goto varying;
  93. k = 0;
  94. if (a == 0)
  95. for (; k < c; k++)
  96. s[k] = 0;
  97. for (;k < n; k++)
  98. s[k] = in[k];
  99. s += n;
  100. in += n;
  101. }
  102. return 1;
  103. for (; w > 0; w--)
  104. {
  105. a = in[n1];
  106. varying:
  107. if (a == 0)
  108. {
  109. for (k = 0; k < c; k++)
  110. s[k] = 0;
  111. for (;k < n1; k++)
  112. s[k] = in[k];
  113. s[k] = 0;
  114. }
  115. else if (a == 255)
  116. {
  117. memcpy(s, in, n);
  118. }
  119. else
  120. {
  121. inva = 255 * 256 / a;
  122. for (k = 0; k < c; k++)
  123. s[k] = (in[k] * inva) >> 8;
  124. for (;k < n1; k++)
  125. s[k] = in[k];
  126. s[n1] = a;
  127. }
  128. s += n;
  129. in += n;
  130. }
  131. return 2;
  132. }
  133. struct fz_icc_link
  134. {
  135. fz_storable storable;
  136. void *handle;
  137. };
  138. #ifdef HAVE_LCMS2MT
  139. static void fz_lcms_log_error(cmsContext id, cmsUInt32Number error_code, const char *error_text)
  140. {
  141. fz_context *ctx = (fz_context *)cmsGetContextUserData(id);
  142. fz_warn(ctx, "lcms: %s.", error_text);
  143. }
  144. static void *fz_lcms_malloc(cmsContext id, unsigned int size)
  145. {
  146. fz_context *ctx = cmsGetContextUserData(id);
  147. return Memento_label(fz_malloc_no_throw(ctx, size), "lcms");
  148. }
  149. static void *fz_lcms_realloc(cmsContext id, void *ptr, unsigned int size)
  150. {
  151. fz_context *ctx = cmsGetContextUserData(id);
  152. return Memento_label(fz_realloc_no_throw(ctx, ptr, size), "lcms");
  153. }
  154. static void fz_lcms_free(cmsContext id, void *ptr)
  155. {
  156. fz_context *ctx = cmsGetContextUserData(id);
  157. fz_free(ctx, ptr);
  158. }
  159. static cmsPluginMemHandler fz_lcms_memhandler =
  160. {
  161. {
  162. cmsPluginMagicNumber,
  163. LCMS_VERSION,
  164. cmsPluginMemHandlerSig,
  165. NULL
  166. },
  167. fz_lcms_malloc,
  168. fz_lcms_free,
  169. fz_lcms_realloc,
  170. NULL,
  171. NULL,
  172. NULL,
  173. };
  174. void fz_new_icc_context(fz_context *ctx)
  175. {
  176. cmsContext glo = cmsCreateContext(&fz_lcms_memhandler, ctx);
  177. if (!glo)
  178. fz_throw(ctx, FZ_ERROR_LIBRARY, "cmsCreateContext failed");
  179. ctx->colorspace->icc_instance = glo;
  180. cmsSetLogErrorHandler(glo, fz_lcms_log_error);
  181. }
  182. void fz_drop_icc_context(fz_context *ctx)
  183. {
  184. cmsContext glo = ctx->colorspace->icc_instance;
  185. if (glo)
  186. cmsDeleteContext(glo);
  187. ctx->colorspace->icc_instance = NULL;
  188. }
  189. #else
  190. static fz_context *glo_ctx = NULL;
  191. static void fz_lcms_log_error(cmsContext id, cmsUInt32Number error_code, const char *error_text)
  192. {
  193. fz_warn(glo_ctx, "lcms: %s.", error_text);
  194. }
  195. void fz_new_icc_context(fz_context *ctx)
  196. {
  197. if (glo_ctx != NULL)
  198. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Stock LCMS2 library cannot be used in multiple contexts!");
  199. glo_ctx = ctx;
  200. cmsSetLogErrorHandler(fz_lcms_log_error);
  201. }
  202. void fz_drop_icc_context(fz_context *ctx)
  203. {
  204. glo_ctx = NULL;
  205. cmsSetLogErrorHandler(NULL);
  206. }
  207. #endif
  208. fz_icc_profile *fz_new_icc_profile(fz_context *ctx, unsigned char *data, size_t size)
  209. {
  210. GLOINIT
  211. fz_icc_profile *profile;
  212. profile = cmsOpenProfileFromMem(GLO data, (cmsUInt32Number)size);
  213. if (profile == NULL)
  214. fz_throw(ctx, FZ_ERROR_FORMAT, "cmsOpenProfileFromMem failed");
  215. return profile;
  216. }
  217. int fz_icc_profile_is_lab(fz_context *ctx, fz_icc_profile *profile)
  218. {
  219. GLOINIT
  220. if (profile == NULL)
  221. return 0;
  222. return (cmsGetColorSpace(GLO profile) == cmsSigLabData);
  223. }
  224. void fz_drop_icc_profile(fz_context *ctx, fz_icc_profile *profile)
  225. {
  226. GLOINIT
  227. if (profile)
  228. cmsCloseProfile(GLO profile);
  229. }
  230. void fz_icc_profile_name(fz_context *ctx, fz_icc_profile *profile, char *name, size_t size)
  231. {
  232. GLOINIT
  233. cmsMLU *descMLU;
  234. descMLU = cmsReadTag(GLO profile, cmsSigProfileDescriptionTag);
  235. name[0] = 0;
  236. cmsMLUgetASCII(GLO descMLU, "en", "US", name, (cmsUInt32Number)size);
  237. }
  238. int fz_icc_profile_components(fz_context *ctx, fz_icc_profile *profile)
  239. {
  240. GLOINIT
  241. return cmsChannelsOf(GLO cmsGetColorSpace(GLO profile));
  242. }
  243. void fz_drop_icc_link_imp(fz_context *ctx, fz_storable *storable)
  244. {
  245. GLOINIT
  246. fz_icc_link *link = (fz_icc_link*)storable;
  247. cmsDeleteTransform(GLO link->handle);
  248. fz_free(ctx, link);
  249. }
  250. void fz_drop_icc_link(fz_context *ctx, fz_icc_link *link)
  251. {
  252. fz_drop_storable(ctx, &link->storable);
  253. }
  254. fz_icc_link *
  255. fz_new_icc_link(fz_context *ctx,
  256. fz_colorspace *src, int src_extras,
  257. fz_colorspace *dst, int dst_extras,
  258. fz_colorspace *prf,
  259. fz_color_params rend,
  260. int format,
  261. int copy_spots,
  262. int premult)
  263. {
  264. GLOINIT
  265. cmsHPROFILE src_pro = src->u.icc.profile;
  266. cmsHPROFILE dst_pro = dst->u.icc.profile;
  267. cmsHPROFILE prf_pro = prf ? prf->u.icc.profile : NULL;
  268. int src_bgr = (src->type == FZ_COLORSPACE_BGR);
  269. int dst_bgr = (dst->type == FZ_COLORSPACE_BGR);
  270. cmsColorSpaceSignature src_cs, dst_cs;
  271. cmsUInt32Number src_fmt, dst_fmt;
  272. cmsUInt32Number flags;
  273. cmsHTRANSFORM transform;
  274. fz_icc_link *link;
  275. flags = cmsFLAGS_LOWRESPRECALC;
  276. src_cs = cmsGetColorSpace(GLO src_pro);
  277. src_fmt = COLORSPACE_SH(_cmsLCMScolorSpace(GLO src_cs));
  278. src_fmt |= CHANNELS_SH(cmsChannelsOf(GLO src_cs));
  279. src_fmt |= DOSWAP_SH(src_bgr);
  280. src_fmt |= SWAPFIRST_SH(src_bgr && (src_extras > 0));
  281. #if LCMS_USE_FLOAT
  282. src_fmt |= BYTES_SH(format ? 4 : 1);
  283. src_fmt |= FLOAT_SH(format ? 1 : 0)
  284. #else
  285. src_fmt |= BYTES_SH(format ? 2 : 1);
  286. #endif
  287. src_fmt |= EXTRA_SH(src_extras);
  288. dst_cs = cmsGetColorSpace(GLO dst_pro);
  289. dst_fmt = COLORSPACE_SH(_cmsLCMScolorSpace(GLO dst_cs));
  290. dst_fmt |= CHANNELS_SH(cmsChannelsOf(GLO dst_cs));
  291. dst_fmt |= DOSWAP_SH(dst_bgr);
  292. dst_fmt |= SWAPFIRST_SH(dst_bgr && (dst_extras > 0));
  293. #if LCMS_USE_FLOAT
  294. dst_fmt |= BYTES_SH(format ? 4 : 1);
  295. dst_fmt |= FLOAT_SH(format ? 1 : 0);
  296. #else
  297. dst_fmt |= BYTES_SH(format ? 2 : 1);
  298. #endif
  299. dst_fmt |= EXTRA_SH(dst_extras);
  300. /* flags */
  301. if (rend.bp)
  302. flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
  303. if (copy_spots)
  304. flags |= cmsFLAGS_COPY_ALPHA;
  305. #ifdef cmsFLAGS_PREMULT
  306. if (premult)
  307. flags |= cmsFLAGS_PREMULT;
  308. #endif
  309. if (prf_pro == NULL)
  310. {
  311. transform = cmsCreateTransform(GLO src_pro, src_fmt, dst_pro, dst_fmt, rend.ri, flags);
  312. if (!transform)
  313. fz_throw(ctx, FZ_ERROR_LIBRARY, "cmsCreateTransform(%s,%s) failed", src->name, dst->name);
  314. }
  315. /* LCMS proof creation links don't work properly with the Ghent test files. Handle this in a brutish manner. */
  316. else if (src_pro == prf_pro)
  317. {
  318. transform = cmsCreateTransform(GLO src_pro, src_fmt, dst_pro, dst_fmt, INTENT_RELATIVE_COLORIMETRIC, flags);
  319. if (!transform)
  320. fz_throw(ctx, FZ_ERROR_LIBRARY, "cmsCreateTransform(src=proof,dst) failed");
  321. }
  322. else if (prf_pro == dst_pro)
  323. {
  324. transform = cmsCreateTransform(GLO src_pro, src_fmt, prf_pro, dst_fmt, rend.ri, flags);
  325. if (!transform)
  326. fz_throw(ctx, FZ_ERROR_LIBRARY, "cmsCreateTransform(src,proof=dst) failed");
  327. }
  328. else
  329. {
  330. cmsHPROFILE src_to_prf_pro;
  331. cmsHTRANSFORM src_to_prf_link;
  332. cmsColorSpaceSignature prf_cs;
  333. cmsUInt32Number prf_fmt;
  334. cmsHPROFILE hProfiles[3];
  335. prf_cs = cmsGetColorSpace(GLO prf_pro);
  336. prf_fmt = COLORSPACE_SH(_cmsLCMScolorSpace(GLO prf_cs));
  337. prf_fmt |= CHANNELS_SH(cmsChannelsOf(GLO prf_cs));
  338. #if LCMS_USE_FLOAT
  339. prf_fmt |= BYTES_SH(format ? 4 : 1);
  340. prf_fmt |= FLOAT_SH(format ? 1 : 0);
  341. #else
  342. prf_fmt |= BYTES_SH(format ? 2 : 1);
  343. #endif
  344. src_to_prf_link = cmsCreateTransform(GLO src_pro, src_fmt, prf_pro, prf_fmt, rend.ri, flags);
  345. if (!src_to_prf_link)
  346. fz_throw(ctx, FZ_ERROR_LIBRARY, "cmsCreateTransform(src,proof) failed");
  347. src_to_prf_pro = cmsTransform2DeviceLink(GLO src_to_prf_link, 3.4, flags);
  348. cmsDeleteTransform(GLO src_to_prf_link);
  349. if (!src_to_prf_pro)
  350. fz_throw(ctx, FZ_ERROR_LIBRARY, "cmsTransform2DeviceLink(src,proof) failed");
  351. hProfiles[0] = src_to_prf_pro;
  352. hProfiles[1] = prf_pro;
  353. hProfiles[2] = dst_pro;
  354. transform = cmsCreateMultiprofileTransform(GLO hProfiles, 3, src_fmt, dst_fmt, INTENT_RELATIVE_COLORIMETRIC, flags);
  355. cmsCloseProfile(GLO src_to_prf_pro);
  356. if (!transform)
  357. fz_throw(ctx, FZ_ERROR_LIBRARY, "cmsCreateMultiprofileTransform(src,proof,dst) failed");
  358. }
  359. fz_try(ctx)
  360. {
  361. link = fz_malloc_struct(ctx, fz_icc_link);
  362. FZ_INIT_STORABLE(link, 1, fz_drop_icc_link_imp);
  363. link->handle = transform;
  364. }
  365. fz_catch(ctx)
  366. {
  367. cmsDeleteTransform(GLO transform);
  368. fz_rethrow(ctx);
  369. }
  370. return link;
  371. }
  372. void
  373. fz_icc_transform_color(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst)
  374. {
  375. GLOINIT
  376. #if LCMS_USE_FLOAT
  377. cmsDoTransform(GLO cc->link->handle, src, dst, 1);
  378. #else
  379. uint16_t s16[FZ_MAX_COLORS];
  380. uint16_t d16[FZ_MAX_COLORS];
  381. int dn = cc->dst_n;
  382. int i;
  383. if (cc->ss->type == FZ_COLORSPACE_LAB)
  384. {
  385. s16[0] = src[0] * 655.35f;
  386. s16[1] = (src[1] + 128) * 257;
  387. s16[2] = (src[2] + 128) * 257;
  388. }
  389. else
  390. {
  391. int sn = cc->ss->n;
  392. for (i = 0; i < sn; ++i)
  393. s16[i] = src[i] * 65535;
  394. }
  395. cmsDoTransform(GLO cc->link->handle, s16, d16, 1);
  396. for (i = 0; i < dn; ++i)
  397. dst[i] = d16[i] / 65535.0f;
  398. #endif
  399. }
  400. void
  401. fz_icc_transform_pixmap(fz_context *ctx, fz_icc_link *link, const fz_pixmap *src, fz_pixmap *dst, int copy_spots)
  402. {
  403. GLOINIT
  404. int cmm_num_src, cmm_num_dst, cmm_extras;
  405. unsigned char *inputpos, *outputpos, *buffer;
  406. int ss = src->stride;
  407. int ds = dst->stride;
  408. int sw = src->w;
  409. int dw = dst->w;
  410. int sn = src->n;
  411. int dn = dst->n;
  412. int sa = src->alpha;
  413. int da = dst->alpha;
  414. int ssp = src->s;
  415. int dsp = dst->s;
  416. int sc = sn - ssp - sa;
  417. int dc = dn - dsp - da;
  418. int h = src->h;
  419. cmsUInt32Number src_format, dst_format;
  420. /* check the channels. */
  421. src_format = cmsGetTransformInputFormat(GLO link->handle);
  422. dst_format = cmsGetTransformOutputFormat(GLO link->handle);
  423. cmm_num_src = T_CHANNELS(src_format);
  424. cmm_num_dst = T_CHANNELS(dst_format);
  425. cmm_extras = T_EXTRA(src_format);
  426. if (cmm_num_src != sc || cmm_num_dst != dc || cmm_extras != ssp+sa || sa != da || (copy_spots && ssp != dsp))
  427. fz_throw(ctx, FZ_ERROR_ARGUMENT, "bad setup in ICC pixmap transform: src: %d vs %d+%d+%d, dst: %d vs %d+%d+%d", cmm_num_src, sc, ssp, sa, cmm_num_dst, dc, dsp, da);
  428. inputpos = src->samples;
  429. outputpos = dst->samples;
  430. #ifdef cmsFLAGS_PREMULT
  431. /* LCMS2MT can only handle premultiplied data if the number of 'extra'
  432. * channels is the same. If not, do it by steam. */
  433. if (sa && cmm_extras != (int)T_EXTRA(dst_format))
  434. #else
  435. /* Vanilla LCMS2 cannot handle premultiplied data. If present, do it by steam. */
  436. if (sa)
  437. #endif
  438. {
  439. buffer = fz_malloc(ctx, ss);
  440. for (; h > 0; h--)
  441. {
  442. int mult = fz_unmultiply_row(ctx, sn, sc, sw, buffer, inputpos);
  443. if (mult == 0)
  444. {
  445. /* Solid transparent row. No point in doing the transform
  446. * because it will premultiplied back to 0. */
  447. memset(outputpos, 0, ds);
  448. }
  449. else
  450. {
  451. cmsDoTransform(GLO link->handle, buffer, outputpos, sw);
  452. if (!copy_spots)
  453. {
  454. /* Copy the alpha by steam */
  455. unsigned char *d = outputpos + dn - 1;
  456. const unsigned char *s = inputpos + sn -1;
  457. int w = sw;
  458. while (w--)
  459. {
  460. *d = *s;
  461. d += dn;
  462. s += sn;
  463. }
  464. }
  465. if (mult == 1)
  466. fz_premultiply_row_0or1(ctx, dn, dc, dw, outputpos);
  467. else if (mult == 2)
  468. fz_premultiply_row(ctx, dn, dc, dw, outputpos);
  469. }
  470. inputpos += ss;
  471. outputpos += ds;
  472. }
  473. fz_free(ctx, buffer);
  474. }
  475. else
  476. for (; h > 0; h--)
  477. {
  478. cmsDoTransform(GLO link->handle, inputpos, outputpos, sw);
  479. inputpos += ss;
  480. outputpos += ds;
  481. }
  482. }
  483. #endif