1 /*
2 *
3 * Gutenprint color management module - traditional Gutenprint algorithm.
4 *
5 * Copyright 1997-2000 Michael Sweet (mike@easysw.com) and
6 * Robert Krawitz (rlk@alum.mit.edu)
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22 /*
23 * This file must include only standard C header files. The core code must
24 * compile on generic platforms that don't support glib, gimp, gtk, etc.
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 #include <gutenprint/gutenprint.h>
31 #include "gutenprint-internal.h"
32 #include <gutenprint/gutenprint-intl-internal.h>
33 #include <gutenprint/curve-cache.h>
34 #include <math.h>
35 #ifdef HAVE_LIMITS_H
36 #include <limits.h>
37 #endif
38 #include <string.h>
39 #include "color-conversion.h"
40
41 #ifdef __GNUC__
42 #define inline __inline__
43 #endif
44
45 static const color_correction_t color_corrections[] =
46 {
47 { "None", N_("Default"), COLOR_CORRECTION_DEFAULT, 1 },
48 { "Accurate", N_("High Accuracy"), COLOR_CORRECTION_ACCURATE, 1 },
49 { "Bright", N_("Bright Colors"), COLOR_CORRECTION_BRIGHT, 1 },
50 { "Hue", N_("Correct Hue Only"), COLOR_CORRECTION_HUE, 1 },
51 { "Uncorrected", N_("Uncorrected"), COLOR_CORRECTION_UNCORRECTED, 0 },
52 { "Desaturated", N_("Desaturated"), COLOR_CORRECTION_DESATURATED, 0 },
53 { "Threshold", N_("Threshold"), COLOR_CORRECTION_THRESHOLD, 0 },
54 { "Density", N_("Density"), COLOR_CORRECTION_DENSITY, 0 },
55 { "Raw", N_("Raw"), COLOR_CORRECTION_RAW, 0 },
56 { "Predithered", N_("Pre-Dithered"), COLOR_CORRECTION_PREDITHERED, 0 },
57 };
58
59 static const int color_correction_count =
60 sizeof(color_corrections) / sizeof(color_correction_t);
61
62 static const channel_param_t channel_params[] =
63 {
64 { CMASK_K, "BlackGamma", "BlackCurve", "WhiteGamma", "WhiteCurve" },
65 { CMASK_C, "CyanGamma", "CyanCurve", "RedGamma", "RedCurve" },
66 { CMASK_M, "MagentaGamma", "MagentaCurve", "GreenGamma", "GreenCurve" },
67 { CMASK_Y, "YellowGamma", "YellowCurve", "BlueGamma", "BlueCurve" },
68 { CMASK_W, "WhiteGamma", "WhiteCurve", "BlackGamma", "BlackCurve" },
69 { CMASK_R, "RedGamma", "RedCurve", "CyanGamma", "CyanCurve" },
70 { CMASK_G, "GreenGamma", "GreenCurve", "MagentaGamma", "MagentaCurve" },
71 { CMASK_B, "BlueGamma", "BlueCurve", "YellowGamma", "YellowCurve" },
72 };
73
74 static const int channel_param_count =
75 sizeof(channel_params) / sizeof(channel_param_t);
76
77 static const channel_param_t raw_channel_params[] =
78 {
79 { 0, "GammaCh0", "CurveCh0", "GammaCh0", "CurveCh0" },
80 { 1, "GammaCh1", "CurveCh1", "GammaCh1", "CurveCh1" },
81 { 2, "GammaCh2", "CurveCh2", "GammaCh2", "CurveCh2" },
82 { 3, "GammaCh3", "CurveCh3", "GammaCh3", "CurveCh3" },
83 { 4, "GammaCh4", "CurveCh4", "GammaCh4", "CurveCh4" },
84 { 5, "GammaCh5", "CurveCh5", "GammaCh5", "CurveCh5" },
85 { 6, "GammaCh6", "CurveCh6", "GammaCh6", "CurveCh6" },
86 { 7, "GammaCh7", "CurveCh7", "GammaCh7", "CurveCh7" },
87 { 8, "GammaCh8", "CurveCh8", "GammaCh8", "CurveCh8" },
88 { 9, "GammaCh9", "CurveCh9", "GammaCh9", "CurveCh9" },
89 { 10, "GammaCh10", "CurveCh10", "GammaCh10", "CurveCh10" },
90 { 11, "GammaCh11", "CurveCh11", "GammaCh11", "CurveCh11" },
91 { 12, "GammaCh12", "CurveCh12", "GammaCh12", "CurveCh12" },
92 { 13, "GammaCh13", "CurveCh13", "GammaCh13", "CurveCh13" },
93 { 14, "GammaCh14", "CurveCh14", "GammaCh14", "CurveCh14" },
94 { 15, "GammaCh15", "CurveCh15", "GammaCh15", "CurveCh15" },
95 { 16, "GammaCh16", "CurveCh16", "GammaCh16", "CurveCh16" },
96 { 17, "GammaCh17", "CurveCh17", "GammaCh17", "CurveCh17" },
97 { 18, "GammaCh18", "CurveCh18", "GammaCh18", "CurveCh18" },
98 { 19, "GammaCh19", "CurveCh19", "GammaCh19", "CurveCh19" },
99 { 20, "GammaCh20", "CurveCh20", "GammaCh20", "CurveCh20" },
100 { 21, "GammaCh21", "CurveCh21", "GammaCh21", "CurveCh21" },
101 { 22, "GammaCh22", "CurveCh22", "GammaCh22", "CurveCh22" },
102 { 23, "GammaCh23", "CurveCh23", "GammaCh23", "CurveCh23" },
103 { 24, "GammaCh24", "CurveCh24", "GammaCh24", "CurveCh24" },
104 { 25, "GammaCh25", "CurveCh25", "GammaCh25", "CurveCh25" },
105 { 26, "GammaCh26", "CurveCh26", "GammaCh26", "CurveCh26" },
106 { 27, "GammaCh27", "CurveCh27", "GammaCh27", "CurveCh27" },
107 { 28, "GammaCh28", "CurveCh28", "GammaCh28", "CurveCh28" },
108 { 29, "GammaCh29", "CurveCh29", "GammaCh29", "CurveCh29" },
109 { 30, "GammaCh30", "CurveCh30", "GammaCh30", "CurveCh30" },
110 { 31, "GammaCh31", "CurveCh31", "GammaCh31", "CurveCh31" },
111 { 32, "GammaCh32", "CurveCh32", "GammaCh32", "CurveCh32" },
112 { 33, "GammaCh33", "CurveCh33", "GammaCh33", "CurveCh33" },
113 { 34, "GammaCh34", "CurveCh34", "GammaCh34", "CurveCh34" },
114 { 35, "GammaCh35", "CurveCh35", "GammaCh35", "CurveCh35" },
115 { 36, "GammaCh36", "CurveCh36", "GammaCh36", "CurveCh36" },
116 { 37, "GammaCh37", "CurveCh37", "GammaCh37", "CurveCh37" },
117 { 38, "GammaCh38", "CurveCh38", "GammaCh38", "CurveCh38" },
118 { 39, "GammaCh39", "CurveCh39", "GammaCh39", "CurveCh39" },
119 { 40, "GammaCh40", "CurveCh40", "GammaCh40", "CurveCh40" },
120 { 41, "GammaCh41", "CurveCh41", "GammaCh41", "CurveCh41" },
121 { 42, "GammaCh42", "CurveCh42", "GammaCh42", "CurveCh42" },
122 { 43, "GammaCh43", "CurveCh43", "GammaCh43", "CurveCh43" },
123 { 44, "GammaCh44", "CurveCh44", "GammaCh44", "CurveCh44" },
124 { 45, "GammaCh45", "CurveCh45", "GammaCh45", "CurveCh45" },
125 { 46, "GammaCh46", "CurveCh46", "GammaCh46", "CurveCh46" },
126 { 47, "GammaCh47", "CurveCh47", "GammaCh47", "CurveCh47" },
127 { 48, "GammaCh48", "CurveCh48", "GammaCh48", "CurveCh48" },
128 { 49, "GammaCh49", "CurveCh49", "GammaCh49", "CurveCh49" },
129 { 50, "GammaCh50", "CurveCh50", "GammaCh50", "CurveCh50" },
130 { 51, "GammaCh51", "CurveCh51", "GammaCh51", "CurveCh51" },
131 { 52, "GammaCh52", "CurveCh52", "GammaCh52", "CurveCh52" },
132 { 53, "GammaCh53", "CurveCh53", "GammaCh53", "CurveCh53" },
133 { 54, "GammaCh54", "CurveCh54", "GammaCh54", "CurveCh54" },
134 { 55, "GammaCh55", "CurveCh55", "GammaCh55", "CurveCh55" },
135 { 56, "GammaCh56", "CurveCh56", "GammaCh56", "CurveCh56" },
136 { 57, "GammaCh57", "CurveCh57", "GammaCh57", "CurveCh57" },
137 { 58, "GammaCh58", "CurveCh58", "GammaCh58", "CurveCh58" },
138 { 59, "GammaCh59", "CurveCh59", "GammaCh59", "CurveCh59" },
139 { 60, "GammaCh60", "CurveCh60", "GammaCh60", "CurveCh60" },
140 { 61, "GammaCh61", "CurveCh61", "GammaCh61", "CurveCh61" },
141 { 62, "GammaCh62", "CurveCh62", "GammaCh62", "CurveCh62" },
142 { 63, "GammaCh63", "CurveCh63", "GammaCh63", "CurveCh63" },
143 };
144
145 static const int raw_channel_param_count =
146 sizeof(raw_channel_params) / sizeof(channel_param_t);
147
148
149 static const color_description_t color_descriptions[] =
150 {
151 { N_("Grayscale"), 1, 1, COLOR_ID_GRAY, COLOR_BLACK, CMASK_K, 1,
152 COLOR_CORRECTION_UNCORRECTED, &stpi_color_convert_to_gray },
153 { N_("Whitescale"), 1, 1, COLOR_ID_WHITE, COLOR_WHITE, CMASK_K, 1,
154 COLOR_CORRECTION_UNCORRECTED, &stpi_color_convert_to_gray },
155 { N_("RGB"), 1, 1, COLOR_ID_RGB, COLOR_WHITE, CMASK_CMY, 3,
156 COLOR_CORRECTION_ACCURATE, &stpi_color_convert_to_color },
157 { N_("CMY"), 1, 1, COLOR_ID_CMY, COLOR_BLACK, CMASK_CMY, 3,
158 COLOR_CORRECTION_ACCURATE, &stpi_color_convert_to_color },
159 { N_("CMYK"), 1, 0, COLOR_ID_CMYK, COLOR_BLACK, CMASK_CMYK, 4,
160 COLOR_CORRECTION_ACCURATE, &stpi_color_convert_to_kcmy },
161 { N_("KCMY"), 1, 1, COLOR_ID_KCMY, COLOR_BLACK, CMASK_CMYK, 4,
162 COLOR_CORRECTION_ACCURATE, &stpi_color_convert_to_kcmy },
163 { N_("Raw"), 1, 1, COLOR_ID_RAW, COLOR_UNKNOWN, 0, -1,
164 COLOR_CORRECTION_RAW, &stpi_color_convert_raw },
165 };
166
167 static const int color_description_count =
168 sizeof(color_descriptions) / sizeof(color_description_t);
169
170
171 static const channel_depth_t channel_depths[] =
172 {
173 { "8", 8 },
174 { "16", 16 }
175 };
176
177 static const int channel_depth_count =
178 sizeof(channel_depths) / sizeof(channel_depth_t);
179
180
181 typedef struct
182 {
183 const stp_parameter_t param;
184 double min;
185 double max;
186 double defval;
187 unsigned channel_mask;
188 int color_only;
189 int is_rgb;
190 } float_param_t;
191
192 #define RAW_GAMMA_CHANNEL(channel) \
193 { \
194 { \
195 "GammaCh" #channel, N_("Channel " #channel " Gamma"), "Color=Yes,Category=Gamma", \
196 N_("Gamma for raw channel " #channel), \
197 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT, \
198 STP_PARAMETER_LEVEL_INTERNAL, 0, 1, channel, 1, 0 \
199 }, 0.1, 4.0, 1.0, CMASK_RAW, 0, -1 \
200 }
201
202 static const float_param_t float_parameters[] =
203 {
204 {
205 {
206 "ColorCorrection", N_("Color Correction"), "Color=Yes,Category=Basic Image Adjustment",
207 N_("Color correction to be applied"),
208 STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_OUTPUT,
209 STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
210 }, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
211 },
212 {
213 {
214 "ChannelBitDepth", N_("Channel Bit Depth"), "Color=Yes,Category=Core Parameter",
215 N_("Bit depth per channel"),
216 STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
217 STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
218 }, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
219 },
220 {
221 {
222 "InputImageType", N_("Input Image Type"), "Color=Yes,Category=Core Parameter",
223 N_("Input image type"),
224 STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
225 STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
226 }, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
227 },
228 {
229 {
230 "STPIOutputType", N_("Output Image Type"), "Color=Yes,Category=Core Parameter",
231 N_("Output image type"),
232 STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
233 STP_PARAMETER_LEVEL_INTERNAL, 1, 1, -1, 1, 0
234 }, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
235 },
236 {
237 {
238 "STPIRawChannels", N_("Raw Channels"), "Color=Yes,Category=Core Parameter",
239 N_("Raw Channels"),
240 STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_CORE,
241 STP_PARAMETER_LEVEL_INTERNAL, 1, 1, -1, 1, 0
242 }, 1.0, STP_CHANNEL_LIMIT, 1.0, CMASK_EVERY, 0, -1
243 },
244 {
245 {
246 "SimpleGamma", N_("SimpleGamma"), "Color=Yes,Category=Gamma",
247 N_("Do not correct for screen gamma"),
248 STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_OUTPUT,
249 STP_PARAMETER_LEVEL_INTERNAL, 0, 1, -1, 1, 0
250 }, 0.0, 1.0, 0.0, CMASK_EVERY, 0, -1
251 },
252 {
253 {
254 "Brightness", N_("Brightness"), "Color=Yes,Category=Basic Image Adjustment",
255 N_("Brightness of the print"),
256 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
257 STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
258 }, 0.0, 2.0, 1.0, CMASK_ALL, 0, -1
259 },
260 {
261 {
262 "Contrast", N_("Contrast"), "Color=Yes,Category=Basic Image Adjustment",
263 N_("Contrast of the print (0 is solid gray)"),
264 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
265 STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
266 }, 0.0, 4.0, 1.0, CMASK_ALL, 0, -1
267 },
268 {
269 {
270 "LinearContrast", N_("Linear Contrast Adjustment"), "Color=Yes,Category=Advanced Image Control",
271 N_("Use linear vs. fixed end point contrast adjustment"),
272 STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_OUTPUT,
273 STP_PARAMETER_LEVEL_ADVANCED3, 1, 1, -1, 1, 0
274 }, 0.0, 0.0, 0.0, CMASK_ALL, 0, -1
275 },
276 {
277 {
278 "Gamma", N_("Composite Gamma"), "Color=Yes,Category=Gamma",
279 N_("Adjust the gamma of the print. Larger values will "
280 "produce a generally brighter print, while smaller "
281 "values will produce a generally darker print. "),
282 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
283 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, -1, 1, 0
284 }, 0.1, 4.0, 1.0, CMASK_EVERY, 0, -1
285 },
286 {
287 {
288 "AppGamma", N_("AppGamma"), "Color=Yes,Category=Gamma",
289 N_("Gamma value assumed by application"),
290 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
291 STP_PARAMETER_LEVEL_INTERNAL, 0, 1, -1, 1, 0
292 }, 0.1, 4.0, 1.0, CMASK_EVERY, 0, -1
293 },
294 {
295 {
296 "CyanGamma", N_("Cyan"), "Color=Yes,Category=Gamma",
297 N_("Adjust the cyan gamma"),
298 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
299 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 1, 1, 0
300 }, 0.0, 4.0, 1.0, CMASK_C, 1, 0
301 },
302 {
303 {
304 "MagentaGamma", N_("Magenta"), "Color=Yes,Category=Gamma",
305 N_("Adjust the magenta gamma"),
306 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
307 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 2, 1, 0
308 }, 0.0, 4.0, 1.0, CMASK_M, 1, 0
309 },
310 {
311 {
312 "YellowGamma", N_("Yellow"), "Color=Yes,Category=Gamma",
313 N_("Adjust the yellow gamma"),
314 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
315 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 3, 1, 0
316 }, 0.0, 4.0, 1.0, CMASK_Y, 1, 0
317 },
318 {
319 {
320 "RedGamma", N_("Red"), "Color=Yes,Category=Gamma",
321 N_("Adjust the red gamma"),
322 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
323 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 1, 1, 0
324 }, 0.0, 4.0, 1.0, CMASK_C, 1, 1
325 },
326 {
327 {
328 "GreenGamma", N_("Green"), "Color=Yes,Category=Gamma",
329 N_("Adjust the green gamma"),
330 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
331 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 2, 1, 0
332 }, 0.0, 4.0, 1.0, CMASK_M, 1, 1
333 },
334 {
335 {
336 "BlueGamma", N_("Blue"), "Color=Yes,Category=Gamma",
337 N_("Adjust the blue gamma"),
338 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
339 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 3, 1, 0
340 }, 0.0, 4.0, 1.0, CMASK_Y, 1, 1
341 },
342 {
343 {
344 "BlackGamma", N_("Black"), "Color=Yes,Category=Gamma",
345 N_("Adjust the black gamma"),
346 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
347 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 3, 1, 0
348 }, 0.0, 4.0, 1.0, CMASK_K, 1, 0
349 },
350 {
351 {
352 "CyanBalance", N_("Cyan Balance"), "Color=Yes,Category=GrayBalance",
353 N_("Adjust the cyan gray balance"),
354 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
355 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 1, 1, 0
356 }, 0.0, 1.0, 1.0, CMASK_C, 1, 0
357 },
358 {
359 {
360 "MagentaBalance", N_("Magenta Balance"), "Color=Yes,Category=GrayBalance",
361 N_("Adjust the magenta gray balance"),
362 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
363 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 2, 1, 0
364 }, 0.0, 1.0, 1.0, CMASK_M, 1, 0
365 },
366 {
367 {
368 "YellowBalance", N_("Yellow Balance"), "Color=Yes,Category=GrayBalance",
369 N_("Adjust the yellow gray balance"),
370 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
371 STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 3, 1, 0
372 }, 0.0, 1.0, 1.0, CMASK_Y, 1, 0
373 },
374 {
375 {
376 "Saturation", N_("Saturation"), "Color=Yes,Category=Basic Image Adjustment",
377 N_("Adjust the saturation (color balance) of the print\n"
378 "Use zero saturation to produce grayscale output "
379 "using color and black inks"),
380 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
381 STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
382 }, 0.0, 9.0, 1.0, CMASK_CMY | CMASK_RGB, 1, 0
383 },
384 /* Need to think this through a bit more -- rlk 20030712 */
385 {
386 {
387 "InkLimit", N_("Ink Limit"), "Color=Yes,Category=Advanced Output Control",
388 N_("Limit the total ink printed to the page"),
389 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
390 STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, -1, 0, 0
391 }, 0.0, STP_CHANNEL_LIMIT, STP_CHANNEL_LIMIT, CMASK_CMY, 0, -1
392 },
393 {
394 {
395 "BlackTrans", N_("GCR Transition"), "Color=Yes,Category=Advanced Output Control",
396 N_("Adjust the gray component transition rate"),
397 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
398 STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
399 }, 0.0, 1.0, 1.0, CMASK_K, 1, 0
400 },
401 {
402 {
403 "GCRLower", N_("GCR Lower Bound"), "Color=Yes,Category=Advanced Output Control",
404 N_("Lower bound of gray component reduction"),
405 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
406 STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
407 }, 0.0, 1.0, 0.2, CMASK_K, 1, 0
408 },
409 {
410 {
411 "GCRUpper", N_("GCR Upper Bound"), "Color=Yes,Category=Advanced Output Control",
412 N_("Upper bound of gray component reduction"),
413 STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
414 STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
415 }, 0.0, 5.0, 0.5, CMASK_K, 1, 0
416 },
417 RAW_GAMMA_CHANNEL(0),
418 RAW_GAMMA_CHANNEL(1),
419 RAW_GAMMA_CHANNEL(2),
420 RAW_GAMMA_CHANNEL(3),
421 RAW_GAMMA_CHANNEL(4),
422 RAW_GAMMA_CHANNEL(5),
423 RAW_GAMMA_CHANNEL(6),
424 RAW_GAMMA_CHANNEL(7),
425 RAW_GAMMA_CHANNEL(8),
426 RAW_GAMMA_CHANNEL(9),
427 RAW_GAMMA_CHANNEL(10),
428 RAW_GAMMA_CHANNEL(11),
429 RAW_GAMMA_CHANNEL(12),
430 RAW_GAMMA_CHANNEL(13),
431 RAW_GAMMA_CHANNEL(14),
432 RAW_GAMMA_CHANNEL(15),
433 RAW_GAMMA_CHANNEL(16),
434 RAW_GAMMA_CHANNEL(17),
435 RAW_GAMMA_CHANNEL(18),
436 RAW_GAMMA_CHANNEL(19),
437 RAW_GAMMA_CHANNEL(20),
438 RAW_GAMMA_CHANNEL(21),
439 RAW_GAMMA_CHANNEL(22),
440 RAW_GAMMA_CHANNEL(23),
441 RAW_GAMMA_CHANNEL(24),
442 RAW_GAMMA_CHANNEL(25),
443 RAW_GAMMA_CHANNEL(26),
444 RAW_GAMMA_CHANNEL(27),
445 RAW_GAMMA_CHANNEL(28),
446 RAW_GAMMA_CHANNEL(29),
447 RAW_GAMMA_CHANNEL(30),
448 RAW_GAMMA_CHANNEL(31),
449 RAW_GAMMA_CHANNEL(32),
450 RAW_GAMMA_CHANNEL(33),
451 RAW_GAMMA_CHANNEL(34),
452 RAW_GAMMA_CHANNEL(35),
453 RAW_GAMMA_CHANNEL(36),
454 RAW_GAMMA_CHANNEL(37),
455 RAW_GAMMA_CHANNEL(38),
456 RAW_GAMMA_CHANNEL(39),
457 RAW_GAMMA_CHANNEL(40),
458 RAW_GAMMA_CHANNEL(41),
459 RAW_GAMMA_CHANNEL(42),
460 RAW_GAMMA_CHANNEL(43),
461 RAW_GAMMA_CHANNEL(44),
462 RAW_GAMMA_CHANNEL(45),
463 RAW_GAMMA_CHANNEL(46),
464 RAW_GAMMA_CHANNEL(47),
465 RAW_GAMMA_CHANNEL(48),
466 RAW_GAMMA_CHANNEL(49),
467 RAW_GAMMA_CHANNEL(50),
468 RAW_GAMMA_CHANNEL(51),
469 RAW_GAMMA_CHANNEL(52),
470 RAW_GAMMA_CHANNEL(53),
471 RAW_GAMMA_CHANNEL(54),
472 RAW_GAMMA_CHANNEL(55),
473 RAW_GAMMA_CHANNEL(56),
474 RAW_GAMMA_CHANNEL(57),
475 RAW_GAMMA_CHANNEL(58),
476 RAW_GAMMA_CHANNEL(59),
477 RAW_GAMMA_CHANNEL(60),
478 RAW_GAMMA_CHANNEL(61),
479 RAW_GAMMA_CHANNEL(62),
480 RAW_GAMMA_CHANNEL(63),
481 {
482 {
483 "LUTDumpFile", N_("LUT dump file"), N_("Advanced Output Control"),
484 N_("Dump file for LUT for external color adjustment"),
485 STP_PARAMETER_TYPE_FILE, STP_PARAMETER_CLASS_OUTPUT,
486 STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, -1, 1, 0
487 }, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
488 },
489 };
490
491 static const int float_parameter_count =
492 sizeof(float_parameters) / sizeof(float_param_t);
493
494 typedef struct
495 {
496 stp_parameter_t param;
497 stp_curve_t **defval;
498 unsigned channel_mask;
499 int hsl_only;
500 int color_only;
501 int is_rgb;
502 } curve_param_t;
503
504 static int standard_curves_initialized = 0;
505
506 static stp_curve_t *hue_map_bounds = NULL;
507 static stp_curve_t *lum_map_bounds = NULL;
508 static stp_curve_t *sat_map_bounds = NULL;
509 static stp_curve_t *color_curve_bounds = NULL;
510 static stp_curve_t *gcr_curve_bounds = NULL;
511
512
513 #define RAW_CURVE_CHANNEL(channel) \
514 { \
515 { \
516 "CurveCh" #channel, N_("Channel " #channel " Curve"), \
517 "Color=Yes,Category=Output Curves", \
518 N_("Curve for raw channel " #channel), \
519 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT, \
520 STP_PARAMETER_LEVEL_INTERNAL, 0, 1, channel, 1, 0 \
521 }, &color_curve_bounds, CMASK_RAW, 0, 0, -1 \
522 }
523
524 static curve_param_t curve_parameters[] =
525 {
526 {
527 {
528 "CyanCurve", N_("Cyan Curve"), "Color=Yes,Category=Output Curves",
529 N_("Cyan curve"),
530 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
531 STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 1, 1, 0
532 }, &color_curve_bounds, CMASK_C, 0, 1, 0
533 },
534 {
535 {
536 "MagentaCurve", N_("Magenta Curve"), "Color=Yes,Category=Output Curves",
537 N_("Magenta curve"),
538 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
539 STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 2, 1, 0
540 }, &color_curve_bounds, CMASK_M, 0, 1, 0
541 },
542 {
543 {
544 "YellowCurve", N_("Yellow Curve"), "Color=Yes,Category=Output Curves",
545 N_("Yellow curve"),
546 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
547 STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 3, 1, 0
548 }, &color_curve_bounds, CMASK_Y, 0, 1, 0
549 },
550 {
551 {
552 "BlackCurve", N_("Black Curve"), "Color=Yes,Category=Output Curves",
553 N_("Black curve"),
554 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
555 STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 0, 1, 0
556 }, &color_curve_bounds, CMASK_K, 0, 0, 0
557 },
558 {
559 {
560 "RedCurve", N_("Red Curve"), "Color=Yes,Category=Output Curves",
561 N_("Red curve"),
562 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
563 STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 1, 1, 0
564 }, &color_curve_bounds, CMASK_C, 0, 1, 1
565 },
566 {
567 {
568 "GreenCurve", N_("Green Curve"), "Color=Yes,Category=Output Curves",
569 N_("Green curve"),
570 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
571 STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 2, 1, 0
572 }, &color_curve_bounds, CMASK_M, 0, 1, 1
573 },
574 {
575 {
576 "BlueCurve", N_("Blue Curve"), "Color=Yes,Category=Output Curves",
577 N_("Blue curve"),
578 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
579 STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 3, 1, 0
580 }, &color_curve_bounds, CMASK_Y, 0, 1, 1
581 },
582 {
583 {
584 "WhiteCurve", N_("White Curve"), "Color=Yes,Category=Output Curves",
585 N_("White curve"),
586 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
587 STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 1, 1, 0
588 }, &color_curve_bounds, CMASK_W, 0, 0, 1
589 },
590 {
591 {
592 "HueMap", N_("Hue Map"), "Color=Yes,Category=Advanced HSL Curves",
593 N_("Hue adjustment curve"),
594 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
595 STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, -1, 1, 0
596 }, &hue_map_bounds, CMASK_CMY | CMASK_RGB, 1, 1, -1
597 },
598 {
599 {
600 "SatMap", N_("Saturation Map"), "Color=Yes,Category=Advanced HSL Curves",
601 N_("Saturation adjustment curve"),
602 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
603 STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, -1, 1, 0
604 }, &sat_map_bounds, CMASK_CMY | CMASK_RGB, 1, 1, -1
605 },
606 {
607 {
608 "LumMap", N_("Luminosity Map"), "Color=Yes,Category=Advanced HSL Curves",
609 N_("Luminosity adjustment curve"),
610 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
611 STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, -1, 1, 0
612 }, &lum_map_bounds, CMASK_CMY | CMASK_RGB, 1, 1, -1
613 },
614 {
615 {
616 "GCRCurve", N_("Gray Component Reduction"), "Color=Yes,Category=Advanced Output Control",
617 N_("Gray component reduction curve"),
618 STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
619 STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
620 }, &gcr_curve_bounds, CMASK_K, 0, 1, -1
621 },
622 RAW_CURVE_CHANNEL(0),
623 RAW_CURVE_CHANNEL(1),
624 RAW_CURVE_CHANNEL(2),
625 RAW_CURVE_CHANNEL(3),
626 RAW_CURVE_CHANNEL(4),
627 RAW_CURVE_CHANNEL(5),
628 RAW_CURVE_CHANNEL(6),
629 RAW_CURVE_CHANNEL(7),
630 RAW_CURVE_CHANNEL(8),
631 RAW_CURVE_CHANNEL(9),
632 RAW_CURVE_CHANNEL(10),
633 RAW_CURVE_CHANNEL(11),
634 RAW_CURVE_CHANNEL(12),
635 RAW_CURVE_CHANNEL(13),
636 RAW_CURVE_CHANNEL(14),
637 RAW_CURVE_CHANNEL(15),
638 RAW_CURVE_CHANNEL(16),
639 RAW_CURVE_CHANNEL(17),
640 RAW_CURVE_CHANNEL(18),
641 RAW_CURVE_CHANNEL(19),
642 RAW_CURVE_CHANNEL(20),
643 RAW_CURVE_CHANNEL(21),
644 RAW_CURVE_CHANNEL(22),
645 RAW_CURVE_CHANNEL(23),
646 RAW_CURVE_CHANNEL(24),
647 RAW_CURVE_CHANNEL(25),
648 RAW_CURVE_CHANNEL(26),
649 RAW_CURVE_CHANNEL(27),
650 RAW_CURVE_CHANNEL(28),
651 RAW_CURVE_CHANNEL(29),
652 RAW_CURVE_CHANNEL(30),
653 RAW_CURVE_CHANNEL(31),
654 RAW_CURVE_CHANNEL(32),
655 RAW_CURVE_CHANNEL(33),
656 RAW_CURVE_CHANNEL(34),
657 RAW_CURVE_CHANNEL(35),
658 RAW_CURVE_CHANNEL(36),
659 RAW_CURVE_CHANNEL(37),
660 RAW_CURVE_CHANNEL(38),
661 RAW_CURVE_CHANNEL(39),
662 RAW_CURVE_CHANNEL(40),
663 RAW_CURVE_CHANNEL(41),
664 RAW_CURVE_CHANNEL(42),
665 RAW_CURVE_CHANNEL(43),
666 RAW_CURVE_CHANNEL(44),
667 RAW_CURVE_CHANNEL(45),
668 RAW_CURVE_CHANNEL(46),
669 RAW_CURVE_CHANNEL(47),
670 RAW_CURVE_CHANNEL(48),
671 RAW_CURVE_CHANNEL(49),
672 RAW_CURVE_CHANNEL(50),
673 RAW_CURVE_CHANNEL(51),
674 RAW_CURVE_CHANNEL(52),
675 RAW_CURVE_CHANNEL(53),
676 RAW_CURVE_CHANNEL(54),
677 RAW_CURVE_CHANNEL(55),
678 RAW_CURVE_CHANNEL(56),
679 RAW_CURVE_CHANNEL(57),
680 RAW_CURVE_CHANNEL(58),
681 RAW_CURVE_CHANNEL(59),
682 RAW_CURVE_CHANNEL(60),
683 RAW_CURVE_CHANNEL(61),
684 RAW_CURVE_CHANNEL(62),
685 RAW_CURVE_CHANNEL(63),
686 };
687
688 static const int curve_parameter_count =
689 sizeof(curve_parameters) / sizeof(curve_param_t);
690
691
692 static const color_description_t *
get_color_description(const char * name)693 get_color_description(const char *name)
694 {
695 int i;
696 if (name)
697 for (i = 0; i < color_description_count; i++)
698 {
699 if (strcmp(name, color_descriptions[i].name) == 0)
700 return &(color_descriptions[i]);
701 }
702 return NULL;
703 }
704
705 static const channel_depth_t *
get_channel_depth(const char * name)706 get_channel_depth(const char *name)
707 {
708 int i;
709 if (name)
710 for (i = 0; i < channel_depth_count; i++)
711 {
712 if (strcmp(name, channel_depths[i].name) == 0)
713 return &(channel_depths[i]);
714 }
715 return NULL;
716 }
717
718 static const color_correction_t *
get_color_correction(const char * name)719 get_color_correction(const char *name)
720 {
721 int i;
722 if (name)
723 for (i = 0; i < color_correction_count; i++)
724 {
725 if (strcmp(name, color_corrections[i].name) == 0)
726 return &(color_corrections[i]);
727 }
728 return NULL;
729 }
730
731 static const color_correction_t *
get_color_correction_by_tag(color_correction_enum_t correction)732 get_color_correction_by_tag(color_correction_enum_t correction)
733 {
734 int i;
735 for (i = 0; i < color_correction_count; i++)
736 {
737 if (correction == color_corrections[i].correction)
738 return &(color_corrections[i]);
739 }
740 return NULL;
741 }
742
743
744 static void
initialize_channels(stp_vars_t * v,stp_image_t * image)745 initialize_channels(stp_vars_t *v, stp_image_t *image)
746 {
747 lut_t *lut = (lut_t *)(stp_get_component_data(v, "Color"));
748 if (stp_check_float_parameter(v, "InkLimit", STP_PARAMETER_ACTIVE))
749 stp_channel_set_ink_limit(v, stp_get_float_parameter(v, "InkLimit"));
750 stp_channel_initialize(v, image, lut->out_channels);
751 lut->channels_are_initialized = 1;
752 }
753
754 static int
stpi_color_traditional_get_row(stp_vars_t * v,stp_image_t * image,int row,unsigned * zero_mask)755 stpi_color_traditional_get_row(stp_vars_t *v,
756 stp_image_t *image,
757 int row,
758 unsigned *zero_mask)
759 {
760 const lut_t *lut = (const lut_t *)(stp_get_component_data(v, "Color"));
761 unsigned zero;
762 if (stp_image_get_row(image, lut->in_data,
763 lut->image_width * lut->in_channels * lut->channel_depth / 8, row)
764 != STP_IMAGE_STATUS_OK)
765 return 2;
766 if (!lut->channels_are_initialized)
767 initialize_channels(v, image);
768 zero = (lut->output_color_description->conversion_function)
769 (v, lut->in_data, stp_channel_get_input(v));
770 if (zero_mask)
771 *zero_mask = zero;
772 stp_channel_convert(v, zero_mask);
773 return 0;
774 }
775
776 static void
free_channels(lut_t * lut)777 free_channels(lut_t *lut)
778 {
779 int i;
780 for (i = 0; i < STP_CHANNEL_LIMIT; i++)
781 stp_curve_free_curve_cache(&(lut->channel_curves[i]));
782 }
783
784 static lut_t *
allocate_lut(void)785 allocate_lut(void)
786 {
787 int i;
788 lut_t *ret = stp_zalloc(sizeof(lut_t));
789 for (i = 0; i < STP_CHANNEL_LIMIT; i++)
790 {
791 ret->gamma_values[i] = 1.0;
792 }
793 ret->print_gamma = 1.0;
794 ret->app_gamma = 1.0;
795 ret->contrast = 1.0;
796 ret->brightness = 1.0;
797 ret->simple_gamma_correction = 0;
798 return ret;
799 }
800
801 static void *
copy_lut(void * vlut)802 copy_lut(void *vlut)
803 {
804 const lut_t *src = (const lut_t *)vlut;
805 int i;
806 lut_t *dest;
807 if (!src)
808 return NULL;
809 dest = allocate_lut();
810 free_channels(dest);
811
812 dest->steps = src->steps;
813 dest->channel_depth = src->channel_depth;
814 dest->image_width = src->image_width;
815 dest->in_channels = src->in_channels;
816 dest->out_channels = src->out_channels;
817 /* Don't copy channels_are_initialized */
818 dest->invert_output = src->invert_output;
819 dest->input_color_description = src->input_color_description;
820 dest->output_color_description = src->output_color_description;
821 dest->color_correction = src->color_correction;
822 for (i = 0; i < STP_CHANNEL_LIMIT; i++)
823 {
824 stp_curve_cache_copy(&(dest->channel_curves[i]), &(src->channel_curves[i]));
825 dest->gamma_values[i] = src->gamma_values[i];
826 }
827 stp_curve_cache_copy(&(dest->brightness_correction),
828 &(src->brightness_correction));
829 stp_curve_cache_copy(&(dest->contrast_correction),
830 &(src->contrast_correction));
831 stp_curve_cache_copy(&(dest->user_color_correction),
832 &(src->user_color_correction));
833 dest->print_gamma = src->print_gamma;
834 dest->app_gamma = src->app_gamma;
835 dest->screen_gamma = src->screen_gamma;
836 dest->contrast = src->contrast;
837 dest->brightness = src->brightness;
838 dest->simple_gamma_correction = src->simple_gamma_correction;
839 dest->linear_contrast_adjustment = src->linear_contrast_adjustment;
840 stp_curve_cache_copy(&(dest->hue_map), &(src->hue_map));
841 stp_curve_cache_copy(&(dest->lum_map), &(src->lum_map));
842 stp_curve_cache_copy(&(dest->sat_map), &(src->sat_map));
843 /* Don't copy gray_tmp */
844 /* Don't copy cmy_tmp */
845 if (src->in_data)
846 {
847 dest->in_data = stp_malloc(src->image_width * src->in_channels);
848 memset(dest->in_data, 0, src->image_width * src->in_channels);
849 }
850 return dest;
851 }
852
853 static void
free_lut(void * vlut)854 free_lut(void *vlut)
855 {
856 lut_t *lut = (lut_t *)vlut;
857 free_channels(lut);
858 stp_curve_free_curve_cache(&(lut->brightness_correction));
859 stp_curve_free_curve_cache(&(lut->contrast_correction));
860 stp_curve_free_curve_cache(&(lut->user_color_correction));
861 stp_curve_free_curve_cache(&(lut->hue_map));
862 stp_curve_free_curve_cache(&(lut->lum_map));
863 stp_curve_free_curve_cache(&(lut->sat_map));
864 STP_SAFE_FREE(lut->gray_tmp);
865 STP_SAFE_FREE(lut->cmy_tmp);
866 STP_SAFE_FREE(lut->in_data);
867 memset(lut, 0, sizeof(lut_t));
868 stp_free(lut);
869 }
870
871 static stp_curve_t *
compute_gcr_curve(const stp_vars_t * vars)872 compute_gcr_curve(const stp_vars_t *vars)
873 {
874 stp_curve_t *curve;
875 lut_t *lut = (lut_t *)(stp_get_component_data(vars, "Color"));
876 double k_lower = 0.0;
877 double k_upper = 1.0;
878 double k_trans = 1.0;
879 double i_k_trans = 1.0;
880 double *tmp_data = stp_malloc(sizeof(double) * lut->steps);
881 int i;
882
883 if (stp_check_float_parameter(vars, "GCRUpper", STP_PARAMETER_DEFAULTED))
884 k_upper = stp_get_float_parameter(vars, "GCRUpper");
885 if (stp_check_float_parameter(vars, "GCRLower", STP_PARAMETER_DEFAULTED))
886 k_lower = stp_get_float_parameter(vars, "GCRLower");
887 if (stp_check_float_parameter(vars, "BlackTrans", STP_PARAMETER_DEFAULTED))
888 k_trans = stp_get_float_parameter(vars, "BlackTrans");
889 if (k_lower >= 1)
890 return NULL;
891 k_upper *= lut->steps;
892 k_lower *= lut->steps;
893 stp_dprintf(STP_DBG_LUT, vars, " k_lower %.3f\n", k_lower);
894 stp_dprintf(STP_DBG_LUT, vars, " k_upper %.3f\n", k_upper);
895
896 if (k_lower > lut->steps)
897 k_lower = lut->steps;
898 if (k_upper < k_lower)
899 k_upper = k_lower + 1;
900 i_k_trans = 1.0 / k_trans;
901
902 for (i = 0; i < k_lower; i ++)
903 tmp_data[i] = 0;
904 if (k_upper < lut->steps)
905 {
906 for (i = ceil(k_lower); i < k_upper; i ++)
907 {
908 double where = (i - k_lower) / (k_upper - k_lower);
909 double g1 = pow(where, i_k_trans);
910 double g2 = 1.0 - pow(1.0 - where, k_trans);
911 double value = (i_k_trans <= 1.0 ? g1 : g2);
912 tmp_data[i] = 65535.0 * k_upper * value / (double) (lut->steps - 1);
913 tmp_data[i] = floor(tmp_data[i] + .5);
914 }
915 for (i = ceil(k_upper); i < lut->steps; i ++)
916 tmp_data[i] = 65535.0 * i / (double) (lut->steps - 1);
917 }
918 else if (k_lower < lut->steps)
919 for (i = ceil(k_lower); i < lut->steps; i ++)
920 {
921 double where = (i - k_lower) / (k_upper - k_lower);
922 double g1 = pow(where, i_k_trans);
923 double g2 = 1.0 - pow(1.0 - where, k_trans);
924 double value = (i_k_trans <= 1.0 ? g1 : g2);
925 tmp_data[i] = 65535.0 * lut->steps * value / (double) (lut->steps - 1);
926 tmp_data[i] = floor(tmp_data[i] + .5);
927 }
928 curve = stp_curve_create(STP_CURVE_WRAP_NONE);
929 stp_curve_set_bounds(curve, 0, 65535);
930 STPI_ASSERT(stp_curve_set_data(curve, lut->steps, tmp_data), vars);
931 stp_free(tmp_data);
932 return curve;
933 }
934
935 static void
initialize_gcr_curve(stp_vars_t * vars)936 initialize_gcr_curve(stp_vars_t *vars)
937 {
938 lut_t *lut = (lut_t *)(stp_get_component_data(vars, "Color"));
939 stp_curve_t *curve = NULL;
940 if (stp_check_curve_parameter(vars, "GCRCurve", STP_PARAMETER_DEFAULTED))
941 {
942 double data;
943 size_t count;
944 int i;
945 curve = stp_curve_create_copy(stp_get_curve_parameter(vars, "GCRCurve"));
946 stp_curve_resample(curve, lut->steps);
947 count = stp_curve_count_points(curve);
948 stp_curve_set_bounds(curve, 0.0, 65535.0);
949 for (i = 0; i < count; i++)
950 {
951 stp_curve_get_point(curve, i, &data);
952 data = 65535.0 * data * (double) i / (count - 1);
953 stp_curve_set_point(curve, i, data);
954 }
955 }
956 else
957 curve = compute_gcr_curve(vars);
958 stp_channel_set_gcr_curve(vars, curve);
959 if (curve)
960 stp_curve_destroy(curve);
961 }
962
963 /*
964 * Channels that are synthesized (e. g. the black channel in CMY -> CMYK
965 * conversion) need to be computed differently from channels that are
966 * mapped 1-1 from input to output. Channels that are simply mapped need
967 * to be inverted if necessary, and in addition the contrast, brightness,
968 * and other gamma factors are factored in. Channels that are synthesized
969 * are never inverted, since they're computed from the output of other
970 * channels that have already been inverted.
971 *
972 * This isn't simply a matter of comparing the input channels to the output
973 * channels, since some of the conversions (K -> CMYK) work by means
974 * of a chain of conversions (K->CMY->CMYK). In this case, we need to
975 * fully compute the CMY channels, but the K channel in the output is
976 * not inverted.
977 *
978 * The rules that are implemented by the logic below are:
979 *
980 * 1) If the input is raw, we always perform the normal computation (without
981 * synthesizing channels).
982 *
983 * 2) If the output is CMY or K only, we never synthesize channels. We've
984 * now covered raw, black, and CMY/RGB outputs, leaving CMYK and MULTI.
985 *
986 * 3) Output channels above CMYK are synthesized.
987 *
988 * 4) If the input is CMYK, we do not synthesize channels.
989 *
990 * 5) The black channel (in CMYK) is synthesized.
991 *
992 * 6) All other channels (CMY/RGB) are not synthesized.
993 */
994
995 static int
channel_is_synthesized(lut_t * lut,int channel)996 channel_is_synthesized(lut_t *lut, int channel)
997 {
998 if (lut->output_color_description->color_id == COLOR_ID_RAW)
999 return 1; /* Case 1 */
1000 else if (lut->output_color_description->channels == CMASK_CMY ||
1001 lut->output_color_description->channels == CMASK_K)
1002 return 0; /* Case 2 */
1003 else if (channel >= CHANNEL_W)
1004 return 1; /* Case 3 */
1005 else if (lut->input_color_description->channels == CMASK_CMYK)
1006 return 0; /* Case 4 */
1007 else if (channel == CHANNEL_K)
1008 return 1; /* Case 5 */
1009 else
1010 return 0; /* Case 6 */
1011 }
1012
1013 static void
compute_user_correction(lut_t * lut)1014 compute_user_correction(lut_t *lut)
1015 {
1016 double *tmp;
1017 double *tmp_brightness;
1018 double *tmp_contrast;
1019 double xcontrast = lut->contrast;
1020 stp_curve_t *curve =
1021 stp_curve_cache_get_curve(&(lut->user_color_correction));
1022 stp_curve_t *brightness_curve =
1023 stp_curve_cache_get_curve(&(lut->brightness_correction));
1024 stp_curve_t *contrast_curve =
1025 stp_curve_cache_get_curve(&(lut->contrast_correction));
1026 double brightness = lut->brightness;
1027 int i;
1028 int isteps = lut->steps;
1029 if (isteps > 256)
1030 isteps = 256;
1031 tmp = stp_malloc(sizeof(double) * lut->steps);
1032 tmp_brightness = stp_malloc(sizeof(double) * lut->steps);
1033 tmp_contrast = stp_malloc(sizeof(double) * lut->steps);
1034 if (brightness < .001)
1035 brightness = 1000;
1036 else if (brightness > 1.999)
1037 brightness = .001;
1038 else if (brightness > 1)
1039 brightness = 2.0 - brightness;
1040 else
1041 brightness = 1.0 / brightness;
1042 for (i = 0; i < isteps; i ++)
1043 {
1044 double temp_pixel, pixel;
1045 pixel = (double) i / (double) (isteps - 1);
1046 /*
1047 * First, correct contrast
1048 */
1049 if (pixel >= .5)
1050 temp_pixel = 1.0 - pixel;
1051 else
1052 temp_pixel = pixel;
1053 if (lut->contrast > 3.99999)
1054 {
1055 if (temp_pixel < .5)
1056 temp_pixel = 0;
1057 else
1058 temp_pixel = 1;
1059 }
1060 if (temp_pixel <= .000001 && lut->contrast <= .0001)
1061 temp_pixel = .5;
1062 else if (temp_pixel > 1)
1063 temp_pixel = .5 * pow(2 * temp_pixel, xcontrast);
1064 else if (temp_pixel < 1)
1065 {
1066 if (lut->linear_contrast_adjustment)
1067 temp_pixel = 0.5 -
1068 ((0.5 - .5 * pow(2 * temp_pixel, lut->contrast)) *
1069 lut->contrast);
1070 else
1071 temp_pixel = 0.5 -
1072 ((0.5 - .5 * pow(2 * temp_pixel, lut->contrast)));
1073 }
1074 if (temp_pixel > .5)
1075 temp_pixel = .5;
1076 else if (temp_pixel < 0)
1077 temp_pixel = 0;
1078 if (pixel < .5)
1079 pixel = temp_pixel;
1080 else
1081 pixel = 1 - temp_pixel;
1082 tmp_contrast[i] = floor((pixel * 65535) + .5);
1083
1084 /*
1085 * Second, do brightness
1086 */
1087 if (brightness < 1)
1088 pixel = pow(pixel, brightness);
1089 else
1090 pixel = 1.0 - pow(1.0 - pixel, 1.0 / brightness);
1091 tmp[i] = floor((pixel * 65535) + .5);
1092
1093 pixel = (double) i / (double) (isteps - 1);
1094 if (brightness < 1)
1095 pixel = pow(pixel, brightness);
1096 else
1097 pixel = 1.0 - pow(1.0 - pixel, 1.0 / brightness);
1098 tmp_brightness[i] = floor((pixel * 65535) + .5);
1099 }
1100 stp_curve_set_data(curve, isteps, tmp);
1101 if (isteps != lut->steps)
1102 stp_curve_resample(curve, lut->steps);
1103 stp_curve_set_data(brightness_curve, isteps, tmp_brightness);
1104 if (isteps != lut->steps)
1105 stp_curve_resample(brightness_curve, lut->steps);
1106 stp_curve_set_data(contrast_curve, isteps, tmp_contrast);
1107 if (isteps != lut->steps)
1108 stp_curve_resample(contrast_curve, lut->steps);
1109 stp_free(tmp);
1110 stp_free(tmp_brightness);
1111 stp_free(tmp_contrast);
1112 }
1113
1114 static void
compute_a_curve_full(lut_t * lut,int channel)1115 compute_a_curve_full(lut_t *lut, int channel)
1116 {
1117 double *tmp;
1118 double pivot = .25;
1119 double ipivot = 1.0 - pivot;
1120 double xgamma = pow(pivot, lut->screen_gamma);
1121 double print_gamma=1.0+9.0*(lut->print_gamma-1.0);
1122 double pivot2 = .75;
1123 double ipivot2 = 1.0 - pivot2;
1124 double xgamma2 = pow(pivot2, print_gamma);
1125 stp_curve_t *curve = stp_curve_cache_get_curve(&(lut->channel_curves[channel]));
1126 int i;
1127 int isteps = lut->steps;
1128 if (isteps > 256)
1129 isteps = 256;
1130 tmp = stp_malloc(sizeof(double) * lut->steps);
1131 for (i = 0; i < isteps; i ++)
1132 {
1133 double pixel = (double) i / (double) (isteps - 1);
1134
1135 if (lut->input_color_description->color_model == COLOR_BLACK)
1136 pixel = 1.0 - pixel;
1137
1138 pixel = 1.0 -
1139 (1.0 / (1.0 - xgamma)) *
1140 (pow(pivot + ipivot * pixel, lut->screen_gamma) - xgamma);
1141
1142 /*
1143 * Fourth, fix up cyan, magenta, yellow values
1144 */
1145 if (pixel < 0.0)
1146 pixel = 0.0;
1147 else if (pixel > 1.0)
1148 pixel = 1.0;
1149
1150 if (pixel > .9999 && lut->gamma_values[channel] < .00001)
1151 pixel = 0;
1152 else
1153 pixel = 1 - pow(1 - pixel, lut->gamma_values[channel]);
1154 /*
1155 * Finally, fix up print gamma and scale
1156 */
1157
1158 /*
1159 * Change to this function suggested by Alastair Robinson
1160 * blackfive@fakenhamweb.co.uk
1161 */
1162 pixel = 65535 * (1.0 / (1.0 - xgamma2)) *
1163 (pow(pivot2 + ipivot2 * pixel, print_gamma) - xgamma2);
1164 if (lut->output_color_description->color_model == COLOR_WHITE)
1165 pixel = 65535 - pixel;
1166
1167 if (pixel <= 0.0)
1168 tmp[i] = 0;
1169 else if (pixel >= 65535.0)
1170 tmp[i] = 65535;
1171 else
1172 tmp[i] = (pixel);
1173 tmp[i] = floor(tmp[i] + 0.5); /* rounding is done here */
1174 }
1175 stp_curve_set_data(curve, isteps, tmp);
1176 if (isteps != lut->steps)
1177 stp_curve_resample(curve, lut->steps);
1178 stp_free(tmp);
1179 }
1180
1181 static void
compute_a_curve_fast(lut_t * lut,int channel)1182 compute_a_curve_fast(lut_t *lut, int channel)
1183 {
1184 double *tmp;
1185 stp_curve_t *curve = stp_curve_cache_get_curve(&(lut->channel_curves[channel]));
1186 int i;
1187 int isteps = lut->steps;
1188 if (isteps > 256)
1189 isteps = 256;
1190 tmp = stp_malloc(sizeof(double) * lut->steps);
1191 for (i = 0; i < isteps; i++)
1192 {
1193 double pixel = (double) i / (double) (isteps - 1);
1194 pixel = 1 - pow(1 - pixel, lut->gamma_values[channel]);
1195 tmp[i] = floor((65535.0 * pixel) + 0.5);
1196 }
1197 stp_curve_set_data(curve, isteps, tmp);
1198 if (isteps != lut->steps)
1199 stp_curve_resample(curve, lut->steps);
1200 stp_free(tmp);
1201 }
1202
1203 static void
compute_a_curve_simple(lut_t * lut,int channel)1204 compute_a_curve_simple(lut_t *lut, int channel)
1205 {
1206 double *tmp;
1207 stp_curve_t *curve = stp_curve_cache_get_curve(&(lut->channel_curves[channel]));
1208 int i;
1209 int isteps = lut->steps;
1210 double gamma = 1.0 / (lut->gamma_values[channel] * lut->print_gamma);
1211 if (isteps > 256)
1212 isteps = 256;
1213 tmp = stp_malloc(sizeof(double) * lut->steps);
1214 for (i = 0; i < isteps; i++)
1215 {
1216 double pixel = (double) i / (double) (isteps - 1);
1217 if (lut->input_color_description->color_model == COLOR_BLACK)
1218 pixel = 1.0 - pixel;
1219 pixel = pow(pixel, gamma);
1220 if (lut->output_color_description->color_model == COLOR_BLACK)
1221 pixel = 1.0 - pixel;
1222 tmp[i] = floor((65535.0 * pixel) + 0.5);
1223 }
1224 stp_curve_set_data(curve, isteps, tmp);
1225 if (isteps != lut->steps)
1226 stp_curve_resample(curve, lut->steps);
1227 stp_free(tmp);
1228 }
1229
1230 /*
1231 * If the input and output color spaces both have a particular channel,
1232 * we want to use the general algorithm. If not (i. e. we have to
1233 * synthesize the channel), use a simple gamma curve.
1234 */
1235 static void
compute_a_curve(lut_t * lut,int channel)1236 compute_a_curve(lut_t *lut, int channel)
1237 {
1238 if (channel_is_synthesized(lut, channel))
1239 compute_a_curve_fast(lut, channel);
1240 else if (lut->simple_gamma_correction)
1241 compute_a_curve_simple(lut, channel);
1242 else
1243 compute_a_curve_full(lut, channel);
1244 }
1245
1246 static void
invert_curve(stp_curve_t * curve,int invert_output)1247 invert_curve(stp_curve_t *curve, int invert_output)
1248 {
1249 double lo, hi;
1250 int i;
1251 size_t count;
1252 const double *data = stp_curve_get_data(curve, &count);
1253 double f_gamma = stp_curve_get_gamma(curve);
1254 double *tmp_data;
1255
1256 stp_curve_get_bounds(curve, &lo, &hi);
1257
1258 if (f_gamma)
1259 stp_curve_set_gamma(curve, -f_gamma);
1260 else
1261 {
1262 tmp_data = stp_malloc(sizeof(double) * count);
1263 for (i = 0; i < count; i++)
1264 tmp_data[i] = data[count - i - 1];
1265 stp_curve_set_data(curve, count, tmp_data);
1266 stp_free(tmp_data);
1267 }
1268 if (!invert_output)
1269 {
1270 stp_curve_rescale(curve, -1, STP_CURVE_COMPOSE_MULTIPLY,
1271 STP_CURVE_BOUNDS_RESCALE);
1272 stp_curve_rescale(curve, lo + hi, STP_CURVE_COMPOSE_ADD,
1273 STP_CURVE_BOUNDS_RESCALE);
1274 }
1275 }
1276
1277 static void
compute_one_lut(lut_t * lut,int i)1278 compute_one_lut(lut_t *lut, int i)
1279 {
1280 stp_curve_t *curve =
1281 stp_curve_cache_get_curve(&(lut->channel_curves[i]));
1282 if (curve)
1283 {
1284 int invert_output =
1285 !channel_is_synthesized(lut, i) && lut->invert_output;
1286 stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
1287 STP_CURVE_BOUNDS_RESCALE);
1288 if (stp_curve_is_piecewise(curve))
1289 stp_curve_resample(curve, lut->steps);
1290 if (lut->invert_output)
1291 invert_curve(curve, invert_output);
1292 stp_curve_resample(curve, lut->steps);
1293 }
1294 else
1295 {
1296 curve = stp_curve_create_copy(color_curve_bounds);
1297 stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
1298 STP_CURVE_BOUNDS_RESCALE);
1299 stp_curve_cache_set_curve(&(lut->channel_curves[i]), curve);
1300 compute_a_curve(lut, i);
1301 }
1302 }
1303
1304 static void
setup_channel(stp_vars_t * v,int i,const channel_param_t * p)1305 setup_channel(stp_vars_t *v, int i, const channel_param_t *p)
1306 {
1307 lut_t *lut = (lut_t *)(stp_get_component_data(v, "Color"));
1308 const char *gamma_name =
1309 (lut->output_color_description->color_model == COLOR_BLACK ?
1310 p->gamma_name : p->rgb_gamma_name);
1311 const char *curve_name =
1312 (lut->output_color_description->color_model == COLOR_BLACK ?
1313 p->curve_name : p->rgb_curve_name);
1314 if (stp_check_float_parameter(v, p->gamma_name, STP_PARAMETER_DEFAULTED))
1315 lut->gamma_values[i] = stp_get_float_parameter(v, gamma_name);
1316
1317 if (stp_get_curve_parameter_active(v, curve_name) > 0 &&
1318 stp_get_curve_parameter_active(v, curve_name) >=
1319 stp_get_float_parameter_active(v, gamma_name))
1320 stp_curve_cache_set_curve_copy
1321 (&(lut->channel_curves[i]), stp_get_curve_parameter(v, curve_name));
1322
1323 stp_dprintf(STP_DBG_LUT, v, " %s %.3f\n", gamma_name, lut->gamma_values[i]);
1324 compute_one_lut(lut, i);
1325 }
1326
1327 static void
stpi_print_lut_curve(FILE * fp,const char * text,stp_cached_curve_t * c,int reverse)1328 stpi_print_lut_curve(FILE *fp, const char *text, stp_cached_curve_t *c,
1329 int reverse)
1330 {
1331 if (stp_curve_cache_get_curve(c))
1332 {
1333 fprintf(fp, "%s: '", text);
1334 if (reverse)
1335 {
1336 stp_curve_t *rev = stp_curve_create_reverse(stp_curve_cache_get_curve(c));
1337 stp_curve_write(fp, rev);
1338 stp_curve_destroy(rev);
1339 }
1340 else
1341 stp_curve_write(fp, stp_curve_cache_get_curve(c));
1342 fprintf(fp, "'\n");
1343 }
1344 }
1345
1346 static void
stpi_do_dump_lut_to_file(stp_vars_t * v,FILE * fp)1347 stpi_do_dump_lut_to_file(stp_vars_t *v, FILE *fp)
1348 {
1349 int i;
1350 lut_t *lut = (lut_t *)(stp_get_component_data(v, "Color"));
1351 const stp_curve_t *curve;
1352 fprintf(fp, "Gutenprint LUT dump version 0\n\n");
1353 fprintf(fp, "Input color description: '%s'\n", lut->input_color_description->name);
1354 fprintf(fp, "Output color description: '%s'\n", lut->output_color_description->name);
1355 fprintf(fp, "Color correction type: '%s'\n", lut->color_correction->name);
1356 fprintf(fp, "Ink limit: %f\n", stp_get_float_parameter(v, "InkLimit"));
1357 stpi_print_lut_curve(fp, "Brightness correction", &(lut->brightness_correction), 0);
1358 stpi_print_lut_curve(fp, "Contrast correction", &(lut->contrast_correction), 0);
1359 stpi_print_lut_curve(fp, "User color correction", &(lut->user_color_correction), 0);
1360 for (i = 0; i < STP_CHANNEL_LIMIT; i++)
1361 {
1362 char buf[64];
1363 sprintf(buf, "Channel %d curve", i);
1364 stpi_print_lut_curve(fp, buf, &(lut->channel_curves[i]),
1365 lut->invert_output && ! channel_is_synthesized(lut, i));
1366 }
1367 curve = stp_channel_get_gcr_curve(v);
1368 if (curve)
1369 {
1370 fprintf(fp, "GCR curve: '");
1371 stp_curve_write(fp, curve);
1372 fprintf(fp, "'\n");
1373 }
1374 }
1375
1376 static void
stpi_dump_lut_to_file(stp_vars_t * v,const char * dump_file)1377 stpi_dump_lut_to_file(stp_vars_t *v, const char *dump_file)
1378 {
1379 FILE *fp;
1380 if (!dump_file)
1381 return;
1382 fp = fopen(dump_file, "w");
1383 if (fp)
1384 {
1385 stp_dprintf(STP_DBG_LUT, v, "Dumping LUT to %s\n", dump_file);
1386 stpi_do_dump_lut_to_file(v, fp);
1387 (void) fclose(fp);
1388 }
1389 }
1390
1391 static void
stpi_compute_lut(stp_vars_t * v)1392 stpi_compute_lut(stp_vars_t *v)
1393 {
1394 int i;
1395 lut_t *lut = (lut_t *)(stp_get_component_data(v, "Color"));
1396 double app_gamma_scale = 4.0;
1397 stp_curve_t *curve;
1398 stp_dprintf(STP_DBG_LUT, v, "stpi_compute_lut\n");
1399
1400 if (lut->input_color_description->color_model == COLOR_UNKNOWN ||
1401 lut->output_color_description->color_model == COLOR_UNKNOWN ||
1402 lut->input_color_description->color_model ==
1403 lut->output_color_description->color_model)
1404 lut->invert_output = 0;
1405 else
1406 lut->invert_output = 1;
1407
1408 lut->linear_contrast_adjustment = 0;
1409 lut->print_gamma = 1.0;
1410 lut->app_gamma = 1.0;
1411 lut->contrast = 1.0;
1412 lut->brightness = 1.0;
1413 lut->simple_gamma_correction = 0;
1414
1415 if (stp_check_boolean_parameter(v, "LinearContrast", STP_PARAMETER_DEFAULTED))
1416 lut->linear_contrast_adjustment =
1417 stp_get_boolean_parameter(v, "LinearContrast");
1418 if (stp_check_float_parameter(v, "Gamma", STP_PARAMETER_DEFAULTED))
1419 lut->print_gamma = stp_get_float_parameter(v, "Gamma");
1420 if (stp_check_float_parameter(v, "Contrast", STP_PARAMETER_DEFAULTED))
1421 lut->contrast = stp_get_float_parameter(v, "Contrast");
1422 if (stp_check_float_parameter(v, "Brightness", STP_PARAMETER_DEFAULTED))
1423 lut->brightness = stp_get_float_parameter(v, "Brightness");
1424
1425 if (stp_check_float_parameter(v, "AppGamma", STP_PARAMETER_ACTIVE))
1426 lut->app_gamma = stp_get_float_parameter(v, "AppGamma");
1427 if (stp_check_float_parameter(v, "AppGammaScale", STP_PARAMETER_ACTIVE))
1428 app_gamma_scale = stp_get_float_parameter(v, "AppGammaScale");
1429 if (stp_check_boolean_parameter(v, "SimpleGamma", STP_PARAMETER_ACTIVE))
1430 lut->simple_gamma_correction = stp_get_boolean_parameter(v, "SimpleGamma");
1431 lut->screen_gamma = lut->app_gamma / app_gamma_scale; /* "Empirical" */
1432 curve = stp_curve_create_copy(color_curve_bounds);
1433 stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
1434 STP_CURVE_BOUNDS_RESCALE);
1435 stp_curve_cache_set_curve(&(lut->user_color_correction), curve);
1436 curve = stp_curve_create_copy(color_curve_bounds);
1437 stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
1438 STP_CURVE_BOUNDS_RESCALE);
1439 stp_curve_cache_set_curve(&(lut->brightness_correction), curve);
1440 curve = stp_curve_create_copy(color_curve_bounds);
1441 stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
1442 STP_CURVE_BOUNDS_RESCALE);
1443 stp_curve_cache_set_curve(&(lut->contrast_correction), curve);
1444 compute_user_correction(lut);
1445
1446 /*
1447 * TODO check that these are wraparound curves and all that
1448 */
1449
1450 if (lut->color_correction->correct_hsl)
1451 {
1452 if (stp_check_curve_parameter(v, "HueMap", STP_PARAMETER_DEFAULTED))
1453 {
1454 lut->hue_map.curve =
1455 stp_curve_create_copy(stp_get_curve_parameter(v, "HueMap"));
1456 if (stp_curve_is_piecewise(lut->hue_map.curve))
1457 stp_curve_resample(lut->hue_map.curve, 384);
1458 }
1459 if (stp_check_curve_parameter(v, "LumMap", STP_PARAMETER_DEFAULTED))
1460 {
1461 lut->lum_map.curve =
1462 stp_curve_create_copy(stp_get_curve_parameter(v, "LumMap"));
1463 if (stp_curve_is_piecewise(lut->lum_map.curve))
1464 stp_curve_resample(lut->lum_map.curve, 384);
1465 }
1466 if (stp_check_curve_parameter(v, "SatMap", STP_PARAMETER_DEFAULTED))
1467 {
1468 lut->sat_map.curve =
1469 stp_curve_create_copy(stp_get_curve_parameter(v, "SatMap"));
1470 if (stp_curve_is_piecewise(lut->sat_map.curve))
1471 stp_curve_resample(lut->sat_map.curve, 384);
1472 }
1473 }
1474
1475 stp_dprintf(STP_DBG_LUT, v, " print_gamma %.3f\n", lut->print_gamma);
1476 stp_dprintf(STP_DBG_LUT, v, " contrast %.3f\n", lut->contrast);
1477 stp_dprintf(STP_DBG_LUT, v, " brightness %.3f\n", lut->brightness);
1478 stp_dprintf(STP_DBG_LUT, v, " screen_gamma %.3f\n", lut->screen_gamma);
1479
1480 for (i = 0; i < STP_CHANNEL_LIMIT; i++)
1481 {
1482 STPI_ASSERT(i < raw_channel_param_count, v);
1483 if (lut->output_color_description->channel_count < 1 &&
1484 i < lut->out_channels)
1485 setup_channel(v, i, &(raw_channel_params[i]));
1486 else if (i < channel_param_count &&
1487 lut->output_color_description->channels & (1 << i))
1488 setup_channel(v, i, &(channel_params[i]));
1489 }
1490 if (((lut->output_color_description->channels & CMASK_CMYK) == CMASK_CMYK) &&
1491 (lut->color_correction->correction == COLOR_CORRECTION_DESATURATED ||
1492 lut->input_color_description->color_id == COLOR_ID_GRAY ||
1493 lut->input_color_description->color_id == COLOR_ID_WHITE ||
1494 lut->input_color_description->color_id == COLOR_ID_RGB ||
1495 lut->input_color_description->color_id == COLOR_ID_CMY))
1496 initialize_gcr_curve(v);
1497 if (stp_check_file_parameter(v, "LUTDumpFile", STP_PARAMETER_ACTIVE))
1498 stpi_dump_lut_to_file(v, stp_get_file_parameter(v, "LUTDumpFile"));
1499 }
1500
1501 static int
stpi_color_traditional_init(stp_vars_t * v,stp_image_t * image,size_t steps)1502 stpi_color_traditional_init(stp_vars_t *v,
1503 stp_image_t *image,
1504 size_t steps)
1505 {
1506 lut_t *lut;
1507 const char *image_type = stp_get_string_parameter(v, "ImageType");
1508 const char *color_correction = stp_get_string_parameter(v, "ColorCorrection");
1509 const channel_depth_t *channel_depth =
1510 get_channel_depth(stp_get_string_parameter(v, "ChannelBitDepth"));
1511 size_t total_channel_bits;
1512
1513 if (steps != 256 && steps != 65536)
1514 {
1515 stp_eprintf(v,
1516 "stpi_color_traditional_init: Invalid color steps %lu (must be 256 or 65536)\n",
1517 (unsigned long) steps);
1518 return -1;
1519 }
1520 if (!channel_depth)
1521 {
1522 stp_eprintf(v, "stpi_color_traditional_init: ChannelBitDepth not set\n");
1523 return -1;
1524 }
1525
1526 lut = allocate_lut();
1527 lut->input_color_description =
1528 get_color_description(stp_get_string_parameter(v, "InputImageType"));
1529 lut->output_color_description =
1530 get_color_description(stp_get_string_parameter(v, "STPIOutputType"));
1531
1532 if (!lut->input_color_description || !lut->output_color_description)
1533 {
1534 stp_eprintf(v, "stpi_color_traditional_init: input/output types not specified\n");
1535 free_lut(lut);
1536 return -1;
1537 }
1538
1539 if (lut->input_color_description->color_id == COLOR_ID_RAW)
1540 {
1541 if (stp_verify_parameter(v, "STPIRawChannels", 1) != PARAMETER_OK)
1542 {
1543 stp_eprintf(v, "stpi_color_traditional_init: raw printing requested but STPIRawChannels not set\n");
1544 free_lut(lut);
1545 return -1;
1546 }
1547 lut->out_channels = stp_get_int_parameter(v, "STPIRawChannels");
1548 lut->in_channels = lut->out_channels;
1549 }
1550 else
1551 {
1552 lut->out_channels = lut->output_color_description->channel_count;
1553 lut->in_channels = lut->input_color_description->channel_count;
1554 }
1555
1556 stp_allocate_component_data(v, "Color", copy_lut, free_lut, lut);
1557 lut->steps = steps;
1558 lut->channel_depth = channel_depth->bits;
1559
1560 if ((!color_correction || strcmp(color_correction, "None") == 0) &&
1561 image_type && strcmp(image_type, "None") != 0)
1562 {
1563 if (strcmp(image_type, "Text") == 0)
1564 lut->color_correction = get_color_correction("Threshold");
1565 else
1566 lut->color_correction = get_color_correction("None");
1567 }
1568 else if (color_correction)
1569 lut->color_correction = get_color_correction(color_correction);
1570 else
1571 lut->color_correction = get_color_correction("None");
1572 if (lut->color_correction->correction == COLOR_CORRECTION_DEFAULT)
1573 lut->color_correction =
1574 (get_color_correction_by_tag
1575 (lut->output_color_description->default_correction));
1576
1577 stpi_compute_lut(v);
1578
1579 lut->image_width = stp_image_width(image);
1580 total_channel_bits = lut->in_channels * lut->channel_depth;
1581 lut->in_data = stp_malloc(((lut->image_width * total_channel_bits) + 7)/8);
1582 memset(lut->in_data, 0, ((lut->image_width * total_channel_bits) + 7) / 8);
1583 return lut->out_channels;
1584 }
1585
1586 static void
initialize_standard_curves(void)1587 initialize_standard_curves(void)
1588 {
1589 if (!standard_curves_initialized)
1590 {
1591 int i;
1592 hue_map_bounds = stp_curve_create_from_string
1593 ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1594 "<gutenprint>\n"
1595 "<curve wrap=\"wrap\" type=\"linear\" gamma=\"0\">\n"
1596 "<sequence count=\"2\" lower-bound=\"-6\" upper-bound=\"6\">\n"
1597 "0 0\n"
1598 "</sequence>\n"
1599 "</curve>\n"
1600 "</gutenprint>");
1601 lum_map_bounds = stp_curve_create_from_string
1602 ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1603 "<gutenprint>\n"
1604 "<curve wrap=\"wrap\" type=\"linear\" gamma=\"0\">\n"
1605 "<sequence count=\"2\" lower-bound=\"0\" upper-bound=\"4\">\n"
1606 "1 1\n"
1607 "</sequence>\n"
1608 "</curve>\n"
1609 "</gutenprint>");
1610 sat_map_bounds = stp_curve_create_from_string
1611 ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1612 "<gutenprint>\n"
1613 "<curve wrap=\"wrap\" type=\"linear\" gamma=\"0\">\n"
1614 "<sequence count=\"2\" lower-bound=\"0\" upper-bound=\"4\">\n"
1615 "1 1\n"
1616 "</sequence>\n"
1617 "</curve>\n"
1618 "</gutenprint>");
1619 color_curve_bounds = stp_curve_create_from_string
1620 ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1621 "<gutenprint>\n"
1622 "<curve wrap=\"nowrap\" type=\"linear\" gamma=\"1.0\">\n"
1623 "<sequence count=\"0\" lower-bound=\"0\" upper-bound=\"1\">\n"
1624 "</sequence>\n"
1625 "</curve>\n"
1626 "</gutenprint>");
1627 gcr_curve_bounds = stp_curve_create_from_string
1628 ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1629 "<gutenprint>\n"
1630 "<curve wrap=\"nowrap\" type=\"linear\" gamma=\"0.0\">\n"
1631 "<sequence count=\"2\" lower-bound=\"0\" upper-bound=\"1\">\n"
1632 "1 1\n"
1633 "</sequence>\n"
1634 "</curve>\n"
1635 "</gutenprint>");
1636 for (i = 0; i < curve_parameter_count; i++)
1637 curve_parameters[i].param.deflt.curve =
1638 *(curve_parameters[i].defval);
1639 standard_curves_initialized = 1;
1640 }
1641 }
1642
1643 static stp_parameter_list_t
stpi_color_traditional_list_parameters(const stp_vars_t * v)1644 stpi_color_traditional_list_parameters(const stp_vars_t *v)
1645 {
1646 stp_list_t *ret = stp_parameter_list_create();
1647 int i;
1648 initialize_standard_curves();
1649 for (i = 0; i < float_parameter_count; i++)
1650 stp_parameter_list_add_param(ret, &(float_parameters[i].param));
1651 for (i = 0; i < curve_parameter_count; i++)
1652 stp_parameter_list_add_param(ret, &(curve_parameters[i].param));
1653 return ret;
1654 }
1655
1656 static void
stpi_color_traditional_describe_parameter(const stp_vars_t * v,const char * name,stp_parameter_t * description)1657 stpi_color_traditional_describe_parameter(const stp_vars_t *v,
1658 const char *name,
1659 stp_parameter_t *description)
1660 {
1661 int i, j;
1662 description->p_type = STP_PARAMETER_TYPE_INVALID;
1663 initialize_standard_curves();
1664 if (name == NULL)
1665 return;
1666
1667 for (i = 0; i < float_parameter_count; i++)
1668 {
1669 const float_param_t *param = &(float_parameters[i]);
1670 if (strcmp(name, param->param.name) == 0)
1671 {
1672 stp_fill_parameter_settings(description, &(param->param));
1673 if (param->channel_mask != CMASK_EVERY)
1674 {
1675 const color_description_t *color_description =
1676 get_color_description(stp_describe_output(v));
1677 if (color_description &&
1678 (param->channel_mask & color_description->channels) &&
1679 (param->is_rgb < 0 ||
1680 (param->is_rgb == 0 &&
1681 color_description->color_model == COLOR_BLACK) ||
1682 (param->is_rgb == 1 &&
1683 color_description->color_model == COLOR_WHITE)) &&
1684 param->channel_mask != CMASK_RAW)
1685 description->is_active = 1;
1686 else
1687 description->is_active = 0;
1688 if (param->color_only && color_description &&
1689 !(color_description->channels & ~CMASK_K))
1690 description->is_active = 0;
1691 }
1692 switch (param->param.p_type)
1693 {
1694 case STP_PARAMETER_TYPE_BOOLEAN:
1695 description->deflt.boolean = (int) param->defval;
1696 break;
1697 case STP_PARAMETER_TYPE_INT:
1698 description->bounds.integer.upper = (int) param->max;
1699 description->bounds.integer.lower = (int) param->min;
1700 description->deflt.integer = (int) param->defval;
1701 break;
1702 case STP_PARAMETER_TYPE_DOUBLE:
1703 description->bounds.dbl.upper = param->max;
1704 description->bounds.dbl.lower = param->min;
1705 description->deflt.dbl = param->defval;
1706 if (strcmp(name, "InkLimit") == 0)
1707 {
1708 stp_parameter_t ink_limit_desc;
1709 stp_describe_parameter(v, "InkChannels", &ink_limit_desc);
1710 if (ink_limit_desc.p_type == STP_PARAMETER_TYPE_INT &&
1711 ink_limit_desc.deflt.integer > 1)
1712 {
1713 description->bounds.dbl.upper =
1714 ink_limit_desc.deflt.integer;
1715 description->deflt.dbl =
1716 ink_limit_desc.deflt.integer;
1717 }
1718 else
1719 description->is_active = 0;
1720
1721 stp_parameter_description_destroy(&ink_limit_desc);
1722 }
1723 break;
1724 case STP_PARAMETER_TYPE_STRING_LIST:
1725 if (!strcmp(param->param.name, "ColorCorrection"))
1726 {
1727 description->bounds.str = stp_string_list_create();
1728 for (j = 0; j < color_correction_count; j++)
1729 stp_string_list_add_string
1730 (description->bounds.str, color_corrections[j].name,
1731 gettext(color_corrections[j].text));
1732 description->deflt.str =
1733 stp_string_list_param(description->bounds.str, 0)->name;
1734 }
1735 else if (strcmp(name, "ChannelBitDepth") == 0)
1736 {
1737 description->bounds.str = stp_string_list_create();
1738 for (j = 0; j < channel_depth_count; j++)
1739 stp_string_list_add_string
1740 (description->bounds.str, channel_depths[j].name,
1741 channel_depths[j].name);
1742 description->deflt.str =
1743 stp_string_list_param(description->bounds.str, 0)->name;
1744 }
1745 else if (strcmp(name, "InputImageType") == 0)
1746 {
1747 description->bounds.str = stp_string_list_create();
1748 for (j = 0; j < color_description_count; j++)
1749 if (color_descriptions[j].input)
1750 {
1751 if (color_descriptions[j].color_id == COLOR_ID_RAW)
1752 {
1753 stp_parameter_t desc;
1754 stp_describe_parameter(v, "RawChannels", &desc);
1755 if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
1756 stp_string_list_add_string
1757 (description->bounds.str,
1758 color_descriptions[j].name,
1759 gettext(color_descriptions[j].name));
1760 stp_parameter_description_destroy(&desc);
1761 }
1762 else
1763 stp_string_list_add_string
1764 (description->bounds.str,
1765 color_descriptions[j].name,
1766 gettext(color_descriptions[j].name));
1767 }
1768 description->deflt.str =
1769 stp_string_list_param(description->bounds.str, 0)->name;
1770 }
1771 else if (strcmp(name, "OutputImageType") == 0)
1772 {
1773 description->bounds.str = stp_string_list_create();
1774 for (j = 0; j < color_description_count; j++)
1775 if (color_descriptions[j].output)
1776 stp_string_list_add_string
1777 (description->bounds.str, color_descriptions[j].name,
1778 gettext(color_descriptions[j].name));
1779 description->deflt.str =
1780 stp_string_list_param(description->bounds.str, 0)->name;
1781 }
1782 break;
1783 default:
1784 break;
1785 }
1786 return;
1787 }
1788 }
1789 for (i = 0; i < curve_parameter_count; i++)
1790 {
1791 curve_param_t *param = &(curve_parameters[i]);
1792 if (strcmp(name, param->param.name) == 0)
1793 {
1794 description->is_active = 1;
1795 stp_fill_parameter_settings(description, &(param->param));
1796 if (param->channel_mask != CMASK_EVERY)
1797 {
1798 const color_description_t *color_description =
1799 get_color_description(stp_describe_output(v));
1800 if (color_description &&
1801 (param->is_rgb < 0 ||
1802 (param->is_rgb == 0 &&
1803 color_description->color_model == COLOR_BLACK) ||
1804 (param->is_rgb == 1 &&
1805 color_description->color_model == COLOR_WHITE)) &&
1806 (param->channel_mask & color_description->channels))
1807 description->is_active = 1;
1808 else
1809 description->is_active = 0;
1810 if (param->color_only && color_description &&
1811 !(color_description->channels & ~CMASK_K))
1812 description->is_active = 0;
1813 }
1814 if (param->hsl_only)
1815 {
1816 const color_correction_t *correction =
1817 (get_color_correction
1818 (stp_get_string_parameter (v, "ColorCorrection")));
1819 if (correction && !correction->correct_hsl)
1820 description->is_active = 0;
1821 }
1822 switch (param->param.p_type)
1823 {
1824 case STP_PARAMETER_TYPE_CURVE:
1825 description->deflt.curve = *(param->defval);
1826 description->bounds.curve =
1827 stp_curve_create_copy(*(param->defval));
1828 break;
1829 default:
1830 break;
1831 }
1832 return;
1833 }
1834 }
1835 }
1836
1837
1838 static const stp_colorfuncs_t stpi_color_traditional_colorfuncs =
1839 {
1840 &stpi_color_traditional_init,
1841 &stpi_color_traditional_get_row,
1842 &stpi_color_traditional_list_parameters,
1843 &stpi_color_traditional_describe_parameter
1844 };
1845
1846 static stp_color_t stpi_color_traditional_module_data =
1847 {
1848 "traditional",
1849 N_("Traditional Gutenprint color conversion"),
1850 &stpi_color_traditional_colorfuncs
1851 };
1852
1853
1854 static int
color_traditional_module_init(void)1855 color_traditional_module_init(void)
1856 {
1857 return stp_color_register(&stpi_color_traditional_module_data);
1858 }
1859
1860
1861 static int
color_traditional_module_exit(void)1862 color_traditional_module_exit(void)
1863 {
1864 return stp_color_unregister(&stpi_color_traditional_module_data);
1865 }
1866
1867
1868 /* Module header */
1869 #define stp_module_version color_traditional_LTX_stp_module_version
1870 #define stp_module_data color_traditional_LTX_stp_module_data
1871
1872 stp_module_version_t stp_module_version = {0, 0};
1873
1874 stp_module_t stp_module_data =
1875 {
1876 "traditional",
1877 VERSION,
1878 "Traditional Gutenprint color conversion",
1879 STP_MODULE_CLASS_COLOR,
1880 NULL,
1881 color_traditional_module_init,
1882 color_traditional_module_exit,
1883 (void *) &stpi_color_traditional_module_data
1884 };
1885