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