Updated for CVS 2.5.32 now.
[fvwm.git] / libs / ColorUtils.c
blob49f2bd2c4050e486d7f0461456216a0d54c2047e
1 /* -*-c-*- */
2 /*
3 * Around 12/20/99 we did the 3rd rewrite of the shadow/hilite stuff.
4 * (That I know about (dje).
5 * The first stuff I saw just applied a percentage.
6 * Then we got some code from SCWM.
7 * This stuff comes from "Visual.c" which is part of Lesstif.
8 * Here's their copyright:
10 * Copyright (C) 1995 Free Software Foundation, Inc.
12 * This file is part of the GNU LessTif Library.
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Library General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with this library; if not, write to the Free
26 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * The routine at the bottom "pixel_to_color_string" was not from Lesstif.
31 * Port by Dan Espen, no additional copyright
34 #include "config.h" /* must be first */
36 #include <stdio.h>
37 #include <X11/Xproto.h> /* for X functions in general */
38 #include "fvwmlib.h" /* prototype GetShadow GetHilit */
39 #include "Parse.h"
40 #include "Colorset.h"
41 #include "PictureBase.h"
42 #include "PictureUtils.h"
43 #include "ColorUtils.h"
45 #define PCT_BRIGHTNESS (6 * 0xffff / 100)
47 /* How much lighter/darker to make things in default routine */
49 #define PCT_DARK_BOTTOM 70 /* lighter (less dark, actually) */
50 #define PCT_DARK_TOP 50 /* lighter */
51 #define PCT_LIGHT_BOTTOM 55 /* darker */
52 #define PCT_LIGHT_TOP 80 /* darker */
53 #define PCT_MEDIUM_BOTTOM_BASE 40 /* darker */
54 #define PCT_MEDIUM_BOTTOM_RANGE 25
55 #define PCT_MEDIUM_TOP_BASE 60 /* lighter */
56 #define PCT_MEDIUM_TOP_RANGE -30
58 /* The "brightness" of an RGB color. The "right" way seems to use
59 * empirical values like the default thresholds below, but it boils down
60 * to red is twice as bright as blue and green is thrice blue.
63 #define BRIGHTNESS(r,g,b) (2*(int)(r) + 3*(int)(g) + 1*(int)(b))
65 /* From Xm.h on Solaris */
66 #define XmDEFAULT_DARK_THRESHOLD 15
67 #define XmDEFAULT_LIGHT_THRESHOLD 85
69 static XColor color;
71 /**** This part is the old fvwm way to calculate colours. Still used for
72 **** 'medium' brigness colours. */
73 #define DARKNESS_FACTOR 0.5
74 #define BRIGHTNESS_FACTOR 1.4
75 #define SCALE 65535.0
76 #define HALF_SCALE (SCALE / 2)
77 typedef enum {
78 R_MAX_G_MIN, R_MAX_B_MIN,
79 G_MAX_B_MIN, G_MAX_R_MIN,
80 B_MAX_R_MIN, B_MAX_G_MIN
81 } MinMaxState;
82 static void
83 color_mult (unsigned short *red,
84 unsigned short *green,
85 unsigned short *blue, double k)
87 if (*red == *green && *red == *blue) {
88 double temp;
89 /* A shade of gray */
90 temp = k * (double) (*red);
91 if (temp > SCALE) {
92 temp = SCALE;
94 *red = (unsigned short)(temp);
95 *green = *red;
96 *blue = *red;
97 } else {
98 /* Non-zero saturation */
99 double r, g, b;
100 double min, max;
101 double a, l, s;
102 double delta;
103 double middle;
104 MinMaxState min_max_state;
106 r = (double) *red;
107 g = (double) *green;
108 b = (double) *blue;
110 if (r > g) {
111 if (r > b) {
112 max = r;
113 if (g < b) {
114 min = g;
115 min_max_state = R_MAX_G_MIN;
116 a = b - g;
117 } else {
118 min = b;
119 min_max_state = R_MAX_B_MIN;
120 a = g - b;
122 } else {
123 max = b;
124 min = g;
125 min_max_state = B_MAX_G_MIN;
126 a = r - g;
128 } else {
129 if (g > b) {
130 max = g;
131 if (b < r) {
132 min = b;
133 min_max_state = G_MAX_B_MIN;
134 a = r - b;
135 } else {
136 min = r;
137 min_max_state = G_MAX_R_MIN;
138 a = b - r;
140 } else {
141 max = b;
142 min = r;
143 min_max_state = B_MAX_R_MIN;
144 a = g - r;
148 delta = max - min;
149 a = a / delta;
151 l = (max + min) / 2;
152 if (l <= HALF_SCALE) {
153 s = max + min;
154 } else {
155 s = 2.0 * SCALE - (max + min);
157 s = delta/s;
159 l *= k;
160 if (l > SCALE) {
161 l = SCALE;
163 s *= k;
164 if (s > 1.0) {
165 s = 1.0;
168 if (l <= HALF_SCALE) {
169 max = l * (1 + s);
170 } else {
171 max = s * SCALE + l - s * l;
174 min = 2 * l - max;
175 delta = max - min;
176 middle = min + delta * a;
178 switch (min_max_state) {
179 case R_MAX_G_MIN:
180 r = max;
181 g = min;
182 b = middle;
183 break;
184 case R_MAX_B_MIN:
185 r = max;
186 g = middle;
187 b = min;
188 break;
189 case G_MAX_B_MIN:
190 r = middle;
191 g = max;
192 b = min;
193 break;
194 case G_MAX_R_MIN:
195 r = min;
196 g = max;
197 b = middle;
198 break;
199 case B_MAX_G_MIN:
200 r = middle;
201 g = min;
202 b = max;
203 break;
204 case B_MAX_R_MIN:
205 r = min;
206 g = middle;
207 b = max;
208 break;
211 *red = (unsigned short) r;
212 *green = (unsigned short) g;
213 *blue = (unsigned short) b;
216 /**** End of original fvwm code. ****/
218 static XColor *GetShadowOrHiliteColor(
219 Pixel background, float light, float dark, float factor)
221 long brightness;
222 unsigned int red, green, blue;
224 memset(&color, 0, sizeof(color));
225 color.pixel = background;
226 XQueryColor(Pdpy, Pcmap, &color);
227 red = color.red;
228 green = color.green;
229 blue = color.blue;
231 brightness = BRIGHTNESS(red, green, blue);
232 /* For "dark" backgrounds, make everything a fixed %age lighter */
233 if (brightness < XmDEFAULT_DARK_THRESHOLD * PCT_BRIGHTNESS)
235 color.red = (unsigned short)
236 (0xffff - ((0xffff - red) * dark + 50) / 100);
237 color.green = (unsigned short)
238 (0xffff - ((0xffff - green) * dark + 50) / 100);
239 color.blue = (unsigned short)
240 (0xffff - ((0xffff - blue) * dark + 50) / 100);
242 /* For "light" background, make everything a fixed %age darker */
243 else if (brightness > XmDEFAULT_LIGHT_THRESHOLD * PCT_BRIGHTNESS)
245 color.red =
246 (unsigned short)((red * light + 50) / 100);
247 color.green =
248 (unsigned short)((green * light + 50) / 100);
249 color.blue =
250 (unsigned short)((blue * light + 50) / 100);
252 /* For "medium" background, select is a fixed %age darker;
253 * top (lighter) and bottom (darker) are a variable %age
254 * based on the background's brightness
256 else
258 color_mult(&color.red, &color.green, &color.blue, factor);
260 return &color;
264 XColor *GetShadowColor(Pixel background)
266 return GetShadowOrHiliteColor(
267 background, PCT_LIGHT_BOTTOM, PCT_DARK_BOTTOM, DARKNESS_FACTOR);
270 Pixel GetShadow(Pixel background)
272 XColor *colorp;
274 colorp = GetShadowColor(background);
275 PictureAllocColor(Pdpy, Pcmap, colorp, True);
276 if (colorp->pixel == background)
278 colorp->pixel = PictureGetNextColor(colorp->pixel, 1);
280 return colorp->pixel;
283 XColor *GetHiliteColor(Pixel background)
285 return GetShadowOrHiliteColor(
286 background, PCT_LIGHT_TOP, PCT_DARK_TOP, BRIGHTNESS_FACTOR);
289 Pixel GetHilite(Pixel background)
291 XColor *colorp;
293 colorp = GetHiliteColor(background);
294 PictureAllocColor(Pdpy, Pcmap, colorp, True);
295 if (colorp->pixel == background)
297 colorp->pixel = PictureGetNextColor(colorp->pixel, -1);
299 return colorp->pixel;
302 XColor *GetForeShadowColor(Pixel foreground, Pixel background)
304 XColor bg_color;
305 float fg[3], bg[3];
306 int result[3];
307 int i;
309 memset(&color, 0, sizeof(color));
310 memset(&bg_color, 0, sizeof(bg_color));
311 color.pixel = foreground;
312 bg_color.pixel = background;
313 XQueryColor(Pdpy, Pcmap, &color);
314 XQueryColor(Pdpy, Pcmap, &bg_color);
315 fg[0] = color.red;
316 fg[1] = color.green;
317 fg[2] = color.blue;
318 bg[0] = bg_color.red;
319 bg[1]= bg_color.green;
320 bg[2] = bg_color.blue;
322 for (i=0; i<3; i++)
324 if (fg[i] - bg[i] < 8192 && fg[i] - bg[i] > -8192)
326 result[i] = 0;
328 else
330 result[i] = (int)((5 * bg[i] - fg[i]) / 4);
331 if (fg[i] < bg[i] || result[i] < 0)
333 result[i] = (int)((3 * bg[i] + fg[i]) / 4);
337 color.red = result[0];
338 color.green = result[1];
339 color.blue = result[2];
341 return &color;
344 Pixel GetForeShadow(Pixel foreground, Pixel background)
346 XColor *colorp;
348 colorp = GetForeShadowColor(foreground, background);
349 PictureAllocColor(Pdpy, Pcmap, colorp, True);
350 if (colorp->pixel == background)
352 colorp->pixel = PictureGetNextColor(colorp->pixel, 1);
354 return colorp->pixel;
357 XColor *GetTintedColor(Pixel in, Pixel tint, int percent)
359 XColor tint_color;
361 memset(&color, 0, sizeof(color));
362 memset(&tint_color, 0, sizeof(tint_color));
363 color.pixel = in;
364 XQueryColor(Pdpy, Pcmap, &color);
365 tint_color.pixel = tint;
366 XQueryColor(Pdpy, Pcmap, &tint_color);
368 color.red = (unsigned short)
369 (((100-percent)*color.red + tint_color.red * percent) / 100);
370 color.green = (unsigned short)
371 (((100-percent)*color.green + tint_color.green * percent) /
372 100);
373 color.blue = (unsigned short)
374 (((100-percent)*color.blue + tint_color.blue * percent) / 100);
375 return &color;
378 Pixel GetTintedPixel(Pixel in, Pixel tint, int percent)
380 XColor *colorp;
382 colorp = GetTintedColor(in, tint, percent);
383 PictureAllocColor(Pdpy, Pcmap, colorp, True);
384 return colorp->pixel;
387 /* This function converts the colour stored in a colorcell (pixel) into the
388 * string representation of a colour. The output is printed at the
389 * address 'output'. It is either in rgb format ("rgb:rrrr/gggg/bbbb") if
390 * use_hash is False or in hash notation ("#rrrrggggbbbb") if use_hash is true.
391 * The return value is the number of characters used by the string. The
392 * rgb values of the output are undefined if the colorcell is invalid. The
393 * memory area pointed at by 'output' must be at least 64 bytes (in case of
394 * future extensions and multibyte characters).*/
395 int pixel_to_color_string(
396 Display *dpy, Colormap cmap, Pixel pixel, char *output, Bool use_hash)
398 XColor color;
399 int n;
401 color.pixel = pixel;
402 color.red = 0;
403 color.green = 0;
404 color.blue = 0;
406 XQueryColor(dpy, cmap, &color);
407 if (!use_hash)
409 sprintf(
410 output, "rgb:%04x/%04x/%04x%n", (int)color.red,
411 (int)color.green, (int)color.blue, &n);
413 else
415 sprintf(
416 output, "#%04x%04x%04x%n", (int)color.red,
417 (int)color.green, (int)color.blue, &n);
420 return n;
423 static char *colorset_names[] =
425 "$[fg.cs",
426 "$[bg.cs",
427 "$[hilight.cs",
428 "$[shadow.cs",
429 NULL
432 Pixel GetSimpleColor(char *name)
434 XColor color;
435 Bool is_illegal_rgb = False;
437 memset(&color, 0, sizeof(color));
438 /* This is necessary because some X servers coredump when presented a
439 * malformed rgb colour name. */
440 if (name && strncasecmp(name, "rgb:", 4) == 0)
442 int i;
443 char *s;
445 for (i = 0, s = name + 4; *s; s++)
447 if (*s == '/')
448 i++;
450 if (i != 2)
451 is_illegal_rgb = True;
454 if (is_illegal_rgb)
456 fprintf(stderr, "Illegal RGB format \"%s\"\n", name);
458 else if (!XParseColor (Pdpy, Pcmap, name, &color))
460 fprintf(stderr, "Cannot parse color \"%s\"\n",
461 name ? name : "<blank>");
463 else if (!PictureAllocColor(Pdpy, Pcmap, &color, True))
465 fprintf(stderr, "Cannot allocate color \"%s\"\n", name);
467 return color.pixel;
470 Pixel GetColor(char *name)
472 int i;
473 int n;
474 int cs;
475 char *rest;
476 XColor color;
478 switch ((i = GetTokenIndex(name, colorset_names, -1, &rest)))
480 case 0:
481 case 1:
482 case 2:
483 case 3:
484 if (!isdigit(*rest) || (*rest == '0' && *(rest + 1) != 0))
486 /* not a non-negative integer without leading zeros */
487 fprintf(stderr,
488 "Invalid colorset number in color '%s'\n",
489 name);
490 return 0;
492 sscanf(rest, "%d%n", &cs, &n);
493 if (*(rest + n) != ']')
495 fprintf(stderr,
496 "No closing brace after '%d' in color '%s'\n",
497 cs, name);
498 return 0;
500 if (*(rest + n + 1) != 0)
502 fprintf(stderr, "Trailing characters after brace in"
503 " color '%s'\n", name);
504 return 0;
506 AllocColorset(cs);
507 switch (i)
509 case 0:
510 color.pixel = Colorset[cs].fg;
511 break;
512 case 1:
513 color.pixel = Colorset[cs].bg;
514 break;
515 case 2:
516 color.pixel = Colorset[cs].hilite;
517 break;
518 case 3:
519 color.pixel = Colorset[cs].shadow;
520 break;
522 if (!PictureAllocColor(Pdpy, Pcmap, &color, True))
524 fprintf(stderr, "Cannot allocate color %d from"
525 " colorset %d\n", i, cs);
526 return 0;
528 return color.pixel;
530 default:
531 break;
534 return GetSimpleColor(name);
537 /* Allocates the color from the input Pixel again */
538 Pixel fvwmlib_clone_color(Pixel p)
540 XColor c;
542 c.pixel = p;
543 XQueryColor(Pdpy, Pcmap, &c);
544 if (!PictureAllocColor(Pdpy, Pcmap, &c, True))
546 fprintf(stderr, "Cannot allocate clone Pixel %d\n", (int)p);
547 return 0;
550 return c.pixel;
553 /* Free an array of colours (n colours), never free black */
554 void fvwmlib_free_colors(Display *dpy, Pixel *pixels, int n, Bool no_limit)
556 int i;
558 /* We don't ever free black - dirty hack to allow freeing colours at
559 * all */
560 /* olicha: ???? */
561 for (i = 0; i < n; i++)
563 if (pixels[i] != 0)
565 PictureFreeColors(
566 dpy, Pcmap, pixels + i, 1, 0, no_limit);
570 return;
573 /* Copy one color and reallocate it */
574 void fvwmlib_copy_color(
575 Display *dpy, Pixel *dst_color, Pixel *src_color, Bool do_free_dest,
576 Bool do_copy_src)
578 if (do_free_dest)
580 fvwmlib_free_colors(dpy, dst_color, 1, True);
582 if (do_copy_src)
584 *dst_color = fvwmlib_clone_color(*src_color);