1/*
2 * Copyright (c) 2007  David Turner
3 * Copyright (c) 2008  M Joonas Pihlaja
4 * Copyright (c) 2011 Intel Corporation
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Authors:
26 *    Chris Wilson <chris@chris-wilson.co.uk>
27 *
28 */
29
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
34#include "sna.h"
35#include "sna_render.h"
36#include "sna_render_inline.h"
37#include "sna_trapezoids.h"
38#include "fb/fbpict.h"
39
40#include <mipict.h>
41
42struct quorem {
43	int32_t quo;
44	int64_t rem;
45};
46
47struct mono_edge {
48	struct mono_edge *next, *prev;
49
50	int32_t height_left;
51	int32_t dir;
52
53	int64_t dy;
54	struct quorem x;
55	struct quorem dxdy;
56};
57
58struct mono_polygon {
59	int num_edges;
60	struct mono_edge *edges;
61	struct mono_edge **y_buckets;
62
63	struct mono_edge *y_buckets_embedded[64];
64	struct mono_edge edges_embedded[32];
65};
66
67struct mono {
68	/* Leftmost edge on the current scan line. */
69	struct mono_edge head, tail;
70	int is_vertical;
71
72	struct sna *sna;
73	struct sna_composite_op op;
74	pixman_region16_t clip;
75	const BoxRec *clip_start, *clip_end;
76
77	fastcall void (*span)(struct mono *, int, int, BoxPtr);
78
79	struct mono_polygon polygon;
80};
81
82#define I(x) pixman_fixed_to_int((x) + pixman_fixed_1_minus_e/2)
83
84static struct quorem
85floored_muldivrem(int32_t x, int32_t a, int32_t b)
86{
87	struct quorem qr;
88	int64_t xa = (int64_t)x*a;
89	qr.quo = xa/b;
90	qr.rem = xa%b;
91	if (qr.rem < 0) {
92		qr.quo -= 1;
93		qr.rem += b;
94	}
95	return qr;
96}
97
98#if HAS_DEBUG_FULL
99static void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function)
100{
101	if (box->x1 < 0 || box->y1 < 0 ||
102	    box->x2 > pixmap->drawable.width ||
103	    box->y2 > pixmap->drawable.height)
104		FatalError("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n",
105			   __FUNCTION__,
106			   box->x1, box->y1, box->x2, box->y2,
107			   pixmap->drawable.width,
108			   pixmap->drawable.height);
109}
110#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__)
111#else
112#define assert_pixmap_contains_box(p, b)
113#endif
114
115static void apply_damage(struct sna_composite_op *op, RegionPtr region)
116{
117	DBG(("%s: damage=%p, region=%dx[(%d, %d), (%d, %d)]\n",
118	     __FUNCTION__, op->damage,
119	     region_num_rects(region),
120	     region->extents.x1, region->extents.y1,
121	     region->extents.x2, region->extents.y2));
122
123	if (op->damage == NULL)
124		return;
125
126	RegionTranslate(region, op->dst.x, op->dst.y);
127
128	assert_pixmap_contains_box(op->dst.pixmap, RegionExtents(region));
129	sna_damage_add(op->damage, region);
130}
131
132static void _apply_damage_box(struct sna_composite_op *op, const BoxRec *box)
133{
134	BoxRec r;
135
136	r.x1 = box->x1 + op->dst.x;
137	r.x2 = box->x2 + op->dst.x;
138	r.y1 = box->y1 + op->dst.y;
139	r.y2 = box->y2 + op->dst.y;
140
141	assert_pixmap_contains_box(op->dst.pixmap, &r);
142	sna_damage_add_box(op->damage, &r);
143}
144
145inline static void apply_damage_box(struct sna_composite_op *op, const BoxRec *box)
146{
147	if (op->damage)
148		_apply_damage_box(op, box);
149}
150
151static bool
152mono_polygon_init(struct mono_polygon *polygon, BoxPtr box, int num_edges)
153{
154	unsigned h = box->y2 - box->y1;
155
156	polygon->y_buckets = polygon->y_buckets_embedded;
157	if (h > ARRAY_SIZE (polygon->y_buckets_embedded)) {
158		polygon->y_buckets = malloc (h * sizeof (struct mono_edge *));
159		if (unlikely (NULL == polygon->y_buckets))
160			return false;
161	}
162
163	polygon->num_edges = 0;
164	polygon->edges = polygon->edges_embedded;
165	if (num_edges > (int)ARRAY_SIZE (polygon->edges_embedded)) {
166		polygon->edges = malloc (num_edges * sizeof (struct mono_edge));
167		if (unlikely (polygon->edges == NULL)) {
168			if (polygon->y_buckets != polygon->y_buckets_embedded)
169				free(polygon->y_buckets);
170			return false;
171		}
172	}
173
174	memset(polygon->y_buckets, 0, h * sizeof (struct edge *));
175	return true;
176}
177
178static void
179mono_polygon_fini(struct mono_polygon *polygon)
180{
181	if (polygon->y_buckets != polygon->y_buckets_embedded)
182		free(polygon->y_buckets);
183
184	if (polygon->edges != polygon->edges_embedded)
185		free(polygon->edges);
186}
187
188static void
189mono_add_line(struct mono *mono,
190	      int dst_x, int dst_y,
191	      xFixed top, xFixed bottom,
192	      const xPointFixed *p1, const xPointFixed *p2,
193	      int dir)
194{
195	struct mono_polygon *polygon = &mono->polygon;
196	struct mono_edge *e;
197	int y, ytop, ybot;
198
199	__DBG(("%s: top=%d, bottom=%d, line=(%d, %d), (%d, %d) delta=%dx%d, dir=%d\n",
200	       __FUNCTION__,
201	       (int)top, (int)bottom,
202	       (int)p1->x, (int)p1->y, (int)p2->x, (int)p2->y,
203	       dst_x, dst_y,
204	       dir));
205
206	if (top > bottom) {
207		const xPointFixed *t;
208
209		y = top;
210		top = bottom;
211		bottom = y;
212
213		t = p1;
214		p1 = p2;
215		p2 = t;
216
217		dir = -dir;
218	}
219
220	y = I(top) + dst_y;
221	ytop = MAX(y, mono->clip.extents.y1);
222
223	y = I(bottom) + dst_y;
224	ybot = MIN(y, mono->clip.extents.y2);
225
226	__DBG(("%s: edge height [%d, %d] = %d\n",
227	       __FUNCTION__, ytop, ybot, ybot - ytop));
228	if (ybot <= ytop) {
229		__DBG(("discard clipped line\n"));
230		return;
231	}
232
233	e = polygon->edges + polygon->num_edges++;
234	e->height_left = ybot - ytop;
235	e->dir = dir;
236
237	if (I(p1->x) == I(p2->x)) {
238		__DBG(("%s: vertical edge x:%d\n", __FUNCTION__, I(p1->x)));
239		e->x.quo = p1->x;
240		e->x.rem = 0;
241		e->dxdy.quo = 0;
242		e->dxdy.rem = 0;
243		e->dy = 0;
244	} else {
245		int64_t dx = (int64_t)p2->x - p1->x;
246		int64_t dy = (int64_t)p2->y - p1->y;
247
248		__DBG(("%s: diagonal edge (%d, %d), x:[%d, %d]\n", __FUNCTION__, dx, dy, I(p1->x), I(p2->x)));
249		assert(dy > 0);
250
251		e->dxdy = floored_muldivrem(dx, pixman_fixed_1, dy);
252
253		e->x = floored_muldivrem((ytop - dst_y) * pixman_fixed_1 + pixman_fixed_1/2 - p1->y,
254					 dx, dy);
255		e->x.quo += p1->x;
256		e->x.rem -= dy;
257
258		e->dy = dy;
259	}
260	e->x.quo += dst_x*pixman_fixed_1;
261	__DBG(("%s: initial x=%d [%d.%d/%d] + dxdy=%d.%d/%d\n",
262	       __FUNCTION__,
263	       I(e->x.quo), e->x.quo, e->x.rem, e->dy,
264	       e->dxdy.quo, e->dxdy.rem, e->dy));
265
266	{
267		struct mono_edge **ptail = &polygon->y_buckets[ytop - mono->clip.extents.y1];
268		assert(ytop - mono->clip.extents.y1 < mono->clip.extents.y2 - mono->clip.extents.y1);
269		if (*ptail)
270			(*ptail)->prev = e;
271		e->next = *ptail;
272		e->prev = NULL;
273		*ptail = e;
274	}
275}
276
277static struct mono_edge *
278mono_merge_sorted_edges(struct mono_edge *head_a, struct mono_edge *head_b)
279{
280	struct mono_edge *head, **next, *prev;
281	int32_t x;
282
283	if (head_b == NULL)
284		return head_a;
285
286	prev = head_a->prev;
287	next = &head;
288	if (head_a->x.quo <= head_b->x.quo) {
289		head = head_a;
290	} else {
291		head = head_b;
292		head_b->prev = prev;
293		goto start_with_b;
294	}
295
296	do {
297		x = head_b->x.quo;
298		while (head_a != NULL && head_a->x.quo <= x) {
299			prev = head_a;
300			next = &head_a->next;
301			head_a = head_a->next;
302		}
303
304		head_b->prev = prev;
305		*next = head_b;
306		if (head_a == NULL)
307			return head;
308
309start_with_b:
310		x = head_a->x.quo;
311		while (head_b != NULL && head_b->x.quo <= x) {
312			prev = head_b;
313			next = &head_b->next;
314			head_b = head_b->next;
315		}
316
317		head_a->prev = prev;
318		*next = head_a;
319		if (head_b == NULL)
320			return head;
321	} while (1);
322}
323
324static struct mono_edge *
325mono_sort_edges(struct mono_edge *list,
326		unsigned int level,
327		struct mono_edge **head_out)
328{
329	struct mono_edge *head_other, *remaining;
330	unsigned int i;
331
332	head_other = list->next;
333
334	if (head_other == NULL) {
335		*head_out = list;
336		return NULL;
337	}
338
339	remaining = head_other->next;
340	if (list->x.quo <= head_other->x.quo) {
341		*head_out = list;
342		head_other->next = NULL;
343	} else {
344		*head_out = head_other;
345		head_other->prev = list->prev;
346		head_other->next = list;
347		list->prev = head_other;
348		list->next = NULL;
349	}
350
351	for (i = 0; i < level && remaining; i++) {
352		remaining = mono_sort_edges(remaining, i, &head_other);
353		*head_out = mono_merge_sorted_edges(*head_out, head_other);
354	}
355
356	return remaining;
357}
358
359static struct mono_edge *mono_filter(struct mono_edge *edges)
360{
361	struct mono_edge *e;
362
363	e = edges;
364	while (e->next) {
365		struct mono_edge *n = e->next;
366		if (e->dir == -n->dir &&
367		    e->height_left == n->height_left &&
368		    e->x.quo == n->x.quo &&
369		    e->x.rem == n->x.rem &&
370		    e->dxdy.quo == n->dxdy.quo &&
371		    e->dxdy.rem == n->dxdy.rem) {
372			assert(e->dy == n->dy);
373			__DBG(("%s: discarding cancellation pair (%d.%d) + (%d.%d)\n",
374			       __FUNCTION__, e->x.quo, e->x.rem, e->dxdy.quo, e->dxdy.rem));
375
376			if (e->prev)
377				e->prev->next = n->next;
378			else
379				edges = n->next;
380			if (n->next)
381				n->next->prev = e->prev;
382			else
383				break;
384
385			e = n->next;
386		} else {
387			__DBG(("%s: adding edge (%d.%d) + (%d.%d)/%d, height=%d\n",
388			       __FUNCTION__, n->x.quo, n->x.rem, n->dxdy.quo, n->dxdy.rem, n->dy, n->height_left));
389			e = n;
390		}
391	}
392
393	return edges;
394}
395
396static struct mono_edge *
397mono_merge_unsorted_edges(struct mono_edge *head, struct mono_edge *unsorted)
398{
399	mono_sort_edges(unsorted, UINT_MAX, &unsorted);
400	return mono_merge_sorted_edges(head, mono_filter(unsorted));
401}
402
403#if 0
404static inline void
405__dbg_mono_edges(const char *function, struct mono_edge *edges)
406{
407	DBG(("%s: ", function));
408	while (edges) {
409		if (edges->x.quo < INT16_MAX << 16) {
410			DBG(("(%d.%06d)+(%d.%06d)x%d, ",
411			     edges->x.quo, edges->x.rem,
412			     edges->dxdy.quo, edges->dxdy.rem,
413			     edges->dy*edges->dir));
414		}
415		edges = edges->next;
416	}
417	DBG(("\n"));
418}
419#define DBG_MONO_EDGES(x) __dbg_mono_edges(__FUNCTION__, x)
420static inline void
421VALIDATE_MONO_EDGES(struct mono_edge *edges)
422{
423	int prev_x = edges->x.quo;
424	while ((edges = edges->next)) {
425		assert(edges->x.quo >= prev_x);
426		prev_x = edges->x.quo;
427	}
428}
429
430#else
431#define DBG_MONO_EDGES(x)
432#define VALIDATE_MONO_EDGES(x)
433#endif
434
435inline static void
436mono_merge_edges(struct mono *c, struct mono_edge *edges)
437{
438	struct mono_edge *e;
439
440	DBG_MONO_EDGES(edges);
441
442	for (e = edges; c->is_vertical && e; e = e->next)
443		c->is_vertical = e->dy == 0;
444
445	c->head.next = mono_merge_unsorted_edges(c->head.next, edges);
446}
447
448fastcall static void
449mono_span(struct mono *c, int x1, int x2, BoxPtr box)
450{
451	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
452
453	box->x1 = x1;
454	box->x2 = x2;
455
456	if (c->clip.data) {
457		pixman_region16_t region;
458
459		pixman_region_init_rects(&region, box, 1);
460		RegionIntersect(&region, &region, &c->clip);
461		if (region_num_rects(&region)) {
462			c->op.boxes(c->sna, &c->op,
463				    region_rects(&region),
464				    region_num_rects(&region));
465			apply_damage(&c->op, &region);
466		}
467		pixman_region_fini(&region);
468	} else {
469		c->op.box(c->sna, &c->op, box);
470		apply_damage_box(&c->op, box);
471	}
472}
473
474fastcall static void
475mono_span__fast(struct mono *c, int x1, int x2, BoxPtr box)
476{
477	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
478
479	box->x1 = x1;
480	box->x2 = x2;
481
482	c->op.box(c->sna, &c->op, box);
483}
484
485fastcall static void
486mono_span__clipped(struct mono *c, int x1, int x2, BoxPtr box)
487{
488	const BoxRec *b;
489
490	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
491
492	c->clip_start =
493		find_clip_box_for_y(c->clip_start, c->clip_end, box->y1);
494
495	b = c->clip_start;
496	while (b != c->clip_end) {
497		BoxRec clipped;
498
499		if (box->y2 <= b->y1)
500			break;
501
502		clipped.x1 = x1;
503		clipped.x2 = x2;
504		clipped.y1 = box->y1;
505		clipped.y2 = box->y2;
506		if (!box_intersect(&clipped, b++))
507			continue;
508
509		c->op.box(c->sna, &c->op, &clipped);
510	}
511}
512
513struct mono_span_thread_boxes {
514	const struct sna_composite_op *op;
515#define MONO_SPAN_MAX_BOXES (8192/sizeof(BoxRec))
516	BoxRec boxes[MONO_SPAN_MAX_BOXES];
517	int num_boxes;
518};
519
520inline static void
521thread_mono_span_add_box(struct mono *c, const BoxRec *box)
522{
523	struct mono_span_thread_boxes *b = c->op.priv;
524
525	if (unlikely(b->num_boxes == MONO_SPAN_MAX_BOXES)) {
526		b->op->thread_boxes(c->sna, b->op, b->boxes, b->num_boxes);
527		b->num_boxes = 0;
528	}
529
530	b->boxes[b->num_boxes++] = *box;
531	assert(b->num_boxes <= MONO_SPAN_MAX_BOXES);
532}
533
534fastcall static void
535thread_mono_span_clipped(struct mono *c, int x1, int x2, BoxPtr box)
536{
537	const BoxRec *b;
538
539	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
540
541	c->clip_start =
542		find_clip_box_for_y(c->clip_start, c->clip_end, box->y1);
543
544	b = c->clip_start;
545	while (b != c->clip_end) {
546		BoxRec clipped;
547
548		if (box->y2 <= b->y1)
549			break;
550
551		clipped.x1 = x1;
552		clipped.x2 = x2;
553		clipped.y1 = box->y1;
554		clipped.y2 = box->y2;
555		if (!box_intersect(&clipped, b++))
556			continue;
557
558		thread_mono_span_add_box(c, &clipped);
559	}
560}
561
562fastcall static void
563thread_mono_span(struct mono *c, int x1, int x2, BoxPtr box)
564{
565	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
566
567	box->x1 = x1;
568	box->x2 = x2;
569	thread_mono_span_add_box(c, box);
570}
571
572inline static void
573mono_row(struct mono *c, int16_t y, int16_t h)
574{
575	struct mono_edge *edge = c->head.next;
576	int prev_x = INT_MIN;
577	int16_t xstart = INT16_MIN;
578	int winding = 0;
579	BoxRec box;
580
581	__DBG(("%s: y=%d, h=%d\n", __FUNCTION__, y, h));
582
583	DBG_MONO_EDGES(edge);
584	VALIDATE_MONO_EDGES(&c->head);
585
586	box.y1 = c->clip.extents.y1 + y;
587	box.y2 = box.y1 + h;
588
589	while (&c->tail != edge) {
590		struct mono_edge *next = edge->next;
591		int16_t xend = I(edge->x.quo);
592
593		__DBG(("%s: adding edge dir=%d [winding=%d], x=%d [%d]\n",
594		       __FUNCTION__, edge->dir, winding + edge->dir, xend, edge->x.quo));
595		if (--edge->height_left) {
596			if (edge->dy) {
597				edge->x.quo += edge->dxdy.quo;
598				edge->x.rem += edge->dxdy.rem;
599				if (edge->x.rem >= 0) {
600					++edge->x.quo;
601					edge->x.rem -= edge->dy;
602				}
603				__DBG(("%s: stepped edge (%d.%d) + (%d.%d)/%d, height=%d, prev_x=%d\n",
604				       __FUNCTION__, edge->x.quo, edge->x.rem, edge->dxdy.quo, edge->dxdy.rem, edge->dy, edge->height_left, edge->x.quo));
605			}
606
607			if (edge->x.quo < prev_x) {
608				struct mono_edge *pos = edge->prev;
609				pos->next = next;
610				next->prev = pos;
611				do {
612					pos = pos->prev;
613				} while (edge->x.quo < pos->x.quo);
614				pos->next->prev = edge;
615				edge->next = pos->next;
616				edge->prev = pos;
617				pos->next = edge;
618			} else
619				prev_x = edge->x.quo;
620		} else {
621			edge->prev->next = next;
622			next->prev = edge->prev;
623		}
624
625		winding += edge->dir;
626		if (winding == 0) {
627			assert(I(next->x.quo) >= xend);
628			if (I(next->x.quo) > xend) {
629				__DBG(("%s: end span: %d\n", __FUNCTION__, xend));
630				if (xstart < c->clip.extents.x1)
631					xstart = c->clip.extents.x1;
632				if (xend > c->clip.extents.x2)
633					xend = c->clip.extents.x2;
634				if (xend > xstart) {
635					__DBG(("%s: emit span [%d, %d]\n", __FUNCTION__, xstart, xend));
636					c->span(c, xstart, xend, &box);
637				}
638				xstart = INT16_MIN;
639			}
640		} else if (xstart == INT16_MIN) {
641			__DBG(("%s: starting new span: %d\n", __FUNCTION__, xend));
642			xstart = xend;
643		}
644
645		edge = next;
646	}
647
648	DBG_MONO_EDGES(c->head.next);
649	VALIDATE_MONO_EDGES(&c->head);
650}
651
652static bool
653mono_init(struct mono *c, int num_edges)
654{
655	if (!mono_polygon_init(&c->polygon, &c->clip.extents, num_edges))
656		return false;
657
658	c->head.dy = 0;
659	c->head.height_left = INT_MAX;
660	c->head.x.quo = INT16_MIN << 16;
661	c->head.prev = NULL;
662	c->head.next = &c->tail;
663	c->tail.prev = &c->head;
664	c->tail.next = NULL;
665	c->tail.x.quo = INT16_MAX << 16;
666	c->tail.height_left = INT_MAX;
667	c->tail.dy = 0;
668
669	c->is_vertical = 1;
670
671	return true;
672}
673
674static void
675mono_fini(struct mono *mono)
676{
677	mono_polygon_fini(&mono->polygon);
678}
679
680static void
681mono_step_edges(struct mono *c, int count)
682{
683	struct mono_edge *edge;
684
685	for (edge = c->head.next; edge != &c->tail; edge = edge->next) {
686		edge->height_left -= count;
687		if (! edge->height_left) {
688			edge->prev->next = edge->next;
689			edge->next->prev = edge->prev;
690		}
691	}
692}
693
694flatten static void
695mono_render(struct mono *mono)
696{
697	struct mono_polygon *polygon = &mono->polygon;
698	int i, j, h = mono->clip.extents.y2 - mono->clip.extents.y1;
699
700	assert(mono->span);
701
702	for (i = 0; i < h; i = j) {
703		j = i + 1;
704
705		__DBG(("%s: row=%d, new edges? %d\n", __FUNCTION__,
706		       i, polygon->y_buckets[i] != NULL));
707
708		if (polygon->y_buckets[i])
709			mono_merge_edges(mono, polygon->y_buckets[i]);
710
711		__DBG(("%s: row=%d, vertical? %d\n", __FUNCTION__,
712		       i, mono->is_vertical));
713		if (mono->is_vertical) {
714			struct mono_edge *e = mono->head.next;
715			int min_height = h - i;
716
717			while (e != &mono->tail) {
718				if (e->height_left < min_height)
719					min_height = e->height_left;
720				e = e->next;
721			}
722
723			while (--min_height >= 1 && polygon->y_buckets[j] == NULL)
724				j++;
725			if (j != i + 1)
726				mono_step_edges(mono, j - (i + 1));
727			__DBG(("%s: %d vertical rows\n", __FUNCTION__, j-i));
728		}
729
730		mono_row(mono, i, j-i);
731
732		/* XXX recompute after dropping edges? */
733		if (mono->head.next == &mono->tail)
734			mono->is_vertical = 1;
735	}
736}
737
738static int operator_is_bounded(uint8_t op)
739{
740	switch (op) {
741	case PictOpOver:
742	case PictOpOutReverse:
743	case PictOpAdd:
744		return true;
745	default:
746		return false;
747	}
748}
749
750struct mono_span_thread {
751	struct sna *sna;
752	const xTrapezoid *traps;
753	const struct sna_composite_op *op;
754	RegionPtr clip;
755	int ntrap;
756	BoxRec extents;
757	int dx, dy;
758};
759
760static void
761mono_span_thread(void *arg)
762{
763	struct mono_span_thread *thread = arg;
764	struct mono mono;
765	struct mono_span_thread_boxes boxes;
766	const xTrapezoid *t;
767	int n;
768
769	mono.sna = thread->sna;
770
771	mono.clip.extents = thread->extents;
772	mono.clip.data = NULL;
773	if (thread->clip->data) {
774		RegionIntersect(&mono.clip, &mono.clip, thread->clip);
775		if (RegionNil(&mono.clip))
776			return;
777	}
778	region_get_boxes(&mono.clip, &mono.clip_start, &mono.clip_end);
779
780	boxes.op = thread->op;
781	boxes.num_boxes = 0;
782	mono.op.priv = &boxes;
783
784	if (!mono_init(&mono, 2*thread->ntrap)) {
785		RegionUninit(&mono.clip);
786		return;
787	}
788
789	for (n = thread->ntrap, t = thread->traps; n--; t++) {
790		if (!xTrapezoidValid(t))
791			continue;
792
793		if (pixman_fixed_to_int(t->top) + thread->dy >= thread->extents.y2 ||
794		    pixman_fixed_to_int(t->bottom) + thread->dy <= thread->extents.y1)
795			continue;
796
797		mono_add_line(&mono, thread->dx, thread->dy,
798			      t->top, t->bottom,
799			      &t->left.p1, &t->left.p2, 1);
800		mono_add_line(&mono, thread->dx, thread->dy,
801			      t->top, t->bottom,
802			      &t->right.p1, &t->right.p2, -1);
803	}
804
805	if (mono.clip.data == NULL)
806		mono.span = thread_mono_span;
807	else
808		mono.span = thread_mono_span_clipped;
809
810	mono_render(&mono);
811	mono_fini(&mono);
812
813	if (boxes.num_boxes)
814		thread->op->thread_boxes(thread->sna, thread->op,
815					 boxes.boxes, boxes.num_boxes);
816	RegionUninit(&mono.clip);
817}
818
819bool
820mono_trapezoids_span_converter(struct sna *sna,
821			       CARD8 op, PicturePtr src, PicturePtr dst,
822			       INT16 src_x, INT16 src_y,
823			       int ntrap, xTrapezoid *traps)
824{
825	struct mono mono;
826	BoxRec extents;
827	int16_t dst_x, dst_y;
828	int16_t dx, dy;
829	bool unbounded;
830	int num_threads, n;
831
832	if (NO_SCAN_CONVERTER)
833		return false;
834
835	trapezoid_origin(&traps[0].left, &dst_x, &dst_y);
836
837	if (!trapezoids_bounds(ntrap, traps, &extents))
838		return true;
839
840	DBG(("%s: extents (%d, %d), (%d, %d)\n",
841	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
842
843	if (!sna_compute_composite_region(&mono.clip,
844					  src, NULL, dst,
845					  src_x + extents.x1 - dst_x,
846					  src_y + extents.y1 - dst_y,
847					  0, 0,
848					  extents.x1, extents.y1,
849					  extents.x2 - extents.x1,
850					  extents.y2 - extents.y1)) {
851		DBG(("%s: trapezoids do not intersect drawable clips\n",
852		     __FUNCTION__)) ;
853		return true;
854	}
855
856	dx = dst->pDrawable->x;
857	dy = dst->pDrawable->y;
858
859	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
860	     __FUNCTION__,
861	     mono.clip.extents.x1, mono.clip.extents.y1,
862	     mono.clip.extents.x2, mono.clip.extents.y2,
863	     dx, dy,
864	     src_x + mono.clip.extents.x1 - dst_x - dx,
865	     src_y + mono.clip.extents.y1 - dst_y - dy));
866
867	unbounded = (!sna_drawable_is_clear(dst->pDrawable) &&
868		     !operator_is_bounded(op));
869
870	if (op == PictOpClear && sna->clear)
871		src = sna->clear;
872
873	mono.sna = sna;
874	if (!mono.sna->render.composite(mono.sna, op, src, NULL, dst,
875				       src_x + mono.clip.extents.x1 - dst_x - dx,
876				       src_y + mono.clip.extents.y1 - dst_y - dy,
877				       0, 0,
878				       mono.clip.extents.x1,  mono.clip.extents.y1,
879				       mono.clip.extents.x2 - mono.clip.extents.x1,
880				       mono.clip.extents.y2 - mono.clip.extents.y1,
881				       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op))))
882		return false;
883
884	num_threads = 1;
885	if (!NO_GPU_THREADS &&
886	    mono.op.thread_boxes &&
887	    mono.op.damage == NULL &&
888	    !unbounded)
889		num_threads = sna_use_threads(mono.clip.extents.x2 - mono.clip.extents.x1,
890					      mono.clip.extents.y2 - mono.clip.extents.y1,
891					      32);
892	if (num_threads > 1) {
893		struct mono_span_thread threads[num_threads];
894		int y, h;
895
896		DBG(("%s: using %d threads for mono span compositing %dx%d\n",
897		     __FUNCTION__, num_threads,
898		     mono.clip.extents.x2 - mono.clip.extents.x1,
899		     mono.clip.extents.y2 - mono.clip.extents.y1));
900
901		threads[0].sna = mono.sna;
902		threads[0].op = &mono.op;
903		threads[0].traps = traps;
904		threads[0].ntrap = ntrap;
905		threads[0].extents = mono.clip.extents;
906		threads[0].clip = &mono.clip;
907		threads[0].dx = dx;
908		threads[0].dy = dy;
909
910		y = extents.y1;
911		h = extents.y2 - extents.y1;
912		h = (h + num_threads - 1) / num_threads;
913		num_threads -= (num_threads-1) * h >= extents.y2 - extents.y1;
914
915		for (n = 1; n < num_threads; n++) {
916			threads[n] = threads[0];
917			threads[n].extents.y1 = y;
918			threads[n].extents.y2 = y += h;
919
920			sna_threads_run(n, mono_span_thread, &threads[n]);
921		}
922
923		threads[0].extents.y1 = y;
924		threads[0].extents.y2 = extents.y2;
925		mono_span_thread(&threads[0]);
926
927		sna_threads_wait();
928		mono.op.done(mono.sna, &mono.op);
929		return true;
930	}
931
932	if (!mono_init(&mono, 2*ntrap))
933		return false;
934
935	for (n = 0; n < ntrap; n++) {
936		if (!xTrapezoidValid(&traps[n]))
937			continue;
938
939		if (pixman_fixed_integer_floor(traps[n].top) + dy >= mono.clip.extents.y2 ||
940		    pixman_fixed_integer_ceil(traps[n].bottom) + dy <= mono.clip.extents.y1)
941			continue;
942
943		mono_add_line(&mono, dx, dy,
944			      traps[n].top, traps[n].bottom,
945			      &traps[n].left.p1, &traps[n].left.p2, 1);
946		mono_add_line(&mono, dx, dy,
947			      traps[n].top, traps[n].bottom,
948			      &traps[n].right.p1, &traps[n].right.p2, -1);
949	}
950
951	if (mono.clip.data == NULL && mono.op.damage == NULL)
952		mono.span = mono_span__fast;
953	else if (mono.clip.data != NULL && mono.op.damage == NULL)
954		mono.span = mono_span__clipped;
955	else
956		mono.span = mono_span;
957
958	region_get_boxes(&mono.clip, &mono.clip_start, &mono.clip_end);
959	mono_render(&mono);
960	mono.op.done(mono.sna, &mono.op);
961	mono_fini(&mono);
962
963	if (unbounded) {
964		xPointFixed p1, p2;
965
966		if (!mono_init(&mono, 2+2*ntrap))
967			return false;
968
969		p1.y = mono.clip.extents.y1 * pixman_fixed_1;
970		p2.y = mono.clip.extents.y2 * pixman_fixed_1;
971
972		p1.x = mono.clip.extents.x1 * pixman_fixed_1;
973		p2.x = mono.clip.extents.x1 * pixman_fixed_1;
974		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
975
976		p1.x = mono.clip.extents.x2 * pixman_fixed_1;
977		p2.x = mono.clip.extents.x2 * pixman_fixed_1;
978		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
979
980		for (n = 0; n < ntrap; n++) {
981			if (!xTrapezoidValid(&traps[n]))
982				continue;
983
984			if (pixman_fixed_to_int(traps[n].top) + dy >= mono.clip.extents.y2 ||
985			    pixman_fixed_to_int(traps[n].bottom) + dy < mono.clip.extents.y1)
986				continue;
987
988			mono_add_line(&mono, dx, dy,
989				      traps[n].top, traps[n].bottom,
990				      &traps[n].left.p1, &traps[n].left.p2, 1);
991			mono_add_line(&mono, dx, dy,
992				      traps[n].top, traps[n].bottom,
993				      &traps[n].right.p1, &traps[n].right.p2, -1);
994		}
995		if (mono.sna->render.composite(mono.sna,
996					       PictOpClear,
997					       mono.sna->clear, NULL, dst,
998					       0, 0,
999					       0, 0,
1000					       mono.clip.extents.x1,  mono.clip.extents.y1,
1001					       mono.clip.extents.x2 - mono.clip.extents.x1,
1002					       mono.clip.extents.y2 - mono.clip.extents.y1,
1003					       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1004			region_get_boxes(&mono.clip, &mono.clip_start, &mono.clip_end);
1005			mono_render(&mono);
1006			mono.op.done(mono.sna, &mono.op);
1007		}
1008		mono_fini(&mono);
1009	}
1010
1011	REGION_UNINIT(NULL, &mono.clip);
1012	return true;
1013}
1014
1015struct mono_inplace_composite {
1016	pixman_image_t *src, *dst;
1017	int dx, dy;
1018	int sx, sy;
1019	int op;
1020};
1021struct mono_inplace_fill {
1022	uint32_t *data, stride;
1023	uint32_t color;
1024	int bpp;
1025};
1026
1027fastcall static void
1028mono_inplace_fill_box(struct sna *sna,
1029		      const struct sna_composite_op *op,
1030		      const BoxRec *box)
1031{
1032	struct mono_inplace_fill *fill = op->priv;
1033
1034	DBG(("(%s: (%d, %d)x(%d, %d):%08x\n",
1035	     __FUNCTION__,
1036	     box->x1, box->y1,
1037	     box->x2 - box->x1,
1038	     box->y2 - box->y1,
1039	     fill->color));
1040	sigtrap_assert_active();
1041	pixman_fill(fill->data, fill->stride, fill->bpp,
1042		    box->x1, box->y1,
1043		    box->x2 - box->x1,
1044		    box->y2 - box->y1,
1045		    fill->color);
1046}
1047
1048static void
1049mono_inplace_fill_boxes(struct sna *sna,
1050			const struct sna_composite_op *op,
1051			const BoxRec *box, int nbox)
1052{
1053	struct mono_inplace_fill *fill = op->priv;
1054
1055	do {
1056		DBG(("(%s: (%d, %d)x(%d, %d):%08x\n",
1057		     __FUNCTION__,
1058		     box->x1, box->y1,
1059		     box->x2 - box->x1,
1060		     box->y2 - box->y1,
1061		     fill->color));
1062		sigtrap_assert_active();
1063		pixman_fill(fill->data, fill->stride, fill->bpp,
1064			    box->x1, box->y1,
1065			    box->x2 - box->x1,
1066			    box->y2 - box->y1,
1067			    fill->color);
1068		box++;
1069	} while (--nbox);
1070}
1071
1072fastcall static void
1073mono_inplace_composite_box(struct sna *sna,
1074			   const struct sna_composite_op *op,
1075			   const BoxRec *box)
1076{
1077	struct mono_inplace_composite *c = op->priv;
1078
1079	pixman_image_composite(c->op, c->src, NULL, c->dst,
1080			       box->x1 + c->sx, box->y1 + c->sy,
1081			       0, 0,
1082			       box->x1 + c->dx, box->y1 + c->dy,
1083			       box->x2 - box->x1,
1084			       box->y2 - box->y1);
1085}
1086
1087static void
1088mono_inplace_composite_boxes(struct sna *sna,
1089			     const struct sna_composite_op *op,
1090			     const BoxRec *box, int nbox)
1091{
1092	struct mono_inplace_composite *c = op->priv;
1093
1094	do {
1095		pixman_image_composite(c->op, c->src, NULL, c->dst,
1096				       box->x1 + c->sx, box->y1 + c->sy,
1097				       0, 0,
1098				       box->x1 + c->dx, box->y1 + c->dy,
1099				       box->x2 - box->x1,
1100				       box->y2 - box->y1);
1101		box++;
1102	} while (--nbox);
1103}
1104
1105bool
1106mono_trapezoid_span_inplace(struct sna *sna,
1107			    CARD8 op,
1108			    PicturePtr src,
1109			    PicturePtr dst,
1110			    INT16 src_x, INT16 src_y,
1111			    int ntrap, xTrapezoid *traps)
1112{
1113	struct mono mono;
1114	union {
1115		struct mono_inplace_fill fill;
1116		struct mono_inplace_composite composite;
1117	} inplace;
1118	int was_clear;
1119	int x, y, n;
1120
1121	if (!trapezoids_bounds(ntrap, traps, &mono.clip.extents))
1122		return true;
1123
1124	DBG(("%s: extents (%d, %d), (%d, %d)\n",
1125	     __FUNCTION__,
1126	     mono.clip.extents.x1, mono.clip.extents.y1,
1127	     mono.clip.extents.x2, mono.clip.extents.y2));
1128
1129	if (!sna_compute_composite_region(&mono.clip,
1130					  src, NULL, dst,
1131					  src_x, src_y,
1132					  0, 0,
1133					  mono.clip.extents.x1, mono.clip.extents.y1,
1134					  mono.clip.extents.x2 - mono.clip.extents.x1,
1135					  mono.clip.extents.y2 - mono.clip.extents.y1)) {
1136		DBG(("%s: trapezoids do not intersect drawable clips\n",
1137		     __FUNCTION__)) ;
1138		return true;
1139	}
1140
1141	DBG(("%s: clipped extents (%d, %d), (%d, %d)\n",
1142	     __FUNCTION__,
1143	     mono.clip.extents.x1, mono.clip.extents.y1,
1144	     mono.clip.extents.x2, mono.clip.extents.y2));
1145
1146	was_clear = sna_drawable_is_clear(dst->pDrawable);
1147	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &mono.clip,
1148					     MOVE_WRITE | MOVE_READ))
1149		return true;
1150
1151	mono.sna = sna;
1152	if (!mono_init(&mono, 2*ntrap))
1153		return false;
1154
1155	mono.op.damage = NULL;
1156
1157	x = dst->pDrawable->x;
1158	y = dst->pDrawable->y;
1159
1160	for (n = 0; n < ntrap; n++) {
1161		if (!xTrapezoidValid(&traps[n]))
1162			continue;
1163
1164		if (pixman_fixed_to_int(traps[n].top) + y >= mono.clip.extents.y2 ||
1165		    pixman_fixed_to_int(traps[n].bottom) + y < mono.clip.extents.y1)
1166			continue;
1167
1168		mono_add_line(&mono, x, y,
1169			      traps[n].top, traps[n].bottom,
1170			      &traps[n].left.p1, &traps[n].left.p2, 1);
1171		mono_add_line(&mono, x, y,
1172			      traps[n].top, traps[n].bottom,
1173			      &traps[n].right.p1, &traps[n].right.p2, -1);
1174	}
1175
1176	if (sna_picture_is_solid(src, &inplace.fill.color) &&
1177	    (op == PictOpSrc || op == PictOpClear ||
1178	     (was_clear && (op == PictOpOver || op == PictOpAdd)) ||
1179	     (op == PictOpOver && inplace.fill.color >> 24 == 0xff))) {
1180		PixmapPtr pixmap;
1181		int16_t dx, dy;
1182		uint8_t *ptr;
1183
1184unbounded_pass:
1185		pixmap = get_drawable_pixmap(dst->pDrawable);
1186
1187		ptr = pixmap->devPrivate.ptr;
1188		if (get_drawable_deltas(dst->pDrawable, pixmap, &dx, &dy))
1189			ptr += dy * pixmap->devKind + dx * pixmap->drawable.bitsPerPixel / 8;
1190		inplace.fill.data = (uint32_t *)ptr;
1191		inplace.fill.stride = pixmap->devKind / sizeof(uint32_t);
1192		inplace.fill.bpp = pixmap->drawable.bitsPerPixel;
1193
1194		if (op == PictOpClear)
1195			inplace.fill.color = 0;
1196		else if (dst->format != PICT_a8r8g8b8)
1197			inplace.fill.color = sna_rgba_to_color(inplace.fill.color, dst->format);
1198
1199		DBG(("%s: fill %x\n", __FUNCTION__, inplace.fill.color));
1200
1201		mono.op.priv = &inplace.fill;
1202		mono.op.box = mono_inplace_fill_box;
1203		mono.op.boxes = mono_inplace_fill_boxes;
1204
1205		op = 0;
1206	} else {
1207		if (src->pDrawable) {
1208			if (!sna_drawable_move_to_cpu(src->pDrawable,
1209						      MOVE_READ)) {
1210				mono_fini(&mono);
1211				return false;
1212			}
1213			if (src->alphaMap &&
1214			    !sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
1215						      MOVE_READ)) {
1216				mono_fini(&mono);
1217				return false;
1218			}
1219		}
1220
1221		inplace.composite.dst = image_from_pict(dst, false,
1222							&inplace.composite.dx,
1223							&inplace.composite.dy);
1224		inplace.composite.src = image_from_pict(src, false,
1225							&inplace.composite.sx,
1226							&inplace.composite.sy);
1227		inplace.composite.sx +=
1228			src_x - pixman_fixed_to_int(traps[0].left.p1.x),
1229		inplace.composite.sy +=
1230			src_y - pixman_fixed_to_int(traps[0].left.p1.y),
1231		inplace.composite.op = op;
1232
1233		mono.op.priv = &inplace.composite;
1234		mono.op.box = mono_inplace_composite_box;
1235		mono.op.boxes = mono_inplace_composite_boxes;
1236	}
1237
1238	if (mono.clip.data == NULL && mono.op.damage == NULL)
1239		mono.span = mono_span__fast;
1240	else
1241		mono.span = mono_span;
1242	if (sigtrap_get() == 0) {
1243		mono_render(&mono);
1244		sigtrap_put();
1245	}
1246	mono_fini(&mono);
1247
1248	if (op) {
1249		free_pixman_pict(src, inplace.composite.src);
1250		free_pixman_pict(dst, inplace.composite.dst);
1251
1252		if (!was_clear && !operator_is_bounded(op)) {
1253			xPointFixed p1, p2;
1254
1255			DBG(("%s: unbounded fixup\n", __FUNCTION__));
1256
1257			if (!mono_init(&mono, 2+2*ntrap))
1258				return false;
1259
1260			p1.y = mono.clip.extents.y1 * pixman_fixed_1;
1261			p2.y = mono.clip.extents.y2 * pixman_fixed_1;
1262
1263			p1.x = mono.clip.extents.x1 * pixman_fixed_1;
1264			p2.x = mono.clip.extents.x1 * pixman_fixed_1;
1265			mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
1266
1267			p1.x = mono.clip.extents.x2 * pixman_fixed_1;
1268			p2.x = mono.clip.extents.x2 * pixman_fixed_1;
1269			mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
1270
1271			for (n = 0; n < ntrap; n++) {
1272				if (!xTrapezoidValid(&traps[n]))
1273					continue;
1274
1275				if (pixman_fixed_to_int(traps[n].top) + x >= mono.clip.extents.y2 ||
1276				    pixman_fixed_to_int(traps[n].bottom) + y < mono.clip.extents.y1)
1277					continue;
1278
1279				mono_add_line(&mono, x, y,
1280					      traps[n].top, traps[n].bottom,
1281					      &traps[n].left.p1, &traps[n].left.p2, 1);
1282				mono_add_line(&mono, x, y,
1283					      traps[n].top, traps[n].bottom,
1284					      &traps[n].right.p1, &traps[n].right.p2, -1);
1285			}
1286
1287			op = PictOpClear;
1288			goto unbounded_pass;
1289		}
1290	}
1291
1292	return true;
1293}
1294
1295bool
1296mono_trap_span_converter(struct sna *sna,
1297			 PicturePtr dst,
1298			 INT16 x, INT16 y,
1299			 int ntrap, xTrap *traps)
1300{
1301	struct mono mono;
1302	xRenderColor white;
1303	PicturePtr src;
1304	int error;
1305	int n;
1306
1307	white.red = white.green = white.blue = white.alpha = 0xffff;
1308	src = CreateSolidPicture(0, &white, &error);
1309	if (src == NULL)
1310		return true;
1311
1312	mono.clip = *dst->pCompositeClip;
1313	x += dst->pDrawable->x;
1314	y += dst->pDrawable->y;
1315
1316	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d)\n",
1317	     __FUNCTION__,
1318	     mono.clip.extents.x1, mono.clip.extents.y1,
1319	     mono.clip.extents.x2, mono.clip.extents.y2,
1320	     x, y));
1321
1322	mono.sna = sna;
1323	if (!mono_init(&mono, 2*ntrap))
1324		return false;
1325
1326	for (n = 0; n < ntrap; n++) {
1327		xPointFixed p1, p2;
1328
1329		if (pixman_fixed_to_int(traps[n].top.y) + y >= mono.clip.extents.y2 ||
1330		    pixman_fixed_to_int(traps[n].bot.y) + y < mono.clip.extents.y1)
1331			continue;
1332
1333		p1.y = traps[n].top.y;
1334		p2.y = traps[n].bot.y;
1335
1336		p1.x = traps[n].top.l;
1337		p2.x = traps[n].bot.l;
1338		mono_add_line(&mono, x, y,
1339			      traps[n].top.y, traps[n].bot.y,
1340			      &p1, &p2, 1);
1341
1342		p1.x = traps[n].top.r;
1343		p2.x = traps[n].bot.r;
1344		mono_add_line(&mono, x, y,
1345			      traps[n].top.y, traps[n].bot.y,
1346			      &p1, &p2, -1);
1347	}
1348
1349	if (mono.sna->render.composite(mono.sna, PictOpAdd, src, NULL, dst,
1350				       0, 0,
1351				       0, 0,
1352				       mono.clip.extents.x1,  mono.clip.extents.y1,
1353				       mono.clip.extents.x2 - mono.clip.extents.x1,
1354				       mono.clip.extents.y2 - mono.clip.extents.y1,
1355				       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1356		if (mono.clip.data == NULL && mono.op.damage == NULL)
1357			mono.span = mono_span__fast;
1358		else
1359			mono.span = mono_span;
1360		mono_render(&mono);
1361		mono.op.done(mono.sna, &mono.op);
1362	}
1363
1364	mono_fini(&mono);
1365	FreePicture(src, 0);
1366	return true;
1367}
1368
1369bool
1370mono_triangles_span_converter(struct sna *sna,
1371			      CARD8 op, PicturePtr src, PicturePtr dst,
1372			      INT16 src_x, INT16 src_y,
1373			      int count, xTriangle *tri)
1374{
1375	struct mono mono;
1376	BoxRec extents;
1377	int16_t dst_x, dst_y;
1378	int16_t dx, dy;
1379	bool was_clear;
1380	int n;
1381
1382	mono.sna = sna;
1383
1384	dst_x = pixman_fixed_to_int(tri[0].p1.x);
1385	dst_y = pixman_fixed_to_int(tri[0].p1.y);
1386
1387	miTriangleBounds(count, tri, &extents);
1388	DBG(("%s: extents (%d, %d), (%d, %d)\n",
1389	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
1390
1391	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
1392		return true;
1393
1394	if (!sna_compute_composite_region(&mono.clip,
1395					  src, NULL, dst,
1396					  src_x + extents.x1 - dst_x,
1397					  src_y + extents.y1 - dst_y,
1398					  0, 0,
1399					  extents.x1, extents.y1,
1400					  extents.x2 - extents.x1,
1401					  extents.y2 - extents.y1)) {
1402		DBG(("%s: triangles do not intersect drawable clips\n",
1403		     __FUNCTION__)) ;
1404		return true;
1405	}
1406
1407	dx = dst->pDrawable->x;
1408	dy = dst->pDrawable->y;
1409
1410	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
1411	     __FUNCTION__,
1412	     mono.clip.extents.x1, mono.clip.extents.y1,
1413	     mono.clip.extents.x2, mono.clip.extents.y2,
1414	     dx, dy,
1415	     src_x + mono.clip.extents.x1 - dst_x - dx,
1416	     src_y + mono.clip.extents.y1 - dst_y - dy));
1417
1418	was_clear = sna_drawable_is_clear(dst->pDrawable);
1419
1420	if (!mono_init(&mono, 3*count))
1421		return false;
1422
1423	for (n = 0; n < count; n++) {
1424		mono_add_line(&mono, dx, dy,
1425			      tri[n].p1.y, tri[n].p2.y,
1426			      &tri[n].p1, &tri[n].p2, 1);
1427		mono_add_line(&mono, dx, dy,
1428			      tri[n].p2.y, tri[n].p3.y,
1429			      &tri[n].p2, &tri[n].p3, 1);
1430		mono_add_line(&mono, dx, dy,
1431			      tri[n].p3.y, tri[n].p1.y,
1432			      &tri[n].p3, &tri[n].p1, 1);
1433	}
1434
1435	if (mono.sna->render.composite(mono.sna, op, src, NULL, dst,
1436				       src_x + mono.clip.extents.x1 - dst_x - dx,
1437				       src_y + mono.clip.extents.y1 - dst_y - dy,
1438				       0, 0,
1439				       mono.clip.extents.x1,  mono.clip.extents.y1,
1440				       mono.clip.extents.x2 - mono.clip.extents.x1,
1441				       mono.clip.extents.y2 - mono.clip.extents.y1,
1442				       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1443		if (mono.clip.data == NULL && mono.op.damage == NULL)
1444			mono.span = mono_span__fast;
1445		else
1446			mono.span = mono_span;
1447		mono_render(&mono);
1448		mono.op.done(mono.sna, &mono.op);
1449	}
1450	mono_fini(&mono);
1451
1452	if (!was_clear && !operator_is_bounded(op)) {
1453		xPointFixed p1, p2;
1454
1455		DBG(("%s: performing unbounded clear\n", __FUNCTION__));
1456
1457		if (!mono_init(&mono, 2+3*count))
1458			return false;
1459
1460		p1.y = mono.clip.extents.y1 * pixman_fixed_1;
1461		p2.y = mono.clip.extents.y2 * pixman_fixed_1;
1462
1463		p1.x = mono.clip.extents.x1 * pixman_fixed_1;
1464		p2.x = mono.clip.extents.x1 * pixman_fixed_1;
1465		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
1466
1467		p1.x = mono.clip.extents.x2 * pixman_fixed_1;
1468		p2.x = mono.clip.extents.x2 * pixman_fixed_1;
1469		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
1470
1471		for (n = 0; n < count; n++) {
1472			mono_add_line(&mono, dx, dy,
1473				      tri[n].p1.y, tri[n].p2.y,
1474				      &tri[n].p1, &tri[n].p2, 1);
1475			mono_add_line(&mono, dx, dy,
1476				      tri[n].p2.y, tri[n].p3.y,
1477				      &tri[n].p2, &tri[n].p3, 1);
1478			mono_add_line(&mono, dx, dy,
1479				      tri[n].p3.y, tri[n].p1.y,
1480				      &tri[n].p3, &tri[n].p1, 1);
1481		}
1482
1483		if (mono.sna->render.composite(mono.sna,
1484					       PictOpClear,
1485					       mono.sna->clear, NULL, dst,
1486					       0, 0,
1487					       0, 0,
1488					       mono.clip.extents.x1,  mono.clip.extents.y1,
1489					       mono.clip.extents.x2 - mono.clip.extents.x1,
1490					       mono.clip.extents.y2 - mono.clip.extents.y1,
1491					       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1492			if (mono.clip.data == NULL && mono.op.damage == NULL)
1493				mono.span = mono_span__fast;
1494			else
1495				mono.span = mono_span;
1496			mono_render(&mono);
1497			mono.op.done(mono.sna, &mono.op);
1498		}
1499		mono_fini(&mono);
1500	}
1501
1502	REGION_UNINIT(NULL, &mono.clip);
1503	return true;
1504}
1505
1506bool
1507mono_tristrip_span_converter(struct sna *sna,
1508			     CARD8 op, PicturePtr src, PicturePtr dst,
1509			     INT16 src_x, INT16 src_y,
1510			     int count, xPointFixed *points)
1511{
1512	struct mono mono;
1513	BoxRec extents;
1514	int16_t dst_x, dst_y;
1515	int16_t dx, dy;
1516	bool was_clear;
1517	int n;
1518
1519	mono.sna = sna;
1520
1521	dst_x = pixman_fixed_to_int(points[0].x);
1522	dst_y = pixman_fixed_to_int(points[0].y);
1523
1524	miPointFixedBounds(count, points, &extents);
1525	DBG(("%s: extents (%d, %d), (%d, %d)\n",
1526	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
1527
1528	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
1529		return true;
1530
1531	if (!sna_compute_composite_region(&mono.clip,
1532					  src, NULL, dst,
1533					  src_x + extents.x1 - dst_x,
1534					  src_y + extents.y1 - dst_y,
1535					  0, 0,
1536					  extents.x1, extents.y1,
1537					  extents.x2 - extents.x1,
1538					  extents.y2 - extents.y1)) {
1539		DBG(("%s: triangles do not intersect drawable clips\n",
1540		     __FUNCTION__)) ;
1541		return true;
1542	}
1543
1544	dx = dst->pDrawable->x;
1545	dy = dst->pDrawable->y;
1546
1547	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
1548	     __FUNCTION__,
1549	     mono.clip.extents.x1, mono.clip.extents.y1,
1550	     mono.clip.extents.x2, mono.clip.extents.y2,
1551	     dx, dy,
1552	     src_x + mono.clip.extents.x1 - dst_x - dx,
1553	     src_y + mono.clip.extents.y1 - dst_y - dy));
1554
1555	was_clear = sna_drawable_is_clear(dst->pDrawable);
1556
1557	if (!mono_init(&mono, 2*count))
1558		return false;
1559
1560	mono_add_line(&mono, dx, dy,
1561		      points[0].y, points[1].y,
1562		      &points[0], &points[1], -1);
1563	n = 2;
1564	do {
1565		mono_add_line(&mono, dx, dy,
1566			      points[n-2].y, points[n].y,
1567			      &points[n-2], &points[n], 1);
1568		if (++n == count)
1569			break;
1570
1571		mono_add_line(&mono, dx, dy,
1572			      points[n-2].y, points[n].y,
1573			      &points[n-2], &points[n], -1);
1574		if (++n == count)
1575			break;
1576	} while (1);
1577	mono_add_line(&mono, dx, dy,
1578		      points[n-2].y, points[n-1].y,
1579		      &points[n-2], &points[n-1], 1);
1580
1581	if (mono.sna->render.composite(mono.sna, op, src, NULL, dst,
1582				       src_x + mono.clip.extents.x1 - dst_x - dx,
1583				       src_y + mono.clip.extents.y1 - dst_y - dy,
1584				       0, 0,
1585				       mono.clip.extents.x1,  mono.clip.extents.y1,
1586				       mono.clip.extents.x2 - mono.clip.extents.x1,
1587				       mono.clip.extents.y2 - mono.clip.extents.y1,
1588				       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1589		if (mono.clip.data == NULL && mono.op.damage == NULL)
1590			mono.span = mono_span__fast;
1591		else
1592			mono.span = mono_span;
1593		mono_render(&mono);
1594		mono.op.done(mono.sna, &mono.op);
1595	}
1596
1597	if (!was_clear && !operator_is_bounded(op)) {
1598		xPointFixed p1, p2;
1599
1600		if (!mono_init(&mono, 2+2*count))
1601			return false;
1602
1603		p1.y = mono.clip.extents.y1 * pixman_fixed_1;
1604		p2.y = mono.clip.extents.y2 * pixman_fixed_1;
1605
1606		p1.x = mono.clip.extents.x1 * pixman_fixed_1;
1607		p2.x = mono.clip.extents.x1 * pixman_fixed_1;
1608		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
1609
1610		p1.x = mono.clip.extents.x2 * pixman_fixed_1;
1611		p2.x = mono.clip.extents.x2 * pixman_fixed_1;
1612		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
1613
1614		mono_add_line(&mono, dx, dy,
1615			      points[0].y, points[1].y,
1616			      &points[0], &points[1], -1);
1617		n = 2;
1618		do {
1619			mono_add_line(&mono, dx, dy,
1620				      points[n-2].y, points[n].y,
1621				      &points[n-2], &points[n], 1);
1622			if (++n == count)
1623				break;
1624
1625			mono_add_line(&mono, dx, dy,
1626				      points[n-2].y, points[n].y,
1627				      &points[n-2], &points[n], -1);
1628			if (++n == count)
1629				break;
1630		} while (1);
1631		mono_add_line(&mono, dx, dy,
1632			      points[n-2].y, points[n-1].y,
1633			      &points[n-2], &points[n-1], 1);
1634
1635		if (mono.sna->render.composite(mono.sna,
1636					       PictOpClear,
1637					       mono.sna->clear, NULL, dst,
1638					       0, 0,
1639					       0, 0,
1640					       mono.clip.extents.x1,  mono.clip.extents.y1,
1641					       mono.clip.extents.x2 - mono.clip.extents.x1,
1642					       mono.clip.extents.y2 - mono.clip.extents.y1,
1643					       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1644			if (mono.clip.data == NULL && mono.op.damage == NULL)
1645				mono.span = mono_span__fast;
1646			else
1647				mono.span = mono_span;
1648			mono_render(&mono);
1649			mono.op.done(mono.sna, &mono.op);
1650		}
1651		mono_fini(&mono);
1652	}
1653
1654	mono_fini(&mono);
1655	REGION_UNINIT(NULL, &mono.clip);
1656	return true;
1657}
1658