/*
*
* Gutenprint color management module - traditional Gutenprint algorithm.
*
* Copyright 1997-2000 Michael Sweet (mike@easysw.com) and
* Robert Krawitz (rlk@alum.mit.edu)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* This file must include only standard C header files. The core code must
* compile on generic platforms that don't support glib, gimp, gtk, etc.
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "gutenprint-internal.h"
#include
#include
#include
#ifdef HAVE_LIMITS_H
#include
#endif
#include
#include "color-conversion.h"
#ifdef __GNUC__
#define inline __inline__
#endif
static const color_correction_t color_corrections[] =
{
{ "None", N_("Default"), COLOR_CORRECTION_DEFAULT, 1 },
{ "Accurate", N_("High Accuracy"), COLOR_CORRECTION_ACCURATE, 1 },
{ "Bright", N_("Bright Colors"), COLOR_CORRECTION_BRIGHT, 1 },
{ "Hue", N_("Correct Hue Only"), COLOR_CORRECTION_HUE, 1 },
{ "Uncorrected", N_("Uncorrected"), COLOR_CORRECTION_UNCORRECTED, 0 },
{ "Desaturated", N_("Desaturated"), COLOR_CORRECTION_DESATURATED, 0 },
{ "Threshold", N_("Threshold"), COLOR_CORRECTION_THRESHOLD, 0 },
{ "Density", N_("Density"), COLOR_CORRECTION_DENSITY, 0 },
{ "Raw", N_("Raw"), COLOR_CORRECTION_RAW, 0 },
{ "Predithered", N_("Pre-Dithered"), COLOR_CORRECTION_PREDITHERED, 0 },
};
static const int color_correction_count =
sizeof(color_corrections) / sizeof(color_correction_t);
static const channel_param_t channel_params[] =
{
{ CMASK_K, "BlackGamma", "BlackCurve", "WhiteGamma", "WhiteCurve" },
{ CMASK_C, "CyanGamma", "CyanCurve", "RedGamma", "RedCurve" },
{ CMASK_M, "MagentaGamma", "MagentaCurve", "GreenGamma", "GreenCurve" },
{ CMASK_Y, "YellowGamma", "YellowCurve", "BlueGamma", "BlueCurve" },
{ CMASK_W, "WhiteGamma", "WhiteCurve", "BlackGamma", "BlackCurve" },
{ CMASK_R, "RedGamma", "RedCurve", "CyanGamma", "CyanCurve" },
{ CMASK_G, "GreenGamma", "GreenCurve", "MagentaGamma", "MagentaCurve" },
{ CMASK_B, "BlueGamma", "BlueCurve", "YellowGamma", "YellowCurve" },
};
static const int channel_param_count =
sizeof(channel_params) / sizeof(channel_param_t);
static const channel_param_t raw_channel_params[] =
{
{ 0, "GammaCh0", "CurveCh0", "GammaCh0", "CurveCh0" },
{ 1, "GammaCh1", "CurveCh1", "GammaCh1", "CurveCh1" },
{ 2, "GammaCh2", "CurveCh2", "GammaCh2", "CurveCh2" },
{ 3, "GammaCh3", "CurveCh3", "GammaCh3", "CurveCh3" },
{ 4, "GammaCh4", "CurveCh4", "GammaCh4", "CurveCh4" },
{ 5, "GammaCh5", "CurveCh5", "GammaCh5", "CurveCh5" },
{ 6, "GammaCh6", "CurveCh6", "GammaCh6", "CurveCh6" },
{ 7, "GammaCh7", "CurveCh7", "GammaCh7", "CurveCh7" },
{ 8, "GammaCh8", "CurveCh8", "GammaCh8", "CurveCh8" },
{ 9, "GammaCh9", "CurveCh9", "GammaCh9", "CurveCh9" },
{ 10, "GammaCh10", "CurveCh10", "GammaCh10", "CurveCh10" },
{ 11, "GammaCh11", "CurveCh11", "GammaCh11", "CurveCh11" },
{ 12, "GammaCh12", "CurveCh12", "GammaCh12", "CurveCh12" },
{ 13, "GammaCh13", "CurveCh13", "GammaCh13", "CurveCh13" },
{ 14, "GammaCh14", "CurveCh14", "GammaCh14", "CurveCh14" },
{ 15, "GammaCh15", "CurveCh15", "GammaCh15", "CurveCh15" },
{ 16, "GammaCh16", "CurveCh16", "GammaCh16", "CurveCh16" },
{ 17, "GammaCh17", "CurveCh17", "GammaCh17", "CurveCh17" },
{ 18, "GammaCh18", "CurveCh18", "GammaCh18", "CurveCh18" },
{ 19, "GammaCh19", "CurveCh19", "GammaCh19", "CurveCh19" },
{ 20, "GammaCh20", "CurveCh20", "GammaCh20", "CurveCh20" },
{ 21, "GammaCh21", "CurveCh21", "GammaCh21", "CurveCh21" },
{ 22, "GammaCh22", "CurveCh22", "GammaCh22", "CurveCh22" },
{ 23, "GammaCh23", "CurveCh23", "GammaCh23", "CurveCh23" },
{ 24, "GammaCh24", "CurveCh24", "GammaCh24", "CurveCh24" },
{ 25, "GammaCh25", "CurveCh25", "GammaCh25", "CurveCh25" },
{ 26, "GammaCh26", "CurveCh26", "GammaCh26", "CurveCh26" },
{ 27, "GammaCh27", "CurveCh27", "GammaCh27", "CurveCh27" },
{ 28, "GammaCh28", "CurveCh28", "GammaCh28", "CurveCh28" },
{ 29, "GammaCh29", "CurveCh29", "GammaCh29", "CurveCh29" },
{ 30, "GammaCh30", "CurveCh30", "GammaCh30", "CurveCh30" },
{ 31, "GammaCh31", "CurveCh31", "GammaCh31", "CurveCh31" },
{ 32, "GammaCh32", "CurveCh32", "GammaCh32", "CurveCh32" },
{ 33, "GammaCh33", "CurveCh33", "GammaCh33", "CurveCh33" },
{ 34, "GammaCh34", "CurveCh34", "GammaCh34", "CurveCh34" },
{ 35, "GammaCh35", "CurveCh35", "GammaCh35", "CurveCh35" },
{ 36, "GammaCh36", "CurveCh36", "GammaCh36", "CurveCh36" },
{ 37, "GammaCh37", "CurveCh37", "GammaCh37", "CurveCh37" },
{ 38, "GammaCh38", "CurveCh38", "GammaCh38", "CurveCh38" },
{ 39, "GammaCh39", "CurveCh39", "GammaCh39", "CurveCh39" },
{ 40, "GammaCh40", "CurveCh40", "GammaCh40", "CurveCh40" },
{ 41, "GammaCh41", "CurveCh41", "GammaCh41", "CurveCh41" },
{ 42, "GammaCh42", "CurveCh42", "GammaCh42", "CurveCh42" },
{ 43, "GammaCh43", "CurveCh43", "GammaCh43", "CurveCh43" },
{ 44, "GammaCh44", "CurveCh44", "GammaCh44", "CurveCh44" },
{ 45, "GammaCh45", "CurveCh45", "GammaCh45", "CurveCh45" },
{ 46, "GammaCh46", "CurveCh46", "GammaCh46", "CurveCh46" },
{ 47, "GammaCh47", "CurveCh47", "GammaCh47", "CurveCh47" },
{ 48, "GammaCh48", "CurveCh48", "GammaCh48", "CurveCh48" },
{ 49, "GammaCh49", "CurveCh49", "GammaCh49", "CurveCh49" },
{ 50, "GammaCh50", "CurveCh50", "GammaCh50", "CurveCh50" },
{ 51, "GammaCh51", "CurveCh51", "GammaCh51", "CurveCh51" },
{ 52, "GammaCh52", "CurveCh52", "GammaCh52", "CurveCh52" },
{ 53, "GammaCh53", "CurveCh53", "GammaCh53", "CurveCh53" },
{ 54, "GammaCh54", "CurveCh54", "GammaCh54", "CurveCh54" },
{ 55, "GammaCh55", "CurveCh55", "GammaCh55", "CurveCh55" },
{ 56, "GammaCh56", "CurveCh56", "GammaCh56", "CurveCh56" },
{ 57, "GammaCh57", "CurveCh57", "GammaCh57", "CurveCh57" },
{ 58, "GammaCh58", "CurveCh58", "GammaCh58", "CurveCh58" },
{ 59, "GammaCh59", "CurveCh59", "GammaCh59", "CurveCh59" },
{ 60, "GammaCh60", "CurveCh60", "GammaCh60", "CurveCh60" },
{ 61, "GammaCh61", "CurveCh61", "GammaCh61", "CurveCh61" },
{ 62, "GammaCh62", "CurveCh62", "GammaCh62", "CurveCh62" },
{ 63, "GammaCh63", "CurveCh63", "GammaCh63", "CurveCh63" },
};
static const int raw_channel_param_count =
sizeof(raw_channel_params) / sizeof(channel_param_t);
static const color_description_t color_descriptions[] =
{
{ N_("Grayscale"), 1, 1, COLOR_ID_GRAY, COLOR_BLACK, CMASK_K, 1,
COLOR_CORRECTION_UNCORRECTED, &stpi_color_convert_to_gray },
{ N_("Whitescale"), 1, 1, COLOR_ID_WHITE, COLOR_WHITE, CMASK_K, 1,
COLOR_CORRECTION_UNCORRECTED, &stpi_color_convert_to_gray },
{ N_("RGB"), 1, 1, COLOR_ID_RGB, COLOR_WHITE, CMASK_CMY, 3,
COLOR_CORRECTION_ACCURATE, &stpi_color_convert_to_color },
{ N_("CMY"), 1, 1, COLOR_ID_CMY, COLOR_BLACK, CMASK_CMY, 3,
COLOR_CORRECTION_ACCURATE, &stpi_color_convert_to_color },
{ N_("CMYK"), 1, 0, COLOR_ID_CMYK, COLOR_BLACK, CMASK_CMYK, 4,
COLOR_CORRECTION_ACCURATE, &stpi_color_convert_to_kcmy },
{ N_("KCMY"), 1, 1, COLOR_ID_KCMY, COLOR_BLACK, CMASK_CMYK, 4,
COLOR_CORRECTION_ACCURATE, &stpi_color_convert_to_kcmy },
{ N_("Raw"), 1, 1, COLOR_ID_RAW, COLOR_UNKNOWN, 0, -1,
COLOR_CORRECTION_RAW, &stpi_color_convert_raw },
};
static const int color_description_count =
sizeof(color_descriptions) / sizeof(color_description_t);
static const channel_depth_t channel_depths[] =
{
{ "8", 8 },
{ "16", 16 }
};
static const int channel_depth_count =
sizeof(channel_depths) / sizeof(channel_depth_t);
typedef struct
{
const stp_parameter_t param;
double min;
double max;
double defval;
unsigned channel_mask;
int color_only;
int is_rgb;
} float_param_t;
#define RAW_GAMMA_CHANNEL(channel) \
{ \
{ \
"GammaCh" #channel, N_("Channel " #channel " Gamma"), "Color=Yes,Category=Gamma", \
N_("Gamma for raw channel " #channel), \
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT, \
STP_PARAMETER_LEVEL_INTERNAL, 0, 1, channel, 1, 0 \
}, 0.1, 4.0, 1.0, CMASK_RAW, 0, -1 \
}
static const float_param_t float_parameters[] =
{
{
{
"ColorCorrection", N_("Color Correction"), "Color=Yes,Category=Basic Image Adjustment",
N_("Color correction to be applied"),
STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
}, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
},
{
{
"ChannelBitDepth", N_("Channel Bit Depth"), "Color=Yes,Category=Core Parameter",
N_("Bit depth per channel"),
STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
}, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
},
{
{
"InputImageType", N_("Input Image Type"), "Color=Yes,Category=Core Parameter",
N_("Input image type"),
STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
}, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
},
{
{
"STPIOutputType", N_("Output Image Type"), "Color=Yes,Category=Core Parameter",
N_("Output image type"),
STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
STP_PARAMETER_LEVEL_INTERNAL, 1, 1, -1, 1, 0
}, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
},
{
{
"STPIRawChannels", N_("Raw Channels"), "Color=Yes,Category=Core Parameter",
N_("Raw Channels"),
STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_CORE,
STP_PARAMETER_LEVEL_INTERNAL, 1, 1, -1, 1, 0
}, 1.0, STP_CHANNEL_LIMIT, 1.0, CMASK_EVERY, 0, -1
},
{
{
"SimpleGamma", N_("SimpleGamma"), "Color=Yes,Category=Gamma",
N_("Do not correct for screen gamma"),
STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_INTERNAL, 0, 1, -1, 1, 0
}, 0.0, 1.0, 0.0, CMASK_EVERY, 0, -1
},
{
{
"Brightness", N_("Brightness"), "Color=Yes,Category=Basic Image Adjustment",
N_("Brightness of the print"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
}, 0.0, 2.0, 1.0, CMASK_ALL, 0, -1
},
{
{
"Contrast", N_("Contrast"), "Color=Yes,Category=Basic Image Adjustment",
N_("Contrast of the print (0 is solid gray)"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
}, 0.0, 4.0, 1.0, CMASK_ALL, 0, -1
},
{
{
"LinearContrast", N_("Linear Contrast Adjustment"), "Color=Yes,Category=Advanced Image Control",
N_("Use linear vs. fixed end point contrast adjustment"),
STP_PARAMETER_TYPE_BOOLEAN, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED3, 1, 1, -1, 1, 0
}, 0.0, 0.0, 0.0, CMASK_ALL, 0, -1
},
{
{
"Gamma", N_("Composite Gamma"), "Color=Yes,Category=Gamma",
N_("Adjust the gamma of the print. Larger values will "
"produce a generally brighter print, while smaller "
"values will produce a generally darker print. "),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, -1, 1, 0
}, 0.1, 4.0, 1.0, CMASK_EVERY, 0, -1
},
{
{
"AppGamma", N_("AppGamma"), "Color=Yes,Category=Gamma",
N_("Gamma value assumed by application"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_INTERNAL, 0, 1, -1, 1, 0
}, 0.1, 4.0, 1.0, CMASK_EVERY, 0, -1
},
{
{
"CyanGamma", N_("Cyan"), "Color=Yes,Category=Gamma",
N_("Adjust the cyan gamma"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 1, 1, 0
}, 0.0, 4.0, 1.0, CMASK_C, 1, 0
},
{
{
"MagentaGamma", N_("Magenta"), "Color=Yes,Category=Gamma",
N_("Adjust the magenta gamma"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 2, 1, 0
}, 0.0, 4.0, 1.0, CMASK_M, 1, 0
},
{
{
"YellowGamma", N_("Yellow"), "Color=Yes,Category=Gamma",
N_("Adjust the yellow gamma"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 3, 1, 0
}, 0.0, 4.0, 1.0, CMASK_Y, 1, 0
},
{
{
"RedGamma", N_("Red"), "Color=Yes,Category=Gamma",
N_("Adjust the red gamma"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 1, 1, 0
}, 0.0, 4.0, 1.0, CMASK_C, 1, 1
},
{
{
"GreenGamma", N_("Green"), "Color=Yes,Category=Gamma",
N_("Adjust the green gamma"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 2, 1, 0
}, 0.0, 4.0, 1.0, CMASK_M, 1, 1
},
{
{
"BlueGamma", N_("Blue"), "Color=Yes,Category=Gamma",
N_("Adjust the blue gamma"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 3, 1, 0
}, 0.0, 4.0, 1.0, CMASK_Y, 1, 1
},
{
{
"BlackGamma", N_("Black"), "Color=Yes,Category=Gamma",
N_("Adjust the black gamma"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 3, 1, 0
}, 0.0, 4.0, 1.0, CMASK_K, 1, 0
},
{
{
"CyanBalance", N_("Cyan Balance"), "Color=Yes,Category=GrayBalance",
N_("Adjust the cyan gray balance"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 1, 1, 0
}, 0.0, 1.0, 1.0, CMASK_C, 1, 0
},
{
{
"MagentaBalance", N_("Magenta Balance"), "Color=Yes,Category=GrayBalance",
N_("Adjust the magenta gray balance"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 2, 1, 0
}, 0.0, 1.0, 1.0, CMASK_M, 1, 0
},
{
{
"YellowBalance", N_("Yellow Balance"), "Color=Yes,Category=GrayBalance",
N_("Adjust the yellow gray balance"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED1, 0, 1, 3, 1, 0
}, 0.0, 1.0, 1.0, CMASK_Y, 1, 0
},
{
{
"Saturation", N_("Saturation"), "Color=Yes,Category=Basic Image Adjustment",
N_("Adjust the saturation (color balance) of the print\n"
"Use zero saturation to produce grayscale output "
"using color and black inks"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_BASIC, 1, 1, -1, 1, 0
}, 0.0, 9.0, 1.0, CMASK_CMY | CMASK_RGB, 1, 0
},
/* Need to think this through a bit more -- rlk 20030712 */
{
{
"InkLimit", N_("Ink Limit"), "Color=Yes,Category=Advanced Output Control",
N_("Limit the total ink printed to the page"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, -1, 0, 0
}, 0.0, STP_CHANNEL_LIMIT, STP_CHANNEL_LIMIT, CMASK_CMY, 0, -1
},
{
{
"BlackTrans", N_("GCR Transition"), "Color=Yes,Category=Advanced Output Control",
N_("Adjust the gray component transition rate"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
}, 0.0, 1.0, 1.0, CMASK_K, 1, 0
},
{
{
"GCRLower", N_("GCR Lower Bound"), "Color=Yes,Category=Advanced Output Control",
N_("Lower bound of gray component reduction"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
}, 0.0, 1.0, 0.2, CMASK_K, 1, 0
},
{
{
"GCRUpper", N_("GCR Upper Bound"), "Color=Yes,Category=Advanced Output Control",
N_("Upper bound of gray component reduction"),
STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
}, 0.0, 5.0, 0.5, CMASK_K, 1, 0
},
RAW_GAMMA_CHANNEL(0),
RAW_GAMMA_CHANNEL(1),
RAW_GAMMA_CHANNEL(2),
RAW_GAMMA_CHANNEL(3),
RAW_GAMMA_CHANNEL(4),
RAW_GAMMA_CHANNEL(5),
RAW_GAMMA_CHANNEL(6),
RAW_GAMMA_CHANNEL(7),
RAW_GAMMA_CHANNEL(8),
RAW_GAMMA_CHANNEL(9),
RAW_GAMMA_CHANNEL(10),
RAW_GAMMA_CHANNEL(11),
RAW_GAMMA_CHANNEL(12),
RAW_GAMMA_CHANNEL(13),
RAW_GAMMA_CHANNEL(14),
RAW_GAMMA_CHANNEL(15),
RAW_GAMMA_CHANNEL(16),
RAW_GAMMA_CHANNEL(17),
RAW_GAMMA_CHANNEL(18),
RAW_GAMMA_CHANNEL(19),
RAW_GAMMA_CHANNEL(20),
RAW_GAMMA_CHANNEL(21),
RAW_GAMMA_CHANNEL(22),
RAW_GAMMA_CHANNEL(23),
RAW_GAMMA_CHANNEL(24),
RAW_GAMMA_CHANNEL(25),
RAW_GAMMA_CHANNEL(26),
RAW_GAMMA_CHANNEL(27),
RAW_GAMMA_CHANNEL(28),
RAW_GAMMA_CHANNEL(29),
RAW_GAMMA_CHANNEL(30),
RAW_GAMMA_CHANNEL(31),
RAW_GAMMA_CHANNEL(32),
RAW_GAMMA_CHANNEL(33),
RAW_GAMMA_CHANNEL(34),
RAW_GAMMA_CHANNEL(35),
RAW_GAMMA_CHANNEL(36),
RAW_GAMMA_CHANNEL(37),
RAW_GAMMA_CHANNEL(38),
RAW_GAMMA_CHANNEL(39),
RAW_GAMMA_CHANNEL(40),
RAW_GAMMA_CHANNEL(41),
RAW_GAMMA_CHANNEL(42),
RAW_GAMMA_CHANNEL(43),
RAW_GAMMA_CHANNEL(44),
RAW_GAMMA_CHANNEL(45),
RAW_GAMMA_CHANNEL(46),
RAW_GAMMA_CHANNEL(47),
RAW_GAMMA_CHANNEL(48),
RAW_GAMMA_CHANNEL(49),
RAW_GAMMA_CHANNEL(50),
RAW_GAMMA_CHANNEL(51),
RAW_GAMMA_CHANNEL(52),
RAW_GAMMA_CHANNEL(53),
RAW_GAMMA_CHANNEL(54),
RAW_GAMMA_CHANNEL(55),
RAW_GAMMA_CHANNEL(56),
RAW_GAMMA_CHANNEL(57),
RAW_GAMMA_CHANNEL(58),
RAW_GAMMA_CHANNEL(59),
RAW_GAMMA_CHANNEL(60),
RAW_GAMMA_CHANNEL(61),
RAW_GAMMA_CHANNEL(62),
RAW_GAMMA_CHANNEL(63),
{
{
"LUTDumpFile", N_("LUT dump file"), N_("Advanced Output Control"),
N_("Dump file for LUT for external color adjustment"),
STP_PARAMETER_TYPE_FILE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, -1, 1, 0
}, 0.0, 0.0, 0.0, CMASK_EVERY, 0, -1
},
};
static const int float_parameter_count =
sizeof(float_parameters) / sizeof(float_param_t);
typedef struct
{
stp_parameter_t param;
stp_curve_t **defval;
unsigned channel_mask;
int hsl_only;
int color_only;
int is_rgb;
} curve_param_t;
static int standard_curves_initialized = 0;
static stp_curve_t *hue_map_bounds = NULL;
static stp_curve_t *lum_map_bounds = NULL;
static stp_curve_t *sat_map_bounds = NULL;
static stp_curve_t *color_curve_bounds = NULL;
static stp_curve_t *gcr_curve_bounds = NULL;
#define RAW_CURVE_CHANNEL(channel) \
{ \
{ \
"CurveCh" #channel, N_("Channel " #channel " Curve"), \
"Color=Yes,Category=Output Curves", \
N_("Curve for raw channel " #channel), \
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT, \
STP_PARAMETER_LEVEL_INTERNAL, 0, 1, channel, 1, 0 \
}, &color_curve_bounds, CMASK_RAW, 0, 0, -1 \
}
static curve_param_t curve_parameters[] =
{
{
{
"CyanCurve", N_("Cyan Curve"), "Color=Yes,Category=Output Curves",
N_("Cyan curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 1, 1, 0
}, &color_curve_bounds, CMASK_C, 0, 1, 0
},
{
{
"MagentaCurve", N_("Magenta Curve"), "Color=Yes,Category=Output Curves",
N_("Magenta curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 2, 1, 0
}, &color_curve_bounds, CMASK_M, 0, 1, 0
},
{
{
"YellowCurve", N_("Yellow Curve"), "Color=Yes,Category=Output Curves",
N_("Yellow curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 3, 1, 0
}, &color_curve_bounds, CMASK_Y, 0, 1, 0
},
{
{
"BlackCurve", N_("Black Curve"), "Color=Yes,Category=Output Curves",
N_("Black curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 0, 1, 0
}, &color_curve_bounds, CMASK_K, 0, 0, 0
},
{
{
"RedCurve", N_("Red Curve"), "Color=Yes,Category=Output Curves",
N_("Red curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 1, 1, 0
}, &color_curve_bounds, CMASK_C, 0, 1, 1
},
{
{
"GreenCurve", N_("Green Curve"), "Color=Yes,Category=Output Curves",
N_("Green curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 2, 1, 0
}, &color_curve_bounds, CMASK_M, 0, 1, 1
},
{
{
"BlueCurve", N_("Blue Curve"), "Color=Yes,Category=Output Curves",
N_("Blue curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 3, 1, 0
}, &color_curve_bounds, CMASK_Y, 0, 1, 1
},
{
{
"WhiteCurve", N_("White Curve"), "Color=Yes,Category=Output Curves",
N_("White curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED2, 0, 1, 1, 1, 0
}, &color_curve_bounds, CMASK_W, 0, 0, 1
},
{
{
"HueMap", N_("Hue Map"), "Color=Yes,Category=Advanced HSL Curves",
N_("Hue adjustment curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, -1, 1, 0
}, &hue_map_bounds, CMASK_CMY | CMASK_RGB, 1, 1, -1
},
{
{
"SatMap", N_("Saturation Map"), "Color=Yes,Category=Advanced HSL Curves",
N_("Saturation adjustment curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, -1, 1, 0
}, &sat_map_bounds, CMASK_CMY | CMASK_RGB, 1, 1, -1
},
{
{
"LumMap", N_("Luminosity Map"), "Color=Yes,Category=Advanced HSL Curves",
N_("Luminosity adjustment curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED3, 0, 1, -1, 1, 0
}, &lum_map_bounds, CMASK_CMY | CMASK_RGB, 1, 1, -1
},
{
{
"GCRCurve", N_("Gray Component Reduction"), "Color=Yes,Category=Advanced Output Control",
N_("Gray component reduction curve"),
STP_PARAMETER_TYPE_CURVE, STP_PARAMETER_CLASS_OUTPUT,
STP_PARAMETER_LEVEL_ADVANCED4, 0, 1, 0, 1, 0
}, &gcr_curve_bounds, CMASK_K, 0, 1, -1
},
RAW_CURVE_CHANNEL(0),
RAW_CURVE_CHANNEL(1),
RAW_CURVE_CHANNEL(2),
RAW_CURVE_CHANNEL(3),
RAW_CURVE_CHANNEL(4),
RAW_CURVE_CHANNEL(5),
RAW_CURVE_CHANNEL(6),
RAW_CURVE_CHANNEL(7),
RAW_CURVE_CHANNEL(8),
RAW_CURVE_CHANNEL(9),
RAW_CURVE_CHANNEL(10),
RAW_CURVE_CHANNEL(11),
RAW_CURVE_CHANNEL(12),
RAW_CURVE_CHANNEL(13),
RAW_CURVE_CHANNEL(14),
RAW_CURVE_CHANNEL(15),
RAW_CURVE_CHANNEL(16),
RAW_CURVE_CHANNEL(17),
RAW_CURVE_CHANNEL(18),
RAW_CURVE_CHANNEL(19),
RAW_CURVE_CHANNEL(20),
RAW_CURVE_CHANNEL(21),
RAW_CURVE_CHANNEL(22),
RAW_CURVE_CHANNEL(23),
RAW_CURVE_CHANNEL(24),
RAW_CURVE_CHANNEL(25),
RAW_CURVE_CHANNEL(26),
RAW_CURVE_CHANNEL(27),
RAW_CURVE_CHANNEL(28),
RAW_CURVE_CHANNEL(29),
RAW_CURVE_CHANNEL(30),
RAW_CURVE_CHANNEL(31),
RAW_CURVE_CHANNEL(32),
RAW_CURVE_CHANNEL(33),
RAW_CURVE_CHANNEL(34),
RAW_CURVE_CHANNEL(35),
RAW_CURVE_CHANNEL(36),
RAW_CURVE_CHANNEL(37),
RAW_CURVE_CHANNEL(38),
RAW_CURVE_CHANNEL(39),
RAW_CURVE_CHANNEL(40),
RAW_CURVE_CHANNEL(41),
RAW_CURVE_CHANNEL(42),
RAW_CURVE_CHANNEL(43),
RAW_CURVE_CHANNEL(44),
RAW_CURVE_CHANNEL(45),
RAW_CURVE_CHANNEL(46),
RAW_CURVE_CHANNEL(47),
RAW_CURVE_CHANNEL(48),
RAW_CURVE_CHANNEL(49),
RAW_CURVE_CHANNEL(50),
RAW_CURVE_CHANNEL(51),
RAW_CURVE_CHANNEL(52),
RAW_CURVE_CHANNEL(53),
RAW_CURVE_CHANNEL(54),
RAW_CURVE_CHANNEL(55),
RAW_CURVE_CHANNEL(56),
RAW_CURVE_CHANNEL(57),
RAW_CURVE_CHANNEL(58),
RAW_CURVE_CHANNEL(59),
RAW_CURVE_CHANNEL(60),
RAW_CURVE_CHANNEL(61),
RAW_CURVE_CHANNEL(62),
RAW_CURVE_CHANNEL(63),
};
static const int curve_parameter_count =
sizeof(curve_parameters) / sizeof(curve_param_t);
static const color_description_t *
get_color_description(const char *name)
{
int i;
if (name)
for (i = 0; i < color_description_count; i++)
{
if (strcmp(name, color_descriptions[i].name) == 0)
return &(color_descriptions[i]);
}
return NULL;
}
static const channel_depth_t *
get_channel_depth(const char *name)
{
int i;
if (name)
for (i = 0; i < channel_depth_count; i++)
{
if (strcmp(name, channel_depths[i].name) == 0)
return &(channel_depths[i]);
}
return NULL;
}
static const color_correction_t *
get_color_correction(const char *name)
{
int i;
if (name)
for (i = 0; i < color_correction_count; i++)
{
if (strcmp(name, color_corrections[i].name) == 0)
return &(color_corrections[i]);
}
return NULL;
}
static const color_correction_t *
get_color_correction_by_tag(color_correction_enum_t correction)
{
int i;
for (i = 0; i < color_correction_count; i++)
{
if (correction == color_corrections[i].correction)
return &(color_corrections[i]);
}
return NULL;
}
static void
initialize_channels(stp_vars_t *v, stp_image_t *image)
{
lut_t *lut = (lut_t *)(stp_get_component_data(v, "Color"));
if (stp_check_float_parameter(v, "InkLimit", STP_PARAMETER_ACTIVE))
stp_channel_set_ink_limit(v, stp_get_float_parameter(v, "InkLimit"));
stp_channel_initialize(v, image, lut->out_channels);
lut->channels_are_initialized = 1;
}
static int
stpi_color_traditional_get_row(stp_vars_t *v,
stp_image_t *image,
int row,
unsigned *zero_mask)
{
const lut_t *lut = (const lut_t *)(stp_get_component_data(v, "Color"));
unsigned zero;
if (stp_image_get_row(image, lut->in_data,
lut->image_width * lut->in_channels * lut->channel_depth / 8, row)
!= STP_IMAGE_STATUS_OK)
return 2;
if (!lut->channels_are_initialized)
initialize_channels(v, image);
zero = (lut->output_color_description->conversion_function)
(v, lut->in_data, stp_channel_get_input(v));
if (zero_mask)
*zero_mask = zero;
stp_channel_convert(v, zero_mask);
return 0;
}
static void
free_channels(lut_t *lut)
{
int i;
for (i = 0; i < STP_CHANNEL_LIMIT; i++)
stp_curve_free_curve_cache(&(lut->channel_curves[i]));
}
static lut_t *
allocate_lut(void)
{
int i;
lut_t *ret = stp_zalloc(sizeof(lut_t));
for (i = 0; i < STP_CHANNEL_LIMIT; i++)
{
ret->gamma_values[i] = 1.0;
}
ret->print_gamma = 1.0;
ret->app_gamma = 1.0;
ret->contrast = 1.0;
ret->brightness = 1.0;
ret->simple_gamma_correction = 0;
return ret;
}
static void *
copy_lut(void *vlut)
{
const lut_t *src = (const lut_t *)vlut;
int i;
lut_t *dest;
if (!src)
return NULL;
dest = allocate_lut();
free_channels(dest);
dest->steps = src->steps;
dest->channel_depth = src->channel_depth;
dest->image_width = src->image_width;
dest->in_channels = src->in_channels;
dest->out_channels = src->out_channels;
/* Don't copy channels_are_initialized */
dest->invert_output = src->invert_output;
dest->input_color_description = src->input_color_description;
dest->output_color_description = src->output_color_description;
dest->color_correction = src->color_correction;
for (i = 0; i < STP_CHANNEL_LIMIT; i++)
{
stp_curve_cache_copy(&(dest->channel_curves[i]), &(src->channel_curves[i]));
dest->gamma_values[i] = src->gamma_values[i];
}
stp_curve_cache_copy(&(dest->brightness_correction),
&(src->brightness_correction));
stp_curve_cache_copy(&(dest->contrast_correction),
&(src->contrast_correction));
stp_curve_cache_copy(&(dest->user_color_correction),
&(src->user_color_correction));
dest->print_gamma = src->print_gamma;
dest->app_gamma = src->app_gamma;
dest->screen_gamma = src->screen_gamma;
dest->contrast = src->contrast;
dest->brightness = src->brightness;
dest->simple_gamma_correction = src->simple_gamma_correction;
dest->linear_contrast_adjustment = src->linear_contrast_adjustment;
stp_curve_cache_copy(&(dest->hue_map), &(src->hue_map));
stp_curve_cache_copy(&(dest->lum_map), &(src->lum_map));
stp_curve_cache_copy(&(dest->sat_map), &(src->sat_map));
/* Don't copy gray_tmp */
/* Don't copy cmy_tmp */
if (src->in_data)
{
dest->in_data = stp_malloc(src->image_width * src->in_channels);
memset(dest->in_data, 0, src->image_width * src->in_channels);
}
return dest;
}
static void
free_lut(void *vlut)
{
lut_t *lut = (lut_t *)vlut;
free_channels(lut);
stp_curve_free_curve_cache(&(lut->brightness_correction));
stp_curve_free_curve_cache(&(lut->contrast_correction));
stp_curve_free_curve_cache(&(lut->user_color_correction));
stp_curve_free_curve_cache(&(lut->hue_map));
stp_curve_free_curve_cache(&(lut->lum_map));
stp_curve_free_curve_cache(&(lut->sat_map));
STP_SAFE_FREE(lut->gray_tmp);
STP_SAFE_FREE(lut->cmy_tmp);
STP_SAFE_FREE(lut->in_data);
memset(lut, 0, sizeof(lut_t));
stp_free(lut);
}
static stp_curve_t *
compute_gcr_curve(const stp_vars_t *vars)
{
stp_curve_t *curve;
lut_t *lut = (lut_t *)(stp_get_component_data(vars, "Color"));
double k_lower = 0.0;
double k_upper = 1.0;
double k_trans = 1.0;
double i_k_trans = 1.0;
double *tmp_data = stp_malloc(sizeof(double) * lut->steps);
int i;
if (stp_check_float_parameter(vars, "GCRUpper", STP_PARAMETER_DEFAULTED))
k_upper = stp_get_float_parameter(vars, "GCRUpper");
if (stp_check_float_parameter(vars, "GCRLower", STP_PARAMETER_DEFAULTED))
k_lower = stp_get_float_parameter(vars, "GCRLower");
if (stp_check_float_parameter(vars, "BlackTrans", STP_PARAMETER_DEFAULTED))
k_trans = stp_get_float_parameter(vars, "BlackTrans");
if (k_lower >= 1)
return NULL;
k_upper *= lut->steps;
k_lower *= lut->steps;
stp_dprintf(STP_DBG_LUT, vars, " k_lower %.3f\n", k_lower);
stp_dprintf(STP_DBG_LUT, vars, " k_upper %.3f\n", k_upper);
if (k_lower > lut->steps)
k_lower = lut->steps;
if (k_upper < k_lower)
k_upper = k_lower + 1;
i_k_trans = 1.0 / k_trans;
for (i = 0; i < k_lower; i ++)
tmp_data[i] = 0;
if (k_upper < lut->steps)
{
for (i = ceil(k_lower); i < k_upper; i ++)
{
double where = (i - k_lower) / (k_upper - k_lower);
double g1 = pow(where, i_k_trans);
double g2 = 1.0 - pow(1.0 - where, k_trans);
double value = (i_k_trans <= 1.0 ? g1 : g2);
tmp_data[i] = 65535.0 * k_upper * value / (double) (lut->steps - 1);
tmp_data[i] = floor(tmp_data[i] + .5);
}
for (i = ceil(k_upper); i < lut->steps; i ++)
tmp_data[i] = 65535.0 * i / (double) (lut->steps - 1);
}
else if (k_lower < lut->steps)
for (i = ceil(k_lower); i < lut->steps; i ++)
{
double where = (i - k_lower) / (k_upper - k_lower);
double g1 = pow(where, i_k_trans);
double g2 = 1.0 - pow(1.0 - where, k_trans);
double value = (i_k_trans <= 1.0 ? g1 : g2);
tmp_data[i] = 65535.0 * lut->steps * value / (double) (lut->steps - 1);
tmp_data[i] = floor(tmp_data[i] + .5);
}
curve = stp_curve_create(STP_CURVE_WRAP_NONE);
stp_curve_set_bounds(curve, 0, 65535);
STPI_ASSERT(stp_curve_set_data(curve, lut->steps, tmp_data), vars);
stp_free(tmp_data);
return curve;
}
static void
initialize_gcr_curve(stp_vars_t *vars)
{
lut_t *lut = (lut_t *)(stp_get_component_data(vars, "Color"));
stp_curve_t *curve = NULL;
if (stp_check_curve_parameter(vars, "GCRCurve", STP_PARAMETER_DEFAULTED))
{
double data;
size_t count;
int i;
curve = stp_curve_create_copy(stp_get_curve_parameter(vars, "GCRCurve"));
stp_curve_resample(curve, lut->steps);
count = stp_curve_count_points(curve);
stp_curve_set_bounds(curve, 0.0, 65535.0);
for (i = 0; i < count; i++)
{
stp_curve_get_point(curve, i, &data);
data = 65535.0 * data * (double) i / (count - 1);
stp_curve_set_point(curve, i, data);
}
}
else
curve = compute_gcr_curve(vars);
stp_channel_set_gcr_curve(vars, curve);
if (curve)
stp_curve_destroy(curve);
}
/*
* Channels that are synthesized (e. g. the black channel in CMY -> CMYK
* conversion) need to be computed differently from channels that are
* mapped 1-1 from input to output. Channels that are simply mapped need
* to be inverted if necessary, and in addition the contrast, brightness,
* and other gamma factors are factored in. Channels that are synthesized
* are never inverted, since they're computed from the output of other
* channels that have already been inverted.
*
* This isn't simply a matter of comparing the input channels to the output
* channels, since some of the conversions (K -> CMYK) work by means
* of a chain of conversions (K->CMY->CMYK). In this case, we need to
* fully compute the CMY channels, but the K channel in the output is
* not inverted.
*
* The rules that are implemented by the logic below are:
*
* 1) If the input is raw, we always perform the normal computation (without
* synthesizing channels).
*
* 2) If the output is CMY or K only, we never synthesize channels. We've
* now covered raw, black, and CMY/RGB outputs, leaving CMYK and MULTI.
*
* 3) Output channels above CMYK are synthesized.
*
* 4) If the input is CMYK, we do not synthesize channels.
*
* 5) The black channel (in CMYK) is synthesized.
*
* 6) All other channels (CMY/RGB) are not synthesized.
*/
static int
channel_is_synthesized(lut_t *lut, int channel)
{
if (lut->output_color_description->color_id == COLOR_ID_RAW)
return 1; /* Case 1 */
else if (lut->output_color_description->channels == CMASK_CMY ||
lut->output_color_description->channels == CMASK_K)
return 0; /* Case 2 */
else if (channel >= CHANNEL_W)
return 1; /* Case 3 */
else if (lut->input_color_description->channels == CMASK_CMYK)
return 0; /* Case 4 */
else if (channel == CHANNEL_K)
return 1; /* Case 5 */
else
return 0; /* Case 6 */
}
static void
compute_user_correction(lut_t *lut)
{
double *tmp;
double *tmp_brightness;
double *tmp_contrast;
double xcontrast = lut->contrast;
stp_curve_t *curve =
stp_curve_cache_get_curve(&(lut->user_color_correction));
stp_curve_t *brightness_curve =
stp_curve_cache_get_curve(&(lut->brightness_correction));
stp_curve_t *contrast_curve =
stp_curve_cache_get_curve(&(lut->contrast_correction));
double brightness = lut->brightness;
int i;
int isteps = lut->steps;
if (isteps > 256)
isteps = 256;
tmp = stp_malloc(sizeof(double) * lut->steps);
tmp_brightness = stp_malloc(sizeof(double) * lut->steps);
tmp_contrast = stp_malloc(sizeof(double) * lut->steps);
if (brightness < .001)
brightness = 1000;
else if (brightness > 1.999)
brightness = .001;
else if (brightness > 1)
brightness = 2.0 - brightness;
else
brightness = 1.0 / brightness;
for (i = 0; i < isteps; i ++)
{
double temp_pixel, pixel;
pixel = (double) i / (double) (isteps - 1);
/*
* First, correct contrast
*/
if (pixel >= .5)
temp_pixel = 1.0 - pixel;
else
temp_pixel = pixel;
if (lut->contrast > 3.99999)
{
if (temp_pixel < .5)
temp_pixel = 0;
else
temp_pixel = 1;
}
if (temp_pixel <= .000001 && lut->contrast <= .0001)
temp_pixel = .5;
else if (temp_pixel > 1)
temp_pixel = .5 * pow(2 * temp_pixel, xcontrast);
else if (temp_pixel < 1)
{
if (lut->linear_contrast_adjustment)
temp_pixel = 0.5 -
((0.5 - .5 * pow(2 * temp_pixel, lut->contrast)) *
lut->contrast);
else
temp_pixel = 0.5 -
((0.5 - .5 * pow(2 * temp_pixel, lut->contrast)));
}
if (temp_pixel > .5)
temp_pixel = .5;
else if (temp_pixel < 0)
temp_pixel = 0;
if (pixel < .5)
pixel = temp_pixel;
else
pixel = 1 - temp_pixel;
tmp_contrast[i] = floor((pixel * 65535) + .5);
/*
* Second, do brightness
*/
if (brightness < 1)
pixel = pow(pixel, brightness);
else
pixel = 1.0 - pow(1.0 - pixel, 1.0 / brightness);
tmp[i] = floor((pixel * 65535) + .5);
pixel = (double) i / (double) (isteps - 1);
if (brightness < 1)
pixel = pow(pixel, brightness);
else
pixel = 1.0 - pow(1.0 - pixel, 1.0 / brightness);
tmp_brightness[i] = floor((pixel * 65535) + .5);
}
stp_curve_set_data(curve, isteps, tmp);
if (isteps != lut->steps)
stp_curve_resample(curve, lut->steps);
stp_curve_set_data(brightness_curve, isteps, tmp_brightness);
if (isteps != lut->steps)
stp_curve_resample(brightness_curve, lut->steps);
stp_curve_set_data(contrast_curve, isteps, tmp_contrast);
if (isteps != lut->steps)
stp_curve_resample(contrast_curve, lut->steps);
stp_free(tmp);
stp_free(tmp_brightness);
stp_free(tmp_contrast);
}
static void
compute_a_curve_full(lut_t *lut, int channel)
{
double *tmp;
double pivot = .25;
double ipivot = 1.0 - pivot;
double xgamma = pow(pivot, lut->screen_gamma);
double print_gamma=1.0+9.0*(lut->print_gamma-1.0);
double pivot2 = .75;
double ipivot2 = 1.0 - pivot2;
double xgamma2 = pow(pivot2, print_gamma);
stp_curve_t *curve = stp_curve_cache_get_curve(&(lut->channel_curves[channel]));
int i;
int isteps = lut->steps;
if (isteps > 256)
isteps = 256;
tmp = stp_malloc(sizeof(double) * lut->steps);
for (i = 0; i < isteps; i ++)
{
double pixel = (double) i / (double) (isteps - 1);
if (lut->input_color_description->color_model == COLOR_BLACK)
pixel = 1.0 - pixel;
pixel = 1.0 -
(1.0 / (1.0 - xgamma)) *
(pow(pivot + ipivot * pixel, lut->screen_gamma) - xgamma);
/*
* Fourth, fix up cyan, magenta, yellow values
*/
if (pixel < 0.0)
pixel = 0.0;
else if (pixel > 1.0)
pixel = 1.0;
if (pixel > .9999 && lut->gamma_values[channel] < .00001)
pixel = 0;
else
pixel = 1 - pow(1 - pixel, lut->gamma_values[channel]);
/*
* Finally, fix up print gamma and scale
*/
/*
* Change to this function suggested by Alastair Robinson
* blackfive@fakenhamweb.co.uk
*/
pixel = 65535 * (1.0 / (1.0 - xgamma2)) *
(pow(pivot2 + ipivot2 * pixel, print_gamma) - xgamma2);
if (lut->output_color_description->color_model == COLOR_WHITE)
pixel = 65535 - pixel;
if (pixel <= 0.0)
tmp[i] = 0;
else if (pixel >= 65535.0)
tmp[i] = 65535;
else
tmp[i] = (pixel);
tmp[i] = floor(tmp[i] + 0.5); /* rounding is done here */
}
stp_curve_set_data(curve, isteps, tmp);
if (isteps != lut->steps)
stp_curve_resample(curve, lut->steps);
stp_free(tmp);
}
static void
compute_a_curve_fast(lut_t *lut, int channel)
{
double *tmp;
stp_curve_t *curve = stp_curve_cache_get_curve(&(lut->channel_curves[channel]));
int i;
int isteps = lut->steps;
if (isteps > 256)
isteps = 256;
tmp = stp_malloc(sizeof(double) * lut->steps);
for (i = 0; i < isteps; i++)
{
double pixel = (double) i / (double) (isteps - 1);
pixel = 1 - pow(1 - pixel, lut->gamma_values[channel]);
tmp[i] = floor((65535.0 * pixel) + 0.5);
}
stp_curve_set_data(curve, isteps, tmp);
if (isteps != lut->steps)
stp_curve_resample(curve, lut->steps);
stp_free(tmp);
}
static void
compute_a_curve_simple(lut_t *lut, int channel)
{
double *tmp;
stp_curve_t *curve = stp_curve_cache_get_curve(&(lut->channel_curves[channel]));
int i;
int isteps = lut->steps;
double gamma = 1.0 / (lut->gamma_values[channel] * lut->print_gamma);
if (isteps > 256)
isteps = 256;
tmp = stp_malloc(sizeof(double) * lut->steps);
for (i = 0; i < isteps; i++)
{
double pixel = (double) i / (double) (isteps - 1);
if (lut->input_color_description->color_model == COLOR_BLACK)
pixel = 1.0 - pixel;
pixel = pow(pixel, gamma);
if (lut->output_color_description->color_model == COLOR_BLACK)
pixel = 1.0 - pixel;
tmp[i] = floor((65535.0 * pixel) + 0.5);
}
stp_curve_set_data(curve, isteps, tmp);
if (isteps != lut->steps)
stp_curve_resample(curve, lut->steps);
stp_free(tmp);
}
/*
* If the input and output color spaces both have a particular channel,
* we want to use the general algorithm. If not (i. e. we have to
* synthesize the channel), use a simple gamma curve.
*/
static void
compute_a_curve(lut_t *lut, int channel)
{
if (channel_is_synthesized(lut, channel))
compute_a_curve_fast(lut, channel);
else if (lut->simple_gamma_correction)
compute_a_curve_simple(lut, channel);
else
compute_a_curve_full(lut, channel);
}
static void
invert_curve(stp_curve_t *curve, int invert_output)
{
double lo, hi;
int i;
size_t count;
const double *data = stp_curve_get_data(curve, &count);
double f_gamma = stp_curve_get_gamma(curve);
double *tmp_data;
stp_curve_get_bounds(curve, &lo, &hi);
if (f_gamma)
stp_curve_set_gamma(curve, -f_gamma);
else
{
tmp_data = stp_malloc(sizeof(double) * count);
for (i = 0; i < count; i++)
tmp_data[i] = data[count - i - 1];
stp_curve_set_data(curve, count, tmp_data);
stp_free(tmp_data);
}
if (!invert_output)
{
stp_curve_rescale(curve, -1, STP_CURVE_COMPOSE_MULTIPLY,
STP_CURVE_BOUNDS_RESCALE);
stp_curve_rescale(curve, lo + hi, STP_CURVE_COMPOSE_ADD,
STP_CURVE_BOUNDS_RESCALE);
}
}
static void
compute_one_lut(lut_t *lut, int i)
{
stp_curve_t *curve =
stp_curve_cache_get_curve(&(lut->channel_curves[i]));
if (curve)
{
int invert_output =
!channel_is_synthesized(lut, i) && lut->invert_output;
stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
STP_CURVE_BOUNDS_RESCALE);
if (stp_curve_is_piecewise(curve))
stp_curve_resample(curve, lut->steps);
if (lut->invert_output)
invert_curve(curve, invert_output);
stp_curve_resample(curve, lut->steps);
}
else
{
curve = stp_curve_create_copy(color_curve_bounds);
stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
STP_CURVE_BOUNDS_RESCALE);
stp_curve_cache_set_curve(&(lut->channel_curves[i]), curve);
compute_a_curve(lut, i);
}
}
static void
setup_channel(stp_vars_t *v, int i, const channel_param_t *p)
{
lut_t *lut = (lut_t *)(stp_get_component_data(v, "Color"));
const char *gamma_name =
(lut->output_color_description->color_model == COLOR_BLACK ?
p->gamma_name : p->rgb_gamma_name);
const char *curve_name =
(lut->output_color_description->color_model == COLOR_BLACK ?
p->curve_name : p->rgb_curve_name);
if (stp_check_float_parameter(v, p->gamma_name, STP_PARAMETER_DEFAULTED))
lut->gamma_values[i] = stp_get_float_parameter(v, gamma_name);
if (stp_get_curve_parameter_active(v, curve_name) > 0 &&
stp_get_curve_parameter_active(v, curve_name) >=
stp_get_float_parameter_active(v, gamma_name))
stp_curve_cache_set_curve_copy
(&(lut->channel_curves[i]), stp_get_curve_parameter(v, curve_name));
stp_dprintf(STP_DBG_LUT, v, " %s %.3f\n", gamma_name, lut->gamma_values[i]);
compute_one_lut(lut, i);
}
static void
stpi_print_lut_curve(FILE *fp, const char *text, stp_cached_curve_t *c,
int reverse)
{
if (stp_curve_cache_get_curve(c))
{
fprintf(fp, "%s: '", text);
if (reverse)
{
stp_curve_t *rev = stp_curve_create_reverse(stp_curve_cache_get_curve(c));
stp_curve_write(fp, rev);
stp_curve_destroy(rev);
}
else
stp_curve_write(fp, stp_curve_cache_get_curve(c));
fprintf(fp, "'\n");
}
}
static void
stpi_do_dump_lut_to_file(stp_vars_t *v, FILE *fp)
{
int i;
lut_t *lut = (lut_t *)(stp_get_component_data(v, "Color"));
const stp_curve_t *curve;
fprintf(fp, "Gutenprint LUT dump version 0\n\n");
fprintf(fp, "Input color description: '%s'\n", lut->input_color_description->name);
fprintf(fp, "Output color description: '%s'\n", lut->output_color_description->name);
fprintf(fp, "Color correction type: '%s'\n", lut->color_correction->name);
fprintf(fp, "Ink limit: %f\n", stp_get_float_parameter(v, "InkLimit"));
stpi_print_lut_curve(fp, "Brightness correction", &(lut->brightness_correction), 0);
stpi_print_lut_curve(fp, "Contrast correction", &(lut->contrast_correction), 0);
stpi_print_lut_curve(fp, "User color correction", &(lut->user_color_correction), 0);
for (i = 0; i < STP_CHANNEL_LIMIT; i++)
{
char buf[64];
sprintf(buf, "Channel %d curve", i);
stpi_print_lut_curve(fp, buf, &(lut->channel_curves[i]),
lut->invert_output && ! channel_is_synthesized(lut, i));
}
curve = stp_channel_get_gcr_curve(v);
if (curve)
{
fprintf(fp, "GCR curve: '");
stp_curve_write(fp, curve);
fprintf(fp, "'\n");
}
}
static void
stpi_dump_lut_to_file(stp_vars_t *v, const char *dump_file)
{
FILE *fp;
if (!dump_file)
return;
fp = fopen(dump_file, "w");
if (fp)
{
stp_dprintf(STP_DBG_LUT, v, "Dumping LUT to %s\n", dump_file);
stpi_do_dump_lut_to_file(v, fp);
(void) fclose(fp);
}
}
static void
stpi_compute_lut(stp_vars_t *v)
{
int i;
lut_t *lut = (lut_t *)(stp_get_component_data(v, "Color"));
double app_gamma_scale = 4.0;
stp_curve_t *curve;
stp_dprintf(STP_DBG_LUT, v, "stpi_compute_lut\n");
if (lut->input_color_description->color_model == COLOR_UNKNOWN ||
lut->output_color_description->color_model == COLOR_UNKNOWN ||
lut->input_color_description->color_model ==
lut->output_color_description->color_model)
lut->invert_output = 0;
else
lut->invert_output = 1;
lut->linear_contrast_adjustment = 0;
lut->print_gamma = 1.0;
lut->app_gamma = 1.0;
lut->contrast = 1.0;
lut->brightness = 1.0;
lut->simple_gamma_correction = 0;
if (stp_check_boolean_parameter(v, "LinearContrast", STP_PARAMETER_DEFAULTED))
lut->linear_contrast_adjustment =
stp_get_boolean_parameter(v, "LinearContrast");
if (stp_check_float_parameter(v, "Gamma", STP_PARAMETER_DEFAULTED))
lut->print_gamma = stp_get_float_parameter(v, "Gamma");
if (stp_check_float_parameter(v, "Contrast", STP_PARAMETER_DEFAULTED))
lut->contrast = stp_get_float_parameter(v, "Contrast");
if (stp_check_float_parameter(v, "Brightness", STP_PARAMETER_DEFAULTED))
lut->brightness = stp_get_float_parameter(v, "Brightness");
if (stp_check_float_parameter(v, "AppGamma", STP_PARAMETER_ACTIVE))
lut->app_gamma = stp_get_float_parameter(v, "AppGamma");
if (stp_check_float_parameter(v, "AppGammaScale", STP_PARAMETER_ACTIVE))
app_gamma_scale = stp_get_float_parameter(v, "AppGammaScale");
if (stp_check_boolean_parameter(v, "SimpleGamma", STP_PARAMETER_ACTIVE))
lut->simple_gamma_correction = stp_get_boolean_parameter(v, "SimpleGamma");
lut->screen_gamma = lut->app_gamma / app_gamma_scale; /* "Empirical" */
curve = stp_curve_create_copy(color_curve_bounds);
stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
STP_CURVE_BOUNDS_RESCALE);
stp_curve_cache_set_curve(&(lut->user_color_correction), curve);
curve = stp_curve_create_copy(color_curve_bounds);
stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
STP_CURVE_BOUNDS_RESCALE);
stp_curve_cache_set_curve(&(lut->brightness_correction), curve);
curve = stp_curve_create_copy(color_curve_bounds);
stp_curve_rescale(curve, 65535.0, STP_CURVE_COMPOSE_MULTIPLY,
STP_CURVE_BOUNDS_RESCALE);
stp_curve_cache_set_curve(&(lut->contrast_correction), curve);
compute_user_correction(lut);
/*
* TODO check that these are wraparound curves and all that
*/
if (lut->color_correction->correct_hsl)
{
if (stp_check_curve_parameter(v, "HueMap", STP_PARAMETER_DEFAULTED))
{
lut->hue_map.curve =
stp_curve_create_copy(stp_get_curve_parameter(v, "HueMap"));
if (stp_curve_is_piecewise(lut->hue_map.curve))
stp_curve_resample(lut->hue_map.curve, 384);
}
if (stp_check_curve_parameter(v, "LumMap", STP_PARAMETER_DEFAULTED))
{
lut->lum_map.curve =
stp_curve_create_copy(stp_get_curve_parameter(v, "LumMap"));
if (stp_curve_is_piecewise(lut->lum_map.curve))
stp_curve_resample(lut->lum_map.curve, 384);
}
if (stp_check_curve_parameter(v, "SatMap", STP_PARAMETER_DEFAULTED))
{
lut->sat_map.curve =
stp_curve_create_copy(stp_get_curve_parameter(v, "SatMap"));
if (stp_curve_is_piecewise(lut->sat_map.curve))
stp_curve_resample(lut->sat_map.curve, 384);
}
}
stp_dprintf(STP_DBG_LUT, v, " print_gamma %.3f\n", lut->print_gamma);
stp_dprintf(STP_DBG_LUT, v, " contrast %.3f\n", lut->contrast);
stp_dprintf(STP_DBG_LUT, v, " brightness %.3f\n", lut->brightness);
stp_dprintf(STP_DBG_LUT, v, " screen_gamma %.3f\n", lut->screen_gamma);
for (i = 0; i < STP_CHANNEL_LIMIT; i++)
{
STPI_ASSERT(i < raw_channel_param_count, v);
if (lut->output_color_description->channel_count < 1 &&
i < lut->out_channels)
setup_channel(v, i, &(raw_channel_params[i]));
else if (i < channel_param_count &&
lut->output_color_description->channels & (1 << i))
setup_channel(v, i, &(channel_params[i]));
}
if (((lut->output_color_description->channels & CMASK_CMYK) == CMASK_CMYK) &&
(lut->color_correction->correction == COLOR_CORRECTION_DESATURATED ||
lut->input_color_description->color_id == COLOR_ID_GRAY ||
lut->input_color_description->color_id == COLOR_ID_WHITE ||
lut->input_color_description->color_id == COLOR_ID_RGB ||
lut->input_color_description->color_id == COLOR_ID_CMY))
initialize_gcr_curve(v);
if (stp_check_file_parameter(v, "LUTDumpFile", STP_PARAMETER_ACTIVE))
stpi_dump_lut_to_file(v, stp_get_file_parameter(v, "LUTDumpFile"));
}
static int
stpi_color_traditional_init(stp_vars_t *v,
stp_image_t *image,
size_t steps)
{
lut_t *lut;
const char *image_type = stp_get_string_parameter(v, "ImageType");
const char *color_correction = stp_get_string_parameter(v, "ColorCorrection");
const channel_depth_t *channel_depth =
get_channel_depth(stp_get_string_parameter(v, "ChannelBitDepth"));
size_t total_channel_bits;
if (steps != 256 && steps != 65536)
{
stp_eprintf(v,
"stpi_color_traditional_init: Invalid color steps %lu (must be 256 or 65536)\n",
(unsigned long) steps);
return -1;
}
if (!channel_depth)
{
stp_eprintf(v, "stpi_color_traditional_init: ChannelBitDepth not set\n");
return -1;
}
lut = allocate_lut();
lut->input_color_description =
get_color_description(stp_get_string_parameter(v, "InputImageType"));
lut->output_color_description =
get_color_description(stp_get_string_parameter(v, "STPIOutputType"));
if (!lut->input_color_description || !lut->output_color_description)
{
stp_eprintf(v, "stpi_color_traditional_init: input/output types not specified\n");
free_lut(lut);
return -1;
}
if (lut->input_color_description->color_id == COLOR_ID_RAW)
{
if (stp_verify_parameter(v, "STPIRawChannels", 1) != PARAMETER_OK)
{
stp_eprintf(v, "stpi_color_traditional_init: raw printing requested but STPIRawChannels not set\n");
free_lut(lut);
return -1;
}
lut->out_channels = stp_get_int_parameter(v, "STPIRawChannels");
lut->in_channels = lut->out_channels;
}
else
{
lut->out_channels = lut->output_color_description->channel_count;
lut->in_channels = lut->input_color_description->channel_count;
}
stp_allocate_component_data(v, "Color", copy_lut, free_lut, lut);
lut->steps = steps;
lut->channel_depth = channel_depth->bits;
if ((!color_correction || strcmp(color_correction, "None") == 0) &&
image_type && strcmp(image_type, "None") != 0)
{
if (strcmp(image_type, "Text") == 0)
lut->color_correction = get_color_correction("Threshold");
else
lut->color_correction = get_color_correction("None");
}
else if (color_correction)
lut->color_correction = get_color_correction(color_correction);
else
lut->color_correction = get_color_correction("None");
if (lut->color_correction->correction == COLOR_CORRECTION_DEFAULT)
lut->color_correction =
(get_color_correction_by_tag
(lut->output_color_description->default_correction));
stpi_compute_lut(v);
lut->image_width = stp_image_width(image);
total_channel_bits = lut->in_channels * lut->channel_depth;
lut->in_data = stp_malloc(((lut->image_width * total_channel_bits) + 7)/8);
memset(lut->in_data, 0, ((lut->image_width * total_channel_bits) + 7) / 8);
return lut->out_channels;
}
static void
initialize_standard_curves(void)
{
if (!standard_curves_initialized)
{
int i;
hue_map_bounds = stp_curve_create_from_string
("\n"
"\n"
"\n"
"\n"
"0 0\n"
"\n"
"\n"
"");
lum_map_bounds = stp_curve_create_from_string
("\n"
"\n"
"\n"
"\n"
"1 1\n"
"\n"
"\n"
"");
sat_map_bounds = stp_curve_create_from_string
("\n"
"\n"
"\n"
"\n"
"1 1\n"
"\n"
"\n"
"");
color_curve_bounds = stp_curve_create_from_string
("\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"");
gcr_curve_bounds = stp_curve_create_from_string
("\n"
"\n"
"\n"
"\n"
"1 1\n"
"\n"
"\n"
"");
for (i = 0; i < curve_parameter_count; i++)
curve_parameters[i].param.deflt.curve =
*(curve_parameters[i].defval);
standard_curves_initialized = 1;
}
}
static stp_parameter_list_t
stpi_color_traditional_list_parameters(const stp_vars_t *v)
{
stp_list_t *ret = stp_parameter_list_create();
int i;
initialize_standard_curves();
for (i = 0; i < float_parameter_count; i++)
stp_parameter_list_add_param(ret, &(float_parameters[i].param));
for (i = 0; i < curve_parameter_count; i++)
stp_parameter_list_add_param(ret, &(curve_parameters[i].param));
return ret;
}
static void
stpi_color_traditional_describe_parameter(const stp_vars_t *v,
const char *name,
stp_parameter_t *description)
{
int i, j;
description->p_type = STP_PARAMETER_TYPE_INVALID;
initialize_standard_curves();
if (name == NULL)
return;
for (i = 0; i < float_parameter_count; i++)
{
const float_param_t *param = &(float_parameters[i]);
if (strcmp(name, param->param.name) == 0)
{
stp_fill_parameter_settings(description, &(param->param));
if (param->channel_mask != CMASK_EVERY)
{
const color_description_t *color_description =
get_color_description(stp_describe_output(v));
if (color_description &&
(param->channel_mask & color_description->channels) &&
(param->is_rgb < 0 ||
(param->is_rgb == 0 &&
color_description->color_model == COLOR_BLACK) ||
(param->is_rgb == 1 &&
color_description->color_model == COLOR_WHITE)) &&
param->channel_mask != CMASK_RAW)
description->is_active = 1;
else
description->is_active = 0;
if (param->color_only && color_description &&
!(color_description->channels & ~CMASK_K))
description->is_active = 0;
}
switch (param->param.p_type)
{
case STP_PARAMETER_TYPE_BOOLEAN:
description->deflt.boolean = (int) param->defval;
break;
case STP_PARAMETER_TYPE_INT:
description->bounds.integer.upper = (int) param->max;
description->bounds.integer.lower = (int) param->min;
description->deflt.integer = (int) param->defval;
break;
case STP_PARAMETER_TYPE_DOUBLE:
description->bounds.dbl.upper = param->max;
description->bounds.dbl.lower = param->min;
description->deflt.dbl = param->defval;
if (strcmp(name, "InkLimit") == 0)
{
stp_parameter_t ink_limit_desc;
stp_describe_parameter(v, "InkChannels", &ink_limit_desc);
if (ink_limit_desc.p_type == STP_PARAMETER_TYPE_INT &&
ink_limit_desc.deflt.integer > 1)
{
description->bounds.dbl.upper =
ink_limit_desc.deflt.integer;
description->deflt.dbl =
ink_limit_desc.deflt.integer;
}
else
description->is_active = 0;
stp_parameter_description_destroy(&ink_limit_desc);
}
break;
case STP_PARAMETER_TYPE_STRING_LIST:
if (!strcmp(param->param.name, "ColorCorrection"))
{
description->bounds.str = stp_string_list_create();
for (j = 0; j < color_correction_count; j++)
stp_string_list_add_string
(description->bounds.str, color_corrections[j].name,
gettext(color_corrections[j].text));
description->deflt.str =
stp_string_list_param(description->bounds.str, 0)->name;
}
else if (strcmp(name, "ChannelBitDepth") == 0)
{
description->bounds.str = stp_string_list_create();
for (j = 0; j < channel_depth_count; j++)
stp_string_list_add_string
(description->bounds.str, channel_depths[j].name,
channel_depths[j].name);
description->deflt.str =
stp_string_list_param(description->bounds.str, 0)->name;
}
else if (strcmp(name, "InputImageType") == 0)
{
description->bounds.str = stp_string_list_create();
for (j = 0; j < color_description_count; j++)
if (color_descriptions[j].input)
{
if (color_descriptions[j].color_id == COLOR_ID_RAW)
{
stp_parameter_t desc;
stp_describe_parameter(v, "RawChannels", &desc);
if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST)
stp_string_list_add_string
(description->bounds.str,
color_descriptions[j].name,
gettext(color_descriptions[j].name));
stp_parameter_description_destroy(&desc);
}
else
stp_string_list_add_string
(description->bounds.str,
color_descriptions[j].name,
gettext(color_descriptions[j].name));
}
description->deflt.str =
stp_string_list_param(description->bounds.str, 0)->name;
}
else if (strcmp(name, "OutputImageType") == 0)
{
description->bounds.str = stp_string_list_create();
for (j = 0; j < color_description_count; j++)
if (color_descriptions[j].output)
stp_string_list_add_string
(description->bounds.str, color_descriptions[j].name,
gettext(color_descriptions[j].name));
description->deflt.str =
stp_string_list_param(description->bounds.str, 0)->name;
}
break;
default:
break;
}
return;
}
}
for (i = 0; i < curve_parameter_count; i++)
{
curve_param_t *param = &(curve_parameters[i]);
if (strcmp(name, param->param.name) == 0)
{
description->is_active = 1;
stp_fill_parameter_settings(description, &(param->param));
if (param->channel_mask != CMASK_EVERY)
{
const color_description_t *color_description =
get_color_description(stp_describe_output(v));
if (color_description &&
(param->is_rgb < 0 ||
(param->is_rgb == 0 &&
color_description->color_model == COLOR_BLACK) ||
(param->is_rgb == 1 &&
color_description->color_model == COLOR_WHITE)) &&
(param->channel_mask & color_description->channels))
description->is_active = 1;
else
description->is_active = 0;
if (param->color_only && color_description &&
!(color_description->channels & ~CMASK_K))
description->is_active = 0;
}
if (param->hsl_only)
{
const color_correction_t *correction =
(get_color_correction
(stp_get_string_parameter (v, "ColorCorrection")));
if (correction && !correction->correct_hsl)
description->is_active = 0;
}
switch (param->param.p_type)
{
case STP_PARAMETER_TYPE_CURVE:
description->deflt.curve = *(param->defval);
description->bounds.curve =
stp_curve_create_copy(*(param->defval));
break;
default:
break;
}
return;
}
}
}
static const stp_colorfuncs_t stpi_color_traditional_colorfuncs =
{
&stpi_color_traditional_init,
&stpi_color_traditional_get_row,
&stpi_color_traditional_list_parameters,
&stpi_color_traditional_describe_parameter
};
static stp_color_t stpi_color_traditional_module_data =
{
"traditional",
N_("Traditional Gutenprint color conversion"),
&stpi_color_traditional_colorfuncs
};
static int
color_traditional_module_init(void)
{
return stp_color_register(&stpi_color_traditional_module_data);
}
static int
color_traditional_module_exit(void)
{
return stp_color_unregister(&stpi_color_traditional_module_data);
}
/* Module header */
#define stp_module_version color_traditional_LTX_stp_module_version
#define stp_module_data color_traditional_LTX_stp_module_data
stp_module_version_t stp_module_version = {0, 0};
stp_module_t stp_module_data =
{
"traditional",
VERSION,
"Traditional Gutenprint color conversion",
STP_MODULE_CLASS_COLOR,
NULL,
color_traditional_module_init,
color_traditional_module_exit,
(void *) &stpi_color_traditional_module_data
};