1/*
2 * XWD image handling
3 */
4
5#include "ctwm.h"
6
7#include <stdio.h>
8#include <stdlib.h>
9
10#include <X11/XWDFile.h>
11
12#include "screen.h"
13#include "animate.h"
14
15#include "image.h"
16#include "image_xwd.h"
17
18
19static Image *LoadXwdImage(const char *filename, ColorPair cp);
20static void compress(XImage *image, XColor *colors, int *ncolors);
21static void swapshort(char *bp, unsigned n);
22static void swaplong(char *bp, unsigned n);
23
24
25
26/*
27 * External entry
28 */
29Image *
30GetXwdImage(const char *name, ColorPair cp)
31{
32	/* Non-animated */
33	if(! strchr(name, '%')) {
34		return (LoadXwdImage(name, cp));
35	}
36
37	/* Animated */
38	return get_image_anim_cp(name, cp, LoadXwdImage);
39}
40
41
42/*
43 * Internal backend
44 */
45static Image *
46LoadXwdImage(const char *filename, ColorPair cp)
47{
48	FILE        *file;
49	char        *fullname;
50	XColor      colors [256];
51	XWDColor    xwdcolors [256];
52	unsigned    buffer_size;
53	XImage      *image;
54	unsigned char *imagedata;
55	Pixmap      pixret;
56	Visual      *visual;
57	char        win_name [256];
58	int         win_name_size;
59	int         ispipe;
60	int         i, len;
61	int         w, h, depth, ncolors;
62	int         scrn;
63	Colormap    cmap;
64	Colormap    stdcmap = Scr->RootColormaps.cwins[0]->colormap->c;
65	GC          gc;
66	XGCValues   gcvalues;
67	XWDFileHeader header;
68	Image       *ret;
69	unsigned long swaptest = 1;
70
71	ispipe = 0;
72	if(filename [0] == '|') {
73		file = (FILE *) popen(filename + 1, "r");
74		if(file == NULL) {
75			return NULL;
76		}
77		ispipe = 1;
78		if(AnimationActive) {
79			StopAnimation();
80		}
81		goto file_opened;
82	}
83	fullname = ExpandPixmapPath(filename);
84	if(! fullname) {
85		return NULL;
86	}
87	file = fopen(fullname, "r");
88	free(fullname);
89	if(file == NULL) {
90		if(reportfilenotfound) {
91			fprintf(stderr, "unable to locate %s\n", filename);
92		}
93		return NULL;
94	}
95file_opened:
96	len = fread((char *) &header, sizeof(header), 1, file);
97	if(len != 1) {
98		fprintf(stderr, "ctwm: cannot read %s\n", filename);
99		return NULL;
100	}
101	if(*(char *) &swaptest) {
102		swaplong((char *) &header, sizeof(header));
103	}
104	if(header.file_version != XWD_FILE_VERSION) {
105		fprintf(stderr, "ctwm: XWD file format version mismatch : %s\n", filename);
106		return NULL;
107	}
108	win_name_size = header.header_size - sizeof(header);
109	len = fread(win_name, win_name_size, 1, file);
110	if(len != 1) {
111		fprintf(stderr, "file %s has not the correct format\n", filename);
112		return NULL;
113	}
114
115	if(header.pixmap_format == XYPixmap) {
116		fprintf(stderr, "ctwm: XYPixmap XWD file not supported : %s\n", filename);
117		return NULL;
118	}
119	w       = header.pixmap_width;
120	h       = header.pixmap_height;
121	depth   = header.pixmap_depth;
122	ncolors = header.ncolors;
123	len = fread((char *) xwdcolors, sizeof(XWDColor), ncolors, file);
124	if(len != ncolors) {
125		fprintf(stderr, "file %s has not the correct format\n", filename);
126		return NULL;
127	}
128	if(*(char *) &swaptest) {
129		for(i = 0; i < ncolors; i++) {
130			swaplong((char *) &xwdcolors [i].pixel, 4);
131			swapshort((char *) &xwdcolors [i].red, 3 * 2);
132		}
133	}
134	for(i = 0; i < ncolors; i++) {
135		colors [i].pixel = xwdcolors [i].pixel;
136		colors [i].red   = xwdcolors [i].red;
137		colors [i].green = xwdcolors [i].green;
138		colors [i].blue  = xwdcolors [i].blue;
139		colors [i].flags = xwdcolors [i].flags;
140		colors [i].pad   = xwdcolors [i].pad;
141	}
142
143	scrn    = Scr->screen;
144	cmap    = AlternateCmap ? AlternateCmap : stdcmap;
145	visual  = Scr->d_visual;
146	gc      = DefaultGC(dpy, scrn);
147
148	buffer_size = header.bytes_per_line * h;
149	imagedata = malloc(buffer_size);
150	if(! imagedata) {
151		fprintf(stderr, "cannot allocate memory for image %s\n", filename);
152		return NULL;
153	}
154	len = fread(imagedata, (int) buffer_size, 1, file);
155	if(len != 1) {
156		free(imagedata);
157		fprintf(stderr, "file %s has not the correct format\n", filename);
158		return NULL;
159	}
160	if(ispipe) {
161		pclose(file);
162	}
163	else {
164		fclose(file);
165	}
166
167	image = XCreateImage(dpy, visual,  depth, header.pixmap_format,
168	                     0, (char *) imagedata, w, h,
169	                     header.bitmap_pad, header.bytes_per_line);
170	if(image == NULL) {
171		free(imagedata);
172		fprintf(stderr, "cannot create image for %s\n", filename);
173		return NULL;
174	}
175	if(header.pixmap_format == ZPixmap) {
176		compress(image, colors, &ncolors);
177	}
178	if(header.pixmap_format != XYBitmap) {
179		for(i = 0; i < ncolors; i++) {
180			XAllocColor(dpy, cmap, &(colors [i]));
181		}
182		for(i = 0; i < buffer_size; i++) {
183			imagedata [i] = (unsigned char) colors [imagedata [i]].pixel;
184		}
185	}
186	if(w > Scr->rootw) {
187		w = Scr->rootw;
188	}
189	if(h > Scr->rooth) {
190		h = Scr->rooth;
191	}
192
193	ret = AllocImage();
194	if(! ret) {
195		fprintf(stderr, "unable to allocate memory for image : %s\n", filename);
196		free(image);
197		free(imagedata);
198		for(i = 0; i < ncolors; i++) {
199			XFreeColors(dpy, cmap, &(colors [i].pixel), 1, 0L);
200		}
201		return NULL;
202	}
203	if(header.pixmap_format == XYBitmap) {
204		gcvalues.foreground = cp.fore;
205		gcvalues.background = cp.back;
206		XChangeGC(dpy, gc, GCForeground | GCBackground, &gcvalues);
207	}
208	if((w > (Scr->rootw / 2)) || (h > (Scr->rooth / 2))) {
209		int x, y;
210
211		pixret = XCreatePixmap(dpy, Scr->Root, Scr->rootw,
212		                       Scr->rooth, Scr->d_depth);
213		x = (Scr->rootw  - w) / 2;
214		y = (Scr->rooth - h) / 2;
215		XFillRectangle(dpy, pixret, gc, 0, 0, Scr->rootw, Scr->rooth);
216		XPutImage(dpy, pixret, gc, image, 0, 0, x, y, w, h);
217		ret->width  = Scr->rootw;
218		ret->height = Scr->rooth;
219	}
220	else {
221		pixret = XCreatePixmap(dpy, Scr->Root, w, h, depth);
222		XPutImage(dpy, pixret, gc, image, 0, 0, 0, 0, w, h);
223		ret->width  = w;
224		ret->height = h;
225	}
226	XDestroyImage(image);
227
228	ret->pixmap = pixret;
229	ret->mask   = None;
230	ret->next   = NULL;
231	return ret;
232}
233
234
235/*
236 * Utils
237 */
238static void
239compress(XImage *image, XColor *colors, int *ncolors)
240{
241	unsigned char ind  [256];
242	unsigned int  used [256];
243	int           i, j, size, nused;
244	unsigned char color;
245	XColor        newcolors [256];
246	unsigned char *imagedata;
247
248	for(i = 0; i < 256; i++) {
249		used [i] = 0;
250		ind  [i] = 0;
251	}
252	nused = 0;
253	size  = image->bytes_per_line * image->height;
254	imagedata = (unsigned char *) image->data;
255	for(i = 0; i < size; i++) {
256		if((i % image->bytes_per_line) > image->width) {
257			continue;
258		}
259		color = imagedata [i];
260		if(used [color] == 0) {
261			for(j = 0; j < nused; j++) {
262				if((colors [color].red   == newcolors [j].red)   &&
263				                (colors [color].green == newcolors [j].green) &&
264				                (colors [color].blue  == newcolors [j].blue)) {
265					break;
266				}
267			}
268			ind  [color] = j;
269			used [color] = 1;
270			if(j == nused) {
271				newcolors [j].red   = colors [color].red;
272				newcolors [j].green = colors [color].green;
273				newcolors [j].blue  = colors [color].blue;
274				nused++;
275			}
276		}
277	}
278	for(i = 0; i < size; i++) {
279		imagedata [i] = ind [imagedata [i]];
280	}
281	for(i = 0; i < nused; i++) {
282		colors [i] = newcolors [i];
283	}
284	*ncolors = nused;
285}
286
287
288static void
289swapshort(char *bp, unsigned n)
290{
291	char c;
292	char *ep = bp + n;
293
294	while(bp < ep) {
295		c = *bp;
296		*bp = *(bp + 1);
297		bp++;
298		*bp++ = c;
299	}
300}
301
302
303static void
304swaplong(char *bp, unsigned n)
305{
306	char c;
307	char *ep = bp + n;
308	char *sp;
309
310	while(bp < ep) {
311		sp = bp + 3;
312		c = *sp;
313		*sp = *bp;
314		*bp++ = c;
315		sp = bp + 1;
316		c = *sp;
317		*sp = *bp;
318		*bp++ = c;
319		bp += 2;
320	}
321}
322