1 /*
2  *  rimage.c:		Interface to wraster lib (conversion of RImage)
3  *
4  *  Written by:		Ullrich Hafner
5  *
6  *  Copyright (C) 1999 Ullrich Hafner <hafner@bigfoot.de>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
21  */
22 
23 /*
24  *  $Date: 2000/10/16 18:24:42 $
25  *  $Author: hafner $
26  *  $Revision: 1.12 $
27  *  $State: Exp $
28  */
29 
30 #include "config.h"
31 
32 #if defined(PREVIEWS) && !defined(CONVERT)
33 
34 #if HAVE_STDLIB_H
35 #	include <stdlib.h>
36 #endif /* not HAVE_STDLIB_H */
37 #if HAVE_STRING_H
38 #	include <string.h>
39 #else /* not HAVE_STRING_H */
40 #	include <strings.h>
41 #endif /* not HAVE_STRING_H */
42 #include <stdio.h>
43 #include <gtk/gtk.h>
44 #include <gdk/gdkx.h>
45 #include <assert.h>
46 #include "proplist_t.h"
47 #include <wraster.h>
48 
49 #include "window.h"
50 #include "rimage.h"
51 #include "error.h"
52 
53 
54 /*****************************************************************************
55 
56 			global and local variables
57 
58 *****************************************************************************/
59 
60 static RContext *rc = NULL;
61 extern GtkWidget *main_window;
62 
63 /*****************************************************************************
64 
65 				prototypes
66 
67 *****************************************************************************/
68 
69 #ifdef HAVE_LIBWMFUN
70 RImage *
71 bilinear (int argc, char **argv, int width, int height, int relief);
72 RImage *
73 fade (int argc, char **argv, int width, int height, int relief);
74 RImage *
75 waves (int argc, char **argv, int width, int height, int relief);
76 void
77 initWindowMaker (Display *d, Colormap c);
78 #endif /* HAVE_LIBWMFUN */
79 static RColor *
80 get_background_color (void);
81 static RImage *
82 rimage_load (const char *filename, int width, int height);
83 static GtkWidget *
84 rimage_2_preview (RImage *image, GtkWidget *preview, bool_t bevel);
85 
86 /*****************************************************************************
87 
88 				public code
89 
90 *****************************************************************************/
91 
92 void
init_wraster_lib(void)93 init_wraster_lib (void)
94 {
95    if (!rc)
96    {
97       RContextAttributes rattr;
98 
99       rattr.flags = 0;
100       rc = RCreateContext (GDK_DISPLAY (), DefaultScreen (GDK_DISPLAY ()),
101 			   &rattr);
102 #ifdef HAVE_LIBWMFUN
103       initWindowMaker (GDK_DISPLAY (), rc->cmap);
104 #endif /* HAVE_LIBWMFUN */
105    }
106 }
107 
108 /* We need this wrapper function since gdk_parse_color() no longer
109  * understands "rgb:RR/GG/BB".
110  */
111 gboolean
make_color(const gchar * spec,GdkColor * color)112 make_color(const gchar *spec, GdkColor *color)
113 {
114   gboolean result;
115   gchar * newspec = g_strdup (spec);
116 
117   if (strncmp(spec, "rgb:", 4) == 0) {
118     const char * red_posn = spec + 4, * green_posn, * blue_posn;
119     int red_len, green_len, blue_len;
120 
121     green_posn = strchr(red_posn, '/');
122     if (!green_posn) return FALSE;
123     red_len = green_posn - red_posn;
124     green_posn++;
125 
126     blue_posn = strchr(green_posn, '/');
127     if (!blue_posn) return FALSE;
128     green_len = blue_posn - green_posn;
129     blue_posn++;
130     blue_len = strlen(spec) - (blue_posn - spec);
131 
132     if (red_len != green_len || green_len != blue_len ||
133 	red_len < 1 || red_len > 4)
134       return FALSE;
135 
136     newspec = malloc(3 * red_len + 2);
137     strcpy(newspec, "#");
138     strncat(newspec, red_posn, red_len);
139     strncat(newspec, green_posn, red_len);
140     strncat(newspec, blue_posn, red_len);
141   }
142 
143   result = gdk_color_parse (newspec, color);
144   if (newspec != spec)
145     free (newspec);
146   return result;
147 }
148 
149 GtkWidget *
make_image(const char * filename,int width,int height,GtkWidget * preview)150 make_image (const char *filename, int width, int height, GtkWidget *preview)
151 {
152    RImage *rimage = rimage_load (filename, width, height);
153 
154    preview = rimage_2_preview (rimage, preview, FALSE);
155    RReleaseImage (rimage);
156 
157    return preview;
158 }
159 
160 GtkWidget *
make_gradient(proplist_t array,unsigned width,unsigned height,gtype_e type,GtkWidget * preview)161 make_gradient (proplist_t array, unsigned width, unsigned height,
162 	       gtype_e type, GtkWidget *preview)
163 {
164    if (!array || !WMIsPLArray (array) || WMGetPropListItemCount (array) < 3)
165       return make_image (PKGDATADIR "/black.xpm", width, height, preview);
166    else
167    {
168       unsigned	n 	 = WMGetPropListItemCount (array);
169       unsigned  start	 = n == 3 ? 1 : 2;
170       RColor	**colors = Calloc (n + 1, sizeof (RColor *));
171       unsigned	k;
172 
173       for (k = start; k < n; k++)
174       {
175 	 GdkColor c;
176 
177 	 if (!make_color (WMGetFromPLString (WMGetFromPLArray (array, k)), &c))
178 	    c.red = c.green = c.blue = 65535; /* white */
179 
180 	 colors [k - start]        = Calloc (1, sizeof (RColor)) ;
181 	 colors [k - start]->red   = c.red >> 8;
182 	 colors [k - start]->green = c.green >> 8;
183 	 colors [k - start]->blue  = c.blue >> 8;
184       }
185       colors [k - start] = NULL;
186 
187       {
188 	 RImage *rimage;
189 
190 	 if (n == 3)
191 	    rimage
192 	       = RRenderGradient (width, height, colors [0], colors [1],
193 				  type == HGRADIENT ? RHorizontalGradient
194 				  : (type == VGRADIENT ? RVerticalGradient
195 				     : RDiagonalGradient));
196 	 else
197 	    rimage
198 	       = RRenderMultiGradient (width, height, colors,
199 				       type == HGRADIENT ? RHorizontalGradient
200 				       : (type == VGRADIENT ? RVerticalGradient
201 					  : RDiagonalGradient));
202 	 preview = rimage_2_preview (rimage, preview,
203 				     TRUE && rimage->height < 100);
204 	 RReleaseImage (rimage);
205       }
206 
207       for (k = start; k < n; k++)
208 	 Free (colors [k - start]);
209       Free (colors);
210 
211       return preview;
212    }
213 }
214 
215 GtkWidget *
make_solid(const char * color,unsigned width,unsigned height,GtkWidget * preview)216 make_solid (const char *color, unsigned width, unsigned height,
217 	    GtkWidget *preview)
218 {
219    if (!preview)
220    {
221       preview = gtk_preview_new (GTK_PREVIEW_COLOR);
222       gtk_preview_size (GTK_PREVIEW (preview), width, height);
223    }
224    else
225    {
226       width  = GTK_WIDGET (preview)->requisition.width;
227       height = GTK_WIDGET (preview)->requisition.height;
228    }
229    {
230       guchar   *row = Calloc (width * 3, sizeof (guchar));
231       unsigned x, y;
232       GdkColor c;
233 
234       if (!make_color (color, &c))
235 	 c.red = c.green = c.blue = 65535; /* white */
236       for (y = 0; y < height; y++)
237       {
238 	 guchar *ptr = row;
239 	 for (x = 0; x < width; x++)
240 	 {
241 	    *ptr++ = c.red >> 8;
242 	    *ptr++ = c.green >> 8;
243 	    *ptr++ = c.blue >> 8;
244 	 }
245 	 gtk_preview_draw_row (GTK_PREVIEW (preview), row, 0, y, width);
246       }
247       Free (row);
248    }
249    gtk_widget_draw (preview, NULL);
250 
251    return preview;
252 }
253 
254 GtkWidget *
make_textured_gradient(const char * filename,proplist_t array,unsigned width,unsigned height,gtype_e type,GtkWidget * preview)255 make_textured_gradient (const char *filename, proplist_t array,
256 			unsigned width, unsigned height,
257 			gtype_e type, GtkWidget *preview)
258 {
259    if (!filename || WMGetPropListItemCount (array) != 5)
260       return make_image (PKGDATADIR "/black.xpm", width, height, preview);
261    else
262    {
263       GdkColor	c1, c2;
264       RColor	r1, r2;
265       int	opacity = strtol (WMGetFromPLString (WMGetFromPLArray (array, 2)),
266 				  NULL, 10);
267       RImage	*gradient, *image, *tiled;
268 
269       if (!make_color (WMGetFromPLString (WMGetFromPLArray (array, 3)), &c1))
270 	 c1.red = c1.green = c1.blue = 65535; /* white */
271       if (!make_color (WMGetFromPLString (WMGetFromPLArray (array, 4)), &c2))
272 	 c2.red = c2.green = c2.blue = 65535; /* white */
273       r1.red   = c1.red >> 8;
274       r1.green = c1.green >> 8;
275       r1.blue  = c1.blue >> 8;
276 
277       r2.red   = c2.red >> 8;
278       r2.green = c2.green >> 8;
279       r2.blue  = c2.blue >> 8;
280 
281       if (!preview)
282       {
283 	 preview = gtk_preview_new (GTK_PREVIEW_COLOR);
284 	 gtk_preview_size (GTK_PREVIEW (preview), width, height);
285       }
286       else
287       {
288 	 width  = GTK_WIDGET (preview)->requisition.width;
289 	 height = GTK_WIDGET (preview)->requisition.height;
290       }
291 
292       gradient = RRenderGradient (width, height, &r1, &r2,
293 				  type == HGRADIENT ? RHorizontalGradient
294 				  : (type == VGRADIENT ? RVerticalGradient
295 				     : RDiagonalGradient));
296 
297       image = rimage_load (filename, -1, -1);
298       tiled = RMakeTiledImage (image, width, height);
299       RReleaseImage (image);
300 
301       if (tiled)
302       {
303 	 RCombineImagesWithOpaqueness (tiled, gradient, opacity);
304 	 RReleaseImage (gradient);
305 
306 	 preview = rimage_2_preview (tiled, preview, FALSE);
307 	 RReleaseImage (tiled);
308       }
309       else
310       {
311 	 preview = rimage_2_preview (gradient, preview, FALSE);
312 	 RReleaseImage (gradient);
313       }
314       return preview;
315    }
316 }
317 
318 #ifdef HAVE_INTERWOVEN_GRADIENT
319 
320 GtkWidget *
make_igradient(proplist_t array,unsigned width,unsigned height,GtkWidget * preview)321 make_igradient (proplist_t array, unsigned width, unsigned height,
322 		GtkWidget *preview)
323 {
324    if (WMGetPropListItemCount (array) != 7)
325       return make_image (PKGDATADIR "/black.xpm", width, height, preview);
326    else
327    {
328       GdkColor	from1, to1, from2, to2;
329       RColor	r1 [2], r2 [2];
330       int	thickness1 = strtol (WMGetFromPLString (WMGetFromPLArray (array,
331 								     3)),
332 				     NULL, 10);
333       int	thickness2 = strtol (WMGetFromPLString (WMGetFromPLArray (array,
334 								     6)),
335 				     NULL, 10);
336       RImage	*gradient;
337 
338       if (!make_color (WMGetFromPLString (WMGetFromPLArray (array, 1)),
339 			    &from1))
340 	 from1.red = from1.green = from1.blue = 65535; /* white */
341       if (!make_color (WMGetFromPLString (WMGetFromPLArray (array, 2)), &to1))
342 	 to1.red = to1.green = to1.blue = 65535; /* white */
343       if (!make_color (WMGetFromPLString (WMGetFromPLArray (array, 4)),
344 			    &from2))
345 	 from2.red = from2.green = from2.blue = 65535; /* white */
346       if (!make_color (WMGetFromPLString (WMGetFromPLArray (array, 5)), &to2))
347 	 to2.red = to2.green = to2.blue = 65535; /* white */
348 
349       r1 [0].red   = from1.red >> 8;
350       r1 [0].green = from1.green >> 8;
351       r1 [0].blue  = from1.blue >> 8;
352 
353       r1 [1].red   = to1.red >> 8;
354       r1 [1].green = to1.green >> 8;
355       r1 [1].blue  = to1.blue >> 8;
356 
357       r2 [0].red   = from2.red >> 8;
358       r2 [0].green = from2.green >> 8;
359       r2 [0].blue  = from2.blue >> 8;
360 
361       r2 [1].red   = to2.red >> 8;
362       r2 [1].green = to2.green >> 8;
363       r2 [1].blue  = to2.blue >> 8;
364 
365       if (!preview)
366       {
367 	 preview = gtk_preview_new (GTK_PREVIEW_COLOR);
368 	 gtk_preview_size (GTK_PREVIEW (preview), width, height);
369       }
370       else
371       {
372 	 width  = GTK_WIDGET (preview)->requisition.width;
373 	 height = GTK_WIDGET (preview)->requisition.height;
374       }
375 
376       gradient = RRenderInterwovenGradient (width, height,
377 					    r1, thickness1,
378 					    r2, thickness2);
379 
380       if (gradient)
381       {
382 	 preview = rimage_2_preview (gradient, preview,
383 				     gradient->height < 100);
384 	 RReleaseImage (gradient);
385       }
386       else
387 	 preview = make_image (PKGDATADIR "/black.xpm",
388 			       width, height, preview);
389 
390       return preview;
391    }
392 }
393 
394 #endif /* HAVE_INTERWOVEN_GRADIENT */
395 
396 #ifdef HAVE_LIBWMFUN
397 GtkWidget *
make_wmfun(proplist_t array,unsigned width,unsigned height,GtkWidget * preview)398 make_wmfun (proplist_t array, unsigned width, unsigned height,
399 	    GtkWidget *preview)
400 {
401    unsigned n 		 = WMGetPropListItemCount (array);
402    const char	*library = n > 2
403 			   ? WMGetFromPLString (WMGetFromPLArray (array, 1)) : NULL;
404    const char	*type 	 = n > 2
405 			   ? WMGetFromPLString (WMGetFromPLArray (array, 0)) : NULL;
406    if (!array || !WMIsPLArray (array) || !library || !type ||
407        !strcaseeq (type, "function") || !strncaseeq (library, "libwmfun",
408 						     strlen ("libwmfun")))
409       return make_image (PKGDATADIR "/black.xpm", width, height, preview);
410    else
411    {
412       unsigned	 n    	  = WMGetPropListItemCount (array);
413       char     **argv 	  = Calloc (n + 2, sizeof (char *));
414       char 	*function = WMGetFromPLString (WMGetFromPLArray (array, 2));
415       unsigned	 k;
416       unsigned	 argc;
417       RImage 	*image;
418 
419       for (k = 2, argc = 0; k < n; k++)
420 	 argv [argc++] = WMGetFromPLString (WMGetFromPLArray (array, k));
421       argv [argc] = NULL;
422 
423       if ((strcaseeq (function, "bilinear")))
424 	 image = bilinear (argc, argv, width, height, 0);
425       else if ((strcaseeq (function, "fade")))
426 	 image = fade (argc, argv, width, height, 0);
427       else if ((strcaseeq (function, "waves")))
428 	 image = waves (argc, argv, width, height, 0);
429       else
430 	 image = NULL;
431 
432       if (image)
433       {
434 	 preview = rimage_2_preview (image, preview, image->height < 100);
435 	 RReleaseImage (image);
436       }
437       else
438 	 preview = make_image (PKGDATADIR "/black.xpm",
439 			       width, height, preview);
440       Free (argv);
441       return preview;
442    }
443 }
444 #endif /* HAVE_LIBWMFUN */
445 
446 /*****************************************************************************
447 
448 				private code
449 
450 *****************************************************************************/
451 
452 static RImage *
rimage_load(const char * filename,int width,int height)453 rimage_load (const char *filename, int width, int height)
454 {
455    char *path = get_pixmap_path (filename);
456 
457    if (!path)
458       return rimage_load (PKGDATADIR "/black.xpm", width, height);
459    else
460    {
461       RImage *image;
462 
463       image = RLoadImage (rc, path, 0);
464       if (!image || !image->width || !image->height)
465 	 return rimage_load (PKGDATADIR "/black.xpm", width, height);
466       if (height > 0 || width > 0)	/* enforce scaling */
467       {
468 	 if (height <= 0)
469 	    height = image->height;
470 	 if (width <= 0)
471 	    width = image->width;
472 
473 	 if (streq (filename, PKGDATADIR "/black.xpm"))
474 	 {
475 	    RImage *scaled = RScaleImage (image, width, height);
476 	    RReleaseImage (image);
477 	    image = scaled;
478 	 }
479 	 else if (image->width > (unsigned) width
480 		  || image->height > (unsigned) height)
481 	 {
482 	    RImage *scaled;
483 
484 	    if (height * image->width > width * image->height)
485 	       height = width * image->height / image->width;
486 	    else
487 	       width  = height * image->width / image->height;
488 
489 	    scaled = RScaleImage (image, max (width, 22), max (height, 22));
490 	    RReleaseImage (image);
491 	    image = scaled;
492 	 }
493       }
494       Free (path);
495       return image;
496    }
497 }
498 
499 static GtkWidget *
rimage_2_preview(RImage * image,GtkWidget * preview,bool_t bevel)500 rimage_2_preview (RImage *image, GtkWidget *preview, bool_t bevel)
501 {
502    assert (image && image->width && image->height);
503 
504 #ifdef HAVE_WRASTER_0_20
505    {
506       guchar		*row;
507       unsigned		x, y;
508       unsigned		x0, y0, width, height;
509       RColor		*bg = get_background_color ();
510       unsigned char	*ptr = image->data;
511 
512       if (bevel)
513 	 RBevelImage (image, RBEV_RAISED2);
514       if (!preview)
515       {
516 	 preview = gtk_preview_new (GTK_PREVIEW_COLOR);
517 	 gtk_preview_size (GTK_PREVIEW (preview), image->width, image->height);
518 	 width 	 = image->width;
519 	 height  = image->height;
520 	 x0 = y0 = 0;
521       }
522       else
523       {
524 	 width  = GTK_WIDGET (preview)->requisition.width;
525 	 height = GTK_WIDGET (preview)->requisition.height;
526 
527 	 assert (image->width <= width && image->height <= height);
528 
529 	 x0 = (width - image->width) / 2;
530 	 y0 = (height - image->height) / 2;
531       }
532 
533       row = Calloc (width * 3, sizeof (guchar));
534       {
535 	 guchar *pixel = row;
536 
537 	 for (x = 0; x < width; x++)
538 	 {
539 	    *pixel++ = bg->red;
540 	    *pixel++ = bg->green;
541 	    *pixel++ = bg->blue;
542 	 }
543       }
544       for (y = 0; y < height; y++)
545 	 gtk_preview_draw_row (GTK_PREVIEW (preview), row, 0, y, width);
546 
547       for (y = 0; y < image->height; y++)
548       {
549 	 guchar *pixel = row;
550 
551 	 for (x = 0; x < image->width; x++)
552 	    if (image->format == RRGBAFormat)
553 	    {
554 	       unsigned int r = *ptr++;
555 	       unsigned int g = *ptr++;
556 	       unsigned int b = *ptr++;
557 	       unsigned int a = *ptr++;
558 	       unsigned int na = 255 - a;
559 
560 	       *pixel++ = (bg->red * na + r * a) / 256;
561 	       *pixel++ = (bg->green * na + g * a) / 256;
562 	       *pixel++ = (bg->blue * na + b * a) / 256;
563 	    }
564 	    else
565 	    {
566 	       *pixel++ = *ptr++;
567 	       *pixel++ = *ptr++;
568 	       *pixel++ = *ptr++;
569 	    }
570 
571 	 gtk_preview_draw_row (GTK_PREVIEW (preview), row, x0, y0 + y,
572 			       image->width);
573       }
574       Free (row);
575       gtk_widget_draw (preview, NULL);
576       return preview;
577    }
578 #else  /* old wraster of wmaker < 0.62.0 */
579    {
580       guchar		*row;
581       unsigned		x, y;
582       unsigned char	*r, *g, *b, *a;
583       unsigned		x0, y0, width, height;
584       RColor		*bg = get_background_color ();
585 
586       r = image->data [0];
587       g = image->data [1];
588       b = image->data [2];
589       a = image->data [3];
590 
591       if (bevel)
592 	 RBevelImage (image, RBEV_RAISED2);
593       if (!preview)
594       {
595 	 preview = gtk_preview_new (GTK_PREVIEW_COLOR);
596 	 gtk_preview_size (GTK_PREVIEW (preview), image->width, image->height);
597 	 width 	 = image->width;
598 	 height  = image->height;
599 	 x0 = y0 = 0;
600       }
601       else
602       {
603 	 width  = GTK_WIDGET (preview)->requisition.width;
604 	 height = GTK_WIDGET (preview)->requisition.height;
605 
606 	 assert (image->width <= width && image->height <= height);
607 
608 	 x0 = (width - image->width) / 2;
609 	 y0 = (height - image->height) / 2;
610       }
611 
612       row = Calloc (width * 3, sizeof (guchar));
613       {
614 	 guchar *pixel = row;
615 
616 	 for (x = 0; x < width; x++)
617 	 {
618 	    *pixel++ = bg->red;
619 	    *pixel++ = bg->green;
620 	    *pixel++ = bg->blue;
621 	 }
622       }
623       for (y = 0; y < height; y++)
624 	 gtk_preview_draw_row (GTK_PREVIEW (preview), row, 0, y, width);
625 
626       for (y = 0; y < image->height; y++)
627       {
628 	 guchar *pixel = row;
629 
630 	 for (x = 0; x < image->width; x++)
631 	    if (!a || *a++)
632 	    {
633 	       *pixel++ = *r++;
634 	       *pixel++ = *g++;
635 	       *pixel++ = *b++;
636 	    }
637 	    else
638 	    {
639 	       *pixel++ = bg->red;
640 	       *pixel++ = bg->green;
641 	       *pixel++ = bg->blue;
642 	       r++;
643 	       g++;
644 	       b++;
645 	    }
646 
647 	 gtk_preview_draw_row (GTK_PREVIEW (preview), row, x0, y0 + y,
648 			       image->width);
649       }
650       Free (row);
651       gtk_widget_draw (preview, NULL);
652       return preview;
653    }
654 #endif
655 }
656 
657 static RColor *
get_background_color(void)658 get_background_color (void)
659 {
660    static RColor *background = NULL;
661 
662    if (!background)
663    {
664       background = Calloc (1, sizeof (RColor));
665 
666       if (main_window && main_window->style)
667       {
668 	 background->red
669 	    = main_window->style->bg [GTK_STATE_NORMAL].red >> 8;
670 	 background->green
671 	    = main_window->style->bg [GTK_STATE_NORMAL].green >> 8;
672 	 background->blue
673 	    = main_window->style->bg [GTK_STATE_NORMAL].blue >> 8;
674       }
675       else
676       {
677 	 background->red   = 0xa8;
678 	 background->green = 0xa8;
679 	 background->blue  = 0xa8;
680       }
681    }
682    return background;
683 }
684 
685 #endif /* defined(PREVIEWS) && !defined(CONVERT) */
686