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$
12 */
13
14#include "tkWinInt.h"
15
16static int		DestroyImage(XImage* data);
17static unsigned long	ImageGetPixel(XImage *image, int x, int y);
18static int		PutPixel(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 pass
27 *	ckfree as a pointer.
28 *
29 * Results:
30 *	None.
31 *
32 * Side effects:
33 *	Deallocates the image.
34 *
35 *----------------------------------------------------------------------
36 */
37
38static int
39DestroyImage(
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(
69    XImage *image,
70    int x, int 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(
117    XImage *image,
118    int x, int 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) = ((GetRValue(pixel) & 0xf8) << 7)
146		| ((GetGValue(pixel) & 0xf8) <<2)
147		| ((GetBValue(pixel) & 0xf8) >> 3);
148	break;
149    case 8:
150	/*
151	 * Pixel is 8-bit index into color table.
152	 */
153
154	(*destPtr) = (unsigned char) pixel;
155	break;
156    case 4:
157	/*
158	 * Pixel is 4-bit index in MSBFirst order.
159	 */
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
176	if (pixel) {
177	    (*destPtr) |= mask;
178	} else {
179	    (*destPtr) &= ~mask;
180	}
181	break;
182    }
183    }
184    return 0;
185}
186
187/*
188 *----------------------------------------------------------------------
189 *
190 * XCreateImage --
191 *
192 *	Allocates storage for a new XImage.
193 *
194 * Results:
195 *	Returns a newly allocated XImage.
196 *
197 * Side effects:
198 *	None.
199 *
200 *----------------------------------------------------------------------
201 */
202
203XImage *
204XCreateImage(
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 *
265 * XGetImageZPixmap --
266 *
267 *	This function copies data from a pixmap or window into an XImage. This
268 *	handles the ZPixmap case only.
269 *
270 * Results:
271 *	Returns a newly allocated image containing the data from the given
272 *	rectangle of the given drawable.
273 *
274 * Side effects:
275 *	None.
276 *
277 * This procedure is adapted from the XGetImage implementation in TkNT. That
278 * code is Copyright (c) 1994 Software Research Associates, Inc.
279 *
280 *----------------------------------------------------------------------
281 */
282
283static XImage *
284XGetImageZPixmap(
285    Display *display,
286    Drawable d,
287    int x, int y,
288    unsigned int width, unsigned int height,
289    unsigned long plane_mask,
290    int	format)
291{
292    TkWinDrawable *twdPtr = (TkWinDrawable *)d;
293    XImage *ret_image;
294    HDC hdc, hdcMem;
295    HBITMAP hbmp, hbmpPrev;
296    BITMAPINFO *bmInfo = NULL;
297    HPALETTE hPal, hPalPrev1 = 0, hPalPrev2 = 0;
298    int size;
299    unsigned int n;
300    unsigned int depth;
301    unsigned char *data;
302    TkWinDCState state;
303    BOOL ret;
304
305    if (format != ZPixmap) {
306	TkpDisplayWarning(
307		"XGetImageZPixmap: only ZPixmap types are implemented",
308		"XGetImageZPixmap Failure");
309	return NULL;
310    }
311
312    hdc = TkWinGetDrawableDC(display, d, &state);
313
314    /*
315     * Need to do a Blt operation to copy into a new bitmap.
316     */
317
318    hbmp = CreateCompatibleBitmap(hdc, (int) width, (int) height);
319    hdcMem = CreateCompatibleDC(hdc);
320    hbmpPrev = SelectObject(hdcMem, hbmp);
321    hPal = state.palette;
322    if (hPal) {
323	hPalPrev1 = SelectPalette(hdcMem, hPal, FALSE);
324	n = RealizePalette(hdcMem);
325	if (n > 0) {
326	    UpdateColors(hdcMem);
327	}
328	hPalPrev2 = SelectPalette(hdc, hPal, FALSE);
329	n = RealizePalette(hdc);
330	if (n > 0) {
331	    UpdateColors(hdc);
332	}
333    }
334
335    ret = BitBlt(hdcMem, 0, 0, (int) width, (int) height, hdc, x, y, SRCCOPY);
336    if (hPal) {
337	SelectPalette(hdc, hPalPrev2, FALSE);
338    }
339    SelectObject(hdcMem, hbmpPrev);
340    TkWinReleaseDrawableDC(d, hdc, &state);
341    if (ret == FALSE) {
342	ret_image = NULL;
343	goto cleanup;
344    }
345    if (twdPtr->type == TWD_WINDOW) {
346	depth = Tk_Depth((Tk_Window) twdPtr->window.winPtr);
347    } else {
348	depth = twdPtr->bitmap.depth;
349    }
350
351    size = sizeof(BITMAPINFO);
352    if (depth <= 8) {
353	size += sizeof(unsigned short) * (1 << depth);
354    }
355    bmInfo = (BITMAPINFO *) ckalloc((unsigned)size);
356
357    bmInfo->bmiHeader.biSize		= sizeof(BITMAPINFOHEADER);
358    bmInfo->bmiHeader.biWidth		= width;
359    bmInfo->bmiHeader.biHeight		= -(int) height;
360    bmInfo->bmiHeader.biPlanes		= 1;
361    bmInfo->bmiHeader.biBitCount	= depth;
362    bmInfo->bmiHeader.biCompression	= BI_RGB;
363    bmInfo->bmiHeader.biSizeImage	= 0;
364    bmInfo->bmiHeader.biXPelsPerMeter	= 0;
365    bmInfo->bmiHeader.biYPelsPerMeter	= 0;
366    bmInfo->bmiHeader.biClrUsed		= 0;
367    bmInfo->bmiHeader.biClrImportant	= 0;
368
369    if (depth == 1) {
370	unsigned char *p, *pend;
371
372	GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS);
373	data = ckalloc(bmInfo->bmiHeader.biSizeImage);
374	if (!data) {
375	    /* printf("Failed to allocate data area for XImage.\n"); */
376	    ret_image = NULL;
377	    goto cleanup;
378	}
379	ret_image = XCreateImage(display, NULL, depth, ZPixmap, 0, data,
380		width, height, 32, (int) ((width + 31) >> 3) & ~1);
381	if (ret_image == NULL) {
382	    ckfree(data);
383	    goto cleanup;
384	}
385
386	/*
387	 * Get the BITMAP info into the Image.
388	 */
389
390	if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo,
391		DIB_PAL_COLORS) == 0) {
392	    ckfree((char *) ret_image->data);
393	    ckfree((char *) ret_image);
394	    ret_image = NULL;
395	    goto cleanup;
396	}
397	p = data;
398	pend = data + bmInfo->bmiHeader.biSizeImage;
399	while (p < pend) {
400	    *p = ~*p;
401	    p++;
402	}
403    } else if (depth == 8) {
404	unsigned short *palette;
405	unsigned int i;
406	unsigned char *p;
407
408	GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS);
409	data = ckalloc(bmInfo->bmiHeader.biSizeImage);
410	if (!data) {
411	    /* printf("Failed to allocate data area for XImage.\n"); */
412	    ret_image = NULL;
413	    goto cleanup;
414	}
415	ret_image = XCreateImage(display, NULL, 8, ZPixmap, 0, data,
416		width, height, 8, (int) width);
417	if (ret_image == NULL) {
418	    ckfree((char *) data);
419	    goto cleanup;
420	}
421
422	/*
423	 * Get the BITMAP info into the Image.
424	 */
425
426	if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo,
427		DIB_PAL_COLORS) == 0) {
428	    ckfree((char *) ret_image->data);
429	    ckfree((char *) ret_image);
430	    ret_image = NULL;
431	    goto cleanup;
432	}
433	p = data;
434	palette = (unsigned short *) bmInfo->bmiColors;
435	for (i = 0; i < bmInfo->bmiHeader.biSizeImage; i++, p++) {
436	    *p = (unsigned char) palette[*p];
437	}
438    } else if (depth == 16) {
439	GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS);
440	data = ckalloc(bmInfo->bmiHeader.biSizeImage);
441	if (!data) {
442	    /* printf("Failed to allocate data area for XImage.\n"); */
443	    ret_image = NULL;
444	    goto cleanup;
445	}
446	ret_image = XCreateImage(display, NULL, 16, ZPixmap, 0, data,
447		width, height, 16, 0 /* will be calc'ed from bitmap_pad */);
448	if (ret_image == NULL) {
449	    ckfree((char *) data);
450	    goto cleanup;
451	}
452
453	/*
454	 * Get the BITMAP info directly into the Image.
455	 */
456
457	if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo,
458		DIB_RGB_COLORS) == 0) {
459	    ckfree((char *) ret_image->data);
460	    ckfree((char *) ret_image);
461	    ret_image = NULL;
462	    goto cleanup;
463	}
464    } else {
465	GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS);
466	data = ckalloc(width * height * 4);
467	if (!data) {
468	    /* printf("Failed to allocate data area for XImage.\n"); */
469	    ret_image = NULL;
470	    goto cleanup;
471	}
472	ret_image = XCreateImage(display, NULL, 32, ZPixmap, 0, data,
473		width, height, 0, (int) width * 4);
474	if (ret_image == NULL) {
475	    ckfree((char *) data);
476	    goto cleanup;
477	}
478
479	if (depth <= 24) {
480	    /*
481	     * This used to handle 16 and 24 bpp, but now just handles 24. It
482	     * can likely be optimized for that. -- hobbs
483	     */
484
485	    unsigned char *smallBitData, *smallBitBase, *bigBitData;
486	    unsigned int byte_width, h, w;
487
488	    byte_width = ((width * 3 + 3) & ~(unsigned)3);
489	    smallBitBase = ckalloc(byte_width * height);
490	    if (!smallBitBase) {
491		ckfree((char *) ret_image->data);
492		ckfree((char *) ret_image);
493		ret_image = NULL;
494		goto cleanup;
495	    }
496	    smallBitData = smallBitBase;
497
498	    /*
499	     * Get the BITMAP info into the Image.
500	     */
501
502	    if (GetDIBits(hdcMem, hbmp, 0, height, smallBitData, bmInfo,
503		    DIB_RGB_COLORS) == 0) {
504		ckfree((char *) ret_image->data);
505		ckfree((char *) ret_image);
506		ckfree((char *) smallBitBase);
507		ret_image = NULL;
508		goto cleanup;
509	    }
510
511	    /*
512	     * Copy the 24 Bit Pixmap to a 32-Bit one.
513	     */
514
515	    for (h = 0; h < height; h++) {
516		bigBitData   = ret_image->data + h * ret_image->bytes_per_line;
517		smallBitData = smallBitBase + h * byte_width;
518
519		for (w = 0; w < width; w++) {
520		    *bigBitData++ = ((*smallBitData++));
521		    *bigBitData++ = ((*smallBitData++));
522		    *bigBitData++ = ((*smallBitData++));
523		    *bigBitData++ = 0;
524		}
525	    }
526
527	    /*
528	     * Free the Device contexts, and the Bitmap.
529	     */
530
531	    ckfree((char *) smallBitBase);
532	} else {
533	    /*
534	     * Get the BITMAP info directly into the Image.
535	     */
536
537	    if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo,
538		    DIB_RGB_COLORS) == 0) {
539		ckfree((char *) ret_image->data);
540		ckfree((char *) ret_image);
541		ret_image = NULL;
542		goto cleanup;
543	    }
544	}
545    }
546
547  cleanup:
548    if (bmInfo) {
549	ckfree((char *) bmInfo);
550    }
551    if (hPal) {
552	SelectPalette(hdcMem, hPalPrev1, FALSE);
553    }
554    DeleteDC(hdcMem);
555    DeleteObject(hbmp);
556
557    return ret_image;
558}
559
560/*
561 *----------------------------------------------------------------------
562 *
563 * XGetImage --
564 *
565 *	This function copies data from a pixmap or window into an XImage.
566 *
567 * Results:
568 *	Returns a newly allocated image containing the data from the given
569 *	rectangle of the given drawable.
570 *
571 * Side effects:
572 *	None.
573 *
574 *----------------------------------------------------------------------
575 */
576
577XImage *
578XGetImage(
579    Display* display,
580    Drawable d,
581    int x, int y,
582    unsigned int width, unsigned int height,
583    unsigned long plane_mask,
584    int	format)
585{
586    TkWinDrawable *twdPtr = (TkWinDrawable *)d;
587    XImage *imagePtr;
588    HDC dc;
589
590    display->request++;
591
592    if (twdPtr == NULL) {
593	/*
594	 * Avoid unmapped windows or bad drawables
595	 */
596
597	return NULL;
598    }
599
600    if (twdPtr->type != TWD_BITMAP) {
601	/*
602	 * This handles TWD_WINDOW or TWD_WINDC, always creating a 32bit
603	 * image. If the window being copied isn't visible (unmapped or
604	 * obscured), we quietly stop copying (no user error). The user will
605	 * see black where the widget should be. This branch is likely
606	 * followed in favor of XGetImageZPixmap as postscript printed widgets
607	 * require RGB data.
608	 */
609
610	TkWinDCState state;
611	unsigned int xx, yy, size;
612	COLORREF pixel;
613
614	dc = TkWinGetDrawableDC(display, d, &state);
615
616	imagePtr = XCreateImage(display, NULL, 32, format, 0, NULL,
617		width, height, 32, 0);
618	size = imagePtr->bytes_per_line * imagePtr->height;
619	imagePtr->data = ckalloc(size);
620	ZeroMemory(imagePtr->data, size);
621
622	for (yy = 0; yy < height; yy++) {
623	    for (xx = 0; xx < width; xx++) {
624		pixel = GetPixel(dc, x+(int)xx, y+(int)yy);
625		if (pixel == CLR_INVALID) {
626		    break;
627		}
628		PutPixel(imagePtr, (int) xx, (int) yy, pixel);
629	    }
630	}
631
632	TkWinReleaseDrawableDC(d, dc, &state);
633    } else if (format == ZPixmap) {
634	/*
635	 * This actually handles most TWD_WINDOW requests, but it varies from
636	 * the above in that it really does a screen capture of an area, which
637	 * is consistent with the Unix behavior, but does not appear to handle
638	 * all bit depths correctly. -- hobbs
639	 */
640
641	imagePtr = XGetImageZPixmap(display, d, x, y,
642		width, height, plane_mask, format);
643    } else {
644	char *errMsg = NULL;
645	char infoBuf[sizeof(BITMAPINFO) + sizeof(RGBQUAD)];
646	BITMAPINFO *infoPtr = (BITMAPINFO*)infoBuf;
647
648	if (twdPtr->bitmap.handle == NULL) {
649	    errMsg = "XGetImage: not implemented for empty bitmap handles";
650	} else if (format != XYPixmap) {
651	    errMsg = "XGetImage: not implemented for format != XYPixmap";
652	} else if (plane_mask != 1) {
653	    errMsg = "XGetImage: not implemented for plane_mask != 1";
654	}
655	if (errMsg != NULL) {
656	    /*
657	     * Do a soft warning for the unsupported XGetImage types.
658	     */
659
660	    TkpDisplayWarning(errMsg, "XGetImage Failure");
661	    return NULL;
662	}
663
664	imagePtr = XCreateImage(display, NULL, 1, XYBitmap, 0, NULL,
665		width, height, 32, 0);
666	imagePtr->data =
667		ckalloc((unsigned) imagePtr->bytes_per_line*imagePtr->height);
668
669	dc = GetDC(NULL);
670
671	GetDIBits(dc, twdPtr->bitmap.handle, 0, height, NULL,
672		infoPtr, DIB_RGB_COLORS);
673
674	infoPtr->bmiHeader.biSize		= sizeof(BITMAPINFOHEADER);
675	infoPtr->bmiHeader.biWidth		= width;
676	infoPtr->bmiHeader.biHeight		= -(LONG)height;
677	infoPtr->bmiHeader.biPlanes		= 1;
678	infoPtr->bmiHeader.biBitCount		= 1;
679	infoPtr->bmiHeader.biCompression	= BI_RGB;
680	infoPtr->bmiHeader.biSizeImage		= 0;
681	infoPtr->bmiHeader.biXPelsPerMeter	= 0;
682	infoPtr->bmiHeader.biYPelsPerMeter	= 0;
683	infoPtr->bmiHeader.biClrUsed		= 0;
684	infoPtr->bmiHeader.biClrImportant	= 0;
685
686	GetDIBits(dc, twdPtr->bitmap.handle, 0, height, imagePtr->data,
687		infoPtr, DIB_RGB_COLORS);
688	ReleaseDC(NULL, dc);
689    }
690
691    return imagePtr;
692}
693
694/*
695 * Local Variables:
696 * mode: c
697 * c-basic-offset: 4
698 * fill-column: 78
699 * End:
700 */
701