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