1 /* This file is part of the GNU plotutils package. Copyright (C) 1995,
2 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3
4 The GNU plotutils package is free software. You may redistribute it
5 and/or modify it under the terms of the GNU General Public License as
6 published by the Free Software foundation; either version 2, or (at your
7 option) any later version.
8
9 The GNU plotutils package is distributed in the hope that it will be
10 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with the GNU plotutils package; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17 Boston, MA 02110-1301, USA. */
18
19 /* This file contains device-specific color computation routines. These
20 routines are called by various XDrawablePlotter (and XPlotter)
21 methods. */
22
23 #include "sys-defines.h"
24 #include "extern.h"
25
26 /* we call this routine to set the foreground color in the X GC used for
27 drawing, only when needed (just before an object is written out) */
28
29 void
_pl_x_set_pen_color(S___ (Plotter * _plotter))30 _pl_x_set_pen_color(S___(Plotter *_plotter))
31 {
32 plColor old, new1;
33 XColor rgb;
34
35 new1 = _plotter->drawstate->fgcolor;
36 old = _plotter->drawstate->x_current_fgcolor; /* i.e. as stored in gc */
37 if (new1.red == old.red && new1.green == old.green && new1.blue == old.blue
38 && _plotter->drawstate->x_gc_fgcolor_status)
39 /* can use current color cell */
40 return;
41
42 rgb.red = new1.red;
43 rgb.green = new1.green;
44 rgb.blue = new1.blue;
45
46 /* retrieve matching color cell, if possible */
47 if (_pl_x_retrieve_color (R___(_plotter) &rgb) == false)
48 return;
49
50 /* select pen color as foreground color in GC used for drawing */
51 XSetForeground (_plotter->x_dpy, _plotter->drawstate->x_gc_fg, rgb.pixel);
52
53 /* save the new pixel value */
54 _plotter->drawstate->x_gc_fgcolor = rgb.pixel;
55
56 /* flag this as a genuine pixel value */
57 _plotter->drawstate->x_gc_fgcolor_status = true;
58
59 /* update non-opaque representation of stored foreground color */
60 _plotter->drawstate->x_current_fgcolor = new1;
61 }
62
63 /* we call this routine to set the foreground color in the X GC used for
64 filling, only when needed (just before an object is written out) */
65
66 void
_pl_x_set_fill_color(S___ (Plotter * _plotter))67 _pl_x_set_fill_color(S___(Plotter *_plotter))
68 {
69 plColor old, new1;
70 XColor rgb;
71
72 if (_plotter->drawstate->fill_type == 0) /* transparent */
73 /* don't do anything, fill color will be ignored when writing objects*/
74 return;
75
76 new1 = _plotter->drawstate->fillcolor;
77 old = _plotter->drawstate->x_current_fillcolor; /* as used in GC */
78 if (new1.red == old.red && new1.green == old.green && new1.blue == old.blue
79 && _plotter->drawstate->x_gc_fillcolor_status)
80 /* can use current color cell */
81 return;
82
83 rgb.red = (short)_plotter->drawstate->fillcolor.red;
84 rgb.green = (short)_plotter->drawstate->fillcolor.green;
85 rgb.blue = (short)_plotter->drawstate->fillcolor.blue;
86
87 /* retrieve matching color cell, if possible */
88 if (_pl_x_retrieve_color (R___(_plotter) &rgb) == false)
89 return;
90
91 /* select fill color as foreground color in GC used for filling */
92 XSetForeground (_plotter->x_dpy, _plotter->drawstate->x_gc_fill, rgb.pixel);
93
94 /* save the new pixel value */
95 _plotter->drawstate->x_gc_fillcolor = rgb.pixel;
96
97 /* flag this as a genuine pixel value */
98 _plotter->drawstate->x_gc_fillcolor_status = true;
99
100 /* update non-opaque representation of stored fill color */
101 _plotter->drawstate->x_current_fillcolor = new1;
102 }
103
104 /* we call this routine to set the foreground color in the X GC used for
105 erasing, only when needed (just before an erasure takes place) */
106
107 void
_pl_x_set_bg_color(S___ (Plotter * _plotter))108 _pl_x_set_bg_color(S___(Plotter *_plotter))
109 {
110 plColor old, new1;
111 XColor rgb;
112
113 new1 = _plotter->drawstate->bgcolor;
114 old = _plotter->drawstate->x_current_bgcolor; /* i.e. as stored in gc */
115 if (new1.red == old.red && new1.green == old.green && new1.blue == old.blue
116 && _plotter->drawstate->x_gc_bgcolor_status)
117 /* can use current color cell */
118 return;
119
120 rgb.red = new1.red;
121 rgb.green = new1.green;
122 rgb.blue = new1.blue;
123
124 /* retrieve matching color cell, if possible */
125 if (_pl_x_retrieve_color (R___(_plotter) &rgb) == false)
126 return;
127
128 /* select background color as foreground color in GC used for erasing */
129 XSetForeground (_plotter->x_dpy, _plotter->drawstate->x_gc_bg, rgb.pixel);
130
131 /* save the new pixel value */
132 _plotter->drawstate->x_gc_bgcolor = rgb.pixel;
133
134 /* flag this as a genuine pixel value */
135 _plotter->drawstate->x_gc_bgcolor_status = true;
136
137 /* update non-opaque representation of stored background color */
138 _plotter->drawstate->x_current_bgcolor = new1;
139 }
140
141 /* This is the internal X color retrieval routine. If the visual class
142 is known and is TrueColor, it computes the X pixel value from a 48-bit
143 RGB without invoking XAllocColor(), which would require a round trip
144 to the server.
145
146 Otherwise, it first searches for a specified RGB in a cache of
147 previously retrieved color cells, and if that fails, tries to allocate a
148 new color cell by calling XAllocColor(). If that fails, and a new
149 colormap can be switched to, it switches to a new colormap and tries
150 again. If that attempt also fails, it searches the cache for the
151 colorcell with an RGB that's closest to the specified RGB. Only if that
152 fails as well (i.e. the cache is empty), does it return false.
153
154 Cache is maintained as a linked list (not optimal, but it facilitates
155 color cell management; see comment in x_erase.c). */
156
157 bool
_pl_x_retrieve_color(R___ (Plotter * _plotter)XColor * rgb_ptr)158 _pl_x_retrieve_color (R___(Plotter *_plotter) XColor *rgb_ptr)
159 {
160 plColorRecord *cptr;
161 int rgb_red = rgb_ptr->red;
162 int rgb_green = rgb_ptr->green;
163 int rgb_blue = rgb_ptr->blue;
164 int xretval;
165
166 #ifdef LIBPLOTTER
167 if (_plotter->x_visual && _plotter->x_visual->c_class == TrueColor)
168 #else
169 #ifdef __cplusplus
170 if (_plotter->x_visual && _plotter->x_visual->c_class == TrueColor)
171 #else
172 if (_plotter->x_visual && _plotter->x_visual->class == TrueColor)
173 #endif
174 #endif
175 /* can compute pixel value from RGB without calling XAllocColor(), by
176 bit-twiddling */
177 {
178 unsigned long red_mask, green_mask, blue_mask;
179 int red_shift, green_shift, blue_shift;
180 int red_bits, green_bits, blue_bits;
181
182 /* first, compute {R,G,B}_bits and {R,G,B}_shift (should be precomputed) */
183
184 red_mask = _plotter->x_visual->red_mask; red_shift = red_bits = 0;
185 while (!(red_mask & 1))
186 {
187 red_mask >>= 1; red_shift++;
188 }
189 while (red_mask & 1)
190 {
191 red_mask >>= 1; red_bits++;
192 }
193 green_mask = _plotter->x_visual->green_mask; green_shift = green_bits = 0;
194 while (!(green_mask & 1))
195 {
196 green_mask >>= 1; green_shift++;
197 }
198 while (green_mask & 1)
199 {
200 green_mask >>= 1; green_bits++;
201 }
202 blue_mask = _plotter->x_visual->blue_mask; blue_shift = blue_bits = 0;
203 while (!(blue_mask & 1))
204 {
205 blue_mask >>= 1; blue_shift++;
206 }
207 while (blue_mask & 1)
208 {
209 blue_mask >>= 1; blue_bits++;
210 }
211
212 /* compute and pass back pixel, as a 32-bit unsigned long */
213 rgb_red = rgb_red >> (16 - red_bits);
214 rgb_green = rgb_green >> (16 - green_bits);
215 rgb_blue = rgb_blue >> (16 - blue_bits);
216 rgb_ptr->pixel = ((rgb_red << red_shift) & _plotter->x_visual->red_mask)
217 | ((rgb_green << green_shift) & _plotter->x_visual->green_mask)
218 | ((rgb_blue << blue_shift) & _plotter->x_visual->blue_mask);
219 #if 0
220 fprintf (stderr, "pixel=0x%lx, R=0x%hx, G=0x%hx, B=0x%hx\n",
221 rgb_ptr->pixel, rgb_ptr->red, rgb_ptr->green, rgb_ptr->blue);
222 #endif
223
224 return true;
225 }
226
227 /* If we got here, we weren't able to compute the pixel value from the
228 RGB without calling XAllocColor(). So may have to do that, but first
229 we consult a list of previously allocated color cells. */
230
231 /* search cache list */
232 for (cptr = _plotter->x_colorlist; cptr; cptr = cptr->next)
233 {
234 XColor cached_rgb;
235
236 cached_rgb = cptr->rgb;
237 if (cached_rgb.red == rgb_red
238 && cached_rgb.green == rgb_green
239 && cached_rgb.blue == rgb_blue)
240 /* found in cache */
241 {
242 /* keep track of page, frame number in which cell was most
243 recently accessed */
244 cptr->page_number = _plotter->data->page_number;
245 cptr->frame_number = _plotter->data->frame_number;
246 /* return stored pixel value */
247 *rgb_ptr = cached_rgb;
248 return true;
249 }
250 }
251
252 /* not in cache, so try to allocate a new color cell, if colormap hasn't
253 been flagged as bad (i.e. full) */
254 if (_plotter->x_cmap_type != X_CMAP_BAD)
255 {
256 xretval = XAllocColor (_plotter->x_dpy, _plotter->x_cmap, rgb_ptr);
257
258 if (xretval == 0)
259 /* failure */
260 {
261 if (_plotter->x_cmap_type == X_CMAP_ORIG)
262 /* colormap is the one we started with, so try switching and
263 reallocating */
264 {
265 /* Which method is invoked here depends on the type of
266 Plotter. If this is an X Plotter, replace its colormap by
267 a copied, private colormap if we can; otherwise we flag
268 the colormap as bad (i.e. filled up). If this is an
269 XDrawable Plotter, this method doesn't do anything, so
270 colormap just gets flagged as bad. */
271 _maybe_get_new_colormap (S___(_plotter));
272 if (_plotter->x_cmap_type != X_CMAP_NEW)
273 _plotter->x_cmap_type = X_CMAP_BAD;
274
275 if (_plotter->x_cmap_type != X_CMAP_BAD)
276 /* got a new colormap; try again to allocate color cell */
277 xretval = XAllocColor (_plotter->x_dpy, _plotter->x_cmap, rgb_ptr);
278 }
279 }
280 }
281 else
282 /* colormap is bad, i.e. full; no hope of allocating a new colorcell */
283 xretval = 0;
284
285 if (xretval == 0)
286 /* allocation failed, and no switching or further switching of
287 colormaps is possible; so simply search cache list for closest
288 color, among previously allocated cells */
289 {
290 XColor cached_rgb;
291 plColorRecord *best_cptr = NULL;
292 double distance = DBL_MAX;
293
294 /* flag colormap as bad, i.e. full; no further color cell allocations
295 will be attempted */
296 _plotter->x_cmap_type = X_CMAP_BAD;
297
298 if (_plotter->x_colormap_warning_issued == false)
299 {
300 _plotter->warning(R___(_plotter)
301 "color supply exhausted, can't create new colors");
302 _plotter->x_colormap_warning_issued = true;
303 }
304
305 for (cptr = _plotter->x_colorlist; cptr; cptr = cptr->next)
306 {
307 double newdistance;
308
309 cached_rgb = cptr->rgb;
310 newdistance = (((rgb_red - cached_rgb.red)
311 * (rgb_red - cached_rgb.red))
312 + ((rgb_green - cached_rgb.green)
313 * (rgb_green - cached_rgb.green))
314 + ((rgb_blue - cached_rgb.blue)
315 * (rgb_blue - cached_rgb.blue)));
316 if (newdistance < distance)
317 {
318 distance = newdistance;
319 best_cptr = cptr;
320 }
321 }
322
323 if (best_cptr != (plColorRecord *)NULL)
324 {
325 /* keep track of page, frame number in which cell was most
326 recently accessed */
327 best_cptr->page_number = _plotter->data->page_number;
328 best_cptr->frame_number = _plotter->data->frame_number;
329 /* return pixel value via pointer */
330 *rgb_ptr = best_cptr->rgb;
331 return true;
332 }
333 else
334 /* cache must be empty; bad news */
335 return false;
336 }
337
338 else
339 /* allocation succeeded, add new color cell to head of cache list */
340 {
341 cptr = (plColorRecord *)_pl_xmalloc (sizeof (plColorRecord));
342 cptr->rgb = *rgb_ptr;
343 /* include unquantized RGB values */
344 cptr->rgb.red = rgb_red;
345 cptr->rgb.green = rgb_green;
346 cptr->rgb.blue = rgb_blue;
347 cptr->allocated = true; /* vestigial field */
348 /* keep track of page, frame number in which cell was allocated */
349 cptr->page_number = _plotter->data->page_number;
350 cptr->frame_number = _plotter->data->frame_number;
351 cptr->next = _plotter->x_colorlist;
352 _plotter->x_colorlist = cptr;
353 #if 0
354 fprintf (stderr, "pixel=0x%lx, R=0x%hx, G=0x%hx, B=0x%hx\n",
355 cptr->rgb.pixel, cptr->rgb.red, cptr->rgb.green, cptr->rgb.blue);
356 #endif
357 return true;
358 }
359 }
360