/***** * colors.c : XmHTML color allocation routines * * This file Version $Revision: 1.13 $ * * Creation date: Mon Dec 16 13:57:41 GMT+0100 1996 * Last modification: $Date: 1999/07/29 01:26:28 $ * By: $Author: sopwith $ * Current State: $State: Exp $ * * Author: newt * (C)Copyright 1995-1996 Ripley Software Development * All Rights Reserved * * This file is part of the XmHTML Widget Library. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * *****/ /***** * ChangeLog * $Log: colors.c,v $ * Revision 1.13 1999/07/29 01:26:28 sopwith * * * Fix all warnings. * * Revision 1.12 1998/05/25 02:36:33 sopwith * * * libgnomeui/gnome-dialog.[ch]: * Fix gnome_dialog_run_modal() to do the "sane thing" (while incorporating * Havoc's bug fixes): * - Havoc, it is plausible that the programmer might want to * get back a "the user did not press ANY button". Let them handle it, * it's more flexible. Also removed other various overkills. * * I think there may still be a possible bug in using gtk_main_quit as a * delete_event handler - perhaps need a gnome_dialog_delete_event_handler() * that just calls gtk_main_quit() and returns FALSE (so the dialog doesn't * get auto-destroyed). * * The other changes, I might wind up reverting because, while my tree * compiles, I have no idea what the xmhtml change is, sorry :( * * Revision 1.11 1998/02/12 03:08:30 unammx * Merge to Koen's XmHTML 1.1.2 + following fixes: * * Wed Feb 11 20:27:19 1998 Miguel de Icaza * * * gtk-forms.c (freeForm): gtk_destroy_widget is no longer needed * with the refcounting changes. * * * gtk-xmhtml.c (gtk_xmhtml_remove): Only god knows why I was * adding the just removed widget. * * Revision 1.10 1998/01/31 00:12:06 unammx * Thu Jan 29 12:17:07 1998 Federico Mena * * * gtk-xmhtml.c (wrap_gdk_cc_get_pixels): Added wrapper function * for gdk_color_context_get_pixels{_incremental}(). This function * will first upscale the color information to 16 bits. This * function can be removed as described next. * * * XCC.c: I defined a USE_EIGHT_BIT_CHANNELS macro that makes the * GetPixel functions expect color data to be in [0, 255]. Two * macros, UPSCALE() and DOWNSCALE(), are used in those functions. * When XmHTML is modified to use 16-bit color information, these * macros and the #ifdef parts can be safely removed, as the * functions already operate with 16-bit colors internally. * * * colors.c (XmHTMLAllocColor): Made this function use 16-bit * values for color matching. * * * toolkit.h (XCCGetPixelsIncremental): Removed un-needed do{}while(0) * * * XCC.c (XCCGetPixel): _red/_green/_blue parameters are now * expected to be in [0, 65535]. This is to be friendlier to the Gdk * port of the XCC. * (XCCGetPixels): Made it use 16-bit color values as well. Fixed * mdist=1000000 buglet (it should start with at least 0x1000000). * (XCCGetPixelsIncremental): Same as for XCCGetPixels(). * * Revision 1.9 1998/01/15 01:40:58 unammx * Comment fixes - Federico * * Revision 1.8 1998/01/15 01:34:03 unammx * Wed Jan 14 19:28:01 1998 Federico Mena * * * colors.c (my_get_colors): Now we allocate the colors, just as * Motif does. I think they should be freed sometime, but they are * not. Lesstif does not free them, either. I don't know if OSF * Motif ever frees these colors... * * Revision 1.7 1998/01/14 04:11:48 unammx * Tue Jan 13 22:04:43 1998 Federico Mena * * * Lots of changes all over the place to fix colors. Things are * *almost* working right now. I think I'm only missing setting the * window backgrounds appropriately. Several things were done: * * - Motif's color and gc fields from Core and XmManager were * replicated inside the GtkXmHTML widget structure. * * - Macros were created in toolkit.h to use these fields. * * - Instead of the old kludgy set_{fore,back}ground_internal * functions, we now set the window background directly. * This does not work perfectly; I'll look into it. * * - I created a shade_color() function in colors.c (ok, ok, * I stole it from gtkstyle.c) which mimics XmGetColors() * -- it calculates shaded colors for the 3D look. * * I hope to fix the remaining problems with window backgrounds real * soon now. * * Revision 1.6 1998/01/10 02:27:02 unammx * First attempt at fixing the RecomputeColors functions. They are still * not perfect, as scrollbar colors are affected, too. - Federico * * Revision 1.5 1998/01/07 01:45:36 unammx * Gtk/XmHTML is ready to be used by the Gnome hackers now! * Weeeeeee! * * This afternoon: * * - Changes to integrate gtk-xmhtml into an autoconf setup. * * - Changes to make gtk-xmhtml a library to be used by Gnome * (simply include strdup * * Revision 1.9 1997/05/28 01:45:14 newt * Changes for the XmHTMLAllocColor and XmHTMLFreeColor functions. * * Revision 1.8 1997/04/29 14:24:51 newt * Fix in _XmHTMLFreeColors * * Revision 1.7 1997/04/03 05:33:37 newt * _XmHTMLGetPixelByName is much more robuster and fault tolerant * (patch by Dick Ported, dick@cymru.net) * * Revision 1.6 1997/03/20 08:08:18 newt * fixed a few bugs in BestPixel and _XmHTMLFreeColors * * Revision 1.5 1997/03/02 23:15:32 newt * some obscure free() changes * * Revision 1.4 1997/02/11 02:06:32 newt * Bugfixes related to color releasing. * * Revision 1.3 1997/01/09 06:55:20 newt * expanded copyright marker * * Revision 1.2 1997/01/09 06:43:42 newt * small fix on ConfirmColor32 * * Revision 1.1 1996/12/19 02:17:07 newt * Initial Revision * *****/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifndef WITH_MOTIF #include #endif #include "XmHTMLP.h" #include "XmHTMLfuncs.h" #ifdef WITH_MOTIF # include "XCCP.h" #endif /*** External Function Prototype Declarations ***/ /*** Public Variable Declarations ***/ /*** Private Datatype Declarations ****/ static struct{ String color; /* for both #rrggbb and named color specs */ TColor xcolor; int used; /* color usage counter */ }color_cache[256]; /*** Private Function Prototype Declarations ****/ static int CreateColormap(XmHTMLWidget html, TColor *cmap); /*** Private Variable Declarations ***/ static int last_color; static Boolean confirm_warning = True; /* HTML-3.2 color names */ static String html_32_color_names[16] = {"black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia", "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"}; /* corresponding HTML-3.2 sRGB values */ static String html_32_color_values[16] = {"#000000", "#c0c0c0", "#808080", "#ffffff", "#800000", "#ff0000", "#800080", "#ff00ff", "#008000", "#00ff00", "#808000", "#ffff00", "#000080", "#0000ff", "#008080", "#00ffff"}; /* for creating a 3/3/2 based palette */ #define RMASK 0xe0 #define RSHIFT 0 #define GMASK 0xe0 #define GSHIFT 3 #define BMASK 0xc0 #define BSHIFT 6 /* XXX: This function does an XQueryColors() the hard way, because there is * no corresponding function in Gdk. */ #ifndef WITH_MOTIF static void my_x_query_colors(GdkColormap *colormap, GdkColor *colors, gint ncolors) { XColor *xcolors; gint i; xcolors = g_new(XColor, ncolors); for (i = 0; i < ncolors; i++) xcolors[i].pixel = colors[i].pixel; XQueryColors(gdk_display, GDK_COLORMAP_XCOLORMAP(colormap), xcolors, ncolors); for (i = 0; i < ncolors; i++) { colors[i].red = xcolors[i].red; colors[i].green = xcolors[i].green; colors[i].blue = xcolors[i].blue; } g_free(xcolors); } #endif /***** * Name: tryColor * Return Type: Boolean * Description: verifies the validity of the given colorname and returns * the corresponding RGB components for the requested color. * In: * dpy: Display on which color should be allocated; * colormap: colormap in which color should be allocated; * color: name of color to allocate. Either symbolic or an RGB triplet; * *defs: TColor struct for allocated color. Filled upon return. * Returns: * True when color name is valid, False if not. * Note: * This routine tries to recover from incorrectly specified RGB triplets. * (not all six fields present). *****/ static Boolean tryColor(Display *dpy, TColormap colormap, String color, TColor *def) { /* * backup color for stupid html writers that don't use a leading hash. * The 000000 will ensure we have a full colorspec if the user didn't * specify the full symbolic color name (2 red, 2 green and 2 blue). */ char hash[]="#000000"; int i; /* first try original name */ if(!(Toolkit_Parse_Color (dpy, colormap, color, def))) { /* * Failed, see if we have a leading hash. This doesn't work with * symbolic color names. Too bad then. */ if(color[0] != '#') { /* * Only prepend the hash sign by setting the second char to NULL. * Can't initialize hash this way (above that is) since the literal * copy below won't work that way. Don't ask me why, it just won't * work. */ hash[1] = '\0'; strncat(hash, color, 6); } /* * Copy up to seven chars. This will make a valid color spec * even if the full triplet hasn't been specified. The strlen check * to prevent a buffer overflow. */ else /* literal copy so we get a valid triplet */ if(strlen(color) < 7) { for(i = 0; i < strlen(color); i++) hash[i] = color[i]; } else strncpy(hash, color, 7); /* NULL terminate */ hash[7] = '\0'; /* try again */ if(!(Toolkit_Parse_Color(dpy, colormap, hash, def))) return(False); } return(True); } /***** * Name: _XmHTMLGetPixelByName * Return Type: Pixel * Description: retrieves the pixel value for a color name. Color can be * given as a color name as well as a color value. * In: * display: display where color value should be retrieved from * color: the color to allocate. * def_pixel: default pixel to return if color allocation fails * Returns: * The pixel value closest to the requested color *****/ Pixel _XmHTMLGetPixelByName(XmHTMLWidget html, String color, Pixel def_pixel) { Display *dpy = Toolkit_Display((TWidget)html); TColor def; int i, slot = -1; unsigned short r[1], g[1], b[1]; TColormap colormap; Pixel pixel[1]; int success = False; /* REQUIRED */ /* sanity check */ if(!color || *color == '\0') return(def_pixel); /* see if we have an xcc for this widget */ _XmHTMLCheckXCC(html); colormap = Toolkit_Widget_Colormap (html); /***** * See if the named color has already been allocated. If so, we don't * allocate it again but return the already obtained pixel value. * This way we don't have to do an XAllocColor for each color we have * already allocated. *****/ for(i = 0 ; i < last_color; i++) { if(color_cache[i].used && !(strcmp(color_cache[i].color, color))) { _XmHTMLDebug(7, ("format.c: GetPixelFromName: color %s already " "allocated.\n", color)); color_cache[i].used++; return(color_cache[i].xcolor.pixel); } if(!color_cache[i].used) slot = i; } /* stack full */ if(last_color > 255 && slot == -1) return(def_pixel); /* no free slots */ if(slot == -1) slot = last_color; if((!tryColor(dpy, colormap, color, &def))) { Boolean again; /* turn of warnings */ confirm_warning = False; /* see if by chance it's one of the 16 appointed colors */ again = _XmHTMLConfirmColor32(color); /* turn on warnings */ confirm_warning = True; /* try it */ if(!again || !tryColor(dpy, colormap, color, &def)) { /* bad color spec, return */ _XmHTMLWarning(__WFUNC__(html, "_XmHTMLGetPixelByName"), "Bad color name %s", color); return(def_pixel); } } r[0] = def.red >> 8; /* downscale */ g[0] = def.green >> 8; b[0] = def.blue >> 8; pixel[0] = None; /* REQUIRED! */ /* try to allocate it */ XCCGetPixels(html->html.xcc, r, g, b, 1, pixel, &success); if(!success) { /* failed, return default pixel */ _XmHTMLWarning(__WFUNC__(html, "_XmHTMLGetPixelByName"), "XAllocColor failed for color %s", color); return(def_pixel); } def.pixel = pixel[0]; /* store in color stack. */ color_cache[slot].color = strdup(color); (void)memset((char*)&color_cache[slot].xcolor, 0, sizeof(TColor)); (void)memcpy((char*)&color_cache[slot].xcolor, (char*)&def, sizeof(TColor)); color_cache[slot].used++; /* don't forget to set colorstack depth */ if(slot == last_color) last_color++; return(def.pixel); } /***** * Name: _XmHTMLConfirmColor32 * Return Type: void * Description: converts the given named color to the corresponding sRGB value. * In: * color: color name to check * Returns: * nothing, but if a match is found, color is updated with the corresponding * sRGB value. * Note: * This routine is here for consistency. The standard HTML 3.2 colors are * referred to as the ``standard 16 color Windows VGA pallete''. This * uttermost *dumb*, *stupid* (you name it) pallete does not only contain an * absolute minimum of 16 colors, but in addition, most of the color names * used are unknown to almost all X servers! Can you imagine a greater m$ * ignorance!!!! Yuck. *****/ Boolean _XmHTMLConfirmColor32(char *color) { register int i; /* an sRGB spec, see if we know it */ if(color[0] == '#') { for(i = 0 ; i < 16; i++) { if(!strcasecmp(color, html_32_color_values[i])) return(True); } } else { for(i = 0 ; i < 16; i++) { if(!strcasecmp(color, html_32_color_names[i])) { color = realloc(color, strlen(html_32_color_values[i])); strcpy(color, html_32_color_values[i]); color[strlen(html_32_color_values[i])] = '\0'; return(True); } } } /* nope, don't know it. Use black */ if(confirm_warning) _XmHTMLWarning(__WFUNC__(NULL, "_XmHTMLConfirmColor32"), "HTML 3.2 color violation: color %s not known, ignoring.\n", color); return(False); } /***** * Name: _XmHTMLFreeColors * Return Type: void * Description: frees all colors we have allocated in the color cache * In: * w: XmHTMLWidget * Returns: * nothing * Note: * color releasing is done only if the usage counter of a certain color reaches * zero, in case no more widgets are using it and thus can be safely destroyed. *****/ void _XmHTMLFreeColors(XmHTMLWidget html) { int i, freed = 0; /* free all colors we have allocated */ for(i = 0 ; i < last_color; i++) { /* only decrease if this color is actively being used */ if(color_cache[i].used) color_cache[i].used--; /* only destroy if the usage counter of this color reaches zero */ if(color_cache[i].color && !color_cache[i].used) { free(color_cache[i].color); color_cache[i].color = NULL; freed++; } } /* no more colors in stack, reset depth */ if(freed == last_color) last_color = 0; /* * not required to reset color stack depth. first free slot allocation * is used. Stack growth direction is variable. */ } #ifndef WITH_MOTIF /* This color shading method is taken from gtkstyle.c. Some * modifications made by me - Federico */ static void rgb_to_hls (gdouble *r, gdouble *g, gdouble *b) { gdouble min; gdouble max; gdouble red; gdouble green; gdouble blue; gdouble h, l, s; gdouble delta; red = *r; green = *g; blue = *b; if (red > green) { if (red > blue) max = red; else max = blue; if (green < blue) min = green; else min = blue; } else { if (green > blue) max = green; else max = blue; if (red < blue) min = red; else min = blue; } l = (max + min) / 2; s = 0; h = 0; if (max != min) { if (l <= 0.5) s = (max - min) / (max + min); else s = (max - min) / (2 - max - min); delta = max -min; if (red == max) h = (green - blue) / delta; else if (green == max) h = 2 + (blue - red) / delta; else if (blue == max) h = 4 + (red - green) / delta; h *= 60; if (h < 0.0) h += 360; } *r = h; *g = l; *b = s; } static void hls_to_rgb (gdouble *h, gdouble *l, gdouble *s) { gdouble hue; gdouble lightness; gdouble saturation; gdouble m1, m2; gdouble r, g, b; lightness = *l; saturation = *s; if (lightness <= 0.5) m2 = lightness * (1 + saturation); else m2 = lightness + saturation - lightness * saturation; m1 = 2 * lightness - m2; if (saturation == 0) { *h = lightness; *l = lightness; *s = lightness; } else { hue = *h + 120; while (hue > 360) hue -= 360; while (hue < 0) hue += 360; if (hue < 60) r = m1 + (m2 - m1) * hue / 60; else if (hue < 180) r = m2; else if (hue < 240) r = m1 + (m2 - m1) * (240 - hue) / 60; else r = m1; hue = *h; while (hue > 360) hue -= 360; while (hue < 0) hue += 360; if (hue < 60) g = m1 + (m2 - m1) * hue / 60; else if (hue < 180) g = m2; else if (hue < 240) g = m1 + (m2 - m1) * (240 - hue) / 60; else g = m1; hue = *h - 120; while (hue > 360) hue -= 360; while (hue < 0) hue += 360; if (hue < 60) b = m1 + (m2 - m1) * hue / 60; else if (hue < 180) b = m2; else if (hue < 240) b = m1 + (m2 - m1) * (240 - hue) / 60; else b = m1; *h = r; *l = g; *s = b; } } static void shade_color(GdkColor *a, GdkColor *b, gdouble k) { gdouble red; gdouble green; gdouble blue; /* Special case for black, so that it looks pretty... */ if ((a->red == 0) && (a->green == 0) && (a->blue == 0)) { a->red = 32768; a->green = 32768; a->blue = 32768; } red = (gdouble) a->red / 65535.0; green = (gdouble) a->green / 65535.0; blue = (gdouble) a->blue / 65535.0; rgb_to_hls (&red, &green, &blue); green *= k; if (green > 1.0) green = 1.0; else if (green < 0.0) green = 0.0; blue *= k; if (blue > 1.0) blue = 1.0; else if (blue < 0.0) blue = 0.0; hls_to_rgb (&red, &green, &blue); b->red = (int) (red * 65535.0 + 0.5); b->green = (int) (green * 65535.0 + 0.5); b->blue = (int) (blue * 65535.0 + 0.5); } #define HIGHLIGHT_MULT 0.7 #define LIGHT_MULT 1.5 #define DARK_MULT 0.5 static void my_get_colors(GdkColormap *colormap, gulong background, gulong *top, gulong *bottom, gulong *highlight) { GdkColor cbackground; GdkColor ctop, cbottom, chighlight; /* I think this should use a ColorContext instead of allocating colors itself... - Federico */ /* FIXME: The colors that are allocated here are never freed. * I think we can save the pixel values in static variables so that we * can call gdk_colors_free() on the next invocation of the function. */ cbackground.pixel = background; my_x_query_colors(colormap, &cbackground, 1); if (top) { shade_color(&cbackground, &ctop, LIGHT_MULT); gdk_color_alloc(colormap, &ctop); *top = ctop.pixel; } if (bottom) { shade_color(&cbackground, &cbottom, DARK_MULT); gdk_color_alloc(colormap, &cbottom); *bottom = cbottom.pixel; } if (highlight) { shade_color(&cbackground, &chighlight, HIGHLIGHT_MULT); gdk_color_alloc(colormap, &chighlight); *highlight = chighlight.pixel; } } static void set_widget_colors(GtkXmHTML *html, gulong *top, gulong *bottom, gulong *highlight) { GdkColor c; if (top) { c.pixel = *top; gdk_gc_set_foreground(html->top_shadow_gc, &c); } if (bottom) { c.pixel = *bottom; gdk_gc_set_foreground(html->bottom_shadow_gc, &c); } if (highlight) { c.pixel = *highlight; gdk_gc_set_foreground(html->highlight_gc, &c); html->highlight_color = *highlight; } } #endif /***** * Name: _XmHTMLRecomputeColors * Return Type: void * Description: computes new values for top and bottom shadows and the * highlight color based on the current background color. * In: * html: XmHTMLWidget id; * Returns: * nothing. *****/ void _XmHTMLRecomputeColors(XmHTMLWidget html) { /* * We can only compute the colors when we have a GC. If we don't * have a GC, the widget is not yet realized. Use managers defaults * then. */ if(html->html.gc != NULL) { #ifdef WITH_MOTIF Pixel top = None, bottom = None, highlight = None; Arg args[3]; XmGetColors(XtScreen((Widget)html), html->core.colormap, html->html.body_bg, NULL, &top, &bottom, &highlight); XtSetArg(args[0], XmNtopShadowColor, top); XtSetArg(args[1], XmNbottomShadowColor, bottom); XtSetArg(args[2], XmNhighlightColor, highlight); XtSetValues((Widget)html, args, 3); #else gulong top, bottom, highlight; if(!GTK_WIDGET_REALIZED(html)) gtk_widget_realize(GTK_WIDGET(html)); my_get_colors(gtk_widget_get_colormap(GTK_WIDGET(html)), html->html.body_bg, &top, &bottom, &highlight); set_widget_colors(html, &top, &bottom, &highlight); #endif } } /***** * Name: _XmHTMLRecomputeHighlightColor * Return Type: void * Description: computes the select color based upon the given color. * In: * html: XmHTMLWidget id; * Returns: * nothing. *****/ void _XmHTMLRecomputeHighlightColor(XmHTMLWidget html, Pixel bg_color) { /* * We can only compute the colors when we have a GC. If we don't * have a GC, the widget is not yet realized. Use managers defaults * then. */ if(html->html.gc != NULL) { #ifdef WITH_MOTIF Pixel highlight = None; Arg args[1]; XmGetColors(XtScreen((Widget)html), html->core.colormap, bg_color, NULL, NULL, NULL, &highlight); XtSetArg(args[0], XmNhighlightColor, highlight); XtSetValues((Widget)html, args, 1); #else gulong highlight; my_get_colors(gtk_widget_get_colormap(GTK_WIDGET(html)), html->html.body_bg, NULL, NULL, &highlight); set_widget_colors(html, NULL, NULL, &highlight); #endif } } /***** * Name: XmHTMLAllocColor * Return Type: Pixel * Description: allocates the named color and takes the XmNmaxImageColors * resource into account. * In: * w: XmHTMLWidget id; * color: colorname, either symbolic or an RGB triplet. * def_pixel: pixel to return when allocation of "color" fails. * Returns: * allocated pixel upon success or def_pixel upon failure to allocate "color". *****/ Pixel XmHTMLAllocColor(TWidget w, String color, Pixel def_pixel) { Display *dpy = Toolkit_Display(w); TColor def; TColormap colormap; int success = True; /* sanity check */ if(!w || !color || *color == '\0') { _XmHTMLWarning(__WFUNC__(w, "XmHTMLAllocColor"), "%s passed to " "XmHTMLAllocColor.", w ? "NULL color name" : "NULL parent"); return(def_pixel); } /* * Get colormap for this widget. Will always succeed as all widgets * (and gadgets as well) are subclassed from core. */ colormap = Toolkit_Widget_Colormap (w); if((!tryColor(dpy, colormap, color, &def))) { /* bad color spec, return */ _XmHTMLWarning(__WFUNC__(w, "XmHTMLAllocColor"), "Bad color " "name %s", color); return(def_pixel); } /* try to allocate it */ if(!Toolkit_Alloc_Color(dpy, colormap, &def)) { /* * Initial allocation failed, try to find a close match from any * colors already allocated in the colormap */ TColor *cmap; int cmapsize; TVisual *visual = NULL; int d, mdist, close, ri, gi, bi; register int i, rd, gd, bd; _XmHTMLDebug(7, ("colors.c: XmHTMLAllocColor: first stage allocation " "for %s failed, trying to match it.\n", color)); Toolkit_Get_Visual (w, visual); /* * Get parent visual if current widget doesn't have one. This will * *always* return a visual. */ if(!visual) visual = XCCGetParentVisual(w); /* we only use up to MAX_IMAGE_COLORS */ #ifdef WITH_MOTIF cmapsize = (visual->map_entries > MAX_IMAGE_COLORS ? MAX_IMAGE_COLORS : visual->map_entries); #else cmapsize = MIN(GDK_VISUAL_XVISUAL(visual)->map_entries, MAX_IMAGE_COLORS); #endif cmap = (TColor*)malloc(cmapsize*sizeof(TColor)); /* initialise pixels */ for(i = 0; i < cmapsize; i++) { cmap[i].pixel = (Pixel)i; cmap[i].red = cmap[i].green = cmap[i].blue = 0; #ifdef WITH_MOTIF cmap[i].flags = DoRed|DoGreen|DoBlue; #endif } /* read the colormap */ #ifdef WITH_MOTIF XQueryColors(dpy, colormap, cmap, cmapsize); #else my_x_query_colors(colormap, cmap, cmapsize); #endif mdist = 0x1000000; close = -1; ri = def.red; gi = def.green; bi = def.blue; /* * walk all colors in the colormap and see which one is the * closest. Uses plain least squares. */ for(i = 0; i < cmapsize && mdist != 0; i++) { /* Don't replace these by shifts; the sign may get clobbered */ rd = (ri - cmap[i].red) / 256; gd = (gi - cmap[i].green) / 256; bd = (bi - cmap[i].blue) / 256; if((d = (rd*rd) + (gd*gd) + (bd*bd)) < mdist) { close = i; mdist = d; } } if(close != -1) { /* we got a match, try to allocate this color */ def.red = cmap[close].red; def.green = cmap[close].green; def.blue = cmap[close].blue; #ifdef WITH_MOTIF def.flags = DoRed|DoGreen|DoBlue; #endif if(!Toolkit_Alloc_Color(dpy, colormap, &def)) success = False; } else success = False; /* no longer needed */ free(cmap); } if(success == False) { /* failed, return default pixel */ _XmHTMLWarning(__WFUNC__(w, "_XmHTMLGetPixelByName"), "XmHTMLAllocColor failed for color %s", color); return(def_pixel); } _XmHTMLDebug(7, ("colors.c: XmHTMLAllocColor: %s allocated!\n", color)); return(def.pixel); } /***** * Name: XmHTMLFreeColor * Return Type: void * Description: releases an allocated pixel * In: * w: XmHTMLWidget id; * pixel: pixel to be freed. * Returns: * nothing. *****/ void XmHTMLFreeColor(TWidget w, Pixel pixel) { /* sanity check */ if(!w) { _XmHTMLBadParent(w, "XmHTMLFreeColor"); return; } /* * ->core.colormap will always yield a colormap, all widgets are * subclassed from core. */ #ifdef WITH_MOTIF XFreeColors(XtDisplay(w), w->core.colormap, &pixel, 1, 0L); #else gdk_colors_free(gtk_widget_get_colormap(w), &pixel, 1, 0L); #endif } Boolean _XmHTMLAddPalette(XmHTMLWidget html) { TColor cmap[MAX_IMAGE_COLORS]; int ncolors = 0, nlines = 0; int i,r,g,b; String chPtr; if(html->html.palette != NULL) { chPtr = html->html.palette; /* skip leading whitespace */ while(*chPtr != '\0' && isspace(*chPtr)) { if(*chPtr == '\n') nlines++; chPtr++; } while(*chPtr != '\0' && ncolors < MAX_IMAGE_COLORS) { if((sscanf(chPtr, "%x %x %x", &r, &g, &b)) != 3) { _XmHTMLWarning(__WFUNC__(html, "_XmHTMLAddPalette"), "Bad color entry on line %i of palette.", nlines); /* skip to next entry */ while(*chPtr != '\0' && !isspace(*chPtr)) chPtr++; } else { RANGE(r,0,255); RANGE(g,0,255); RANGE(b,0,255); cmap[ncolors].red = r; cmap[ncolors].green = g; cmap[ncolors].blue = b; ncolors++; /* skip them */ for(i = 0; i < 3; i++) { while(*chPtr != '\0' && isalnum(*chPtr)) chPtr++; while(*chPtr != '\0' && isspace(*chPtr)) { if(*chPtr == '\n') nlines++; chPtr++; } } } /* move to next slot */ while(*chPtr != '\0' && isspace(*chPtr)) { if(*chPtr == '\n') nlines++; chPtr++; } } /* check against maxImageColors */ if(ncolors != html->html.max_image_colors) { if(ncolors < html->html.max_image_colors) html->html.max_image_colors = ncolors; else { /* check how many colors are really allowed on this display */ if(ncolors < html->html.xcc->num_colors) html->html.max_image_colors = ncolors; else ncolors = html->html.max_image_colors; } } } else ncolors = CreateColormap(html, &cmap[0]); /* allocate this palette */ ncolors = XCCAddPalette(html->html.xcc, cmap, ncolors); /* check if we need to initialize dithering */ if(html->html.map_to_palette == XmBEST || html->html.map_to_palette == XmFAST) XCCInitDither(html->html.xcc); _XmHTMLDebug(7, ("colors.c, _XmHTMLAddPalette, added a palette with %i " "colors\n", ncolors)); return(True); } #ifdef DITHER_SIMPLE_COLORMAP static int CreateColormap(XmHTMLWidget html, TColor *cmap) { int i, idx, ncolors; float mul; ncolors = html->html.max_image_colors; mul = (float)MAX_IMAGE_COLORS/(float)html->html.max_image_colors; if(html->html.xcc->mode == MODE_BW || html->html.xcc->mode == MODE_MY_GRAY) { /* grayscale visual */ for (i = 0; i < ncolors; ++i) { idx = (int)(i * mul); cmap[i].red = idx; cmap[i].green = idx; cmap[i].blue = idx; } } else { for(i = 0; i < ncolors; i++) { idx = (int)(i * mul); cmap[i].red = (((idx << 0) & 0xe0)*255 + 0.5*0xe0)/0xe0; cmap[i].green= (((idx << 3) & 0xe0)*255 + 0.5*0xe0)/0xe0; cmap[i].blue = (((idx << 6) & 0xc0)*255 + 0.5*0xc0)/0xc0; } } return(ncolors); } #else /***** * Name: CreateColormap * Return Type: int * Description: creates a colormap (with equally spaced color components) * In: * html: XmHTMLWidget id; * cmap: colormap storage room. Filled upon return. * Returns: * actual size of colormap. This is at most equal to the value of the * XmNmaxImageColors resource (unless the value of this resource is less than * 8, in which case it will be set to 8). *****/ static int CreateColormap(XmHTMLWidget html, TColor *cmap) { int iroot, nc, max_colors, blksize, blkdist, nci, val, maxsample; int i, j, k, l, total_colors, Ncolors[3], temp; static int RGB[3] = {1, 0, 2}; Boolean changed; Byte **colormap; #ifdef WITH_MOTIF /* number of components per color */ if(html->html.xcc->mode == MODE_BW || html->html.xcc->mode == MODE_MY_GRAY) nc = 1; /* grayscale */ else nc = 3; /* color */ #else { GdkColorContextMode mode; mode = html->html.xcc->mode; if (mode == GDK_CC_MODE_BW || mode == GDK_CC_MODE_MY_GRAY) nc = 1; else nc = 3; } #endif /***** * requested colormap size. * To get an even distribution of the colors, we require this value to be * a triple power, with a minumum of 8 colors. *****/ max_colors = html->html.max_image_colors; if(max_colors < 8) max_colors = 8; iroot = 1; do { iroot++; temp = iroot; for(i = 1; i < nc; i++) temp *= iroot; }while(temp <= max_colors); iroot--; /* Set number of distinct values for each color component */ total_colors = 1; for(i = 0; i < nc; i++) { Ncolors[i] = iroot; total_colors *= iroot; } /***** * See if we can increase the number of distinct color components * without exceeding the allowed number of colors. *****/ do { changed = False; for(i = 0; i < nc; i++) { j = (nc == 1 ? 0 : RGB[i]); temp = total_colors/Ncolors[j]; temp *= Ncolors[j]+1; if(temp > max_colors) break; /* too large, done with this pass */ Ncolors[j]++; total_colors = (int)temp; changed = True; } }while(changed); if(total_colors != html->html.max_image_colors) { _XmHTMLWarning(__WFUNC__(html, "makeDitherCmap"), "Requested XmNmaxImageColors value of %i could not be matched " "exactly.\n Using %i colors out of %i total.", html->html.max_image_colors, total_colors, MAX_IMAGE_COLORS); html->html.max_image_colors = total_colors; } /* temporary storage */ colormap = (Byte**)calloc(nc, sizeof(Byte*)); for(i = 0; i < nc; i++) colormap[i] = (Byte*)calloc(total_colors, sizeof(Byte)); /* distance between groups of identical entries for a component */ blkdist = total_colors; /* maximum value of a single color component */ maxsample = MAX_IMAGE_COLORS-1; /* now go and fill the palette */ for(i = 0; i < nc; i++) { /* fill in entries for i'th color */ nci = Ncolors[i]; /* no of distinct values for this color */ blksize = blkdist/nci; for(j = 0; j < nci; j++) { /* get color value */ val = (int)(((unsigned long)(j * maxsample + (nci-1)/2))/ (nci-1)); /* fill all entries that have this value for this component. */ for(k = j * blksize; k < total_colors; k+= blkdist) { /* fill in blksize entries starting at k */ for(l = 0; l < blksize; l++) colormap[i][k+l] = (Byte)val; } } blkdist = blksize; /* size of this color is offset to next color */ } /* now save colormap in private storage */ if(nc == 1) /* grayscale */ { for(i = 0; i < total_colors; i++) cmap[i].red = cmap[i].green = cmap[i].blue = colormap[0][i]; } else /* rgb map */ { for(i = 0; i < total_colors; i++) { cmap[i].red = colormap[0][i]; cmap[i].green = colormap[1][i]; cmap[i].blue = colormap[2][i]; } } /* no longer needed */ for(i = 0; i < nc; i++) free(colormap[i]); free(colormap); /* all done */ return(total_colors); } #endif