/** * \file ColorHandler.C * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author unknown * * Full author contact details are available in file CREDITS. */ #include #include "ColorHandler.h" #include "debug.h" #include "gettext.h" #include "LColor.h" #include "support/lstrings.h" #include "support/tostr.h" #include "lyx_forms.h" #include #include #ifndef CXX_GLOBAL_CSTD using std::pow; #endif using std::endl; using std::string; namespace lyx { namespace support { namespace { string tostr(XColor const & col) { return bformat("(%1$s,%2$s,%3$s)", ::tostr(col.red), ::tostr(col.green), ::tostr(col.blue)); } } // namespace } // namespace support using support::bformat; using support::tostr; namespace frontend { LyXColorHandler::LyXColorHandler() : colorGCcache(LColor::ignore + 1) { display = fl_get_display(); drawable = XCreatePixmap(display, RootWindow(display, fl_screen), 10, 10, fl_get_visual_depth()); colormap = fl_state[fl_get_vclass()].colormap; // Clear the GC cache for (string::size_type i = 0; i < colorGCcache.size(); ++i) { colorGCcache[i] = 0; } } LyXColorHandler::~LyXColorHandler() { // Release all the registered GCs for (unsigned i = 0; i < colorGCcache.size(); ++i) { if (colorGCcache[i] != 0) { XFreeGC(display, colorGCcache[i]); } } // Iterate over the line cache and Free the GCs for (LineGCCache::iterator lit = lineGCcache.begin(); lit != lineGCcache.end(); ++lit) { XFreeGC(display, lit->second); } } unsigned long LyXColorHandler::colorPixel(LColor_color c) { XGCValues val; XGetGCValues(display, getGCForeground(c), GCForeground, &val); return val.foreground; } GC LyXColorHandler::getGCForeground(string const & s) { XColor xcol; XColor ccol; XGCValues val; // Look up the RGB values for the color, and an approximate // color that we can hope to get on this display. if (XLookupColor(display, colormap, s.c_str(), &xcol, &ccol) == 0) { lyxerr << bformat( _("LyX: Unknown X11 color %1$s\n" " Using black instead, sorry!"), s) << endl; unsigned long bla = BlackPixel(display, DefaultScreen(display)); val.foreground = bla; // Try the exact RGB values first, then the approximate. } else if (XAllocColor(display, colormap, &xcol) != 0) { if (lyxerr.debugging(Debug::GUI)) { lyxerr << bformat(_("LyX: X11 color %1$s allocated"), s) << endl; } val.foreground = xcol.pixel; } else { // Here we are traversing the current colormap to find // the color closest to the one we want. Visual * vi = DefaultVisual(display, DefaultScreen(display)); boost::scoped_array cmap(new XColor[vi->map_entries]); for (int i = 0; i < vi->map_entries; ++i) { cmap[i].pixel = i; } XQueryColors(display, colormap, cmap.get(), vi->map_entries); // Walk through the cmap and look for close colors. int closest_pixel = 0; double closest_distance = 1e20; // we want to minimize this double distance = 0; for (int t = 0; t < vi->map_entries; ++t) { // The Euclidean distance between two points in // a three-dimensional space, the RGB color-cube, // is used as the distance measurement between two // colors. // Since square-root is monotonous, we don't have to // take the square-root to find the minimum, and thus // we use the squared distance instead to be faster. // If we want to get fancy, we could convert the RGB // coordinates to a different color-cube, maybe HSV, // but the RGB cube seems to work great. (Asger) distance = pow(cmap[t].red - xcol.red, 2.0) + pow(cmap[t].green - xcol.green, 2.0) + pow(cmap[t].blue - xcol.blue, 2.0); if (distance < closest_distance) { closest_distance = distance; closest_pixel = t; } } lyxerr << bformat( _("LyX: Couldn't allocate '%1$s' with (r,g,b)=%3$s.\n"), s, tostr(xcol)); lyxerr << bformat( _(" Using closest allocated color with (r,g,b)=%1$s instead.\n" "Pixel [%2$s] is used."), tostr(cmap[closest_pixel]), ::tostr(closest_pixel)) << endl; val.foreground = cmap[closest_pixel].pixel; } val.function = GXcopy; return XCreateGC(display, drawable, GCForeground | GCFunction, &val); } // Gets GC according to color // Uses caching GC LyXColorHandler::getGCForeground(LColor_color c) { if (static_cast(c) >= colorGCcache.size()) { colorGCcache.resize(c + 1, 0); } if (colorGCcache[c] != 0) { return colorGCcache[c]; } XColor xcol; XColor ccol; string const s = lcolor.getX11Name(c); // Look up the RGB values for the color, and an approximate // color that we can hope to get on this display. if (XLookupColor(display, colormap, s.c_str(), &xcol, &ccol) == 0) { lyxerr << bformat( _("LyX: Unknown X11 color %1$s for %2$s\n"), s, lcolor.getGUIName(c)) << endl; } return colorGCcache[c] = getGCForeground(s); } // Gets GC for line GC LyXColorHandler::getGCLinepars(Painter::line_style ls, Painter::line_width lw, LColor_color c) { //if (lyxerr.debugging()) { // lyxerr << "Painter drawable: " << drawable() << endl; //} int index = lw + (ls << 1) + (c << 6); LineGCCache::iterator it = lineGCcache.find(index); if (it != lineGCcache.end()) return it->second; XGCValues val; XGetGCValues(display, getGCForeground(c), GCForeground, &val); switch (lw) { case Painter::line_thin: val.line_width = 0; break; case Painter::line_thick: val.line_width = 2; break; } switch (ls) { case Painter::line_solid: val.line_style = LineSolid; break; case Painter::line_onoffdash: val.line_style = LineOnOffDash; break; } val.cap_style = CapRound; val.join_style = JoinRound; val.function = GXcopy; return lineGCcache[index] = XCreateGC(display, drawable, GCForeground | GCLineStyle | GCLineWidth | GCCapStyle | GCJoinStyle | GCFunction, &val); } // update GC cache after color redefinition void LyXColorHandler::updateColor (LColor_color c) { // color GC cache GC gc = colorGCcache[c]; if (gc != 0) { XFreeGC(display, gc); colorGCcache[c] = NULL; getGCForeground(c); } // line GC cache for (int ls = 0; ls < 3; ++ls) for (int lw = 0; lw < 2; ++lw) { int const index = lw + (ls << 1) + (c << 6); LineGCCache::iterator it = lineGCcache.find(index); if (it != lineGCcache.end()) { gc = it->second; XFreeGC(display, gc); lineGCcache.erase(it); getGCLinepars(Painter::line_style(ls), Painter::line_width(lw), c); } } } // boost::scoped_ptr lyxColorHandler; } // namespace frontend } // namespace lyx