/* -*-c-*- */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Graphics.c: misc convenience functions for drawing stuff */ /* ---------------------------- included header files ---------------------- */ #include "config.h" #include #include #include #include "defaults.h" #include "libs/fvwmlib.h" #include "libs/Parse.h" #include "libs/PictureBase.h" #include "libs/PictureUtils.h" #include "libs/PictureGraphics.h" #include "libs/gravity.h" #include "libs/FImage.h" #include "libs/Graphics.h" /* ---------------------------- local definitions -------------------------- */ /* Define some standard constants that are not included in the C89 standard */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef M_PI_2 #define M_PI_2 1.57079632679489661923 #endif #ifndef M_1_PI #define M_1_PI 0.31830988618379067154 #endif /* ---------------------------- local macros ------------------------------- */ /* ---------------------------- imports ------------------------------------ */ /* ---------------------------- included code files ------------------------ */ /* ---------------------------- local types -------------------------------- */ /* ---------------------------- forward declarations ----------------------- */ /* ---------------------------- local variables ---------------------------- */ /* ---------------------------- exported variables (globals) --------------- */ /* ---------------------------- local functions ---------------------------- */ /* ---------------------------- interface functions ------------------------ */ /* Draws the relief pattern around a window * Draws a line_width wide rectangle from (x,y) to (x+w,y+h) i.e w+1 wide, * h+1 high * Draws end points assuming CAP_NOT_LAST style in GC * Draws anti-clockwise in case CAP_BUTT is the style and the end points overlap * Top and bottom lines come out full length, the sides come out 1 pixel less * This is so FvwmBorder windows have a correct bottom edge and the sticky lines * look like just lines * rotation rotate the relief and shadow part */ void do_relieve_rectangle_with_rotation( Display *dpy, Drawable d, int x, int y, int w, int h, GC ReliefGC, GC ShadowGC, int line_width, Bool use_alternate_shading, int rotation) { XSegment* seg; GC shadow_gc, relief_gc; int i,i2; int a; int l; int max_w; int max_h; a = (use_alternate_shading) ? 1 : 0; l = 1 - a; if (w <= 0 || h <= 0) { return; } /* If line_width is negative, reverse the rotation, which will */ /* have the effect of inverting the relief. */ if (line_width < 0) { line_width = -line_width; rotation = gravity_add_rotations(rotation, ROTATION_180); } switch (rotation) { case ROTATION_180: case ROTATION_270: rotation = gravity_add_rotations(rotation, ROTATION_180); shadow_gc = ReliefGC; relief_gc = ShadowGC; break; default: shadow_gc = ShadowGC; relief_gc = ReliefGC; break; } max_w = min((w + 1) / 2, line_width); max_h = min((h + 1) / 2, line_width); seg = (XSegment*)alloca((sizeof(XSegment) * line_width) * 2); /* from 0 to the lesser of line_width & just over half w */ for (i = 0; i < max_w; i++) { if (rotation == ROTATION_0) { /* left */ seg[i].x1 = x+i; seg[i].y1 = y+i+a; seg[i].x2 = x+i; seg[i].y2 = y+h-i+a; } else /* ROTATION_90 */ { /* right */ seg[i].x1 = x+w-i; seg[i].y1 = y+h-i-a; seg[i].x2 = x+w-i; seg[i].y2 = y+i+1-a; } } i2 = i; /* draw top segments */ for (i = 0; i < max_h; i++,i2++) { seg[i2].x1 = x+w-i-a; seg[i2].y1 = y+i; seg[i2].x2 = x+i+1-a; seg[i2].y2 = y+i; } XDrawSegments(dpy, d, relief_gc, seg, i2); /* bottom */ for (i = 0; i < max_h; i++) { seg[i].x1 = x+i+a+l; seg[i].y1 = y+h-i; seg[i].x2 = x+w-i-1+a; seg[i].y2 = y+h-i; } i2 = i; for (i = 0; i < max_w; i++,i2++) { if (rotation == ROTATION_0) { /* right */ seg[i2].x1 = x+w-i; seg[i2].y1 = y+h-i-a; seg[i2].x2 = x+w-i; seg[i2].y2 = y+i+1-a; } else /* ROTATION_90 */ { /* left */ seg[i2].x1 = x+i; seg[i2].y1 = y+i+a; seg[i2].x2 = x+i; seg[i2].y2 = y+h-i+a; } } XDrawSegments(dpy, d, shadow_gc, seg, i2); return; } void do_relieve_rectangle( Display *dpy, Drawable d, int x, int y, int w, int h, GC ReliefGC, GC ShadowGC, int line_width, Bool use_alternate_shading) { do_relieve_rectangle_with_rotation( dpy, d, x, y, w, h, ReliefGC, ShadowGC, line_width, use_alternate_shading, ROTATION_0); return; } /* Creates a pixmap that is a horizontally stretched version of the input * pixmap */ Pixmap CreateStretchXPixmap( Display *dpy, Pixmap src, int src_width, int src_height, int src_depth, int dest_width, GC gc) { int i; Pixmap pixmap; GC my_gc = None; if (src_width < 0 || src_height < 0 || dest_width < 0) { return None; } pixmap = XCreatePixmap(dpy, src, dest_width, src_height, src_depth); if (pixmap == None) { return None; } if (gc == None) { my_gc = fvwmlib_XCreateGC(dpy, pixmap, 0, 0); } for (i = 0; i < dest_width; i++) { XCopyArea( dpy, src, pixmap, (gc == None)? my_gc:gc, (i * src_width) / dest_width, 0, 1, src_height, i, 0); } if (my_gc) { XFreeGC(dpy, my_gc); } return pixmap; } /* Creates a pixmap that is a vertically stretched version of the input * pixmap */ Pixmap CreateStretchYPixmap( Display *dpy, Pixmap src, int src_width, int src_height, int src_depth, int dest_height, GC gc) { int i; Pixmap pixmap; GC my_gc = None; if (src_height < 0 || src_depth < 0 || dest_height < 0) { return None; } pixmap = XCreatePixmap(dpy, src, src_width, dest_height, src_depth); if (pixmap == None) { return None; } if (gc == None) { my_gc = fvwmlib_XCreateGC(dpy, pixmap, 0, 0); } for (i = 0; i < dest_height; i++) { XCopyArea( dpy, src, pixmap, (gc == None)? my_gc:gc, 0, (i * src_height) / dest_height, src_width, 1, 0, i); } if (my_gc) { XFreeGC(dpy, my_gc); } return pixmap; } /* Creates a pixmap that is a stretched version of the input * pixmap */ Pixmap CreateStretchPixmap( Display *dpy, Pixmap src, int src_width, int src_height, int src_depth, int dest_width, int dest_height, GC gc) { Pixmap pixmap = None; Pixmap temp_pixmap; GC my_gc = None; if (src_width < 0 || src_height < 0 || src_depth < 0 || dest_width < 0) { return None; } if (gc == None) { my_gc = fvwmlib_XCreateGC(dpy, src, 0, 0); } temp_pixmap = CreateStretchXPixmap( dpy, src, src_width, src_height, src_depth, dest_width, (gc == None)? my_gc:gc); if (temp_pixmap == None) { if (my_gc) { XFreeGC(dpy, my_gc); } return None; } pixmap = CreateStretchYPixmap( dpy, temp_pixmap, dest_width, src_height, src_depth, dest_height, (gc == None)? my_gc:gc); XFreePixmap(dpy, temp_pixmap); if (my_gc) { XFreeGC(dpy, my_gc); } return pixmap; } /* Creates a pixmap that is a tiled version of the input pixmap. Modifies the * sets the fill_style of the GC to FillSolid and the tile to None. */ Pixmap CreateTiledPixmap( Display *dpy, Pixmap src, int src_width, int src_height, int dest_width, int dest_height, int depth, GC gc) { XGCValues xgcv; Pixmap pixmap; if (src_width < 0 || src_height < 0 || dest_width < 0 || dest_height < 0) { return None; } pixmap = XCreatePixmap(dpy, src, dest_width, dest_height, depth); if (pixmap == None) { return None; } xgcv.fill_style = FillTiled; xgcv.tile = src; xgcv.ts_x_origin = 0; xgcv.ts_y_origin = 0; XChangeGC( dpy, gc, GCFillStyle | GCTile | GCTileStipXOrigin | GCTileStipYOrigin, &xgcv); XFillRectangle(dpy, pixmap, gc, 0, 0, dest_width, dest_height); xgcv.fill_style = FillSolid; XChangeGC(dpy, gc, GCFillStyle, &xgcv); return pixmap; } Pixmap CreateRotatedPixmap( Display *dpy, Pixmap src, int src_width, int src_height, int depth, GC gc, int rotation) { GC my_gc = None; Pixmap pixmap = None; int dest_width, dest_height, i, j; Bool error = False; FImage *fim = NULL; FImage *src_fim = NULL; if (src_width <= 0 || src_height <= 0) { return None; } switch(rotation) { case ROTATION_90: case ROTATION_270: dest_width = src_height; dest_height = src_width; break; case ROTATION_0: case ROTATION_180: dest_width = src_width; dest_height = src_height; break; default: return None; break; } pixmap = XCreatePixmap(dpy, src, dest_width, dest_height, depth); if (pixmap == None) { return None; } if (gc == None) { my_gc = fvwmlib_XCreateGC(dpy, src, 0, 0); } if (rotation == ROTATION_0) { XCopyArea( dpy, src, pixmap, (gc == None)? my_gc:gc, 0, 0, src_width, src_height, 0, 0); goto bail; } if (!(src_fim = FGetFImage( dpy, src, Pvisual, depth, 0, 0, src_width, src_height, AllPlanes, ZPixmap))) { error = True; goto bail; } if (!(fim = FCreateFImage( dpy, Pvisual, depth, ZPixmap, dest_width, dest_height))) { error = True; goto bail; } for (j = 0; j < src_height; j++) { for (i = 0; i < src_width; i++) { switch(rotation) { case ROTATION_270: XPutPixel( fim->im, j, src_width - i - 1, XGetPixel(src_fim->im, i, j)); break; case ROTATION_90: XPutPixel( fim->im, src_height - j - 1, i, XGetPixel(src_fim->im, i, j)); break; case ROTATION_180: XPutPixel( fim->im, src_width - i - 1, src_height - j - 1, XGetPixel(src_fim->im, i, j)); break; default: break; } } } FPutFImage(dpy, pixmap, gc, fim, 0, 0, 0, 0, dest_width, dest_height); bail: if (error && pixmap) { XFreePixmap(dpy,pixmap); pixmap = None; } if (fim) { FDestroyFImage(dpy, fim); } if (src_fim) { FDestroyFImage(dpy, src_fim); } if (my_gc) { XFreeGC(dpy, my_gc); } return pixmap; } /* * * Returns True if the given type of gradient is supported. * */ Bool IsGradientTypeSupported(char type) { switch (toupper(type)) { case V_GRADIENT: case H_GRADIENT: case B_GRADIENT: case D_GRADIENT: case R_GRADIENT: case Y_GRADIENT: case S_GRADIENT: case C_GRADIENT: return True; default: fprintf(stderr, "%cGradient type is not supported\n", toupper(type)); return False; } } /* * * Allocates a linear color gradient (veliaa@rpi.edu) * */ static XColor *AllocLinearGradient( char *s_from, char *s_to, int npixels, int skip_first_color, int dither) { XColor *xcs; XColor from, to, c; float r; float dr; float g; float dg; float b; float db; int i; int got_all = 1; int div; if (npixels < 1) { fprintf(stderr, "AllocLinearGradient: Invalid number of pixels: %d\n", npixels); return NULL; } if (!s_from || !XParseColor(Pdpy, Pcmap, s_from, &from)) { fprintf(stderr, "Cannot parse color \"%s\"\n", s_from ? s_from : ""); return NULL; } if (!s_to || !XParseColor(Pdpy, Pcmap, s_to, &to)) { fprintf(stderr, "Cannot parse color \"%s\"\n", s_to ? s_to : ""); return NULL; } /* divisor must not be zero, hence this calculation */ div = (npixels == 1) ? 1 : npixels - 1; c = from; /* red part and step width */ r = from.red; dr = (float)(to.red - from.red); /* green part and step width */ g = from.green; dg = (float)(to.green - from.green); /* blue part and step width */ b = from.blue; db = (float)(to.blue - from.blue); xcs = (XColor *)safemalloc(sizeof(XColor) * npixels); memset(xcs, 0, sizeof(XColor) * npixels); c.flags = DoRed | DoGreen | DoBlue; for (i = (skip_first_color) ? 1 : 0; i < npixels && div > 0; ++i) { c.red = (unsigned short) ((int)(r + dr / (float)div * (float)i + 0.5)); c.green = (unsigned short) ((int)(g + dg / (float)div * (float)i + 0.5)); c.blue = (unsigned short) ((int)(b + db / (float)div * (float)i + 0.5)); if (dither == 0 && !PictureAllocColor(Pdpy, Pcmap, &c, False)) { got_all = 0; } xcs[i] = c; } if (!got_all && dither == 0) { fprintf(stderr, "Cannot alloc color gradient %s to %s\n", s_from, s_to); } return xcs; } /* * * Allocates a nonlinear color gradient (veliaa@rpi.edu) * */ static XColor *AllocNonlinearGradient( char *s_colors[], int clen[], int nsegs, int npixels, int dither) { XColor *xcs = (XColor *)safemalloc(sizeof(XColor) * npixels); int i; int curpixel = 0; int *seg_end_colors; int seg_sum = 0; float color_sum = 0.0; if (nsegs < 1 || npixels < 2) { fprintf(stderr, "Gradients must specify at least one segment and" " two colors\n"); free(xcs); return NULL; } for (i = 0; i < npixels; i++) { xcs[i].pixel = 0; } /* get total length of all segments */ for (i = 0; i < nsegs; i++) { seg_sum += clen[i]; } /* calculate the index of a segment's las color */ seg_end_colors = alloca(nsegs * sizeof(int)); if (nsegs == 1) { seg_end_colors[0] = npixels - 1; } else { for (i = 0; i < nsegs; i++) { color_sum += (float)(clen[i] * (npixels - 1)) / (float)(seg_sum); seg_end_colors[i] = (int)(color_sum + 0.5); } if (seg_end_colors[nsegs - 1] > npixels - 1) { fprintf(stderr, "BUG: (AllocNonlinearGradient): " "seg_end_colors[nsegs - 1] (%d)" " > npixels - 1 (%d)." " Gradient drawing aborted\n", seg_end_colors[nsegs - 1], npixels - 1); return NULL; } /* take care of rounding errors */ seg_end_colors[nsegs - 1] = npixels - 1; } for (i = 0; i < nsegs; ++i) { XColor *c = NULL; int j; int n; int skip_first_color = (curpixel != 0); if (i == 0) { n = seg_end_colors[0] + 1; } else { n = seg_end_colors[i] - seg_end_colors[i - 1] + 1; } if (n > 1) { c = AllocLinearGradient( s_colors[i], s_colors[i + 1], n, skip_first_color, dither); if (!c && (n - skip_first_color) != 0) { free(xcs); return NULL; } for (j = skip_first_color; j < n; ++j) { xcs[curpixel + j] = c[j]; } curpixel += n - 1; } if (c) { free(c); c = NULL; } if (curpixel != seg_end_colors[i]) { fprintf(stderr, "BUG: (AllocNonlinearGradient): " "nsegs %d, i %d, curpixel %d," " seg_end_colors[i] = %d," " npixels %d, n %d\n", nsegs, i, curpixel, seg_end_colors[i],npixels,n); return NULL; } } return xcs; } /* Convenience function. Calls AllocNonLinearGradient to fetch all colors and * then frees the color names and the perc and color_name arrays. */ XColor *AllocAllGradientColors( char *color_names[], int perc[], int nsegs, int ncolors, int dither) { XColor *xcs = NULL; int i; /* grab the colors */ xcs = AllocNonlinearGradient( color_names, perc, nsegs, ncolors, dither); for (i = 0; i <= nsegs; i++) { if (color_names[i]) { free(color_names[i]); } } free(color_names); free(perc); if (!xcs) { fprintf(stderr, "couldn't create gradient\n"); return NULL; } return xcs; } /* groks a gradient string and creates arrays of colors and percentages * returns the number of colors asked for (No. allocated may be less due * to the ColorLimit command). A return of 0 indicates an error */ int ParseGradient( char *gradient, char **rest, char ***colors_return, int **perc_return, int *nsegs_return) { char *item; char *orig; int npixels; char **s_colors; int *perc; int nsegs, i, sum; Bool is_syntax_error = False; /* get the number of colors specified */ if (rest) { *rest = gradient; } orig = gradient; if (GetIntegerArguments(gradient, &gradient, (int *)&npixels, 1) != 1 || npixels < 2) { fprintf( stderr, "ParseGradient: illegal number of colors in" " gradient: '%s'\n", orig); return 0; } /* get the starting color or number of segments */ gradient = GetNextToken(gradient, &item); if (gradient) { gradient = SkipSpaces(gradient, NULL, 0); } if (!gradient || !*gradient || !item) { fprintf(stderr, "Incomplete gradient style: '%s'\n", orig); if (item) { free(item); } if (rest) { *rest = gradient; } return 0; } if (GetIntegerArguments(item, NULL, &nsegs, 1) != 1) { /* get the end color of a simple gradient */ s_colors = (char **)safemalloc(sizeof(char *) * 2); perc = (int *)safemalloc(sizeof(int)); nsegs = 1; s_colors[0] = item; gradient = GetNextToken(gradient, &item); s_colors[1] = item; perc[0] = 100; } else { free(item); /* get a list of colors and percentages */ if (nsegs < 1) nsegs = 1; if (nsegs > MAX_GRADIENT_SEGMENTS) nsegs = MAX_GRADIENT_SEGMENTS; s_colors = (char **)safemalloc(sizeof(char *) * (nsegs + 1)); perc = (int *)safemalloc(sizeof(int) * nsegs); for (i = 0; !is_syntax_error && i <= nsegs; i++) { s_colors[i] = 0; gradient = GetNextToken(gradient, &s_colors[i]); if (i < nsegs) { if (GetIntegerArguments( gradient, &gradient, &perc[i], 1) != 1 || perc[i] <= 0) { /* illegal size */ perc[i] = 0; } } } if (s_colors[nsegs] == NULL) { fprintf( stderr, "ParseGradient: too few gradient" " segments: '%s'\n", orig); is_syntax_error = True; } } /* sanity check */ for (i = 0, sum = 0; !is_syntax_error && i < nsegs; ++i) { int old_sum = sum; sum += perc[i]; if (sum < old_sum) { /* integer overflow */ fprintf( stderr, "ParseGradient: multi gradient" " overflow: '%s'", orig); is_syntax_error = 1; break; } } if (is_syntax_error) { for (i = 0; i <= nsegs; ++i) { if (s_colors[i]) { free(s_colors[i]); } } free(s_colors); free(perc); if (rest) { *rest = gradient; } return 0; } /* sensible limits */ if (npixels < 2) npixels = 2; if (npixels > MAX_GRADIENT_COLORS) npixels = MAX_GRADIENT_COLORS; /* send data back */ *colors_return = s_colors; *perc_return = perc; *nsegs_return = nsegs; if (rest) *rest = gradient; return npixels; } /* Calculate the prefered dimensions of a gradient, based on the number of * colors and the gradient type. Returns False if the gradient type is not * supported. */ Bool CalculateGradientDimensions( Display *dpy, Drawable d, int ncolors, char type, int dither, int *width_ret, int *height_ret) { static int best_width = 0, best_height = 0; int dither_factor = (dither > 0)? 128:1; /* get the best tile size (once) */ if (!best_width) { if (!XQueryBestTile( dpy, d, 1, 1, (unsigned int*)&best_width, (unsigned int*)&best_height)) { best_width = 0; best_height = 0; } /* this is needed for buggy X servers like XFree 3.3.3.1 */ if (!best_width) best_width = 1; if (!best_height) best_height = 1; } switch (type) { case H_GRADIENT: *width_ret = ncolors; *height_ret = best_height * dither_factor; break; case V_GRADIENT: *width_ret = best_width * dither_factor; *height_ret = ncolors; break; case D_GRADIENT: case B_GRADIENT: /* diagonal gradients are rendered into a rectangle for which * the width plus the height is equal to ncolors + 1. The * rectangle is square when ncolors is odd and one pixel * taller than wide with even numbers */ *width_ret = (ncolors + 1) / 2; *height_ret = ncolors + 1 - *width_ret; break; case S_GRADIENT: /* square gradients have the last color as a single pixel in * the centre */ *width_ret = *height_ret = 2 * ncolors - 1; break; case C_GRADIENT: /* circular gradients have the first color as a pixel in each * corner */ *width_ret = *height_ret = 2 * ncolors - 1; break; case R_GRADIENT: case Y_GRADIENT: /* swept types need each color to occupy at least one pixel at * the edge. Get the smallest odd number that will provide * enough */ for (*width_ret = 1; (double)(*width_ret - 1) * M_PI < (double)ncolors; *width_ret += 2) { /* nothing to do here */ } *height_ret = *width_ret; break; default: fprintf(stderr, "%cGradient not supported\n", type); return False; } return True; } /* Does the actual drawing of the pixmap. If the in_drawable argument is None, * a new pixmap of the given depth, width and height is created. If it is not * None the gradient is drawn into it. The d_width, d_height, d_x and d_y * describe the traget rectangle within the drawable. */ Drawable CreateGradientPixmap( Display *dpy, Drawable d, GC gc, int type, int g_width, int g_height, int ncolors, XColor *xcs, int dither, Pixel **d_pixels, int *d_npixels, Drawable in_drawable, int d_x, int d_y, int d_width, int d_height, XRectangle *rclip) { Pixmap pixmap = None; PictureImageColorAllocator *pica = NULL; XColor c; FImage *fim; register int i, j; XGCValues xgcv; Drawable target; int t_x; int t_y; int t_width; int t_height; int ps; if (d_pixels != NULL && *d_pixels != NULL) { if (d_npixels != NULL && *d_npixels > 0) { PictureFreeColors( dpy, Pcmap, *d_pixels, *d_npixels, 0, False); } free(*d_pixels); *d_pixels = NULL; } if (d_npixels != NULL) { *d_npixels = 0; } if (g_height < 0 || g_width < 0 || d_width < 0 || d_height < 0) return None; if (in_drawable == None) { /* create a pixmap to use */ pixmap = XCreatePixmap(dpy, d, g_width, g_height, Pdepth); if (pixmap == None) return None; target = pixmap; t_x = 0; t_y = 0; t_width = g_width; t_height = g_height; } else { target = in_drawable; t_x = d_x; t_y = d_y; t_width = d_width; t_height = d_height; } fim = FCreateFImage( dpy, Pvisual, Pdepth, ZPixmap, t_width, t_height); if (!fim) { fprintf(stderr, "%cGradient couldn't get image\n", type); if (pixmap != None) XFreePixmap(dpy, pixmap); return None; } if (dither) { pica = PictureOpenImageColorAllocator( dpy, Pcmap, t_width, t_height, False, False, dither, False); } ps = t_width * t_height; /* now do the fancy drawing */ switch (type) { case H_GRADIENT: { for (i = 0; i < t_width; i++) { int d = i * ncolors / t_width; c = xcs[d]; for (j = 0; j < t_height; j++) { if (dither) { c = xcs[d]; PictureAllocColorImage( dpy, pica, &c, i, j); } XPutPixel(fim->im, i, j, c.pixel); } } } break; case V_GRADIENT: { for (j = 0; j < t_height; j++) { int d = j * ncolors / t_height; c = xcs[d]; for (i = 0; i < t_width; i++) { if (dither) { c = xcs[d]; PictureAllocColorImage( dpy, pica, &c, i, j); } XPutPixel(fim->im, i, j, c.pixel); } } break; } case D_GRADIENT: { register int t_scale = t_width + t_height - 1; for (i = 0; i < t_width; i++) { for (j = 0; j < t_height; j++) { c = xcs[(i+j) * ncolors / t_scale]; if (dither) { PictureAllocColorImage( dpy, pica, &c, i, j); } XPutPixel(fim->im, i, j, c.pixel); } } break; } case B_GRADIENT: { register int t_scale = t_width + t_height - 1; for (i = 0; i < t_width; i++) { for (j = 0; j < t_height; j++) { c = xcs[(i + (t_height - j - 1)) * ncolors / t_scale]; if (dither) { PictureAllocColorImage( dpy, pica, &c, i, j); } XPutPixel(fim->im, i, j, c.pixel); } } break; } case S_GRADIENT: { register int t_scale = t_width * t_height; register int myncolors = ncolors * 2; for (i = 0; i < t_width; i++) { register int pi = min(i, t_width - 1 - i) * t_height; for (j = 0; j < t_height; j++) { register int pj = min(j, t_height - 1 - j) * t_width; c = xcs[(min(pi, pj) * myncolors - 1) / t_scale]; if (dither) { PictureAllocColorImage( dpy, pica, &c, i, j); } XPutPixel(fim->im, i, j, c.pixel); } } } break; case C_GRADIENT: { register double t_scale = (double)(t_width * t_height) / sqrt(8); for (i = 0; i < t_width; i++) { for (j = 0; j < t_height; j++) { register double x = (double)((2 * i - t_width) * t_height) / 4.0; register double y = (double)((t_height - 2 * j) * t_width) / 4.0; register double rad = sqrt(x * x + y * y); c = xcs[(int)((rad * ncolors - 0.5) / t_scale)]; if (dither) { PictureAllocColorImage( dpy, pica, &c, i, j); } XPutPixel(fim->im, i, j, c.pixel); } } break; } case R_GRADIENT: { register int w = t_width - 1; register int h = t_height - 1; /* g_width == g_height, both are odd, therefore x can be 0.0 */ for (i = 0; i <= w; i++) { for (j = 0; j <= h; j++) { register double x = (double)((2 * i - w) * h) / 4.0; register double y = (double)((h - 2 * j) * w) / 4.0; /* angle ranges from -pi/2 to +pi/2 */ register double angle; if (x != 0.0) { angle = atan(y / x); } else { angle = (y < 0) ? - M_PI_2 : M_PI_2; } /* extend to -pi/2 to 3pi/2 */ if (x < 0) angle += M_PI; /* move range from -pi/2:3*pi/2 to 0:2*pi */ if (angle < 0.0) angle += M_PI * 2.0; /* normalize to gradient */ c = xcs[(int)(angle * M_1_PI * 0.5 * ncolors)]; if (dither) { PictureAllocColorImage( dpy, pica, &c, i, j); } XPutPixel(fim->im, i, j, c.pixel); } } } break; /* * The Yin Yang gradient style and the following code are: * Copyright 1999 Sir Boris. (email to sir_boris@bigfoot.com may be * read by his groom but is not guaranteed to elicit a response) * No restrictions are placed on this code, as long as the copyright * notice is preserved. */ case Y_GRADIENT: { register int r = t_width * t_height / 4; for (i = 0; i < t_width; i++) { for (j = 0; j < t_height; j++) { register double x = (double)((2 * i - t_width) * t_height) / 4.0; register double y = (double)((t_height - 2 * j) * t_width) / 4.0; register double rad = sqrt(x * x + y * y); /* angle ranges from -pi/2 to +pi/2 */ register double angle; if (x != 0.0) { angle = atan(y / x); } else { angle = (y < 0) ? - M_PI_2 : M_PI_2; } /* extend to -pi/2 to 3pi/2 */ if (x < 0) angle += M_PI; /* warp the angle within the yinyang circle */ if (rad <= r) { angle -= acos(rad / r); } /* move range from -pi/2:3*pi/2 to 0:2*pi */ if (angle < 0.0) angle += M_PI * 2.0; /* normalize to gradient */ c = xcs[(int)(angle * M_1_PI * 0.5 * ncolors)]; if (dither) { PictureAllocColorImage( dpy, pica, &c, i, j); } XPutPixel(fim->im, i, j, c.pixel); } } } break; default: /* placeholder function, just fills the pixmap with the first * color */ memset(fim->im->data, 0, fim->im->bytes_per_line * t_height); XAddPixel(fim->im, xcs[0].pixel); break; } if (dither) { if (d_pixels != NULL && d_npixels != NULL) { PictureCloseImageColorAllocator( dpy, pica, d_npixels, d_pixels, 0); } else { /* possible color leak */ } } /* set the gc style */ xgcv.function = GXcopy; xgcv.plane_mask = AllPlanes; xgcv.fill_style = FillSolid; xgcv.clip_mask = None; XChangeGC(dpy, gc, GCFunction|GCPlaneMask|GCFillStyle|GCClipMask, &xgcv); if (rclip) { XSetClipRectangles(dpy, gc, 0, 0, rclip, 1, Unsorted); } /* copy the image to the server */ FPutFImage(dpy, target, gc, fim, 0, 0, t_x, t_y, t_width, t_height); if (rclip) { XSetClipMask(dpy, gc, None); } FDestroyFImage(dpy, fim); return target; } /* Create a pixmap from a gradient specifier, width and height are hints * that are only used for gradients that can be tiled e.g. H or V types * types are HVDBSCRY for Horizontal, Vertical, Diagonal, Back-diagonal, Square, * Circular, Radar and Yin/Yang respectively (in order of bloatiness) */ Pixmap CreateGradientPixmapFromString( Display *dpy, Drawable d, GC gc, int type, char *action, int *width_return, int *height_return, Pixel **pixels_return, int *nalloc_pixels, int dither) { Pixel *d_pixels = NULL; int d_npixels = 0; XColor *xcs = NULL; int ncolors = 0; char **colors; int *perc, nsegs; Pixmap pixmap = None; /* set return pixels to NULL in case of premature return */ if (pixels_return) *pixels_return = NULL; if (nalloc_pixels) *nalloc_pixels = 0; /* translate the gradient string into an array of colors etc */ if (!(ncolors = ParseGradient(action, NULL, &colors, &perc, &nsegs))) { fprintf(stderr, "Can't parse gradient: '%s'\n", action); return None; } /* grab the colors */ xcs = AllocAllGradientColors( colors, perc, nsegs, ncolors, dither); if (xcs == NULL) { return None; } /* grok the size to create from the type */ type = toupper(type); if (CalculateGradientDimensions( dpy, d, ncolors, type, dither, width_return, height_return)) { pixmap = CreateGradientPixmap( dpy, d, gc, type, *width_return, *height_return, ncolors, xcs, dither, &d_pixels, &d_npixels, None, 0, 0, 0, 0, NULL); } /* if the caller has not asked for the pixels there is probably a leak */ if (PUseDynamicColors) { if (!(pixels_return && nalloc_pixels)) { /* if the caller has not asked for the pixels there is * probably a leak */ fprintf(stderr, "CreateGradient: potential color leak, losing track" " of pixels\n"); if (d_pixels != NULL) { free(d_pixels); } } else { if (!dither) { Pixel *pixels; int i; pixels = (Pixel *)safemalloc( ncolors * sizeof(Pixel)); for(i=0; igraphics_exposures; if (!(valuemask & GCGraphicsExposures)) { valuemask |= GCGraphicsExposures; values->graphics_exposures = 0; } gc = XCreateGC(display, drawable, valuemask, values); values->graphics_exposures = f; return gc; }