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