1 //
2 // "$Id: fl_read_image.cxx 6475 2008-10-19 20:35:32Z matt $"
3 //
4 // X11 image reading routines 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 #include <FL/x.H>
29 #include <FL/Fl.H>
30 #include <FL/fl_draw.H>
31 #include "flstring.h"
32 
33 #ifdef DEBUG
34 #  include <stdio.h>
35 #endif // DEBUG
36 
37 #ifdef WIN32
38 #  include "fl_read_image_win32.cxx"
39 #elif defined(__APPLE__)
40 #  include "fl_read_image_mac.cxx"
41 #else
42 #  include <X11/Xutil.h>
43 #  ifdef __sgi
44 #    include <X11/extensions/readdisplay.h>
45 #  else
46 #    include <stdlib.h>
47 #  endif // __sgi
48 
49 // Defined in fl_color.cxx
50 extern uchar fl_redmask, fl_greenmask, fl_bluemask;
51 extern int fl_redshift, fl_greenshift, fl_blueshift, fl_extrashift;
52 
53 //
54 // 'fl_subimage_offsets()' - Calculate subimage offsets for an axis
55 static inline int
fl_subimage_offsets(int a,int aw,int b,int bw,int & obw)56 fl_subimage_offsets(int a, int aw, int b, int bw, int &obw)
57 {
58   int off;
59   int ob;
60 
61   if (b >= a) {
62     ob = b;
63     off = 0;
64   } else {
65     ob = a;
66     off = a - b;
67   }
68 
69   bw -= off;
70 
71   if (ob + bw <= a + aw) {
72     obw = bw;
73   } else {
74     obw = (a + aw) - ob;
75   }
76 
77   return off;
78 }
79 
80 // this handler will catch and ignore exceptions during XGetImage
81 // to avoid an application crash
xgetimageerrhandler(Display * display,XErrorEvent * error)82 static int xgetimageerrhandler(Display *display, XErrorEvent *error) {
83   return 0;
84 }
85 
86 //
87 // 'fl_read_image()' - Read an image from the current window.
88 //
89 
90 uchar *				// O - Pixel buffer or NULL if failed
fl_read_image(uchar * p,int X,int Y,int w,int h,int alpha)91 fl_read_image(uchar *p,		// I - Pixel buffer or NULL to allocate
92               int   X,		// I - Left position
93 	      int   Y,		// I - Top position
94 	      int   w,		// I - Width of area to read
95 	      int   h,		// I - Height of area to read
96 	      int   alpha) {	// I - Alpha value for image (0 for none)
97   XImage	*image;		// Captured image
98   int		i, maxindex;	// Looping vars
99   int           x, y;		// Current X & Y in image
100   int		d;		// Depth of image
101   unsigned char *line,		// Array to hold image row
102 		*line_ptr;	// Pointer to current line image
103   unsigned char	*pixel;		// Current color value
104   XColor	colors[4096];	// Colors from the colormap...
105   unsigned char	cvals[4096][3];	// Color values from the colormap...
106   unsigned	index_mask,
107 		index_shift,
108 		red_mask,
109 		red_shift,
110 		green_mask,
111 		green_shift,
112 		blue_mask,
113 		blue_shift;
114 
115 
116   //
117   // Under X11 we have the option of the XGetImage() interface or SGI's
118   // ReadDisplay extension which does all of the really hard work for
119   // us...
120   //
121 
122 #  ifdef __sgi
123   if (XReadDisplayQueryExtension(fl_display, &i, &i)) {
124     image = XReadDisplay(fl_display, fl_window, X, Y, w, h, 0, NULL);
125   } else
126 #  else
127   image = 0;
128 #  endif // __sgi
129 
130   if (!image) {
131     // fetch absolute coordinates
132     int dx, dy, sx, sy, sw, sh;
133     Window child_win;
134     Fl_Window *win = fl_find(fl_window);
135     if (win) {
136       XTranslateCoordinates(fl_display, fl_window,
137           RootWindow(fl_display, fl_screen), X, Y, &dx, &dy, &child_win);
138       // screen dimensions
139       Fl::screen_xywh(sx, sy, sw, sh, fl_screen);
140     }
141     if (!win || (dx >= sx && dy >= sy && dx + w <= sw && dy + h <= sh)) {
142       // the image is fully contained, we can use the traditional method
143       // however, if the window is obscured etc. the function will still fail. Make sure we
144       // catch the error and continue, otherwise an exception will be thrown.
145       XErrorHandler old_handler = XSetErrorHandler(xgetimageerrhandler);
146       image = XGetImage(fl_display, fl_window, X, Y, w, h, AllPlanes, ZPixmap);
147       XSetErrorHandler(old_handler);
148     } else {
149       // image is crossing borders, determine visible region
150       int nw, nh, noffx, noffy;
151       noffx = fl_subimage_offsets(sx, sw, dx, w, nw);
152       noffy = fl_subimage_offsets(sy, sh, dy, h, nh);
153       if (nw <= 0 || nh <= 0) return 0;
154 
155       // allocate the image
156       int bpp = fl_visual->depth + ((fl_visual->depth / 8) % 2) * 8;
157       char* buf = (char*)malloc(bpp / 8 * w * h);
158       image = XCreateImage(fl_display, fl_visual->visual,
159 	  fl_visual->depth, ZPixmap, 0, buf, w, h, bpp, 0);
160       if (!image) {
161 	if (buf) free(buf);
162 	return 0;
163       }
164 
165       XErrorHandler old_handler = XSetErrorHandler(xgetimageerrhandler);
166       XImage *subimg = XGetSubImage(fl_display, fl_window, X + noffx, Y + noffy,
167 	                            nw, nh, AllPlanes, ZPixmap, image, noffx, noffy);
168       XSetErrorHandler(old_handler);
169       if (!subimg) {
170 	XDestroyImage(image);
171 	return 0;
172       }
173     }
174   }
175 
176   if (!image) return 0;
177 
178 #ifdef DEBUG
179   printf("width            = %d\n", image->width);
180   printf("height           = %d\n", image->height);
181   printf("xoffset          = %d\n", image->xoffset);
182   printf("format           = %d\n", image->format);
183   printf("data             = %p\n", image->data);
184   printf("byte_order       = %d\n", image->byte_order);
185   printf("bitmap_unit      = %d\n", image->bitmap_unit);
186   printf("bitmap_bit_order = %d\n", image->bitmap_bit_order);
187   printf("bitmap_pad       = %d\n", image->bitmap_pad);
188   printf("depth            = %d\n", image->depth);
189   printf("bytes_per_line   = %d\n", image->bytes_per_line);
190   printf("bits_per_pixel   = %d\n", image->bits_per_pixel);
191   printf("red_mask         = %08x\n", image->red_mask);
192   printf("green_mask       = %08x\n", image->green_mask);
193   printf("blue_mask        = %08x\n", image->blue_mask);
194   printf("map_entries      = %d\n", fl_visual->visual->map_entries);
195 #endif // DEBUG
196 
197   d = alpha ? 4 : 3;
198 
199   // Allocate the image data array as needed...
200   if (!p) p = new uchar[w * h * d];
201 
202   // Initialize the default colors/alpha in the whole image...
203   memset(p, alpha, w * h * d);
204 
205   // Check that we have valid mask/shift values...
206   if (!image->red_mask && image->bits_per_pixel > 12) {
207     // Greater than 12 bits must be TrueColor...
208     image->red_mask   = fl_visual->visual->red_mask;
209     image->green_mask = fl_visual->visual->green_mask;
210     image->blue_mask  = fl_visual->visual->blue_mask;
211 
212 #ifdef DEBUG
213     puts("\n---- UPDATED ----");
214     printf("fl_redmask       = %08x\n", fl_redmask);
215     printf("fl_redshift      = %d\n", fl_redshift);
216     printf("fl_greenmask     = %08x\n", fl_greenmask);
217     printf("fl_greenshift    = %d\n", fl_greenshift);
218     printf("fl_bluemask      = %08x\n", fl_bluemask);
219     printf("fl_blueshift     = %d\n", fl_blueshift);
220     printf("red_mask         = %08x\n", image->red_mask);
221     printf("green_mask       = %08x\n", image->green_mask);
222     printf("blue_mask        = %08x\n", image->blue_mask);
223 #endif // DEBUG
224   }
225 
226   // Check if we have colormap image...
227   if (!image->red_mask) {
228     // Get the colormap entries for this window...
229     maxindex = fl_visual->visual->map_entries;
230 
231     for (i = 0; i < maxindex; i ++) colors[i].pixel = i;
232 
233     XQueryColors(fl_display, fl_colormap, colors, maxindex);
234 
235     for (i = 0; i < maxindex; i ++) {
236       cvals[i][0] = colors[i].red >> 8;
237       cvals[i][1] = colors[i].green >> 8;
238       cvals[i][2] = colors[i].blue >> 8;
239     }
240 
241     // Read the pixels and output an RGB image...
242     for (y = 0; y < image->height; y ++) {
243       pixel = (unsigned char *)(image->data + y * image->bytes_per_line);
244       line  = p + y * w * d;
245 
246       switch (image->bits_per_pixel) {
247         case 1 :
248 	  for (x = image->width, line_ptr = line, index_mask = 128;
249 	       x > 0;
250 	       x --, line_ptr += d) {
251 	    if (*pixel & index_mask) {
252 	      line_ptr[0] = cvals[1][0];
253 	      line_ptr[1] = cvals[1][1];
254 	      line_ptr[2] = cvals[1][2];
255             } else {
256 	      line_ptr[0] = cvals[0][0];
257 	      line_ptr[1] = cvals[0][1];
258 	      line_ptr[2] = cvals[0][2];
259             }
260 
261             if (index_mask > 1) {
262 	      index_mask >>= 1;
263 	    } else {
264               index_mask = 128;
265               pixel ++;
266             }
267 	  }
268           break;
269 
270         case 2 :
271 	  for (x = image->width, line_ptr = line, index_shift = 6;
272 	       x > 0;
273 	       x --, line_ptr += d) {
274 	    i = (*pixel >> index_shift) & 3;
275 
276 	    line_ptr[0] = cvals[i][0];
277 	    line_ptr[1] = cvals[i][1];
278 	    line_ptr[2] = cvals[i][2];
279 
280             if (index_shift > 0) {
281               index_mask >>= 2;
282               index_shift -= 2;
283             } else {
284               index_mask  = 192;
285               index_shift = 6;
286               pixel ++;
287             }
288 	  }
289           break;
290 
291         case 4 :
292 	  for (x = image->width, line_ptr = line, index_shift = 4;
293 	       x > 0;
294 	       x --, line_ptr += d) {
295 	    if (index_shift == 4) i = (*pixel >> 4) & 15;
296 	    else i = *pixel & 15;
297 
298 	    line_ptr[0] = cvals[i][0];
299 	    line_ptr[1] = cvals[i][1];
300 	    line_ptr[2] = cvals[i][2];
301 
302             if (index_shift > 0) {
303               index_shift = 0;
304 	    } else {
305               index_shift = 4;
306               pixel ++;
307             }
308 	  }
309           break;
310 
311         case 8 :
312 	  for (x = image->width, line_ptr = line;
313 	       x > 0;
314 	       x --, line_ptr += d, pixel ++) {
315 	    line_ptr[0] = cvals[*pixel][0];
316 	    line_ptr[1] = cvals[*pixel][1];
317 	    line_ptr[2] = cvals[*pixel][2];
318 	  }
319           break;
320 
321         case 12 :
322 	  for (x = image->width, line_ptr = line, index_shift = 0;
323 	       x > 0;
324 	       x --, line_ptr += d) {
325 	    if (index_shift == 0) {
326 	      i = ((pixel[0] << 4) | (pixel[1] >> 4)) & 4095;
327 	    } else {
328 	      i = ((pixel[1] << 8) | pixel[2]) & 4095;
329 	    }
330 
331 	    line_ptr[0] = cvals[i][0];
332 	    line_ptr[1] = cvals[i][1];
333 	    line_ptr[2] = cvals[i][2];
334 
335             if (index_shift == 0) {
336               index_shift = 4;
337             } else {
338               index_shift = 0;
339               pixel += 3;
340             }
341 	  }
342           break;
343       }
344     }
345   } else {
346     // RGB(A) image, so figure out the shifts & masks...
347     red_mask  = image->red_mask;
348     red_shift = 0;
349 
350     while ((red_mask & 1) == 0) {
351       red_mask >>= 1;
352       red_shift ++;
353     }
354 
355     green_mask  = image->green_mask;
356     green_shift = 0;
357 
358     while ((green_mask & 1) == 0) {
359       green_mask >>= 1;
360       green_shift ++;
361     }
362 
363     blue_mask  = image->blue_mask;
364     blue_shift = 0;
365 
366     while ((blue_mask & 1) == 0) {
367       blue_mask >>= 1;
368       blue_shift ++;
369     }
370 
371     // Read the pixels and output an RGB image...
372     for (y = 0; y < image->height; y ++) {
373       pixel = (unsigned char *)(image->data + y * image->bytes_per_line);
374       line  = p + y * w * d;
375 
376       switch (image->bits_per_pixel) {
377         case 8 :
378 	  for (x = image->width, line_ptr = line;
379 	       x > 0;
380 	       x --, line_ptr += d, pixel ++) {
381 	    i = *pixel;
382 
383 	    line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask;
384 	    line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask;
385 	    line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask;
386 	  }
387           break;
388 
389         case 12 :
390 	  for (x = image->width, line_ptr = line, index_shift = 0;
391 	       x > 0;
392 	       x --, line_ptr += d) {
393 	    if (index_shift == 0) {
394 	      i = ((pixel[0] << 4) | (pixel[1] >> 4)) & 4095;
395 	    } else {
396 	      i = ((pixel[1] << 8) | pixel[2]) & 4095;
397             }
398 
399 	    line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask;
400 	    line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask;
401 	    line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask;
402 
403             if (index_shift == 0) {
404               index_shift = 4;
405             } else {
406               index_shift = 0;
407               pixel += 3;
408             }
409 	  }
410           break;
411 
412         case 16 :
413           if (image->byte_order == LSBFirst) {
414             // Little-endian...
415 	    for (x = image->width, line_ptr = line;
416 	         x > 0;
417 	         x --, line_ptr += d, pixel += 2) {
418 	      i = (pixel[1] << 8) | pixel[0];
419 
420 	      line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask;
421 	      line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask;
422 	      line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask;
423 	    }
424 	  } else {
425             // Big-endian...
426 	    for (x = image->width, line_ptr = line;
427 	         x > 0;
428 	         x --, line_ptr += d, pixel += 2) {
429 	      i = (pixel[0] << 8) | pixel[1];
430 
431 	      line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask;
432 	      line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask;
433 	      line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask;
434 	    }
435 	  }
436           break;
437 
438         case 24 :
439           if (image->byte_order == LSBFirst) {
440             // Little-endian...
441 	    for (x = image->width, line_ptr = line;
442 	         x > 0;
443 	         x --, line_ptr += d, pixel += 3) {
444 	      i = (((pixel[2] << 8) | pixel[1]) << 8) | pixel[0];
445 
446 	      line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask;
447 	      line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask;
448 	      line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask;
449 	    }
450 	  } else {
451             // Big-endian...
452 	    for (x = image->width, line_ptr = line;
453 	         x > 0;
454 	         x --, line_ptr += d, pixel += 3) {
455 	      i = (((pixel[0] << 8) | pixel[1]) << 8) | pixel[2];
456 
457 	      line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask;
458 	      line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask;
459 	      line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask;
460 	    }
461 	  }
462           break;
463 
464         case 32 :
465           if (image->byte_order == LSBFirst) {
466             // Little-endian...
467 	    for (x = image->width, line_ptr = line;
468 	         x > 0;
469 	         x --, line_ptr += d, pixel += 4) {
470 	      i = (((((pixel[3] << 8) | pixel[2]) << 8) | pixel[1]) << 8) | pixel[0];
471 
472 	      line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask;
473 	      line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask;
474 	      line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask;
475 	    }
476 	  } else {
477             // Big-endian...
478 	    for (x = image->width, line_ptr = line;
479 	         x > 0;
480 	         x --, line_ptr += d, pixel += 4) {
481 	      i = (((((pixel[0] << 8) | pixel[1]) << 8) | pixel[2]) << 8) | pixel[3];
482 
483 	      line_ptr[0] = 255 * ((i >> red_shift) & red_mask) / red_mask;
484 	      line_ptr[1] = 255 * ((i >> green_shift) & green_mask) / green_mask;
485 	      line_ptr[2] = 255 * ((i >> blue_shift) & blue_mask) / blue_mask;
486 	    }
487 	  }
488           break;
489       }
490     }
491   }
492 
493   // Destroy the X image we've read and return the RGB(A) image...
494   XDestroyImage(image);
495 
496   return p;
497 }
498 
499 #endif
500 
501 //
502 // End of "$Id: fl_read_image.cxx 6475 2008-10-19 20:35:32Z matt $".
503 //
504