e454f97acc
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@7903 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
475 lines
15 KiB
C++
475 lines
15 KiB
C++
//
|
|
// "$Id$"
|
|
//
|
|
// Color functions for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// Copyright 1998-2010 by Bill Spitzak and others.
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Library General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library 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
|
|
// Library General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Library General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
// USA.
|
|
//
|
|
// Please report all bugs and problems on the following page:
|
|
//
|
|
// http://www.fltk.org/str.php
|
|
//
|
|
|
|
/**
|
|
\file fl_color.cxx
|
|
\brief Color handling
|
|
*/
|
|
|
|
// Implementation of fl_color(i), fl_color(r,g,b).
|
|
|
|
#ifdef WIN32
|
|
# include "fl_color_win32.cxx"
|
|
#elif defined(__APPLE__)
|
|
# include "fl_color_mac.cxx"
|
|
#else
|
|
|
|
// Also code to look at the X visual and figure out the best way to turn
|
|
// a color into a pixel value.
|
|
|
|
// SGI compiler seems to have problems with unsigned char arguments
|
|
// being used to index arrays. So I always copy them to an integer
|
|
// before use.
|
|
|
|
# include "Fl_XColor.H"
|
|
# include <FL/Fl.H>
|
|
# include <FL/x.H>
|
|
# include <FL/fl_draw.H>
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// figure_out_visual() calculates masks & shifts for generating
|
|
// pixels in true-color visuals:
|
|
|
|
uchar fl_redmask; /**< color mask used in current color map handling */
|
|
uchar fl_greenmask; /**< color mask used in current color map handling */
|
|
uchar fl_bluemask; /**< color mask used in current color map handling */
|
|
|
|
int fl_redshift; /**< color shift used in current color map handling */
|
|
int fl_greenshift; /**< color shift used in current color map handling */
|
|
int fl_blueshift; /**< color shift used in current color map handling */
|
|
int fl_extrashift; /**< color shift used in current color map handling */
|
|
|
|
static uchar beenhere;
|
|
|
|
static void figure_out_visual() {
|
|
beenhere = 1;
|
|
if (!fl_visual->red_mask || !fl_visual->green_mask || !fl_visual->blue_mask){
|
|
# if USE_COLORMAP
|
|
fl_redmask = 0;
|
|
return;
|
|
# else
|
|
Fl::fatal("Requires true color visual");
|
|
# endif
|
|
}
|
|
|
|
// get the bit masks into a more useful form:
|
|
int i,j,m;
|
|
|
|
for (i = 0, m = 1; m; i++, m<<=1) if (fl_visual->red_mask & m) break;
|
|
for (j = i; m; j++, m<<=1) if (!(fl_visual->red_mask & m)) break;
|
|
fl_redshift = j-8;
|
|
fl_redmask = (j-i >= 8) ? 0xFF : 0xFF-(255>>(j-i));
|
|
|
|
for (i = 0, m = 1; m; i++, m<<=1) if (fl_visual->green_mask & m) break;
|
|
for (j = i; m; j++, m<<=1) if (!(fl_visual->green_mask & m)) break;
|
|
fl_greenshift = j-8;
|
|
fl_greenmask = (j-i >= 8) ? 0xFF : 0xFF-(255>>(j-i));
|
|
|
|
for (i = 0, m = 1; m; i++, m<<=1) if (fl_visual->blue_mask & m) break;
|
|
for (j = i; m; j++, m<<=1) if (!(fl_visual->blue_mask & m)) break;
|
|
fl_blueshift = j-8;
|
|
fl_bluemask = (j-i >= 8) ? 0xFF : 0xFF-(255>>(j-i));
|
|
|
|
i = fl_redshift;
|
|
if (fl_greenshift < i) i = fl_greenshift;
|
|
if (fl_blueshift < i) i = fl_blueshift;
|
|
if (i < 0) {
|
|
fl_extrashift = -i;
|
|
fl_redshift -= i; fl_greenshift -= i; fl_blueshift -= i;
|
|
} else
|
|
fl_extrashift = 0;
|
|
|
|
}
|
|
|
|
static unsigned fl_cmap[256] = {
|
|
#include "fl_cmap.h" // this is a file produced by "cmap.cxx":
|
|
};
|
|
|
|
# if HAVE_OVERLAY
|
|
/** HAVE_OVERLAY determines whether fl_xmap is one or two planes */
|
|
Fl_XColor fl_xmap[2][256];
|
|
/** HAVE_OVERLAY determines whether fl_overlay is variable or defined as 0 */
|
|
uchar fl_overlay;
|
|
Colormap fl_overlay_colormap;
|
|
XVisualInfo* fl_overlay_visual;
|
|
ulong fl_transparent_pixel;
|
|
# else
|
|
/** HAVE_OVERLAY determines whether fl_xmap is one or two planes */
|
|
Fl_XColor fl_xmap[1][256];
|
|
/** HAVE_OVERLAY determines whether fl_overlay is variable or defined as 0 */
|
|
# define fl_overlay 0
|
|
# endif
|
|
|
|
/** Current color for drawing operations */
|
|
Fl_Color fl_color_;
|
|
|
|
void Fl_Graphics_Driver::color(Fl_Color i) {
|
|
if (i & 0xffffff00) {
|
|
unsigned rgb = (unsigned)i;
|
|
fl_color((uchar)(rgb >> 24), (uchar)(rgb >> 16), (uchar)(rgb >> 8));
|
|
} else {
|
|
fl_color_ = i;
|
|
if(!fl_gc) return; // don't get a default gc if current window is not yet created/valid
|
|
XSetForeground(fl_display, fl_gc, fl_xpixel(i));
|
|
}
|
|
}
|
|
|
|
void Fl_Graphics_Driver::color(uchar r,uchar g,uchar b) {
|
|
fl_color_ = fl_rgb_color(r, g, b);
|
|
if(!fl_gc) return; // don't get a default gc if current window is not yet created/valid
|
|
XSetForeground(fl_display, fl_gc, fl_xpixel(r,g,b));
|
|
}
|
|
|
|
/** \addtogroup fl_attributes
|
|
@{ */
|
|
////////////////////////////////////////////////////////////////
|
|
// Get an rgb color. This is easy for a truecolor visual. For
|
|
// colormapped it picks the closest color out of the cube in the
|
|
// fltk colormap. However if this color cube entry has been
|
|
// requested before, you will get the earlier requested color, and
|
|
// even this may be approximated if the X colormap was full.
|
|
|
|
/**
|
|
Returns the X pixel number used to draw the given rgb color.
|
|
This is the X pixel that fl_color() would use.
|
|
\param[in] r,g,b color components
|
|
\return X pixel number
|
|
*/
|
|
ulong fl_xpixel(uchar r,uchar g,uchar b) {
|
|
if (!beenhere) figure_out_visual();
|
|
# if USE_COLORMAP
|
|
if (!fl_redmask) {
|
|
// find closest entry in the colormap:
|
|
Fl_Color i =
|
|
fl_color_cube(r*FL_NUM_RED/256,g*FL_NUM_GREEN/256,b*FL_NUM_BLUE/256);
|
|
Fl_XColor &xmap = fl_xmap[fl_overlay][i];
|
|
if (xmap.mapped) return xmap.pixel;
|
|
// if not black or white, change the entry to be an exact match:
|
|
if (i != FL_COLOR_CUBE && i != 0xFF)
|
|
fl_cmap[i] = (r<<24)|(g<<16)|(b<<8);
|
|
return fl_xpixel(i); // allocate an X color
|
|
}
|
|
# endif
|
|
return
|
|
(((r&fl_redmask) << fl_redshift)+
|
|
((g&fl_greenmask)<<fl_greenshift)+
|
|
((b&fl_bluemask)<< fl_blueshift)
|
|
) >> fl_extrashift;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Get a color out of the fltk colormap. Again for truecolor
|
|
// visuals this is easy. For colormap this actually tries to allocate
|
|
// an X color, and does a least-squares match to find the closest
|
|
// color if X cannot allocate that color.
|
|
|
|
// calculate what color is actually on the screen for a mask:
|
|
static inline uchar realcolor(uchar color, uchar mask) {
|
|
# if 0
|
|
// accurate version if the display has linear gamma, but fl_draw_image
|
|
// works better with the simpler version on most screens...
|
|
uchar m = mask;
|
|
uchar result = color&m;
|
|
for (;;) {
|
|
while (m&mask) {m>>=1; color>>=1;}
|
|
if (!m) break;
|
|
mask = m;
|
|
result |= color&m;
|
|
}
|
|
return result;
|
|
# else
|
|
return (color&mask) | ( (~mask)&(mask>>1) );
|
|
# endif
|
|
}
|
|
|
|
/**
|
|
Returns the X pixel number used to draw the given FLTK color index.
|
|
This is the X pixel that fl_color() would use.
|
|
\param[in] i color index
|
|
\return X pixel number
|
|
*/
|
|
ulong fl_xpixel(Fl_Color i) {
|
|
if (i & 0xffffff00) {
|
|
return fl_xpixel((i >> 24) & 255, (i >> 16) & 255, (i >> 8) & 255);
|
|
}
|
|
|
|
Fl_XColor &xmap = fl_xmap[fl_overlay][i];
|
|
if (xmap.mapped) return xmap.pixel;
|
|
|
|
if (!beenhere) figure_out_visual();
|
|
|
|
uchar r,g,b;
|
|
{unsigned c = fl_cmap[i]; r=uchar(c>>24); g=uchar(c>>16); b=uchar(c>>8);}
|
|
|
|
# if USE_COLORMAP
|
|
Colormap colormap = fl_colormap;
|
|
# if HAVE_OVERLAY
|
|
if (fl_overlay) colormap = fl_overlay_colormap; else
|
|
# endif
|
|
if (fl_redmask) {
|
|
# endif
|
|
// return color for a truecolor visual:
|
|
xmap.mapped = 2; // 2 prevents XFreeColor from being called
|
|
xmap.r = realcolor(r, fl_redmask);
|
|
xmap.g = realcolor(g, fl_greenmask);
|
|
xmap.b = realcolor(b, fl_bluemask);
|
|
return xmap.pixel =
|
|
(((r&fl_redmask) << fl_redshift)+
|
|
((g&fl_greenmask)<<fl_greenshift)+
|
|
((b&fl_bluemask)<< fl_blueshift)
|
|
) >> fl_extrashift;
|
|
# if USE_COLORMAP
|
|
}
|
|
# if HAVE_OVERLAY
|
|
static XColor* ac[2];
|
|
XColor*& allcolors = ac[fl_overlay];
|
|
static int nc[2];
|
|
int& numcolors = nc[fl_overlay];
|
|
# else
|
|
static XColor *allcolors;
|
|
static int numcolors;
|
|
# endif
|
|
|
|
// I don't try to allocate colors with XAllocColor once it fails
|
|
// with any color. It is possible that it will work, since a color
|
|
// may have been freed, but some servers are extremely slow and this
|
|
// avoids one round trip:
|
|
if (!numcolors) { // don't try after a failure
|
|
XColor xcol;
|
|
xcol.red = r<<8; xcol.green = g<<8; xcol.blue = b<<8;
|
|
if (XAllocColor(fl_display, colormap, &xcol)) {
|
|
xmap.mapped = 1;
|
|
xmap.r = xcol.red>>8;
|
|
xmap.g = xcol.green>>8;
|
|
xmap.b = xcol.blue>>8;
|
|
return xmap.pixel = xcol.pixel;
|
|
}
|
|
|
|
// I only read the colormap once. Again this is due to the slowness
|
|
// of round-trips to the X server, even though other programs may alter
|
|
// the colormap after this and make decisions here wrong.
|
|
# if HAVE_OVERLAY
|
|
if (fl_overlay) numcolors = fl_overlay_visual->colormap_size; else
|
|
# endif
|
|
numcolors = fl_visual->colormap_size;
|
|
if (!allcolors) allcolors = new XColor[numcolors];
|
|
for (int p = numcolors; p--;) allcolors[p].pixel = p;
|
|
XQueryColors(fl_display, colormap, allcolors, numcolors);
|
|
}
|
|
|
|
// find least-squares match:
|
|
int mindist = 0x7FFFFFFF;
|
|
unsigned int bestmatch = 0;
|
|
for (unsigned int n = numcolors; n--;) {
|
|
# if HAVE_OVERLAY
|
|
if (fl_overlay && n == fl_transparent_pixel) continue;
|
|
# endif
|
|
XColor &a = allcolors[n];
|
|
int d, t;
|
|
t = int(r)-int(a.red>>8); d = t*t;
|
|
t = int(g)-int(a.green>>8); d += t*t;
|
|
t = int(b)-int(a.blue>>8); d += t*t;
|
|
if (d <= mindist) {bestmatch = n; mindist = d;}
|
|
}
|
|
XColor &p = allcolors[bestmatch];
|
|
|
|
// It appears to "work" to not call this XAllocColor, which will
|
|
// avoid another round-trip to the server. But then X does not
|
|
// know that this program "owns" this value, and can (and will)
|
|
// change it when the program that did allocate it exits:
|
|
if (XAllocColor(fl_display, colormap, &p)) {
|
|
xmap.mapped = 1;
|
|
xmap.pixel = p.pixel;
|
|
} else {
|
|
// However, if that XAllocColor fails, I have to give up and
|
|
// assume the pixel is ok for the duration of the program. This
|
|
// is due to bugs (?) in the Solaris X and some X terminals
|
|
// where XAllocColor *always* fails when the colormap is full,
|
|
// even if we ask for a color already in it...
|
|
xmap.mapped = 2; // 2 prevents XFreeColor from being called
|
|
xmap.pixel = bestmatch;
|
|
}
|
|
xmap.r = p.red>>8;
|
|
xmap.g = p.green>>8;
|
|
xmap.b = p.blue>>8;
|
|
return xmap.pixel;
|
|
# endif
|
|
}
|
|
|
|
/**
|
|
Free color \p i if used, and clear mapping table entry.
|
|
\param[in] i color index
|
|
\param[in] overlay 0 for normal, 1 for overlay color
|
|
*/
|
|
void Fl::free_color(Fl_Color i, int overlay) {
|
|
# if HAVE_OVERLAY
|
|
# else
|
|
if (overlay) return;
|
|
# endif
|
|
if (fl_xmap[overlay][i].mapped) {
|
|
# if USE_COLORMAP
|
|
# if HAVE_OVERLAY
|
|
Colormap colormap = overlay ? fl_overlay_colormap : fl_colormap;
|
|
# else
|
|
Colormap colormap = fl_colormap;
|
|
# endif
|
|
if (fl_xmap[overlay][i].mapped == 1)
|
|
XFreeColors(fl_display, colormap, &(fl_xmap[overlay][i].pixel), 1, 0);
|
|
# endif
|
|
fl_xmap[overlay][i].mapped = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Set color mapping table entry \p i to color \p c
|
|
\param[in] i color index
|
|
\param[in] c color
|
|
*/
|
|
void Fl::set_color(Fl_Color i, unsigned c) {
|
|
if (fl_cmap[i] != c) {
|
|
free_color(i,0);
|
|
# if HAVE_OVERLAY
|
|
free_color(i,1);
|
|
# endif
|
|
fl_cmap[i] = c;
|
|
}
|
|
}
|
|
|
|
#endif // end of X-specific code
|
|
/**
|
|
Returns the RGB value(s) for the given FLTK color index.
|
|
|
|
This form returns the RGB values packed in a 32-bit unsigned
|
|
integer with the red value in the upper 8 bits, the green value
|
|
in the next 8 bits, and the blue value in bits 8-15. The lower
|
|
8 bits will always be 0.
|
|
*/
|
|
unsigned Fl::get_color(Fl_Color i) {
|
|
if (i & 0xffffff00) return (i);
|
|
else return fl_cmap[i];
|
|
}
|
|
/**
|
|
Sets an entry in the fl_color index table. You can set it to
|
|
any 8-bit RGB color. The color is not allocated until fl_color(i)
|
|
is used.
|
|
*/
|
|
void Fl::set_color(Fl_Color i, uchar red, uchar green, uchar blue) {
|
|
Fl::set_color((Fl_Color)(i & 255),
|
|
((unsigned)red<<24)+((unsigned)green<<16)+((unsigned)blue<<8));
|
|
}
|
|
/**
|
|
Returns the RGB value(s) for the given FLTK color index.
|
|
|
|
This form returns the red, green, and blue values
|
|
separately in referenced variables.
|
|
|
|
See also unsigned get_color(Fl_Color c)
|
|
*/
|
|
void Fl::get_color(Fl_Color i, uchar &red, uchar &green, uchar &blue) {
|
|
unsigned c;
|
|
|
|
if (i & 0xffffff00) c = (unsigned)i;
|
|
else c = fl_cmap[i];
|
|
|
|
red = uchar(c>>24);
|
|
green = uchar(c>>16);
|
|
blue = uchar(c>>8);
|
|
}
|
|
|
|
/**
|
|
Returns the weighted average color between the two given colors.
|
|
The red, green and blue values are averages using the following formula:
|
|
\code
|
|
color = color1 * weight + color2 * (1 - weight)
|
|
\endcode
|
|
Thus, a \p weight value of 1.0 will return the first color, while a
|
|
value of 0.0 will return the second color.
|
|
\param[in] color1, color2 boundary colors
|
|
\param[in] weight weighting factor
|
|
*/
|
|
Fl_Color fl_color_average(Fl_Color color1, Fl_Color color2, float weight) {
|
|
unsigned rgb1;
|
|
unsigned rgb2;
|
|
uchar r, g, b;
|
|
|
|
if (color1 & 0xffffff00) rgb1 = color1;
|
|
else rgb1 = fl_cmap[color1 & 255];
|
|
|
|
if (color2 & 0xffffff00) rgb2 = color2;
|
|
else rgb2 = fl_cmap[color2 & 255];
|
|
|
|
r = (uchar)(((uchar)(rgb1>>24))*weight + ((uchar)(rgb2>>24))*(1-weight));
|
|
g = (uchar)(((uchar)(rgb1>>16))*weight + ((uchar)(rgb2>>16))*(1-weight));
|
|
b = (uchar)(((uchar)(rgb1>>8))*weight + ((uchar)(rgb2>>8))*(1-weight));
|
|
|
|
return fl_rgb_color(r, g, b);
|
|
}
|
|
|
|
/**
|
|
Returns the inactive, dimmed version of the given color
|
|
*/
|
|
Fl_Color fl_inactive(Fl_Color c) {
|
|
return fl_color_average(c, FL_GRAY, .33f);
|
|
}
|
|
|
|
/**
|
|
Returns a color that contrasts with the background color.
|
|
This will be the foreground color if it contrasts sufficiently with the
|
|
background color. Otherwise, returns \p FL_WHITE or \p FL_BLACK depending
|
|
on which color provides the best contrast.
|
|
\param[in] fg,bg foreground and background colors
|
|
\return contrasting color
|
|
*/
|
|
Fl_Color fl_contrast(Fl_Color fg, Fl_Color bg) {
|
|
unsigned c1, c2; // RGB colors
|
|
int l1, l2; // Luminosities
|
|
|
|
|
|
// Get the RGB values for each color...
|
|
if (fg & 0xffffff00) c1 = (unsigned)fg;
|
|
else c1 = fl_cmap[fg];
|
|
|
|
if (bg & 0xffffff00) c2 = (unsigned)bg;
|
|
else c2 = fl_cmap[bg];
|
|
|
|
// Compute the luminosity...
|
|
l1 = ((c1 >> 24) * 30 + ((c1 >> 16) & 255) * 59 + ((c1 >> 8) & 255) * 11) / 100;
|
|
l2 = ((c2 >> 24) * 30 + ((c2 >> 16) & 255) * 59 + ((c2 >> 8) & 255) * 11) / 100;
|
|
|
|
// Compare and return the contrasting color...
|
|
if ((l1 - l2) > 99) return fg;
|
|
else if ((l2 - l1) > 99) return fg;
|
|
else if (l2 > 127) return FL_BLACK;
|
|
else return FL_WHITE;
|
|
}
|
|
/**
|
|
@}
|
|
*/
|
|
//
|
|
// End of "$Id$".
|
|
//
|