From 9f1e0390c1dfb5643fd3cc419e28888f1d17a95b Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Mon, 12 Feb 2024 14:03:36 +0100 Subject: [PATCH] Add color-managed CMYK to RGB conversion (#3288) --- Cargo.lock | 7 +++ Cargo.toml | 1 + NOTICE | 3 +- crates/typst/Cargo.toml | 1 + .../typst/assets/CGATS001Compat-v2-micro.icc | Bin 0 -> 8464 bytes crates/typst/src/visualize/color.rs | 53 ++++++++++++++++-- tests/ref/bugs/gradient-cmyk-encode.png | Bin 875 -> 1601 bytes tests/ref/compiler/color.png | Bin 1344 -> 1407 bytes tests/typ/compiler/methods.typ | 4 +- 9 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 crates/typst/assets/CGATS001Compat-v2-micro.icc diff --git a/Cargo.lock b/Cargo.lock index 842e9abe7..df30f43dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1778,6 +1778,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "qcms" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edecfcd5d755a5e5d98e24cf43113e7cdaec5a070edd0f6b250c03a573da30fa" + [[package]] name = "quick-xml" version = "0.28.2" @@ -2554,6 +2560,7 @@ dependencies = [ "log", "once_cell", "palette", + "qcms", "phf", "rayon", "regex", diff --git a/Cargo.toml b/Cargo.toml index b711b35f6..9da3f904b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ pixglyph = "0.3" proc-macro2 = "1" pulldown-cmark = "0.9" quote = "1" +qcms = "0.3.0" rayon = "1.7.0" regex = "1" resvg = { version = "0.38.0", default-features = false, features = ["raster-images"] } diff --git a/NOTICE b/NOTICE index 1291b0b3f..129ee3214 100644 --- a/NOTICE +++ b/NOTICE @@ -2,7 +2,8 @@ Licenses for third party components used by this project can be found below. ================================================================================ The Creative Commons Zero v1.0 Universal License applies to: -* The ICC profiles found in `crates/typst/icc/*` +* The ICC profiles found in `crates/typst-pdf/src/icc/*` and + `crates/typst/assets/*`. CC0 1.0 Universal diff --git a/crates/typst/Cargo.toml b/crates/typst/Cargo.toml index b9e3b494c..650f53c38 100644 --- a/crates/typst/Cargo.toml +++ b/crates/typst/Cargo.toml @@ -41,6 +41,7 @@ lipsum = { workspace = true } log = { workspace = true } once_cell = { workspace = true } palette = { workspace = true } +qcms = { workspace = true } phf = { workspace = true } rayon = { workspace = true } regex = { workspace = true } diff --git a/crates/typst/assets/CGATS001Compat-v2-micro.icc b/crates/typst/assets/CGATS001Compat-v2-micro.icc new file mode 100644 index 0000000000000000000000000000000000000000..b5a73495bfc7509837b300800d8197d94e83fab7 GIT binary patch literal 8464 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 U@I8Hy^1FWP|Gv&X!`9FL1Def^p8x;= literal 0 HcmV?d00001 diff --git a/crates/typst/src/visualize/color.rs b/crates/typst/src/visualize/color.rs index 90ac8c3d0..735a922cb 100644 --- a/crates/typst/src/visualize/color.rs +++ b/crates/typst/src/visualize/color.rs @@ -9,6 +9,7 @@ use palette::encoding::{self, Linear}; use palette::{ Darken, Desaturate, FromColor, Lighten, Okhsva, OklabHue, RgbHue, Saturate, ShiftHue, }; +use qcms::Profile; use crate::diag::{bail, At, SourceResult, StrResult}; use crate::foundations::{ @@ -30,6 +31,36 @@ pub type Luma = palette::luma::Luma; /// Equivalent of [`std::f32::EPSILON`] but for hue angles. const ANGLE_EPSILON: f32 = 1e-5; +/// The ICC profile used to convert from CMYK to RGB. +/// +/// This is a minimal CMYK profile that only contains the necessary information +/// to convert from CMYK to RGB. It is based on the CGATS TR 001-1995 +/// specification. See +/// https://github.com/saucecontrol/Compact-ICC-Profiles#cmyk. +static CGATS001_COMPACT_PROFILE: Lazy> = Lazy::new(|| { + let bytes = include_bytes!("../../assets/CGATS001Compat-v2-micro.icc"); + Profile::new_from_slice(bytes, false).unwrap() +}); + +/// The target sRGB profile. +static SRGB_PROFILE: Lazy> = Lazy::new(|| { + let mut out = Profile::new_sRGB(); + out.precache_output_transform(); + out +}); + +static TO_SRGB: Lazy = Lazy::new(|| { + qcms::Transform::new_to( + &CGATS001_COMPACT_PROFILE, + &SRGB_PROFILE, + qcms::DataType::CMYK, + qcms::DataType::RGB8, + // Our input profile only supports perceptual intent. + qcms::Intent::Perceptual, + ) + .unwrap() +}); + /// A color in a specific color space. /// /// Typst supports: @@ -1691,6 +1722,8 @@ impl Cmyk { Cmyk::new(l * 0.75, l * 0.68, l * 0.67, l * 0.90) } + // This still uses naive conversion, because qcms does not support + // converting to CMYK yet. fn from_rgba(rgba: Rgb) -> Self { let r = rgba.red; let g = rgba.green; @@ -1709,11 +1742,23 @@ impl Cmyk { } fn to_rgba(self) -> Rgb { - let r = (1.0 - self.c) * (1.0 - self.k); - let g = (1.0 - self.m) * (1.0 - self.k); - let b = (1.0 - self.y) * (1.0 - self.k); + let mut dest: [u8; 3] = [0; 3]; + TO_SRGB.convert( + &[ + (self.c * 255.0).round() as u8, + (self.m * 255.0).round() as u8, + (self.y * 255.0).round() as u8, + (self.k * 255.0).round() as u8, + ], + &mut dest, + ); - Rgb::new(r, g, b, 1.0) + Rgb::new( + dest[0] as f32 / 255.0, + dest[1] as f32 / 255.0, + dest[2] as f32 / 255.0, + 1.0, + ) } fn lighten(self, factor: f32) -> Self { diff --git a/tests/ref/bugs/gradient-cmyk-encode.png b/tests/ref/bugs/gradient-cmyk-encode.png index 7bd82ccef48bfa82462a9a1fe8dab50e0e3bda7a..5002442f7bdfeefe343da84f74e99d06cd1efef7 100644 GIT binary patch literal 1601 zcmchW>raz+6vvDDVww;(WA=h&&IB@VAPRC(%cW3gOX-Eu=V|*C3ndb!>`#D)Z{qS;yNmJ9+5Z$o8}I-%pVoc z?U&9UZ*&}Pc7-(9{Uy$@dRuUVBdF04T5k_-u=`1zC+cjW^|mA8xuZ>vka~Myqdlz7 z7SJ?zvetU6(SfY71lQYw8f>R(tS4%%r)n&L4fgQr+2i##WVI!tYBspe8d_@!ubPdl zoC&G5hSgXiDrciAW+EzQqAI3CYb+-OAQ#V zA->cQ$2BCB=APRG+}nVM}#{3p!kh4qKul7HbK`Izq7)$I+6C z#)w5)V$oRqg)vg$7`}KkzIcpWFiI*I#TSi|^Icm_% zAsUT@RtJNeJI?Ue3A?e;~9e6ga}U-{&!Q~;in zEn_|CU1YWP-F&<;RcH;Q5>%>Z`_*pluVF!+Kru6zW)69NM}m58e)1;W-*bJPl^6Cb zw>1E`1EzGpdT3YPM~m0=Uia=*dS8chAz+F)K*M>DS$w2( z0A@N{qPY9$-LO13gsPCJLIBO_r(kOPb^bd@bJf(|w~Y6?E8(^A2in%%knnX8o6G7a z5ze$n#E5q6-0h2P-iP}C#YS{z(k`6^}&_GlbCN>{a0VT-0;ZX-nto-{^9Kx-QLb?t6$o^Iwb6sHnmY;R93&@ zr0#V?8}Hvtu791s?4wwH`Fqxf7cZ21`bS rkhwm|uKqg*)91K1-XEp#bN*6fCJ%$@2=I}?zXpMeC8FigX`;UYxlpza literal 875 zcmeAS@N?(olHy`uVBq!ia0y~yU|a%Z|KMN(k~*TSQ49>sMxHK?Ar-gYTyxAjWFX*h zab}nFROTt%U(W2E8GThqnmhW)w5E&I-2$rLSR!gvf%@R!UYy(Y>**`K-yIJ+&o8_B z(~E0Al0N&kbsxWeJM%E7yPua&PW;A=U#9Esu0A#A+oR0gMSHUUHqDuRLGRql%%4Xd zr^(Niyn4R;Sh9Q-{~q3r%t!bf-Y{Sg0@e&0nK1}#DuH5HR072?%mnMkFcYjBX7INV z2D67}K`z2#CQK2U!Elug(+fM--8NNveEx65tDg&#L*?#D#pmohS@7{km36Mg!xNsJ zoo}_xrv2NLTJAsda-2dvpY^JcN}-kXdavI=DkN$eOz!wsMm zCV^GuPi}I2fT9QCO|t-sL*?s#<;Ab&l)QUqcJTbi;y;Sjz8}(nKE(75*oRoW1PlLX3>4w-kL3YUmjZl3T>t<7|M%bj7th{Z zzj*J&{tLU-9cK9dpZC)z$!E_PUj1J?cbnSHo5p9(IPBlQuz$_;>C=5TZ(db(??U#5 zyxtFy%a%RNDDIi4VxL3 zJxFV4(44VYb8%`_og2f3QnSUHkqr%v8U4Nu4Gs+r6|wEc4Gn4y4U!EFybTTi89Fip ziWnLibc_6_X)a!@nZDRWQ`3KOq;tHF=3*~%KWl9}J$XF^QAJ^H5jG|^21X{JCxPBt zec|YK1_qW4PZ!6Kid%2*ZqK`&z~C0R^!vTHDt_Jh4;|#PEyCKJ%UdVj1$X%-aGR0~Z-;%$X@waAwQ-Qh@S<7W*i@0WFQ(6xC zPt3pc{EKO=_x;QLUyT2H?Y}&q1=E_uDwgBfSp5+pnz+vq?y?BpgV)orSS+B>)4O5f zwAgz-r}gH0Sx?tr##qjTY0blW31Q~fo-gJ_~oA^R&za$U*1?^A?w91j2R&< zF9l`RBT@&tx(7om$~DJU$B)D+zXxEj`#5?$@*HYDXDqsa9MDLX3>4w-kL3YUCj)#!T!Hle|Nq~;{(kTJ zyK_gL?b~<{$mjj^Y5n}`lFyzoy!x+p^QQ5cGYfjpJM7;-efo6Y&6`(M-Mf&zA+PsC zh7N{?hH09M z7i*?3Hqi{To1p2x*hh1*i(!$8YPg!TgOsq2Add_ey8sI_J0m00%M~0;7#LWLJY5_^ zDsH{Kd(-a<1A|-Q9S0UcQE@ZZLPr*sBemcE%AY#o?PoMeZGzJMoo|l%e2Vq`?{{0% zbCSx-C9%5_92*!InOHak6daHk55EZB<9U1IxeU*n8^`xZNukSQ>L4U?PPR$<*j-b8 zT%V-s#5mUf`lYKSs> zGwYV#&(y-=DT#)JxXp%6NS=f_rGSfLl8Wc0BY$$r-eCy~#zQj|be4OzhhJK9c>AS> z^=}f<0t;jgv%r~bGdE<3o)?#Fum#&QQ)E@}K(Dylsp2_l&$}Z#nX&15F8WxyyRD%)?^JAK`<>QMo8t1Qz{sIGcb8qU6v&hV1ugr{~9)2my;i N22WQ%mvv4FO#sU^hphkr diff --git a/tests/typ/compiler/methods.typ b/tests/typ/compiler/methods.typ index 262f14972..a3d8c658c 100644 --- a/tests/typ/compiler/methods.typ +++ b/tests/typ/compiler/methods.typ @@ -143,8 +143,8 @@ #test(rgb(1, 2, 3).to-hex(), "#010203") #test(rgb(1, 2, 3, 4).to-hex(), "#01020304") #test(luma(40).to-hex(), "#282828") -#test-repr(cmyk(4%, 5%, 6%, 7%).to-hex(), "#e4e1df") -#test-repr(rgb(cmyk(4%, 5%, 6%, 7%)).components(), (89.28%, 88.35%, 87.42%, 100%)) +#test-repr(cmyk(4%, 5%, 6%, 7%).to-hex(), "#e0dcda") +#test-repr(rgb(cmyk(4%, 5%, 6%, 7%)).components(), (87.84%, 86.27%, 85.49%, 100%)) #test-repr(rgb(luma(40%)).components(alpha: false), (40%, 40%, 40%)) #test-repr(cmyk(luma(40)).components(), (11.76%, 10.67%, 10.51%, 14.12%)) #test-repr(cmyk(rgb(1, 2, 3)), cmyk(66.67%, 33.33%, 0%, 98.82%))