1 /*
2  * imgWindow.c --
3  *
4  * A photo image file handler to put the content of a window in a photo.
5  *
6  * Author : Jan Nijtmans
7  *
8  */
9 
10 /*
11  * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
12  */
13 
14 #include "init.c"
15 
16 #include "X11/Xutil.h"
17 #if !defined(__WIN32__)
18 #  if !defined(MAC_OSX_TK)
19 #   include "X11/Xproto.h"
20 #  else
21 #   include "X11/Xlib.h"
22 #   include "X11/Xfuncproto.h"
23 #   undef X_GetImage
24 #  endif
25 #else
26 #   include "X11/Xlib.h"
27 #   include "tkInt.h"
28 #   include "tkWinInt.h"
29 #   include "X11/Xfuncproto.h"
30 #   undef X_GetImage
31 #endif
32 
33 /*
34  * The format record for the Win data format:
35  */
36 
37 #ifdef X_GetImage
38 static int xerrorhandler(ClientData clientData, XErrorEvent *e);
39 #endif
40 
41 typedef struct ColormapData {	/* Hold color information for a window */
42     int separated;		/* Whether to use separate color bands */
43     int color;			/* Whether window is color or black/white */
44     int ncolors;		/* Number of color values stored */
45     XColor *colors;		/* Pixel value -> RGB mappings */
46     int red_mask, green_mask, blue_mask;	/* Masks and shifts for each */
47     int red_shift, green_shift, blue_shift;	/* color band */
48 } ColormapData;
49 
50 /*
51  * Prototypes for local procedures defined in this file:
52  */
53 
54 #define UCHAR(c) ((unsigned char) (c))
55 /*
56  *--------------------------------------------------------------
57  *
58  * xerrorhandler --
59  *
60  *	This is a dummy function to catch X11 errors during an
61  *	attempt to convert a window to a photo image.
62  *
63  * Results:
64  *	None.
65  *
66  * Side effects:
67  *	None.
68  *
69  *--------------------------------------------------------------
70  */
71 
72 #ifdef X_GetImage
73 static int
xerrorhandler(clientData,e)74 xerrorhandler(clientData, e)
75     ClientData clientData;
76     XErrorEvent *e;
77 {
78     return 0;
79 }
80 #endif
81 
82 static int
ChnRead(Tcl_Interp * interp,Tcl_Channel chan,const char * fileName,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)83 ChnRead(
84     Tcl_Interp *interp,
85     Tcl_Channel chan,
86     const char *fileName,
87     Tcl_Obj *format,
88     Tk_PhotoHandle imageHandle,
89     int destX, int destY,
90     int width, int height,
91     int srcX, int srcY
92 ) {
93     return 0;
94 }
95 
ChnWrite(Tcl_Interp * interp,const char * filename,Tcl_Obj * format,Tk_PhotoImageBlock * blockPtr)96 static int ChnWrite(
97     Tcl_Interp *interp,
98     const char *filename,
99     Tcl_Obj *format,
100     Tk_PhotoImageBlock *blockPtr
101 ) {
102     return 0;
103 }
104 
StringWrite(Tcl_Interp * interp,Tcl_Obj * format,Tk_PhotoImageBlock * blockPtr)105 static int StringWrite(
106     Tcl_Interp *interp,
107     Tcl_Obj *format,
108     Tk_PhotoImageBlock *blockPtr
109 ) {
110     return 0;
111 }
112 
113 /*
114  *----------------------------------------------------------------------
115  *
116  * ChnMatch --
117  *
118  *	This procedure is invoked by the photo image type to see if
119  *	a file contains image data in WINDOW format.
120  *
121  * Results:
122  *	The return value is always 0, because a window cannot be
123  *	read from a file.
124  *
125  * Side effects:
126  *	None.
127  *
128  *----------------------------------------------------------------------
129  */
130 
ChnMatch(Tcl_Channel chan,const char * filename,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)131 static int ChnMatch(
132     Tcl_Channel chan,
133     const char *filename,
134     Tcl_Obj *format,
135     int *widthPtr,
136     int *heightPtr,
137     Tcl_Interp *interp
138 ) {
139     return 0;
140 }
141 
142 /*
143  *----------------------------------------------------------------------
144  *
145  * ObjMatch --
146  *
147  *  This procedure is invoked by the photo image type to see if
148  *  an object contains image data which can be read from a window.
149  *
150  * Results:
151  *  The return value is 1 if data contains a valid window name.
152  *
153  * Side effects:
154  *  the size of the image is placed in widthPtr and heightPtr.
155  *
156  *----------------------------------------------------------------------
157  */
158 
ObjMatch(Tcl_Obj * data,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)159 static int ObjMatch(
160     Tcl_Obj *data,
161     Tcl_Obj *format,
162     int *widthPtr,
163     int *heightPtr,
164     Tcl_Interp *interp
165 ) {
166     Tk_Window tkwin;
167     const char *name;
168 
169     name = tkimg_GetStringFromObj2(data, NULL);
170 
171     if (interp && name && (name[0] == '.') &&
172         ((name[1] == 0) || islower(UCHAR(name[1])))) {
173 	tkwin = Tk_MainWindow(interp);
174 	if (tkwin == NULL) {
175 	    return 0;
176 	}
177 	tkwin = Tk_NameToWindow(interp, name, tkwin);
178 	if (tkwin == NULL) {
179 	    *widthPtr = *heightPtr = 0;
180 	    return 1;
181 	}
182 	*widthPtr =  Tk_Width(tkwin);
183 	*heightPtr = Tk_Height(tkwin);
184 	return 1;
185     }
186     return 0;
187 }
188 
189 #if defined(__WIN32__)
190     typedef struct _BITMAPCAPTURE {
191         HBITMAP hbm;
192         LPDWORD pixels;
193         INT     width;
194         INT     height;
195     } BITMAPCAPTURE;
196 
CaptureWindow(BITMAPCAPTURE * bmpCapture,Tk_Window tkwin)197     static BOOL CaptureWindow (BITMAPCAPTURE *bmpCapture, Tk_Window tkwin)
198     {
199         BOOL bResult = FALSE;
200         HWND hWnd;
201         HDC hdcScreen;
202         HDC hdcCapture;
203         int nWidth;
204         int nHeight;
205         LPBYTE lpCapture;
206         BITMAPINFO bmiCapture = {
207             { sizeof(BITMAPINFOHEADER), 0, 0, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }
208         };
209 
210         if (!bmpCapture) {
211             return bResult;
212         }
213 
214         ZeroMemory (bmpCapture, sizeof(BITMAPCAPTURE));
215 
216         hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
217         if( ! hWnd ) {
218             return bResult;
219         }
220         hdcScreen  = GetDC(hWnd);
221         hdcCapture = CreateCompatibleDC(NULL);
222         nWidth  = Tk_Width(tkwin);
223         nHeight = Tk_Height(tkwin);
224 
225         bmiCapture.bmiHeader.biWidth  = nWidth;
226         bmiCapture.bmiHeader.biHeight = -nHeight;
227 
228         bmpCapture->hbm = CreateDIBSection (hdcScreen, &bmiCapture,
229             DIB_RGB_COLORS, (LPVOID *)&lpCapture, NULL, 0);
230         if (bmpCapture->hbm) {
231             HBITMAP hbmOld = (HBITMAP)SelectObject(hdcCapture, bmpCapture->hbm);
232             BitBlt(hdcCapture, 0, 0, nWidth, nHeight, hdcScreen, 0, 0, SRCCOPY);
233             SelectObject(hdcCapture, hbmOld);
234             bmpCapture->pixels = (LPDWORD)lpCapture;
235             bmpCapture->width  = nWidth;
236             bmpCapture->height = nHeight;
237             bResult = TRUE;
238         }
239         DeleteDC(hdcCapture);
240         DeleteDC(hdcScreen);
241         return bResult;
242     }
243 
244 #endif /* __WIN32__ */
245 
246 /*
247  *----------------------------------------------------------------------
248  *
249  * ObjRead --
250  *
251  *	This procedure is called by the photo image type to read
252  *	the contents of a window and give it to the photo image.
253  *
254  * Results:
255  *	A standard TCL completion code.  If TCL_ERROR is returned
256  *	then an error message is left in interp->result.
257  *
258  * Side effects:
259  *	new data is added to the image given by imageHandle.
260  *
261  *----------------------------------------------------------------------
262  */
ObjRead(Tcl_Interp * interp,Tcl_Obj * data,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)263 static int ObjRead(
264     Tcl_Interp *interp,
265     Tcl_Obj *data,
266     Tcl_Obj *format,
267     Tk_PhotoHandle imageHandle,
268     int destX, int destY,
269     int width, int height,
270     int srcX, int srcY
271 ) {
272     Tk_PhotoImageBlock block;
273     Tk_Window tkwin;
274     int fileWidth, fileHeight, nBytes, x, y;
275     const char *name;
276 #if !defined(__WIN32__)
277     XImage *ximage;
278     ColormapData cdata;
279     Colormap cmap;
280     int i, ncolors;
281     Visual *visual;
282 #else
283 #   undef XGetPixel
284 #   define XGetPixel(P,X,Y) GetPixel(P, X, Y)
285     TkWinDCState DCi;
286     HDC         ximage;
287     BITMAPCAPTURE grab;
288 #endif
289     unsigned char *p;
290 #ifdef X_GetImage
291     Tk_ErrorHandler	handle;
292 #endif
293     int green, blue;
294     int result = TCL_OK;
295 
296     name = tkimg_GetStringFromObj2(data, NULL);
297 
298     tkwin = Tk_NameToWindow(interp, name, Tk_MainWindow(interp));
299 
300     if (!tkwin) {
301 	Tcl_AppendResult(interp, "Window \"", name,"\" doesn't exist", (char *) NULL);
302 	return TCL_ERROR;
303     }
304 
305     if (!Tk_WindowId(tkwin)) {
306 	Tcl_AppendResult(interp, "Window \"", name,"\" is not mapped", (char *) NULL);
307 	return TCL_ERROR;
308     }
309 
310     fileWidth  = Tk_Width(tkwin);
311     fileHeight = Tk_Height(tkwin);
312 
313     if ((srcX + width) > fileWidth) {
314 	width = fileWidth - srcX;
315     }
316     if ((srcY + height) > fileHeight) {
317 	height = fileHeight - srcY;
318     }
319     if ((width <= 0) || (height <= 0)) {
320 	return TCL_OK;
321     }
322 
323     /*
324      * If the window is off the screen it will generate an BadMatch/XError
325      * We catch any BadMatch errors here
326      */
327 
328 #ifdef X_GetImage
329     handle = Tk_CreateErrorHandler(Tk_Display(tkwin), BadMatch,
330 	    X_GetImage, -1, xerrorhandler, (ClientData) tkwin);
331 #endif
332 
333 #if !defined(__WIN32__)
334     /*
335      * Generate an XImage from the window.  We can then read pixel
336      * values out of the XImage.
337      */
338 
339     ximage = XGetImage(Tk_Display(tkwin), Tk_WindowId(tkwin), srcX, srcY,
340 	width, height, AllPlanes, ZPixmap);
341 
342 #ifdef X_GetImage
343     Tk_DeleteErrorHandler(handle);
344 #endif
345 
346     if (ximage == (XImage*) NULL) {
347 	Tcl_AppendResult(interp, "Window \"", name,
348 		"\" cannot be transformed into a pixmap (possibly obscured?)",
349 		(char *) NULL);
350 	return TCL_ERROR;
351     }
352 #else
353     ximage = TkWinGetDrawableDC(Tk_Display(tkwin), Tk_WindowId(tkwin), &DCi);
354 
355     if ( ! CaptureWindow (&grab, tkwin)) {
356         Tcl_AppendResult(interp, "Window \"", name, "\" cannot be grabbed", (char *) NULL);
357         return TCL_ERROR;
358     }
359 
360 #ifdef X_GetImage
361     Tk_DeleteErrorHandler(handle);
362 #endif
363 #endif
364 
365     if (tkimg_PhotoExpand(interp, imageHandle, destX + width, destY + height) == TCL_ERROR) {
366 	return TCL_ERROR;
367     }
368 
369 #if !defined(__WIN32__)
370     visual = Tk_Visual(tkwin);
371     cmap = Tk_Colormap(tkwin);
372 
373     /*
374      * Obtain information about the colormap, ie the mapping between
375      * pixel values and RGB values. The code below should work
376      * for all Visual types.
377      */
378 
379     ncolors = visual->map_entries;
380     cdata.colors = (XColor *) ckalloc(sizeof(XColor) * ncolors);
381     cdata.ncolors = ncolors;
382     if (visual->class == DirectColor || visual->class == TrueColor) {
383 	cdata.separated = 1;
384 	cdata.red_mask = visual->red_mask;
385 	cdata.green_mask = visual->green_mask;
386 	cdata.blue_mask = visual->blue_mask;
387 	cdata.red_shift = 0;
388 	cdata.green_shift = 0;
389 	cdata.blue_shift = 0;
390 	while ((0x0001 & (cdata.red_mask >> cdata.red_shift)) == 0)
391 	    cdata.red_shift ++;
392 	while ((0x0001 & (cdata.green_mask >> cdata.green_shift)) == 0)
393 	    cdata.green_shift ++;
394 	while ((0x0001 & (cdata.blue_mask >> cdata.blue_shift)) == 0)
395 	    cdata.blue_shift ++;
396 	for (i = 0; i < ncolors; i ++)
397 	    cdata.colors[i].pixel =
398 		    ((i << cdata.red_shift) & cdata.red_mask) |
399 		    ((i << cdata.green_shift) & cdata.green_mask) |
400 		    ((i << cdata.blue_shift) & cdata.blue_mask);
401     } else {
402 	cdata.separated = 0;
403 	cdata.red_mask = 0;
404 	cdata.green_mask = 0;
405 	cdata.blue_mask = 0;
406 	cdata.red_shift = 0;
407 	cdata.green_shift = 0;
408 	cdata.blue_shift = 0;
409 	for (i = 0; i < ncolors; i ++) cdata.colors[i].pixel = i;
410     }
411     cdata.color = !(visual->class == StaticGray || visual->class == GrayScale);
412 
413     XQueryColors(Tk_Display(tkwin), cmap, cdata.colors, ncolors);
414 #endif
415 
416     block.offset[0] = 0;
417     block.offset[3] = 0;
418 #if !defined(__WIN32__)
419     if (cdata.color) {
420 #endif
421 	block.pixelSize = 3;
422 	block.offset[1] = green = 1;
423 	block.offset[2] = blue = 2;
424 #if !defined(__WIN32__)
425     } else {
426 	block.pixelSize = 1;
427 	block.offset[1] = green = 0;
428 	block.offset[2] = blue = 0;
429     }
430 #endif
431     block.width = width;
432     block.height = height;
433     block.pitch = block.pixelSize * width;
434     nBytes = block.pitch * height;
435     block.pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes);
436 
437     p = block.pixelPtr;
438     for (y = 0; y<height; y++) {
439 	for (x = 0; x<width; x++) {
440 #if !defined(__WIN32__)
441 	    unsigned long pixel = XGetPixel(ximage, x, y);
442 	    if (cdata.separated) {
443 		int r = (pixel & cdata.red_mask) >> cdata.red_shift;
444 		p[0] = cdata.colors[r].red >> 8;
445 		if (cdata.color) {
446 		    int g = (pixel & cdata.green_mask) >> cdata.green_shift;
447 		    int b = (pixel & cdata.blue_mask) >> cdata.blue_shift;
448 		    p[1] = cdata.colors[g].green >> 8;
449 		    p[2] = cdata.colors[b].blue >> 8;
450 		}
451 	    } else {
452 		p[0] = cdata.colors[pixel].red >> 8;
453 		if (cdata.color) {
454 		    p[1] = cdata.colors[pixel].green >> 8;
455 		    p[2] = cdata.colors[pixel].blue >> 8;
456 		}
457 	    }
458 #else
459             /* Bitmap has order ARGB. */
460             #define BITMAP_PIXEL(b, x, y) ((b).pixels[(y) * (b).width + (x)])
461             COLORREF pixel = BITMAP_PIXEL (grab, x, y);
462             p[0] = (pixel & 0xFF0000) >> 16;
463             p[1] = (pixel & 0xFF00)   >>  8;
464             p[2] = (pixel & 0xFF);
465 #endif
466 	    p += block.pixelSize;
467 	}
468     }
469 
470     if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, destY, width, height, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
471 	result = TCL_ERROR;
472     }
473 
474 #if !defined(__WIN32__)
475     XDestroyImage(ximage);
476     ckfree((char *) cdata.colors);
477 #else
478     DeleteObject(grab.hbm);
479     TkWinReleaseDrawableDC(Tk_WindowId(tkwin), ximage, &DCi);
480 #endif
481     ckfree((char *) block.pixelPtr);
482     return result;
483 }
484