1 /* +-------------------------------------------------------------------+ */
2 /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)            | */
3 /* | Copyright 1995, 1996 Torsten Martinsen (bullestock@dk-online.dk)  | */
4 /* |                                                                   | */
5 /* | Permission to use, copy, modify, and to distribute this software  | */
6 /* | and its documentation for any purpose is hereby granted without   | */
7 /* | fee, provided that the above copyright notice appear in all       | */
8 /* | copies and that both that copyright notice and this permission    | */
9 /* | notice appear in supporting documentation.  There is no           | */
10 /* | representations about the suitability of this software for        | */
11 /* | any purpose.  this software is provided "as is" without express   | */
12 /* | or implied warranty.                                              | */
13 /* |                                                                   | */
14 /* +-------------------------------------------------------------------+ */
15 
16 /* Portions copyright 1995, 1995 Torsten Martinsen */
17 
18 /* $Id: PaintRegion.c,v 1.17 2005/03/20 20:15:32 demailly Exp $ */
19 
20 /*
21 ** PaintRegion.c -- Hopefully all of the routines to get, set and
22 **   manipulate the selection region.
23 **
24 **  Not part of the "selection" operation, since this really
25 **   need to know lots of hidden information (why?)
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <math.h>
31 #ifdef __EMX__
32 #include <float.h>
33 #endif
34 #include <X11/IntrinsicP.h>
35 #include <X11/StringDefs.h>
36 #include <X11/cursorfont.h>
37 #include <X11/Xatom.h>
38 
39 #include "xaw_incdir/Grip.h"
40 
41 #include "xpaint.h"
42 #include "PaintP.h"
43 #include "image.h"
44 #include "protocol.h"
45 
46 #define SHAPE
47 
48 #ifdef SHAPE
49 #include <X11/extensions/shape.h>
50 #endif
51 
52 extern void motionExtern(Widget, XEvent *, int x, int y, int flag);
53 
54 #define regionRedraw(pw)  PwRegionExpose(pw->paint.region.child, pw, NULL, NULL)
55 
56 #define BoolStr(flg)	((flg) ? "True" : "False")
57 
58 #undef INTERACTIVE
59 
60 /*
61 **  Border Width of child widget
62  */
63 #define BW	0
64 
65 /*
66 **  2x2 matrix stuff
67 **
68  */
69 
70 #define XFORM(x,y,mat,nx,ny)	nx = mat[0][0] * x + mat[0][1] * y; \
71 				ny = mat[1][0] * x + mat[1][1] * y
72 #define COPY_MAT(s,d)	d[0][0] = s[0][0]; d[0][1] = s[0][1]; \
73 			d[1][0] = s[1][0]; d[1][1] = s[1][1]
74 
75 #define INVERT_MAT(mat, inv) {			        \
76 		double _d = 1.0 / (mat[0][0] * mat[1][1] \
77 			      - mat[0][1] * mat[1][0]);	\
78 		(inv)[0][0] =  (mat)[1][1] * _d;	\
79 		(inv)[1][1] =  (mat)[0][0] * _d;	\
80 		(inv)[0][1] = -(mat)[0][1] * _d;	\
81 		(inv)[1][0] = -(mat)[1][0] * _d;	\
82 	}
83 
84 #define ZERO(v)		(((v) > -1e-5) && ((v) < 1e-5))
85 
86 #ifndef MIN
87 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
88 #endif
89 #ifndef MAX
90 #define MAX(a,b)        (((a) > (b)) ? (a) : (b))
91 #endif
92 #ifndef SIGN
93 #define SIGN(a)		(((a) < 0) ? -1 : 1)
94 #endif
95 #ifndef ABS
96 #define ABS(a)          ((a > 0) ? (a) : 0 - (a))
97 #endif
98 
99 #define MKMAT(pw) do {							\
100 		pwMatrix	m;					\
101 		m[0][0] = pw->paint.region.scaleX;			\
102 		m[1][1] = pw->paint.region.scaleY;			\
103 		m[0][1] = m[1][0] = 0.0;				\
104 		mm(pw->paint.region.rotMat, m, pw->paint.region.mat);	\
105 	} while (0)
106 
107 extern Pixmap lastpix;
108 
109 static pwMatrix matIdentity =
110 {
111     {1, 0},
112     {0, 1}
113 };
114 
115 int alpha_special_mode = 0;
116 
117 void
XXsync(Display * dpy,Boolean flag)118 XXsync(Display *dpy, Boolean flag)
119 {
120 }
121 
122 static void
doCallbacks(PaintWidget pw,int flag)123 doCallbacks(PaintWidget pw, int flag)
124 {
125     PaintWidget tpw = (pw->paint.paint)? (PaintWidget) pw->paint.paint : pw;
126     int i;
127 
128     XtCallCallbackList((Widget) tpw, tpw->paint.regionCalls,
129                        (XtPointer)(long)flag);
130     for (i = 0; i < tpw->paint.paintChildrenSize; i++) {
131 	PaintWidget p = (PaintWidget) tpw->paint.paintChildren[i];
132 	XtCallCallbackList((Widget) p, p->paint.regionCalls,
133                            (XtPointer)(long)flag);
134     }
135 }
136 
137 /*
138  * Multiply matrices a and b and store result in r.
139  */
140 static void
mm(pwMatrix a,pwMatrix b,pwMatrix r)141 mm(pwMatrix a, pwMatrix b, pwMatrix r)
142 {
143     double t00, t01, t10, t11;
144     t00 = a[0][0] * b[0][0] + a[0][1] * b[1][0];
145     t01 = a[0][0] * b[0][1] + a[0][1] * b[1][1];
146     t10 = a[1][0] * b[0][0] + a[1][1] * b[1][0];
147     t11 = a[1][0] * b[0][1] + a[1][1] * b[1][1];
148     r[0][0] = t00;
149     r[0][1] = t01;
150     r[1][0] = t10;
151     r[1][1] = t11;
152 }
153 
154 /*
155 **   PwRegionSet -- set the active image region
156 **     add handles and other useful things
157 **     if the pix == None, use the current paint info in rect
158 **     else use the pixmap.
159 **
160  */
161 static void
buildSources(PaintWidget pw)162 buildSources(PaintWidget pw)
163 {
164     if (pw->paint.region.source == None) return;
165 
166     if (pw->paint.region.sourceImg == NULL) {
167 	pw->paint.region.sourceImg = XGetImage(XtDisplay(pw),
168 		                               pw->paint.region.source, 0, 0,
169 					       pw->paint.region.orig.width,
170 		                               pw->paint.region.orig.height,
171                                                AllPlanes, ZPixmap);
172     }
173 
174     if (pw->paint.region.mask == None)
175 	return;
176 
177     if (pw->paint.region.maskImg == NULL) {
178 	pw->paint.region.maskImg = XGetImage(XtDisplay(pw),
179 					     pw->paint.region.mask, 0, 0,
180 					     pw->paint.region.orig.width,
181 		       pw->paint.region.orig.height, AllPlanes, ZPixmap);
182     }
183 }
184 
185 void
RegionTransparency(PaintWidget pw)186 RegionTransparency(PaintWidget pw)
187 {
188     PaintWidget tpw;
189     Widget w;
190     XImage *src1, *src2, *src3 = NULL;
191     Pixel pix;
192     GC gc;
193     int rwidth, rheight, widthp, heightp, zoom, skip;
194     int px, py, rx, ry, x, y, i, j, bytes_per_pixel, zoom_bytes_per_pixel;
195     char bg[4];
196     int opaque, transparent;
197 
198     if (pw->paint.region.source == None) return;
199     tpw = (pw->paint.paint)? (PaintWidget)pw->paint.paint : pw;
200 
201     XtVaGetValues((Widget)pw, XtNtransparent, &transparent, NULL);
202     if (transparent==0) return;
203 
204     /* transparent=2 is transitional state - things should be redrawn once */
205     if (transparent&2)
206         XtVaSetValues((Widget)pw, XtNtransparent, transparent-2, NULL);
207 
208     opaque = 1-(transparent&1);
209     zoom = GET_ZOOM(pw);
210 
211     w = pw->paint.region.child;
212     if (!pw->paint.region.source || !w) {
213        return;
214     }
215 
216     px = pw->paint.region.rect.x;
217     py = pw->paint.region.rect.y;
218 
219     if (px<0) {
220         rx = -px;
221         px = 0;
222     } else
223         rx = 0;
224     if (py<0) {
225         ry = -py;
226         py = 0;
227       } else
228         ry = 0;
229 
230     if (zoom==1) {
231         rwidth = w->core.width - rx;
232         rheight = w->core.height - ry;
233     } else {
234         rwidth = w->core.width;
235         rheight = w->core.height;
236     }
237 
238     if (zoom>0) {
239         widthp = (rwidth+zoom-1)/zoom;
240         heightp = (rheight+zoom-1)/zoom;
241     } else {
242         widthp = rwidth*(-zoom);
243         heightp = rheight*(-zoom);
244     }
245 
246     i = pw->paint.drawWidth - px;
247     j = pw->paint.drawHeight - py;
248 
249     if (widthp>i) widthp = i;
250     if (heightp>j) heightp = j;
251     if (widthp<=0 || heightp<=0)
252        return;
253 
254     gc = (pw->paint.region.fg_gc == None)?
255          pw->paint.tgc : pw->paint.region.fg_gc;
256 
257     src1 = XGetImage(XtDisplay(pw), pw->paint.region.source,
258        	            (zoom==1)?rx:0, (zoom==1)?ry:0, widthp, heightp,
259  		    AllPlanes, ZPixmap);
260 
261     if (tpw->paint.alpha_mode && tpw->paint.region.alpha)
262         AlphaTwistImage(tpw, src1, tpw->paint.region.alpha,
263                         src1->width,
264                         0, 0,
265                         pw->paint.region.orig.x, pw->paint.region.orig.y);
266 
267     if (!src1) return;
268 
269     if ((zoom==1) && opaque) {
270         src2 = src1;
271         bytes_per_pixel = src2->bits_per_pixel/8;
272     } else {
273         src2 = XGetImage(XtDisplay(pw), GET_PIXMAP(pw),
274  		         px, py, widthp, heightp,
275  		         AllPlanes, ZPixmap);
276         if (src2) {
277 	    if (tpw->paint.alpha_mode && tpw->paint.current.alpha)
278                 AlphaTwistImage(tpw, src2, tpw->paint.current.alpha,
279                         tpw->paint.drawWidth,
280 		        px, py,
281                         px, py);
282             bytes_per_pixel = src2->bits_per_pixel/8;
283             if (zoom>0)
284             for (y=0; y<heightp; y++) {
285                 i = src2->bytes_per_line * y;
286                 for (x=0; x<widthp; x++) {
287  	            if (opaque || (x+y)&1)
288  	                memcpy(&src2->data[i], &src1->data[i], bytes_per_pixel);
289  	            i += bytes_per_pixel;
290  	        }
291             } else
292             for (y=0; y<heightp; y++) {
293  	        i = src1->bytes_per_line * y;
294                 for (x=0; x<widthp; x++) {
295  		    if (opaque || (x/(-zoom)+y/(-zoom))&1)
296  	                memcpy(&src2->data[i], &src1->data[i], bytes_per_pixel);
297  	            i += bytes_per_pixel;
298  	        }
299             }
300         }
301         XDestroyImage(src1);
302         if (!src2) return;
303     }
304 
305     if (zoom == 1) {
306         XPutImage(XtDisplay(pw), XtWindow(w), gc,
307            src2, 0, 0, rx, ry, rwidth, rheight);
308         XDestroyImage(src2);
309     } else {
310         src3 = XCreateImage(XtDisplay(pw), pw->paint.visual,
311                             src2->depth, ZPixmap, 0, NULL,
312  		            rwidth, rheight,
313  		            32, 0);
314         if (!src3) {
315             XDestroyImage(src2);
316             return;
317 	}
318         src3->data = (char *) XtMalloc(rheight * src3->bytes_per_line);
319         if (!src3->data) {
320             XDestroyImage(src2);
321             XDestroyImage(src3);
322             return;
323 	}
324         bytes_per_pixel = src3->bits_per_pixel/8;
325         XtVaGetValues((Widget)pw, XtNbackground, &pix, NULL);
326         xxPutPixel(src3, 0, 0, pix);
327         memcpy(bg, src3->data, bytes_per_pixel);
328         if (zoom>0) {
329             skip = -(zoom<=ZOOM_THRESH);
330             px = MIN(rwidth, widthp*zoom);
331             py = MIN(rheight, heightp*zoom);
332             for (y=0; y<py; y++) {
333                 j = src3->bytes_per_line * y;
334  	        if ((y+1)%zoom == skip) {
335                     for (x=0; x<rwidth; x++) {
336  	                memcpy(&src3->data[j], bg, bytes_per_pixel);
337  	                j += bytes_per_pixel;
338 		    }
339  	        } else {
340 		    i = src2->bytes_per_line * (y/zoom);
341                     for (x=0; x<px; x++) {
342  	                if ((x+1)%zoom == skip)
343  		            memcpy(&src3->data[j], bg, bytes_per_pixel);
344                         else
345  	                    memcpy(&src3->data[j], &src2->data[i], bytes_per_pixel);
346  	                j += bytes_per_pixel;
347                         if ((x+1)%zoom == 0)
348                             i += bytes_per_pixel;
349 		    }
350 		}
351 	    }
352         } else {
353 	    zoom_bytes_per_pixel = bytes_per_pixel * (-zoom);
354             px = MIN(rwidth, widthp/(-zoom));
355             py = MIN(rheight, heightp/(-zoom));
356  	    for (y = 0; y < py; y++) {
357 	        i = src2->bytes_per_line * y * (-zoom);
358  	        j = src3->bytes_per_line * y;
359  	        for (x = 0; x < px; x++) {
360  	            memcpy(&src3->data[j], &src2->data[i], bytes_per_pixel);
361                     i += zoom_bytes_per_pixel;
362  	            j += bytes_per_pixel;
363  		}
364  	    }
365         }
366 
367         XDestroyImage(src2);
368 
369         if (zoom!=1 && pw->paint.region.mask != None) {
370 	    XImage * dstMask, *srcMask;
371             Pixmap mask;
372             GC gcbw;
373             if (zoom>0) {
374                 px = MIN(rwidth, widthp*zoom);
375                 py = MIN(rheight, heightp*zoom);
376 	    } else {
377                 px = MIN(rwidth, widthp/(-zoom));
378                 py = MIN(rheight, heightp/(-zoom));
379 	    }
380 	    srcMask = XGetImage(XtDisplay(pw), pw->paint.region.mask,
381 			        0, 0, widthp, heightp,
382 			        AllPlanes, ZPixmap);
383 	    dstMask = XCreateImage(XtDisplay(pw), pw->paint.visual,
384 			        1, ZPixmap, 0, NULL, px, py, 32, 0);
385 	    dstMask->data = (char *) XtMalloc(py*dstMask->bytes_per_line);
386             memset(dstMask->data, 0, py*dstMask->bytes_per_line);
387             if (zoom>0) {
388                 for (y=0; y<py; y++) {
389                     for (x=0; x<px; x++) {
390 		        if (XGetPixel(srcMask, x/zoom, y/zoom))
391 			    XPutPixel(dstMask, x, y, True);
392 		    }
393 		}
394 	    } else {
395                 for (y=0; y<py; y++) {
396                     for (x=0; x<px; x++) {
397 		        if (XGetPixel(srcMask, x*(-zoom), y*(-zoom)))
398 			    XPutPixel(dstMask, x, y, True);
399 		    }
400 		}
401 	    }
402             XDestroyImage(srcMask);
403             mask = XCreatePixmap(XtDisplay(w), XtWindow(w), px, py, 1);
404             gcbw = XCreateGC(XtDisplay(w), mask, 0, 0);
405             XPutImage(XtDisplay(w), mask, gcbw, dstMask, 0, 0, 0, 0, px, py);
406             XSetClipOrigin(XtDisplay(w), gc, 0, 0);
407             XSetClipMask(XtDisplay(w), gc, mask);
408             XPutImage(XtDisplay(w), XtWindow(w), gc, src3, 0, 0, 0, 0, px, py);
409             XSync(XtDisplay(w), False);
410             XDestroyImage(dstMask);
411             XDestroyImage(src3);
412             XFreePixmap(XtDisplay(w), mask);
413             XFreeGC(XtDisplay(w), gcbw);
414             XSetClipOrigin(XtDisplay(w), gc, 0, 0);
415 	    XSetClipMask(XtDisplay(w), gc,  pw->paint.region.mask);
416             return;
417 	}
418 
419         XPutImage(XtDisplay(w), XtWindow(w), gc,
420  	       src3, 0, 0, (zoom==1)?rx:0, (zoom==1)?ry:0, rwidth, rheight);
421         XDestroyImage(src3);
422     }
423 }
424 
425 static void
resizeImg(PaintWidget pw,pwMatrix inv,Pixmap * pix,XImage * pixSrc,Pixmap * mask,XImage * maskSrc,Pixmap * shape)426 resizeImg(PaintWidget pw, pwMatrix inv, Pixmap * pix, XImage * pixSrc,
427 	  Pixmap * mask, XImage * maskSrc, Pixmap * shape)
428 {
429     int width, height, depth;
430     int x, y, zoom;
431     XImage *pixDst, *maskDst = NULL, *shapeDst = NULL;
432     Pixel pixel;
433     int sourceW, sourceH;
434     int ix, iy, fx, fy, cx, cy;
435     double dx, dy, sx, sy;
436 
437     if (pixSrc == NULL || pix == NULL)
438 	return;
439     if (pw->paint.region.rect.width == 0 || pw->paint.region.rect.height == 0)
440 	return;
441 
442     /*
443     **  Construct the dest pixmap.
444      */
445 
446     if (*pix != None)
447 	XFreePixmap(XtDisplay(pw), *pix);
448     if (*mask != None && maskSrc != NULL)
449 	XFreePixmap(XtDisplay(pw), *mask);
450 
451     depth = pixSrc->depth;
452     zoom = GET_ZOOM(pw);
453 
454     if (zoom>0) {
455         width = pw->paint.region.rect.width / zoom;
456 	height = pw->paint.region.rect.height / zoom;
457     } else {
458         width = pw->paint.region.rect.width * (-zoom);
459         height = pw->paint.region.rect.height * (-zoom);
460     }
461 
462     *pix = XCreatePixmap(XtDisplay(pw), XtWindow(pw), width, height, depth);
463     XtVaGetValues((Widget) pw, XtNbackground, &pixel, NULL);
464 
465     pixDst = XCreateImage(XtDisplay(pw), pw->paint.visual,
466 			  depth, ZPixmap, 0, NULL, width, height, 32, 0);
467     XSetForeground(XtDisplay(pw), pw->paint.tgc, WhitePixelOfScreen(XtScreen(pw)));
468     pixDst->data = (char *) XtMalloc(height * pixDst->bytes_per_line);
469 
470     if (maskSrc != NULL) {
471 	*mask = XCreatePixmap(XtDisplay(pw), XtWindow(pw), width, height, 1);
472 	maskDst = XCreateImage(XtDisplay(pw), pw->paint.visual,
473 			      1, ZPixmap, 0, NULL, width, height, 32, 0);
474 	maskDst->data = (char *) XtMalloc(height * maskDst->bytes_per_line);
475 #ifdef SHAPE
476 	if (shape != NULL) {
477 	    *shape = XCreatePixmap(XtDisplay(pw), XtWindow(pw), width, height, 1);
478 	    shapeDst = XCreateImage(XtDisplay(pw), pw->paint.visual,
479 			      1, ZPixmap, 0, NULL, width, height, 32, 0);
480 	    shapeDst->data = (char *) XtMalloc(height * shapeDst->bytes_per_line);
481 	}
482 #endif
483     }
484 
485     cx = pw->paint.region.orig.width / 2;
486     cy = pw->paint.region.orig.height / 2;
487     fx = (int) (-width / 2);
488     fy = (int) (-height / 2);
489     sourceW = pixSrc->width;
490     sourceH = pixSrc->height;
491 
492     for (y = 0, dy = fy; y < height; y++, dy++) {
493 	for (x = 0, dx = fx; x < width; x++, dx++) {
494 	    XFORM(dx, dy, inv, sx, sy);
495 	    ix = (sx + cx + 0.5);
496 	    iy = (sy + cy + 0.5);
497 	    if (ix >= 0 && ix < sourceW && iy >= 0 && iy < sourceH) {
498 		xxPutPixel(pixDst, x, y, xxGetPixel(pixSrc, ix, iy));
499 		if (maskSrc != NULL)
500 		    XPutPixel(maskDst, x, y, XGetPixel(maskSrc, ix, iy));
501 #ifdef SHAPE
502 		if (shapeDst != NULL)
503 		    XPutPixel(shapeDst, x, y, True);
504 #endif
505 	    } else {
506                 xxPutPixel(pixDst, x, y, pixel);
507 	        if (maskSrc != NULL)
508 		    XPutPixel(maskDst, x, y, False);
509 #ifdef SHAPE
510 		if (shapeDst != NULL)
511 		    XPutPixel(shapeDst, x, y, False);
512 #endif
513 	    }
514 	}
515     }
516 
517     XPutImage(XtDisplay(pw), *pix, pw->paint.tgc, pixDst,
518 	      0, 0, 0, 0, width, height);
519     XDestroyImage(pixDst);
520 
521     if (maskSrc != NULL) {
522 	XPutImage(XtDisplay(pw), *mask, pw->paint.mgc, maskDst,
523 		  0, 0, 0, 0, width, height);
524 	XDestroyImage(maskDst);
525     }
526 #ifdef SHAPE
527     if (shapeDst != NULL) {
528 	XPutImage(XtDisplay(pw), *shape, pw->paint.mgc, shapeDst,
529 		  0, 0, 0, 0, width, height);
530 	XDestroyImage(shapeDst);
531     }
532 #endif
533 }
534 
535 void
regionCreateNotMask(PaintWidget pw)536 regionCreateNotMask(PaintWidget pw)
537 {
538     int width, height, zoom;
539 
540     if (pw->paint.region.rect.width == 0 || pw->paint.region.rect.height == 0)
541 	return;
542 
543     if (pw->paint.region.mask == None)
544 	return;
545 
546     if (pw->paint.region.notMask != None)
547 	XFreePixmap(XtDisplay(pw), pw->paint.region.notMask);
548 
549     XtVaGetValues((Widget)pw, XtNzoom, &zoom, NULL);
550 
551     width = pw->paint.region.rect.width;
552     height = pw->paint.region.rect.height;
553     if (zoom<0) {
554         width *= (-zoom);
555         height *= (-zoom);
556     }
557 
558     pw->paint.region.notMask = XCreatePixmap(XtDisplay(pw), XtWindow(pw),
559  	                                     width, height, 1);
560     XSetFunction(XtDisplay(pw), pw->paint.mgc, GXcopyInverted);
561     XCopyArea(XtDisplay(pw), pw->paint.region.mask, pw->paint.region.notMask,
562 	      pw->paint.mgc, 0, 0,
563 	      width, height, 0, 0);
564     XSetFunction(XtDisplay(pw), pw->paint.mgc, GXcopy);
565 
566     XSetClipOrigin(XtDisplay(pw), pw->paint.region.bg_gc, 0, 0);
567     XSetClipMask(XtDisplay(pw), pw->paint.region.bg_gc,
568 		 pw->paint.region.notMask);
569     XSetClipOrigin(XtDisplay(pw), pw->paint.region.fg_gc, 0, 0);
570     XSetClipMask(XtDisplay(pw), pw->paint.region.fg_gc, pw->paint.region.mask);
571 }
572 
573 static void
createVtxPts(PaintWidget pw,double vtx[9][2],Boolean flag,Boolean useZoom)574 createVtxPts(PaintWidget pw, double vtx[9][2], Boolean flag, Boolean useZoom)
575 {
576     int zoom;
577     int i;
578     int x0, x1, y0, y1;
579     int width = pw->paint.region.orig.width;
580     int height = pw->paint.region.orig.height;
581 
582     if (useZoom)
583 	zoom = GET_ZOOM(pw);
584     else
585 	zoom = 1;
586 
587     x0 = (-width / 2);
588     x1 = (width + x0);
589     y0 = (-height / 2);
590     y1 = (height + y0);
591     if (zoom>0) {
592         x0 *= zoom;
593         x1 *= zoom;
594         y0 *= zoom;
595         y1 *= zoom;
596     } else {
597         x0 /= -zoom;
598         x1 /= -zoom;
599         y0 /= -zoom;
600         y1 /= -zoom;
601     }
602 
603     /*
604     **  Watch out, these are points 0,1, and _3__
605      */
606     XFORM(x0, y0, pw->paint.region.mat, vtx[0][0], vtx[0][1]);
607     XFORM(x1, y0, pw->paint.region.mat, vtx[1][0], vtx[1][1]);
608     XFORM(x0, y1, pw->paint.region.mat, vtx[3][0], vtx[3][1]);
609 
610     if (flag) {
611 	XFORM(x1, y1, pw->paint.region.mat, vtx[2][0], vtx[2][1]);
612         if (zoom>0)
613 	for (i = 0; i < 4; i++) {
614 	    vtx[i][0] += pw->paint.region.centerX * zoom;
615 	    vtx[i][1] += pw->paint.region.centerY * zoom;
616 	} else
617 	for (i = 0; i < 4; i++) {
618 	    vtx[i][0] += pw->paint.region.centerX / (-zoom);
619 	    vtx[i][1] += pw->paint.region.centerY / (-zoom);
620 	}
621     } else {
622 	/*
623 	**  sort the points, so that point 0,0 is top left corner
624 	 */
625 	if (vtx[0][0] > vtx[1][0]) {
626 	    double t = x0;
627 	    x0 = x1;
628 	    x1 = t;
629 	}
630 	if (vtx[0][1] > vtx[3][1]) {
631 	    double t = y0;
632 	    y0 = y1;
633 	    y1 = t;
634 	}
635 	XFORM(x0, y0, pw->paint.region.mat, vtx[0][0], vtx[0][1]);
636 	XFORM(0, y0, pw->paint.region.mat, vtx[1][0], vtx[1][1]);
637 	XFORM(x1, y0, pw->paint.region.mat, vtx[2][0], vtx[2][1]);
638 
639 	XFORM(x0, 0, pw->paint.region.mat, vtx[3][0], vtx[3][1]);
640 	XFORM(0, 0, pw->paint.region.mat, vtx[4][0], vtx[4][1]);
641 	XFORM(x1, 0, pw->paint.region.mat, vtx[5][0], vtx[5][1]);
642 
643 	XFORM(x0, y1, pw->paint.region.mat, vtx[6][0], vtx[6][1]);
644 	XFORM(0, y1, pw->paint.region.mat, vtx[7][0], vtx[7][1]);
645 	XFORM(x1, y1, pw->paint.region.mat, vtx[8][0], vtx[8][1]);
646     }
647 }
648 
649 static void
doResize(PaintWidget pw)650 doResize(PaintWidget pw)
651 {
652     pwMatrix inv;
653     Pixmap shape, *shp = &shape;
654 
655     buildSources(pw);
656 
657     /*
658     **  First find out the bounding extent of the transformed
659     **   area, then scale it to fit inside of the "region box"
660      */
661 
662     if (pw->paint.region.maskImg == NULL) {
663 	Boolean needMask = False;
664 	double vtx[9][2];
665 	double minX, minY, maxX, maxY;
666 	int x, cmin, cmax;
667 
668 	createVtxPts(pw, vtx, True, False);
669 
670 	minX = MIN(vtx[0][0], MIN(vtx[1][0], MIN(vtx[2][0], vtx[3][0])));
671 	minY = MIN(vtx[0][1], MIN(vtx[1][1], MIN(vtx[2][1], vtx[3][1])));
672 	maxX = MAX(vtx[0][0], MAX(vtx[1][0], MAX(vtx[2][0], vtx[3][0])));
673 	maxY = MAX(vtx[0][1], MAX(vtx[1][1], MAX(vtx[2][1], vtx[3][1])));
674 
675 	/*
676 	**  After computing min, max see if there are points
677 	**   on all vertices, if so then set the correct return code
678 	 */
679 	for (cmin = cmax = x = 0; x < 4; x++) {
680 	    if ((int) vtx[x][0] == (int) minX)
681 		cmin++;
682 	    else if ((int) vtx[x][0] == (int) maxX)
683 		cmax++;
684 	}
685 	needMask |= (cmin != 2 || cmax != 2);
686 	for (cmin = cmax = x = 0; x < 4; x++) {
687 	    if ((int) vtx[x][1] == (int) minY)
688 		cmin++;
689 	    else if ((int) vtx[x][1] == (int) maxY)
690 		cmax++;
691 	}
692 
693 	needMask |= (cmin != 2 || cmax != 2);
694 
695 	if (needMask) {
696             Pixmap mask;
697             GC mgc;
698             int width, height, zoom;
699 	    /*
700 	    **  If the image we just transformed needs a mask
701 	    **  and one doesn't exist, construct one.
702 	     */
703             width = pw->paint.region.orig.width;
704             height = pw->paint.region.orig.height;
705             XtVaGetValues((Widget)pw, XtNzoom, &zoom, NULL);
706             if (zoom<0) {
707 	        width *= (-zoom);
708 	        height *= (-zoom);
709 	    }
710 	    mask = pw->paint.region.mask =
711 		XCreatePixmap(XtDisplay(pw), XtWindow(pw), width, height, 1);
712 	    mgc = GET_MGC(pw, mask);
713 	    XSetFunction(XtDisplay(pw), mgc, GXset);
714 	    XFillRectangle(XtDisplay(pw), mask, mgc, 0, 0, width, height);
715 	    XSetFunction(XtDisplay(pw), mgc, GXcopy);
716 
717 	    buildSources(pw);
718 
719 	    if (pw->paint.region.fg_gc == None) {
720 		pw->paint.region.fg_gc = XCreateGC(XtDisplay(pw), XtWindow(pw),
721 						   0, 0);
722 		pw->paint.region.bg_gc = XCreateGC(XtDisplay(pw), XtWindow(pw),
723 						   0, 0);
724 	    }
725 	}
726     }
727     INVERT_MAT(pw->paint.region.mat, inv);
728 #ifdef SHAPE
729     if (pw->paint.region.maskImg == NULL || GET_ZOOM(pw) != 1)
730 	shp = NULL;
731 #else
732     shp = NULL;
733 #endif
734 
735     resizeImg(pw, inv, &pw->paint.region.source, pw->paint.region.sourceImg,
736 	      &pw->paint.region.mask, pw->paint.region.maskImg, shp);
737     regionCreateNotMask(pw);
738 
739 #ifdef SHAPE
740     if (shp != NULL) {
741 	XShapeCombineMask(XtDisplay(pw), XtWindow(pw->paint.region.child),
742 			  ShapeBounding, 0, 0, shape, ShapeSet);
743 	XFreePixmap(XtDisplay(pw), shape);
744     }
745 #endif
746 
747     pw->paint.region.needResize = False;
748 }
749 
750 static void
drawRegionBox(PaintWidget pw,Boolean flag)751 drawRegionBox(PaintWidget pw, Boolean flag)
752 {
753     static XPoint xvtxLast[5];
754     XPoint xvtx[5];
755     double vtx[9][2];
756     Window window = XtWindow(pw);
757     int i;
758 
759     createVtxPts(pw, vtx, True, True);
760 
761     xvtx[0].x = vtx[0][0];
762     xvtx[0].y = vtx[0][1];
763     xvtx[1].x = vtx[1][0];
764     xvtx[1].y = vtx[1][1];
765     xvtx[2].x = vtx[2][0];
766     xvtx[2].y = vtx[2][1];
767     xvtx[3].x = vtx[3][0];
768     xvtx[3].y = vtx[3][1];
769     xvtx[4].x = vtx[0][0];
770     xvtx[4].y = vtx[0][1];
771 
772     for (i = 0; i < 5; i++) {
773 	xvtx[i].x += pw->paint.region.child->core.x;
774 	xvtx[i].y += pw->paint.region.child->core.y;
775     }
776 
777     if (pw->paint.region.isDrawn) {
778 	XDrawLines(XtDisplay(pw), window, pw->paint.xgc, xvtxLast, 5,
779 		   CoordModeOrigin);
780 	pw->paint.region.isDrawn = False;
781     }
782     if (flag) {
783 	XDrawLines(XtDisplay(pw), window, pw->paint.xgc, xvtx, 5, CoordModeOrigin);
784 	memcpy(xvtxLast, xvtx, sizeof(xvtxLast));
785 	pw->paint.region.isDrawn = True;
786     }
787 }
788 
789 /*
790 **
791  */
792 
793 static void
regionResizeWindow(PaintWidget pw,Boolean sameCenter)794 regionResizeWindow(PaintWidget pw, Boolean sameCenter)
795 {
796     int zoom = GET_ZOOM(pw);
797     int minX, minY, maxX, maxY;
798     int width, height, dx, dy, nx, ny;
799     int newX, newY;
800     int i;
801     double vtx[9][2];
802 
803 #ifndef INTERACTIVE
804     if (pw->paint.region.isTracking)
805 	return;
806 #endif
807 
808     createVtxPts(pw, vtx, False, False);
809 
810     minX = MIN(vtx[0][0], vtx[6][0]);
811     maxX = MAX(vtx[2][0], vtx[8][0]);
812     minY = MIN(vtx[0][1], vtx[2][1]);
813     maxY = MAX(vtx[6][1], vtx[8][1]);
814 
815     width = maxX - minX + 0.5;
816     height = maxY - minY + 0.5;
817 
818     newX = pw->paint.region.centerX - (width / 2);
819     newY = pw->paint.region.centerY - (height / 2);
820 
821     newX += pw->paint.region.rect.x;
822     newY += pw->paint.region.rect.y;
823     if (!sameCenter) {
824 	pw->paint.region.centerX = width / 2;
825 	pw->paint.region.centerY = height / 2;
826     } else {
827 	pw->paint.region.centerX = width / 2;
828 	pw->paint.region.centerY = height / 2;
829     }
830 
831     if (zoom>0) {
832         if ((width *= zoom) < 10)
833 	    width = 10;
834         if ((height *= zoom) < 10)
835 	    height = 10;
836         XtResizeWidget(pw->paint.region.child, width, height, BW);
837         nx = (newX - pw->paint.zoomX) * zoom;
838         ny = (newY - pw->paint.zoomY) * zoom;
839     } else {
840         if ((width /= (-zoom)) < 10)
841 	    width = 10;
842         if ((height /= (-zoom)) < 10)
843 	    height = 10;
844         XtResizeWidget(pw->paint.region.child, width, height, BW);
845         nx = (newX - pw->paint.zoomX) / (-zoom);
846         ny = (newY - pw->paint.zoomY) / (-zoom);
847     }
848     XtMoveWidget(pw->paint.region.child, nx - BW, ny - BW);
849 
850     pw->paint.region.rect.x = newX;
851     pw->paint.region.rect.y = newY;
852     pw->paint.region.rect.width = width;
853     pw->paint.region.rect.height = height;
854 
855     /*
856     **  Now place all the grips.
857      */
858     createVtxPts(pw, vtx, False, True);
859     width = pw->paint.region.grip[0]->core.width;
860     height = pw->paint.region.grip[0]->core.height;
861     if (zoom>0) {
862         dx = pw->paint.region.centerX * zoom - width / 2;
863         dy = pw->paint.region.centerY * zoom - height / 2;
864     } else {
865         dx = pw->paint.region.centerX / (-zoom) - width / 2;
866         dy = pw->paint.region.centerY / (-zoom) - height / 2;
867     }
868     for (i = 0; i < 9; i++) {
869 	int x, y;
870 
871 	if (i == 4)
872 	    continue;
873 
874 	x = vtx[i][0] + dx;
875 	y = vtx[i][1] + dy;
876 
877 	if (x < 0)
878 	    x = 0;
879 	if (y < 0)
880 	    y = 0;
881 	if (x + width > pw->paint.region.rect.width)
882 	    x = pw->paint.region.rect.width - width;
883 	if (y + height > pw->paint.region.rect.height)
884 	    y = pw->paint.region.rect.height - height;
885 
886 	XtMoveWidget(pw->paint.region.grip[i], x, y);
887     }
888 }
889 
890 /*
891 **
892  */
893 
894 static void
moveGrips(PaintWidget pw)895 moveGrips(PaintWidget pw)
896 {
897     int width, height;
898     int i, gx=0, gy=0;
899     Dimension w, h;
900     Widget widget = pw->paint.region.grip[0];
901 
902     w = widget->core.width;
903     h = widget->core.height;
904     width = widget->core.parent->core.width;
905     height = widget->core.parent->core.height;
906 
907     for (i = 0; i < 9; i++) {
908 	if (i == 4)
909 	    continue;
910 
911 	switch (i % 3) {
912 	case 0:
913 	    gx = 0;
914 	    break;
915 	case 1:
916 	    gx = width / 2 - w / 2;
917 	    break;
918 	case 2:
919 	    gx = width - w;
920 	    break;
921 	}
922 	switch (i / 3) {
923 	case 0:
924 	    gy = 0;
925 	    break;
926 	case 1:
927 	    gy = height / 2 - h / 2;
928 	    break;
929 	case 2:
930 	    gy = height - h;
931 	    break;
932 	}
933 
934 	XtMoveWidget(pw->paint.region.grip[i], gx, gy);
935     }
936 }
937 
938 static void
gripPress(Widget w,PaintWidget pw,XButtonEvent * event,Boolean * junk)939 gripPress(Widget w, PaintWidget pw, XButtonEvent * event, Boolean * junk)
940 {
941     static int fixedPoint[] =
942     {8, 8, 6, 8, -1, 6, 2, 2, 0};
943     double vtx[9][2], fvtx[9][2];
944     double x0, x1, x2, y0, y1, y2, t1, t2, l;
945     int index, i;
946 
947     pw->paint.region.offX = event->x;
948     pw->paint.region.offY = event->y;
949     pw->paint.region.baseX = event->x_root - w->core.x;
950     pw->paint.region.baseY = event->y_root - w->core.y;
951 
952     pw->paint.region.isTracking = True;
953 
954     /*
955     **  Compute which grip was grabbed, to determine constrain line.
956      */
957     for (index = 0; index < 9 && pw->paint.region.grip[index] != w; index++);
958 
959     createVtxPts(pw, vtx, False, False);
960     createVtxPts(pw, fvtx, True, False);
961 
962     x0 = vtx[0][0];
963     y0 = vtx[0][1];
964     x1 = vtx[2][0];
965     y1 = vtx[2][1];
966     x2 = vtx[6][0];
967     y2 = vtx[6][1];
968 
969     pw->paint.region.lineBase[0] = 0;
970     pw->paint.region.lineBase[1] = 0;
971 
972     t1 = x1 - x0;
973     t2 = y1 - y0;
974     l = sqrt(t1 * t1 + t2 * t2);
975     pw->paint.region.lineDelta[0] = t1 / l;
976     pw->paint.region.lineDelta[1] = t2 / l;
977 
978     t1 = x2 - x0;
979     t2 = y2 - y0;
980     l = sqrt(t1 * t1 + t2 * t2);
981     pw->paint.region.lineDelta[2] = t1 / l;
982     pw->paint.region.lineDelta[3] = t2 / l;
983 
984     pw->paint.region.startScaleX = pw->paint.region.scaleX;
985     pw->paint.region.startScaleY = pw->paint.region.scaleY;
986 
987     /*
988     **  Now compute which corner of the 4 cornered box doesn't move
989     **    as the object is resized.
990      */
991     for (i = 0; i < 4; i++) {
992 	double fx = vtx[fixedPoint[index]][0];
993 	double fy = vtx[fixedPoint[index]][1];
994 	double px = fvtx[i][0] - pw->paint.region.centerX;
995 	double py = fvtx[i][1] - pw->paint.region.centerY;
996 
997 	if (ZERO(fx - px) && ZERO(fy - py))
998 	    break;
999     }
1000     pw->paint.region.fixedPoint = i;
1001 }
1002 
1003 static void
regionButtonPress(Widget w,PaintWidget pw,XButtonEvent * event,Boolean * junk)1004 regionButtonPress(Widget w, PaintWidget pw, XButtonEvent * event, Boolean * junk)
1005 {
1006     int zoom = GET_ZOOM(pw);
1007     static long prev_time = 0;
1008     Boolean value;
1009 
1010     pw->paint.region.isRotate = (event->button == Button2);
1011 
1012     pw->paint.region.offX = event->x;
1013     pw->paint.region.offY = event->y;
1014     pw->paint.region.baseX = event->x_root - w->core.x;
1015     pw->paint.region.baseY = event->y_root - w->core.y;
1016 
1017     if (event->button == Button1 && Global.transparent) {
1018         if (abs(event->time-prev_time) > 300) {
1019             XtVaGetValues((Widget)pw, XtNtransparent, &value, NULL);
1020             value = 3 - (value&1);
1021             XtVaSetValues((Widget)pw, XtNtransparent, value, NULL);
1022             PwRegionTear((Widget)pw);
1023             RegionTransparency(pw);
1024 	} else {
1025             prev_time = event->time;
1026 	    return;
1027 	}
1028         prev_time = event->time;
1029     }
1030 
1031     if (zoom>0)
1032     motionExtern((Widget)pw, (XEvent *) event,
1033 	pw->paint.region.rect.x,
1034         pw->paint.region.rect.y + pw->paint.region.rect.height/zoom - 1, 1);
1035     else
1036     motionExtern((Widget)pw, (XEvent *) event,
1037 	pw->paint.region.rect.x,
1038         pw->paint.region.rect.y + pw->paint.region.rect.height*(-zoom)- 1, 1);
1039 
1040     pw->paint.region.lastX = event->x_root;
1041     pw->paint.region.lastY = event->y_root;
1042 
1043     if (pw->paint.region.isRotate) {
1044 	XDefineCursor(XtDisplay(w), XtWindow(pw->paint.region.child),
1045 		      XCreateFontCursor(XtDisplay(w), XC_exchange));
1046 	pw->paint.region.lastAngle = 0.0;
1047     }
1048     /*
1049     **  Only draw the interactive box when we are rotating.
1050      */
1051     pw->paint.region.isTracking = pw->paint.region.isRotate;
1052 }
1053 
1054 static void
gripRelease(Widget w,PaintWidget pw,XButtonEvent * event,Boolean * junk)1055 gripRelease(Widget w, PaintWidget pw, XButtonEvent * event, Boolean * junk)
1056 {
1057     pw->paint.region.isTracking = False;
1058     drawRegionBox(pw, False);
1059 #ifndef INTERACTIVE
1060     if (pw->paint.region.needResize) {
1061 	regionResizeWindow(pw, False);
1062 	regionRedraw(pw);
1063     }
1064 #endif
1065 }
1066 
1067 static void
regionButtonRelease(Widget w,PaintWidget pw,XButtonEvent * event,Boolean * junk)1068 regionButtonRelease(Widget w, PaintWidget pw, XButtonEvent * event,
1069 		    Boolean * junk)
1070 {
1071     int value;
1072 
1073     if (event->button == Button1 && Global.transparent) {
1074         XtVaGetValues((Widget)pw, XtNtransparent, &value, NULL);
1075         value = 3 -(value&1);
1076         XtVaSetValues((Widget)pw, XtNtransparent, value, NULL);
1077         PwRegionTear((Widget)pw);
1078         RegionTransparency(pw);
1079     }
1080 
1081     pw->paint.region.isTracking = False;
1082     drawRegionBox(pw, False);
1083 
1084     if (!pw->paint.region.isRotate)
1085 	return;
1086 
1087 #ifndef INTERACTIVE
1088     if (pw->paint.region.needResize) {
1089 	regionResizeWindow(pw, False);
1090 	regionRedraw(pw);
1091     }
1092 #endif
1093 
1094     XDefineCursor(XtDisplay(w), XtWindow(pw->paint.region.child),
1095 		  XCreateFontCursor(XtDisplay(w), XC_fleur));
1096 }
1097 static void
regionGrab(Widget w,PaintWidget pw,XMotionEvent * event,Boolean * junk)1098 regionGrab(Widget w, PaintWidget pw, XMotionEvent * event, Boolean * junk)
1099 {
1100     int dx, dy, nx, ny;
1101 
1102     while (XCheckTypedWindowEvent(XtDisplay(w), XtWindow(w),
1103 				  MotionNotify, (XEvent *) event));
1104 
1105     PwRegionTear((Widget) pw);
1106 
1107     if (pw->paint.region.isRotate) {
1108 	double da, na;
1109 	pwMatrix m;
1110 
1111 	dx = event->x - pw->paint.region.rect.width / 2;
1112 	dy = event->y - pw->paint.region.rect.height / 2;
1113 	na = atan2((double) dy, (double) dx);
1114 	/*
1115 	 * If Shift is pressed, constrain rotation to multiples of 15 degrees.
1116 	 */
1117 	if (event->state & ShiftMask)
1118 	    na = ((int) (na / (15.0 / 180.0 * M_PI))) * (15.0 / 180.0 * M_PI);
1119 	da = na - pw->paint.region.lastAngle;
1120 	pw->paint.region.lastAngle = na;
1121 
1122 	m[0][0] = cos(da);
1123 	m[0][1] = -sin(da);
1124 	m[1][0] = sin(da);
1125 	m[1][1] = cos(da);
1126 
1127 	PwRegionAppendMatrix((Widget) pw, m);
1128     } else {
1129 	int zoom = GET_ZOOM(pw);
1130 
1131 	nx = event->x_root - pw->paint.region.baseX;
1132 	ny = event->y_root - pw->paint.region.baseY;
1133 
1134 	/*
1135 	 * If Shift is pressed, constrain movement to horizontal or vertical
1136 	 */
1137 	if (event->state & ShiftMask) {
1138 	    if (ABS(event->x_root - pw->paint.region.lastX) >
1139 		ABS(event->y_root - pw->paint.region.lastY))
1140 		ny = pw->paint.region.lastY - pw->paint.region.baseY;
1141 	    else
1142 		nx = pw->paint.region.lastX - pw->paint.region.baseX;
1143 	}
1144 
1145         if (zoom>0) {
1146 	    dx = (nx - w->core.x) / zoom;
1147 	    dy = (ny - w->core.y) / zoom;
1148 	} else {
1149 	    dx = (nx - w->core.x) * (-zoom);
1150 	    dy = (ny - w->core.y) * (-zoom);
1151 	}
1152 
1153 	if (dx == 0 && dy == 0)
1154 	    return;
1155 
1156 	pw->paint.region.rect.x += dx;
1157 	pw->paint.region.rect.y += dy;
1158 
1159         if (zoom>0) {
1160 	    nx = (pw->paint.region.rect.x - pw->paint.zoomX) * zoom;
1161 	    ny = (pw->paint.region.rect.y - pw->paint.zoomY) * zoom;
1162 	} else {
1163 	    nx = (pw->paint.region.rect.x - pw->paint.zoomX) / (-zoom);
1164 	    ny = (pw->paint.region.rect.y - pw->paint.zoomY) / (-zoom);
1165 	}
1166 
1167 	XtMoveWidget(pw->paint.region.child, nx - BW, ny - BW);
1168         if (zoom>0)
1169         motionExtern((Widget)pw, (XEvent *) event,
1170 	   pw->paint.region.rect.x,
1171            pw->paint.region.rect.y + pw->paint.region.rect.height/zoom - 1, 0);
1172         else
1173         motionExtern((Widget)pw, (XEvent *) event,
1174 	   pw->paint.region.rect.x,
1175            pw->paint.region.rect.y+pw->paint.region.rect.height*(-zoom)- 1, 0);
1176         RegionTransparency(pw);
1177     }
1178 }
1179 
1180 void
PwDrawVisibleGrid(PaintWidget pw,Widget w,Boolean flag,int sx,int sy,int ex,int ey)1181 PwDrawVisibleGrid(PaintWidget pw, Widget w, Boolean flag,
1182 		  int sx, int sy, int ex, int ey)
1183 {
1184     GC gc;
1185     Display *dpy = XtDisplay(w);
1186     Window win = XtWindow(w);
1187     XGCValues values;
1188     Pixel gridcolor;
1189     int i, j, snap_x, snap_y, zoom, zx, zy;
1190     int gridmode;
1191 
1192     zoom = GET_ZOOM(pw);
1193     XtVaGetValues((Widget)pw,
1194                   XtNgridmode, &gridmode,
1195                   XtNgridcolor, &gridcolor, NULL);
1196 
1197     if ((gridmode>>31)&1) {
1198         if (pw->paint.snapOn) {
1199             snap_x = pw->paint.snap_x;
1200             snap_y = pw->paint.snap_y;
1201             if (snap_x < 1) snap_x = 1;
1202             if (snap_y < 1) snap_y = 1;
1203         } else
1204             snap_x = snap_y = 1;
1205         if (zoom>0) {
1206             zx = zoom*snap_x;
1207             zy = zoom*snap_y;
1208 	} else {
1209 	    zx = snap_x/(-zoom);
1210             zy = snap_y/(-zoom);
1211 	}
1212         if (zx <= ZOOM_THRESH/2 || zy <= ZOOM_THRESH/2) return;
1213     } else {
1214         if  (zoom <= ZOOM_THRESH) return;
1215         snap_x = snap_y = 1;
1216         zx = zy = zoom;
1217     }
1218 
1219     sx = (sx/snap_x)*zx;
1220     sy = (sy/snap_y)*zy;
1221     if (zoom>0) {
1222         ex = ex*zoom;
1223         ey = ey*zoom;
1224     } else {
1225         ex = ex/(-zoom);
1226         ey = ey/(-zoom);
1227     }
1228 
1229     if (flag) {
1230         values.foreground = gridcolor;
1231         values.background = WhitePixelOfScreen(XtScreen(w));
1232         gc = XCreateGC(dpy, XtWindow(w), GCBackground|GCForeground, &values);
1233     } else {
1234 	/*
1235 	**  Turning off visible grid, and we need to clear
1236 	**    to no space between pixels...
1237 	 */
1238 	if (zx <= ZOOM_THRESH || zy <= ZOOM_THRESH) {
1239             if ((gridmode&3) >= 2) {
1240 	    if (sx>0) sx -= 1;
1241 	    if (sy>0) sy -= 1;
1242 	        ex += 2;
1243                 ey += 2;
1244             }
1245   	    XClearArea(dpy, win, sx, sy, ex - sx, ey - sy, True);
1246 	    return;
1247 	}
1248         values.background = BlackPixelOfScreen(XtScreen(w));
1249         values.foreground = WhitePixelOfScreen(XtScreen(w));
1250         gc = XCreateGC(dpy, XtWindow(w), GCBackground|GCForeground, &values);
1251     }
1252 
1253     switch(gridmode&3) {
1254         case 0:
1255             for (i = sx-1; i <ex; i += zx)
1256 	        XDrawLine(dpy, win, gc, i, sy, i, ey);
1257             for (i = sy-1; i <ey; i += zy)
1258 	        XDrawLine(dpy, win, gc, sx, i, ex, i);
1259             break;
1260         case 1:
1261             for (i = sx-1; i < ex; i += zx)
1262             for (j = sy-1; j < ey; j += zy)
1263                 XDrawPoint(dpy, win, gc, i, j);
1264 	    break;
1265         case 2:
1266             for (i = sx-1; i < ex; i += zx)
1267 	    for (j = sy-1; j < ey; j += zy) {
1268 	        XDrawLine(dpy, win, gc, i-1, j, i+1, j);
1269 	        XDrawLine(dpy, win, gc, i, j-1, i, j+1);
1270 	    }
1271 	    break;
1272         case 3:
1273 	    for (i = sx-1; i < ex; i += zx)
1274 	    for (j = sy-1; j < ey; j += zy)
1275 	        XDrawRectangle(dpy, win, gc, i-1, j-1, 2, 2);
1276             break;
1277     }
1278     XFreeGC(dpy, gc);
1279 }
1280 
1281 void
PwRegionExpose(Widget w,PaintWidget pw,XEvent * event,Boolean * junk)1282 PwRegionExpose(Widget w, PaintWidget pw, XEvent * event, Boolean * junk)
1283 {
1284     PaintWidget tpw;
1285     XImage *xim, *src, *mask;
1286     XRectangle rect, nrect, tr;
1287     int isExpose;
1288     int width=0, height=0;
1289     int zoom = GET_ZOOM(pw);
1290     int pixW, pixH;
1291     int dx, dy;
1292 
1293     if (!pw->paint.region.isVisible)
1294 	return;
1295 
1296     if (event == NULL) {
1297 	isExpose = True;
1298 	rect.x = 0;
1299 	rect.y = 0;
1300 	rect.width = w->core.width;
1301 	rect.height = w->core.height;
1302     } else if (event->xany.type == Expose) {
1303 	rect.x = event->xexpose.x;
1304 	rect.y = event->xexpose.y;
1305 	rect.width = event->xexpose.width;
1306 	rect.height = event->xexpose.height;
1307 	isExpose = True;
1308     } else if (event->xany.type == ConfigureNotify) {
1309 	isExpose = False;
1310 	rect.x = 0;
1311 	rect.y = 0;
1312 	rect.width = w->core.width;
1313 	rect.height = w->core.height;
1314     } else
1315 	return;
1316 
1317 #ifndef INTERACTIVE
1318     if (pw->paint.region.isTracking) {
1319 	drawRegionBox(pw, True);
1320 	return;
1321     }
1322 #endif
1323     if (pw->paint.region.needResize)
1324 	doResize(pw);
1325 
1326     if (zoom != 1) {
1327         if (zoom>0) {
1328 	    nrect.x = rect.x / zoom;
1329 	    nrect.y = rect.y / zoom;
1330 	    width = (rect.width + zoom - 1) / zoom;
1331 	    height = (rect.height + zoom - 1) / zoom;
1332 	    pixW = pw->paint.region.rect.width / zoom;
1333 	    pixH = pw->paint.region.rect.height / zoom;
1334 	} else {
1335   	    nrect.x = rect.x * (-zoom);
1336 	    nrect.y = rect.y * (-zoom);
1337 	    width = rect.width * (-zoom);
1338 	    height = rect.height * (-zoom);
1339 	    pixW = pw->paint.region.rect.width * (-zoom);
1340 	    pixH = pw->paint.region.rect.height * (-zoom);
1341 	}
1342 	/*
1343 	**  It is possible for this to happen, when resizes and
1344 	**    redraws are slightly out of sync.
1345 	 */
1346 	if (width + nrect.x > pixW)
1347 	    width = pixW - nrect.x;
1348 	if (height + nrect.y > pixH)
1349 	    height = pixH - nrect.y;
1350 	if (width <= 0 || height <= 0)
1351 	    return;
1352 	nrect.width = width;
1353 	nrect.height = height;
1354     } else
1355         nrect = rect;
1356 
1357     if (isExpose) {
1358 	if (zoom == 1) {
1359 	    if (pw->paint.region.alpha && pw->paint.alpha_mode) {
1360 	        src = XGetImage(XtDisplay(pw), pw->paint.region.source,
1361 			        rect.x, rect.y, rect.width, rect.height,
1362 			        AllPlanes, ZPixmap);
1363                 AlphaTwistImage(pw, src, pw->paint.region.alpha,
1364                                 pw->paint.region.rect.width,
1365                                 rect.x, rect.y,
1366                                 rect.x+pw->paint.region.orig.x,
1367                                 rect.y+pw->paint.region.orig.y);
1368                 XPutImage(XtDisplay(w), XtWindow(w),
1369                           pw->paint.region.fg_gc == None ?
1370 		          pw->paint.tgc : pw->paint.region.fg_gc, src,
1371                           0, 0, rect.x, rect.y, rect.width, rect.height);
1372 	        XDestroyImage(src);
1373 	    } else
1374 	        XCopyArea(XtDisplay(w), pw->paint.region.source, XtWindow(w),
1375 		          pw->paint.region.fg_gc == None ?
1376 		          pw->paint.tgc : pw->paint.region.fg_gc,
1377 		          rect.x, rect.y, rect.width, rect.height,
1378                           rect.x, rect.y);
1379             if (pw->paint.grid)
1380 	        PwDrawVisibleGrid(pw, w, True, rect.x, rect.y,
1381                                   rect.x+rect.width, rect.y+rect.height);
1382 	} else {
1383             mask = NULL;
1384 	    tr.x = 0;
1385 	    tr.y = 0;
1386             tr.width = width;
1387             tr.height = height;
1388 	    src = XGetImage(XtDisplay(pw), pw->paint.region.source,
1389 			    nrect.x, nrect.y, nrect.width, nrect.height,
1390 			    AllPlanes, ZPixmap);
1391 	    tpw = (pw->paint.paint) ? (PaintWidget) pw->paint.paint : pw;
1392 	    if (tpw->paint.region.alpha && tpw->paint.alpha_mode)
1393 	        AlphaTwistImage(pw, src, tpw->paint.region.alpha,
1394                                 pw->paint.region.orig.width,
1395                                 nrect.x, nrect.y,
1396                                 nrect.x + pw->paint.region.orig.x,
1397                                 nrect.y + pw->paint.region.orig.y);
1398 	    if (pw->paint.region.mask != None)
1399 		mask = XGetImage(XtDisplay(pw), pw->paint.region.mask,
1400 			        nrect.x, nrect.y, nrect.width, nrect.height,
1401 			        AllPlanes, ZPixmap);
1402             dx =  tpw->paint.alpha_mode;
1403             tpw->paint.alpha_mode = 0;
1404 	    PwZoomDraw(pw, w, pw->paint.tgc, src, mask,
1405 		       False, nrect.x, nrect.y, zoom, &tr);
1406             XSync(XtDisplay(pw), False);
1407             tpw->paint.alpha_mode = dx;
1408 	    XDestroyImage(src);
1409 	    if (mask != NULL)
1410 		XDestroyImage(mask);
1411 	}
1412     }
1413 
1414     /*
1415     **  XXX -- This should merge, and do fun things.. but.
1416      */
1417     if (isExpose && (event != NULL && event->xexpose.count != 0))
1418         return;
1419 
1420 
1421     if (pw->paint.region.mask != None) {
1422 	int x = w->core.x + w->core.border_width;
1423 	int y = w->core.y + w->core.border_width;
1424 
1425 	/*
1426 	**  Copy in the background picture
1427 	 */
1428 	if (zoom != 1 || pw->paint.current.alpha) {
1429 	    tpw = (pw->paint.paint) ? (PaintWidget) pw->paint.paint : pw;
1430 
1431             if (zoom>0) {
1432 	        tr.x = (nrect.x + x) / zoom + pw->paint.zoomX;
1433 	        tr.y = (nrect.y + y) / zoom + pw->paint.zoomY;
1434 	    } else {
1435 	        tr.x = (nrect.x + x) * (-zoom) + pw->paint.zoomX;
1436 	        tr.y = (nrect.y + y) * (-zoom) + pw->paint.zoomY;
1437 	    }
1438 	    width = nrect.width;
1439 	    height = nrect.height;
1440 
1441 	    /*
1442 	    **  We could use PwGetImage, but it returns
1443 	    **    the original image, not the sub-region
1444 	     */
1445             dx = 0;
1446             dy = 0;
1447 	    if (tr.x < 0) {
1448 		width += tr.x;
1449 		dx = -tr.x;
1450 		tr.x = 0;
1451 	    }
1452 	    if (tr.y < 0) {
1453 		height += tr.y;
1454 		dy = -tr.y;
1455 		tr.y = 0;
1456 	    }
1457 	    if (tr.x + width > tpw->paint.drawWidth)
1458 		width = tpw->paint.drawWidth - tr.x;
1459 	    if (tr.y + height > tpw->paint.drawHeight)
1460 		height = tpw->paint.drawHeight - tr.y;
1461 	    if (width > 0 && height > 0 && !isExpose &&
1462                 pw->paint.region.notMask) {
1463 		tr.width = width;
1464 		tr.height = height;
1465 		mask = XGetImage(XtDisplay(pw), pw->paint.region.notMask,
1466 		   	        nrect.x + dx, nrect.y + dy, tr.width, tr.height,
1467 				AllPlanes, ZPixmap);
1468                 if (mask) {
1469 		    xim = XGetImage(XtDisplay(w), GET_PIXMAP(pw),
1470 				    tr.x, tr.y, tr.width, tr.height,
1471 				    AllPlanes, ZPixmap);
1472 		    alpha_special_mode = 1;
1473 		    PwZoomDraw(pw, w, pw->paint.tgc, xim, mask, False,
1474 			       dx, dy, zoom, &tr);
1475                     alpha_special_mode = 0;
1476                     XSync(XtDisplay(pw), False);
1477 		    XDestroyImage(mask);
1478 		    XDestroyImage(xim);
1479 		}
1480 	    }
1481 	} else {
1482 	    XCopyArea(XtDisplay(w), GET_PIXMAP(pw), XtWindow(w),
1483 		      pw->paint.region.bg_gc,
1484 		      x, y, w->core.width, w->core.height, 0, 0);
1485             if (pw->paint.grid)
1486 	        PwDrawVisibleGrid(pw, w, True, x, y,
1487                                   x+w->core.width, y+w->core.height);
1488 	}
1489     }
1490 }
1491 
1492 static void
regionMove(Widget w,PaintWidget pw,XEvent * event,Boolean * junk)1493 regionMove(Widget w, PaintWidget pw, XEvent * event, Boolean * junk)
1494 {
1495     if (pw->paint.region.mask == None)
1496 	return;
1497     /*
1498     **  For some reason, I had to generate an extra function..
1499      */
1500     PwRegionExpose(w, pw, event, junk);
1501 }
1502 
1503 
1504 static void
regionSetGripCursors(PaintWidget pw)1505 regionSetGripCursors(PaintWidget pw)
1506 {
1507     static int cursors[9] =
1508     {
1509 	XC_top_left_corner,
1510 	XC_top_side,
1511 	XC_top_right_corner,
1512 	XC_left_side,
1513 	0,
1514 	XC_right_side,
1515 	XC_bottom_left_corner,
1516 	XC_bottom_side,
1517 	XC_bottom_right_corner
1518     };
1519     static int list[9] =
1520     {None, None, None, None, None, None, None, None, None};
1521     int i;
1522 
1523     if (list[0] == None) {
1524 	for (i = 0; i < 9; i++) {
1525 	    if (i != 4)
1526 		list[i] = XCreateFontCursor(XtDisplay(pw), cursors[i]);
1527 	}
1528     }
1529     for (i = 0; i < 9; i++)
1530 	if (i != 4)
1531 	    XDefineCursor(XtDisplay(pw), XtWindow(pw->paint.region.grip[i]), list[i]);
1532 }
1533 
1534 static void
gripGrab(Widget w,PaintWidget pw,XMotionEvent * event,Boolean * junk)1535 gripGrab(Widget w, PaintWidget pw, XMotionEvent * event, Boolean * junk)
1536 {
1537     static Boolean isLeftEdge[] =
1538     {True, False, False,
1539      True, False, False,
1540      True, False, False};
1541     static Boolean isTopEdge[] =
1542     {True, True, True,
1543      False, False, False,
1544      False, False, False};
1545     static Boolean isMiddle[] =
1546     {False, True, False,
1547      True, False, True,
1548      False, True, False};
1549     Boolean sameScale;
1550     int index, i, fp;
1551     int zoom = GET_ZOOM(pw);
1552     double v[2];
1553     int width, height;
1554     double ovtx[9][2], nvtx[9][2];
1555     double dx, dy;
1556 
1557     for (index = 0; index < 9 && pw->paint.region.grip[index] != w; index++);
1558 
1559     /*
1560     **  Find the intersection point
1561      */
1562     for (i = 0; i < 2; i++) {
1563 	double x0 = pw->paint.region.lineBase[0];
1564 	double y0 = pw->paint.region.lineBase[1];
1565 	double xm = event->x;
1566 	double ym = event->y;
1567 
1568 	dx = pw->paint.region.lineDelta[0 + i * 2];
1569 	dy = pw->paint.region.lineDelta[1 + i * 2];
1570 	v[i] = dx * (xm - x0) - dy * (y0 - ym);
1571     }
1572 
1573     if (pw->paint.region.startScaleX < 0)
1574 	v[0] = -v[0];
1575     if (pw->paint.region.startScaleY < 0)
1576 	v[1] = -v[1];
1577     if (isLeftEdge[index])
1578 	v[0] = -v[0];
1579     if (isTopEdge[index])
1580 	v[1] = -v[1];
1581 
1582     if (zoom>0) {
1583         v[0] /= (double) zoom;
1584         v[1] /= (double) zoom;
1585     } else {
1586         v[0] *= (double)(-zoom);
1587 	v[1] *= (double)(-zoom);
1588     }
1589 
1590     PwRegionTear((Widget) pw);
1591 
1592     sameScale = False;
1593     if (isMiddle[index] || (event->state & ShiftMask) != 0) {
1594 	/*
1595 	**  Apply the constraint
1596 	 */
1597 	if (index == 1 || index == 7)
1598 	    v[0] = 0;
1599 	else if (index == 3 || index == 5)
1600 	    v[1] = 0;
1601 	else
1602 	    sameScale = True;
1603     }
1604     width = pw->paint.region.startScaleX * pw->paint.region.orig.width;
1605     height = pw->paint.region.startScaleY * pw->paint.region.orig.height;
1606 
1607     createVtxPts(pw, ovtx, True, False);
1608 
1609     pw->paint.region.scaleX =
1610 	(double) ((int) (width + v[0])) / pw->paint.region.orig.width;
1611     pw->paint.region.scaleY =
1612 	(double) ((int) (height + v[1])) / pw->paint.region.orig.height;
1613 
1614     if (sameScale) {
1615 	double sx = ABS(pw->paint.region.scaleX);
1616 	double sy = ABS(pw->paint.region.scaleY);
1617 
1618 	sx = MIN(sx, sy);
1619 
1620 	pw->paint.region.scaleX = SIGN(pw->paint.region.scaleX) * sx;
1621 	pw->paint.region.scaleY = SIGN(pw->paint.region.scaleY) * sx;
1622     }
1623     MKMAT(pw);
1624     createVtxPts(pw, nvtx, True, False);
1625 
1626     fp = pw->paint.region.fixedPoint;
1627     dx = ovtx[fp][0] - nvtx[fp][0];
1628     dy = ovtx[fp][1] - nvtx[fp][1];
1629 
1630     pw->paint.region.centerX += dx;
1631     pw->paint.region.centerY += dy;
1632 
1633     pw->paint.region.needResize = True;
1634     regionRedraw(pw);
1635 }
1636 
1637 /*
1638  * Copy alpha channel of region to paint canvas, taking mask into account
1639  * Create alpha channel of region, if non existent and needed by canvas.
1640  */
1641 static void
writeRegionAlphaChannel(PaintWidget pw)1642 writeRegionAlphaChannel(PaintWidget pw)
1643 {
1644     int i, j, x, y, x0, x1, y0, y1;
1645     pwMatrix inv;
1646     double sx, sy, dx, dy, cx, cy;
1647     PaintWidget tpw = (pw->paint.paint) ? (PaintWidget)pw->paint.paint : pw;
1648 
1649     if (!tpw->paint.current.alpha && !tpw->paint.region.alpha) return;
1650 
1651     if (!tpw->paint.region.alpha) {
1652         i = pw->paint.region.orig.width * pw->paint.region.orig.height;
1653         tpw->paint.region.alpha = (unsigned char *) XtMalloc(i);
1654         memset(tpw->paint.region.alpha, (unsigned char)Global.alpha_bg, i);
1655     }
1656 
1657     if (!tpw->paint.current.alpha) {
1658 	i = tpw->paint.drawWidth * tpw->paint.drawHeight;
1659         tpw->paint.current.alpha = (unsigned char *) XtMalloc(i);
1660         memset(tpw->paint.current.alpha, (unsigned char)Global.alpha_bg, i);
1661     }
1662 
1663     if (pw->paint.region.mask != None && pw->paint.region.maskImg == NULL) {
1664 	pw->paint.region.maskImg = XGetImage(XtDisplay(pw),
1665 					     pw->paint.region.mask, 0, 0,
1666 					     pw->paint.region.orig.width,
1667 		       pw->paint.region.orig.height, AllPlanes, ZPixmap);
1668     }
1669 
1670     INVERT_MAT(pw->paint.region.mat, inv);
1671 
1672     cx = (pw->paint.region.orig.width+1) * 0.5;
1673     cy = (pw->paint.region.orig.height+1) * 0.5;
1674     dx = pw->paint.region.rect.x + pw->paint.region.orig.width * 0.5;
1675     dy = pw->paint.region.rect.y + pw->paint.region.orig.height * 0.5;
1676 
1677     XFORM(-cx, -cy, pw->paint.region.mat, sx, sy);
1678     x1 = x0 = (int)(sx + dx + 0.5);
1679     y1 = y0 = (int)(sy + dy + 0.5);
1680     XFORM(cx, -cy, pw->paint.region.mat, sx, sy);
1681     i = (int)(sx + dx + 0.5);
1682     j = (int)(sy + dy + 0.5);
1683     if (i<x0) x0 = i; if (i>x1) x1 = i;
1684     if (j<y0) y0 = j; if (j>y1) y1 = j;
1685     XFORM(-cx, cy, pw->paint.region.mat, sx, sy);
1686     i = (int)(sx + dx + 0.5);
1687     j = (int)(sy + dy + 0.5);
1688     if (i<x0) x0 = i; if (i>x1) x1 = i;
1689     if (j<y0) y0 = j; if (j>y1) y1 = j;
1690     XFORM(cx, cy, pw->paint.region.mat, sx, sy);
1691     i = (int)(sx + dx + 0.5);
1692     j = (int)(sy + dy + 0.5);
1693     if (i<x0) x0 = i; if (i>x1) x1 = i;
1694     if (j<y0) y0 = j; if (j>y1) y1 = j;
1695     if (x0>=pw->paint.drawWidth || y0>=pw->paint.drawHeight) return;
1696     if (x1<0 || y1<0) return;
1697     if (x0<0) x0 = 0;
1698     if (y0<0) y0 = 0;
1699     if (x1>=pw->paint.drawWidth) x1 = pw->paint.drawWidth-1;
1700     if (y1>=pw->paint.drawHeight) y1 = pw->paint.drawHeight-1;
1701 
1702     for (y=y0; y<=y1; y++)
1703     for (x=x0; x<=x1; x++) {
1704         XFORM((x-dx), (y-dy), inv, sx, sy);
1705         i = (int)(sx + cx);
1706         j = (int)(sy + cy);
1707         if (i>=0 && i<pw->paint.region.orig.width &&
1708             j>=0 && j<pw->paint.region.orig.height) {
1709             if (pw->paint.region.mask == None ||
1710                 XGetPixel(pw->paint.region.maskImg, i, j)) {
1711                 tpw->paint.current.alpha[x+y*tpw->paint.drawWidth] =
1712 		    tpw->paint.region.alpha[i+j*pw->paint.region.orig.width];
1713 	    }
1714 	}
1715     }
1716 }
1717 
1718 /*
1719 **
1720 **
1721  */
1722 static void
writeRegion(PaintWidget pw)1723 writeRegion(PaintWidget pw)
1724 {
1725     GC gc;
1726     AlphaPixmap pix;
1727     Pixmap src;
1728     XRectangle nr;
1729     int zoom = GET_ZOOM(pw);
1730 
1731     if (!pw->paint.region.isVisible)
1732 	return;
1733 
1734     /* Write alpha channel first, if any */
1735     writeRegionAlphaChannel(pw);
1736 
1737     /*
1738     **  No need to write it, it's still on the drawable.
1739      */
1740     if (pw->paint.region.isAttached)
1741 	return;
1742 
1743     nr.x = pw->paint.region.rect.x;
1744     nr.y = pw->paint.region.rect.y;
1745     if (zoom>0) {
1746         nr.width = pw->paint.region.rect.width / zoom;
1747         nr.height = pw->paint.region.rect.height / zoom;
1748     } else {
1749         nr.width = pw->paint.region.rect.width * (-zoom);
1750         nr.height = pw->paint.region.rect.height * (-zoom);
1751     }
1752 
1753     /*
1754     **  If we've already modified the background (aka ripped the
1755     **    image up)  then don't get a new undo buffer.
1756     **    The second case of the if is if you've done an undo in between.
1757      */
1758     if (pw->paint.region.undo_alphapix.pixmap == None ||
1759 	pw->paint.region.undo_alphapix.pixmap != GET_PIXMAP(pw))
1760 	pix = PwUndoStart((Widget) pw, &nr);
1761     else
1762 	pix = pw->paint.region.undo_alphapix;
1763 
1764     PwUndoAddRectangle((Widget) pw, &nr);
1765 
1766     if (pw->paint.region.fg_gc != None) {
1767 	gc = pw->paint.region.fg_gc;
1768 	XSetClipOrigin(XtDisplay(pw), gc, nr.x, nr.y);
1769     } else {
1770 	gc = pw->paint.tgc;
1771     }
1772 
1773     if (pw->paint.region.proc != NULL) {
1774 	XImage *sim = pw->paint.region.sourceImg;
1775 	Boolean made = False;
1776 
1777 	if (sim == NULL) {
1778 	    sim = XGetImage(XtDisplay(pw),
1779 			    pw->paint.region.source, 0, 0,
1780 			    pw->paint.region.orig.width,
1781 		       pw->paint.region.orig.height, AllPlanes, ZPixmap);
1782 	    made = True;
1783 	}
1784 
1785 	src = (*pw->paint.region.proc) ((Widget) pw, sim, pw->paint.region.mat);
1786 	if (made)
1787 	    XDestroyImage(sim);
1788     } else {
1789 	src = pw->paint.region.source;
1790     }
1791 
1792     XCopyArea(XtDisplay(pw), src, pix.pixmap, gc,
1793 	      0, 0,
1794 	      nr.width, nr.height,
1795 	      nr.x, nr.y);
1796 
1797     if (pw->paint.region.fg_gc != None)
1798 	XSetClipOrigin(XtDisplay(pw), gc, 0, 0);
1799 
1800     PwUpdate((Widget) pw, &nr, False);
1801 }
1802 
1803 /*
1804 **  Called when the parent widgets zoom factor changes
1805  */
1806 static int
zoomScaling(int val,int z1,int z2)1807 zoomScaling(int val, int z1, int z2)
1808 {
1809     if (z2>0) val *= z2;
1810     if (z1<0) val *= -z1;
1811     if (z2<0) val = (val-z2-1)/(-z2);
1812     if (z1>0) val = (val+z1-1)/z1;
1813     return val;
1814 }
1815 
1816 static void
zoomValueChanged(Widget w,XtPointer junk1,XtPointer junk2)1817 zoomValueChanged(Widget w, XtPointer junk1, XtPointer junk2)
1818 {
1819     PaintWidget pw = (PaintWidget) w;
1820     int zoom, curZoom;
1821     int nx, ny;
1822     int nw, nh;
1823 
1824     zoom = GET_ZOOM(pw);
1825     curZoom = pw->paint.region.curZoom;
1826 
1827     if (zoom == curZoom)
1828 	return;
1829 
1830     if (!pw->paint.region.isVisible)
1831 	return;
1832 
1833     if (zoom>0) {
1834         nx = (pw->paint.region.rect.x - pw->paint.zoomX) * zoom;
1835         ny = (pw->paint.region.rect.y - pw->paint.zoomY) * zoom;
1836     } else {
1837         nx = (pw->paint.region.rect.x - pw->paint.zoomX) / (-zoom);
1838         ny = (pw->paint.region.rect.y - pw->paint.zoomY) / (-zoom);
1839     }
1840     nw = zoomScaling(pw->paint.region.rect.width, curZoom, zoom);
1841     pw->paint.region.rect.width = nw;
1842     nh = zoomScaling(pw->paint.region.rect.height, curZoom, zoom);
1843     pw->paint.region.rect.height = nh;
1844 
1845     pw->paint.region.curZoom = zoom;
1846 
1847     XtMoveWidget(pw->paint.region.child, nx - BW, ny - BW);
1848     XtResizeWidget(pw->paint.region.child, nw, nh, BW);
1849 
1850     pw->paint.region.needResize = True;
1851     moveGrips(pw);
1852     regionRedraw(pw);
1853 }
1854 
1855 void
PwRegionZoomPosChanged(PaintWidget pw)1856 PwRegionZoomPosChanged(PaintWidget pw)
1857 {
1858     int nx, ny;
1859     int zoom;
1860 
1861     if (!pw->paint.region.isVisible)
1862 	return;
1863 
1864     zoom = GET_ZOOM(pw);
1865 
1866     if (zoom>0) {
1867         nx = (pw->paint.region.rect.x - pw->paint.zoomX) * zoom;
1868         ny = (pw->paint.region.rect.y - pw->paint.zoomY) * zoom;
1869     } else {
1870         nx = (pw->paint.region.rect.x - pw->paint.zoomX) / (-zoom);
1871         ny = (pw->paint.region.rect.y - pw->paint.zoomY) / (-zoom);
1872     }
1873 
1874     XtMoveWidget(pw->paint.region.child, nx - BW, ny - BW);
1875 }
1876 
1877 
1878 static void
writeCleanRegion(PaintWidget pw,Boolean flag,Boolean write)1879 writeCleanRegion(PaintWidget pw, Boolean flag, Boolean write)
1880 {
1881     if (!pw->paint.region.isVisible)
1882 	return;
1883 
1884     if (write)
1885 	writeRegion(pw);
1886 
1887     /*
1888     **  Free up temporary images
1889      */
1890     if (pw->paint.region.sourceImg != NULL)
1891 	XDestroyImage(pw->paint.region.sourceImg);
1892     if (pw->paint.region.maskImg != NULL)
1893 	XDestroyImage(pw->paint.region.maskImg);
1894     if (pw->paint.region.alpha != NULL)
1895         free(pw->paint.region.alpha);
1896 
1897     pw->paint.region.sourceImg = NULL;
1898     pw->paint.region.maskImg = NULL;
1899     pw->paint.region.alpha = NULL;
1900 
1901     if (pw->paint.region.source != None) {
1902 	XFreePixmap(XtDisplay(pw), pw->paint.region.source);
1903 	pw->paint.region.source = None;
1904     }
1905     if (pw->paint.region.mask != None) {
1906 	XFreePixmap(XtDisplay(pw), pw->paint.region.mask);
1907 	pw->paint.region.mask = None;
1908     }
1909     if (pw->paint.region.notMask != None) {
1910 	XFreePixmap(XtDisplay(pw), pw->paint.region.notMask);
1911 	pw->paint.region.notMask = None;
1912     }
1913     if (pw->paint.region.unfilterPixmap != None) {
1914 	XFreePixmap(XtDisplay(pw), pw->paint.region.unfilterPixmap);
1915 	pw->paint.region.unfilterPixmap = None;
1916     }
1917 
1918     pw->paint.region.undo_alphapix.pixmap = None;
1919     pw->paint.region.undo_alphapix.alpha = NULL;
1920 
1921     if (flag) {
1922 	if (pw->paint.region.child != None)
1923 	    XtUnmapWidget(pw->paint.region.child);
1924 	pw->paint.region.isVisible = False;
1925     }
1926 }
1927 
1928 /*  Turn off the selected region after writing it to the background.
1929  *  flag == True for all widgets
1930  */
1931 void
PwRegionFinish(Widget w,Boolean flag)1932 PwRegionFinish(Widget w, Boolean flag)
1933 {
1934     PaintWidget pw = (PaintWidget) w;
1935     PaintWidget pp = (PaintWidget) pw->paint.paint;
1936     PaintWidget tpw;
1937     int i;
1938 
1939     if (flag) {
1940 	tpw = (pp) ? pp : pw;
1941 	writeCleanRegion(tpw, True, True);
1942 	for (i = 0; i < tpw->paint.paintChildrenSize; i++)
1943 	    writeCleanRegion((PaintWidget) tpw->paint.paintChildren[i], True, True);
1944 	doCallbacks(pw, False);
1945     } else {
1946 	writeCleanRegion(pw, True, True);
1947     }
1948 }
1949 
1950 /*
1951  * Turn off the selected region, but do not write it to the background.
1952  * Return False if no region, else True.
1953  *  flag == True for all widgets
1954  */
1955 Boolean
PwRegionOff(Widget w,Boolean flag)1956 PwRegionOff(Widget w, Boolean flag)
1957 {
1958     PaintWidget pw = (PaintWidget) w;
1959     PaintWidget pp = (PaintWidget) pw->paint.paint;
1960     PaintWidget tpw;
1961     int i;
1962 
1963     tpw = pw;
1964     if (flag)
1965 	tpw = (pp) ? pp : pw;
1966 
1967     if (!tpw->paint.region.isVisible)
1968 	return False;
1969 
1970     writeCleanRegion(tpw, True, False);
1971     if (flag) {
1972 	for (i = 0; i < tpw->paint.paintChildrenSize; i++)
1973 	    writeCleanRegion((PaintWidget) tpw->paint.paintChildren[i], True, False);
1974 	doCallbacks(pw, False);
1975     }
1976     return True;
1977 }
1978 
1979 /*  Set the region pixmap, and mask */
1980 void
PwRegionSet(Widget w,XRectangle * rect,Pixmap pix,Pixmap mask)1981 PwRegionSet(Widget w, XRectangle * rect, Pixmap pix, Pixmap mask)
1982 {
1983     PaintWidget pw = (PaintWidget) w;
1984     PaintWidget tpw = (pw->paint.paint)? (PaintWidget)pw->paint.paint : pw;
1985     int i, j, k1, k2, l1, l2;
1986     int nx, ny, x, y, width, height;
1987     int zoom = GET_ZOOM(pw);
1988     Boolean setIsAttached = False;
1989 
1990 
1991     /*
1992     **  If there is an image, write it
1993     **     rect == NULL, then this is just a "write" & "unmap" request
1994      */
1995     PwRegionFinish(w, True);
1996 
1997     if (pw->paint.region.sourceImg) {
1998         XDestroyImage(pw->paint.region.sourceImg);
1999         pw->paint.region.sourceImg = NULL;
2000     }
2001     if (rect == NULL)
2002         return;
2003 
2004     pw->paint.region.curZoom = zoom;
2005     XtVaSetValues(w, XtNtransparent, 0, NULL);
2006 
2007     if (zoom>0) {
2008         x = (rect->x + pw->paint.zoomX) * zoom;
2009         y = (rect->y + pw->paint.zoomY) * zoom;
2010         width = rect->width * zoom;
2011         height = rect->height * zoom;
2012     } else {
2013         x = (rect->x + pw->paint.zoomX) / (-zoom);
2014         y = (rect->y + pw->paint.zoomY) / (-zoom);
2015         width = rect->width / (-zoom);
2016         height = rect->height / (-zoom);
2017     }
2018 
2019     /*
2020     **  A little "initializing"
2021      */
2022     pw->paint.region.isDrawn = False;
2023     pw->paint.region.isTracking = False;
2024     pw->paint.region.needResize = False;
2025     pw->paint.region.unfilterPixmap = None;
2026 
2027     pw->paint.region.rect = *rect;
2028 
2029     if (pix == None) {
2030 	setIsAttached = True;
2031 
2032 	if (rect->x < 0) {
2033 	    rect->width += rect->x;
2034 	    rect->x = 0;
2035 	}
2036 	if (rect->y < 0) {
2037 	    rect->height += rect->y;
2038 	    rect->y = 0;
2039 	}
2040 	if (rect->x+rect->width > pw->paint.drawWidth)
2041 	    rect->width = pw->paint.drawWidth-rect->x;
2042 	if (rect->y+rect->height > pw->paint.drawHeight)
2043 	    rect->height = pw->paint.drawHeight-rect->y;
2044 
2045 	pw->paint.region.source = XCreatePixmap(XtDisplay(pw), XtWindow(pw),
2046 						rect->width, rect->height,
2047 						pw->core.depth);
2048 	XCopyArea(XtDisplay(pw), GET_PIXMAP(pw), pw->paint.region.source,
2049 		  pw->paint.gc,
2050 		  rect->x, rect->y,
2051 		  rect->width, rect->height,
2052 		  0, 0);
2053     } else {
2054 	pw->paint.region.source = pix;
2055     }
2056 
2057     PwUndoStart((Widget) pw, rect);
2058     PwUndoAddRectangle((Widget) pw, rect);
2059 
2060     pw->paint.region.mask = mask;
2061     pw->paint.region.orig = *rect;
2062 
2063     /*
2064      * import alpha channel from PaintWidget
2065      */
2066     if (setIsAttached && tpw->paint.current.alpha) {
2067         if (pw->paint.region.mask == None)
2068 	    pw->paint.region.maskImg = NULL;
2069         else
2070 	    pw->paint.region.maskImg = XGetImage(XtDisplay(pw),
2071 		       pw->paint.region.mask, 0, 0,
2072 		       pw->paint.region.orig.width,
2073 		       pw->paint.region.orig.height, AllPlanes, ZPixmap);
2074         i = rect->width * rect->height;
2075         tpw->paint.region.alpha = (unsigned char *) XtMalloc(i);
2076         memset(tpw->paint.region.alpha, (unsigned char)Global.alpha_bg, i);
2077         k1 = -pw->paint.region.rect.x;
2078         if (k1<0) k1 = 0;
2079         k2 = pw->paint.region.orig.width;
2080         if (k2>(i=pw->paint.drawWidth-pw->paint.region.rect.x)) k2 = i;
2081         l1 = -pw->paint.region.rect.y;
2082         if (l1<0) l1 = 0;
2083         l2 = pw->paint.region.orig.height;
2084         if (l2>(i=pw->paint.drawHeight-pw->paint.region.rect.y)) l2 = i;
2085         if (k1<k2 && l1<l2)
2086         for (y=l1; y<l2; y++) {
2087 	    i = y * pw->paint.region.orig.width;
2088 	    j = pw->paint.region.rect.x +
2089                 pw->paint.drawWidth*(y+pw->paint.region.rect.y);
2090             if (pw->paint.region.mask != None) {
2091                 for (x=k1; x<k2; x++) {
2092 		    if (XGetPixel(pw->paint.region.maskImg, x, y)) {
2093 		        tpw->paint.region.alpha[x+i] = tpw->paint.current.alpha[x+j];
2094                         if (setIsAttached)
2095                             tpw->paint.current.alpha[x+j] =
2096 			        (unsigned char) Global.alpha_bg;
2097 		    }
2098 	        }
2099             } else {
2100                 memcpy(tpw->paint.region.alpha+i+k1, tpw->paint.current.alpha+j+k1, k2-k1);
2101                 if (setIsAttached)
2102                     memset(tpw->paint.current.alpha+j+k1,
2103                            (unsigned char) Global.alpha_bg, k2-k1);
2104 	    }
2105         }
2106     }
2107 
2108     /*
2109     **  If there is a clipping mask, create a fg and bg GC with clip-masks
2110     **    to draw through.
2111      */
2112     if (mask != None) {
2113 
2114 	if (pw->paint.region.fg_gc == None) {
2115 	    pw->paint.region.fg_gc = XCreateGC(XtDisplay(w), XtWindow(w), 0, 0);
2116 	    pw->paint.region.bg_gc = XCreateGC(XtDisplay(w), XtWindow(w), 0, 0);
2117 	}
2118 	/*
2119 	**  Make sure the Mask GC is built.
2120 	 */
2121 	GET_MGC(pw, mask);
2122 	regionCreateNotMask(pw);
2123     } else {
2124 	/*
2125 	**  No clip mask, make sure we aren't using one.
2126 	 */
2127 	if (pw->paint.region.fg_gc != None) {
2128 	    XSetClipMask(XtDisplay(w), pw->paint.region.fg_gc, None);
2129 	    XSetClipMask(XtDisplay(w), pw->paint.region.bg_gc, None);
2130 	}
2131     }
2132 
2133     if (pw->paint.region.child == None) {
2134 	pw->paint.region.child = XtVaCreateWidget("region",
2135 						  compositeWidgetClass, w,
2136 						  XtNborderWidth, BW,
2137 						  NULL);
2138 	XtAddEventHandler(pw->paint.region.child, ButtonPressMask,
2139 			  False,
2140 			  (XtEventHandler) regionButtonPress,
2141 			  (XtPointer) pw);
2142 	XtAddEventHandler(pw->paint.region.child, ButtonReleaseMask,
2143 			  False,
2144 			  (XtEventHandler) regionButtonRelease,
2145 			  (XtPointer) pw);
2146 	XtAddEventHandler(pw->paint.region.child, ButtonMotionMask,
2147 			  False,
2148 			  (XtEventHandler) regionGrab,
2149 			  (XtPointer) pw);
2150 	XtAddEventHandler(pw->paint.region.child, ExposureMask,
2151 			  False,
2152 			  (XtEventHandler) PwRegionExpose,
2153 			  (XtPointer) pw);
2154 	XtAddEventHandler(pw->paint.region.child, StructureNotifyMask,
2155 			  False,
2156 			  (XtEventHandler) regionMove,
2157 			  (XtPointer) pw);
2158 	XtAddCallback((Widget) pw, XtNsizeChanged,
2159 		    (XtCallbackProc) zoomValueChanged, (XtPointer) NULL);
2160 	XtVaSetValues(pw->paint.region.child, XtNx, x, XtNy, y,
2161 		      XtNwidth, width, XtNheight, height, NULL);
2162 	XtManageChild(pw->paint.region.child);
2163 	XDefineCursor(XtDisplay(w), XtWindow(pw->paint.region.child),
2164 		      XCreateFontCursor(XtDisplay(w), XC_fleur));
2165 
2166 	for (i = 0; i < 9; i++) {
2167 	    if (i == 4)
2168 		continue;
2169 
2170 	    pw->paint.region.grip[i] =
2171 		XtVaCreateManagedWidget("grip",
2172 				 gripWidgetClass, pw->paint.region.child,
2173 					XtNwidth, 6, XtNheight, 6, NULL);
2174 
2175 	    XtAddEventHandler(pw->paint.region.grip[i], ButtonPressMask,
2176 			      False,
2177 			      (XtEventHandler) gripPress,
2178 			      (XtPointer) pw);
2179 	    XtAddEventHandler(pw->paint.region.grip[i], ButtonMotionMask,
2180 			      False,
2181 			      (XtEventHandler) gripGrab,
2182 			      (XtPointer) pw);
2183 	    XtAddEventHandler(pw->paint.region.grip[i], ButtonReleaseMask,
2184 			      False,
2185 			      (XtEventHandler) gripRelease,
2186 			      (XtPointer) pw);
2187 	}
2188 	regionSetGripCursors(pw);
2189 	pw->paint.region.isVisible = True;
2190     } else {
2191 	XClearArea(XtDisplay(pw), XtWindow(pw->paint.region.child),
2192 		   0, 0, 0, 0, True);
2193     }
2194 
2195     if (setIsAttached) {
2196 	nx = rect->x;
2197 	ny = rect->y;
2198     } else {
2199 	nx = pw->paint.downX;
2200 	ny = pw->paint.downY;
2201     }
2202     pw->paint.region.rect.x = nx;
2203     pw->paint.region.rect.y = ny;
2204     nx -= pw->paint.zoomX;
2205     ny -= pw->paint.zoomY;
2206     if (zoom>0)
2207         XtVaSetValues(pw->paint.region.child, XtNx, nx * zoom - BW,
2208 		      XtNy, ny * zoom - BW,
2209 		      XtNwidth, width,
2210 		      XtNheight, height,
2211 		      NULL);
2212     else
2213         XtVaSetValues(pw->paint.region.child, XtNx, nx / (-zoom) - BW,
2214 		      XtNy, ny / (-zoom) - BW,
2215 		      XtNwidth, width,
2216 		      XtNheight, height,
2217 		      NULL);
2218 
2219     pw->paint.region.scaleX = 1.0;
2220     pw->paint.region.scaleY = 1.0;
2221     pw->paint.region.centerX = pw->paint.region.orig.width / 2;
2222     pw->paint.region.centerY = pw->paint.region.orig.height / 2;
2223     COPY_MAT(matIdentity, pw->paint.region.rotMat);
2224     MKMAT(pw);
2225 
2226     if (zoom > 1) {
2227 	pw->paint.region.rect.width *= zoom;
2228 	pw->paint.region.rect.height *= zoom;
2229     }
2230     if (zoom < -1) {
2231         pw->paint.region.rect.width =
2232             (pw->paint.region.rect.width-zoom-1) / (-zoom);
2233         pw->paint.region.rect.height =
2234             (pw->paint.region.rect.height-zoom-1) / (-zoom);
2235     }
2236     moveGrips(pw);
2237 
2238     if (!pw->paint.region.isVisible) {
2239 	XtMapWidget(pw->paint.region.child);
2240         XSync(XtDisplay(pw), False);
2241 	pw->paint.region.isVisible = True;
2242     }
2243 #ifdef SHAPE
2244     XShapeCombineMask(XtDisplay(pw), XtWindow(pw->paint.region.child),
2245 		      ShapeBounding, 0, 0, None, ShapeSet);
2246 #endif
2247 
2248     pw->paint.region.isAttached = setIsAttached;
2249     doCallbacks(pw, True);
2250 }
2251 
2252 static PaintWidget
getActiveRegion(PaintWidget pw)2253 getActiveRegion(PaintWidget pw)
2254 {
2255     PaintWidget tpw = (pw->paint.paint) ? (PaintWidget) pw->paint.paint : pw;
2256     int i;
2257 
2258     if (pw->paint.region.isVisible && pw->paint.region.source != None)
2259 	return pw;
2260 
2261     if (tpw->paint.region.isVisible && tpw->paint.region.source != None)
2262 	return tpw;
2263 
2264     for (i = 0; i < tpw->paint.paintChildrenSize; i++) {
2265 	PaintWidget p = (PaintWidget) tpw->paint.paintChildren[i];
2266 	if (p->paint.region.source != None)
2267 	    return p;
2268     }
2269 
2270     return None;
2271 }
2272 
2273 /*  Set the foreground pixmap, changing it in place */
2274 void
PwRegionSetRawPixmap(Widget w,Pixmap pix)2275 PwRegionSetRawPixmap(Widget w, Pixmap pix)
2276 {
2277     PaintWidget pw = getActiveRegion((PaintWidget) w);
2278 
2279     if (pw == None)
2280 	return;
2281 
2282     XFreePixmap(XtDisplay(pw), pw->paint.region.source);
2283 
2284     if (pw->paint.region.sourceImg != NULL) {
2285 	XDestroyImage(pw->paint.region.sourceImg);
2286 	pw->paint.region.sourceImg = NULL;
2287     }
2288     pw->paint.region.source = pix;
2289 
2290     doResize(pw);
2291     regionRedraw(pw);
2292 }
2293 
2294 /*  Get a copy of the current image & mask, True if exist */
2295 Boolean
PwRegionGet(Widget w,Pixmap * pix,Pixmap * mask)2296 PwRegionGet(Widget w, Pixmap * pix, Pixmap * mask)
2297 {
2298     Display *dpy = XtDisplay(w);
2299     Window win = XtWindow(w);
2300     PaintWidget pw = getActiveRegion((PaintWidget) w);
2301     Pixmap myMask = None, notMask = None;
2302     int zoom;
2303     int width, height;
2304 
2305     if (pw == None)
2306 	return False;
2307     zoom = GET_ZOOM(pw);
2308     width = pw->paint.region.orig.width;
2309     height = pw->paint.region.orig.height;
2310 
2311     if (pix) *pix = None;
2312     if (mask) *mask = None;
2313 
2314     if (pw->paint.region.source != None && pix != NULL) {
2315 	*pix = XCreatePixmap(dpy, win, width, height, pw->core.depth);
2316 	if (pw->paint.region.sourceImg != NULL) {
2317 	    XPutImage(dpy, *pix, pw->paint.tgc,
2318 		      pw->paint.region.sourceImg,
2319 		      0, 0, 0, 0, width, height);
2320 	} else {
2321 	    XCopyArea(dpy, pw->paint.region.source,
2322 		      *pix, pw->paint.tgc,
2323 		      0, 0, width, height, 0, 0);
2324 	}
2325     }
2326     if (pw->paint.region.mask != None) {
2327 	myMask = XCreatePixmap(dpy, win, width, height, 1);
2328 	notMask = XCreatePixmap(dpy, win, width, height, 1);
2329 
2330 	if (pw->paint.region.maskImg != NULL) {
2331 	    XPutImage(dpy, myMask, pw->paint.mgc,
2332 		      pw->paint.region.maskImg,
2333 		      0, 0, 0, 0, width, height);
2334 	} else {
2335 	    XCopyArea(dpy, pw->paint.region.mask,
2336 		      myMask, pw->paint.mgc,
2337 		      0, 0, width, height, 0, 0);
2338 	}
2339 
2340 	XSetFunction(dpy, pw->paint.mgc, GXcopyInverted);
2341 	XCopyArea(dpy, myMask, notMask, pw->paint.mgc, 0, 0,
2342 		  width, height, 0, 0);
2343 	XSetFunction(dpy, pw->paint.mgc, GXcopy);
2344 
2345 	if (mask == NULL)
2346 	    XFreePixmap(dpy, myMask);
2347 	else
2348 	    *mask = myMask;
2349     }
2350     if (notMask != None && pix != NULL) {
2351 	XSetClipOrigin(dpy, pw->paint.igc, 0, 0);
2352 	XSetClipMask(XtDisplay(pw), pw->paint.igc, notMask);
2353 	XFillRectangle(XtDisplay(pw), *pix, pw->paint.igc, 0, 0,
2354 		       width, height);
2355         XSync(XtDisplay(w), False);
2356 	XSetClipMask(XtDisplay(pw), pw->paint.igc, None);
2357 
2358     }
2359     if (notMask != None)
2360 	XFreePixmap(dpy, notMask);
2361 
2362     return True;
2363 }
2364 
2365 /*  Clear the region to the current background color */
2366 void
PwRegionClear(Widget w)2367 PwRegionClear(Widget w)
2368 {
2369     PaintWidget pw = getActiveRegion((PaintWidget) w);
2370 
2371     if (pw == None)
2372 	return;
2373 
2374     PwRegionTear(w);
2375 
2376     pw->paint.region.isVisible = False;
2377     if (pw->paint.region.child != None)
2378 	XtUnmapWidget(pw->paint.region.child);
2379     doCallbacks(pw, False);
2380 }
2381 
2382 void
PwRegionTear(Widget w)2383 PwRegionTear(Widget w)
2384 {
2385     PaintWidget pw;
2386     XRectangle nr;
2387     int zoom;
2388 
2389     pw = getActiveRegion((PaintWidget) w);
2390 
2391     if (pw == None)
2392 	return;
2393     if (!pw->paint.region.isAttached || !pw->paint.region.isVisible)
2394 	return;
2395 
2396     zoom = GET_ZOOM(pw);
2397     nr = pw->paint.region.rect;
2398 
2399     if (zoom>0) {
2400         nr.width = (nr.width+zoom-1)/zoom;
2401         nr.height = (nr.height+zoom-1)/zoom;
2402     } else {
2403         nr.width *= (-zoom);
2404         nr.height *= (-zoom);
2405     }
2406 
2407     pw->paint.region.undo_alphapix = PwUndoStart((Widget) pw, &nr);
2408 
2409     if (pw->paint.region.mask != None) {
2410 	XSetClipOrigin(XtDisplay(pw), pw->paint.igc, nr.x, nr.y);
2411 	XSetClipMask(XtDisplay(pw), pw->paint.igc, pw->paint.region.mask);
2412     }
2413 
2414     XFillRectangles(XtDisplay(pw), pw->paint.region.undo_alphapix.pixmap,
2415                     pw->paint.igc, &nr, 1);
2416 
2417     PwUpdate((Widget) pw, &nr, True);
2418 
2419     if (pw->paint.region.mask != None) {
2420 	XSetClipOrigin(XtDisplay(pw), pw->paint.igc, 0, 0);
2421 	XSetClipMask(XtDisplay(pw), pw->paint.igc, None);
2422     }
2423 
2424     pw->paint.region.isAttached = False;
2425 }
2426 
2427 /*  Append a transformation matrix to the current transform */
2428 void
PwRegionAppendMatrix(Widget w,pwMatrix mat)2429 PwRegionAppendMatrix(Widget w, pwMatrix mat)
2430 {
2431     PaintWidget pw = getActiveRegion((PaintWidget) w);
2432 
2433     if (pw == None)
2434 	return;
2435 
2436     PwRegionTear((Widget) pw);
2437 
2438     mm(pw->paint.region.rotMat, mat, pw->paint.region.rotMat);
2439     MKMAT(pw);
2440 
2441     pw->paint.region.needResize = True;
2442     regionResizeWindow(pw, False);
2443     regionRedraw(pw);
2444 }
2445 
2446 /*  Set the current transformation matrix */
2447 void
PwRegionSetMatrix(Widget w,pwMatrix mat)2448 PwRegionSetMatrix(Widget w, pwMatrix mat)
2449 {
2450     PaintWidget pw = getActiveRegion((PaintWidget) w);
2451 
2452     if (pw == None)
2453 	return;
2454 
2455     PwRegionTear((Widget) pw);
2456 
2457     COPY_MAT(mat, pw->paint.region.rotMat);
2458     MKMAT(pw);
2459 
2460     pw->paint.region.needResize = True;
2461     regionResizeWindow(pw, False);
2462     regionRedraw(pw);
2463 }
2464 
2465 /* Append the current values to the scale */
2466 void
PwRegionAddScale(Widget w,double * xs,double * ys)2467 PwRegionAddScale(Widget w, double *xs, double *ys)
2468 {
2469     PaintWidget pw = getActiveRegion((PaintWidget) w);
2470 
2471     if (pw == None)
2472 	return;
2473 
2474     PwRegionTear((Widget) pw);
2475 
2476     if (xs != NULL)
2477 	pw->paint.region.scaleX *= *xs;
2478     if (ys != NULL)
2479 	pw->paint.region.scaleY *= *ys;
2480 
2481     MKMAT(pw);
2482 
2483     pw->paint.region.needResize = True;
2484     regionResizeWindow(pw, False);
2485     regionRedraw(pw);
2486 }
2487 
2488 /* Set the current X & Y scale values */
2489 void
PwRegionSetScale(Widget w,double * xs,double * ys)2490 PwRegionSetScale(Widget w, double *xs, double *ys)
2491 {
2492     PaintWidget pw = getActiveRegion((PaintWidget) w);
2493 
2494     if (pw == None)
2495 	return;
2496 
2497     if (xs != NULL)
2498 	pw->paint.region.scaleX = *xs;
2499     if (ys != NULL)
2500 	pw->paint.region.scaleY = *ys;
2501 
2502     MKMAT(pw);
2503 
2504     pw->paint.region.needResize = True;
2505     regionResizeWindow(pw, False);
2506     regionRedraw(pw);
2507 }
2508 
2509 /* Reset both the rotation and scale back to identity */
2510 void
PwRegionReset(Widget w,Boolean flag)2511 PwRegionReset(Widget w, Boolean flag)
2512 {
2513     PaintWidget pw = getActiveRegion((PaintWidget) w);
2514     pwMatrix mat;
2515 
2516     if (pw == None)
2517 	return;
2518 
2519     PwRegionTear((Widget) pw);
2520 
2521     mat[0][0] = mat[1][1] = 1;
2522     mat[1][0] = mat[0][1] = 0;
2523 
2524     COPY_MAT(mat, pw->paint.region.rotMat);
2525     pw->paint.region.scaleY = 1.0;
2526     pw->paint.region.scaleX = 1.0;
2527     MKMAT(pw);
2528 
2529     pw->paint.region.needResize = True;
2530     regionResizeWindow(pw, False);
2531     regionRedraw(pw);
2532 
2533     /* XXX flag should reset X & Y position as well */
2534 }
2535 
2536 /*
2537  * Crop to region: replaces the image with the region.
2538  */
2539 void
RegionCrop(PaintWidget paint)2540 RegionCrop(PaintWidget paint)
2541 {
2542     PaintWidget pw = getActiveRegion(paint);
2543     Pixmap pix;
2544 
2545 
2546     StateSetBusy(True);
2547 
2548     if (!PwRegionGet((Widget) paint, &pix, None)) {
2549 	StateSetBusy(False);
2550 	return;			/* No region selected */
2551     }
2552     pw->paint.dirty = True;
2553 
2554     /* Make the region inactive */
2555     PwRegionFinish((Widget) paint, True);
2556 
2557     XtVaSetValues((Widget) paint,
2558 		  XtNpixmap, pix,
2559 		  XtNdrawWidth, pw->paint.region.orig.width,
2560 		  XtNdrawHeight, pw->paint.region.orig.height,
2561 		  NULL);
2562 
2563     PwUpdateDrawable((Widget) paint, XtWindow(paint), NULL);
2564     StateSetBusy(False);
2565 }
2566 
2567 void
RegionMove(PaintWidget pw,int dx,int dy)2568 RegionMove(PaintWidget pw, int dx, int dy)
2569 {
2570     int nx, ny;
2571     int zoom = GET_ZOOM(pw);
2572 
2573     if (getActiveRegion(pw) == None)
2574 	return;
2575 
2576     PwRegionTear((Widget) pw);
2577 
2578     pw->paint.region.rect.x += dx;
2579     pw->paint.region.rect.y += dy;
2580     if (zoom>0) {
2581         nx = (pw->paint.region.rect.x - pw->paint.zoomX) * zoom;
2582         ny = (pw->paint.region.rect.y - pw->paint.zoomY) * zoom;
2583     } else {
2584         nx = (pw->paint.region.rect.x - pw->paint.zoomX) / (-zoom);
2585         ny = (pw->paint.region.rect.y - pw->paint.zoomY) / (-zoom);
2586     }
2587 
2588     XtMoveWidget(pw->paint.region.child, nx - BW, ny - BW);
2589     drawRegionBox(pw, False);
2590 }
2591