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