1 /*
2  * imgWindow.c --
3  *
4  * A photo image file handler to put the content of a window in a photo.
5  *
6  */
7 
8 /* Author : Jan Nijtmans */
9 /* Date   : 11/16/97     */
10 
11 #include "tkPort.h"
12 #include "Lang.h"
13 
14 #include <string.h>
15 #include <ctype.h>
16 #include <stdlib.h>
17 
18 #include "imgInt.h"
19 #include "X11/Xutil.h"
20 #ifndef	__WIN32__
21 #   include "X11/Xproto.h"
22 #else
23 #   include <windows.h>
24 #   include "X11/Xlib.h"
25 #   include "tkInt.h"
26 #   include "tkWinInt.h"
27 #   include "X11/Xfuncproto.h"
28 #   undef X_GetImage
29 #endif
30 
31 /*
32  * The format record for the Win data format:
33  */
34 
35 static int ChanMatchWin _ANSI_ARGS_((Tcl_Channel chan, Tcl_Obj *filename,
36 	Tcl_Obj *format, int *widthPtr, int *heightPtr, Tcl_Interp *interp));
37 static int ObjMatchWin _ANSI_ARGS_((Tcl_Obj *dataObj,
38 	Tcl_Obj *format, int *widthPtr, int *heightPtr, Tcl_Interp *interp));
39 static int ObjReadWin _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *dataObj,
40 	Tcl_Obj *format, Tk_PhotoHandle imageHandle,
41 	int destX, int destY, int width, int height, int srcX, int srcY));
42 #ifdef X_GetImage
43 static int		xerrorhandler _ANSI_ARGS_((ClientData clientData,
44 			    XErrorEvent *e));
45 #endif
46 
47 Tk_PhotoImageFormat imgFmtWin = {
48     "window",					/* name */
49     ChanMatchWin,				/* fileMatchProc */
50     ObjMatchWin,				/* stringMatchProc */
51     (Tk_ImageFileReadProc *) NULL,		/* fileReadProc */
52     ObjReadWin,					/* stringReadProc */
53     (Tk_ImageFileWriteProc *) NULL,		/* fileWriteProc */
54     (Tk_ImageStringWriteProc *) NULL,		/* stringWriteProc */
55 };
56 
57 typedef struct ColormapData {	/* Hold color information for a window */
58     int separated;		/* Whether to use separate color bands */
59     int color;			/* Whether window is color or black/white */
60     int ncolors;		/* Number of color values stored */
61     XColor *colors;		/* Pixel value -> RGB mappings */
62     int red_mask, green_mask, blue_mask;	/* Masks and shifts for each */
63     int red_shift, green_shift, blue_shift;	/* color band */
64 } ColormapData;
65 
66 /*
67  * Prototypes for local procedures defined in this file:
68  */
69 
70 #define UCHAR(c) ((unsigned char) (c))
71 /*
72  *--------------------------------------------------------------
73  *
74  * xerrorhandler --
75  *
76  *	This is a dummy function to catch X11 errors during an
77  *	attempt to convert a window to a photo image.
78  *
79  * Results:
80  *	None.
81  *
82  * Side effects:
83  *	None.
84  *
85  *--------------------------------------------------------------
86  */
87 
88 #ifdef X_GetImage
89 static int
xerrorhandler(clientData,e)90 xerrorhandler(clientData, e)
91     ClientData clientData;
92     XErrorEvent *e;
93 {
94     return 0;
95 }
96 #endif
97 
98 /*
99  *----------------------------------------------------------------------
100  *
101  * ChanMatchWin --
102  *
103  *	This procedure is invoked by the photo image type to see if
104  *	a file contains image data in WINDOW format.
105  *
106  * Results:
107  *	The return value is always 0, because a window cannot be
108  *	read from a file.
109  *
110  * Side effects:
111  *	None.
112  *
113  *----------------------------------------------------------------------
114  */
115 
ChanMatchWin(chan,filename,format,widthPtr,heightPtr,interp)116 static int ChanMatchWin(chan, filename, format, widthPtr, heightPtr, interp)
117     Tcl_Channel chan;
118     Tcl_Obj *filename;
119     Tcl_Obj *format;
120     int *widthPtr, *heightPtr;
121     Tcl_Interp *interp;
122 {
123     return 0;
124 }
125 
126 /*
127  *----------------------------------------------------------------------
128  *
129  * ObjMatchWin --
130  *
131  *  This procedure is invoked by the photo image type to see if
132  *  an object contains image data which can be read from a window.
133  *
134  * Results:
135  *  The return value is 1 if data contains a valid window name.
136  *
137  * Side effects:
138  *  the size of the image is placed in widthPtr and heightPtr.
139  *
140  *----------------------------------------------------------------------
141  */
142 
ObjMatchWin(data,format,widthPtr,heightPtr,interp)143 static int ObjMatchWin(data, format, widthPtr, heightPtr, interp)
144     Tcl_Obj *data;
145     Tcl_Obj *format;
146     int *widthPtr, *heightPtr;
147     Tcl_Interp *interp;
148 {
149     Tk_Window tkwin;
150     char *name;
151 
152     ImgFixObjMatchProc(&interp, &data, &format, &widthPtr, &heightPtr);
153 
154     name = ImgGetStringFromObj(data, NULL);
155 
156     if (interp && name && (name[0] == '.') && ((name[1] == 0) || islower(UCHAR(name[1])))) {
157 	tkwin = Tk_MainWindow(interp);
158 	if (tkwin == NULL) {
159 	    return 0;
160 	}
161 	tkwin = Tk_NameToWindow(interp, name, tkwin);
162 	if (tkwin == NULL) {
163 	    *widthPtr = *heightPtr = 0;
164 	    return 1;
165 	}
166 	*widthPtr =  Tk_Width(tkwin);
167 	*heightPtr = Tk_Height(tkwin);
168 	return 1;
169     }
170     return 0;
171 }
172 
173 /*
174  *----------------------------------------------------------------------
175  *
176  * ObjReadWin --
177  *
178  *	This procedure is called by the photo image type to read
179  *	the contents of a window and give it to the photo image.
180  *
181  * Results:
182  *	A standard TCL completion code.  If TCL_ERROR is returned
183  *	then an error message is left in interp->result.
184  *
185  * Side effects:
186  *	new data is added to the image given by imageHandle.
187  *
188  *----------------------------------------------------------------------
189  */
190 
191 typedef struct myblock {
192     Tk_PhotoImageBlock ck;
193     int dummy; /* extra space for offset[3], in case it is not
194 		  included already in Tk_PhotoImageBlock */
195 } myblock;
196 
197 #define block bl.ck
198 
ObjReadWin(interp,data,format,imageHandle,destX,destY,width,height,srcX,srcY)199 static int ObjReadWin(interp, data, format, imageHandle,
200 	destX, destY, width, height, srcX, srcY)
201     Tcl_Interp *interp;
202     Tcl_Obj *data;
203     Tcl_Obj *format;
204     Tk_PhotoHandle imageHandle;
205     int destX, destY;
206     int width, height;
207     int srcX, srcY;
208 {
209     myblock bl;
210     Tk_Window tkwin;
211     int fileWidth, fileHeight, i, depth, ncolors, nBytes, x, y;
212     char *name;
213 #ifndef	__WIN32__
214     XImage *ximage;
215     ColormapData cdata;
216 #else
217 #   undef XGetPixel
218 #   define XGetPixel(P,X,Y) GetPixel(P, X, Y)
219     TkWinDCState DCi;
220     HDC			ximage;
221 #endif
222     Colormap cmap;
223     Visual *visual;
224     unsigned char *p;
225 #ifdef X_GetImage
226     Tk_ErrorHandler	handle;
227 #endif
228     int green, blue;
229 
230     name = ImgGetStringFromObj(data, NULL);
231 
232     tkwin = Tk_NameToWindow(interp, name, Tk_MainWindow(interp));
233 
234     if (!tkwin) {
235 	Tcl_AppendResult(interp, "Window \"", name,"\" doesn't exist", (char *) NULL);
236 	return TCL_ERROR;
237     }
238 
239     if (!Tk_WindowId(tkwin)) {
240 	Tcl_AppendResult(interp, "Window \"", name,"\" is not mapped", (char *) NULL);
241 	return TCL_ERROR;
242     }
243 
244     fileWidth = Tk_Width(tkwin);
245     fileHeight = Tk_Height(tkwin);
246 
247     if ((srcX + width) > fileWidth) {
248 	width = fileWidth - srcX;
249     }
250     if ((srcY + height) > fileHeight) {
251 	height = fileHeight - srcY;
252     }
253     if ((width <= 0) || (height <= 0)) {
254 	return TCL_OK;
255     }
256 
257     /*
258      * If the window is off the screen it will generate an BadMatch/XError
259      * We catch any BadMatch errors here
260      */
261 
262 #ifdef X_GetImage
263     handle = Tk_CreateErrorHandler(Tk_Display(tkwin), BadMatch,
264 	    X_GetImage, -1, xerrorhandler, (ClientData) tkwin);
265 #endif
266 
267 #ifndef	__WIN32__
268     /*
269      * Generate an XImage from the window.  We can then read pixel
270      * values out of the XImage.
271      */
272 
273     ximage = XGetImage(Tk_Display(tkwin), Tk_WindowId(tkwin), srcX, srcY,
274 	width, height, AllPlanes, ZPixmap);
275 
276 #ifdef X_GetImage
277     Tk_DeleteErrorHandler(handle);
278 #endif
279 
280     if (ximage == (XImage*) NULL) {
281 	Tcl_AppendResult(interp, "Window \"", name,
282 		"\" cannot be transformed into a pixmap (possibly obscured?)",
283 		(char *) NULL);
284 	return TCL_ERROR;
285     }
286 #else
287     ximage = TkWinGetDrawableDC(Tk_Display(tkwin), Tk_WindowId(tkwin), &DCi);
288 #endif
289 
290     depth = Tk_Depth(tkwin);
291     visual = Tk_Visual(tkwin);
292 #ifndef	__WIN32__
293     cmap = Tk_Colormap(tkwin);
294 
295     /*
296      * Obtain information about the colormap, ie the mapping between
297      * pixel values and RGB values.  The code below should work
298      * for all Visual types.
299      */
300 
301     ncolors = visual->map_entries;
302     cdata.colors = (XColor *) ckalloc(sizeof(XColor) * ncolors);
303     cdata.ncolors = ncolors;
304     if (visual->class == DirectColor || visual->class == TrueColor) {
305 	cdata.separated = 1;
306 	cdata.red_mask = visual->red_mask;
307 	cdata.green_mask = visual->green_mask;
308 	cdata.blue_mask = visual->blue_mask;
309 	cdata.red_shift = 0;
310 	cdata.green_shift = 0;
311 	cdata.blue_shift = 0;
312 	while ((0x0001 & (cdata.red_mask >> cdata.red_shift)) == 0)
313 	    cdata.red_shift ++;
314 	while ((0x0001 & (cdata.green_mask >> cdata.green_shift)) == 0)
315 	    cdata.green_shift ++;
316 	while ((0x0001 & (cdata.blue_mask >> cdata.blue_shift)) == 0)
317 	    cdata.blue_shift ++;
318 	for (i = 0; i < ncolors; i ++)
319 	    cdata.colors[i].pixel =
320 		    ((i << cdata.red_shift) & cdata.red_mask) |
321 		    ((i << cdata.green_shift) & cdata.green_mask) |
322 		    ((i << cdata.blue_shift) & cdata.blue_mask);
323     } else {
324 	cdata.separated=0;
325 	for (i = 0; i < ncolors; i ++) cdata.colors[i].pixel = i;
326     }
327     cdata.color = !(visual->class == StaticGray || visual->class == GrayScale);
328 
329     XQueryColors(Tk_Display(tkwin), cmap, cdata.colors, ncolors);
330 #endif
331 
332     Tk_PhotoExpand(imageHandle, destX + width, destY + height);
333     block.offset[0] = 0;
334     block.offset[3] = 0;
335 #ifndef	__WIN32__
336     if (cdata.color) {
337 #endif
338 	block.pixelSize = 3;
339 	block.offset[1] = green = 1;
340 	block.offset[2] = blue = 2;
341 #ifndef	__WIN32__
342     } else {
343 	block.pixelSize = 1;
344 	block.offset[1] = green = 0;
345 	block.offset[2] = blue = 0;
346     }
347 #endif
348     block.width = width;
349     block.height = height;
350     block.pitch = block.pixelSize * width;
351     nBytes = block.pitch * height;
352     block.pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes);
353 
354     p = block.pixelPtr;
355     for (y = 0; y<height; y++) {
356 	for (x = 0; x<width; x++) {
357 	    unsigned long pixel = XGetPixel(ximage, x, y);
358 #ifndef	__WIN32__
359 	    if (cdata.separated) {
360 		int r = (pixel & cdata.red_mask) >> cdata.red_shift;
361 		p[0] = cdata.colors[r].red >> 8;
362 		if (cdata.color) {
363 		    int g = (pixel & cdata.green_mask) >> cdata.green_shift;
364 		    int b = (pixel & cdata.blue_mask) >> cdata.blue_shift;
365 		    p[1] = cdata.colors[g].green >> 8;
366 		    p[2] = cdata.colors[b].blue >> 8;
367 		}
368 	    } else {
369 		p[0] = cdata.colors[pixel].red >> 8;
370 		if (cdata.color) {
371 		    p[1] = cdata.colors[pixel].green >> 8;
372 		    p[2] = cdata.colors[pixel].blue >> 8;
373 		}
374 	    }
375 #else
376 	    p[0] = GetRValue(pixel);
377 	    p[1] = GetGValue(pixel);
378 	    p[2] = GetBValue(pixel);
379 #endif
380 	    p += block.pixelSize;
381 	}
382     }
383 
384     Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, height, TK_PHOTO_COMPOSITE_SET);
385 
386 #ifndef	__WIN32__
387     XDestroyImage(ximage);
388     ckfree((char *) cdata.colors);
389 #else
390 #   undef XGetPixel
391     TkWinReleaseDrawableDC(Tk_WindowId(tkwin), ximage, &DCi);
392 #endif
393     ckfree((char *) block.pixelPtr);
394     return TCL_OK;
395 }
396 
397