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