1 //
2 // "$Id: Fl_Pixmap.cxx 5190 2006-06-09 16:16:34Z mike $"
3 //
4 // Pixmap drawing code for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2005 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 // http://www.fltk.org/str.php
26 //
27
28 // Draws X pixmap data, keeping it stashed in a server pixmap so it
29 // redraws fast.
30
31 // See fl_draw_pixmap.cxx for code used to get the actual data into pixmap.
32 // Implemented without using the xpm library (which I can't use because
33 // it interferes with the color cube used by fl_draw_image).
34
35 #include <FL/Fl.H>
36 #include <FL/fl_draw.H>
37 #include <FL/x.H>
38 #include <FL/Fl_Widget.H>
39 #include <FL/Fl_Menu_Item.H>
40 #include <FL/Fl_Pixmap.H>
41
42 #include <stdio.h>
43 #include "flstring.h"
44 #include <ctype.h>
45
46 #ifdef WIN32
47 extern void fl_release_dc(HWND, HDC); // located in Fl_win32.cxx
48 #endif
49
50 #ifdef __APPLE_QUARTZ__
51 extern Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h);
52 #endif
53
54 extern uchar **fl_mask_bitmap; // used by fl_draw_pixmap.cxx to store mask
55 void fl_restore_clip(); // in fl_rect.cxx
56
measure()57 void Fl_Pixmap::measure() {
58 int W, H;
59
60 // ignore empty or bad pixmap data:
61 if (w()<0 && data()) {
62 fl_measure_pixmap(data(), W, H);
63 w(W); h(H);
64 }
65 }
66
draw(int XP,int YP,int WP,int HP,int cx,int cy)67 void Fl_Pixmap::draw(int XP, int YP, int WP, int HP, int cx, int cy) {
68 // ignore empty or bad pixmap data:
69 if (!data()) {
70 draw_empty(XP, YP);
71 return;
72 }
73 if (w()<0) measure();
74 if (WP==-1) {
75 WP = w();
76 HP = h();
77 }
78 if (!w()) {
79 draw_empty(XP, YP);
80 return;
81 }
82 // account for current clip region (faster on Irix):
83 int X,Y,W,H; fl_clip_box(XP,YP,WP,HP,X,Y,W,H);
84 cx += X-XP; cy += Y-YP;
85 // clip the box down to the size of image, quit if empty:
86 if (cx < 0) {W += cx; X -= cx; cx = 0;}
87 if (cx+W > w()) W = w()-cx;
88 if (W <= 0) return;
89 if (cy < 0) {H += cy; Y -= cy; cy = 0;}
90 if (cy+H > h()) H = h()-cy;
91 if (H <= 0) return;
92 if (!id) {
93 #ifdef __APPLE_QUARTZ__
94 id = fl_create_offscreen_with_alpha(w(), h());
95 fl_begin_offscreen((Fl_Offscreen)id);
96 fl_draw_pixmap(data(), 0, 0, FL_GREEN);
97 fl_end_offscreen();
98 #else
99 id = fl_create_offscreen(w(), h());
100 fl_begin_offscreen((Fl_Offscreen)id);
101 uchar *bitmap = 0;
102 fl_mask_bitmap = &bitmap;
103 fl_draw_pixmap(data(), 0, 0, FL_BLACK);
104 fl_mask_bitmap = 0;
105 if (bitmap) {
106 mask = fl_create_bitmask(w(), h(), bitmap);
107 delete[] bitmap;
108 }
109 fl_end_offscreen();
110 #endif
111 }
112 #ifdef WIN32
113 if (mask) {
114 HDC new_gc = CreateCompatibleDC(fl_gc);
115 int save = SaveDC(new_gc);
116 SelectObject(new_gc, (void*)mask);
117 BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCAND);
118 SelectObject(new_gc, (void*)id);
119 BitBlt(fl_gc, X, Y, W, H, new_gc, cx, cy, SRCPAINT);
120 RestoreDC(new_gc,save);
121 DeleteDC(new_gc);
122 } else {
123 fl_copy_offscreen(X, Y, W, H, (Fl_Offscreen)id, cx, cy);
124 }
125 #elif defined(__APPLE_QD__)
126 if (mask) {
127 Rect src, dst;
128 src.left = cx; src.right = cx+W;
129 src.top = cy; src.bottom = cy+H;
130 dst.left = X; dst.right = X+W;
131 dst.top = Y; dst.bottom = Y+H;
132 RGBColor rgb, oldfg, oldbg;
133 GetForeColor(&oldfg);
134 GetBackColor(&oldbg);
135 rgb.red = 0xffff; rgb.green = 0xffff; rgb.blue = 0xffff;
136 RGBBackColor(&rgb);
137 rgb.red = 0x0000; rgb.green = 0x0000; rgb.blue = 0x0000;
138 RGBForeColor(&rgb);
139 CopyMask(GetPortBitMapForCopyBits((GrafPtr)id),
140 GetPortBitMapForCopyBits((GrafPtr)mask),
141 GetPortBitMapForCopyBits(GetWindowPort(fl_window)),
142 &src, &src, &dst);
143 RGBBackColor(&oldbg);
144 RGBForeColor(&oldfg);
145 } else {
146 fl_copy_offscreen(X, Y, W, H, (Fl_Offscreen)id, cx, cy);
147 }
148 #elif defined(__APPLE_QUARTZ__)
149 fl_copy_offscreen(X, Y, W, H, (Fl_Offscreen)id, cx, cy);
150 #else
151 if (mask) {
152 // I can't figure out how to combine a mask with existing region,
153 // so cut the image down to a clipped rectangle:
154 int nx, ny; fl_clip_box(X,Y,W,H,nx,ny,W,H);
155 cx += nx-X; X = nx;
156 cy += ny-Y; Y = ny;
157 // make X use the bitmap as a mask:
158 XSetClipMask(fl_display, fl_gc, mask);
159 int ox = X-cx; if (ox < 0) ox += w();
160 int oy = Y-cy; if (oy < 0) oy += h();
161 XSetClipOrigin(fl_display, fl_gc, X-cx, Y-cy);
162 }
163 fl_copy_offscreen(X, Y, W, H, id, cx, cy);
164 if (mask) {
165 // put the old clip region back
166 XSetClipOrigin(fl_display, fl_gc, 0, 0);
167 fl_restore_clip();
168 }
169 #endif
170 }
171
~Fl_Pixmap()172 Fl_Pixmap::~Fl_Pixmap() {
173 uncache();
174 delete_data();
175 }
176
uncache()177 void Fl_Pixmap::uncache() {
178 if (id) {
179 fl_delete_offscreen((Fl_Offscreen)id);
180 id = 0;
181 }
182
183 if (mask) {
184 fl_delete_bitmask((Fl_Bitmask)mask);
185 mask = 0;
186 }
187 }
188
label(Fl_Widget * widget)189 void Fl_Pixmap::label(Fl_Widget* widget) {
190 widget->image(this);
191 }
192
label(Fl_Menu_Item * m)193 void Fl_Pixmap::label(Fl_Menu_Item* m) {
194 Fl::set_labeltype(_FL_IMAGE_LABEL, labeltype, Fl_Image::measure);
195 m->label(_FL_IMAGE_LABEL, (const char*)this);
196 }
197
copy_data()198 void Fl_Pixmap::copy_data() {
199 if (alloc_data) return;
200
201 char **new_data, // New data array
202 **new_row; // Current row in image
203 int i, // Looping var
204 ncolors, // Number of colors in image
205 chars_per_pixel,// Characters per color
206 chars_per_line; // Characters per line
207
208 // Figure out how many colors there are, and how big they are...
209 sscanf(data()[0],"%*d%*d%d%d", &ncolors, &chars_per_pixel);
210 chars_per_line = chars_per_pixel * w() + 1;
211
212 // Allocate memory for the new array...
213 if (ncolors < 0) new_data = new char *[h() + 2];
214 else new_data = new char *[h() + ncolors + 1];
215
216 new_data[0] = new char[strlen(data()[0]) + 1];
217 strcpy(new_data[0], data()[0]);
218
219 // Copy colors...
220 if (ncolors < 0) {
221 // Copy FLTK colormap values...
222 ncolors = -ncolors;
223 new_row = new_data + 1;
224 *new_row = new char[ncolors * 4];
225 memcpy(*new_row, data()[1], ncolors * 4);
226 ncolors = 1;
227 new_row ++;
228 } else {
229 // Copy standard XPM colormap values...
230 for (i = 0, new_row = new_data + 1; i < ncolors; i ++, new_row ++) {
231 *new_row = new char[strlen(data()[i + 1]) + 1];
232 strcpy(*new_row, data()[i + 1]);
233 }
234 }
235
236 // Copy image data...
237 for (i = 0; i < h(); i ++, new_row ++) {
238 *new_row = new char[chars_per_line];
239 memcpy(*new_row, data()[i + ncolors + 1], chars_per_line);
240 }
241
242 // Update pointers...
243 data((const char **)new_data, h() + ncolors + 1);
244 alloc_data = 1;
245 }
246
copy(int W,int H)247 Fl_Image *Fl_Pixmap::copy(int W, int H) {
248 Fl_Pixmap *new_image; // New pixmap
249
250 // Optimize the simple copy where the width and height are the same...
251 if (W == w() && H == h()) {
252 // Make an exact copy of the image and return it...
253 new_image = new Fl_Pixmap(data());
254 new_image->copy_data();
255 return new_image;
256 }
257 if (W <= 0 || H <= 0) return 0;
258
259 // OK, need to resize the image data; allocate memory and
260 char **new_data, // New array for image data
261 **new_row, // Pointer to row in image data
262 *new_ptr, // Pointer into new array
263 new_info[255]; // New information line
264 const char *old_ptr; // Pointer into old array
265 int i, // Looping var
266 c, // Channel number
267 sy, // Source coordinate
268 dx, dy, // Destination coordinates
269 xerr, yerr, // X & Y errors
270 xmod, ymod, // X & Y moduli
271 xstep, ystep; // X & Y step increments
272 int ncolors, // Number of colors in image
273 chars_per_pixel,// Characters per color
274 chars_per_line; // Characters per line
275
276 // Figure out how many colors there are, and how big they are...
277 sscanf(data()[0],"%*d%*d%d%d", &ncolors, &chars_per_pixel);
278 chars_per_line = chars_per_pixel * W + 1;
279
280 sprintf(new_info, "%d %d %d %d", W, H, ncolors, chars_per_pixel);
281
282 // Figure out Bresenheim step/modulus values...
283 xmod = w() % W;
284 xstep = (w() / W) * chars_per_pixel;
285 ymod = h() % H;
286 ystep = h() / H;
287
288 // Allocate memory for the new array...
289 if (ncolors < 0) new_data = new char *[H + 2];
290 else new_data = new char *[H + ncolors + 1];
291 new_data[0] = new char[strlen(new_info) + 1];
292 strcpy(new_data[0], new_info);
293
294 // Copy colors...
295 if (ncolors < 0) {
296 // Copy FLTK colormap values...
297 ncolors = -ncolors;
298 new_row = new_data + 1;
299 *new_row = new char[ncolors * 4];
300 memcpy(*new_row, data()[1], ncolors * 4);
301 ncolors = 1;
302 new_row ++;
303 } else {
304 // Copy standard XPM colormap values...
305 for (i = 0, new_row = new_data + 1; i < ncolors; i ++, new_row ++) {
306 *new_row = new char[strlen(data()[i + 1]) + 1];
307 strcpy(*new_row, data()[i + 1]);
308 }
309 }
310
311 // Scale the image using a nearest-neighbor algorithm...
312 for (dy = H, sy = 0, yerr = H; dy > 0; dy --, new_row ++) {
313 *new_row = new char[chars_per_line];
314 new_ptr = *new_row;
315
316 for (dx = W, xerr = W, old_ptr = data()[sy + ncolors + 1];
317 dx > 0;
318 dx --) {
319 for (c = 0; c < chars_per_pixel; c ++) *new_ptr++ = old_ptr[c];
320
321 old_ptr += xstep;
322 xerr -= xmod;
323
324 if (xerr <= 0) {
325 xerr += W;
326 old_ptr += chars_per_pixel;
327 }
328 }
329
330 *new_ptr = '\0';
331 sy += ystep;
332 yerr -= ymod;
333 if (yerr <= 0) {
334 yerr += H;
335 sy ++;
336 }
337 }
338
339 new_image = new Fl_Pixmap((char*const*)new_data);
340 new_image->alloc_data = 1;
341
342 return new_image;
343 }
344
color_average(Fl_Color c,float i)345 void Fl_Pixmap::color_average(Fl_Color c, float i) {
346 // Delete any existing pixmap/mask objects...
347 uncache();
348
349 // Allocate memory as needed...
350 copy_data();
351
352 // Get the color to blend with...
353 uchar r, g, b;
354 unsigned ia, ir, ig, ib;
355
356 Fl::get_color(c, r, g, b);
357 if (i < 0.0f) i = 0.0f;
358 else if (i > 1.0f) i = 1.0f;
359
360 ia = (unsigned)(256 * i);
361 ir = r * (256 - ia);
362 ig = g * (256 - ia);
363 ib = b * (256 - ia);
364
365 // Update the colormap to do the blend...
366 char line[255]; // New colormap line
367 int color, // Looping var
368 ncolors, // Number of colors in image
369 chars_per_pixel;// Characters per color
370
371
372 sscanf(data()[0],"%*d%*d%d%d", &ncolors, &chars_per_pixel);
373
374 if (ncolors < 0) {
375 // Update FLTK colormap...
376 ncolors = -ncolors;
377 uchar *cmap = (uchar *)(data()[1]);
378 for (color = 0; color < ncolors; color ++, cmap += 4) {
379 cmap[1] = (ia * cmap[1] + ir) >> 8;
380 cmap[2] = (ia * cmap[2] + ig) >> 8;
381 cmap[3] = (ia * cmap[3] + ib) >> 8;
382 }
383 } else {
384 // Update standard XPM colormap...
385 for (color = 0; color < ncolors; color ++) {
386 // look for "c word", or last word if none:
387 const char *p = data()[color + 1] + chars_per_pixel + 1;
388 const char *previous_word = p;
389 for (;;) {
390 while (*p && isspace(*p)) p++;
391 char what = *p++;
392 while (*p && !isspace(*p)) p++;
393 while (*p && isspace(*p)) p++;
394 if (!*p) {p = previous_word; break;}
395 if (what == 'c') break;
396 previous_word = p;
397 while (*p && !isspace(*p)) p++;
398 }
399
400 if (fl_parse_color(p, r, g, b)) {
401 r = (ia * r + ir) >> 8;
402 g = (ia * g + ig) >> 8;
403 b = (ia * b + ib) >> 8;
404
405 if (chars_per_pixel > 1) sprintf(line, "%c%c c #%02X%02X%02X",
406 data()[color + 1][0],
407 data()[color + 1][1], r, g, b);
408 else sprintf(line, "%c c #%02X%02X%02X", data()[color + 1][0], r, g, b);
409
410 delete[] (char *)data()[color + 1];
411 ((char **)data())[color + 1] = new char[strlen(line) + 1];
412 strcpy((char *)data()[color + 1], line);
413 }
414 }
415 }
416 }
417
delete_data()418 void Fl_Pixmap::delete_data() {
419 if (alloc_data) {
420 for (int i = 0; i < count(); i ++) delete[] (char *)data()[i];
421 delete[] (char **)data();
422 }
423 }
424
set_data(const char * const * p)425 void Fl_Pixmap::set_data(const char * const * p) {
426 int height, // Number of lines in image
427 ncolors; // Number of colors in image
428
429 if (p) {
430 sscanf(p[0],"%*d%d%d", &height, &ncolors);
431 if (ncolors < 0) data(p, height + 2);
432 else data(p, height + ncolors + 1);
433 }
434 }
435
436
desaturate()437 void Fl_Pixmap::desaturate() {
438 // Delete any existing pixmap/mask objects...
439 uncache();
440
441 // Allocate memory as needed...
442 copy_data();
443
444 // Update the colormap to grayscale...
445 char line[255]; // New colormap line
446 int i, // Looping var
447 ncolors, // Number of colors in image
448 chars_per_pixel;// Characters per color
449 uchar r, g, b;
450
451 sscanf(data()[0],"%*d%*d%d%d", &ncolors, &chars_per_pixel);
452
453 if (ncolors < 0) {
454 // Update FLTK colormap...
455 ncolors = -ncolors;
456 uchar *cmap = (uchar *)(data()[1]);
457 for (i = 0; i < ncolors; i ++, cmap += 4) {
458 g = (uchar)((cmap[1] * 31 + cmap[2] * 61 + cmap[3] * 8) / 100);
459 cmap[1] = cmap[2] = cmap[3] = g;
460 }
461 } else {
462 // Update standard XPM colormap...
463 for (i = 0; i < ncolors; i ++) {
464 // look for "c word", or last word if none:
465 const char *p = data()[i + 1] + chars_per_pixel + 1;
466 const char *previous_word = p;
467 for (;;) {
468 while (*p && isspace(*p)) p++;
469 char what = *p++;
470 while (*p && !isspace(*p)) p++;
471 while (*p && isspace(*p)) p++;
472 if (!*p) {p = previous_word; break;}
473 if (what == 'c') break;
474 previous_word = p;
475 while (*p && !isspace(*p)) p++;
476 }
477
478 if (fl_parse_color(p, r, g, b)) {
479 g = (uchar)((r * 31 + g * 61 + b * 8) / 100);
480
481 if (chars_per_pixel > 1) sprintf(line, "%c%c c #%02X%02X%02X", data()[i + 1][0],
482 data()[i + 1][1], g, g, g);
483 else sprintf(line, "%c c #%02X%02X%02X", data()[i + 1][0], g, g, g);
484
485 delete[] (char *)data()[i + 1];
486 ((char **)data())[i + 1] = new char[strlen(line) + 1];
487 strcpy((char *)data()[i + 1], line);
488 }
489 }
490 }
491 }
492
493 //
494 // End of "$Id: Fl_Pixmap.cxx 5190 2006-06-09 16:16:34Z mike $".
495 //
496