1 /* -*-c-*- */
2 /* Copyright (C) 1993, Robert Nation
3  * Copyright (C) 1999 Carsten Haitzler and various contributors (imlib2)
4  * Copyright (C) 2002  Olivier Chapuis */
5 /* This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see: <http://www.gnu.org/licenses/>
17  */
18 
19 /*
20  *
21  * The png loader and PImageRGBtoPixel are from imlib2. The code is from raster
22  * (Carsten Haitzler) <raster@rasterman.com> <raster@valinux.com>
23  *
24  */
25 
26 /* ---------------------------- included header files ---------------------- */
27 
28 #include "config.h"
29 
30 #include <stdio.h>
31 #include <signal.h>
32 #include <ctype.h>
33 
34 #include <X11/Xlib.h>
35 #include <X11/Xmd.h>
36 
37 #include <fvwmlib.h>
38 #include "System.h"
39 #include "Strings.h"
40 #include "Picture.h"
41 #include "PictureUtils.h"
42 #include "Graphics.h"
43 #include "ColorUtils.h"
44 #include "Fxpm.h"
45 #include "Fpng.h"
46 #include "Fsvg.h"
47 #include "FRenderInit.h"
48 #include "Fcursor.h"
49 #include "FImage.h"
50 
51 /* ---------------------------- local definitions -------------------------- */
52 #define FIMAGE_CMD_ARGS \
53 	Display *dpy, char *path, CARD32 **argb_data, int *width, int *height
54 
55 #define FIMAGE_PASS_ARGS \
56 	dpy, path, argb_data, width, height
57 
58 typedef struct PImageLoader
59 {
60   char *extension;
61 #ifdef __STDC__
62   int (*func)(FIMAGE_CMD_ARGS);
63 #else
64   int (*func)();
65 #endif
66 } PImageLoader;
67 
68 /* ---------------------------- local macros ------------------------------- */
69 
70 /* ---------------------------- imports ------------------------------------ */
71 
72 /* ---------------------------- included code files ------------------------ */
73 
74 /* ---------------------------- local types -------------------------------- */
75 
76 /* ---------------------------- forward declarations ----------------------- */
77 
78 static Bool PImageLoadSvg(FIMAGE_CMD_ARGS);
79 static Bool PImageLoadPng(FIMAGE_CMD_ARGS);
80 static Bool PImageLoadXpm(FIMAGE_CMD_ARGS);
81 
82 /* ---------------------------- local variables ---------------------------- */
83 
84 PImageLoader Loaders[] =
85 {
86 	{ "xpm", PImageLoadXpm },
87 	{ "svg", PImageLoadSvg },
88 	{ "png", PImageLoadPng },
89 	{NULL,0}
90 };
91 
92 /* ---------------------------- exported variables (globals) --------------- */
93 
94 /* ---------------------------- local functions ---------------------------- */
95 
96 static
PImageLoadArgbDataFromFile(FIMAGE_CMD_ARGS)97 Bool PImageLoadArgbDataFromFile(FIMAGE_CMD_ARGS)
98 {
99 	int done = 0, i = 0, tried = -1;
100 	char *ext = NULL;
101 
102 	if (path == NULL)
103 		return False;
104 
105 	if (strlen(path) > 3)
106 	{
107 		ext = path + strlen(path) - 3;
108 	}
109 	/* first try to load by extension */
110 	while (!done && ext != NULL && Loaders[i].extension != NULL)
111 	{
112 		if (StrEquals(Loaders[i].extension, ext))
113 		{
114 			if (Loaders[i].func(FIMAGE_PASS_ARGS))
115 			{
116 				return True;
117 			}
118 			tried = i;
119 			done = 1;
120 		}
121 		i++;
122 	}
123 
124 	i = 0;
125 	while (Loaders[i].extension != NULL)
126 	{
127 		if (i != tried && Loaders[i].func(FIMAGE_PASS_ARGS))
128 		{
129 			return True;
130 		}
131 		i++;
132 	}
133 
134 	return False;
135 }
136 
137 /*
138  *
139  * svg loader
140  *
141  */
142 static
PImageLoadSvg(FIMAGE_CMD_ARGS)143 Bool PImageLoadSvg(FIMAGE_CMD_ARGS)
144 {
145 	char *allocated_path;
146 	char *render_opts;
147 	FRsvgHandle *rsvg;
148 	FRsvgDimensionData dim;
149 	CARD32 *data;
150 	Fcairo_surface_t *surface;
151 	Fcairo_t *cr;
152 	int i;
153 	int j;
154 	int b1;
155 	int b2;
156 	int w = 0;
157 	int h = 0;
158 	int dw = 0;
159 	int dh = 0;
160 	int w_sgn = 1;
161 	int h_sgn = 1;
162 	double angle = 0;
163 	double w_scale = 1;
164 	double h_scale = 1;
165 	double buf;
166 	Bool transpose = False;
167 	unsigned char a_value;
168 	unsigned char r_value;
169 	unsigned char g_value;
170 	unsigned char b_value;
171 
172 	if (!USE_SVG)
173 	{
174 		fvwm_debug(__func__,
175 				"[PImageLoadSvg]: Tried to load SVG file "
176 				"when FVWM has not been compiled with SVG support.\n");
177 		return False;
178 	}
179 
180 	/* Separate rendering options from path */
181 	render_opts = path = allocated_path = fxstrdup(path);
182 	if (*path == ':' && (path = strchr(path + 1, ':')))
183 	{
184 		*path = 0;
185 		path ++;
186 		render_opts ++;
187 	}
188 	else
189 	{
190 		render_opts = "";
191 	}
192 
193 	if (!(rsvg = Frsvg_handle_new_from_file(path, NULL)))
194 	{
195 		free(allocated_path);
196 
197 		return False;
198 	}
199 
200 	/* Parsing of rendering options */
201 	while (*render_opts)
202 	{
203 		i = 0;
204 		switch (*render_opts)
205 		{
206 		case '!':
207 			transpose = !transpose;
208 			break;
209 		case '*':
210 			if (sscanf(render_opts, "*%lf%n", &buf, &i) >= 1)
211 			{
212 				switch (render_opts[i])
213 				{
214 				case 'x':
215 					w_scale *= buf;
216 					i ++;
217 					break;
218 				case 'y':
219 					h_scale *= buf;
220 					i ++;
221 					break;
222 				default:
223 					w_scale *= buf;
224 					h_scale *= buf;
225 				}
226 			}
227 			break;
228 		case '/':
229 			if (sscanf(render_opts, "/%lf%n", &buf, &i) >= 1 &&
230 			    buf)
231 			{
232 				switch (render_opts[i])
233 				{
234 				case 'x':
235 					w_scale /= buf;
236 					i ++;
237 					break;
238 				case 'y':
239 					h_scale /= buf;
240 					i ++;
241 					break;
242 				default:
243 					w_scale /= buf;
244 					h_scale /= buf;
245 				}
246 			}
247 			break;
248 		case '@':
249 			if (sscanf(render_opts, "@%lf%n", &buf, &i) >= 1)
250 			{
251 				angle += buf;
252 			}
253 			break;
254 		default:
255 			j = 0;
256 			if (
257 				sscanf(
258 					render_opts, "%dx%n%d%n", &b1, &j, &b2,
259 					&i) >= 2 &&
260 				i > j)
261 			{
262 				w = b1;
263 				h = b2;
264 
265 				if (w < 0 || (!w && render_opts[0] == '-'))
266 				{
267 					w *= (w_sgn = -1);
268 				}
269 				if (h < 0 || (!h && render_opts[j] == '-'))
270 				{
271 					h *= (h_sgn = -1);
272 				}
273 			}
274 			else if (
275 				sscanf(render_opts, "%d%d%n", &b1, &b2, &i) >=
276 				2)
277 			{
278 				dw += b1;
279 				dh += b2;
280 			}
281 		}
282 		render_opts += i ? i : 1;
283 	}
284 	free(allocated_path);
285 
286 	/* Keep the original aspect ratio when either w or h is 0 */
287         Frsvg_handle_get_dimensions(rsvg, &dim);
288 	if (!w && !h)
289 	{
290 		w = dim.width;
291 		h = dim.height;
292 	}
293 	else if (!w)
294 	{
295 		w = h * dim.em / dim.ex;
296 	}
297 	else if (!h)
298 	{
299 		h = w * dim.ex / dim.em;
300 	}
301 
302 	w_scale *= w;
303 	h_scale *= h;
304 
305 	if (transpose)
306 	{
307 		b1 = w;
308 		w = h;
309 		h = b1;
310 
311 		b1 = w_sgn;
312 		w_sgn = - h_sgn;
313 		h_sgn = b1;
314 
315 		b1 = dw;
316 		dw = - dh;
317 		dh = b1;
318 
319 		angle += 90;
320 	}
321 
322 	data = fxcalloc(1, w * h * sizeof(CARD32));
323 	surface = Fcairo_image_surface_create_for_data((unsigned char *)data,
324 		FCAIRO_FORMAT_ARGB32, w, h, w * sizeof(CARD32));
325 	if (Fcairo_surface_status(surface) != FCAIRO_STATUS_SUCCESS)
326 	{
327 		Fg_object_unref(FG_OBJECT(rsvg));
328 		free(data);
329 		if (surface)
330 		{
331 		   	Fcairo_surface_destroy(surface);
332 		}
333 
334 		return False;
335 	}
336 	cr = Fcairo_create(surface);
337 	Fcairo_surface_destroy(surface);
338 	if (Fcairo_status(cr) != FCAIRO_STATUS_SUCCESS)
339 	{
340 		Fg_object_unref(FG_OBJECT(rsvg));
341 		free(data);
342 		if (cr)
343 		{
344 			Fcairo_destroy(cr);
345 		}
346 
347 		return False;
348 	}
349 
350 	/* Affine transformations ...
351 	 * mirroring, rotation, scaling and translation */
352 	Fcairo_translate(cr, .5 * w, .5 * h);
353 	Fcairo_scale(cr, w_sgn, h_sgn);
354 	Fcairo_translate(cr, dw, dh);
355 	Fcairo_rotate(cr, angle * M_PI / 180);
356 	Fcairo_scale(cr, w_scale, h_scale);
357 	Fcairo_translate(cr, -.5, -.5);
358 	Fcairo_scale(cr, 1 / dim.em, 1 / dim.ex);
359 
360 	Frsvg_handle_render_cairo(rsvg, cr);
361 	Fg_object_unref(FG_OBJECT(rsvg));
362 	Fcairo_destroy(cr);
363 
364 	/* Cairo gave us alpha prescaled RGB values, hence we need
365 	 * to rescale them for PImageCreatePixmapFromArgbData() */
366 	for (i = 0; i < w * h; i++)
367 	{
368 		if ((a_value = (data[i] >> 030) & 0xff))
369 		{
370 			r_value = ((data[i] >> 020) & 0xff) * 0xff / a_value;
371 			g_value = ((data[i] >> 010) & 0xff) * 0xff / a_value;
372 			b_value =  (data[i]         & 0xff) * 0xff / a_value;
373 
374 			data[i] =
375 				(a_value << 030) | (r_value << 020) |
376 				(g_value << 010) | b_value;
377 		}
378 	}
379 
380 	*width = w;
381 	*height = h;
382 	*argb_data = data;
383 
384 	return True;
385 }
386 
387 /*
388  *
389  * png loader
390  *
391  */
392 static
PImageLoadPng(FIMAGE_CMD_ARGS)393 Bool PImageLoadPng(FIMAGE_CMD_ARGS)
394 {
395 	Fpng_uint_32 w32, h32;
396 	Fpng_structp Fpng_ptr = NULL;
397 	Fpng_infop Finfo_ptr = NULL;
398 	CARD32 *data;
399 	int w, h;
400 	char hasa = 0, hasg = 0;
401 	FILE *f;
402 	int bit_depth;
403 	int color_type;
404 	int interlace_type;
405 	unsigned char buf[FPNG_BYTES_TO_CHECK];
406 	unsigned char **lines;
407 	int i;
408 
409 	if (!PngSupport)
410 	{
411 		/* suppress compiler warning */
412 		bit_depth = 0;
413 
414 		fvwm_debug(__func__,
415 				"[PImageLoadPng]: Tried to load PNG file "
416 				"when FVWM has not been compiled with PNG support.\n");
417 
418 		return False;
419 	}
420 	if (!(f = fopen(path, "rb")))
421 	{
422 		return False;
423 	}
424 	{
425 		int n;
426 
427 		n = fread(buf, 1, FPNG_BYTES_TO_CHECK, f);
428 		(void)n;
429 	}
430 	if (!Fpng_check_sig(buf, FPNG_BYTES_TO_CHECK))
431 	{
432 		fclose(f);
433 		return False;
434 	}
435 	rewind(f);
436 	Fpng_ptr = Fpng_create_read_struct(FPNG_LIBPNG_VER_STRING,
437 					   NULL, NULL, NULL);
438 	if (!Fpng_ptr)
439 	{
440 		fclose(f);
441 		return False;
442 	}
443 	Finfo_ptr = Fpng_create_info_struct(Fpng_ptr);
444 	if (!Finfo_ptr)
445 	{
446 		Fpng_destroy_read_struct(&Fpng_ptr, NULL, NULL);
447 		fclose(f);
448 		return False;
449 	}
450 #if 0
451 	if (setjmp(Fpng_ptr->jmpbuf))
452 	{
453 		Fpng_destroy_read_struct(&Fpng_ptr, &Finfo_ptr, NULL);
454 		fclose(f);
455 		return False;
456 	}
457 #endif
458 	Fpng_init_io(Fpng_ptr, f);
459 	Fpng_read_info(Fpng_ptr, Finfo_ptr);
460 	Fpng_get_IHDR(
461 		Fpng_ptr, Finfo_ptr, (Fpng_uint_32 *) (&w32),
462 		(Fpng_uint_32 *) (&h32), &bit_depth, &color_type,
463 		&interlace_type, NULL, NULL);
464 	interlace_type = 0; /* not used */
465 	*width = w = (int) w32;
466 	*height = h = (int) h32;
467 	if (color_type == FPNG_COLOR_TYPE_PALETTE)
468 	{
469 		Fpng_set_expand(Fpng_ptr);
470 	}
471 
472 	/* TA:  XXX:  (2011-02-14) -- Happy Valentines Day.
473 	 *
474 	 * png_get_color_type() defined in libpng 1.5 now hides a data member
475 	 * to a struct:
476 	 *
477 	 * Finfo_ptr->color_type
478 	 *
479 	 * I'm not going to wrap this up in more #ifdef madness, but should
480 	 * this fail to build on much older libpng versions which we support
481 	 * (pre 1.3), then I might have to.
482 	 */
483 	if (png_get_color_type(Fpng_ptr, Finfo_ptr) == FPNG_COLOR_TYPE_RGB_ALPHA)
484 	{
485 		hasa = 1;
486 	}
487 	if (png_get_color_type(Fpng_ptr, Finfo_ptr) == FPNG_COLOR_TYPE_GRAY_ALPHA)
488 	{
489 		hasa = 1;
490 		hasg = 1;
491 	}
492 	if (png_get_color_type(Fpng_ptr, Finfo_ptr) == FPNG_COLOR_TYPE_GRAY)
493 	{
494 		hasg = 1;
495 	}
496 
497 	if (hasa)
498 		Fpng_set_expand(Fpng_ptr);
499 	/* we want ARGB */
500 	/* note form raster:
501 	* thanks to mustapha for helping debug this on PPC Linux remotely by
502 	* sending across screenshots all the time and me figuring out form them
503 	* what the hell was up with the colors
504 	* now png loading should work on big endian machines nicely   */
505 #ifdef WORDS_BIGENDIAN
506 	Fpng_set_swap_alpha(Fpng_ptr);
507 	Fpng_set_filler(Fpng_ptr, 0xff, FPNG_FILLER_BEFORE);
508 #else
509 	Fpng_set_bgr(Fpng_ptr);
510 	Fpng_set_filler(Fpng_ptr, 0xff, FPNG_FILLER_AFTER);
511 #endif
512 	/* 16bit color -> 8bit color */
513 	Fpng_set_strip_16(Fpng_ptr);
514 	/* pack all pixels to byte boundaires */
515 	Fpng_set_packing(Fpng_ptr);
516 	if (Fpng_get_valid(Fpng_ptr, Finfo_ptr, FPNG_INFO_tRNS))
517 	{
518 		Fpng_set_expand(Fpng_ptr);
519 	}
520 
521 	data = fxmalloc(w * h * sizeof(CARD32));
522 	lines = fxmalloc(h * sizeof(unsigned char *));
523 
524 	if (hasg)
525 	{
526 		Fpng_set_gray_to_rgb(Fpng_ptr);
527 		if (Fpng_get_bit_depth(Fpng_ptr, Finfo_ptr) < 8)
528 		{
529 			Fpng_set_gray_1_2_4_to_8(Fpng_ptr);
530 		}
531 	}
532 	for (i = 0; i < h; i++)
533 	{
534 		lines[i] = (unsigned char *)data + (i * w * sizeof(CARD32));
535 	}
536 	Fpng_read_image(Fpng_ptr, lines);
537 	Fpng_read_end(Fpng_ptr, Finfo_ptr);
538 	Fpng_destroy_read_struct(&Fpng_ptr, &Finfo_ptr, (png_infopp) NULL);
539 	fclose(f);
540 	free(lines);
541 	*argb_data = data;
542 
543 	return True;
544 }
545 
546 /*
547  *
548  * xpm loader
549  *
550  */
551 static
PImageLoadXpm(FIMAGE_CMD_ARGS)552 Bool PImageLoadXpm(FIMAGE_CMD_ARGS)
553 {
554 	FxpmImage xpm_im;
555 	FxpmColor *xpm_color;
556 	XColor color;
557 	CARD32 *colors;
558 	CARD32 *data;
559 	char *visual_color;
560 	int i;
561 	int w;
562 	int h;
563 	int rc;
564 #ifdef HAVE_SIGACTION
565 	struct sigaction defaultHandler;
566 	struct sigaction originalHandler;
567 #else
568 	RETSIGTYPE (*originalHandler)(int);
569 #endif
570 
571 	if (!XpmSupport)
572 	{
573 		fvwm_debug(__func__,
574 				"[PImageLoadXpm]: Tried to load XPM file "
575 				"when FVWM has not been compiled with XPm Support.\n");
576 		return False;
577 	}
578 	memset(&xpm_im, 0, sizeof(FxpmImage));
579 
580 #ifdef HAVE_SIGACTION
581 	sigemptyset(&defaultHandler.sa_mask);
582 	defaultHandler.sa_flags = 0;
583 	defaultHandler.sa_handler = SIG_DFL;
584 	sigaction(SIGCHLD, &defaultHandler, &originalHandler);
585 #else
586 	originalHandler = signal(SIGCHLD, SIG_DFL);
587 #endif
588 
589 
590 	rc = FxpmReadFileToXpmImage(path, &xpm_im, NULL);
591 
592 #ifdef HAVE_SIGACTION
593 	sigaction(SIGCHLD, &originalHandler, NULL);
594 #else
595 	signal(SIGCHLD, originalHandler);
596 #endif
597 
598 	if (rc != FxpmSuccess)
599 	{
600 		return False;
601 	}
602 
603 	if (xpm_im.ncolors <= 0)
604 	{
605 		FxpmFreeXpmImage(&xpm_im);
606 		return False;
607 	}
608 	colors = fxmalloc(xpm_im.ncolors * sizeof(CARD32));
609 	for (i=0; i < xpm_im.ncolors; i++)
610 	{
611 		xpm_color = &xpm_im.colorTable[i];
612 		if (xpm_color->c_color)
613 		{
614 			visual_color = xpm_color->c_color;
615 		}
616 		else if (xpm_color->g_color)
617 		{
618 			visual_color = xpm_color->g_color;
619 		}
620 		else if (xpm_color->g4_color)
621 		{
622 			visual_color = xpm_color->g4_color;
623 		}
624 		else
625 		{
626 			visual_color = xpm_color->m_color;
627 		}
628 		if (XParseColor(dpy, Pcmap, visual_color, &color))
629 		{
630 			colors[i] = 0xff000000 |
631 				((color.red  << 8) & 0xff0000) |
632 				((color.green    ) & 0xff00) |
633 				((color.blue >> 8) & 0xff);
634 		}
635 		else
636 		{
637 			colors[i] = 0;
638 		}
639 	}
640 	*width = w = xpm_im.width;
641 	*height = h = xpm_im.height;
642 	data = fxmalloc(w * h * sizeof(CARD32));
643 	for (i=0; i < w * h; i++)
644 	{
645 		data[i] = colors[xpm_im.data[i]];
646 	}
647 	free(colors);
648 	*argb_data = data;
649 
650 	return True;
651 }
652 
653 /*
654  *
655  * copy image to server
656  *
657  */
658 static
PImageCreatePixmapFromFImage(Display * dpy,Window win,FImage * fimage)659 Pixmap PImageCreatePixmapFromFImage(Display *dpy, Window win, FImage *fimage)
660 {
661 	GC gc;
662 	Pixmap pixmap;
663 	int w;
664 	int h;
665 	int depth;
666 
667 	w = fimage->im->width;
668 	h = fimage->im->height;
669 	depth = fimage->im->depth;
670 	pixmap = XCreatePixmap(dpy, win, w, h, depth);
671 	if (depth == Pdepth)
672 	{
673 		gc = PictureDefaultGC(dpy, win);
674 	}
675 	else
676 	{
677 		gc = fvwmlib_XCreateGC(dpy, pixmap, 0, NULL);
678 	}
679 	FPutFImage(dpy, pixmap, gc, fimage, 0, 0, 0, 0, w, h);
680 	if (depth != Pdepth)
681 	{
682 		XFreeGC(dpy, gc);
683 	}
684 
685 	return pixmap;
686 }
687 
688 /* ---------------------------- interface functions ------------------------ */
689 
690 /*
691  *
692  * argb data to pixmaps
693  *
694  */
PImageCreatePixmapFromArgbData(Display * dpy,Window win,CARD32 * data,int start,int width,int height,Pixmap * pixmap,Pixmap * mask,Pixmap * alpha,int * nalloc_pixels,Pixel ** alloc_pixels,int * no_limit,FvwmPictureAttributes fpa)695 Bool PImageCreatePixmapFromArgbData(
696 	Display *dpy, Window win, CARD32 *data, int start, int width,
697 	int height, Pixmap *pixmap, Pixmap *mask, Pixmap *alpha,
698 	int *nalloc_pixels, Pixel **alloc_pixels, int *no_limit,
699 	FvwmPictureAttributes fpa)
700 {
701 	FImage *fim;
702 	FImage *m_fim = NULL;
703 	FImage *a_fim = NULL;
704 	XColor c;
705 	int i;
706 	int j;
707 	int a;
708 	PictureImageColorAllocator *pica = NULL;
709 	int alpha_limit = PICTURE_ALPHA_LIMIT;
710 	int alpha_depth = FRenderGetAlphaDepth();
711 	Bool have_mask = False;
712 	Bool have_alpha = False;
713 
714 	fim = FCreateFImage(
715 		dpy, Pvisual, (fpa.mask & FPAM_MONOCHROME) ? 1 : Pdepth,
716 		ZPixmap, width, height);
717 	if (!fim)
718 	{
719 		return False;
720 	}
721 	if (mask)
722 	{
723 		m_fim = FCreateFImage(
724 			dpy, Pvisual, 1, ZPixmap, width, height);
725 	}
726 	if (alpha && !(fpa.mask & FPAM_NO_ALPHA) && alpha_depth)
727 	{
728 		alpha_limit = 0;
729 		a_fim = FCreateFImage(
730 			dpy, Pvisual, alpha_depth, ZPixmap, width, height);
731 	}
732 	if (!(fpa.mask & FPAM_MONOCHROME))
733 	{
734 		c.flags = DoRed | DoGreen | DoBlue;
735 		pica = PictureOpenImageColorAllocator(
736 			dpy, Pcmap, width, height,
737 			!!(fpa.mask & FPAM_NO_COLOR_LIMIT),
738 			!!(fpa.mask & FPAM_NO_ALLOC_PIXELS),
739 			!!(fpa.mask & FPAM_DITHER),
740 			True);
741 	}
742 	data += start;
743 	for (j = 0; j < height; j++)
744 	{
745 		for (i = 0; i < width; i++, data++)
746 		{
747 			a = (*data >> 030) & 0xff;
748 			if (a > alpha_limit)
749 			{
750 				c.red   = (*data >> 16) & 0xff;
751 				c.green = (*data >>  8) & 0xff;
752 				c.blue  = (*data      ) & 0xff;
753 				if (pica)
754 				{
755 					PictureAllocColorImage(
756 						dpy, pica, &c, i, j);
757 					XPutPixel(fim->im, i, j, c.pixel);
758 				}
759 				/* Brightness threshold */
760 				else if ((0x99  * c.red +
761 					  0x12D * c.green +
762 					  0x3A  * c.blue) >> 16)
763 				{
764 					XPutPixel(fim->im, i, j, 1);
765 				}
766 				else
767 				{
768 					XPutPixel(fim->im, i, j, 0);
769 				}
770 				if (m_fim)
771 				{
772 					XPutPixel(m_fim->im, i, j, 1);
773 				}
774 			}
775 			else if (m_fim != NULL)
776 			{
777 				XPutPixel(m_fim->im, i, j, 0);
778 				have_mask = True;
779 			}
780 			if (a_fim != NULL)
781 			{
782 				XPutPixel(a_fim->im, i, j, a);
783 				if (a > 0 && a < 0xff)
784 				{
785 					have_alpha = True;
786 				}
787 			}
788 		}
789 	}
790 	if (pica)
791 	{
792 		PictureCloseImageColorAllocator(
793 			dpy, pica, nalloc_pixels, alloc_pixels, no_limit);
794 	}
795 	*pixmap = PImageCreatePixmapFromFImage(dpy, win, fim);
796 	if (have_alpha)
797 	{
798 		*alpha = PImageCreatePixmapFromFImage(dpy, win, a_fim);
799 	}
800 	else if (have_mask)
801 	{
802 		*mask = PImageCreatePixmapFromFImage(dpy, win, m_fim);
803 	}
804 	FDestroyFImage(dpy, fim);
805 	if (m_fim)
806 	{
807 		FDestroyFImage(dpy, m_fim);
808 	}
809 	if (a_fim)
810 	{
811 		FDestroyFImage(dpy, a_fim);
812 	}
813 
814 	return True;
815 }
816 
817 
818 /*
819  *
820  * the images loaders
821  *
822  */
823 
PImageLoadPixmapFromFile(Display * dpy,Window win,char * path,Pixmap * pixmap,Pixmap * mask,Pixmap * alpha,int * width,int * height,int * depth,int * nalloc_pixels,Pixel ** alloc_pixels,int * no_limit,FvwmPictureAttributes fpa)824 Bool PImageLoadPixmapFromFile(
825 	Display *dpy, Window win, char *path, Pixmap *pixmap, Pixmap *mask,
826 	Pixmap *alpha, int *width, int *height, int *depth,
827 	int *nalloc_pixels, Pixel **alloc_pixels,
828 	int *no_limit, FvwmPictureAttributes fpa)
829 {
830 	CARD32 *data;
831 
832 	if (PImageLoadArgbDataFromFile(dpy, path, &data, width, height))
833 	{
834 		*depth = (fpa.mask & FPAM_MONOCHROME) ? 1 : Pdepth;
835 		if (PImageCreatePixmapFromArgbData(
836 			dpy, win, data, 0, *width, *height, pixmap, mask,
837 			alpha, nalloc_pixels, alloc_pixels, no_limit, fpa))
838 		{
839 			free(data);
840 
841 			return True;
842 		}
843 		free(data);
844 	}
845 	/* Bitmap fallback */
846 	else if (
847 		XReadBitmapFile(
848 			dpy, win, path, (unsigned int *)width,
849 			(unsigned int *)height, pixmap, NULL, NULL) ==
850 		BitmapSuccess)
851 	{
852 		*depth = 1;
853 		*mask = None;
854 
855 		return True;
856 	}
857 	pixmap = None;
858 	mask = None;
859 	alpha = None;
860 	*width = *height = *depth = 0;
861 	if (nalloc_pixels != NULL)
862 	{
863 		*nalloc_pixels = 0;
864 	}
865 	if (alloc_pixels != NULL)
866 	{
867 		*alloc_pixels = NULL;
868 	}
869 	return False;
870 }
871 
PImageLoadFvwmPictureFromFile(Display * dpy,Window win,char * path,FvwmPictureAttributes fpa)872 FvwmPicture *PImageLoadFvwmPictureFromFile(
873 	Display *dpy, Window win, char *path, FvwmPictureAttributes fpa)
874 {
875 	FvwmPicture *p;
876 	Pixmap pixmap = None;
877 	Pixmap mask = None;
878 	Pixmap alpha = None;
879 	int width = 0, height = 0;
880 	int depth = 0, no_limit;
881 	int nalloc_pixels = 0;
882 	Pixel *alloc_pixels = NULL;
883 	char *real_path;
884 
885         /* Remove any svg rendering options from real_path */
886 	if (USE_SVG && *path == ':' &&
887 	    (real_path = strchr(path + 1, ':')))
888 	{
889 		real_path ++;
890 	}
891 	else
892 	{
893 		real_path = path;
894 	}
895 	if (!PImageLoadPixmapFromFile(
896 		dpy, win, path, &pixmap, &mask, &alpha, &width, &height,
897 		&depth, &nalloc_pixels, &alloc_pixels, &no_limit, fpa))
898 	{
899 		return NULL;
900 	}
901 
902 	p = fxcalloc(1, sizeof(FvwmPicture));
903 	p->count = 1;
904 	p->name = path;
905 	p->fpa_mask = fpa.mask;
906 	p->next = NULL;
907 	setFileStamp(&p->stamp, real_path);
908 	p->picture = pixmap;
909 	p->mask = mask;
910 	p->alpha = alpha;
911 	p->width = width;
912 	p->height = height;
913 	p->depth = depth;
914 	p->nalloc_pixels = nalloc_pixels;
915 	p->alloc_pixels = alloc_pixels;
916 	p->no_limit = no_limit;
917 	return p;
918 }
919 
PImageLoadCursorFromFile(Display * dpy,Window win,char * path,int x_hot,int y_hot)920 Cursor PImageLoadCursorFromFile(
921 	Display *dpy, Window win, char *path, int x_hot, int y_hot)
922 {
923 	Cursor cursor = 0;
924 	CARD32 *data;
925 	int width;
926 	int height;
927 	int i;
928 	FcursorImages *fcis;
929 	FcursorImage *fci;
930 
931 	/* First try the Xcursor loader (animated cursors) */
932 	if ((fcis = FcursorFilenameLoadImages(
933 		path, FcursorGetDefaultSize(dpy))))
934 	{
935 		for (i = 0; i < fcis->nimage; i++)
936 		{
937 			if (x_hot < fcis->images[i]->width && x_hot >= 0 &&
938 			    y_hot < fcis->images[i]->height && y_hot >= 0)
939 			{
940 				fcis->images[i]->xhot = x_hot;
941 				fcis->images[i]->yhot = y_hot;
942 			}
943 		}
944 		cursor = FcursorImagesLoadCursor(dpy, fcis);
945 		FcursorImagesDestroy(fcis);
946 	}
947 	/* Get cursor data from the regular image loader */
948 	else if (PImageLoadArgbDataFromFile(dpy, path, &data, &width, &height))
949 	{
950 		Pixmap src;
951 		Pixmap msk = None;
952 		FvwmPictureAttributes fpa;
953 
954 		fpa.mask = FPAM_NO_ALPHA | FPAM_MONOCHROME;
955 
956 		/* Adjust the hot-spot if necessary */
957 		if (
958 			x_hot < 0 || x_hot >= width ||
959 			y_hot < 0 || y_hot >= height)
960 		{
961 			FxpmImage xpm_im;
962 			FxpmInfo xpm_info;
963 
964 			memset(&xpm_im, 0, sizeof(FxpmImage));
965 			memset(&xpm_info, 0, sizeof(FxpmInfo));
966 			if (FxpmReadFileToXpmImage(path, &xpm_im, &xpm_info)
967 			    == FxpmSuccess)
968 			{
969 				if (xpm_info.valuemask & FxpmHotspot)
970 				{
971 					x_hot = xpm_info.x_hotspot;
972 					y_hot = xpm_info.y_hotspot;
973 				}
974 				FxpmFreeXpmImage(&xpm_im);
975 				FxpmFreeXpmInfo(&xpm_info);
976 			}
977 			if (x_hot < 0 || x_hot >= width)
978 			{
979 				x_hot = width / 2;
980 			}
981 			if (y_hot < 0 || y_hot >= height)
982 			{
983 				y_hot = height / 2;
984 			}
985 		}
986 		/* Use the Xcursor library to create the argb cursor */
987 		if ((fci = FcursorImageCreate(width, height)))
988 		{
989 			unsigned char alpha;
990 			unsigned char red;
991 			unsigned char green;
992 			unsigned char blue;
993 
994 			/* Xcursor expects alpha prescaled RGB values */
995 			for (i = 0; i < width * height; i++)
996 			{
997 				alpha = ((data[i] >> 24) & 0xff);
998 				red   = ((data[i] >> 16) & 0xff) * alpha/0xff;
999 				green = ((data[i] >>  8) & 0xff) * alpha/0xff;
1000 				blue  = ((data[i]      ) & 0xff) * alpha/0xff;
1001 
1002 				data[i] =
1003 					(alpha << 24) | (red << 16) |
1004 					(green <<  8) | blue;
1005 			}
1006 
1007 			fci->xhot = x_hot;
1008 			fci->yhot = y_hot;
1009 			fci->delay = 0;
1010 			fci->pixels = (FcursorPixel *)data;
1011 			cursor = FcursorImageLoadCursor(dpy, fci);
1012 			FcursorImageDestroy(fci);
1013 		}
1014 		/* Create monochrome cursor from argb data */
1015 		else if (PImageCreatePixmapFromArgbData(
1016 			dpy, win, data, 0, width, height,
1017 			&src, &msk, 0, 0, 0, 0, fpa))
1018 		{
1019 			XColor c[2];
1020 
1021 			c[0].pixel = GetColor(DEFAULT_CURSOR_FORE_COLOR);
1022 			c[1].pixel = GetColor(DEFAULT_CURSOR_BACK_COLOR);
1023 			XQueryColors(dpy, Pcmap, c, 2);
1024 			cursor = XCreatePixmapCursor(
1025 				dpy, src, msk, &(c[0]), &(c[1]), x_hot, y_hot);
1026 			XFreePixmap(dpy, src);
1027 			XFreePixmap(dpy, msk);
1028 		}
1029 		free(data);
1030 	}
1031 
1032 	return cursor;
1033 }
1034 
1035 /* FIXME: Use color limit */
PImageLoadPixmapFromXpmData(Display * dpy,Window win,int color_limit,char ** data,Pixmap * pixmap,Pixmap * mask,int * width,int * height,int * depth)1036 Bool PImageLoadPixmapFromXpmData(
1037 	Display *dpy, Window win, int color_limit,
1038 	char **data,
1039 	Pixmap *pixmap, Pixmap *mask,
1040 	int *width, int *height, int *depth)
1041 {
1042 	FxpmAttributes xpm_attributes;
1043 
1044 	if (!XpmSupport)
1045 	{
1046 		return False;
1047 	}
1048 	xpm_attributes.valuemask = FxpmCloseness |
1049 		FxpmExtensions | FxpmVisual | FxpmColormap | FxpmDepth;
1050 	xpm_attributes.closeness = 40000;
1051 	xpm_attributes.visual = Pvisual;
1052 	xpm_attributes.colormap = Pcmap;
1053 	xpm_attributes.depth = Pdepth;
1054 	/* suppress compiler warning if xpm library is not compiled in */
1055 	xpm_attributes.width = 0;
1056 	xpm_attributes.height = 0;
1057 	if (
1058 		FxpmCreatePixmapFromData(
1059 			dpy, win, data, pixmap, mask, &xpm_attributes) !=
1060 		FxpmSuccess)
1061 	{
1062 		return False;
1063 	}
1064 	*width = xpm_attributes.width;
1065 	*height = xpm_attributes.height;
1066 	*depth = Pdepth;
1067 
1068 	return True;
1069 }
1070