KnobImageSource: add get_state_hash()
[libprolooks.git] / prolooks / GlossGradient.vala
blob0903bfb271d45a8075ac0df08bbf0545bc0ed4dc
1 using Gtk;
2 using Gdk;
4 // code courtesy http://cocoawithlove.com/2008/09/drawing-gloss-gradients-in-coregraphics.html
5 // adapted to Vala by Hans Baier
7 namespace Prolooks {
9 private static double perceptualGlossFractionForColor(Cairo.Color color) {
10 double REFLECTION_SCALE_NUMBER = 0.2;
11 double NTSC_RED_FRACTION = 0.299;
12 double NTSC_GREEN_FRACTION = 0.587;
13 double NTSC_BLUE_FRACTION = 0.114;
15 double glossScale =
16 NTSC_RED_FRACTION * color.red +
17 NTSC_GREEN_FRACTION * color.green +
18 NTSC_BLUE_FRACTION * color.blue;
20 glossScale = Math.pow(glossScale, REFLECTION_SCALE_NUMBER);
22 return glossScale;
25 private static Cairo.Color perceptualCausticColorForColor(Cairo.Color input) {
26 double CAUSTIC_FRACTION = 0.60;
27 double COSINE_ANGLE_SCALE = 1.4;
28 double MIN_RED_THRESHOLD = 0.95;
29 double MAX_BLUE_THRESHOLD = 0.7;
30 double GRAYSCALE_CAUSTIC_SATURATION = 0.2;
32 Cairo.Color source = input;
34 HSV source_hsv = new HSV();
35 source_hsv.from_cairo_color(source);
37 double hue = source_hsv.hue;
38 double saturation = source_hsv.saturation;
39 double value = source_hsv.value;
40 double alpha = input.alpha;
42 HSV target_hsv = new HSV();
43 target_hsv.from_cairo_color(new Cairo.Color(1.0, 1.0, 0.0));
45 double targetHue = target_hsv.hue;
46 double targetSaturation = target_hsv.saturation;
47 double targetValue = target_hsv.value;
49 if (saturation < 0.0001) {
50 hue = targetHue;
51 saturation = GRAYSCALE_CAUSTIC_SATURATION;
54 if (hue > MIN_RED_THRESHOLD) {
55 hue -= 1.0;
56 } else if (hue > MAX_BLUE_THRESHOLD) {
57 target_hsv.from_cairo_color(new Cairo.Color(1.0, 0.0, 1.0));
59 targetHue = target_hsv.hue;
60 targetSaturation = target_hsv.saturation;
61 targetValue = target_hsv.value;
64 double scaledCaustic = CAUSTIC_FRACTION * 0.5 * (1.0 + Math.cos(COSINE_ANGLE_SCALE * Math.PI * (hue - targetHue) / 360.0));
66 target_hsv.hue = hue * (1.0 - scaledCaustic) + targetHue * scaledCaustic;
67 target_hsv.saturation = saturation;
68 target_hsv.value = value * (1.0 - scaledCaustic) + targetValue * scaledCaustic;
70 Cairo.Color target_color = target_hsv.to_cairo_color();
71 target_color.alpha = alpha;
73 return target_color;
76 private struct GlossParameters {
77 Cairo.Color color;
78 Cairo.Color caustic;
79 double expCoefficient;
80 double expScale;
81 double expOffset;
82 double initialWhite;
83 double finalWhite;
86 private static Cairo.Color glossInterpolation(double progress, ref GlossParameters params) {
87 Cairo.Color output = new Cairo.Color();
89 if (progress < 0.5)
91 progress = progress * 2.0;
93 progress =
94 1.0 - params.expScale * (Math.exp(progress * (-params.expCoefficient)) - params.expOffset);
96 double currentWhite = progress * (params.finalWhite - params.initialWhite) + params.initialWhite;
98 output.red = params.color.red * (1.0 - currentWhite) + currentWhite;
99 output.green = params.color.green * (1.0 - currentWhite) + currentWhite;
100 output.blue = params.color.blue * (1.0 - currentWhite) + currentWhite;
101 output.alpha = params.color.alpha * (1.0 - currentWhite) + currentWhite;
103 else
105 progress = (progress - 0.5) * 2.0;
108 progress = params.expScale *
109 (Math.exp((1.0 - progress) * (-params.expCoefficient)) - params.expOffset);
111 output.red = params.color.red * (1.0 - progress) + params.caustic.red * progress;
112 output.green = params.color.green * (1.0 - progress) + params.caustic.green * progress;
113 output.blue = params.color.blue * (1.0 - progress) + params.caustic.blue * progress;
114 output.alpha = params.color.alpha * (1.0 - progress) + params.caustic.alpha * progress;
117 return output;
120 public static Cairo.Pattern gloss_gradient_pattern(double x0, double y0, double x1, double y1, Cairo.Color color, int no_steps = 100) {
121 double EXP_COEFFICIENT = 1.2;
122 double REFLECTION_MAX = 0.60;
123 double REFLECTION_MIN = 0.20;
125 GlossParameters params = GlossParameters();
127 params.expCoefficient = EXP_COEFFICIENT;
128 params.expOffset = Math.exp(-params.expCoefficient);
129 params.expScale = 1.0 / (1.0 - params.expOffset);
131 params.color = color;
133 params.caustic = perceptualCausticColorForColor(params.color);
135 double glossScale = perceptualGlossFractionForColor(params.color);
137 params.initialWhite = glossScale * REFLECTION_MAX;
138 params.finalWhite = glossScale * REFLECTION_MIN;
140 Cairo.Pattern result = new Cairo.Pattern.linear(x0, y0, x1, y1);
142 for (double offset = 0; offset <= 1.0; offset += 1.0 / (double) no_steps) {
143 Cairo.Color stop = glossInterpolation(offset, ref params);
144 result.add_color_stop_rgba(offset, stop.red, stop.green, stop.blue, stop.alpha);
147 return result;
150 } // namespace Prolooks