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