1 /*****************************************/
2 /*                                       */
3 /*  XRender-based graphics               */
4 /*                                       */
5 /*****************************************/
6 
7 #include "unix/guts.h"
8 #include "Image.h"
9 
10 #ifdef HAVE_X11_EXTENSIONS_XRENDER_H
11 
12 #define SORT(a,b)	{ int swp; if ((a) > (b)) { swp=(a); (a)=(b); (b)=swp; }}
13 #define REVERT(a)	(XX-> size. y - (a) - 1)
14 #define SHIFT(a,b)	{ (a) += XX-> gtransform. x + XX-> btransform. x; \
15 									(b) += XX-> gtransform. y + XX-> btransform. y; }
16 #define RANGE(a)        { if ((a) < -16383) (a) = -16383; else if ((a) > 16383) a = 16383; }
17 #define RANGE2(a,b)     RANGE(a) RANGE(b)
18 #define RANGE4(a,b,c,d) RANGE(a) RANGE(b) RANGE(c) RANGE(d)
19 
20 /*
21 https://gitlab.freedesktop.org/xorg/lib/libxrender/-/issues/1:
22 tesselation in xrender is horribly broken, but for now I'll give it a go to at least not throw a coredump
23 
24 source is from https://github.com/freedesktop/xorg-libXrender/blob/master/src/Poly.c, (c) Keith Packard
25 
26 */
27 int
28 my_XRenderCompositeDoublePoly (Display		    *dpy,
29 			    int			    op,
30 			    Picture		    src,
31 			    Picture		    dst,
32 			    _Xconst XRenderPictFormat	*maskFormat,
33 			    int			    xSrc,
34 			    int			    ySrc,
35 			    int			    xDst,
36 			    int			    yDst,
37 			    _Xconst XPointDouble    *fpoints,
38 			    int			    npoints,
39 			    int			    winding);
40 
41 typedef struct {
42 	Picture   picture;
43 	Pixmap    pixmap;
44 	GC        gc;
45 	XGCValues gcv;
46 } Pen;
47 
48 static Pen pen;
49 
50 #ifdef NEED_X11_EXTENSIONS_XRENDER_H
51 /* piece of Xrender guts */
52 typedef struct _XExtDisplayInfo {
53 	struct _XExtDisplayInfo *next;
54 	Display *display;
55 	XExtCodes *codes;
56 	XPointer data;
57 } XExtDisplayInfo;
58 
59 extern XExtDisplayInfo *
60 XRenderFindDisplay (Display *dpy);
61 #endif
62 
63 
64 Bool
prima_init_xrender_subsystem(char * error_buf)65 prima_init_xrender_subsystem(char * error_buf)
66 {
67 	int i, count;
68 	XRenderPictFormat *f;
69 	XVisualInfo template, *list;
70 	XRenderPictureAttributes xrp_attr;
71 
72 	{
73 		int dummy;
74 		if ( XRenderQueryExtension( DISP, &dummy, &dummy))
75 			guts. render_extension = true;
76 	}
77 
78 	if ( !guts. render_extension ) return true;
79 
80 #ifdef NEED_X11_EXTENSIONS_XRENDER_H
81 	{ /* snatch error code from xrender guts */
82 		XExtDisplayInfo *info = XRenderFindDisplay( DISP);
83 		if ( info && info-> codes)
84 			guts. xft_xrender_major_opcode = info-> codes-> major_opcode;
85 	}
86 #endif
87 
88 
89 	template. depth = 32; /* XXX should try non-32 bit alpha'ed visuals */
90 	list = XGetVisualInfo( DISP, VisualDepthMask, &template, &count);
91 	for ( i = 0; i < count; i++) {
92 		if (!(f = XRenderFindVisualFormat(DISP, list[i].visual))) continue;
93 		if (f->direct.alphaMask == 0) continue;
94 		guts. argb_visual = list[i];
95 		guts. argb_bits. red_mask   = f->direct. redMask   << f->direct. red;
96 		guts. argb_bits. green_mask = f->direct. greenMask << f->direct. green;
97 		guts. argb_bits. blue_mask  = f->direct. blueMask  << f->direct. blue;
98 		guts. argb_bits. alpha_mask = f->direct. alphaMask << f->direct. alpha;
99 		prima_find_color_mask_range( guts. argb_bits. red_mask,   &guts. argb_bits. red_shift,   &guts. argb_bits. red_range);
100 		prima_find_color_mask_range( guts. argb_bits. green_mask, &guts. argb_bits. green_shift, &guts. argb_bits. green_range);
101 		prima_find_color_mask_range( guts. argb_bits. blue_mask,  &guts. argb_bits. blue_shift,  &guts. argb_bits. blue_range);
102 		prima_find_color_mask_range( guts. argb_bits. alpha_mask, &guts. argb_bits. alpha_shift, &guts. argb_bits. alpha_range);
103 		guts. xrender_argb32_format = f;
104 		guts. argb_depth = f-> depth;
105 		Pdebug("selected visual 0x%x for ARGB operations\n", list[i].visualid);
106 		break;
107 	}
108 	if ( list) XFree( list);
109 
110 	/* find compat format for putting regular pixmaps */
111 	if (!(guts. xrender_display_format = XRenderFindVisualFormat(DISP, guts.visual.visual))) {
112 		guts. xrender_argb32_format = NULL;
113 		guts. argb_visual. visual = NULL;
114 		guts. argb_depth = 0;
115 	}
116 
117 	if ( guts. argb_visual. visual )
118 		guts. argbColormap = XCreateColormap( DISP, guts. root, guts. argb_visual. visual, AllocNone);
119 	else {
120 		Pdebug("no ARGB visual found\n");
121 		guts. render_extension = false;
122 		return true;
123 	}
124 
125 	guts. xrender_a8_format = XRenderFindStandardFormat(DISP, PictStandardA8);
126 	guts. xrender_a1_format = XRenderFindStandardFormat(DISP, PictStandardA1);
127 
128 	pen.pixmap      = XCreatePixmap( DISP, guts.root, 8, 8, 32);
129 	pen.gcv.graphics_exposures = false;
130 	pen.gc          = XCreateGC( DISP, pen.pixmap, GCGraphicsExposures, &pen.gcv);
131 	xrp_attr.repeat = RepeatNormal;
132 	pen.picture     = XRenderCreatePicture( DISP, pen.pixmap, guts.xrender_argb32_format, CPRepeat, &xrp_attr);
133 	guts.xrender_pen_dirty = true;
134 
135 	return true;
136 }
137 
138 void
prima_done_xrender_subsystem()139 prima_done_xrender_subsystem()
140 {
141 	if ( !guts. render_extension ) return;
142 
143 	if ( guts. argbColormap )
144 		XFreeColormap( DISP, guts. argbColormap );
145 	XRenderFreePicture( DISP, pen.picture);
146 	XFreePixmap( DISP, pen.pixmap );
147 	XFreeGC( DISP, pen.gc);
148 	XCHECKPOINT;
149 }
150 
151 Picture
prima_render_create_picture(XDrawable drawable,int depth)152 prima_render_create_picture(XDrawable drawable, int depth)
153 {
154 	XRenderPictFormat *xf;
155 	switch (depth) {
156 	case 1:
157 		xf = guts.xrender_a1_format;
158 		break;
159 	case 32:
160 		xf = guts.xrender_argb32_format;
161 		break;
162 	default:
163 		xf = guts.xrender_display_format;
164 	}
165 	return XRenderCreatePicture( DISP, drawable, xf, 0, NULL);
166 }
167 
168 static void
pen_update(Handle self)169 pen_update(Handle self)
170 {
171 	DEFXX;
172 	int flags =
173 		GCTileStipXOrigin | GCTileStipYOrigin |
174 		GCForeground      | GCFillStyle       |
175 		0;
176 	int alpha = XX->paint_alpha, red, green, blue;
177 
178 	switch ( XX-> paint_rop) {
179 	case ropNotPut:
180 		pen.gcv.foreground = ~XX-> fore.primary;
181 		pen.gcv.background = ~XX-> back.primary;
182 		break;
183 	case ropBlackness:
184 		pen.gcv.foreground = 0x000000;
185 		pen.gcv.background = 0x000000;
186 		break;
187 	case ropWhiteness:
188 		pen.gcv.foreground = 0xffffff;
189 		pen.gcv.background = 0xffffff;
190 		break;
191 	case ropNoOper:
192 		pen.gcv.foreground = 0x000000;
193 		pen.gcv.background = 0x000000;
194 		alpha = 0;
195 		break;
196 	default:
197 		pen.gcv.foreground = XX-> fore.primary;
198 		pen.gcv.background = XX-> back.primary;
199 	}
200 
201 #define COMP(src,c) \
202 	c = ((float) src) / 255.0 * (float)alpha + .5; \
203 	c &= 0xff; \
204 	c = ((c << guts.argb_bits.c##_range) >> 8) << guts.argb_bits.c##_shift;
205 
206 	COMP(COLOR_R(XX->fore.color),red);
207 	COMP(COLOR_G(XX->fore.color),green);
208 	COMP(COLOR_B(XX->fore.color),blue);
209 	pen.gcv.foreground = red | green | blue;
210 
211 	COMP(COLOR_R(XX->back.color),red);
212 	COMP(COLOR_G(XX->back.color),green);
213 	COMP(COLOR_B(XX->back.color),blue);
214 	pen.gcv.background = red | green | blue;
215 #undef COMP
216 
217 	alpha = ((alpha << guts.argb_bits.alpha_range) >> 8) << guts.argb_bits.alpha_shift;
218 	pen.gcv.foreground |= alpha;
219 	pen.gcv.background |= alpha;
220 
221 	pen.gcv.ts_x_origin = XX-> fill_pattern_offset.x;
222 	pen.gcv.ts_y_origin = XX-> fill_pattern_offset.y;
223 	if ( !XX-> flags.brush_null_hatch ) {
224 		pen.gcv.stipple = prima_get_hatch( &XX-> fill_pattern);
225 		if ( !pen.gcv.stipple ) goto SOLID;
226 		if ( XX-> paint_rop2 == ropNoOper )
227 			pen.gcv.background = 0x00000000;
228 		pen.gcv.fill_style = FillOpaqueStippled;
229 		flags |= GCStipple | GCBackground;
230 	} else {
231 	SOLID:
232 		pen.gcv.fill_style = FillSolid;
233 	}
234 	XChangeGC( DISP, pen.gc, flags, &pen.gcv);
235 
236 	XFillRectangle( DISP, pen.pixmap, pen.gc, 0, 0, 8, 8);
237 	guts.xrender_pen_dirty = false;
238 }
239 
240 Bool
apc_gp_aa_fill_poly(Handle self,int numPts,NPoint * points)241 apc_gp_aa_fill_poly( Handle self, int numPts, NPoint * points)
242 {
243 	XPointDouble *p;
244 	int i, ok;
245 	DEFXX;
246 
247 	if ( PObject( self)-> options. optInDrawInfo) return false;
248 	if ( !XF_IN_PAINT(XX)) return false;
249 
250 	if ( !( p = malloc(( numPts + 1) * sizeof( XPointDouble)))) return false;
251 
252 	for ( i = 0; i < numPts; i++) {
253 		p[i].x = points[i]. x + XX-> gtransform. x + XX-> btransform. x;
254 		p[i].y = REVERT(points[i]. y + XX-> gtransform. y + XX-> btransform. y);
255 		RANGE2(p[i].x, p[i].y);
256 	}
257 	p[numPts].x = points[0]. x + XX-> gtransform. x + XX-> btransform. x;
258 	p[numPts].y = REVERT(points[0]. y + XX-> gtransform. y + XX-> btransform. y);
259 	RANGE2(p[numPts].x, p[numPts].y);
260 
261 	if ( guts.xrender_pen_dirty ) pen_update(self);
262 	ok = my_XRenderCompositeDoublePoly(
263 		DISP, PictOpOver, pen.picture, XX->argb_picture,
264 		XX->flags.antialias ? guts.xrender_a8_format : guts.xrender_a1_format,
265 		0, 0, 0, 0, p, numPts,
266 		((XX->fill_mode & fmWinding) == fmAlternate) ? EvenOddRule : WindingRule
267 	);
268 	free( p);
269 
270 	XSync(DISP, false);
271 	XCHECKPOINT;
272 	return ok;
273 }
274 
275 /*
276  *
277  * Copyright © 2002 Keith Packard
278  *
279  * Permission to use, copy, modify, distribute, and sell this software and its
280  * documentation for any purpose is hereby granted without fee, provided that
281  * the above copyright notice appear in all copies and that both that
282  * copyright notice and this permission notice appear in supporting
283  * documentation, and that the name of Keith Packard not be used in
284  * advertising or publicity pertaining to distribution of the software without
285  * specific, written prior permission.  Keith Packard makes no
286  * representations about the suitability of this software for any purpose.  It
287  * is provided "as is" without express or implied warranty.
288  *
289  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
290  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
291  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
292  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
293  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
294  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
295  * PERFORMANCE OF THIS SOFTWARE.
296  */
297 
298 typedef struct _Edge Edge;
299 
300 struct _Edge {
301     XLineFixed	edge;
302     XFixed	current_x;
303     Bool	clockWise;
304     Edge	*next, *prev;
305 };
306 
307 static int
CompareEdge(const void * o1,const void * o2)308 CompareEdge (const void *o1, const void *o2)
309 {
310     const Edge	*e1 = o1, *e2 = o2;
311 
312     return e1->edge.p1.y - e2->edge.p1.y;
313 }
314 
315 static XFixed
XRenderComputeX(XLineFixed * line,XFixed y)316 XRenderComputeX (XLineFixed *line, XFixed y)
317 {
318     XFixed  dx = line->p2.x - line->p1.x;
319     double  ex = (double) (y - line->p1.y) * (double) dx;
320     XFixed  dy = line->p2.y - line->p1.y;
321 
322     return (XFixed) line->p1.x + (XFixed) (ex / dy);
323 }
324 
325 static double
XRenderComputeInverseSlope(XLineFixed * l)326 XRenderComputeInverseSlope (XLineFixed *l)
327 {
328     return (XFixedToDouble (l->p2.x - l->p1.x) /
329 	    XFixedToDouble (l->p2.y - l->p1.y));
330 }
331 
332 static double
XRenderComputeXIntercept(XLineFixed * l,double inverse_slope)333 XRenderComputeXIntercept (XLineFixed *l, double inverse_slope)
334 {
335     return XFixedToDouble (l->p1.x) - inverse_slope * XFixedToDouble (l->p1.y);
336 }
337 
338 static XFixed
XRenderComputeIntersect(XLineFixed * l1,XLineFixed * l2)339 XRenderComputeIntersect (XLineFixed *l1, XLineFixed *l2)
340 {
341     /*
342      * x = m1y + b1
343      * x = m2y + b2
344      * m1y + b1 = m2y + b2
345      * y * (m1 - m2) = b2 - b1
346      * y = (b2 - b1) / (m1 - m2)
347      */
348     double  m1 = XRenderComputeInverseSlope (l1);
349     double  b1 = XRenderComputeXIntercept (l1, m1);
350     double  m2 = XRenderComputeInverseSlope (l2);
351     double  b2 = XRenderComputeXIntercept (l2, m2);
352     if ( m1 == m2 ) return XDoubleToFixed(32766); /* lines do not intersect */
353 
354     return XDoubleToFixed ((b2 - b1) / (m1 - m2));
355 }
356 
357 static int
XRenderComputeTrapezoids(Edge * edges,int nedges,int winding,XTrapezoid * traps,int maxtraps,int * ntraps)358 XRenderComputeTrapezoids (Edge		*edges,
359 			  int		nedges,
360 			  int		winding,
361 			  XTrapezoid	*traps,
362 			  int           maxtraps,
363 			  int           *ntraps
364 			  )
365 {
366     int		inactive, ok = 1;
367     Edge	*active;
368     Edge	*e, *en, *next;
369     XFixed	y, next_y, intersect;
370 
371     *ntraps = 0;
372     qsort (edges, nedges, sizeof (Edge), CompareEdge);
373 
374     y = edges[0].edge.p1.y;
375     active = NULL;
376     inactive = 0;
377     while (ok && (active || inactive < nedges))
378     {
379 	/* insert new active edges into list */
380 	while (inactive < nedges)
381 	{
382 	    e = &edges[inactive];
383 	    if (e->edge.p1.y > y)
384 		break;
385 	    /* move this edge into the active list */
386 	    inactive++;
387 	    e->next = active;
388 	    e->prev = NULL;
389 	    if (active)
390 		active->prev = e;
391 	    active = e;
392 	}
393 
394 	/* compute x coordinates along this group */
395 	for (e = active; e; e = e->next)
396 	    e->current_x = XRenderComputeX (&e->edge, y);
397 
398 	/* sort active list */
399 	for (e = active; e; e = next)
400 	{
401 	    next = e->next;
402 	    /*
403 	     * Find one later in the list that belongs before the
404 	     * current one
405 	     */
406 	    for (en = next; en; en = en->next)
407 	    {
408 		if (en->current_x < e->current_x ||
409 		    (en->current_x == e->current_x &&
410 		     en->edge.p2.x < e->edge.p2.x))
411 		{
412 		    /*
413 		     * insert en before e
414 		     *
415 		     * extract en
416 		     */
417 		    en->prev->next = en->next;
418 		    if (en->next)
419 			en->next->prev = en->prev;
420 		    /*
421 		     * insert en
422 		     */
423 		    if (e->prev)
424 			e->prev->next = en;
425 		    else
426 			active = en;
427 		    en->prev = e->prev;
428 		    e->prev = en;
429 		    en->next = e;
430 		    /*
431 		     * start over at en
432 		     */
433 		    next = en;
434 		    break;
435 		}
436 	    }
437 	}
438 #if 0
439 	printf ("y: %6.3g:", y / 65536.0);
440 	for (e = active; e; e = e->next)
441 	{
442 	    printf (" %6.3g", e->current_x / 65536.0);
443 	}
444 	printf ("\n");
445 #endif
446 	/* find next inflection point */
447 	next_y = active->edge.p2.y;
448 	for (e = active; e; e = en)
449 	{
450 	    if (e->edge.p2.y < next_y)
451 		next_y = e->edge.p2.y;
452 	    en = e->next;
453 	    /* check intersect */
454 	    if (en && e->edge.p2.x > en->edge.p2.x)
455 	    {
456 		intersect = XRenderComputeIntersect (&e->edge, &e->next->edge);
457 		/* make sure this point is below the actual intersection */
458 		intersect = intersect + 1;
459 		if (intersect < next_y && intersect > y)
460 		    next_y = intersect;
461 	    }
462 	}
463 	/* check next inactive point */
464 	if (inactive < nedges && edges[inactive].edge.p1.y < next_y)
465 	    next_y = edges[inactive].edge.p1.y;
466 
467 	if ( y == next_y ) {
468 	    /* emergency brake #1 */
469 	    ok = 0;
470 	    break;
471 	}
472 
473 	/* walk the list generating trapezoids */
474 	for (e = active; e && (en = e->next); e = en->next)
475 	{
476 	    traps->top = y;
477 	    traps->bottom = next_y;
478 	    traps->left = e->edge;
479 	    if ( winding == WindingRule ) {
480 	       int cw = (e->clockWise ? 1 : -1) + (en->clockWise ? 1 : -1);
481 	       while (en && cw != 0) {
482                    en = en->next;
483 	           cw += en->clockWise ? 1 : -1;
484 	       }
485 	       traps->right = en->edge;
486 	       if ( !en ) {
487 	          ok = 0;
488 		  break;
489 	       }
490 	    }
491 	    traps->right = en->edge;
492 	    traps++;
493 	    (*ntraps)++;
494 	    if ( --maxtraps <= 0 ) {
495 	        /* emergency brake #2 */
496 	        ok = 0;
497 	    	break;
498 	    }
499 	}
500 
501 	y = next_y;
502 
503 	/* delete inactive edges from list */
504 	for (e = active; e; e = next)
505 	{
506 	    next = e->next;
507 	    if (e->edge.p2.y <= y)
508 	    {
509 		if (e->prev)
510 		    e->prev->next = e->next;
511 		else
512 		    active = e->next;
513 		if (e->next)
514 		    e->next->prev = e->prev;
515 	    }
516 	}
517     }
518     return ok;
519 }
520 
521 int
my_XRenderCompositeDoublePoly(Display * dpy,int op,Picture src,Picture dst,_Xconst XRenderPictFormat * maskFormat,int xSrc,int ySrc,int xDst,int yDst,_Xconst XPointDouble * fpoints,int npoints,int winding)522 my_XRenderCompositeDoublePoly (Display		    *dpy,
523 			    int			    op,
524 			    Picture		    src,
525 			    Picture		    dst,
526 			    _Xconst XRenderPictFormat	*maskFormat,
527 			    int			    xSrc,
528 			    int			    ySrc,
529 			    int			    xDst,
530 			    int			    yDst,
531 			    _Xconst XPointDouble    *fpoints,
532 			    int			    npoints,
533 			    int			    winding)
534 {
535     Edge	    *edges;
536     XTrapezoid	    *traps;
537     int		    i, ok = 0, nedges, ntraps;
538     XFixed	    x, y, prevx = 0, prevy = 0, firstx = 0, firsty = 0;
539     XFixed	    top = 0, bottom = 0;	/* GCCism */
540 
541     edges = (Edge *) malloc (npoints * sizeof (Edge) +
542 			      ((npoints * npoints + 1) * sizeof (XTrapezoid)));
543     if (!edges)
544 	return 0;
545     traps = (XTrapezoid *) (edges + npoints);
546     nedges = 0;
547     for (i = 0; i <= npoints; i++)
548     {
549 	if (i == npoints)
550 	{
551 	    x = firstx;
552 	    y = firsty;
553 	}
554 	else
555 	{
556 	    x = XDoubleToFixed (fpoints[i].x);
557 	    y = XDoubleToFixed (fpoints[i].y);
558 	}
559 	if (i)
560 	{
561 	    if (y < top)
562 		top = y;
563 	    else if (y > bottom)
564 		bottom = y;
565 	    if (prevy < y)
566 	    {
567 		edges[nedges].edge.p1.x = prevx;
568 		edges[nedges].edge.p1.y = prevy;
569 		edges[nedges].edge.p2.x = x;
570 		edges[nedges].edge.p2.y = y;
571 		edges[nedges].clockWise = True;
572 		nedges++;
573 	    }
574 	    else if (prevy > y)
575 	    {
576 		edges[nedges].edge.p1.x = x;
577 		edges[nedges].edge.p1.y = y;
578 		edges[nedges].edge.p2.x = prevx;
579 		edges[nedges].edge.p2.y = prevy;
580 		edges[nedges].clockWise = False;
581 		nedges++;
582 	    }
583 	    /* drop horizontal edges */
584 	}
585 	else
586 	{
587 	    top = y;
588 	    bottom = y;
589 	    firstx = x;
590 	    firsty = y;
591 	}
592 	prevx = x;
593 	prevy = y;
594     }
595 
596     ok = XRenderComputeTrapezoids (edges, nedges, winding, traps, npoints * npoints + 1, &ntraps);
597     /* XXX adjust xSrc/xDst */
598     if ( ok )
599        XRenderCompositeTrapezoids (dpy, op, src, dst, maskFormat, xSrc, ySrc, traps, ntraps);
600     free (edges);
601     return ok;
602 }
603 
604 #else
605 
prima_init_xrender_subsystem(char * error_buf)606 Bool prima_init_xrender_subsystem(char * error_buf)                 { return true; }
prima_done_xrender_subsystem(void)607 void prima_done_xrender_subsystem(void)                             { }
apc_gp_aa_fill_poly(Handle self,int numPts,NPoint * points)608 Bool apc_gp_aa_fill_poly( Handle self, int numPts, NPoint * points) { return false; }
609 
610 #endif
611 
612