1/*
2 * tkWinImage.c --
3 *
4 *	This file contains routines for manipulation full-color images.
5 *
6 * Copyright (c) 1995 Sun Microsystems, Inc.
7 *
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 * RCS: @(#) $Id: tkWinImage.c,v 1.6.2.2 2003/10/29 01:08:02 hobbs Exp $
12 */
13
14#include "tkWinInt.h"
15
16static int		DestroyImage _ANSI_ARGS_((XImage* data));
17static unsigned long	ImageGetPixel _ANSI_ARGS_((XImage *image, int x, int y));
18static int		PutPixel _ANSI_ARGS_((XImage *image, int x, int y,
19			    unsigned long pixel));
20
21/*
22 *----------------------------------------------------------------------
23 *
24 * DestroyImage --
25 *
26 *	This is a trivial wrapper around ckfree to make it possible to
27 *	pass ckfree as a pointer.
28 *
29 * Results:
30 *	None.
31 *
32 * Side effects:
33 *	Deallocates the image.
34 *
35 *----------------------------------------------------------------------
36 */
37
38static int
39DestroyImage(imagePtr)
40     XImage *imagePtr;		/* image to free */
41{
42    if (imagePtr) {
43	if (imagePtr->data) {
44	    ckfree((char*)imagePtr->data);
45	}
46	ckfree((char*)imagePtr);
47    }
48    return 0;
49}
50
51/*
52 *----------------------------------------------------------------------
53 *
54 * ImageGetPixel --
55 *
56 *	Get a single pixel from an image.
57 *
58 * Results:
59 *	Returns the 32 bit pixel value.
60 *
61 * Side effects:
62 *	None.
63 *
64 *----------------------------------------------------------------------
65 */
66
67static unsigned long
68ImageGetPixel(image, x, y)
69    XImage *image;
70    int x, y;
71{
72    unsigned long pixel = 0;
73    unsigned char *srcPtr = &(image->data[(y * image->bytes_per_line)
74	    + ((x * image->bits_per_pixel) / NBBY)]);
75
76    switch (image->bits_per_pixel) {
77	case 32:
78	case 24:
79	    pixel = RGB(srcPtr[2], srcPtr[1], srcPtr[0]);
80	    break;
81	case 16:
82	    pixel = RGB(((((WORD*)srcPtr)[0]) >> 7) & 0xf8,
83		    ((((WORD*)srcPtr)[0]) >> 2) & 0xf8,
84		    ((((WORD*)srcPtr)[0]) << 3) & 0xf8);
85	    break;
86	case 8:
87	    pixel = srcPtr[0];
88	    break;
89	case 4:
90	    pixel = ((x%2) ? (*srcPtr) : ((*srcPtr) >> 4)) & 0x0f;
91	    break;
92	case 1:
93	    pixel = ((*srcPtr) & (0x80 >> (x%8))) ? 1 : 0;
94	    break;
95    }
96    return pixel;
97}
98
99/*
100 *----------------------------------------------------------------------
101 *
102 * PutPixel --
103 *
104 *	Set a single pixel in an image.
105 *
106 * Results:
107 *	None.
108 *
109 * Side effects:
110 *	None.
111 *
112 *----------------------------------------------------------------------
113 */
114
115static int
116PutPixel(image, x, y, pixel)
117    XImage *image;
118    int x, y;
119    unsigned long pixel;
120{
121    unsigned char *destPtr = &(image->data[(y * image->bytes_per_line)
122	    + ((x * image->bits_per_pixel) / NBBY)]);
123
124    switch  (image->bits_per_pixel) {
125	case 32:
126	    /*
127	     * Pixel is DWORD: 0x00BBGGRR
128	     */
129
130	    destPtr[3] = 0;
131	case 24:
132	    /*
133	     * Pixel is triplet: 0xBBGGRR.
134	     */
135
136	    destPtr[0] = (unsigned char) GetBValue(pixel);
137	    destPtr[1] = (unsigned char) GetGValue(pixel);
138	    destPtr[2] = (unsigned char) GetRValue(pixel);
139	    break;
140	case 16:
141	    /*
142	     * Pixel is WORD: 5-5-5 (R-G-B)
143	     */
144
145	    (*(WORD*)destPtr) =
146		((GetRValue(pixel) & 0xf8) << 7)
147		| ((GetGValue(pixel) & 0xf8) <<2)
148		| ((GetBValue(pixel) & 0xf8) >> 3);
149	    break;
150	case 8:
151	    /*
152	     * Pixel is 8-bit index into color table.
153	     */
154
155	    (*destPtr) = (unsigned char) pixel;
156	    break;
157	case 4:
158	    /*
159	     * Pixel is 4-bit index in MSBFirst order.
160	     */
161	    if (x%2) {
162		(*destPtr) = (unsigned char) (((*destPtr) & 0xf0)
163		    | (pixel & 0x0f));
164	    } else {
165		(*destPtr) = (unsigned char) (((*destPtr) & 0x0f)
166		    | ((pixel << 4) & 0xf0));
167	    }
168	    break;
169	case 1: {
170	    /*
171	     * Pixel is bit in MSBFirst order.
172	     */
173
174	    int mask = (0x80 >> (x%8));
175	    if (pixel) {
176		(*destPtr) |= mask;
177	    } else {
178		(*destPtr) &= ~mask;
179	    }
180	}
181	break;
182    }
183    return 0;
184}
185
186/*
187 *----------------------------------------------------------------------
188 *
189 * XCreateImage --
190 *
191 *	Allocates storage for a new XImage.
192 *
193 * Results:
194 *	Returns a newly allocated XImage.
195 *
196 * Side effects:
197 *	None.
198 *
199 *----------------------------------------------------------------------
200 */
201
202XImage *
203XCreateImage(display, visual, depth, format, offset, data, width, height,
204	bitmap_pad, bytes_per_line)
205    Display* display;
206    Visual* visual;
207    unsigned int depth;
208    int format;
209    int offset;
210    char* data;
211    unsigned int width;
212    unsigned int height;
213    int bitmap_pad;
214    int bytes_per_line;
215{
216    XImage* imagePtr = (XImage *) ckalloc(sizeof(XImage));
217    imagePtr->width = width;
218    imagePtr->height = height;
219    imagePtr->xoffset = offset;
220    imagePtr->format = format;
221    imagePtr->data = data;
222    imagePtr->byte_order = LSBFirst;
223    imagePtr->bitmap_unit = 8;
224    imagePtr->bitmap_bit_order = LSBFirst;
225    imagePtr->bitmap_pad = bitmap_pad;
226    imagePtr->bits_per_pixel = depth;
227    imagePtr->depth = depth;
228
229    /*
230     * Under Windows, bitmap_pad must be on an LONG data-type boundary.
231     */
232
233#define LONGBITS    (sizeof(LONG) * 8)
234
235    bitmap_pad = (bitmap_pad + LONGBITS - 1) / LONGBITS * LONGBITS;
236
237    /*
238     * Round to the nearest bitmap_pad boundary.
239     */
240
241    if (bytes_per_line) {
242	imagePtr->bytes_per_line = bytes_per_line;
243    } else {
244	imagePtr->bytes_per_line = (((depth * width)
245		+ (bitmap_pad - 1)) >> 3) & ~((bitmap_pad >> 3) - 1);
246    }
247
248    imagePtr->red_mask = 0;
249    imagePtr->green_mask = 0;
250    imagePtr->blue_mask = 0;
251
252    imagePtr->f.put_pixel = PutPixel;
253    imagePtr->f.get_pixel = ImageGetPixel;
254    imagePtr->f.destroy_image = DestroyImage;
255    imagePtr->f.create_image = NULL;
256    imagePtr->f.sub_image = NULL;
257    imagePtr->f.add_pixel = NULL;
258
259    return imagePtr;
260}
261
262/*
263 *----------------------------------------------------------------------
264 * XGetImageZPixmap --
265 *
266 *	This function copies data from a pixmap or window into an
267 *	XImage.  This handles the ZPixmap case only.
268 *
269 * Results:
270 *	Returns a newly allocated image containing the data from the
271 *	given rectangle of the given drawable.
272 *
273 * Side effects:
274 *	None.
275 *
276 * This procedure is adapted from the XGetImage implementation in TkNT.
277 * That code is Copyright (c) 1994 Software Research Associates, Inc.
278 *
279 *----------------------------------------------------------------------
280 */
281
282static XImage *
283XGetImageZPixmap(display, d, x, y, width, height, plane_mask, format)
284    Display* display;
285    Drawable d;
286    int x;
287    int y;
288    unsigned int width;
289    unsigned int height;
290    unsigned long plane_mask;
291    int	format;
292{
293    TkWinDrawable *twdPtr = (TkWinDrawable *)d;
294    XImage *ret_image;
295    HDC hdc, hdcMem;
296    HBITMAP hbmp, hbmpPrev;
297    BITMAPINFO *bmInfo = NULL;
298    HPALETTE hPal, hPalPrev1, hPalPrev2;
299    int size;
300    unsigned int n;
301    unsigned int depth;
302    unsigned char *data;
303    TkWinDCState state;
304    BOOL ret;
305
306    if (format != ZPixmap) {
307	TkpDisplayWarning(
308	    "XGetImageZPixmap: only ZPixmap types are implemented",
309	    "XGetImageZPixmap Failure");
310	return NULL;
311    }
312
313    hdc = TkWinGetDrawableDC(display, d, &state);
314
315    /* Need to do a Blt operation to copy into a new bitmap */
316    hbmp = CreateCompatibleBitmap(hdc, width, height);
317    hdcMem = CreateCompatibleDC(hdc);
318    hbmpPrev = SelectObject(hdcMem, hbmp);
319    hPal = state.palette;
320    if (hPal) {
321        hPalPrev1 = SelectPalette(hdcMem, hPal, FALSE);
322        n = RealizePalette(hdcMem);
323        if (n > 0) {
324            UpdateColors (hdcMem);
325        }
326	hPalPrev2 = SelectPalette(hdc, hPal, FALSE);
327        n = RealizePalette(hdc);
328        if (n > 0) {
329            UpdateColors (hdc);
330        }
331    }
332
333    ret = BitBlt(hdcMem, 0, 0, width, height, hdc, x, y, SRCCOPY);
334    if (hPal) {
335	SelectPalette(hdc, hPalPrev2, FALSE);
336    }
337    SelectObject(hdcMem, hbmpPrev);
338    TkWinReleaseDrawableDC(d, hdc, &state);
339    if (ret == FALSE) {
340	goto cleanup;
341    }
342    if (twdPtr->type == TWD_WINDOW) {
343	depth = Tk_Depth((Tk_Window) twdPtr->window.winPtr);
344    } else {
345	depth = twdPtr->bitmap.depth;
346    }
347
348    size = sizeof(BITMAPINFO);
349    if (depth <= 8) {
350	size += sizeof(unsigned short) * (1 << depth);
351    }
352    bmInfo = (BITMAPINFO *) ckalloc(size);
353
354    bmInfo->bmiHeader.biSize               = sizeof(BITMAPINFOHEADER);
355    bmInfo->bmiHeader.biWidth              = width;
356    bmInfo->bmiHeader.biHeight             = -(int) height;
357    bmInfo->bmiHeader.biPlanes             = 1;
358    bmInfo->bmiHeader.biBitCount           = depth;
359    bmInfo->bmiHeader.biCompression        = BI_RGB;
360    bmInfo->bmiHeader.biSizeImage          = 0;
361    bmInfo->bmiHeader.biXPelsPerMeter      = 0;
362    bmInfo->bmiHeader.biYPelsPerMeter      = 0;
363    bmInfo->bmiHeader.biClrUsed            = 0;
364    bmInfo->bmiHeader.biClrImportant       = 0;
365
366    if (depth == 1) {
367	unsigned char *p, *pend;
368	GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS);
369	data = ckalloc(bmInfo->bmiHeader.biSizeImage);
370	if (!data) {
371	    /* printf("Failed to allocate data area for XImage.\n"); */
372	    ret_image = NULL;
373	    goto cleanup;
374	}
375	ret_image = XCreateImage(display, NULL, depth, ZPixmap, 0, data,
376		width, height, 32, ((width + 31) >> 3) & ~1);
377	if (ret_image == NULL) {
378	    ckfree(data);
379	    goto cleanup;
380	}
381
382	/* Get the BITMAP info into the Image. */
383	if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo,
384		DIB_PAL_COLORS) == 0) {
385	    ckfree((char *) ret_image->data);
386	    ckfree((char *) ret_image);
387	    ret_image = NULL;
388	    goto cleanup;
389	}
390	p = data;
391	pend = data + bmInfo->bmiHeader.biSizeImage;
392	while (p < pend) {
393	    *p = ~*p;
394	    p++;
395	}
396    } else if (depth == 8) {
397	unsigned short *palette;
398	unsigned int i;
399	unsigned char *p;
400
401	GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS);
402	data = ckalloc(bmInfo->bmiHeader.biSizeImage);
403	if (!data) {
404	    /* printf("Failed to allocate data area for XImage.\n"); */
405	    ret_image = NULL;
406	    goto cleanup;
407	}
408	ret_image = XCreateImage(display, NULL, 8, ZPixmap, 0, data,
409		width, height, 8, width);
410	if (ret_image == NULL) {
411	    ckfree((char *) data);
412	    goto cleanup;
413	}
414
415	/* Get the BITMAP info into the Image. */
416	if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo,
417		DIB_PAL_COLORS) == 0) {
418	    ckfree((char *) ret_image->data);
419	    ckfree((char *) ret_image);
420	    ret_image = NULL;
421	    goto cleanup;
422	}
423	p = data;
424	palette = (unsigned short *) bmInfo->bmiColors;
425	for (i = 0; i < bmInfo->bmiHeader.biSizeImage; i++, p++) {
426	    *p = (unsigned char) palette[*p];
427	}
428    } else if (depth == 16) {
429	GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS);
430	data = ckalloc(bmInfo->bmiHeader.biSizeImage);
431	if (!data) {
432	    /* printf("Failed to allocate data area for XImage.\n"); */
433	    ret_image = NULL;
434	    goto cleanup;
435	}
436	ret_image = XCreateImage(display, NULL, 16, ZPixmap, 0, data,
437		width, height, 16, 0 /* will be calc'ed from bitmap_pad */);
438	if (ret_image == NULL) {
439	    ckfree((char *) data);
440	    goto cleanup;
441	}
442
443	/* Get the BITMAP info directly into the Image. */
444	if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo,
445		    DIB_RGB_COLORS) == 0) {
446	    ckfree((char *) ret_image->data);
447	    ckfree((char *) ret_image);
448	    ret_image = NULL;
449	    goto cleanup;
450	}
451    } else {
452	GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS);
453	data = ckalloc(width * height * 4);
454	if (!data) {
455	    /* printf("Failed to allocate data area for XImage.\n"); */
456	    ret_image = NULL;
457	    goto cleanup;
458	}
459	ret_image = XCreateImage(display, NULL, 32, ZPixmap, 0, data,
460		width, height, 0, width * 4);
461	if (ret_image == NULL) {
462	    ckfree((char *) data);
463	    goto cleanup;
464	}
465
466	if (depth <= 24) {
467	    /*
468	     * This used to handle 16 and 24 bpp, but now just handles 24.
469	     * It can likely be optimized for that. -- hobbs
470	     */
471	    unsigned char *smallBitData, *smallBitBase, *bigBitData;
472	    unsigned int byte_width, h, w;
473
474	    byte_width = ((width * 3 + 3) & ~3);
475	    smallBitBase = ckalloc(byte_width * height);
476	    if (!smallBitBase) {
477		ckfree((char *) ret_image->data);
478		ckfree((char *) ret_image);
479		ret_image = NULL;
480		goto cleanup;
481	    }
482	    smallBitData = smallBitBase;
483
484	    /* Get the BITMAP info into the Image. */
485	    if (GetDIBits(hdcMem, hbmp, 0, height, smallBitData, bmInfo,
486			DIB_RGB_COLORS) == 0) {
487		ckfree((char *) ret_image->data);
488		ckfree((char *) ret_image);
489		ckfree((char *) smallBitBase);
490		ret_image = NULL;
491		goto cleanup;
492	    }
493	    /* Copy the 24 Bit Pixmap to a 32-Bit one. */
494	    for (h = 0; h < height; h++) {
495		bigBitData   = ret_image->data + h * ret_image->bytes_per_line;
496		smallBitData = smallBitBase + h * byte_width;
497
498		for (w = 0; w < width; w++) {
499		    *bigBitData++ = ((*smallBitData++));
500		    *bigBitData++ = ((*smallBitData++));
501		    *bigBitData++ = ((*smallBitData++));
502		    *bigBitData++ = 0;
503		}
504	    }
505	    /* Free the Device contexts, and the Bitmap */
506	    ckfree((char *) smallBitBase);
507	} else {
508	    /* Get the BITMAP info directly into the Image. */
509	    if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo,
510		    DIB_RGB_COLORS) == 0) {
511		ckfree((char *) ret_image->data);
512		ckfree((char *) ret_image);
513		ret_image = NULL;
514		goto cleanup;
515	    }
516	}
517    }
518
519  cleanup:
520    if (bmInfo) {
521	ckfree((char *) bmInfo);
522    }
523    if (hPal) {
524	SelectPalette(hdcMem, hPalPrev1, FALSE);
525    }
526    DeleteDC(hdcMem);
527    DeleteObject(hbmp);
528
529    return ret_image;
530}
531
532/*
533 *----------------------------------------------------------------------
534 *
535 * XGetImage --
536 *
537 *	This function copies data from a pixmap or window into an
538 *	XImage.
539 *
540 * Results:
541 *	Returns a newly allocated image containing the data from the
542 *	given rectangle of the given drawable.
543 *
544 * Side effects:
545 *	None.
546 *
547 *----------------------------------------------------------------------
548 */
549
550XImage *
551XGetImage(display, d, x, y, width, height, plane_mask, format)
552    Display* display;
553    Drawable d;
554    int x;
555    int y;
556    unsigned int width;
557    unsigned int height;
558    unsigned long plane_mask;
559    int	format;
560{
561    TkWinDrawable *twdPtr = (TkWinDrawable *)d;
562    XImage *imagePtr;
563    HDC dc;
564
565    display->request++;
566
567    if (twdPtr == NULL) {
568	/*
569	 * Avoid unmapped windows or bad drawables
570	 */
571	return NULL;
572    }
573
574    if (twdPtr->type != TWD_BITMAP) {
575	/*
576	 * This handles TWD_WINDOW or TWD_WINDC, always creating a 32bit
577	 * image.  If the window being copied isn't visible (unmapped or
578	 * obscured), we quietly stop copying (no user error).
579	 * The user will see black where the widget should be.
580	 * This branch is likely followed in favor of XGetImageZPixmap as
581	 * postscript printed widgets require RGB data.
582	 */
583	TkWinDCState state;
584	unsigned int xx, yy, size;
585	COLORREF pixel;
586
587	dc = TkWinGetDrawableDC(display, d, &state);
588
589	imagePtr = XCreateImage(display, NULL, 32,
590		format, 0, NULL, width, height, 32, 0);
591	size = imagePtr->bytes_per_line * imagePtr->height;
592	imagePtr->data = ckalloc(size);
593	ZeroMemory(imagePtr->data, size);
594
595	for (yy = 0; yy < height; yy++) {
596	    for (xx = 0; xx < width; xx++) {
597		pixel = GetPixel(dc, x+(int)xx, y+(int)yy);
598		if (pixel == CLR_INVALID) {
599		    break;
600		}
601		PutPixel(imagePtr, xx, yy, pixel);
602	    }
603	}
604
605	TkWinReleaseDrawableDC(d, dc, &state);
606    } else if (format == ZPixmap) {
607	/*
608	 * This actually handles most TWD_WINDOW requests, but it varies
609	 * from the above in that it really does a screen capture of
610	 * an area, which is consistent with the Unix behavior, but does
611	 * not appear to handle all bit depths correctly. -- hobbs
612	 */
613	imagePtr = XGetImageZPixmap(display, d, x, y,
614		width, height, plane_mask, format);
615    } else {
616	char *errMsg = NULL;
617	char infoBuf[sizeof(BITMAPINFO) + sizeof(RGBQUAD)];
618	BITMAPINFO *infoPtr = (BITMAPINFO*)infoBuf;
619
620	if (twdPtr->bitmap.handle == NULL) {
621	    errMsg = "XGetImage: not implemented for empty bitmap handles";
622	} else if (format != XYPixmap) {
623	    errMsg = "XGetImage: not implemented for format != XYPixmap";
624	} else if (plane_mask != 1) {
625	    errMsg = "XGetImage: not implemented for plane_mask != 1";
626	}
627	if (errMsg != NULL) {
628	    /*
629	     * Do a soft warning for the unsupported XGetImage types.
630	     */
631	    TkpDisplayWarning(errMsg, "XGetImage Failure");
632	    return NULL;
633	}
634
635	imagePtr = XCreateImage(display, NULL, 1, XYBitmap, 0, NULL,
636		width, height, 32, 0);
637	imagePtr->data = ckalloc(imagePtr->bytes_per_line * imagePtr->height);
638
639	dc = GetDC(NULL);
640
641	GetDIBits(dc, twdPtr->bitmap.handle, 0, height, NULL,
642		infoPtr, DIB_RGB_COLORS);
643
644	infoPtr->bmiHeader.biSize		= sizeof(BITMAPINFOHEADER);
645	infoPtr->bmiHeader.biWidth		= width;
646	infoPtr->bmiHeader.biHeight		= -(LONG)height;
647	infoPtr->bmiHeader.biPlanes		= 1;
648	infoPtr->bmiHeader.biBitCount		= 1;
649	infoPtr->bmiHeader.biCompression	= BI_RGB;
650	infoPtr->bmiHeader.biSizeImage          = 0;
651	infoPtr->bmiHeader.biXPelsPerMeter	= 0;
652	infoPtr->bmiHeader.biYPelsPerMeter	= 0;
653	infoPtr->bmiHeader.biClrUsed		= 0;
654	infoPtr->bmiHeader.biClrImportant	= 0;
655
656	GetDIBits(dc, twdPtr->bitmap.handle, 0, height, imagePtr->data,
657		infoPtr, DIB_RGB_COLORS);
658	ReleaseDC(NULL, dc);
659    }
660
661    return imagePtr;
662}
663