* A little clean-up.
[fvwm.git] / libs / Graphics.c
blob3ee628118c691798c6e7a95d90257d0c35a5062e
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 /* Graphics.c: misc convenience functions for drawing stuff */
19 /* ---------------------------- included header files ---------------------- */
21 #include "config.h"
23 #include <X11/Xlib.h>
24 #include <stdio.h>
25 #include <math.h>
27 #include "defaults.h"
28 #include "libs/fvwmlib.h"
29 #include "libs/PictureBase.h"
30 #include "libs/PictureUtils.h"
31 #include "libs/PictureGraphics.h"
32 #include "libs/gravity.h"
33 #include "libs/FImage.h"
35 /* ---------------------------- local definitions -------------------------- */
37 /* Define some standard constants that are not defined on QNX 4.25 */
38 #ifndef M_PI
39 #define M_PI 3.14159265358979323846
40 #endif
41 #ifndef M_PI_2
42 #define M_PI_2 1.57079632679489661923
43 #endif
45 /* ---------------------------- local macros ------------------------------- */
47 /* ---------------------------- imports ------------------------------------ */
49 /* ---------------------------- included code files ------------------------ */
51 /* ---------------------------- local types -------------------------------- */
53 /* ---------------------------- forward declarations ----------------------- */
55 /* ---------------------------- local variables ---------------------------- */
57 /* ---------------------------- exported variables (globals) --------------- */
59 /* ---------------------------- local functions ---------------------------- */
61 /* ---------------------------- interface functions ------------------------ */
63 /* Draws the relief pattern around a window
64 * Draws a line_width wide rectangle from (x,y) to (x+w,y+h) i.e w+1 wide,
65 * h+1 high
66 * Draws end points assuming CAP_NOT_LAST style in GC
67 * Draws anti-clockwise in case CAP_BUTT is the style and the end points overlap
68 * Top and bottom lines come out full length, the sides come out 1 pixel less
69 * This is so FvwmBorder windows have a correct bottom edge and the sticky lines
70 * look like just lines
71 * rotation rotate the relief and shadow part
73 void do_relieve_rectangle_with_rotation(
74 Display *dpy, Drawable d, int x, int y, int w, int h,
75 GC ReliefGC, GC ShadowGC, int line_width, Bool use_alternate_shading,
76 int rotation)
78 XSegment* seg;
79 GC shadow_gc, relief_gc;
80 int i,i2;
81 int a;
82 int l;
84 a = (use_alternate_shading) ? 1 : 0;
85 l = 1 - a;
86 if (w <= 0 || h <= 0)
88 return;
90 /* If line_width is negative, reverse the rotation, which will */
91 /* have the effect of inverting the relief. */
92 if (line_width < 0)
94 line_width = -line_width;
95 rotation = gravity_add_rotations(rotation, ROTATION_180);
97 switch (rotation)
99 case ROTATION_180:
100 case ROTATION_270:
101 rotation = gravity_add_rotations(rotation, ROTATION_180);
102 shadow_gc = ReliefGC;
103 relief_gc = ShadowGC;
104 break;
105 default:
106 shadow_gc = ShadowGC;
107 relief_gc = ReliefGC;
108 break;
110 seg = (XSegment*)alloca((sizeof(XSegment) * line_width) * 2);
111 /* from 0 to the lesser of line_width & just over half w */
112 for (i = 0; (i < line_width) && (i <= w / 2); i++)
114 if (rotation == ROTATION_0)
116 /* left */
117 seg[i].x1 = x+i; seg[i].y1 = y+i+a;
118 seg[i].x2 = x+i; seg[i].y2 = y+h-i+a;
120 else /* ROTATION_90 */
122 /* right */
123 seg[i].x1 = x+w-i; seg[i].y1 = y+h-i-a;
124 seg[i].x2 = x+w-i; seg[i].y2 = y+i+1-a;
127 i2 = i;
128 /* draw top segments */
129 for (i = 0; (i < line_width) && (i <= h / 2); i++,i2++)
131 seg[i2].x1 = x+w-i-a; seg[i2].y1 = y+i;
132 seg[i2].x2 = x+i+1-a; seg[i2].y2 = y+i;
134 XDrawSegments(dpy, d, relief_gc, seg, i2);
135 /* bottom */
136 for (i = 0; (i < line_width) && (i <= h / 2); i++)
138 seg[i].x1 = x+i+a+l; seg[i].y1 = y+h-i;
139 seg[i].x2 = x+w-i-1+a; seg[i].y2 = y+h-i;
141 i2 = i;
142 for (i = 0; (i < line_width) && (i <= w / 2); i++,i2++)
144 if (rotation == ROTATION_0)
146 /* right */
147 seg[i2].x1 = x+w-i; seg[i2].y1 = y+h-i-a;
148 seg[i2].x2 = x+w-i; seg[i2].y2 = y+i+1-a;
150 else /* ROTATION_90 */
152 /* left */
153 seg[i2].x1 = x+i; seg[i2].y1 = y+i+a;
154 seg[i2].x2 = x+i; seg[i2].y2 = y+h-i+a;
157 XDrawSegments(dpy, d, shadow_gc, seg, i2);
159 return;
162 void do_relieve_rectangle(
163 Display *dpy, Drawable d, int x, int y, int w, int h,
164 GC ReliefGC, GC ShadowGC, int line_width, Bool use_alternate_shading)
166 do_relieve_rectangle_with_rotation(
167 dpy, d, x, y, w, h, ReliefGC, ShadowGC, line_width,
168 use_alternate_shading, ROTATION_0);
169 return;
172 /* Creates a pixmap that is a horizontally stretched version of the input
173 * pixmap
175 Pixmap CreateStretchXPixmap(
176 Display *dpy, Pixmap src, int src_width, int src_height, int src_depth,
177 int dest_width, GC gc)
179 int i;
180 Pixmap pixmap;
181 GC my_gc = None;
183 if (src_width < 0 || src_height < 0 || dest_width < 0)
185 return None;
187 pixmap = XCreatePixmap(dpy, src, dest_width, src_height, src_depth);
188 if (pixmap == None)
190 return None;
192 if (gc == None)
194 my_gc = fvwmlib_XCreateGC(dpy, pixmap, 0, 0);
196 for (i = 0; i < dest_width; i++)
198 XCopyArea(
199 dpy, src, pixmap, (gc == None)? my_gc:gc,
200 (i * src_width) / dest_width, 0, 1, src_height, i, 0);
202 if (my_gc)
204 XFreeGC(dpy, my_gc);
206 return pixmap;
210 /* Creates a pixmap that is a vertically stretched version of the input
211 * pixmap
213 Pixmap CreateStretchYPixmap(
214 Display *dpy, Pixmap src, int src_width, int src_height, int src_depth,
215 int dest_height, GC gc)
217 int i;
218 Pixmap pixmap;
219 GC my_gc = None;
221 if (src_height < 0 || src_depth < 0 || dest_height < 0)
223 return None;
225 pixmap = XCreatePixmap(dpy, src, src_width, dest_height, src_depth);
226 if (pixmap == None)
228 return None;
230 if (gc == None)
232 my_gc = fvwmlib_XCreateGC(dpy, pixmap, 0, 0);
234 for (i = 0; i < dest_height; i++)
236 XCopyArea(
237 dpy, src, pixmap, (gc == None)? my_gc:gc,
238 0, (i * src_height) / dest_height, src_width, 1, 0, i);
240 if (my_gc)
242 XFreeGC(dpy, my_gc);
244 return pixmap;
248 /* Creates a pixmap that is a stretched version of the input
249 * pixmap
251 Pixmap CreateStretchPixmap(
252 Display *dpy, Pixmap src, int src_width, int src_height, int src_depth,
253 int dest_width, int dest_height, GC gc)
255 Pixmap pixmap = None;
256 Pixmap temp_pixmap;
257 GC my_gc = None;
259 if (src_width < 0 || src_height < 0 || src_depth < 0 || dest_width < 0)
261 return None;
263 if (gc == None)
265 my_gc = fvwmlib_XCreateGC(dpy, src, 0, 0);
267 temp_pixmap = CreateStretchXPixmap(
268 dpy, src, src_width, src_height, src_depth, dest_width,
269 (gc == None)? my_gc:gc);
270 if (temp_pixmap == None)
272 if (my_gc)
274 XFreeGC(dpy, my_gc);
276 return None;
278 pixmap = CreateStretchYPixmap(
279 dpy, temp_pixmap, dest_width, src_height, src_depth,
280 dest_height, (gc == None)? my_gc:gc);
281 XFreePixmap(dpy, temp_pixmap);
282 if (my_gc)
284 XFreeGC(dpy, my_gc);
286 return pixmap;
289 /* Creates a pixmap that is a tiled version of the input pixmap. Modifies the
290 * sets the fill_style of the GC to FillSolid and the tile to None. */
291 Pixmap CreateTiledPixmap(
292 Display *dpy, Pixmap src, int src_width, int src_height,
293 int dest_width, int dest_height, int depth, GC gc)
295 XGCValues xgcv;
296 Pixmap pixmap;
298 if (src_width < 0 || src_height < 0 ||
299 dest_width < 0 || dest_height < 0)
301 return None;
303 pixmap = XCreatePixmap(dpy, src, dest_width, dest_height, depth);
304 if (pixmap == None)
306 return None;
308 xgcv.fill_style = FillTiled;
309 xgcv.tile = src;
310 xgcv.ts_x_origin = 0;
311 xgcv.ts_y_origin = 0;
312 XChangeGC(
313 dpy, gc, GCFillStyle | GCTile | GCTileStipXOrigin |
314 GCTileStipYOrigin, &xgcv);
315 XFillRectangle(dpy, pixmap, gc, 0, 0, dest_width, dest_height);
316 xgcv.fill_style = FillSolid;
317 XChangeGC(dpy, gc, GCFillStyle, &xgcv);
319 return pixmap;
322 Pixmap CreateRotatedPixmap(
323 Display *dpy, Pixmap src, int src_width, int src_height, int depth,
324 GC gc, int rotation)
326 GC my_gc = None;
327 Pixmap pixmap = None;
328 int dest_width, dest_height, i, j;
329 Bool error = False;
330 FImage *fim = NULL;
331 FImage *src_fim = NULL;
333 if (src_width <= 0 || src_height <= 0)
335 return None;
337 switch(rotation)
339 case ROTATION_90:
340 case ROTATION_270:
341 dest_width = src_height;
342 dest_height = src_width;
343 break;
344 case ROTATION_0:
345 case ROTATION_180:
346 dest_width = src_width;
347 dest_height = src_height;
348 break;
349 default:
350 return None;
351 break;
353 pixmap = XCreatePixmap(dpy, src, dest_width, dest_height, depth);
354 if (pixmap == None)
356 return None;
358 if (gc == None)
360 my_gc = fvwmlib_XCreateGC(dpy, src, 0, 0);
362 if (rotation == ROTATION_0)
364 XCopyArea(
365 dpy, src, pixmap, (gc == None)? my_gc:gc,
366 0, 0, src_width, src_height, 0, 0);
367 goto bail;
370 if (!(src_fim = FGetFImage(
371 dpy, src, Pvisual, depth, 0, 0, src_width, src_height,
372 AllPlanes, ZPixmap)))
374 error = True;
375 goto bail;
377 if (!(fim = FCreateFImage(
378 dpy, Pvisual, depth, ZPixmap, dest_width, dest_height)))
380 error = True;
381 goto bail;
384 for (j = 0; j < src_height; j++)
386 for (i = 0; i < src_width; i++)
388 switch(rotation)
390 case ROTATION_270:
391 XPutPixel(
392 fim->im, j, src_width - i - 1,
393 XGetPixel(src_fim->im, i, j));
394 break;
395 case ROTATION_90:
396 XPutPixel(
397 fim->im, src_height - j - 1, i,
398 XGetPixel(src_fim->im, i, j));
399 break;
400 case ROTATION_180:
401 XPutPixel(
402 fim->im,
403 src_width - i - 1, src_height - j - 1,
404 XGetPixel(src_fim->im, i, j));
405 break;
406 default:
407 break;
411 FPutFImage(dpy, pixmap, gc, fim, 0, 0, 0, 0, dest_width, dest_height);
412 bail:
413 if (error && pixmap)
415 XFreePixmap(dpy,pixmap);
416 pixmap = None;
418 if (fim)
420 FDestroyFImage(dpy, fim);
422 if (src_fim)
424 FDestroyFImage(dpy, src_fim);
426 if (my_gc)
428 XFreeGC(dpy, my_gc);
430 return pixmap;
435 * Returns True if the given type of gradient is supported.
438 Bool IsGradientTypeSupported(char type)
440 switch (toupper(type))
442 case V_GRADIENT:
443 case H_GRADIENT:
444 case B_GRADIENT:
445 case D_GRADIENT:
446 case R_GRADIENT:
447 case Y_GRADIENT:
448 case S_GRADIENT:
449 case C_GRADIENT:
450 return True;
451 default:
452 fprintf(stderr, "%cGradient type is not supported\n",
453 toupper(type));
454 return False;
460 * Allocates a linear color gradient (veliaa@rpi.edu)
463 static
464 XColor *AllocLinearGradient(
465 char *s_from, char *s_to, int npixels, int skip_first_color, int dither)
467 XColor *xcs;
468 XColor from, to, c;
469 float r;
470 float dr;
471 float g;
472 float dg;
473 float b;
474 float db;
475 int i;
476 int got_all = 1;
477 int div;
479 if (npixels < 1)
481 fprintf(stderr,
482 "AllocLinearGradient: Invalid number of pixels: %d\n",
483 npixels);
484 return NULL;
486 if (!s_from || !XParseColor(Pdpy, Pcmap, s_from, &from))
488 fprintf(stderr, "Cannot parse color \"%s\"\n",
489 s_from ? s_from : "<blank>");
490 return NULL;
492 if (!s_to || !XParseColor(Pdpy, Pcmap, s_to, &to))
494 fprintf(stderr, "Cannot parse color \"%s\"\n",
495 s_to ? s_to : "<blank>");
496 return NULL;
499 /* divisor must not be zero, hence this calculation */
500 div = (npixels == 1) ? 1 : npixels - 1;
501 c = from;
502 /* red part and step width */
503 r = from.red;
504 dr = (float)(to.red - from.red);
505 /* green part and step width */
506 g = from.green;
507 dg = (float)(to.green - from.green);
508 /* blue part and step width */
509 b = from.blue;
510 db = (float)(to.blue - from.blue);
511 xcs = (XColor *)safemalloc(sizeof(XColor) * npixels);
512 memset(xcs, 0, sizeof(XColor) * npixels);
513 c.flags = DoRed | DoGreen | DoBlue;
514 for (i = (skip_first_color) ? 1 : 0; i < npixels && div > 0; ++i)
516 c.red = (unsigned short)
517 ((int)(r + dr / (float)div * (float)i + 0.5));
518 c.green = (unsigned short)
519 ((int)(g + dg / (float)div * (float)i + 0.5));
520 c.blue = (unsigned short)
521 ((int)(b + db / (float)div * (float)i + 0.5));
522 if (dither == 0 && !PictureAllocColor(Pdpy, Pcmap, &c, False))
524 got_all = 0;
526 xcs[i] = c;
528 if (!got_all && dither == 0)
530 fprintf(stderr, "Cannot alloc color gradient %s to %s\n",
531 s_from, s_to);
534 return xcs;
539 * Allocates a nonlinear color gradient (veliaa@rpi.edu)
542 static XColor *AllocNonlinearGradient(
543 char *s_colors[], int clen[], int nsegs, int npixels, int dither)
545 XColor *xcs = (XColor *)safemalloc(sizeof(XColor) * npixels);
546 int i;
547 int curpixel = 0;
548 int *seg_end_colors;
549 int seg_sum = 0;
550 float color_sum = 0.0;
552 if (nsegs < 1 || npixels < 2)
554 fprintf(stderr,
555 "Gradients must specify at least one segment and"
556 " two colors\n");
557 free(xcs);
558 return NULL;
560 for (i = 0; i < npixels; i++)
562 xcs[i].pixel = 0;
565 /* get total length of all segments */
566 for (i = 0; i < nsegs; i++)
568 seg_sum += clen[i];
571 /* calculate the index of a segment's las color */
572 seg_end_colors = alloca(nsegs * sizeof(int));
573 if (nsegs == 1)
575 seg_end_colors[0] = npixels - 1;
577 else
579 for (i = 0; i < nsegs; i++)
581 color_sum += (float)(clen[i] * (npixels - 1)) /
582 (float)(seg_sum);
583 seg_end_colors[i] = (int)(color_sum + 0.5);
585 if (seg_end_colors[nsegs - 1] > npixels - 1)
587 fprintf(stderr,
588 "BUG: (AllocNonlinearGradient): "
589 "seg_end_colors[nsegs - 1] (%d)"
590 " > npixels - 1 (%d)."
591 " Gradient drawing aborted\n",
592 seg_end_colors[nsegs - 1], npixels - 1);
593 return NULL;
595 /* take care of rounding errors */
596 seg_end_colors[nsegs - 1] = npixels - 1;
599 for (i = 0; i < nsegs; ++i)
601 XColor *c = NULL;
602 int j;
603 int n;
604 int skip_first_color = (curpixel != 0);
606 if (i == 0)
608 n = seg_end_colors[0] + 1;
610 else
612 n = seg_end_colors[i] - seg_end_colors[i - 1] + 1;
615 if (n > 1)
617 c = AllocLinearGradient(
618 s_colors[i], s_colors[i + 1], n,
619 skip_first_color, dither);
620 if (!c && (n - skip_first_color) != 0)
622 free(xcs);
623 return NULL;
625 for (j = skip_first_color; j < n; ++j)
627 xcs[curpixel + j] = c[j];
629 curpixel += n - 1;
631 if (c)
633 free(c);
634 c = NULL;
636 if (curpixel != seg_end_colors[i])
638 fprintf(stderr,
639 "BUG: (AllocNonlinearGradient): "
640 "nsegs %d, i %d, curpixel %d,"
641 " seg_end_colors[i] = %d,"
642 " npixels %d, n %d\n",
643 nsegs, i, curpixel,
644 seg_end_colors[i],npixels,n);
645 return NULL;
648 return xcs;
651 /* Convenience function. Calls AllocNonLinearGradient to fetch all colors and
652 * then frees the color names and the perc and color_name arrays. */
653 XColor *AllocAllGradientColors(
654 char *color_names[], int perc[], int nsegs, int ncolors, int dither)
656 XColor *xcs = NULL;
657 int i;
659 /* grab the colors */
660 xcs = AllocNonlinearGradient(
661 color_names, perc, nsegs, ncolors, dither);
662 for (i = 0; i <= nsegs; i++)
664 if (color_names[i])
666 free(color_names[i]);
669 free(color_names);
670 free(perc);
671 if (!xcs)
673 fprintf(stderr, "couldn't create gradient\n");
674 return NULL;
677 return xcs;
680 /* groks a gradient string and creates arrays of colors and percentages
681 * returns the number of colors asked for (No. allocated may be less due
682 * to the ColorLimit command). A return of 0 indicates an error
684 unsigned int ParseGradient(
685 char *gradient, char **rest, char ***colors_return, int **perc_return,
686 int *nsegs_return)
688 char *item;
689 char *orig;
690 unsigned int npixels;
691 char **s_colors;
692 int *perc;
693 int nsegs, i, sum;
694 Bool is_syntax_error = False;
696 /* get the number of colors specified */
697 if (rest)
699 *rest = gradient;
701 orig = gradient;
703 if (GetIntegerArguments(gradient, &gradient, (int *)&npixels, 1) != 1 ||
704 npixels < 2)
706 fprintf(
707 stderr, "ParseGradient: illegal number of colors in"
708 " gradient: '%s'\n", orig);
709 return 0;
712 /* get the starting color or number of segments */
713 gradient = GetNextToken(gradient, &item);
714 if (gradient)
716 gradient = SkipSpaces(gradient, NULL, 0);
718 if (!gradient || !*gradient || !item)
720 fprintf(stderr, "Incomplete gradient style: '%s'\n", orig);
721 if (item)
723 free(item);
725 if (rest)
727 *rest = gradient;
729 return 0;
732 if (GetIntegerArguments(item, NULL, &nsegs, 1) != 1)
734 /* get the end color of a simple gradient */
735 s_colors = (char **)safemalloc(sizeof(char *) * 2);
736 perc = (int *)safemalloc(sizeof(int));
737 nsegs = 1;
738 s_colors[0] = item;
739 gradient = GetNextToken(gradient, &item);
740 s_colors[1] = item;
741 perc[0] = 100;
743 else
745 free(item);
746 /* get a list of colors and percentages */
747 if (nsegs < 1)
748 nsegs = 1;
749 if (nsegs > MAX_GRADIENT_SEGMENTS)
750 nsegs = MAX_GRADIENT_SEGMENTS;
751 s_colors = (char **)safemalloc(sizeof(char *) * (nsegs + 1));
752 perc = (int *)safemalloc(sizeof(int) * nsegs);
753 for (i = 0; !is_syntax_error && i <= nsegs; i++)
755 s_colors[i] = 0;
756 gradient = GetNextToken(gradient, &s_colors[i]);
757 if (i < nsegs)
759 if (GetIntegerArguments(
760 gradient, &gradient, &perc[i], 1)
761 != 1 || perc[i] <= 0)
763 /* illegal size */
764 perc[i] = 0;
768 if (s_colors[nsegs] == NULL)
770 fprintf(
771 stderr, "ParseGradient: too few gradient"
772 " segments: '%s'\n", orig);
773 is_syntax_error = True;
777 /* sanity check */
778 for (i = 0, sum = 0; !is_syntax_error && i < nsegs; ++i)
780 int old_sum = sum;
782 sum += perc[i];
783 if (sum < old_sum)
785 /* integer overflow */
786 fprintf(
787 stderr, "ParseGradient: multi gradient"
788 " overflow: '%s'", orig);
789 is_syntax_error = 1;
790 break;
793 if (is_syntax_error)
795 for (i = 0; i <= nsegs; ++i)
797 if (s_colors[i])
799 free(s_colors[i]);
802 free(s_colors);
803 free(perc);
804 if (rest)
806 *rest = gradient;
808 return 0;
812 /* sensible limits */
813 if (npixels < 2)
814 npixels = 2;
815 if (npixels > MAX_GRADIENT_COLORS)
816 npixels = MAX_GRADIENT_COLORS;
818 /* send data back */
819 *colors_return = s_colors;
820 *perc_return = perc;
821 *nsegs_return = nsegs;
822 if (rest)
823 *rest = gradient;
825 return npixels;
828 /* Calculate the prefered dimensions of a gradient, based on the number of
829 * colors and the gradient type. Returns False if the gradient type is not
830 * supported. */
831 Bool CalculateGradientDimensions(
832 Display *dpy, Drawable d, int ncolors, char type, int dither,
833 unsigned int *width_ret, unsigned int *height_ret)
835 static unsigned int best_width = 0, best_height = 0;
836 int dither_factor = (dither > 0)? 128:1;
838 /* get the best tile size (once) */
839 if (!best_width)
841 if (!XQueryBestTile(dpy, d, 1, 1, &best_width, &best_height))
843 best_width = 0;
844 best_height = 0;
846 /* this is needed for buggy X servers like XFree 3.3.3.1 */
847 if (!best_width)
848 best_width = 1;
849 if (!best_height)
850 best_height = 1;
853 switch (type) {
854 case H_GRADIENT:
855 *width_ret = ncolors;
856 *height_ret = best_height * dither_factor;
857 break;
858 case V_GRADIENT:
859 *width_ret = best_width * dither_factor;
860 *height_ret = ncolors;
861 break;
862 case D_GRADIENT:
863 case B_GRADIENT:
864 /* diagonal gradients are rendered into a rectangle for which
865 * the width plus the height is equal to ncolors + 1. The
866 * rectangle is square when ncolors is odd and one pixel
867 * taller than wide with even numbers */
868 *width_ret = (ncolors + 1) / 2;
869 *height_ret = ncolors + 1 - *width_ret;
870 break;
871 case S_GRADIENT:
872 /* square gradients have the last color as a single pixel in
873 * the centre */
874 *width_ret = *height_ret = 2 * ncolors - 1;
875 break;
876 case C_GRADIENT:
877 /* circular gradients have the first color as a pixel in each
878 * corner */
879 *width_ret = *height_ret = 2 * ncolors - 1;
880 break;
881 case R_GRADIENT:
882 case Y_GRADIENT:
883 /* swept types need each color to occupy at least one pixel at
884 * the edge. Get the smallest odd number that will provide
885 * enough */
886 for (*width_ret = 1;
887 (double)(*width_ret - 1) * M_PI < (double)ncolors;
888 *width_ret += 2)
890 /* nothing to do here */
892 *height_ret = *width_ret;
893 break;
894 default:
895 fprintf(stderr, "%cGradient not supported\n", type);
896 return False;
898 return True;
901 /* Does the actual drawing of the pixmap. If the in_drawable argument is None,
902 * a new pixmap of the given depth, width and height is created. If it is not
903 * None the gradient is drawn into it. The d_width, d_height, d_x and d_y
904 * describe the traget rectangle within the drawable. */
905 Drawable CreateGradientPixmap(
906 Display *dpy, Drawable d, GC gc, int type, int g_width, int g_height,
907 int ncolors, XColor *xcs, int dither, Pixel **d_pixels, int *d_npixels,
908 Drawable in_drawable, int d_x, int d_y, int d_width, int d_height,
909 XRectangle *rclip)
911 Pixmap pixmap = None;
912 PictureImageColorAllocator *pica = NULL;
913 XColor c;
914 FImage *fim;
915 register int i, j;
916 XGCValues xgcv;
917 Drawable target;
918 int t_x;
919 int t_y;
920 int t_width;
921 int t_height;
922 int ps;
924 if (d_pixels != NULL && *d_pixels != NULL)
926 if (d_npixels != NULL && *d_npixels > 0)
928 PictureFreeColors(
929 dpy, Pcmap, *d_pixels, *d_npixels, 0, False);
931 free(*d_pixels);
932 *d_pixels = NULL;
934 if (d_npixels != NULL)
936 *d_npixels = 0;
938 if (g_height < 0 || g_width < 0 || d_width < 0 || d_height < 0)
939 return None;
941 if (in_drawable == None)
943 /* create a pixmap to use */
944 pixmap = XCreatePixmap(dpy, d, g_width, g_height, Pdepth);
945 if (pixmap == None)
946 return None;
947 target = pixmap;
948 t_x = 0;
949 t_y = 0;
950 t_width = g_width;
951 t_height = g_height;
953 else
955 target = in_drawable;
956 t_x = d_x;
957 t_y = d_y;
958 t_width = d_width;
959 t_height = d_height;
962 fim = FCreateFImage(
963 dpy, Pvisual, Pdepth, ZPixmap, t_width, t_height);
964 if (!fim)
966 fprintf(stderr, "%cGradient couldn't get image\n", type);
967 if (pixmap != None)
968 XFreePixmap(dpy, pixmap);
969 return None;
971 if (dither)
973 pica = PictureOpenImageColorAllocator(
974 dpy, Pcmap, t_width, t_height,
975 False, False, dither, False);
977 ps = t_width * t_height;
978 /* now do the fancy drawing */
979 switch (type)
981 case H_GRADIENT:
983 for (i = 0; i < t_width; i++)
985 int d = i * ncolors / t_width;
986 c = xcs[d];
987 for (j = 0; j < t_height; j++)
989 if (dither)
991 c = xcs[d];
992 PictureAllocColorImage(
993 dpy, pica, &c, i, j);
995 XPutPixel(fim->im, i, j, c.pixel);
999 break;
1000 case V_GRADIENT:
1002 for (j = 0; j < t_height; j++)
1004 int d = j * ncolors / t_height;
1005 c = xcs[d];
1006 for (i = 0; i < t_width; i++)
1008 if (dither)
1010 c = xcs[d];
1011 PictureAllocColorImage(
1012 dpy, pica, &c, i, j);
1014 XPutPixel(fim->im, i, j, c.pixel);
1017 break;
1019 case D_GRADIENT:
1021 register int t_scale = t_width + t_height - 1;
1022 for (i = 0; i < t_width; i++)
1024 for (j = 0; j < t_height; j++)
1026 c = xcs[(i+j) * ncolors / t_scale];
1027 if (dither)
1029 PictureAllocColorImage(
1030 dpy, pica, &c, i, j);
1032 XPutPixel(fim->im, i, j, c.pixel);
1035 break;
1037 case B_GRADIENT:
1039 register int t_scale = t_width + t_height - 1;
1040 for (i = 0; i < t_width; i++)
1042 for (j = 0; j < t_height; j++)
1044 c = xcs[(i + (t_height - j - 1)) * ncolors /
1045 t_scale];
1046 if (dither)
1048 PictureAllocColorImage(
1049 dpy, pica, &c, i, j);
1051 XPutPixel(fim->im, i, j, c.pixel);
1054 break;
1056 case S_GRADIENT:
1058 register int t_scale = t_width * t_height;
1059 register int myncolors = ncolors * 2;
1060 for (i = 0; i < t_width; i++) {
1061 register int pi = min(i, t_width - 1 - i) * t_height;
1062 for (j = 0; j < t_height; j++) {
1063 register int pj =
1064 min(j, t_height - 1 - j) * t_width;
1065 c = xcs[(min(pi, pj) * myncolors - 1) /
1066 t_scale];
1067 if (dither)
1069 PictureAllocColorImage(
1070 dpy, pica, &c, i, j);
1072 XPutPixel(fim->im, i, j, c.pixel);
1076 break;
1077 case C_GRADIENT:
1079 register double t_scale =
1080 (double)(t_width * t_height) / sqrt(8);
1081 for (i = 0; i < t_width; i++)
1083 for (j = 0; j < t_height; j++)
1085 register double x =
1086 (double)((2 * i - t_width) * t_height) /
1087 4.0;
1088 register double y =
1089 (double)((t_height - 2 * j) * t_width) /
1090 4.0;
1091 register double rad = sqrt(x * x + y * y);
1092 c = xcs[(int)((rad * ncolors - 0.5) / t_scale)];
1093 if (dither)
1095 PictureAllocColorImage(
1096 dpy, pica, &c, i, j);
1098 XPutPixel(fim->im, i, j, c.pixel);
1101 break;
1103 case R_GRADIENT:
1105 register int w = t_width - 1;
1106 register int h = t_height - 1;
1107 /* g_width == g_height, both are odd, therefore x can be 0.0 */
1108 for (i = 0; i <= w; i++) {
1109 for (j = 0; j <= h; j++) {
1110 register double x =
1111 (double)((2 * i - w) * h) / 4.0;
1112 register double y =
1113 (double)((h - 2 * j) * w) / 4.0;
1114 /* angle ranges from -pi/2 to +pi/2 */
1115 register double angle;
1116 if (x != 0.0) {
1117 angle = atan(y / x);
1118 } else {
1119 angle = (y < 0) ? - M_PI_2 : M_PI_2;
1121 /* extend to -pi/2 to 3pi/2 */
1122 if (x < 0)
1123 angle += M_PI;
1124 /* move range from -pi/2:3*pi/2 to 0:2*pi */
1125 if (angle < 0.0)
1126 angle += M_PI * 2.0;
1127 /* normalize to gradient */
1128 c = xcs[(int)(angle * M_1_PI * 0.5 * ncolors)];
1129 if (dither)
1131 PictureAllocColorImage(
1132 dpy, pica, &c, i, j);
1134 XPutPixel(fim->im, i, j, c.pixel);
1138 break;
1140 * The Yin Yang gradient style and the following code are:
1141 * Copyright 1999 Sir Boris. (email to sir_boris@bigfoot.com may be
1142 * read by his groom but is not guaranteed to elicit a response)
1143 * No restrictions are placed on this code, as long as the copyright
1144 * notice is preserved.
1146 case Y_GRADIENT:
1148 register int r = t_width * t_height / 4;
1149 for (i = 0; i < t_width; i++) {
1150 for (j = 0; j < t_height; j++) {
1151 register double x =
1152 (double)((2 * i - t_width) * t_height) /
1153 4.0;
1154 register double y =
1155 (double)((t_height - 2 * j) * t_width) /
1156 4.0;
1157 register double rad = sqrt(x * x + y * y);
1158 /* angle ranges from -pi/2 to +pi/2 */
1159 register double angle;
1161 if (x != 0.0) {
1162 angle = atan(y / x);
1163 } else {
1164 angle = (y < 0) ? - M_PI_2 : M_PI_2;
1166 /* extend to -pi/2 to 3pi/2 */
1167 if (x < 0)
1168 angle += M_PI;
1169 /* warp the angle within the yinyang circle */
1170 if (rad <= r) {
1171 angle -= acos(rad / r);
1173 /* move range from -pi/2:3*pi/2 to 0:2*pi */
1174 if (angle < 0.0)
1175 angle += M_PI * 2.0;
1176 /* normalize to gradient */
1177 c = xcs[(int)(angle * M_1_PI * 0.5 * ncolors)];
1178 if (dither)
1180 PictureAllocColorImage(
1181 dpy, pica, &c, i, j);
1183 XPutPixel(fim->im, i, j, c.pixel);
1187 break;
1188 default:
1189 /* placeholder function, just fills the pixmap with the first
1190 * color */
1191 memset(fim->im->data, 0, fim->im->bytes_per_line * t_height);
1192 XAddPixel(fim->im, xcs[0].pixel);
1193 break;
1196 if (dither)
1198 if (d_pixels != NULL && d_npixels != NULL)
1200 PictureCloseImageColorAllocator(
1201 dpy, pica, d_npixels, d_pixels, 0);
1203 else
1205 /* possible color leak */
1209 /* set the gc style */
1210 xgcv.function = GXcopy;
1211 xgcv.plane_mask = AllPlanes;
1212 xgcv.fill_style = FillSolid;
1213 xgcv.clip_mask = None;
1214 XChangeGC(dpy, gc, GCFunction|GCPlaneMask|GCFillStyle|GCClipMask,
1215 &xgcv);
1216 if (rclip)
1218 XSetClipRectangles(dpy, gc, 0, 0, rclip, 1, Unsorted);
1220 /* copy the image to the server */
1221 FPutFImage(dpy, target, gc, fim, 0, 0, t_x, t_y, t_width, t_height);
1222 if (rclip)
1224 XSetClipMask(dpy, gc, None);
1226 FDestroyFImage(dpy, fim);
1227 return target;
1231 /* Create a pixmap from a gradient specifier, width and height are hints
1232 * that are only used for gradients that can be tiled e.g. H or V types
1233 * types are HVDBSCRY for Horizontal, Vertical, Diagonal, Back-diagonal, Square,
1234 * Circular, Radar and Yin/Yang respectively (in order of bloatiness)
1236 Pixmap CreateGradientPixmapFromString(
1237 Display *dpy, Drawable d, GC gc, int type, char *action,
1238 unsigned int *width_return, unsigned int *height_return,
1239 Pixel **pixels_return, int *nalloc_pixels, int dither)
1241 Pixel *d_pixels = NULL;
1242 unsigned int d_npixels = 0;
1243 XColor *xcs = NULL;
1244 unsigned int ncolors = 0;
1245 char **colors;
1246 int *perc, nsegs;
1247 Pixmap pixmap = None;
1249 /* set return pixels to NULL in case of premature return */
1250 if (pixels_return)
1251 *pixels_return = NULL;
1252 if (nalloc_pixels)
1253 *nalloc_pixels = 0;
1255 /* translate the gradient string into an array of colors etc */
1256 if (!(ncolors = ParseGradient(action, NULL, &colors, &perc, &nsegs))) {
1257 fprintf(stderr, "Can't parse gradient: '%s'\n", action);
1258 return None;
1260 /* grab the colors */
1261 xcs = AllocAllGradientColors(
1262 colors, perc, nsegs, ncolors, dither);
1263 if (xcs == NULL)
1265 return None;
1268 /* grok the size to create from the type */
1269 type = toupper(type);
1271 if (CalculateGradientDimensions(
1272 dpy, d, ncolors, type, dither, width_return, height_return))
1274 pixmap = CreateGradientPixmap(
1275 dpy, d, gc, type, *width_return, *height_return,
1276 ncolors, xcs, dither, &d_pixels, &d_npixels,
1277 None, 0, 0, 0, 0, NULL);
1280 /* if the caller has not asked for the pixels there is probably a leak
1282 if (PUseDynamicColors)
1284 if (!(pixels_return && nalloc_pixels))
1286 /* if the caller has not asked for the pixels there is
1287 * probably a leak */
1288 fprintf(stderr,
1289 "CreateGradient: potential color leak, losing track"
1290 " of pixels\n");
1291 if (d_pixels != NULL)
1293 free(d_pixels);
1296 else
1298 if (!dither)
1300 Pixel *pixels;
1301 int i;
1303 pixels = (Pixel *)safemalloc(
1304 ncolors * sizeof(Pixel));
1305 for(i=0; i<ncolors; i++)
1307 pixels[i] = xcs[i].pixel;
1309 *pixels_return = pixels;
1310 *nalloc_pixels = ncolors;
1312 else
1314 *pixels_return = d_pixels;
1315 *nalloc_pixels = d_npixels;
1319 else if (d_pixels != NULL)
1321 /* should not happen */
1322 free(d_pixels);
1325 free(xcs);
1327 return pixmap;
1332 * Draws a little Triangle pattern within a window
1335 void DrawTrianglePattern(
1336 Display *dpy, Drawable d, GC ReliefGC, GC ShadowGC, GC FillGC,
1337 int x, int y, int width, int height, int bw, char orientation,
1338 Bool draw_relief, Bool do_fill, Bool is_pressed)
1340 const struct
1342 const char line[3];
1343 const char point[3];
1344 } hi[4] =
1346 { { 1, 0, 0 }, { 1, 1, 0 } }, /* up */
1347 { { 1, 0, 1 }, { 1, 0, 0 } }, /* down */
1348 { { 1, 0, 0 }, { 1, 1, 0 } }, /* left */
1349 { { 1, 0, 1 }, { 1, 1, 0 } } /* right */
1351 XPoint points[4];
1352 GC temp_gc;
1353 int short_side;
1354 int long_side;
1355 int t_width;
1356 int t_height;
1357 int i;
1358 int type;
1360 /* remove border width from target area */
1361 width -= 2 * bw;
1362 height -= 2 * bw;
1363 x += bw;
1364 y += bw;
1365 if (width < 1 || height < 1)
1366 /* nothing to do */
1367 return;
1369 orientation = tolower(orientation);
1370 switch (orientation)
1372 case 'u':
1373 case 'd':
1374 long_side = width;
1375 short_side = height;
1376 type = (orientation == 'd');
1377 break;
1378 case 'l':
1379 case 'r':
1380 long_side = height;
1381 short_side = width;
1382 type = (orientation == 'r') + 2;
1383 break;
1384 default:
1385 /* unknowm orientation */
1386 return;
1389 /* assure the base side has an odd length */
1390 if ((long_side & 0x1) == 0)
1391 long_side--;
1392 /* reduce base length if short sides don't fit */
1393 if (short_side < long_side / 2 + 1)
1394 long_side = 2 * short_side - 1;
1395 else
1396 short_side = long_side / 2 + 1;
1398 if (orientation == 'u' || orientation == 'd')
1400 t_width = long_side;
1401 t_height = short_side;
1403 else
1405 t_width = short_side;
1406 t_height = long_side;
1408 /* find proper x/y coordinate */
1409 x += (width - t_width) / 2;
1410 y += (height - t_height) / 2;
1411 /* decrement width and height for convenience of calculation */
1412 t_width--;
1413 t_height--;
1415 /* get the list of points to draw */
1416 switch (orientation)
1418 case 'u':
1419 y += t_height;
1420 t_height = -t_height;
1421 case 'd':
1422 points[1].x = x + t_width / 2;
1423 points[1].y = y + t_height;
1424 points[2].x = x + t_width;
1425 points[2].y = y;
1426 break;
1427 case 'l':
1428 x += t_width;
1429 t_width = -t_width;
1430 case 'r':
1431 points[1].x = x + t_width;
1432 points[1].y = y + t_height / 2;
1433 points[2].x = x;
1434 points[2].y = y + t_height;
1435 break;
1437 points[0].x = x;
1438 points[0].y = y;
1439 points[3].x = x;
1440 points[3].y = y;
1442 if (do_fill)
1444 /* solid triangle */
1445 XFillPolygon(
1446 dpy, d, FillGC, points, 3, Convex, CoordModeOrigin);
1448 if (draw_relief)
1450 /* relief triangle */
1451 for (i = 0; i < 3; i++)
1453 temp_gc = (is_pressed ^ hi[type].line[i]) ?
1454 ReliefGC : ShadowGC;
1455 XDrawLine(
1456 dpy, d, temp_gc, points[i].x, points[i].y,
1457 points[i+1].x, points[i+1].y);
1459 for (i = 0; i < 3; i++)
1461 temp_gc = (is_pressed ^ hi[type].point[i]) ?
1462 ReliefGC : ShadowGC;
1463 XDrawPoint(dpy, d, temp_gc, points[i].x, points[i].y);
1467 return;
1470 GC fvwmlib_XCreateGC(
1471 Display *display, Drawable drawable, unsigned long valuemask,
1472 XGCValues *values)
1474 GC gc;
1475 Bool f;
1476 XGCValues gcv;
1478 if (!values)
1480 values = &gcv;
1482 f = values->graphics_exposures;
1483 if (!(valuemask & GCGraphicsExposures))
1485 valuemask |= GCGraphicsExposures;
1486 values->graphics_exposures = 0;
1488 gc = XCreateGC(display, drawable, valuemask, values);
1489 values->graphics_exposures = f;
1491 return gc;