/* +-------------------------------------------------------------------+ */ /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com) | */ /* | Copyright 1995, 1996 Torsten Martinsen (bullestock@dk-online.dk) | */ /* | | */ /* | Permission to use, copy, modify, and to distribute this software | */ /* | and its documentation for any purpose is hereby granted without | */ /* | fee, provided that the above copyright notice appear in all | */ /* | copies and that both that copyright notice and this permission | */ /* | notice appear in supporting documentation. There is no | */ /* | representations about the suitability of this software for | */ /* | any purpose. this software is provided "as is" without express | */ /* | or implied warranty. | */ /* | | */ /* +-------------------------------------------------------------------+ */ /* $Id: brushOp.c,v 1.17 2005/03/20 20:15:32 demailly Exp $ */ #include #include #include #include #include #include "xaw_incdir/Box.h" #include "xaw_incdir/Form.h" #include "xaw_incdir/Command.h" #include "xaw_incdir/Toggle.h" #include #include "xpaint.h" #include "misc.h" #include "Paint.h" #include "PaintP.h" #include "palette.h" #include "graphic.h" #include "protocol.h" #include "image.h" #include "ops.h" #include "rc.h" #include "bitmaps/brushes/paintA.xpm" #include "bitmaps/brushes/paintB.xpm" #include "bitmaps/brushes/paintC.xpm" #include "bitmaps/brushes/paintD.xpm" #include "bitmaps/brushes/paintE.xpm" #include "bitmaps/brushes/paintF.xpm" #include "bitmaps/brushes/paintG.xpm" #include "bitmaps/brushes/paintH.xpm" #include "bitmaps/brushes/paintI.xpm" #include "bitmaps/brushes/paintJ.xpm" #include "bitmaps/brushes/paintK.xpm" #include "bitmaps/brushes/paintL.xpm" #include "bitmaps/brushes/paintM.xpm" #include "bitmaps/brushes/paintN.xpm" #include "bitmaps/brushes/paintO.xpm" #include "bitmaps/brushes/paintP.xpm" #include "bitmaps/brushes/paintQ.xpm" #include "bitmaps/brushes/paintR.xpm" #include "bitmaps/brushes/paintS.xpm" #include "bitmaps/brushes/paintT.xpm" enum { OPAQUE, TRANSPARENT, STAIN }; #define BRUSH(name) (char **) CONCAT(name, _xpm) /* this blend function ensures to reach the final color - ACZ: */ #define BLEND(a, b, x) ((x)*(b) + (1.0039-(x))*(a)) static BrushItem baseBrushList[] = { {None, None, BRUSH(paintA)}, {None, None, BRUSH(paintB)}, {None, None, BRUSH(paintC)}, {None, None, BRUSH(paintD)}, {None, None, BRUSH(paintE)}, {None, None, BRUSH(paintF)}, {None, None, BRUSH(paintG)}, {None, None, BRUSH(paintH)}, {None, None, BRUSH(paintI)}, {None, None, BRUSH(paintJ)}, {None, None, BRUSH(paintK)}, {None, None, BRUSH(paintL)}, {None, None, BRUSH(paintM)}, {None, None, BRUSH(paintN)}, {None, None, BRUSH(paintO)}, {None, None, BRUSH(paintP)}, {None, None, BRUSH(paintQ)}, {None, None, BRUSH(paintR)}, {None, None, BRUSH(paintS)}, {None, None, BRUSH(paintT)} }; #define BBNUMBER XtNumber(baseBrushList) static XpmColorSymbol monoColorSymbols[5] = { {"A", NULL, 0}, {"B", NULL, 1}, {"C", NULL, 1}, {"D", NULL, 1}, {"E", NULL, 1} }; typedef enum { ERASE, SMEAR, PLAIN } BrushType; typedef struct { Widget form, box, close; } BrushboxInfo; typedef struct { int useSecond; BrushType brushtype; Boolean tracking; Pixmap pixmap; int width, height; int lastX, lastY; Palette *brushPalette; } LocalInfo; static BrushItem *currentBrush = NULL; static Boolean eraseMode = True; static int transparentMode = 0; static float brushOpacity = 0.2; static XImage *brushImage; /* RPC */ static void stain(Widget w, OpInfo * info, GC gc, LocalInfo * l, int sx, int sy); /* RPC */ static void smear(Widget w, OpInfo * info, GC gc, LocalInfo * l, int sx, int sy); static void wbrush(Widget w, OpInfo * info, GC gc, LocalInfo * l, int sx, int sy); static void draw(Widget w, OpInfo * info, LocalInfo * l, int x, int y) { XRectangle undo; int sx = x - l->width / 2; int sy = y - l->height / 2; GC gc; if (l->brushtype == ERASE) gc = info->base_gc; else gc = l->useSecond ? info->second_gc : info->first_gc; XSetClipOrigin(XtDisplay(w), gc, sx, sy); if ((l->brushtype == ERASE) && eraseMode && (info->base != None)) { XCopyArea(XtDisplay(w), info->base, info->drawable, gc, sx, sy, l->width, l->height, sx, sy); } else if (l->brushtype == SMEAR) { smear(w, info, gc, l, sx, sy); } else if (transparentMode==TRANSPARENT) { wbrush(w, info, gc, l, sx, sy); /* RPC */ } else if (transparentMode==STAIN) { stain(w, info, gc, l, sx, sy ); /* RPC */ } else { /* plain opaque brush */ XFillRectangle(XtDisplay(w), info->drawable, gc, sx, sy, l->width, l->height); } if (info->surface == opPixmap) { XYtoRECT(sx, sy, sx + l->width, sy + l->height, &undo); UndoGrow(w, sx, sy); UndoGrow(w, sx + l->width, sy + l->height); PwUpdate(w, &undo, False); } } static void press(Widget w, LocalInfo * l, XButtonEvent * event, OpInfo * info) { /* ** Check to make sure all buttons are up, before doing this */ if (event->button >= Button4) return; if ((event->state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) != 0) return; if (event->button == Button3) return; if (info->surface == opWindow && info->isFat) return; l->useSecond = (event->button == Button2); l->width = currentBrush->width; l->height = currentBrush->height; l->tracking = True; XSetClipMask(XtDisplay(w), info->first_gc, l->pixmap); XSetClipMask(XtDisplay(w), info->second_gc, l->pixmap); XSetClipMask(XtDisplay(w), info->base_gc, l->pixmap); UndoStart(w, info); draw(w, info, l, event->x, event->y); if (info->surface == opPixmap) { l->lastX = event->x; l->lastY = event->y; } } static void motion(Widget w, LocalInfo * l, XMotionEvent * event, OpInfo * info) { int x = l->lastX; int y = l->lastY; int d, dx, dy, x_incrE, y_incrE = 0, x_incrNE, y_incrNE, incrE, incrNE; if (!l->tracking) return; if (l->useSecond == -1) return; if (!event->state || ((info->surface == opWindow) && info->isFat)) return; x_incrE = x_incrNE = x < event->x ? 1 : (x == event->x ? 0 : -1); y_incrNE = y < event->y ? 1 : (y == event->y ? 0 : -1); dx = abs(event->x - x); dy = abs(event->y - y); if (dy > dx) { int t = dx; dx = dy; dy = t; x_incrE = 0; y_incrE = y_incrNE; } d = dy * 2 - dx; incrE = dy*2; incrNE = (dy - dx) * 2; do { if (d <= 0) { d += incrE; x += x_incrE; y += y_incrE; } else { d += incrNE; x += x_incrNE; y += y_incrNE; } draw(w, info, l, x, y); } while (x != event->x || y != event->y); if (info->surface == opPixmap) { l->lastX = event->x; l->lastY = event->y; } } static void release(Widget w, LocalInfo * l, XButtonEvent * event, OpInfo * info) { int mask; /* ** Check to make sure all buttons are up, before doing this */ if (event->button >= Button4) return; mask = AllButtonsMask; switch (event->button) { case Button1: mask ^= Button1Mask; break; case Button2: mask ^= Button2Mask; break; case Button3: mask ^= Button3Mask; break; case Button4: mask ^= Button4Mask; break; case Button5: mask ^= Button5Mask; break; } if ((event->state & mask) != 0) return; if (event->button == Button3) return; l->tracking = False; XSetClipMask(XtDisplay(w), info->first_gc, None); XSetClipMask(XtDisplay(w), info->second_gc, None); XSetClipMask(XtDisplay(w), info->base_gc, None); } /* RPC */ static void rgbTOhsv(float r, float g, float b, float *h, float *s, float *v) { float max = MAX(r, MAX(g, b)); float min = MIN(r, MIN(g, b)); float delta; *v = max; if (max != 0.0) *s = (max - min) / max; else *s = 0.0; if (*s == 0.0) { *h = 0.0; } else { delta = max - min; if (r == max) *h = (g - b) / delta; else if (g == max) *h = 2.0 + (b - r) / delta; else /* if (b == max) */ *h = 4.0 + (r - g) / delta; *h *= 60.0; if (*h < 0.0) *h += 360.0; } } static void hsvTOrgb(float h, float s, float v, float *r, float *g, float *b) { int i; float f, p, q, t; if (s == 0 && h == 0) { *r = *g = *b = v; } else { if (h >= 360.0) h = 0.0; h /= 60.0; i = h; f = h - i; p = v * (1 - s); q = v * (1 - (s * f)); t = v * (1 - (s * (1 - f))); switch (i) { case 0: *r = v; *g = t; *b = p; break; case 1: *r = q; *g = v; *b = p; break; case 2: *r = p; *g = v; *b = t; break; case 3: *r = p; *g = q; *b = v; break; case 4: *r = t; *g = p; *b = v; break; case 5: *r = v; *g = p; *b = q; break; } } } /* ** drawing routine for stain operator */ static void stain(Widget wid, OpInfo * info, GC gc, LocalInfo * l, int sx, int sy) { int x, y, dx, dy, w, h, d; unsigned char *brushbits; Display *dpy = XtDisplay(wid); XGCValues gcValues; XColor *brushCol, *col; Pixel p; float r, g, b, H, S, V; float brushH, brushS, brushV; /* * Perform manual clipping to avoid XPutImage crashing on us */ XGetGCValues( dpy, gc, GCForeground, &gcValues ); brushCol = PaletteLookup(l->brushPalette, gcValues.foreground); r = brushCol->red / 65535.0; g = brushCol->green / 65535.0; b = brushCol->blue / 65535.0; rgbTOhsv( r, g, b, &brushH, &brushS, &brushV ); dx = dy = 0; w = l->width; h = l->height; if (sx < 0) { dx = -sx; w += sx; sx = 0; } if ((d = (sx + w - ((PaintWidget) wid)->paint.drawWidth)) > 0) w -= d; if (sy < 0) { dy = -sy; h += sy; sy = 0; } if ((d = (sy + h - ((PaintWidget) wid)->paint.drawHeight)) > 0) h -= d; if ((w <= 0) || (h <= 0)) return; /* copy portion of image under brush into brushImage */ XGetSubImage(dpy, info->drawable, sx, sy, w, h, AllPlanes, ZPixmap, brushImage, dx, dy); brushbits = (unsigned char *) currentBrush->brushbits; for (y = 0; y < l->height; ++y) for (x = 0; x < l->width; ++x) if (*brushbits++) { p = XGetPixel(brushImage, x, y); col = PaletteLookup(l->brushPalette, p); r = col->red; g = col->green; b = col->blue; rgbTOhsv( r, g, b, &H, &S, &V ); H = brushH; S = brushS; hsvTOrgb( H, S, V, &r, &g, &b ); col->red = (unsigned short int) (r); col->green = (unsigned short int) (g); col->blue = (unsigned short int) (b); p = PaletteAlloc(l->brushPalette, col); XPutPixel(brushImage, x, y, p ); } XPutImage(dpy, info->drawable, gc, brushImage, dx, dy, sx, sy, w, h); } /* RPC */ /* ** drawing routine for smear operator */ static void smear(Widget wid, OpInfo * info, GC gc, LocalInfo * l, int sx, int sy) { int x, y, n, dx, dy, w, h, d, m; unsigned long r, g, b; unsigned char *brushbits; Pixel p; Display *dpy = XtDisplay(wid); XColor *col, newcol; /* * Perform manual clipping to avoid XPutImage crashing on us */ dx = dy = 0; w = l->width; h = l->height; if (sx < 0) { dx = -sx; w += sx; sx = 0; } if ((d = (sx + w - ((PaintWidget) wid)->paint.drawWidth)) > 0) w -= d; if (sy < 0) { dy = -sy; h += sy; sy = 0; } if ((d = (sy + h - ((PaintWidget) wid)->paint.drawHeight)) > 0) h -= d; if ((w <= 0) || (h <= 0)) return; /* copy portion of image under brush into brushImage */ XGetSubImage(dpy, info->drawable, sx, sy, w, h, AllPlanes, ZPixmap, brushImage, dx, dy); /* compute average of pixels inside brush */ r = g = b = 0; brushbits = (unsigned char *) currentBrush->brushbits; for (y = 0; y < l->height; ++y) for (x = 0; x < l->width; ++x) if (*brushbits++) { p = XGetPixel(brushImage, x, y); col = PaletteLookup(l->brushPalette, p); r += col->red; g += col->green; b += col->blue; } n = currentBrush->numpixels; r = r / 256 / n; g = g / 256 / n; b = b / 256 / n; /* now blend each surface pixel with average */ brushbits = (unsigned char *) currentBrush->brushbits; for (y = 0; y < l->height; ++y) for (x = 0; x < l->width; ++x) { if ((m = *brushbits++) != 0) { float mix = m / 5.0; p = XGetPixel(brushImage, x, y); col = PaletteLookup(l->brushPalette, p); newcol.red = 256 * BLEND(col->red / 256, r, mix); newcol.green = 256 * BLEND(col->green / 256, g, mix); newcol.blue = 256 * BLEND(col->blue / 256, b, mix); p = PaletteAlloc(l->brushPalette, &newcol); XPutPixel(brushImage, x, y, p); } } XPutImage(dpy, info->drawable, gc, brushImage, dx, dy, sx, sy, w, h); } /* ** drawing routine for transparent brush */ static void wbrush(Widget wid, OpInfo * info, GC gc, LocalInfo * l, int sx, int sy) { int x, y, dx, dy, m, w, h, d; unsigned long r, g, b; unsigned char *brushbits; Pixel p; Display *dpy = XtDisplay(wid); XColor *col, newcol; XGCValues gcval; /* * Perform manual clipping to avoid XPutImage crashing on us */ dx = dy = 0; w = l->width; h = l->height; if (sx < 0) { dx = -sx; w += sx; sx = 0; } if ((d = (sx + w - ((PaintWidget) wid)->paint.drawWidth)) > 0) w -= d; if (sy < 0) { dy = -sy; h += sy; sy = 0; } if ((d = (sy + h - ((PaintWidget) wid)->paint.drawHeight)) > 0) h -= d; if ((w <= 0) || (h <= 0)) return; /* copy portion of image under brush into brushImage */ XGetSubImage(dpy, info->drawable, sx, sy, w, h, AllPlanes, ZPixmap, brushImage, dx, dy); /* get current colour */ XGetGCValues(dpy, gc, GCForeground, &gcval); col = PaletteLookup(l->brushPalette, gcval.foreground); r = col->red / 256; g = col->green / 256; b = col->blue / 256; brushbits = (unsigned char *) currentBrush->brushbits; for (y = 0; y < l->height; ++y) for (x = 0; x < l->width; ++x) if ((m = *brushbits++) != 0) { float mix = m / 5.0 * brushOpacity; p = XGetPixel(brushImage, x, y); col = PaletteLookup(l->brushPalette, p); newcol.red = 256 * BLEND(col->red / 256, r, mix); newcol.green = 256 * BLEND(col->green / 256, g, mix); newcol.blue = 256 * BLEND(col->blue / 256, b, mix); p = PaletteAlloc(l->brushPalette, &newcol); XPutPixel(brushImage, x, y, p); } XPutImage(dpy, info->drawable, gc, brushImage, dx, dy, sx, sy, w, h); } static void setPixmap(Widget w, void *brushArg) { BrushItem *brush = (BrushItem *) brushArg; LocalInfo *l = (LocalInfo *) GraphicGetData(w); l->pixmap = brush->pixmap; } static void setCursor(Widget wid, void *brushArg) { static Boolean inited = False; static XColor xcols[2]; BrushItem *brush = (BrushItem *) brushArg; Display *dpy = XtDisplay(wid); PaintWidget paint = (PaintWidget) wid; if (!inited) { Colormap map; Screen *screen = XtScreen(wid); inited = True; xcols[0].pixel = WhitePixelOfScreen(screen); xcols[1].pixel = BlackPixelOfScreen(screen); XtVaGetValues(wid, XtNcolormap, &map, NULL); XQueryColors(dpy, map, xcols, XtNumber(xcols)); } if (brush->cursor == None) { Pixmap source, mask; XImage *src, *msk; GC gc; int x, y, w, h, ow, oh, n; unsigned char *brushbits; XpmAttributes xpmAttr; static XpmColorSymbol colorsymbols[5] = { {"A", NULL, 0}, {"B", NULL, 1}, {"C", NULL, 2}, {"D", NULL, 3}, {"E", NULL, 4} }; ow = brush->width; oh = brush->height; w = ow + 2; h = oh + 2; /* add 1 pixel border for mask */ /* get full depth pixmap */ xpmAttr.valuemask = XpmColorSymbols; xpmAttr.numsymbols = 5; xpmAttr.colorsymbols = colorsymbols; XpmCreatePixmapFromData(dpy, RootWindowOfScreen(XtScreen(wid)), brush->bits, &source, NULL, &xpmAttr); GetPixmapWHD(dpy, source, NULL, NULL, (int *) &xpmAttr.depth); src = NewXImage(dpy, NULL, xpmAttr.depth, w, h); memset(src->data, 0, src->bytes_per_line * h); /* copy colour pixmap to center of XImage */ XGetSubImage(dpy, source, 0, 0, ow, oh, AllPlanes, ZPixmap, src, 1, 1); msk = NewXImage(dpy, NULL, 1, w, h); brushbits = (unsigned char *) xmalloc(ow * oh); brush->brushbits = (char *) brushbits; n = 0; for (y = 0; y < h; y++) for (x = 0; x < w; x++) { Pixel p = XGetPixel(src, x, y); if ((y != 0) && (y != h - 1) && (x != 0) && (x != w - 1)) *brushbits++ = p; if (p) ++n; if (!p && x > 0) p = XGetPixel(src, x - 1, y); if (!p && x < w - 1) p = XGetPixel(src, x + 1, y); if (!p && y > 0) p = XGetPixel(src, x, y - 1); if (!p && y < h - 1) p = XGetPixel(src, x, y + 1); XPutPixel(msk, x, y, p ? 1 : 0); } XFreePixmap(dpy, source); XDestroyImage(src); brush->numpixels = n; source = XCreatePixmap(dpy, brush->pixmap, w, h, 1); mask = XCreatePixmap(dpy, brush->pixmap, w, h, 1); gc = XCreateGC(dpy, mask, 0, 0); XSetForeground(dpy, gc, 0); XFillRectangle(dpy, source, gc, 0, 0, w, h); XCopyArea(dpy, brush->pixmap, source, gc, 0, 0, ow, oh, 1, 1); XPutImage(dpy, mask, gc, msk, 0, 0, 0, 0, w, h); XDestroyImage(msk); XFreeGC(dpy, gc); brush->cursor = XCreatePixmapCursor(dpy, source, mask, &xcols[1], &xcols[0], w / 2, h / 2); XFreePixmap(dpy, source); XFreePixmap(dpy, mask); } FatCursorSet(wid, brush->pixmap); /* don't set cursor to brush shape if zoom is larger than 1 */ if (paint->paint.zoom <= 1) XtVaSetValues(wid, XtNcursor, brush->cursor, NULL); else { SetCrossHairCursor(wid); FatCursorAddZoom(paint->paint.zoom, wid); XtAddCallback(wid, XtNdestroyCallback, FatCursorDestroyCallback, wid); } } /* ** Those public functions */ Boolean EraseGetMode(void) { return eraseMode; } void EraseSetMode(Boolean mode) { eraseMode = mode; } void BrushSetMode(int mode) { transparentMode = mode; } void BrushSetParameters(float opacity) { brushOpacity = opacity; } void * BrushAdd(Widget w) { static LocalInfo *l; Colormap cmap; l = XtNew(LocalInfo); XtVaSetValues(w, XtNcompress, False, NULL); l->brushtype = PLAIN; l->pixmap = currentBrush->pixmap; XtVaGetValues(w, XtNcolormap, &cmap, NULL); l->brushPalette = PaletteFind(w, cmap); l->useSecond = -1; l->tracking = False; OpAddEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE, (OpEventProc) press, (XtPointer) l); OpAddEventHandler(w, opWindow | opPixmap, PointerMotionMask, FALSE, (OpEventProc) motion, (XtPointer) l); OpAddEventHandler(w, opWindow | opPixmap, ButtonReleaseMask, FALSE, (OpEventProc) release, (XtPointer) l); setCursor(w, (void *) currentBrush); return l; } void BrushRemove(Widget w, void *l) { OpRemoveEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE, (OpEventProc) press, (XtPointer) l); OpRemoveEventHandler(w, opWindow | opPixmap, PointerMotionMask, FALSE, (OpEventProc) motion, (XtPointer) l); OpRemoveEventHandler(w, opWindow | opPixmap, ButtonReleaseMask, FALSE, (OpEventProc) release, (XtPointer) l); XtFree((XtPointer) l); FatCursorOff(w); } void * EraseAdd(Widget w) { LocalInfo *l = XtNew(LocalInfo); XtVaSetValues(w, XtNcompress, False, NULL); l->brushtype = ERASE; l->useSecond = -1; l->pixmap = currentBrush->pixmap; l->tracking = False; OpAddEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE, (OpEventProc) press, (XtPointer) l); OpAddEventHandler(w, opWindow | opPixmap, PointerMotionMask, FALSE, (OpEventProc) motion, (XtPointer) l); OpAddEventHandler(w, opWindow | opPixmap, ButtonReleaseMask, FALSE, (OpEventProc) release, (XtPointer) l); setCursor(w, (void *) currentBrush); return l; } void EraseRemove(Widget w, void *l) { OpRemoveEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE, (OpEventProc) press, (XtPointer) l); OpRemoveEventHandler(w, opWindow | opPixmap, PointerMotionMask, FALSE, (OpEventProc) motion, (XtPointer) l); OpRemoveEventHandler(w, opWindow | opPixmap, ButtonReleaseMask, FALSE, (OpEventProc) release, (XtPointer) l); XtFree((XtPointer) l); FatCursorOff(w); } void * SmearAdd(Widget w) { LocalInfo *l = XtNew(LocalInfo); Colormap cmap; XtVaSetValues(w, XtNcompress, False, NULL); l->brushtype = SMEAR; l->pixmap = currentBrush->pixmap; l->useSecond = -1; l->tracking = False; XtVaGetValues(w, XtNcolormap, &cmap, NULL); l->brushPalette = PaletteFind(w, cmap); OpAddEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE, (OpEventProc) press, (XtPointer) l); OpAddEventHandler(w, opWindow | opPixmap, PointerMotionMask, FALSE, (OpEventProc) motion, (XtPointer) l); OpAddEventHandler(w, opWindow | opPixmap, ButtonReleaseMask, FALSE, (OpEventProc) release, (XtPointer) l); setCursor(w, (void *) currentBrush); return l; } void SmearRemove(Widget w, void *l) { OpRemoveEventHandler(w, opWindow | opPixmap, ButtonPressMask, FALSE, (OpEventProc) press, (XtPointer) l); OpRemoveEventHandler(w, opWindow | opPixmap, PointerMotionMask, FALSE, (OpEventProc) motion, (XtPointer) l); OpRemoveEventHandler(w, opWindow | opPixmap, ButtonReleaseMask, FALSE, (OpEventProc) release, (XtPointer) l); XtFree((XtPointer) l); FatCursorOff(w); } /* ** Initializer to create a default brush */ void BrushInit(Widget toplevel) { XpmAttributes xpmAttr; currentBrush = &baseBrushList[0]; /* force depth of one */ xpmAttr.depth = 1; xpmAttr.colorsymbols = monoColorSymbols; xpmAttr.numsymbols = 5; xpmAttr.valuemask = XpmDepth | XpmColorSymbols; XpmCreatePixmapFromData(XtDisplay(toplevel), RootWindowOfScreen(XtScreen(toplevel)), currentBrush->bits, ¤tBrush->pixmap, NULL, &xpmAttr); currentBrush->width = xpmAttr.width; currentBrush->height = xpmAttr.height; brushImage = NewXImage(XtDisplay(toplevel), NULL, DefaultDepthOfScreen(XtScreen(toplevel)), currentBrush->width, currentBrush->height); } /* ** The brush selection dialog */ static void closePopup(Widget button, Widget shell) { XtPopdown(shell); } void selectBrush(Widget shell, BrushItem * nc) { currentBrush = nc; if ((CurrentOp->add == BrushAdd) || (CurrentOp->add == EraseAdd) || (CurrentOp->add == SmearAdd)) { GraphicAll(setCursor, (void *) currentBrush); GraphicAll(setPixmap, (void *) currentBrush); } GraphicAll(setBrushIconPixmap, (void *) currentBrush); if (brushImage != NULL) XDestroyImage(brushImage); brushImage = NewXImage(XtDisplay(shell), NULL, DefaultDepthOfScreen(XtScreen(shell)), currentBrush->width, currentBrush->height); } static void brushboxResized(Widget w, BrushboxInfo * l, XConfigureEvent * event, Boolean * flg) { Dimension width, height; XtVaGetValues(w, XtNwidth, &width, XtNheight, &height, NULL); if (width<10) width = 10; if (height<35) height = 35; XtResizeWidget(l->box, width-6, height-32, #ifdef XAW3D 1 #else 0 #endif ); XtUnmanageChild(l->close); XMapWindow(XtDisplay(l->close), XtWindow(l->close)); XtMoveWidget(l->close, 4, height-25); } static Widget createBrushDialog(Widget w) { Widget shell, icon, firstIcon = 0; static BrushboxInfo *info = NULL; GC gc; XGCValues values; RCInfo *rcInfo; static BrushItem * brushList = NULL; static int brushnum = BBNUMBER; int i, j; Pixel fg, bg; Pixmap pix; int nw, nh, ox, oy; XpmAttributes xpmAttr; Arg args[4]; int nargs = 0; if (!info) info = (BrushboxInfo *)XtMalloc(sizeof(BrushboxInfo)); shell = XtVisCreatePopupShell("brush", topLevelShellWidgetClass, w, args, nargs); info->form = XtVaCreateManagedWidget(NULL, formWidgetClass, shell, NULL); info->box = XtVaCreateManagedWidget("box", boxWidgetClass, info->form, NULL); values.foreground = WhitePixelOfScreen(XtScreen(w)); values.background = BlackPixelOfScreen(XtScreen(w)); gc = XCreateGC(XtDisplay(w), RootWindowOfScreen(XtScreen(w)), GCForeground | GCBackground, &values); values.background = WhitePixelOfScreen(XtScreen(w)); values.foreground = BlackPixelOfScreen(XtScreen(w)); rcInfo = ReadDefaultRC(); if (rcInfo->nbrushes || Global.nbrushes) { /* There are brushes specified from the RC file, */ /* or the user defined brushes with the Pattern Editor */ brushList = (BrushItem *) realloc(brushList, (BBNUMBER+rcInfo->nbrushes+Global.nbrushes)*sizeof(BrushItem)); for (i = 0; i < BBNUMBER; i++) brushList[i] = baseBrushList[i]; for (i=brushnum; inbrushes+Global.nbrushes; i++) bzero(&brushList[i++], sizeof(BrushItem)); brushnum = BBNUMBER+rcInfo->nbrushes+Global.nbrushes; } if (!brushList) brushList = baseBrushList; for (i = 0; i < BBNUMBER+rcInfo->nbrushes+Global.nbrushes; i++) { if (i >= BBNUMBER && brushList[i].pixmap == None) { /* Create a brush specified from RC file */ Display *dpy = XtDisplay(w); int u, v, k, scale; GC gc0, gc1; Image * brush; char *ptr; XGCValues values1; values1.foreground = values.background; values1.background = values.foreground; j = i - BBNUMBER; if (j < rcInfo->nbrushes) brush = rcInfo->brushes[j]; else brush = (Image *) (Global.brushes[j-rcInfo->nbrushes]); brushList[i].width = brush->width; brushList[i].height = brush->height; brushList[i].pixmap = XCreatePixmap(dpy, RootWindowOfScreen(XtScreen(info->box)), brushList[i].width, brushList[i].height, 1); scale = brush->scale; ptr = (char*) brush->data; gc0 = XCreateGC(dpy, brushList[i].pixmap, GCForeground | GCBackground, &values); gc1 = XCreateGC(dpy, brushList[i].pixmap, GCForeground | GCBackground, &values1); for (v = 0; v < brushList[i].height; v++) for (u = 0; u < brushList[i].width; u++) { k = 0; while (kbox), RootWindowOfScreen(XtScreen(info->box)), brushList[i].bits, &brushList[i].pixmap, NULL, &xpmAttr)) continue; brushList[i].width = xpmAttr.width; brushList[i].height = xpmAttr.height; } icon = XtVaCreateManagedWidget("icon", toggleWidgetClass, info->box, XtNradioGroup, firstIcon, #ifdef XAW3D XtNforeground, 0xc0c0c0, #endif NULL); nw = brushList[i].width; nh = brushList[i].height; ox = oy = 0; if (nw < 16) { ox = (16 - nw) / 2; nw = 16; } if (nh < 16) { oy = (16 - nh) / 2; nh = 16; } pix = XCreatePixmap(XtDisplay(info->box), RootWindowOfScreen(XtScreen(info->box)), nw, nh, DefaultDepthOfScreen(XtScreen(info->box))); XtVaGetValues(icon, XtNbackground, &bg, NULL); fg = XtScreen(w)->black_pixel; /* ** Clear then draw the clipped rectangle in */ XSetClipMask(XtDisplay(w), gc, None); XSetForeground(XtDisplay(w), gc, bg); XFillRectangle(XtDisplay(w), pix, gc, 0, 0, nw, nh); XSetClipMask(XtDisplay(w), gc, brushList[i].pixmap); XSetClipOrigin(XtDisplay(w), gc, ox, oy); XSetForeground(XtDisplay(w), gc, fg); XFillRectangle(XtDisplay(w), pix, gc, 0, 0, nw, nh); XtVaSetValues(icon, XtNbitmap, pix, NULL); if (firstIcon == NULL) { XtVaSetValues(icon, XtNstate, True, NULL); firstIcon = icon; } XtAddCallback(icon, XtNcallback, (XtCallbackProc) selectBrush, (XtPointer) & brushList[i]); } info->close = XtVaCreateManagedWidget("close", commandWidgetClass, info->form, XtNfromVert, info->box, XtNtop, XtChainBottom, NULL); XtAddCallback(info->close, XtNcallback, (XtCallbackProc) closePopup, (XtPointer) shell); XFreeGC(XtDisplay(w), gc); AddDestroyCallback(shell, (DestroyCallbackFunc) closePopup, shell); XtAddEventHandler(shell, StructureNotifyMask, False, (XtEventHandler) brushboxResized, (XtPointer) info); return shell; } void BrushSelect(Widget w) { if (Global.brushpopup == NULL) Global.brushpopup = createBrushDialog(GetToplevel(w)); XtPopup(Global.brushpopup, XtGrabNone); XMapRaised(XtDisplay(Global.brushpopup), XtWindow(Global.brushpopup)); } void setBrushIconOnWidget(Widget w) { if (w != None) XtVaSetValues(w, XtNbitmap, currentBrush->pixmap, NULL); }