* make_fvwmdist.sh (wrong_dir): Use portable way of redirecting
[fvwm.git] / libs / ColorUtils.c
blob3e00f1ebb6b90af357ae23dce51733a8838ef95f
1 /*
2 Around 12/20/99 we did the 3rd rewrite of the shadow/hilite stuff.
3 (That I know about (dje).
4 The first stuff I saw just applied a percentage.
5 Then we got some code from SCWM.
6 This stuff comes from "Visual.c" which is part of Lesstif.
7 Here's their copyright:
9 * Copyright (C) 1995 Free Software Foundation, Inc.
11 * This file is part of the GNU LessTif Library.
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Library General Public License for more details.
23 * You should have received a copy of the GNU Library General Public
24 * License along with this library; if not, write to the Free
25 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 The routine at the bottom "pixel_to_color_string" was not from Lesstif.
30 Port by Dan Espen, no additional copyright
33 #include "config.h" /* must be first */
35 #include <stdio.h>
36 #include <X11/Xproto.h> /* for X functions in general */
37 #include "fvwmlib.h" /* prototype GetShadow GetHilit */
39 #define PCT_BRIGHTNESS (6 * 0xffff / 100)
41 /* How much lighter/darker to make things in default routine */
43 #define PCT_DARK_BOTTOM 70 /* lighter (less dark, actually) */
44 #define PCT_DARK_TOP 50 /* lighter */
45 #define PCT_LIGHT_BOTTOM 55 /* darker */
46 #define PCT_LIGHT_TOP 80 /* darker */
47 #define PCT_MEDIUM_BOTTOM_BASE 40 /* darker */
48 #define PCT_MEDIUM_BOTTOM_RANGE 25
49 #define PCT_MEDIUM_TOP_BASE 60 /* lighter */
50 #define PCT_MEDIUM_TOP_RANGE -30
52 /* The "brightness" of an RGB color. The "right" way seems to use
53 * empirical values like the default thresholds below, but it boils down
54 * to red is twice as bright as blue and green is thrice blue.
57 #define BRIGHTNESS(r,g,b) (2*(int)(r) + 3*(int)(g) + 1*(int)(b))
59 /* From Xm.h on Solaris */
60 #define XmDEFAULT_DARK_THRESHOLD 15
61 #define XmDEFAULT_LIGHT_THRESHOLD 85
62 extern Colormap Pcmap;
63 extern Display *Pdpy;
65 static XColor color;
67 /**** This part is the old fvwm way to calculate colours. Still used for
68 **** 'medium' brigness colours. */
69 #define DARKNESS_FACTOR 0.5
70 #define BRIGHTNESS_FACTOR 1.4
71 #define SCALE 65535.0
72 #define HALF_SCALE (SCALE / 2)
73 typedef enum {
74 R_MAX_G_MIN, R_MAX_B_MIN,
75 G_MAX_B_MIN, G_MAX_R_MIN,
76 B_MAX_R_MIN, B_MAX_G_MIN
77 } MinMaxState;
78 static void
79 color_mult (unsigned short *red,
80 unsigned short *green,
81 unsigned short *blue, double k)
83 if (*red == *green && *red == *blue) {
84 double temp;
85 /* A shade of gray */
86 temp = k * (double) (*red);
87 if (temp > SCALE) {
88 temp = SCALE;
90 *red = (unsigned short)(temp);
91 *green = *red;
92 *blue = *red;
93 } else {
94 /* Non-zero saturation */
95 double r, g, b;
96 double min, max;
97 double a, l, s;
98 double delta;
99 double middle;
100 MinMaxState min_max_state;
102 r = (double) *red;
103 g = (double) *green;
104 b = (double) *blue;
106 if (r > g) {
107 if (r > b) {
108 max = r;
109 if (g < b) {
110 min = g;
111 min_max_state = R_MAX_G_MIN;
112 a = b - g;
113 } else {
114 min = b;
115 min_max_state = R_MAX_B_MIN;
116 a = g - b;
118 } else {
119 max = b;
120 min = g;
121 min_max_state = B_MAX_G_MIN;
122 a = r - g;
124 } else {
125 if (g > b) {
126 max = g;
127 if (b < r) {
128 min = b;
129 min_max_state = G_MAX_B_MIN;
130 a = r - b;
131 } else {
132 min = r;
133 min_max_state = G_MAX_R_MIN;
134 a = b - r;
136 } else {
137 max = b;
138 min = r;
139 min_max_state = B_MAX_R_MIN;
140 a = g - r;
144 delta = max - min;
145 a = a / delta;
147 l = (max + min) / 2;
148 if (l <= HALF_SCALE) {
149 s = max + min;
150 } else {
151 s = 2.0 * SCALE - (max + min);
153 s = delta/s;
155 l *= k;
156 if (l > SCALE) {
157 l = SCALE;
159 s *= k;
160 if (s > 1.0) {
161 s = 1.0;
164 if (l <= HALF_SCALE) {
165 max = l * (1 + s);
166 } else {
167 max = s * SCALE + l - s * l;
170 min = 2 * l - max;
171 delta = max - min;
172 middle = min + delta * a;
174 switch (min_max_state) {
175 case R_MAX_G_MIN:
176 r = max;
177 g = min;
178 b = middle;
179 break;
180 case R_MAX_B_MIN:
181 r = max;
182 g = middle;
183 b = min;
184 break;
185 case G_MAX_B_MIN:
186 r = middle;
187 g = max;
188 b = min;
189 break;
190 case G_MAX_R_MIN:
191 r = min;
192 g = max;
193 b = middle;
194 break;
195 case B_MAX_G_MIN:
196 r = middle;
197 g = min;
198 b = max;
199 break;
200 case B_MAX_R_MIN:
201 r = min;
202 g = middle;
203 b = max;
204 break;
207 *red = (unsigned short) r;
208 *green = (unsigned short) g;
209 *blue = (unsigned short) b;
212 /**** End of original fvwm code. ****/
214 static XColor *GetShadowOrHiliteColor(
215 Pixel background, float light, float dark, float factor)
217 long brightness;
218 unsigned short red, green, blue;
220 memset(&color, 0, sizeof(color));
221 color.pixel = background;
222 XQueryColor(Pdpy, Pcmap, &color);
223 red = color.red;
224 green = color.green;
225 blue = color.blue;
227 brightness = BRIGHTNESS(red, green, blue);
228 /* For "dark" backgrounds, make everything a fixed %age lighter */
229 if (brightness < XmDEFAULT_DARK_THRESHOLD * PCT_BRIGHTNESS)
231 color.red = 0xffff - ((0xffff - red) * dark + 50) / 100;
232 color.green = 0xffff - ((0xffff - green) * dark + 50) / 100;
233 color.blue = 0xffff - ((0xffff - blue) * dark + 50) / 100;
235 /* For "light" background, make everything a fixed %age darker */
236 else if (brightness > XmDEFAULT_LIGHT_THRESHOLD * PCT_BRIGHTNESS)
238 color.red = (red * light + 50) / 100;
239 color.green = (green * light + 50) / 100;
240 color.blue = (blue * light + 50) / 100;
242 /* For "medium" background, select is a fixed %age darker;
243 * top (lighter) and bottom (darker) are a variable %age
244 * based on the background's brightness
246 else
248 color_mult(&color.red, &color.green, &color.blue, factor);
250 return &color;
254 XColor *GetShadowColor(Pixel background)
256 return GetShadowOrHiliteColor(
257 background, PCT_DARK_BOTTOM, PCT_DARK_TOP, DARKNESS_FACTOR);
260 Pixel GetShadow(Pixel background)
262 XColor *colorp;
264 colorp = GetShadowColor(background);
265 XAllocColor (Pdpy, Pcmap, colorp);
266 return colorp->pixel;
269 XColor *GetHiliteColor(Pixel background)
271 return GetShadowOrHiliteColor(
272 background, PCT_LIGHT_BOTTOM, PCT_LIGHT_BOTTOM, BRIGHTNESS_FACTOR);
275 Pixel GetHilite(Pixel background)
277 XColor *colorp;
279 colorp = GetHiliteColor(background);
280 XAllocColor (Pdpy, Pcmap, colorp);
281 return colorp->pixel;
284 /* This function converts the colour stored in a colorcell (pixel) into the
285 * string representation of a colour. The output is printed at the
286 * address 'output'. It is either in rgb format ("rgb:rrrr/gggg/bbbb") if
287 * use_hash is False or in hash notation ("#rrrrggggbbbb") if use_hash is true.
288 * The return value is the number of characters used by the string. The
289 * rgb values of the output are undefined if the colorcell is invalid. The
290 * memory area pointed at by 'output' must be at least 64 bytes (in case of
291 * future extensions and multibyte characters).*/
292 int pixel_to_color_string(
293 Display *dpy, Colormap cmap, Pixel pixel, char *output, Bool use_hash)
295 XColor color;
296 int n;
298 color.pixel = pixel;
299 color.red = 0;
300 color.green = 0;
301 color.blue = 0;
303 XQueryColor(dpy, cmap, &color);
304 if (!use_hash)
306 sprintf(output, "rgb:%04x/%04x/%04x%n", (int)color.red, (int)color.green,
307 (int)color.blue, &n);
309 else
311 sprintf(output, "#%04x%04x%04x%n", (int)color.red, (int)color.green,
312 (int)color.blue, &n);
315 return n;