/src/ghostpdl/base/gxdcconv.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Conversion between device color spaces for Ghostscript */ |
18 | | #include "gx.h" |
19 | | #include "gsdcolor.h" /* for gxcmap.h */ |
20 | | #include "gxdcconv.h" /* interface */ |
21 | | #include "gxdevice.h" /* for gxcmap.h */ |
22 | | #include "gxcmap.h" |
23 | | #include "gxfarith.h" |
24 | | #include "gxlum.h" |
25 | | #include "gxgstate.h" |
26 | | #include "gsstate.h" /* for gs_currentcpsimode */ |
27 | | |
28 | | /* |
29 | | * The CMYK to RGB algorithms specified by Adobe are, e.g., |
30 | | * R = 1.0 - min(1.0, C + K) |
31 | | * C = max(0.0, min(1.0, 1 - R - UCR)) |
32 | | * We got better results on displays with |
33 | | * R = (1.0 - C) * (1.0 - K) |
34 | | * C = max(0.0, min(1.0, 1 - R / (1 - UCR))) |
35 | | * For PLRM compatibility, we use the Adobe algorithms by default, |
36 | | * but what Adobe says and what they do are two different things. |
37 | | * Testing on CPSI shows that they use the 'better' algorithm. |
38 | | */ |
39 | | |
40 | | /* ------ Color space conversion ------ */ |
41 | | |
42 | | /* Only 4 of the 6 conversions are implemented here; */ |
43 | | /* the other 2 (Gray to RGB/CMYK) are trivial. */ |
44 | | |
45 | | /* Convert RGB to Gray. */ |
46 | | frac |
47 | | color_rgb_to_gray(frac r, frac g, frac b, const gs_gstate * pgs) |
48 | 25.8k | { |
49 | 25.8k | return (r * (unsigned long)lum_red_weight + |
50 | 25.8k | g * (unsigned long)lum_green_weight + |
51 | 25.8k | b * (unsigned long)lum_blue_weight + |
52 | 25.8k | (lum_all_weights / 2)) |
53 | 25.8k | / lum_all_weights; |
54 | 25.8k | } |
55 | | |
56 | | /* Convert RGB to CMYK. */ |
57 | | /* Note that this involves black generation and undercolor removal. */ |
58 | | void |
59 | | color_rgb_to_cmyk(frac r, frac g, frac b, const gs_gstate * pgs, |
60 | | frac cmyk[4], gs_memory_t *mem) |
61 | 0 | { |
62 | 0 | frac c = frac_1 - r, m = frac_1 - g, y = frac_1 - b; |
63 | 0 | frac k = (c < m ? min(c, y) : min(m, y)); |
64 | | |
65 | | /* |
66 | | * The default UCR and BG functions are pretty arbitrary, |
67 | | * but they must agree with the ones in gs_init.ps. |
68 | | */ |
69 | 0 | frac bg = |
70 | 0 | (pgs == NULL ? k : pgs->black_generation == NULL ? frac_0 : |
71 | 0 | gx_map_color_frac(pgs, k, black_generation)); |
72 | 0 | signed_frac ucr = |
73 | 0 | (pgs == NULL ? k : pgs->undercolor_removal == NULL ? frac_0 : |
74 | 0 | gx_map_color_frac(pgs, k, undercolor_removal)); |
75 | |
|
76 | 0 | if (ucr == frac_1) |
77 | 0 | cmyk[0] = cmyk[1] = cmyk[2] = 0; |
78 | 0 | else if (ucr == frac_0) |
79 | 0 | cmyk[0] = c, cmyk[1] = m, cmyk[2] = y; |
80 | 0 | else { |
81 | 0 | if (!gs_currentcpsimode(mem)) { |
82 | | /* C = max(0.0, min(1.0, 1 - R - UCR)), etc. */ |
83 | 0 | signed_frac not_ucr = (ucr < 0 ? frac_1 + ucr : frac_1); |
84 | |
|
85 | 0 | cmyk[0] = (c < ucr ? frac_0 : c > not_ucr ? frac_1 : c - ucr); |
86 | 0 | cmyk[1] = (m < ucr ? frac_0 : m > not_ucr ? frac_1 : m - ucr); |
87 | 0 | cmyk[2] = (y < ucr ? frac_0 : y > not_ucr ? frac_1 : y - ucr); |
88 | 0 | } else { |
89 | | /* Adobe CPSI method */ |
90 | | /* C = max(0.0, min(1.0, 1 - R / (1 - UCR))), etc. */ |
91 | 0 | float denom = frac2float(frac_1 - ucr); /* unscaled */ |
92 | 0 | float v; |
93 | |
|
94 | 0 | v = (float)frac_1 - r / denom; /* unscaled */ |
95 | 0 | cmyk[0] = |
96 | 0 | (is_fneg(v) ? frac_0 : v >= (float)frac_1 ? frac_1 : (frac) v); |
97 | 0 | v = (float)frac_1 - g / denom; /* unscaled */ |
98 | 0 | cmyk[1] = |
99 | 0 | (is_fneg(v) ? frac_0 : v >= (float)frac_1 ? frac_1 : (frac) v); |
100 | 0 | v = (float)frac_1 - b / denom; /* unscaled */ |
101 | 0 | cmyk[2] = |
102 | 0 | (is_fneg(v) ? frac_0 : v >= (float)frac_1 ? frac_1 : (frac) v); |
103 | 0 | } |
104 | 0 | } |
105 | 0 | cmyk[3] = bg; |
106 | 0 | if_debug7m('c', mem, "[c]RGB 0x%x,0x%x,0x%x -> CMYK 0x%x,0x%x,0x%x,0x%x\n", |
107 | 0 | r, g, b, cmyk[0], cmyk[1], cmyk[2], cmyk[3]); |
108 | 0 | } |
109 | | |
110 | | /* Convert CMYK to Gray. */ |
111 | | frac |
112 | | color_cmyk_to_gray(frac c, frac m, frac y, frac k, const gs_gstate * pgs) |
113 | 3.40k | { |
114 | 3.40k | frac not_gray = color_rgb_to_gray(c, m, y, pgs); |
115 | | |
116 | 3.40k | return (not_gray > frac_1 - k ? /* gray + k > 1.0 */ |
117 | 3.40k | frac_0 : frac_1 - (not_gray + k)); |
118 | 3.40k | } |
119 | | |
120 | | /* Convert CMYK to RGB. */ |
121 | | void |
122 | | color_cmyk_to_rgb(frac c, frac m, frac y, frac k, const gs_gstate * pgs, |
123 | | frac rgb[3], gs_memory_t *mem) |
124 | 868 | { |
125 | 868 | switch (k) { |
126 | 434 | case frac_0: |
127 | 434 | rgb[0] = frac_1 - c; |
128 | 434 | rgb[1] = frac_1 - m; |
129 | 434 | rgb[2] = frac_1 - y; |
130 | 434 | break; |
131 | 44 | case frac_1: |
132 | 44 | rgb[0] = rgb[1] = rgb[2] = frac_0; |
133 | 44 | break; |
134 | 390 | default: |
135 | 390 | if (!gs_currentcpsimode(mem)) { |
136 | | /* R = 1.0 - min(1.0, C + K), etc. */ |
137 | 390 | frac not_k = frac_1 - k; |
138 | | |
139 | 390 | rgb[0] = (c > not_k ? frac_0 : not_k - c); |
140 | 390 | rgb[1] = (m > not_k ? frac_0 : not_k - m); |
141 | 390 | rgb[2] = (y > not_k ? frac_0 : not_k - y); |
142 | 390 | } else { |
143 | | /* R = (1.0 - C) * (1.0 - K), etc. */ |
144 | 0 | ulong not_k = frac_1 - k; |
145 | | |
146 | | /* Compute not_k * (frac_1 - v) / frac_1 efficiently. */ |
147 | 0 | ulong prod; |
148 | |
|
149 | 0 | #define deduct_black(v)\ |
150 | 0 | (prod = (frac_1 - (v)) * not_k, frac_1_quo(prod)) |
151 | 0 | rgb[0] = deduct_black(c); |
152 | 0 | rgb[1] = deduct_black(m); |
153 | 0 | rgb[2] = deduct_black(y); |
154 | 0 | #undef deduct_black |
155 | 0 | } |
156 | 868 | } |
157 | 868 | if_debug7m('c', mem, "[c]CMYK 0x%x,0x%x,0x%x,0x%x -> RGB 0x%x,0x%x,0x%x\n", |
158 | 868 | c, m, y, k, rgb[0], rgb[1], rgb[2]); |
159 | 868 | } |