xenocara/app/fvwm/libs/ColorUtils.c

262 lines
7.1 KiB
C

/*
* The following GPL code from scwm implements a good, fast 3D-shadowing
* algorithm. It converts the color from RGB to HLS space, then
* multiplies both the luminosity and the saturation by a specified
* factor (clipping at the extremes). Then it converts back to RGB and
* creates a color. The guts of it, i.e. the `color_mult' routine, looks
* a bit longish, but this is only because there are 6-way conditionals
* at the begining and end; it actually runs quite fast. The algorithm is
* the same as Gtk's, but the implemenation is independent and more
* streamlined.
*
* Calling `adjust_pixel_brightness' with a `factor' of 1.3 for hilights
* and 0.7 for shadows exactly emulates Gtk's shadowing, which is, IMO
* the most visually pleasing shadowing of any widget set; using 1.2 and
* 0.5 respectively gives something closer to the "classic" fvwm effect
* with deeper shadows and more subtle hilights, but still (IMO) smoother
* and more attractive than fvwm.
*
* The only color these routines do not usefully handle is black; black
* will be returned even for a factor greater than 1.0, when optimally
* one would like to see a very dark gray. This could possibly be
* addressed by adding a small additive factor when brightening
* colors. If anyone adds that feature, please feed it upstream to me.
*
* Feel free to use this code in fvwm2, of course.
*
* - Maciej Stachowiak
*
* And, of course, history shows, we took him up on the offer.
* Integrated into fvwm2 by Dan Espen, 11/13/98.
*/
/*
* Copyright (C) 1997, 1998, Maciej Stachowiak and Greg J. Badros
*
* 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, 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 software; see the file COPYING.GPL. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*
*/
#include "config.h" /* must be first */
#include <stdio.h>
#include <X11/Xproto.h> /* for X functions in general */
#include "fvwmlib.h" /* prototype GetShadow GetHilit */
#define SCALE 65535.0
#define HALF_SCALE (SCALE / 2)
typedef enum {
R_MAX_G_MIN, R_MAX_B_MIN,
G_MAX_B_MIN, G_MAX_R_MIN,
B_MAX_R_MIN, B_MAX_G_MIN
} MinMaxState;
/* Multiply the HLS-space lightness and saturation of the color by the
given multiple, k - based on the way gtk does shading, but independently
coded. Should give better relief colors for many cases than the old
fvwm algorithm. */
/* FIXMS: This can probably be optimized more, examine later. */
static void
color_mult (unsigned short *red,
unsigned short *green,
unsigned short *blue, double k)
{
if (*red == *green && *red == *blue) {
double temp;
/* A shade of gray */
temp = k * (double) (*red);
if (temp > SCALE) {
temp = SCALE;
}
*red = (unsigned short)(temp);
*green = *red;
*blue = *red;
} else {
/* Non-zero saturation */
double r, g, b;
double min, max;
double a, l, s;
double delta;
double middle;
MinMaxState min_max_state;
r = (double) *red;
g = (double) *green;
b = (double) *blue;
if (r > g) {
if (r > b) {
max = r;
if (g < b) {
min = g;
min_max_state = R_MAX_G_MIN;
a = b - g;
} else {
min = b;
min_max_state = R_MAX_B_MIN;
a = g - b;
}
} else {
max = b;
min = g;
min_max_state = B_MAX_G_MIN;
a = r - g;
}
} else {
if (g > b) {
max = g;
if (b < r) {
min = b;
min_max_state = G_MAX_B_MIN;
a = r - b;
} else {
min = r;
min_max_state = G_MAX_R_MIN;
a = b - r;
}
} else {
max = b;
min = r;
min_max_state = B_MAX_R_MIN;
a = g - r;
}
}
delta = max - min;
a = a / delta;
l = (max + min) / 2;
if (l <= HALF_SCALE) {
s = max + min;
} else {
s = 2.0 * SCALE - (max + min);
}
s = delta/s;
l *= k;
if (l > SCALE) {
l = SCALE;
}
s *= k;
if (s > 1.0) {
s = 1.0;
}
if (l <= HALF_SCALE) {
max = l * (1 + s);
} else {
max = s * SCALE + l - s * l;
}
min = 2 * l - max;
delta = max - min;
middle = min + delta * a;
switch (min_max_state) {
case R_MAX_G_MIN:
r = max;
g = min;
b = middle;
break;
case R_MAX_B_MIN:
r = max;
g = middle;
b = min;
break;
case G_MAX_B_MIN:
r = middle;
g = max;
b = min;
break;
case G_MAX_R_MIN:
r = min;
g = max;
b = middle;
break;
case B_MAX_G_MIN:
r = middle;
g = min;
b = max;
break;
case B_MAX_R_MIN:
r = min;
g = middle;
b = max;
break;
}
*red = (unsigned short) r;
*green = (unsigned short) g;
*blue = (unsigned short) b;
}
}
/*
* This routine uses PictureSaveDisplay and PictureCMap which must be
* created by InitPictureCMAP in Picture.c.
*
* If you attempt to use GetShadow and GetHilit, make sure your module
* calls InitPictureCMAP first.
*/
static Pixel
adjust_pixel_brightness(Pixel pixel, double factor)
{
extern Colormap PictureCMap;
extern Display *PictureSaveDisplay;
XColor c;
c.pixel = pixel;
XQueryColor (PictureSaveDisplay, PictureCMap, &c);
color_mult(&c.red, &c.green, &c.blue, factor);
XAllocColor (PictureSaveDisplay, PictureCMap, &c);
return c.pixel;
}
/*
* These are the original fvwm2 APIs, one for highlights and one for
* shadows. Together, if used in a frame around a rectangle, they
* produce a 3d appearance.
*
* The input pixel, is normally the background color used in the
* rectangle. One would hope, when the user selects to color something
* with a multi-color pixmap, they will have the insight to also assign a
* background color to the pixmaped area that approximates the average
* color of the pixmap.
*
* Currently callers handle monochrome before calling this routine. The
* next logical enhancement is for that logic to be moved here. Probably
* a new API that deals with foreground/background/hilite/shadow
* allocation all in 1 call is the next logical extenstion.
*
* Color allocation is also a good candidate for becoming a library
* routine. The color allocation logic in FvwmButtons using the XPM
* library closeness stuff may be the ideal model.
* (dje 11/15/98)
*/
#define DARKNESS_FACTOR 0.5
Pixel GetShadow(Pixel background) {
return adjust_pixel_brightness(background, DARKNESS_FACTOR);
}
#define BRIGHTNESS_FACTOR 1.4
Pixel GetHilite(Pixel background) {
return adjust_pixel_brightness(background, BRIGHTNESS_FACTOR);
}