1428d7b3dSmrg/*
2428d7b3dSmrg * Copyright (c) 2007  David Turner
3428d7b3dSmrg * Copyright (c) 2008  M Joonas Pihlaja
4428d7b3dSmrg * Copyright (c) 2011 Intel Corporation
5428d7b3dSmrg *
6428d7b3dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
7428d7b3dSmrg * copy of this software and associated documentation files (the "Software"),
8428d7b3dSmrg * to deal in the Software without restriction, including without limitation
9428d7b3dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10428d7b3dSmrg * and/or sell copies of the Software, and to permit persons to whom the
11428d7b3dSmrg * Software is furnished to do so, subject to the following conditions:
12428d7b3dSmrg *
13428d7b3dSmrg * The above copyright notice and this permission notice (including the next
14428d7b3dSmrg * paragraph) shall be included in all copies or substantial portions of the
15428d7b3dSmrg * Software.
16428d7b3dSmrg *
17428d7b3dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18428d7b3dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19428d7b3dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20428d7b3dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21428d7b3dSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22428d7b3dSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23428d7b3dSmrg * SOFTWARE.
24428d7b3dSmrg *
25428d7b3dSmrg * Authors:
26428d7b3dSmrg *    Chris Wilson <chris@chris-wilson.co.uk>
27428d7b3dSmrg *
28428d7b3dSmrg */
29428d7b3dSmrg
30428d7b3dSmrg#ifdef HAVE_CONFIG_H
31428d7b3dSmrg#include "config.h"
32428d7b3dSmrg#endif
33428d7b3dSmrg
34428d7b3dSmrg#include "sna.h"
35428d7b3dSmrg#include "sna_render.h"
36428d7b3dSmrg#include "sna_render_inline.h"
37428d7b3dSmrg#include "sna_trapezoids.h"
38428d7b3dSmrg#include "fb/fbpict.h"
39428d7b3dSmrg
40428d7b3dSmrg#include <mipict.h>
41428d7b3dSmrg
42428d7b3dSmrgstruct quorem {
43428d7b3dSmrg	int32_t quo;
44428d7b3dSmrg	int64_t rem;
45428d7b3dSmrg};
46428d7b3dSmrg
47428d7b3dSmrgstruct mono_edge {
48428d7b3dSmrg	struct mono_edge *next, *prev;
49428d7b3dSmrg
50428d7b3dSmrg	int32_t height_left;
51428d7b3dSmrg	int32_t dir;
52428d7b3dSmrg
53428d7b3dSmrg	int64_t dy;
54428d7b3dSmrg	struct quorem x;
55428d7b3dSmrg	struct quorem dxdy;
56428d7b3dSmrg};
57428d7b3dSmrg
58428d7b3dSmrgstruct mono_polygon {
59428d7b3dSmrg	int num_edges;
60428d7b3dSmrg	struct mono_edge *edges;
61428d7b3dSmrg	struct mono_edge **y_buckets;
62428d7b3dSmrg
63428d7b3dSmrg	struct mono_edge *y_buckets_embedded[64];
64428d7b3dSmrg	struct mono_edge edges_embedded[32];
65428d7b3dSmrg};
66428d7b3dSmrg
67428d7b3dSmrgstruct mono {
68428d7b3dSmrg	/* Leftmost edge on the current scan line. */
69428d7b3dSmrg	struct mono_edge head, tail;
70428d7b3dSmrg	int is_vertical;
71428d7b3dSmrg
72428d7b3dSmrg	struct sna *sna;
73428d7b3dSmrg	struct sna_composite_op op;
74428d7b3dSmrg	pixman_region16_t clip;
75428d7b3dSmrg
76428d7b3dSmrg	fastcall void (*span)(struct mono *, int, int, BoxPtr);
77428d7b3dSmrg
78428d7b3dSmrg	struct mono_polygon polygon;
79428d7b3dSmrg};
80428d7b3dSmrg
81428d7b3dSmrg#define I(x) pixman_fixed_to_int ((x) + pixman_fixed_1_minus_e/2)
82428d7b3dSmrg
83428d7b3dSmrgstatic struct quorem
84428d7b3dSmrgfloored_muldivrem(int32_t x, int32_t a, int32_t b)
85428d7b3dSmrg{
86428d7b3dSmrg	struct quorem qr;
87428d7b3dSmrg	int64_t xa = (int64_t)x*a;
88428d7b3dSmrg	qr.quo = xa/b;
89428d7b3dSmrg	qr.rem = xa%b;
90428d7b3dSmrg	if (qr.rem < 0) {
91428d7b3dSmrg		qr.quo -= 1;
92428d7b3dSmrg		qr.rem += b;
93428d7b3dSmrg	}
94428d7b3dSmrg	return qr;
95428d7b3dSmrg}
96428d7b3dSmrg
97428d7b3dSmrg#if HAS_DEBUG_FULL
98428d7b3dSmrgstatic void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function)
99428d7b3dSmrg{
100428d7b3dSmrg	if (box->x1 < 0 || box->y1 < 0 ||
101428d7b3dSmrg	    box->x2 > pixmap->drawable.width ||
102428d7b3dSmrg	    box->y2 > pixmap->drawable.height)
103428d7b3dSmrg		FatalError("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n",
104428d7b3dSmrg			   __FUNCTION__,
105428d7b3dSmrg			   box->x1, box->y1, box->x2, box->y2,
106428d7b3dSmrg			   pixmap->drawable.width,
107428d7b3dSmrg			   pixmap->drawable.height);
108428d7b3dSmrg}
109428d7b3dSmrg#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__)
110428d7b3dSmrg#else
111428d7b3dSmrg#define assert_pixmap_contains_box(p, b)
112428d7b3dSmrg#endif
113428d7b3dSmrg
114428d7b3dSmrgstatic void apply_damage(struct sna_composite_op *op, RegionPtr region)
115428d7b3dSmrg{
116428d7b3dSmrg	DBG(("%s: damage=%p, region=%dx[(%d, %d), (%d, %d)]\n",
117428d7b3dSmrg	     __FUNCTION__, op->damage,
118428d7b3dSmrg	     region_num_rects(region),
119428d7b3dSmrg	     region->extents.x1, region->extents.y1,
120428d7b3dSmrg	     region->extents.x2, region->extents.y2));
121428d7b3dSmrg
122428d7b3dSmrg	if (op->damage == NULL)
123428d7b3dSmrg		return;
124428d7b3dSmrg
125428d7b3dSmrg	RegionTranslate(region, op->dst.x, op->dst.y);
126428d7b3dSmrg
127428d7b3dSmrg	assert_pixmap_contains_box(op->dst.pixmap, RegionExtents(region));
128428d7b3dSmrg	sna_damage_add(op->damage, region);
129428d7b3dSmrg}
130428d7b3dSmrg
131428d7b3dSmrgstatic void _apply_damage_box(struct sna_composite_op *op, const BoxRec *box)
132428d7b3dSmrg{
133428d7b3dSmrg	BoxRec r;
134428d7b3dSmrg
135428d7b3dSmrg	r.x1 = box->x1 + op->dst.x;
136428d7b3dSmrg	r.x2 = box->x2 + op->dst.x;
137428d7b3dSmrg	r.y1 = box->y1 + op->dst.y;
138428d7b3dSmrg	r.y2 = box->y2 + op->dst.y;
139428d7b3dSmrg
140428d7b3dSmrg	assert_pixmap_contains_box(op->dst.pixmap, &r);
141428d7b3dSmrg	sna_damage_add_box(op->damage, &r);
142428d7b3dSmrg}
143428d7b3dSmrg
144428d7b3dSmrginline static void apply_damage_box(struct sna_composite_op *op, const BoxRec *box)
145428d7b3dSmrg{
146428d7b3dSmrg	if (op->damage)
147428d7b3dSmrg		_apply_damage_box(op, box);
148428d7b3dSmrg}
149428d7b3dSmrg
150428d7b3dSmrgstatic bool
151428d7b3dSmrgmono_polygon_init(struct mono_polygon *polygon, BoxPtr box, int num_edges)
152428d7b3dSmrg{
153428d7b3dSmrg	unsigned h = box->y2 - box->y1;
154428d7b3dSmrg
155428d7b3dSmrg	polygon->y_buckets = polygon->y_buckets_embedded;
156428d7b3dSmrg	if (h > ARRAY_SIZE (polygon->y_buckets_embedded)) {
157428d7b3dSmrg		polygon->y_buckets = malloc (h * sizeof (struct mono_edge *));
158428d7b3dSmrg		if (unlikely (NULL == polygon->y_buckets))
159428d7b3dSmrg			return false;
160428d7b3dSmrg	}
161428d7b3dSmrg
162428d7b3dSmrg	polygon->num_edges = 0;
163428d7b3dSmrg	polygon->edges = polygon->edges_embedded;
164428d7b3dSmrg	if (num_edges > (int)ARRAY_SIZE (polygon->edges_embedded)) {
165428d7b3dSmrg		polygon->edges = malloc (num_edges * sizeof (struct mono_edge));
166428d7b3dSmrg		if (unlikely (polygon->edges == NULL)) {
167428d7b3dSmrg			if (polygon->y_buckets != polygon->y_buckets_embedded)
168428d7b3dSmrg				free(polygon->y_buckets);
169428d7b3dSmrg			return false;
170428d7b3dSmrg		}
171428d7b3dSmrg	}
172428d7b3dSmrg
173428d7b3dSmrg	memset(polygon->y_buckets, 0, h * sizeof (struct edge *));
174428d7b3dSmrg	return true;
175428d7b3dSmrg}
176428d7b3dSmrg
177428d7b3dSmrgstatic void
178428d7b3dSmrgmono_polygon_fini(struct mono_polygon *polygon)
179428d7b3dSmrg{
180428d7b3dSmrg	if (polygon->y_buckets != polygon->y_buckets_embedded)
181428d7b3dSmrg		free(polygon->y_buckets);
182428d7b3dSmrg
183428d7b3dSmrg	if (polygon->edges != polygon->edges_embedded)
184428d7b3dSmrg		free(polygon->edges);
185428d7b3dSmrg}
186428d7b3dSmrg
187428d7b3dSmrgstatic void
188428d7b3dSmrgmono_add_line(struct mono *mono,
189428d7b3dSmrg	      int dst_x, int dst_y,
190428d7b3dSmrg	      xFixed top, xFixed bottom,
191428d7b3dSmrg	      const xPointFixed *p1, const xPointFixed *p2,
192428d7b3dSmrg	      int dir)
193428d7b3dSmrg{
194428d7b3dSmrg	struct mono_polygon *polygon = &mono->polygon;
195428d7b3dSmrg	struct mono_edge *e;
196428d7b3dSmrg	int y, ytop, ybot;
197428d7b3dSmrg
198428d7b3dSmrg	__DBG(("%s: top=%d, bottom=%d, line=(%d, %d), (%d, %d) delta=%dx%d, dir=%d\n",
199428d7b3dSmrg	       __FUNCTION__,
200428d7b3dSmrg	       (int)top, (int)bottom,
201428d7b3dSmrg	       (int)p1->x, (int)p1->y, (int)p2->x, (int)p2->y,
202428d7b3dSmrg	       dst_x, dst_y,
203428d7b3dSmrg	       dir));
204428d7b3dSmrg
205428d7b3dSmrg	if (top > bottom) {
206428d7b3dSmrg		const xPointFixed *t;
207428d7b3dSmrg
208428d7b3dSmrg		y = top;
209428d7b3dSmrg		top = bottom;
210428d7b3dSmrg		bottom = y;
211428d7b3dSmrg
212428d7b3dSmrg		t = p1;
213428d7b3dSmrg		p1 = p2;
214428d7b3dSmrg		p2 = t;
215428d7b3dSmrg
216428d7b3dSmrg		dir = -dir;
217428d7b3dSmrg	}
218428d7b3dSmrg
219428d7b3dSmrg	y = I(top) + dst_y;
220428d7b3dSmrg	ytop = MAX(y, mono->clip.extents.y1);
221428d7b3dSmrg
222428d7b3dSmrg	y = I(bottom) + dst_y;
223428d7b3dSmrg	ybot = MIN(y, mono->clip.extents.y2);
224428d7b3dSmrg
225428d7b3dSmrg	__DBG(("%s: edge height [%d, %d] = %d\n",
226428d7b3dSmrg	       __FUNCTION__, ytop, ybot, ybot - ytop));
227428d7b3dSmrg	if (ybot <= ytop) {
228428d7b3dSmrg		__DBG(("discard clipped line\n"));
229428d7b3dSmrg		return;
230428d7b3dSmrg	}
231428d7b3dSmrg
232428d7b3dSmrg	e = polygon->edges + polygon->num_edges++;
233428d7b3dSmrg	e->height_left = ybot - ytop;
234428d7b3dSmrg	e->dir = dir;
235428d7b3dSmrg
236428d7b3dSmrg	if (I(p1->x) == I(p2->x)) {
237428d7b3dSmrg		__DBG(("%s: vertical edge x:%d\n", __FUNCTION__, I(p1->x)));
238428d7b3dSmrg		e->x.quo = p1->x;
239428d7b3dSmrg		e->x.rem = 0;
240428d7b3dSmrg		e->dxdy.quo = 0;
241428d7b3dSmrg		e->dxdy.rem = 0;
242428d7b3dSmrg		e->dy = 0;
243428d7b3dSmrg	} else {
244428d7b3dSmrg		int64_t dx = (int64_t)p2->x - p1->x;
245428d7b3dSmrg		int64_t dy = (int64_t)p2->y - p1->y;
246428d7b3dSmrg
247428d7b3dSmrg		__DBG(("%s: diagonal edge (%d, %d), x:[%d, %d]\n", __FUNCTION__, dx, dy, I(p1->x), I(p2->x)));
248428d7b3dSmrg		assert(dy > 0);
249428d7b3dSmrg
250428d7b3dSmrg		e->dxdy = floored_muldivrem(dx, pixman_fixed_1, dy);
251428d7b3dSmrg
252428d7b3dSmrg		e->x = floored_muldivrem((ytop - dst_y) * pixman_fixed_1 + pixman_fixed_1_minus_e/2 - p1->y,
253428d7b3dSmrg					 dx, dy);
254428d7b3dSmrg		e->x.quo += p1->x;
255428d7b3dSmrg		e->x.rem -= dy;
256428d7b3dSmrg
257428d7b3dSmrg		e->dy = dy;
258428d7b3dSmrg
259428d7b3dSmrg		__DBG(("%s: initial x=%d [%d.%d/%d] + dxdy=%d.%d/%d\n",
260428d7b3dSmrg		       __FUNCTION__,
261428d7b3dSmrg		       I(e->x.quo), e->x.quo, e->x.rem, e->dy,
262428d7b3dSmrg		       e->dxdy.quo, e->dxdy.rem, e->dy));
263428d7b3dSmrg	}
264428d7b3dSmrg	e->x.quo += dst_x*pixman_fixed_1;
265428d7b3dSmrg
266428d7b3dSmrg	{
267428d7b3dSmrg		struct mono_edge **ptail = &polygon->y_buckets[ytop - mono->clip.extents.y1];
268428d7b3dSmrg		if (*ptail)
269428d7b3dSmrg			(*ptail)->prev = e;
270428d7b3dSmrg		e->next = *ptail;
271428d7b3dSmrg		e->prev = NULL;
272428d7b3dSmrg		*ptail = e;
273428d7b3dSmrg	}
274428d7b3dSmrg}
275428d7b3dSmrg
276428d7b3dSmrgstatic struct mono_edge *
277428d7b3dSmrgmono_merge_sorted_edges(struct mono_edge *head_a, struct mono_edge *head_b)
278428d7b3dSmrg{
279428d7b3dSmrg	struct mono_edge *head, **next, *prev;
280428d7b3dSmrg	int32_t x;
281428d7b3dSmrg
282428d7b3dSmrg	if (head_b == NULL)
283428d7b3dSmrg		return head_a;
284428d7b3dSmrg
285428d7b3dSmrg	prev = head_a->prev;
286428d7b3dSmrg	next = &head;
287428d7b3dSmrg	if (head_a->x.quo <= head_b->x.quo) {
288428d7b3dSmrg		head = head_a;
289428d7b3dSmrg	} else {
290428d7b3dSmrg		head = head_b;
291428d7b3dSmrg		head_b->prev = prev;
292428d7b3dSmrg		goto start_with_b;
293428d7b3dSmrg	}
294428d7b3dSmrg
295428d7b3dSmrg	do {
296428d7b3dSmrg		x = head_b->x.quo;
297428d7b3dSmrg		while (head_a != NULL && head_a->x.quo <= x) {
298428d7b3dSmrg			prev = head_a;
299428d7b3dSmrg			next = &head_a->next;
300428d7b3dSmrg			head_a = head_a->next;
301428d7b3dSmrg		}
302428d7b3dSmrg
303428d7b3dSmrg		head_b->prev = prev;
304428d7b3dSmrg		*next = head_b;
305428d7b3dSmrg		if (head_a == NULL)
306428d7b3dSmrg			return head;
307428d7b3dSmrg
308428d7b3dSmrgstart_with_b:
309428d7b3dSmrg		x = head_a->x.quo;
310428d7b3dSmrg		while (head_b != NULL && head_b->x.quo <= x) {
311428d7b3dSmrg			prev = head_b;
312428d7b3dSmrg			next = &head_b->next;
313428d7b3dSmrg			head_b = head_b->next;
314428d7b3dSmrg		}
315428d7b3dSmrg
316428d7b3dSmrg		head_a->prev = prev;
317428d7b3dSmrg		*next = head_a;
318428d7b3dSmrg		if (head_b == NULL)
319428d7b3dSmrg			return head;
320428d7b3dSmrg	} while (1);
321428d7b3dSmrg}
322428d7b3dSmrg
323428d7b3dSmrgstatic struct mono_edge *
324428d7b3dSmrgmono_sort_edges(struct mono_edge *list,
325428d7b3dSmrg		unsigned int level,
326428d7b3dSmrg		struct mono_edge **head_out)
327428d7b3dSmrg{
328428d7b3dSmrg	struct mono_edge *head_other, *remaining;
329428d7b3dSmrg	unsigned int i;
330428d7b3dSmrg
331428d7b3dSmrg	head_other = list->next;
332428d7b3dSmrg
333428d7b3dSmrg	if (head_other == NULL) {
334428d7b3dSmrg		*head_out = list;
335428d7b3dSmrg		return NULL;
336428d7b3dSmrg	}
337428d7b3dSmrg
338428d7b3dSmrg	remaining = head_other->next;
339428d7b3dSmrg	if (list->x.quo <= head_other->x.quo) {
340428d7b3dSmrg		*head_out = list;
341428d7b3dSmrg		head_other->next = NULL;
342428d7b3dSmrg	} else {
343428d7b3dSmrg		*head_out = head_other;
344428d7b3dSmrg		head_other->prev = list->prev;
345428d7b3dSmrg		head_other->next = list;
346428d7b3dSmrg		list->prev = head_other;
347428d7b3dSmrg		list->next = NULL;
348428d7b3dSmrg	}
349428d7b3dSmrg
350428d7b3dSmrg	for (i = 0; i < level && remaining; i++) {
351428d7b3dSmrg		remaining = mono_sort_edges(remaining, i, &head_other);
352428d7b3dSmrg		*head_out = mono_merge_sorted_edges(*head_out, head_other);
353428d7b3dSmrg	}
354428d7b3dSmrg
355428d7b3dSmrg	return remaining;
356428d7b3dSmrg}
357428d7b3dSmrg
358428d7b3dSmrgstatic struct mono_edge *mono_filter(struct mono_edge *edges)
359428d7b3dSmrg{
360428d7b3dSmrg	struct mono_edge *e;
361428d7b3dSmrg
362428d7b3dSmrg	e = edges;
363428d7b3dSmrg	while (e->next) {
364428d7b3dSmrg		struct mono_edge *n = e->next;
365428d7b3dSmrg		if (e->dir == -n->dir &&
366428d7b3dSmrg		    e->height_left == n->height_left &&
367428d7b3dSmrg		    e->x.quo == n->x.quo &&
368428d7b3dSmrg		    e->x.rem == n->x.rem &&
369428d7b3dSmrg		    e->dxdy.quo == n->dxdy.quo &&
370428d7b3dSmrg		    e->dxdy.rem == n->dxdy.rem) {
371428d7b3dSmrg			if (e->prev)
372428d7b3dSmrg				e->prev->next = n->next;
373428d7b3dSmrg			else
374428d7b3dSmrg				edges = n->next;
375428d7b3dSmrg			if (n->next)
376428d7b3dSmrg				n->next->prev = e->prev;
377428d7b3dSmrg			else
378428d7b3dSmrg				break;
379428d7b3dSmrg
380428d7b3dSmrg			e = n->next;
381428d7b3dSmrg		} else
382428d7b3dSmrg			e = n;
383428d7b3dSmrg	}
384428d7b3dSmrg
385428d7b3dSmrg	return edges;
386428d7b3dSmrg}
387428d7b3dSmrg
388428d7b3dSmrgstatic struct mono_edge *
389428d7b3dSmrgmono_merge_unsorted_edges(struct mono_edge *head, struct mono_edge *unsorted)
390428d7b3dSmrg{
391428d7b3dSmrg	mono_sort_edges(unsorted, UINT_MAX, &unsorted);
392428d7b3dSmrg	return mono_merge_sorted_edges(head, mono_filter(unsorted));
393428d7b3dSmrg}
394428d7b3dSmrg
395428d7b3dSmrg#if 0
396428d7b3dSmrgstatic inline void
397428d7b3dSmrg__dbg_mono_edges(const char *function, struct mono_edge *edges)
398428d7b3dSmrg{
399428d7b3dSmrg	DBG(("%s: ", function));
400428d7b3dSmrg	while (edges) {
401428d7b3dSmrg		if (edges->x.quo < INT16_MAX << 16) {
402428d7b3dSmrg			DBG(("(%d.%06d)+(%d.%06d)x%d, ",
403428d7b3dSmrg			     edges->x.quo, edges->x.rem,
404428d7b3dSmrg			     edges->dxdy.quo, edges->dxdy.rem,
405428d7b3dSmrg			     edges->dy*edges->dir));
406428d7b3dSmrg		}
407428d7b3dSmrg		edges = edges->next;
408428d7b3dSmrg	}
409428d7b3dSmrg	DBG(("\n"));
410428d7b3dSmrg}
411428d7b3dSmrg#define DBG_MONO_EDGES(x) __dbg_mono_edges(__FUNCTION__, x)
412428d7b3dSmrgstatic inline void
413428d7b3dSmrgVALIDATE_MONO_EDGES(struct mono_edge *edges)
414428d7b3dSmrg{
415428d7b3dSmrg	int prev_x = edges->x.quo;
416428d7b3dSmrg	while ((edges = edges->next)) {
417428d7b3dSmrg		assert(edges->x.quo >= prev_x);
418428d7b3dSmrg		prev_x = edges->x.quo;
419428d7b3dSmrg	}
420428d7b3dSmrg}
421428d7b3dSmrg
422428d7b3dSmrg#else
423428d7b3dSmrg#define DBG_MONO_EDGES(x)
424428d7b3dSmrg#define VALIDATE_MONO_EDGES(x)
425428d7b3dSmrg#endif
426428d7b3dSmrg
427428d7b3dSmrginline static void
428428d7b3dSmrgmono_merge_edges(struct mono *c, struct mono_edge *edges)
429428d7b3dSmrg{
430428d7b3dSmrg	struct mono_edge *e;
431428d7b3dSmrg
432428d7b3dSmrg	DBG_MONO_EDGES(edges);
433428d7b3dSmrg
434428d7b3dSmrg	for (e = edges; c->is_vertical && e; e = e->next)
435428d7b3dSmrg		c->is_vertical = e->dy == 0;
436428d7b3dSmrg
437428d7b3dSmrg	c->head.next = mono_merge_unsorted_edges(c->head.next, edges);
438428d7b3dSmrg}
439428d7b3dSmrg
440428d7b3dSmrgfastcall static void
441428d7b3dSmrgmono_span(struct mono *c, int x1, int x2, BoxPtr box)
442428d7b3dSmrg{
443428d7b3dSmrg	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
444428d7b3dSmrg
445428d7b3dSmrg	box->x1 = x1;
446428d7b3dSmrg	box->x2 = x2;
447428d7b3dSmrg
448428d7b3dSmrg	if (c->clip.data) {
449428d7b3dSmrg		pixman_region16_t region;
450428d7b3dSmrg
451428d7b3dSmrg		pixman_region_init_rects(&region, box, 1);
452428d7b3dSmrg		RegionIntersect(&region, &region, &c->clip);
453428d7b3dSmrg		if (region_num_rects(&region)) {
454428d7b3dSmrg			c->op.boxes(c->sna, &c->op,
455428d7b3dSmrg				    region_rects(&region),
456428d7b3dSmrg				    region_num_rects(&region));
457428d7b3dSmrg			apply_damage(&c->op, &region);
458428d7b3dSmrg		}
459428d7b3dSmrg		pixman_region_fini(&region);
460428d7b3dSmrg	} else {
461428d7b3dSmrg		c->op.box(c->sna, &c->op, box);
462428d7b3dSmrg		apply_damage_box(&c->op, box);
463428d7b3dSmrg	}
464428d7b3dSmrg}
465428d7b3dSmrg
466428d7b3dSmrgfastcall static void
467428d7b3dSmrgmono_span__fast(struct mono *c, int x1, int x2, BoxPtr box)
468428d7b3dSmrg{
469428d7b3dSmrg	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
470428d7b3dSmrg
471428d7b3dSmrg	box->x1 = x1;
472428d7b3dSmrg	box->x2 = x2;
473428d7b3dSmrg
474428d7b3dSmrg	c->op.box(c->sna, &c->op, box);
475428d7b3dSmrg}
476428d7b3dSmrg
477428d7b3dSmrgstruct mono_span_thread_boxes {
478428d7b3dSmrg	const struct sna_composite_op *op;
479428d7b3dSmrg#define MONO_SPAN_MAX_BOXES (8192/sizeof(BoxRec))
480428d7b3dSmrg	BoxRec boxes[MONO_SPAN_MAX_BOXES];
481428d7b3dSmrg	int num_boxes;
482428d7b3dSmrg};
483428d7b3dSmrg
484428d7b3dSmrginline static void
485428d7b3dSmrgthread_mono_span_add_boxes(struct mono *c, const BoxRec *box, int count)
486428d7b3dSmrg{
487428d7b3dSmrg	struct mono_span_thread_boxes *b = c->op.priv;
488428d7b3dSmrg
489428d7b3dSmrg	assert(count > 0 && count <= MONO_SPAN_MAX_BOXES);
490428d7b3dSmrg	if (unlikely(b->num_boxes + count > MONO_SPAN_MAX_BOXES)) {
491428d7b3dSmrg		b->op->thread_boxes(c->sna, b->op, b->boxes, b->num_boxes);
492428d7b3dSmrg		b->num_boxes = 0;
493428d7b3dSmrg	}
494428d7b3dSmrg
495428d7b3dSmrg	memcpy(b->boxes + b->num_boxes, box, count*sizeof(BoxRec));
496428d7b3dSmrg	b->num_boxes += count;
497428d7b3dSmrg	assert(b->num_boxes <= MONO_SPAN_MAX_BOXES);
498428d7b3dSmrg}
499428d7b3dSmrg
500428d7b3dSmrgfastcall static void
501428d7b3dSmrgthread_mono_span_clipped(struct mono *c, int x1, int x2, BoxPtr box)
502428d7b3dSmrg{
503428d7b3dSmrg	pixman_region16_t region;
504428d7b3dSmrg
505428d7b3dSmrg	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
506428d7b3dSmrg
507428d7b3dSmrg	box->x1 = x1;
508428d7b3dSmrg	box->x2 = x2;
509428d7b3dSmrg
510428d7b3dSmrg	assert(c->clip.data);
511428d7b3dSmrg
512428d7b3dSmrg	pixman_region_init_rects(&region, box, 1);
513428d7b3dSmrg	RegionIntersect(&region, &region, &c->clip);
514428d7b3dSmrg	if (region_num_rects(&region))
515428d7b3dSmrg		thread_mono_span_add_boxes(c,
516428d7b3dSmrg					   region_rects(&region),
517428d7b3dSmrg					   region_num_rects(&region));
518428d7b3dSmrg	pixman_region_fini(&region);
519428d7b3dSmrg}
520428d7b3dSmrg
521428d7b3dSmrgfastcall static void
522428d7b3dSmrgthread_mono_span(struct mono *c, int x1, int x2, BoxPtr box)
523428d7b3dSmrg{
524428d7b3dSmrg	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
525428d7b3dSmrg
526428d7b3dSmrg	box->x1 = x1;
527428d7b3dSmrg	box->x2 = x2;
528428d7b3dSmrg	thread_mono_span_add_boxes(c, box, 1);
529428d7b3dSmrg}
530428d7b3dSmrg
531428d7b3dSmrginline static void
532428d7b3dSmrgmono_row(struct mono *c, int16_t y, int16_t h)
533428d7b3dSmrg{
534428d7b3dSmrg	struct mono_edge *edge = c->head.next;
535428d7b3dSmrg	int prev_x = INT_MIN;
536428d7b3dSmrg	int16_t xstart = INT16_MIN;
537428d7b3dSmrg	int winding = 0;
538428d7b3dSmrg	BoxRec box;
539428d7b3dSmrg
540428d7b3dSmrg	DBG_MONO_EDGES(edge);
541428d7b3dSmrg	VALIDATE_MONO_EDGES(&c->head);
542428d7b3dSmrg
543428d7b3dSmrg	box.y1 = c->clip.extents.y1 + y;
544428d7b3dSmrg	box.y2 = box.y1 + h;
545428d7b3dSmrg
546428d7b3dSmrg	while (&c->tail != edge) {
547428d7b3dSmrg		struct mono_edge *next = edge->next;
548428d7b3dSmrg		int16_t xend = I(edge->x.quo);
549428d7b3dSmrg
550428d7b3dSmrg		if (--edge->height_left) {
551428d7b3dSmrg			if (edge->dy) {
552428d7b3dSmrg				edge->x.quo += edge->dxdy.quo;
553428d7b3dSmrg				edge->x.rem += edge->dxdy.rem;
554428d7b3dSmrg				if (edge->x.rem >= 0) {
555428d7b3dSmrg					++edge->x.quo;
556428d7b3dSmrg					edge->x.rem -= edge->dy;
557428d7b3dSmrg				}
558428d7b3dSmrg			}
559428d7b3dSmrg
560428d7b3dSmrg			if (edge->x.quo < prev_x) {
561428d7b3dSmrg				struct mono_edge *pos = edge->prev;
562428d7b3dSmrg				pos->next = next;
563428d7b3dSmrg				next->prev = pos;
564428d7b3dSmrg				do {
565428d7b3dSmrg					pos = pos->prev;
566428d7b3dSmrg				} while (edge->x.quo < pos->x.quo);
567428d7b3dSmrg				pos->next->prev = edge;
568428d7b3dSmrg				edge->next = pos->next;
569428d7b3dSmrg				edge->prev = pos;
570428d7b3dSmrg				pos->next = edge;
571428d7b3dSmrg			} else
572428d7b3dSmrg				prev_x = edge->x.quo;
573428d7b3dSmrg		} else {
574428d7b3dSmrg			edge->prev->next = next;
575428d7b3dSmrg			next->prev = edge->prev;
576428d7b3dSmrg		}
577428d7b3dSmrg
578428d7b3dSmrg		winding += edge->dir;
579428d7b3dSmrg		if (winding == 0) {
580428d7b3dSmrg			assert(I(next->x.quo) >= xend);
581428d7b3dSmrg			if (I(next->x.quo) > xend + 1) {
582428d7b3dSmrg				if (xstart < c->clip.extents.x1)
583428d7b3dSmrg					xstart = c->clip.extents.x1;
584428d7b3dSmrg				if (xend > c->clip.extents.x2)
585428d7b3dSmrg					xend = c->clip.extents.x2;
586428d7b3dSmrg				if (xend > xstart)
587428d7b3dSmrg					c->span(c, xstart, xend, &box);
588428d7b3dSmrg				xstart = INT16_MIN;
589428d7b3dSmrg			}
590428d7b3dSmrg		} else if (xstart == INT16_MIN)
591428d7b3dSmrg			xstart = xend;
592428d7b3dSmrg
593428d7b3dSmrg		edge = next;
594428d7b3dSmrg	}
595428d7b3dSmrg
596428d7b3dSmrg	DBG_MONO_EDGES(c->head.next);
597428d7b3dSmrg	VALIDATE_MONO_EDGES(&c->head);
598428d7b3dSmrg}
599428d7b3dSmrg
600428d7b3dSmrgstatic bool
601428d7b3dSmrgmono_init(struct mono *c, int num_edges)
602428d7b3dSmrg{
603428d7b3dSmrg	if (!mono_polygon_init(&c->polygon, &c->clip.extents, num_edges))
604428d7b3dSmrg		return false;
605428d7b3dSmrg
606428d7b3dSmrg	c->head.dy = 0;
607428d7b3dSmrg	c->head.height_left = INT_MAX;
608428d7b3dSmrg	c->head.x.quo = INT16_MIN << 16;
609428d7b3dSmrg	c->head.prev = NULL;
610428d7b3dSmrg	c->head.next = &c->tail;
611428d7b3dSmrg	c->tail.prev = &c->head;
612428d7b3dSmrg	c->tail.next = NULL;
613428d7b3dSmrg	c->tail.x.quo = INT16_MAX << 16;
614428d7b3dSmrg	c->tail.height_left = INT_MAX;
615428d7b3dSmrg	c->tail.dy = 0;
616428d7b3dSmrg
617428d7b3dSmrg	c->is_vertical = 1;
618428d7b3dSmrg
619428d7b3dSmrg	return true;
620428d7b3dSmrg}
621428d7b3dSmrg
622428d7b3dSmrgstatic void
623428d7b3dSmrgmono_fini(struct mono *mono)
624428d7b3dSmrg{
625428d7b3dSmrg	mono_polygon_fini(&mono->polygon);
626428d7b3dSmrg}
627428d7b3dSmrg
628428d7b3dSmrgstatic void
629428d7b3dSmrgmono_step_edges(struct mono *c, int count)
630428d7b3dSmrg{
631428d7b3dSmrg	struct mono_edge *edge;
632428d7b3dSmrg
633428d7b3dSmrg	for (edge = c->head.next; edge != &c->tail; edge = edge->next) {
634428d7b3dSmrg		edge->height_left -= count;
635428d7b3dSmrg		if (! edge->height_left) {
636428d7b3dSmrg			edge->prev->next = edge->next;
637428d7b3dSmrg			edge->next->prev = edge->prev;
638428d7b3dSmrg		}
639428d7b3dSmrg	}
640428d7b3dSmrg}
641428d7b3dSmrg
642428d7b3dSmrgflatten static void
643428d7b3dSmrgmono_render(struct mono *mono)
644428d7b3dSmrg{
645428d7b3dSmrg	struct mono_polygon *polygon = &mono->polygon;
646428d7b3dSmrg	int i, j, h = mono->clip.extents.y2 - mono->clip.extents.y1;
647428d7b3dSmrg
648428d7b3dSmrg	assert(mono->span);
649428d7b3dSmrg
650428d7b3dSmrg	for (i = 0; i < h; i = j) {
651428d7b3dSmrg		j = i + 1;
652428d7b3dSmrg
653428d7b3dSmrg		if (polygon->y_buckets[i])
654428d7b3dSmrg			mono_merge_edges(mono, polygon->y_buckets[i]);
655428d7b3dSmrg
656428d7b3dSmrg		if (mono->is_vertical) {
657428d7b3dSmrg			struct mono_edge *e = mono->head.next;
658428d7b3dSmrg			int min_height = h - i;
659428d7b3dSmrg
660428d7b3dSmrg			while (e != &mono->tail) {
661428d7b3dSmrg				if (e->height_left < min_height)
662428d7b3dSmrg					min_height = e->height_left;
663428d7b3dSmrg				e = e->next;
664428d7b3dSmrg			}
665428d7b3dSmrg
666428d7b3dSmrg			while (--min_height >= 1 && polygon->y_buckets[j] == NULL)
667428d7b3dSmrg				j++;
668428d7b3dSmrg			if (j != i + 1)
669428d7b3dSmrg				mono_step_edges(mono, j - (i + 1));
670428d7b3dSmrg		}
671428d7b3dSmrg
672428d7b3dSmrg		mono_row(mono, i, j-i);
673428d7b3dSmrg
674428d7b3dSmrg		/* XXX recompute after dropping edges? */
675428d7b3dSmrg		if (mono->head.next == &mono->tail)
676428d7b3dSmrg			mono->is_vertical = 1;
677428d7b3dSmrg	}
678428d7b3dSmrg}
679428d7b3dSmrg
680428d7b3dSmrgstatic int operator_is_bounded(uint8_t op)
681428d7b3dSmrg{
682428d7b3dSmrg	switch (op) {
683428d7b3dSmrg	case PictOpOver:
684428d7b3dSmrg	case PictOpOutReverse:
685428d7b3dSmrg	case PictOpAdd:
686428d7b3dSmrg		return true;
687428d7b3dSmrg	default:
688428d7b3dSmrg		return false;
689428d7b3dSmrg	}
690428d7b3dSmrg}
691428d7b3dSmrg
692428d7b3dSmrgstruct mono_span_thread {
693428d7b3dSmrg	struct sna *sna;
694428d7b3dSmrg	const xTrapezoid *traps;
695428d7b3dSmrg	const struct sna_composite_op *op;
696428d7b3dSmrg	RegionPtr clip;
697428d7b3dSmrg	int ntrap;
698428d7b3dSmrg	BoxRec extents;
699428d7b3dSmrg	int dx, dy;
700428d7b3dSmrg};
701428d7b3dSmrg
702428d7b3dSmrgstatic void
703428d7b3dSmrgmono_span_thread(void *arg)
704428d7b3dSmrg{
705428d7b3dSmrg	struct mono_span_thread *thread = arg;
706428d7b3dSmrg	struct mono mono;
707428d7b3dSmrg	struct mono_span_thread_boxes boxes;
708428d7b3dSmrg	const xTrapezoid *t;
709428d7b3dSmrg	int n;
710428d7b3dSmrg
711428d7b3dSmrg	mono.sna = thread->sna;
712428d7b3dSmrg
713428d7b3dSmrg	mono.clip.extents = thread->extents;
714428d7b3dSmrg	mono.clip.data = NULL;
715428d7b3dSmrg	if (thread->clip->data) {
716428d7b3dSmrg		RegionIntersect(&mono.clip, &mono.clip, thread->clip);
717428d7b3dSmrg		if (RegionNil(&mono.clip))
718428d7b3dSmrg			return;
719428d7b3dSmrg	}
720428d7b3dSmrg
721428d7b3dSmrg	boxes.op = thread->op;
722428d7b3dSmrg	boxes.num_boxes = 0;
723428d7b3dSmrg	mono.op.priv = &boxes;
724428d7b3dSmrg
725428d7b3dSmrg	if (!mono_init(&mono, 2*thread->ntrap)) {
726428d7b3dSmrg		RegionUninit(&mono.clip);
727428d7b3dSmrg		return;
728428d7b3dSmrg	}
729428d7b3dSmrg
730428d7b3dSmrg	for (n = thread->ntrap, t = thread->traps; n--; t++) {
731428d7b3dSmrg		if (!xTrapezoidValid(t))
732428d7b3dSmrg			continue;
733428d7b3dSmrg
734428d7b3dSmrg		if (pixman_fixed_to_int(t->top) + thread->dy >= thread->extents.y2 ||
735428d7b3dSmrg		    pixman_fixed_to_int(t->bottom) + thread->dy <= thread->extents.y1)
736428d7b3dSmrg			continue;
737428d7b3dSmrg
738428d7b3dSmrg		mono_add_line(&mono, thread->dx, thread->dy,
739428d7b3dSmrg			      t->top, t->bottom,
740428d7b3dSmrg			      &t->left.p1, &t->left.p2, 1);
741428d7b3dSmrg		mono_add_line(&mono, thread->dx, thread->dy,
742428d7b3dSmrg			      t->top, t->bottom,
743428d7b3dSmrg			      &t->right.p1, &t->right.p2, -1);
744428d7b3dSmrg	}
745428d7b3dSmrg
746428d7b3dSmrg	if (mono.clip.data == NULL)
747428d7b3dSmrg		mono.span = thread_mono_span;
748428d7b3dSmrg	else
749428d7b3dSmrg		mono.span = thread_mono_span_clipped;
750428d7b3dSmrg
751428d7b3dSmrg	mono_render(&mono);
752428d7b3dSmrg	mono_fini(&mono);
753428d7b3dSmrg
754428d7b3dSmrg	if (boxes.num_boxes)
755428d7b3dSmrg		thread->op->thread_boxes(thread->sna, thread->op,
756428d7b3dSmrg					 boxes.boxes, boxes.num_boxes);
757428d7b3dSmrg	RegionUninit(&mono.clip);
758428d7b3dSmrg}
759428d7b3dSmrg
760428d7b3dSmrgbool
761428d7b3dSmrgmono_trapezoids_span_converter(struct sna *sna,
762428d7b3dSmrg			       CARD8 op, PicturePtr src, PicturePtr dst,
763428d7b3dSmrg			       INT16 src_x, INT16 src_y,
764428d7b3dSmrg			       int ntrap, xTrapezoid *traps)
765428d7b3dSmrg{
766428d7b3dSmrg	struct mono mono;
767428d7b3dSmrg	BoxRec extents;
768428d7b3dSmrg	int16_t dst_x, dst_y;
769428d7b3dSmrg	int16_t dx, dy;
770428d7b3dSmrg	bool unbounded;
771428d7b3dSmrg	int num_threads, n;
772428d7b3dSmrg
773428d7b3dSmrg	if (NO_SCAN_CONVERTER)
774428d7b3dSmrg		return false;
775428d7b3dSmrg
776428d7b3dSmrg	trapezoid_origin(&traps[0].left, &dst_x, &dst_y);
777428d7b3dSmrg
778428d7b3dSmrg	if (!trapezoids_bounds(ntrap, traps, &extents))
779428d7b3dSmrg		return true;
780428d7b3dSmrg
781428d7b3dSmrg	DBG(("%s: extents (%d, %d), (%d, %d)\n",
782428d7b3dSmrg	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
783428d7b3dSmrg
784428d7b3dSmrg	if (!sna_compute_composite_region(&mono.clip,
785428d7b3dSmrg					  src, NULL, dst,
786428d7b3dSmrg					  src_x + extents.x1 - dst_x,
787428d7b3dSmrg					  src_y + extents.y1 - dst_y,
788428d7b3dSmrg					  0, 0,
789428d7b3dSmrg					  extents.x1, extents.y1,
790428d7b3dSmrg					  extents.x2 - extents.x1,
791428d7b3dSmrg					  extents.y2 - extents.y1)) {
792428d7b3dSmrg		DBG(("%s: trapezoids do not intersect drawable clips\n",
793428d7b3dSmrg		     __FUNCTION__)) ;
794428d7b3dSmrg		return true;
795428d7b3dSmrg	}
796428d7b3dSmrg
797428d7b3dSmrg	dx = dst->pDrawable->x;
798428d7b3dSmrg	dy = dst->pDrawable->y;
799428d7b3dSmrg
800428d7b3dSmrg	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
801428d7b3dSmrg	     __FUNCTION__,
802428d7b3dSmrg	     mono.clip.extents.x1, mono.clip.extents.y1,
803428d7b3dSmrg	     mono.clip.extents.x2, mono.clip.extents.y2,
804428d7b3dSmrg	     dx, dy,
805428d7b3dSmrg	     src_x + mono.clip.extents.x1 - dst_x - dx,
806428d7b3dSmrg	     src_y + mono.clip.extents.y1 - dst_y - dy));
807428d7b3dSmrg
808428d7b3dSmrg	unbounded = (!sna_drawable_is_clear(dst->pDrawable) &&
809428d7b3dSmrg		     !operator_is_bounded(op));
810428d7b3dSmrg
811428d7b3dSmrg	if (op == PictOpClear && sna->clear)
812428d7b3dSmrg		src = sna->clear;
813428d7b3dSmrg
814428d7b3dSmrg	mono.sna = sna;
815428d7b3dSmrg	if (!mono.sna->render.composite(mono.sna, op, src, NULL, dst,
816428d7b3dSmrg				       src_x + mono.clip.extents.x1 - dst_x - dx,
817428d7b3dSmrg				       src_y + mono.clip.extents.y1 - dst_y - dy,
818428d7b3dSmrg				       0, 0,
819428d7b3dSmrg				       mono.clip.extents.x1,  mono.clip.extents.y1,
820428d7b3dSmrg				       mono.clip.extents.x2 - mono.clip.extents.x1,
821428d7b3dSmrg				       mono.clip.extents.y2 - mono.clip.extents.y1,
822428d7b3dSmrg				       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op))))
823428d7b3dSmrg		return false;
824428d7b3dSmrg
825428d7b3dSmrg	num_threads = 1;
826428d7b3dSmrg	if (!NO_GPU_THREADS &&
827428d7b3dSmrg	    mono.op.thread_boxes &&
828428d7b3dSmrg	    mono.op.damage == NULL &&
829428d7b3dSmrg	    !unbounded)
830428d7b3dSmrg		num_threads = sna_use_threads(mono.clip.extents.x2 - mono.clip.extents.x1,
831428d7b3dSmrg					      mono.clip.extents.y2 - mono.clip.extents.y1,
832428d7b3dSmrg					      32);
833428d7b3dSmrg	if (num_threads > 1) {
834428d7b3dSmrg		struct mono_span_thread threads[num_threads];
835428d7b3dSmrg		int y, h;
836428d7b3dSmrg
837428d7b3dSmrg		DBG(("%s: using %d threads for mono span compositing %dx%d\n",
838428d7b3dSmrg		     __FUNCTION__, num_threads,
839428d7b3dSmrg		     mono.clip.extents.x2 - mono.clip.extents.x1,
840428d7b3dSmrg		     mono.clip.extents.y2 - mono.clip.extents.y1));
841428d7b3dSmrg
842428d7b3dSmrg		threads[0].sna = mono.sna;
843428d7b3dSmrg		threads[0].op = &mono.op;
844428d7b3dSmrg		threads[0].traps = traps;
845428d7b3dSmrg		threads[0].ntrap = ntrap;
846428d7b3dSmrg		threads[0].extents = mono.clip.extents;
847428d7b3dSmrg		threads[0].clip = &mono.clip;
848428d7b3dSmrg		threads[0].dx = dx;
849428d7b3dSmrg		threads[0].dy = dy;
850428d7b3dSmrg
851428d7b3dSmrg		y = extents.y1;
852428d7b3dSmrg		h = extents.y2 - extents.y1;
853428d7b3dSmrg		h = (h + num_threads - 1) / num_threads;
854428d7b3dSmrg		num_threads -= (num_threads-1) * h >= extents.y2 - extents.y1;
855428d7b3dSmrg
856428d7b3dSmrg		for (n = 1; n < num_threads; n++) {
857428d7b3dSmrg			threads[n] = threads[0];
858428d7b3dSmrg			threads[n].extents.y1 = y;
859428d7b3dSmrg			threads[n].extents.y2 = y += h;
860428d7b3dSmrg
861428d7b3dSmrg			sna_threads_run(n, mono_span_thread, &threads[n]);
862428d7b3dSmrg		}
863428d7b3dSmrg
864428d7b3dSmrg		threads[0].extents.y1 = y;
865428d7b3dSmrg		threads[0].extents.y2 = extents.y2;
866428d7b3dSmrg		mono_span_thread(&threads[0]);
867428d7b3dSmrg
868428d7b3dSmrg		sna_threads_wait();
869428d7b3dSmrg		mono.op.done(mono.sna, &mono.op);
870428d7b3dSmrg		return true;
871428d7b3dSmrg	}
872428d7b3dSmrg
873428d7b3dSmrg	if (!mono_init(&mono, 2*ntrap))
874428d7b3dSmrg		return false;
875428d7b3dSmrg
876428d7b3dSmrg	for (n = 0; n < ntrap; n++) {
877428d7b3dSmrg		if (!xTrapezoidValid(&traps[n]))
878428d7b3dSmrg			continue;
879428d7b3dSmrg
880428d7b3dSmrg		if (pixman_fixed_integer_floor(traps[n].top) + dy >= mono.clip.extents.y2 ||
881428d7b3dSmrg		    pixman_fixed_integer_ceil(traps[n].bottom) + dy <= mono.clip.extents.y1)
882428d7b3dSmrg			continue;
883428d7b3dSmrg
884428d7b3dSmrg		mono_add_line(&mono, dx, dy,
885428d7b3dSmrg			      traps[n].top, traps[n].bottom,
886428d7b3dSmrg			      &traps[n].left.p1, &traps[n].left.p2, 1);
887428d7b3dSmrg		mono_add_line(&mono, dx, dy,
888428d7b3dSmrg			      traps[n].top, traps[n].bottom,
889428d7b3dSmrg			      &traps[n].right.p1, &traps[n].right.p2, -1);
890428d7b3dSmrg	}
891428d7b3dSmrg
892428d7b3dSmrg	if (mono.clip.data == NULL && mono.op.damage == NULL)
893428d7b3dSmrg		mono.span = mono_span__fast;
894428d7b3dSmrg	else
895428d7b3dSmrg		mono.span = mono_span;
896428d7b3dSmrg
897428d7b3dSmrg	mono_render(&mono);
898428d7b3dSmrg	mono.op.done(mono.sna, &mono.op);
899428d7b3dSmrg	mono_fini(&mono);
900428d7b3dSmrg
901428d7b3dSmrg	if (unbounded) {
902428d7b3dSmrg		xPointFixed p1, p2;
903428d7b3dSmrg
904428d7b3dSmrg		if (!mono_init(&mono, 2+2*ntrap))
905428d7b3dSmrg			return false;
906428d7b3dSmrg
907428d7b3dSmrg		p1.y = mono.clip.extents.y1 * pixman_fixed_1;
908428d7b3dSmrg		p2.y = mono.clip.extents.y2 * pixman_fixed_1;
909428d7b3dSmrg
910428d7b3dSmrg		p1.x = mono.clip.extents.x1 * pixman_fixed_1;
911428d7b3dSmrg		p2.x = mono.clip.extents.x1 * pixman_fixed_1;
912428d7b3dSmrg		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
913428d7b3dSmrg
914428d7b3dSmrg		p1.x = mono.clip.extents.x2 * pixman_fixed_1;
915428d7b3dSmrg		p2.x = mono.clip.extents.x2 * pixman_fixed_1;
916428d7b3dSmrg		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
917428d7b3dSmrg
918428d7b3dSmrg		for (n = 0; n < ntrap; n++) {
919428d7b3dSmrg			if (!xTrapezoidValid(&traps[n]))
920428d7b3dSmrg				continue;
921428d7b3dSmrg
922428d7b3dSmrg			if (pixman_fixed_to_int(traps[n].top) + dy >= mono.clip.extents.y2 ||
923428d7b3dSmrg			    pixman_fixed_to_int(traps[n].bottom) + dy < mono.clip.extents.y1)
924428d7b3dSmrg				continue;
925428d7b3dSmrg
926428d7b3dSmrg			mono_add_line(&mono, dx, dy,
927428d7b3dSmrg				      traps[n].top, traps[n].bottom,
928428d7b3dSmrg				      &traps[n].left.p1, &traps[n].left.p2, 1);
929428d7b3dSmrg			mono_add_line(&mono, dx, dy,
930428d7b3dSmrg				      traps[n].top, traps[n].bottom,
931428d7b3dSmrg				      &traps[n].right.p1, &traps[n].right.p2, -1);
932428d7b3dSmrg		}
933428d7b3dSmrg		if (mono.sna->render.composite(mono.sna,
934428d7b3dSmrg					       PictOpClear,
935428d7b3dSmrg					       mono.sna->clear, NULL, dst,
936428d7b3dSmrg					       0, 0,
937428d7b3dSmrg					       0, 0,
938428d7b3dSmrg					       mono.clip.extents.x1,  mono.clip.extents.y1,
939428d7b3dSmrg					       mono.clip.extents.x2 - mono.clip.extents.x1,
940428d7b3dSmrg					       mono.clip.extents.y2 - mono.clip.extents.y1,
941428d7b3dSmrg					       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
942428d7b3dSmrg			mono_render(&mono);
943428d7b3dSmrg			mono.op.done(mono.sna, &mono.op);
944428d7b3dSmrg		}
945428d7b3dSmrg		mono_fini(&mono);
946428d7b3dSmrg	}
947428d7b3dSmrg
948428d7b3dSmrg	REGION_UNINIT(NULL, &mono.clip);
949428d7b3dSmrg	return true;
950428d7b3dSmrg}
951428d7b3dSmrg
952428d7b3dSmrgstruct mono_inplace_composite {
953428d7b3dSmrg	pixman_image_t *src, *dst;
954428d7b3dSmrg	int dx, dy;
955428d7b3dSmrg	int sx, sy;
956428d7b3dSmrg	int op;
957428d7b3dSmrg};
958428d7b3dSmrgstruct mono_inplace_fill {
959428d7b3dSmrg	uint32_t *data, stride;
960428d7b3dSmrg	uint32_t color;
961428d7b3dSmrg	int bpp;
962428d7b3dSmrg};
963428d7b3dSmrg
964428d7b3dSmrgfastcall static void
965428d7b3dSmrgmono_inplace_fill_box(struct sna *sna,
966428d7b3dSmrg		      const struct sna_composite_op *op,
967428d7b3dSmrg		      const BoxRec *box)
968428d7b3dSmrg{
969428d7b3dSmrg	struct mono_inplace_fill *fill = op->priv;
970428d7b3dSmrg
971428d7b3dSmrg	DBG(("(%s: (%d, %d)x(%d, %d):%08x\n",
972428d7b3dSmrg	     __FUNCTION__,
973428d7b3dSmrg	     box->x1, box->y1,
974428d7b3dSmrg	     box->x2 - box->x1,
975428d7b3dSmrg	     box->y2 - box->y1,
976428d7b3dSmrg	     fill->color));
977428d7b3dSmrg	pixman_fill(fill->data, fill->stride, fill->bpp,
978428d7b3dSmrg		    box->x1, box->y1,
979428d7b3dSmrg		    box->x2 - box->x1,
980428d7b3dSmrg		    box->y2 - box->y1,
981428d7b3dSmrg		    fill->color);
982428d7b3dSmrg}
983428d7b3dSmrg
984428d7b3dSmrgstatic void
985428d7b3dSmrgmono_inplace_fill_boxes(struct sna *sna,
986428d7b3dSmrg			const struct sna_composite_op *op,
987428d7b3dSmrg			const BoxRec *box, int nbox)
988428d7b3dSmrg{
989428d7b3dSmrg	struct mono_inplace_fill *fill = op->priv;
990428d7b3dSmrg
991428d7b3dSmrg	do {
992428d7b3dSmrg		DBG(("(%s: (%d, %d)x(%d, %d):%08x\n",
993428d7b3dSmrg		     __FUNCTION__,
994428d7b3dSmrg		     box->x1, box->y1,
995428d7b3dSmrg		     box->x2 - box->x1,
996428d7b3dSmrg		     box->y2 - box->y1,
997428d7b3dSmrg		     fill->color));
998428d7b3dSmrg		pixman_fill(fill->data, fill->stride, fill->bpp,
999428d7b3dSmrg			    box->x1, box->y1,
1000428d7b3dSmrg			    box->x2 - box->x1,
1001428d7b3dSmrg			    box->y2 - box->y1,
1002428d7b3dSmrg			    fill->color);
1003428d7b3dSmrg		box++;
1004428d7b3dSmrg	} while (--nbox);
1005428d7b3dSmrg}
1006428d7b3dSmrg
1007428d7b3dSmrgfastcall static void
1008428d7b3dSmrgmono_inplace_composite_box(struct sna *sna,
1009428d7b3dSmrg			   const struct sna_composite_op *op,
1010428d7b3dSmrg			   const BoxRec *box)
1011428d7b3dSmrg{
1012428d7b3dSmrg	struct mono_inplace_composite *c = op->priv;
1013428d7b3dSmrg
1014428d7b3dSmrg	pixman_image_composite(c->op, c->src, NULL, c->dst,
1015428d7b3dSmrg			       box->x1 + c->sx, box->y1 + c->sy,
1016428d7b3dSmrg			       0, 0,
1017428d7b3dSmrg			       box->x1 + c->dx, box->y1 + c->dy,
1018428d7b3dSmrg			       box->x2 - box->x1,
1019428d7b3dSmrg			       box->y2 - box->y1);
1020428d7b3dSmrg}
1021428d7b3dSmrg
1022428d7b3dSmrgstatic void
1023428d7b3dSmrgmono_inplace_composite_boxes(struct sna *sna,
1024428d7b3dSmrg			     const struct sna_composite_op *op,
1025428d7b3dSmrg			     const BoxRec *box, int nbox)
1026428d7b3dSmrg{
1027428d7b3dSmrg	struct mono_inplace_composite *c = op->priv;
1028428d7b3dSmrg
1029428d7b3dSmrg	do {
1030428d7b3dSmrg		pixman_image_composite(c->op, c->src, NULL, c->dst,
1031428d7b3dSmrg				       box->x1 + c->sx, box->y1 + c->sy,
1032428d7b3dSmrg				       0, 0,
1033428d7b3dSmrg				       box->x1 + c->dx, box->y1 + c->dy,
1034428d7b3dSmrg				       box->x2 - box->x1,
1035428d7b3dSmrg				       box->y2 - box->y1);
1036428d7b3dSmrg		box++;
1037428d7b3dSmrg	} while (--nbox);
1038428d7b3dSmrg}
1039428d7b3dSmrg
1040428d7b3dSmrgbool
1041428d7b3dSmrgmono_trapezoid_span_inplace(struct sna *sna,
1042428d7b3dSmrg			    CARD8 op,
1043428d7b3dSmrg			    PicturePtr src,
1044428d7b3dSmrg			    PicturePtr dst,
1045428d7b3dSmrg			    INT16 src_x, INT16 src_y,
1046428d7b3dSmrg			    int ntrap, xTrapezoid *traps)
1047428d7b3dSmrg{
1048428d7b3dSmrg	struct mono mono;
1049428d7b3dSmrg	union {
1050428d7b3dSmrg		struct mono_inplace_fill fill;
1051428d7b3dSmrg		struct mono_inplace_composite composite;
1052428d7b3dSmrg	} inplace;
1053428d7b3dSmrg	int was_clear;
1054428d7b3dSmrg	int x, y, n;
1055428d7b3dSmrg
1056428d7b3dSmrg	if (!trapezoids_bounds(ntrap, traps, &mono.clip.extents))
1057428d7b3dSmrg		return true;
1058428d7b3dSmrg
1059428d7b3dSmrg	DBG(("%s: extents (%d, %d), (%d, %d)\n",
1060428d7b3dSmrg	     __FUNCTION__,
1061428d7b3dSmrg	     mono.clip.extents.x1, mono.clip.extents.y1,
1062428d7b3dSmrg	     mono.clip.extents.x2, mono.clip.extents.y2));
1063428d7b3dSmrg
1064428d7b3dSmrg	if (!sna_compute_composite_region(&mono.clip,
1065428d7b3dSmrg					  src, NULL, dst,
1066428d7b3dSmrg					  src_x, src_y,
1067428d7b3dSmrg					  0, 0,
1068428d7b3dSmrg					  mono.clip.extents.x1, mono.clip.extents.y1,
1069428d7b3dSmrg					  mono.clip.extents.x2 - mono.clip.extents.x1,
1070428d7b3dSmrg					  mono.clip.extents.y2 - mono.clip.extents.y1)) {
1071428d7b3dSmrg		DBG(("%s: trapezoids do not intersect drawable clips\n",
1072428d7b3dSmrg		     __FUNCTION__)) ;
1073428d7b3dSmrg		return true;
1074428d7b3dSmrg	}
1075428d7b3dSmrg
1076428d7b3dSmrg	DBG(("%s: clipped extents (%d, %d), (%d, %d)\n",
1077428d7b3dSmrg	     __FUNCTION__,
1078428d7b3dSmrg	     mono.clip.extents.x1, mono.clip.extents.y1,
1079428d7b3dSmrg	     mono.clip.extents.x2, mono.clip.extents.y2));
1080428d7b3dSmrg
1081428d7b3dSmrg	was_clear = sna_drawable_is_clear(dst->pDrawable);
1082428d7b3dSmrg	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &mono.clip,
1083428d7b3dSmrg					     MOVE_WRITE | MOVE_READ))
1084428d7b3dSmrg		return true;
1085428d7b3dSmrg
1086428d7b3dSmrg	mono.sna = sna;
1087428d7b3dSmrg	if (!mono_init(&mono, 2*ntrap))
1088428d7b3dSmrg		return false;
1089428d7b3dSmrg
1090428d7b3dSmrg	mono.op.damage = NULL;
1091428d7b3dSmrg
1092428d7b3dSmrg	x = dst->pDrawable->x;
1093428d7b3dSmrg	y = dst->pDrawable->y;
1094428d7b3dSmrg
1095428d7b3dSmrg	for (n = 0; n < ntrap; n++) {
1096428d7b3dSmrg		if (!xTrapezoidValid(&traps[n]))
1097428d7b3dSmrg			continue;
1098428d7b3dSmrg
1099428d7b3dSmrg		if (pixman_fixed_to_int(traps[n].top) + y >= mono.clip.extents.y2 ||
1100428d7b3dSmrg		    pixman_fixed_to_int(traps[n].bottom) + y < mono.clip.extents.y1)
1101428d7b3dSmrg			continue;
1102428d7b3dSmrg
1103428d7b3dSmrg		mono_add_line(&mono, x, y,
1104428d7b3dSmrg			      traps[n].top, traps[n].bottom,
1105428d7b3dSmrg			      &traps[n].left.p1, &traps[n].left.p2, 1);
1106428d7b3dSmrg		mono_add_line(&mono, x, y,
1107428d7b3dSmrg			      traps[n].top, traps[n].bottom,
1108428d7b3dSmrg			      &traps[n].right.p1, &traps[n].right.p2, -1);
1109428d7b3dSmrg	}
1110428d7b3dSmrg
1111428d7b3dSmrg	if (sna_picture_is_solid(src, &inplace.fill.color) &&
1112428d7b3dSmrg	    (op == PictOpSrc || op == PictOpClear ||
1113428d7b3dSmrg	     (was_clear && (op == PictOpOver || op == PictOpAdd)) ||
1114428d7b3dSmrg	     (op == PictOpOver && inplace.fill.color >> 24 == 0xff))) {
1115428d7b3dSmrg		PixmapPtr pixmap;
1116428d7b3dSmrg		int16_t dx, dy;
1117428d7b3dSmrg		uint8_t *ptr;
1118428d7b3dSmrg
1119428d7b3dSmrgunbounded_pass:
1120428d7b3dSmrg		pixmap = get_drawable_pixmap(dst->pDrawable);
1121428d7b3dSmrg
1122428d7b3dSmrg		ptr = pixmap->devPrivate.ptr;
1123428d7b3dSmrg		if (get_drawable_deltas(dst->pDrawable, pixmap, &dx, &dy))
1124428d7b3dSmrg			ptr += dy * pixmap->devKind + dx * pixmap->drawable.bitsPerPixel / 8;
1125428d7b3dSmrg		inplace.fill.data = (uint32_t *)ptr;
1126428d7b3dSmrg		inplace.fill.stride = pixmap->devKind / sizeof(uint32_t);
1127428d7b3dSmrg		inplace.fill.bpp = pixmap->drawable.bitsPerPixel;
1128428d7b3dSmrg
1129428d7b3dSmrg		if (op == PictOpClear)
1130428d7b3dSmrg			inplace.fill.color = 0;
1131428d7b3dSmrg		else if (dst->format != PICT_a8r8g8b8)
1132428d7b3dSmrg			inplace.fill.color = sna_rgba_to_color(inplace.fill.color, dst->format);
1133428d7b3dSmrg
1134428d7b3dSmrg		DBG(("%s: fill %x\n", __FUNCTION__, inplace.fill.color));
1135428d7b3dSmrg
1136428d7b3dSmrg		mono.op.priv = &inplace.fill;
1137428d7b3dSmrg		mono.op.box = mono_inplace_fill_box;
1138428d7b3dSmrg		mono.op.boxes = mono_inplace_fill_boxes;
1139428d7b3dSmrg
1140428d7b3dSmrg		op = 0;
1141428d7b3dSmrg	} else {
1142428d7b3dSmrg		if (src->pDrawable) {
1143428d7b3dSmrg			if (!sna_drawable_move_to_cpu(src->pDrawable,
1144428d7b3dSmrg						      MOVE_READ)) {
1145428d7b3dSmrg				mono_fini(&mono);
1146428d7b3dSmrg				return false;
1147428d7b3dSmrg			}
1148428d7b3dSmrg			if (src->alphaMap &&
1149428d7b3dSmrg			    !sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
1150428d7b3dSmrg						      MOVE_READ)) {
1151428d7b3dSmrg				mono_fini(&mono);
1152428d7b3dSmrg				return false;
1153428d7b3dSmrg			}
1154428d7b3dSmrg		}
1155428d7b3dSmrg
1156428d7b3dSmrg		inplace.composite.dst = image_from_pict(dst, false,
1157428d7b3dSmrg							&inplace.composite.dx,
1158428d7b3dSmrg							&inplace.composite.dy);
1159428d7b3dSmrg		inplace.composite.src = image_from_pict(src, false,
1160428d7b3dSmrg							&inplace.composite.sx,
1161428d7b3dSmrg							&inplace.composite.sy);
1162428d7b3dSmrg		inplace.composite.sx +=
1163428d7b3dSmrg			src_x - pixman_fixed_to_int(traps[0].left.p1.x),
1164428d7b3dSmrg		inplace.composite.sy +=
1165428d7b3dSmrg			src_y - pixman_fixed_to_int(traps[0].left.p1.y),
1166428d7b3dSmrg		inplace.composite.op = op;
1167428d7b3dSmrg
1168428d7b3dSmrg		mono.op.priv = &inplace.composite;
1169428d7b3dSmrg		mono.op.box = mono_inplace_composite_box;
1170428d7b3dSmrg		mono.op.boxes = mono_inplace_composite_boxes;
1171428d7b3dSmrg	}
1172428d7b3dSmrg
1173428d7b3dSmrg	if (mono.clip.data == NULL && mono.op.damage == NULL)
1174428d7b3dSmrg		mono.span = mono_span__fast;
1175428d7b3dSmrg	else
1176428d7b3dSmrg		mono.span = mono_span;
1177428d7b3dSmrg	if (sigtrap_get() == 0) {
1178428d7b3dSmrg		mono_render(&mono);
1179428d7b3dSmrg		sigtrap_put();
1180428d7b3dSmrg	}
1181428d7b3dSmrg	mono_fini(&mono);
1182428d7b3dSmrg
1183428d7b3dSmrg	if (op) {
1184428d7b3dSmrg		free_pixman_pict(src, inplace.composite.src);
1185428d7b3dSmrg		free_pixman_pict(dst, inplace.composite.dst);
1186428d7b3dSmrg
1187428d7b3dSmrg		if (!was_clear && !operator_is_bounded(op)) {
1188428d7b3dSmrg			xPointFixed p1, p2;
1189428d7b3dSmrg
1190428d7b3dSmrg			DBG(("%s: unbounded fixup\n", __FUNCTION__));
1191428d7b3dSmrg
1192428d7b3dSmrg			if (!mono_init(&mono, 2+2*ntrap))
1193428d7b3dSmrg				return false;
1194428d7b3dSmrg
1195428d7b3dSmrg			p1.y = mono.clip.extents.y1 * pixman_fixed_1;
1196428d7b3dSmrg			p2.y = mono.clip.extents.y2 * pixman_fixed_1;
1197428d7b3dSmrg
1198428d7b3dSmrg			p1.x = mono.clip.extents.x1 * pixman_fixed_1;
1199428d7b3dSmrg			p2.x = mono.clip.extents.x1 * pixman_fixed_1;
1200428d7b3dSmrg			mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
1201428d7b3dSmrg
1202428d7b3dSmrg			p1.x = mono.clip.extents.x2 * pixman_fixed_1;
1203428d7b3dSmrg			p2.x = mono.clip.extents.x2 * pixman_fixed_1;
1204428d7b3dSmrg			mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
1205428d7b3dSmrg
1206428d7b3dSmrg			for (n = 0; n < ntrap; n++) {
1207428d7b3dSmrg				if (!xTrapezoidValid(&traps[n]))
1208428d7b3dSmrg					continue;
1209428d7b3dSmrg
1210428d7b3dSmrg				if (pixman_fixed_to_int(traps[n].top) + x >= mono.clip.extents.y2 ||
1211428d7b3dSmrg				    pixman_fixed_to_int(traps[n].bottom) + y < mono.clip.extents.y1)
1212428d7b3dSmrg					continue;
1213428d7b3dSmrg
1214428d7b3dSmrg				mono_add_line(&mono, x, y,
1215428d7b3dSmrg					      traps[n].top, traps[n].bottom,
1216428d7b3dSmrg					      &traps[n].left.p1, &traps[n].left.p2, 1);
1217428d7b3dSmrg				mono_add_line(&mono, x, y,
1218428d7b3dSmrg					      traps[n].top, traps[n].bottom,
1219428d7b3dSmrg					      &traps[n].right.p1, &traps[n].right.p2, -1);
1220428d7b3dSmrg			}
1221428d7b3dSmrg
1222428d7b3dSmrg			op = PictOpClear;
1223428d7b3dSmrg			goto unbounded_pass;
1224428d7b3dSmrg		}
1225428d7b3dSmrg	}
1226428d7b3dSmrg
1227428d7b3dSmrg	return true;
1228428d7b3dSmrg}
1229428d7b3dSmrg
1230428d7b3dSmrgbool
1231428d7b3dSmrgmono_trap_span_converter(struct sna *sna,
1232428d7b3dSmrg			 PicturePtr dst,
1233428d7b3dSmrg			 INT16 x, INT16 y,
1234428d7b3dSmrg			 int ntrap, xTrap *traps)
1235428d7b3dSmrg{
1236428d7b3dSmrg	struct mono mono;
1237428d7b3dSmrg	xRenderColor white;
1238428d7b3dSmrg	PicturePtr src;
1239428d7b3dSmrg	int error;
1240428d7b3dSmrg	int n;
1241428d7b3dSmrg
1242428d7b3dSmrg	white.red = white.green = white.blue = white.alpha = 0xffff;
1243428d7b3dSmrg	src = CreateSolidPicture(0, &white, &error);
1244428d7b3dSmrg	if (src == NULL)
1245428d7b3dSmrg		return true;
1246428d7b3dSmrg
1247428d7b3dSmrg	mono.clip = *dst->pCompositeClip;
1248428d7b3dSmrg	x += dst->pDrawable->x;
1249428d7b3dSmrg	y += dst->pDrawable->y;
1250428d7b3dSmrg
1251428d7b3dSmrg	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d)\n",
1252428d7b3dSmrg	     __FUNCTION__,
1253428d7b3dSmrg	     mono.clip.extents.x1, mono.clip.extents.y1,
1254428d7b3dSmrg	     mono.clip.extents.x2, mono.clip.extents.y2,
1255428d7b3dSmrg	     x, y));
1256428d7b3dSmrg
1257428d7b3dSmrg	mono.sna = sna;
1258428d7b3dSmrg	if (!mono_init(&mono, 2*ntrap))
1259428d7b3dSmrg		return false;
1260428d7b3dSmrg
1261428d7b3dSmrg	for (n = 0; n < ntrap; n++) {
1262428d7b3dSmrg		xPointFixed p1, p2;
1263428d7b3dSmrg
1264428d7b3dSmrg		if (pixman_fixed_to_int(traps[n].top.y) + y >= mono.clip.extents.y2 ||
1265428d7b3dSmrg		    pixman_fixed_to_int(traps[n].bot.y) + y < mono.clip.extents.y1)
1266428d7b3dSmrg			continue;
1267428d7b3dSmrg
1268428d7b3dSmrg		p1.y = traps[n].top.y;
1269428d7b3dSmrg		p2.y = traps[n].bot.y;
1270428d7b3dSmrg
1271428d7b3dSmrg		p1.x = traps[n].top.l;
1272428d7b3dSmrg		p2.x = traps[n].bot.l;
1273428d7b3dSmrg		mono_add_line(&mono, x, y,
1274428d7b3dSmrg			      traps[n].top.y, traps[n].bot.y,
1275428d7b3dSmrg			      &p1, &p2, 1);
1276428d7b3dSmrg
1277428d7b3dSmrg		p1.x = traps[n].top.r;
1278428d7b3dSmrg		p2.x = traps[n].bot.r;
1279428d7b3dSmrg		mono_add_line(&mono, x, y,
1280428d7b3dSmrg			      traps[n].top.y, traps[n].bot.y,
1281428d7b3dSmrg			      &p1, &p2, -1);
1282428d7b3dSmrg	}
1283428d7b3dSmrg
1284428d7b3dSmrg	if (mono.sna->render.composite(mono.sna, PictOpAdd, src, NULL, dst,
1285428d7b3dSmrg				       0, 0,
1286428d7b3dSmrg				       0, 0,
1287428d7b3dSmrg				       mono.clip.extents.x1,  mono.clip.extents.y1,
1288428d7b3dSmrg				       mono.clip.extents.x2 - mono.clip.extents.x1,
1289428d7b3dSmrg				       mono.clip.extents.y2 - mono.clip.extents.y1,
1290428d7b3dSmrg				       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1291428d7b3dSmrg		if (mono.clip.data == NULL && mono.op.damage == NULL)
1292428d7b3dSmrg			mono.span = mono_span__fast;
1293428d7b3dSmrg		else
1294428d7b3dSmrg			mono.span = mono_span;
1295428d7b3dSmrg		mono_render(&mono);
1296428d7b3dSmrg		mono.op.done(mono.sna, &mono.op);
1297428d7b3dSmrg	}
1298428d7b3dSmrg
1299428d7b3dSmrg	mono_fini(&mono);
1300428d7b3dSmrg	FreePicture(src, 0);
1301428d7b3dSmrg	return true;
1302428d7b3dSmrg}
1303428d7b3dSmrg
1304428d7b3dSmrgbool
1305428d7b3dSmrgmono_triangles_span_converter(struct sna *sna,
1306428d7b3dSmrg			      CARD8 op, PicturePtr src, PicturePtr dst,
1307428d7b3dSmrg			      INT16 src_x, INT16 src_y,
1308428d7b3dSmrg			      int count, xTriangle *tri)
1309428d7b3dSmrg{
1310428d7b3dSmrg	struct mono mono;
1311428d7b3dSmrg	BoxRec extents;
1312428d7b3dSmrg	int16_t dst_x, dst_y;
1313428d7b3dSmrg	int16_t dx, dy;
1314428d7b3dSmrg	bool was_clear;
1315428d7b3dSmrg	int n;
1316428d7b3dSmrg
1317428d7b3dSmrg	mono.sna = sna;
1318428d7b3dSmrg
1319428d7b3dSmrg	dst_x = pixman_fixed_to_int(tri[0].p1.x);
1320428d7b3dSmrg	dst_y = pixman_fixed_to_int(tri[0].p1.y);
1321428d7b3dSmrg
1322428d7b3dSmrg	miTriangleBounds(count, tri, &extents);
1323428d7b3dSmrg	DBG(("%s: extents (%d, %d), (%d, %d)\n",
1324428d7b3dSmrg	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
1325428d7b3dSmrg
1326428d7b3dSmrg	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
1327428d7b3dSmrg		return true;
1328428d7b3dSmrg
1329428d7b3dSmrg	if (!sna_compute_composite_region(&mono.clip,
1330428d7b3dSmrg					  src, NULL, dst,
1331428d7b3dSmrg					  src_x + extents.x1 - dst_x,
1332428d7b3dSmrg					  src_y + extents.y1 - dst_y,
1333428d7b3dSmrg					  0, 0,
1334428d7b3dSmrg					  extents.x1, extents.y1,
1335428d7b3dSmrg					  extents.x2 - extents.x1,
1336428d7b3dSmrg					  extents.y2 - extents.y1)) {
1337428d7b3dSmrg		DBG(("%s: triangles do not intersect drawable clips\n",
1338428d7b3dSmrg		     __FUNCTION__)) ;
1339428d7b3dSmrg		return true;
1340428d7b3dSmrg	}
1341428d7b3dSmrg
1342428d7b3dSmrg	dx = dst->pDrawable->x;
1343428d7b3dSmrg	dy = dst->pDrawable->y;
1344428d7b3dSmrg
1345428d7b3dSmrg	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
1346428d7b3dSmrg	     __FUNCTION__,
1347428d7b3dSmrg	     mono.clip.extents.x1, mono.clip.extents.y1,
1348428d7b3dSmrg	     mono.clip.extents.x2, mono.clip.extents.y2,
1349428d7b3dSmrg	     dx, dy,
1350428d7b3dSmrg	     src_x + mono.clip.extents.x1 - dst_x - dx,
1351428d7b3dSmrg	     src_y + mono.clip.extents.y1 - dst_y - dy));
1352428d7b3dSmrg
1353428d7b3dSmrg	was_clear = sna_drawable_is_clear(dst->pDrawable);
1354428d7b3dSmrg
1355428d7b3dSmrg	if (!mono_init(&mono, 3*count))
1356428d7b3dSmrg		return false;
1357428d7b3dSmrg
1358428d7b3dSmrg	for (n = 0; n < count; n++) {
1359428d7b3dSmrg		mono_add_line(&mono, dx, dy,
1360428d7b3dSmrg			      tri[n].p1.y, tri[n].p2.y,
1361428d7b3dSmrg			      &tri[n].p1, &tri[n].p2, 1);
1362428d7b3dSmrg		mono_add_line(&mono, dx, dy,
1363428d7b3dSmrg			      tri[n].p2.y, tri[n].p3.y,
1364428d7b3dSmrg			      &tri[n].p2, &tri[n].p3, 1);
1365428d7b3dSmrg		mono_add_line(&mono, dx, dy,
1366428d7b3dSmrg			      tri[n].p3.y, tri[n].p1.y,
1367428d7b3dSmrg			      &tri[n].p3, &tri[n].p1, 1);
1368428d7b3dSmrg	}
1369428d7b3dSmrg
1370428d7b3dSmrg	if (mono.sna->render.composite(mono.sna, op, src, NULL, dst,
1371428d7b3dSmrg				       src_x + mono.clip.extents.x1 - dst_x - dx,
1372428d7b3dSmrg				       src_y + mono.clip.extents.y1 - dst_y - dy,
1373428d7b3dSmrg				       0, 0,
1374428d7b3dSmrg				       mono.clip.extents.x1,  mono.clip.extents.y1,
1375428d7b3dSmrg				       mono.clip.extents.x2 - mono.clip.extents.x1,
1376428d7b3dSmrg				       mono.clip.extents.y2 - mono.clip.extents.y1,
1377428d7b3dSmrg				       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1378428d7b3dSmrg		if (mono.clip.data == NULL && mono.op.damage == NULL)
1379428d7b3dSmrg			mono.span = mono_span__fast;
1380428d7b3dSmrg		else
1381428d7b3dSmrg			mono.span = mono_span;
1382428d7b3dSmrg		mono_render(&mono);
1383428d7b3dSmrg		mono.op.done(mono.sna, &mono.op);
1384428d7b3dSmrg	}
1385428d7b3dSmrg
1386428d7b3dSmrg	if (!was_clear && !operator_is_bounded(op)) {
1387428d7b3dSmrg		xPointFixed p1, p2;
1388428d7b3dSmrg
1389428d7b3dSmrg		if (!mono_init(&mono, 2+3*count))
1390428d7b3dSmrg			return false;
1391428d7b3dSmrg
1392428d7b3dSmrg		p1.y = mono.clip.extents.y1 * pixman_fixed_1;
1393428d7b3dSmrg		p2.y = mono.clip.extents.y2 * pixman_fixed_1;
1394428d7b3dSmrg
1395428d7b3dSmrg		p1.x = mono.clip.extents.x1 * pixman_fixed_1;
1396428d7b3dSmrg		p2.x = mono.clip.extents.x1 * pixman_fixed_1;
1397428d7b3dSmrg		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
1398428d7b3dSmrg
1399428d7b3dSmrg		p1.x = mono.clip.extents.x2 * pixman_fixed_1;
1400428d7b3dSmrg		p2.x = mono.clip.extents.x2 * pixman_fixed_1;
1401428d7b3dSmrg		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
1402428d7b3dSmrg
1403428d7b3dSmrg		for (n = 0; n < count; n++) {
1404428d7b3dSmrg			mono_add_line(&mono, dx, dy,
1405428d7b3dSmrg				      tri[n].p1.y, tri[n].p2.y,
1406428d7b3dSmrg				      &tri[n].p1, &tri[n].p2, 1);
1407428d7b3dSmrg			mono_add_line(&mono, dx, dy,
1408428d7b3dSmrg				      tri[n].p2.y, tri[n].p3.y,
1409428d7b3dSmrg				      &tri[n].p2, &tri[n].p3, 1);
1410428d7b3dSmrg			mono_add_line(&mono, dx, dy,
1411428d7b3dSmrg				      tri[n].p3.y, tri[n].p1.y,
1412428d7b3dSmrg				      &tri[n].p3, &tri[n].p1, 1);
1413428d7b3dSmrg		}
1414428d7b3dSmrg
1415428d7b3dSmrg		if (mono.sna->render.composite(mono.sna,
1416428d7b3dSmrg					       PictOpClear,
1417428d7b3dSmrg					       mono.sna->clear, NULL, dst,
1418428d7b3dSmrg					       0, 0,
1419428d7b3dSmrg					       0, 0,
1420428d7b3dSmrg					       mono.clip.extents.x1,  mono.clip.extents.y1,
1421428d7b3dSmrg					       mono.clip.extents.x2 - mono.clip.extents.x1,
1422428d7b3dSmrg					       mono.clip.extents.y2 - mono.clip.extents.y1,
1423428d7b3dSmrg					       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1424428d7b3dSmrg			if (mono.clip.data == NULL && mono.op.damage == NULL)
1425428d7b3dSmrg				mono.span = mono_span__fast;
1426428d7b3dSmrg			else
1427428d7b3dSmrg				mono.span = mono_span;
1428428d7b3dSmrg			mono_render(&mono);
1429428d7b3dSmrg			mono.op.done(mono.sna, &mono.op);
1430428d7b3dSmrg		}
1431428d7b3dSmrg		mono_fini(&mono);
1432428d7b3dSmrg	}
1433428d7b3dSmrg
1434428d7b3dSmrg	mono_fini(&mono);
1435428d7b3dSmrg	REGION_UNINIT(NULL, &mono.clip);
1436428d7b3dSmrg	return true;
1437428d7b3dSmrg}
1438428d7b3dSmrg
1439428d7b3dSmrgbool
1440428d7b3dSmrgmono_tristrip_span_converter(struct sna *sna,
1441428d7b3dSmrg			     CARD8 op, PicturePtr src, PicturePtr dst,
1442428d7b3dSmrg			     INT16 src_x, INT16 src_y,
1443428d7b3dSmrg			     int count, xPointFixed *points)
1444428d7b3dSmrg{
1445428d7b3dSmrg	struct mono mono;
1446428d7b3dSmrg	BoxRec extents;
1447428d7b3dSmrg	int16_t dst_x, dst_y;
1448428d7b3dSmrg	int16_t dx, dy;
1449428d7b3dSmrg	bool was_clear;
1450428d7b3dSmrg	int n;
1451428d7b3dSmrg
1452428d7b3dSmrg	mono.sna = sna;
1453428d7b3dSmrg
1454428d7b3dSmrg	dst_x = pixman_fixed_to_int(points[0].x);
1455428d7b3dSmrg	dst_y = pixman_fixed_to_int(points[0].y);
1456428d7b3dSmrg
1457428d7b3dSmrg	miPointFixedBounds(count, points, &extents);
1458428d7b3dSmrg	DBG(("%s: extents (%d, %d), (%d, %d)\n",
1459428d7b3dSmrg	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
1460428d7b3dSmrg
1461428d7b3dSmrg	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
1462428d7b3dSmrg		return true;
1463428d7b3dSmrg
1464428d7b3dSmrg	if (!sna_compute_composite_region(&mono.clip,
1465428d7b3dSmrg					  src, NULL, dst,
1466428d7b3dSmrg					  src_x + extents.x1 - dst_x,
1467428d7b3dSmrg					  src_y + extents.y1 - dst_y,
1468428d7b3dSmrg					  0, 0,
1469428d7b3dSmrg					  extents.x1, extents.y1,
1470428d7b3dSmrg					  extents.x2 - extents.x1,
1471428d7b3dSmrg					  extents.y2 - extents.y1)) {
1472428d7b3dSmrg		DBG(("%s: triangles do not intersect drawable clips\n",
1473428d7b3dSmrg		     __FUNCTION__)) ;
1474428d7b3dSmrg		return true;
1475428d7b3dSmrg	}
1476428d7b3dSmrg
1477428d7b3dSmrg	dx = dst->pDrawable->x;
1478428d7b3dSmrg	dy = dst->pDrawable->y;
1479428d7b3dSmrg
1480428d7b3dSmrg	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
1481428d7b3dSmrg	     __FUNCTION__,
1482428d7b3dSmrg	     mono.clip.extents.x1, mono.clip.extents.y1,
1483428d7b3dSmrg	     mono.clip.extents.x2, mono.clip.extents.y2,
1484428d7b3dSmrg	     dx, dy,
1485428d7b3dSmrg	     src_x + mono.clip.extents.x1 - dst_x - dx,
1486428d7b3dSmrg	     src_y + mono.clip.extents.y1 - dst_y - dy));
1487428d7b3dSmrg
1488428d7b3dSmrg	was_clear = sna_drawable_is_clear(dst->pDrawable);
1489428d7b3dSmrg
1490428d7b3dSmrg	if (!mono_init(&mono, 2*count))
1491428d7b3dSmrg		return false;
1492428d7b3dSmrg
1493428d7b3dSmrg	mono_add_line(&mono, dx, dy,
1494428d7b3dSmrg		      points[0].y, points[1].y,
1495428d7b3dSmrg		      &points[0], &points[1], -1);
1496428d7b3dSmrg	n = 2;
1497428d7b3dSmrg	do {
1498428d7b3dSmrg		mono_add_line(&mono, dx, dy,
1499428d7b3dSmrg			      points[n-2].y, points[n].y,
1500428d7b3dSmrg			      &points[n-2], &points[n], 1);
1501428d7b3dSmrg		if (++n == count)
1502428d7b3dSmrg			break;
1503428d7b3dSmrg
1504428d7b3dSmrg		mono_add_line(&mono, dx, dy,
1505428d7b3dSmrg			      points[n-2].y, points[n].y,
1506428d7b3dSmrg			      &points[n-2], &points[n], -1);
1507428d7b3dSmrg		if (++n == count)
1508428d7b3dSmrg			break;
1509428d7b3dSmrg	} while (1);
1510428d7b3dSmrg	mono_add_line(&mono, dx, dy,
1511428d7b3dSmrg		      points[n-2].y, points[n-1].y,
1512428d7b3dSmrg		      &points[n-2], &points[n-1], 1);
1513428d7b3dSmrg
1514428d7b3dSmrg	if (mono.sna->render.composite(mono.sna, op, src, NULL, dst,
1515428d7b3dSmrg				       src_x + mono.clip.extents.x1 - dst_x - dx,
1516428d7b3dSmrg				       src_y + mono.clip.extents.y1 - dst_y - dy,
1517428d7b3dSmrg				       0, 0,
1518428d7b3dSmrg				       mono.clip.extents.x1,  mono.clip.extents.y1,
1519428d7b3dSmrg				       mono.clip.extents.x2 - mono.clip.extents.x1,
1520428d7b3dSmrg				       mono.clip.extents.y2 - mono.clip.extents.y1,
1521428d7b3dSmrg				       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1522428d7b3dSmrg		if (mono.clip.data == NULL && mono.op.damage == NULL)
1523428d7b3dSmrg			mono.span = mono_span__fast;
1524428d7b3dSmrg		else
1525428d7b3dSmrg			mono.span = mono_span;
1526428d7b3dSmrg		mono_render(&mono);
1527428d7b3dSmrg		mono.op.done(mono.sna, &mono.op);
1528428d7b3dSmrg	}
1529428d7b3dSmrg
1530428d7b3dSmrg	if (!was_clear && !operator_is_bounded(op)) {
1531428d7b3dSmrg		xPointFixed p1, p2;
1532428d7b3dSmrg
1533428d7b3dSmrg		if (!mono_init(&mono, 2+2*count))
1534428d7b3dSmrg			return false;
1535428d7b3dSmrg
1536428d7b3dSmrg		p1.y = mono.clip.extents.y1 * pixman_fixed_1;
1537428d7b3dSmrg		p2.y = mono.clip.extents.y2 * pixman_fixed_1;
1538428d7b3dSmrg
1539428d7b3dSmrg		p1.x = mono.clip.extents.x1 * pixman_fixed_1;
1540428d7b3dSmrg		p2.x = mono.clip.extents.x1 * pixman_fixed_1;
1541428d7b3dSmrg		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
1542428d7b3dSmrg
1543428d7b3dSmrg		p1.x = mono.clip.extents.x2 * pixman_fixed_1;
1544428d7b3dSmrg		p2.x = mono.clip.extents.x2 * pixman_fixed_1;
1545428d7b3dSmrg		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
1546428d7b3dSmrg
1547428d7b3dSmrg		mono_add_line(&mono, dx, dy,
1548428d7b3dSmrg			      points[0].y, points[1].y,
1549428d7b3dSmrg			      &points[0], &points[1], -1);
1550428d7b3dSmrg		n = 2;
1551428d7b3dSmrg		do {
1552428d7b3dSmrg			mono_add_line(&mono, dx, dy,
1553428d7b3dSmrg				      points[n-2].y, points[n].y,
1554428d7b3dSmrg				      &points[n-2], &points[n], 1);
1555428d7b3dSmrg			if (++n == count)
1556428d7b3dSmrg				break;
1557428d7b3dSmrg
1558428d7b3dSmrg			mono_add_line(&mono, dx, dy,
1559428d7b3dSmrg				      points[n-2].y, points[n].y,
1560428d7b3dSmrg				      &points[n-2], &points[n], -1);
1561428d7b3dSmrg			if (++n == count)
1562428d7b3dSmrg				break;
1563428d7b3dSmrg		} while (1);
1564428d7b3dSmrg		mono_add_line(&mono, dx, dy,
1565428d7b3dSmrg			      points[n-2].y, points[n-1].y,
1566428d7b3dSmrg			      &points[n-2], &points[n-1], 1);
1567428d7b3dSmrg
1568428d7b3dSmrg		if (mono.sna->render.composite(mono.sna,
1569428d7b3dSmrg					       PictOpClear,
1570428d7b3dSmrg					       mono.sna->clear, NULL, dst,
1571428d7b3dSmrg					       0, 0,
1572428d7b3dSmrg					       0, 0,
1573428d7b3dSmrg					       mono.clip.extents.x1,  mono.clip.extents.y1,
1574428d7b3dSmrg					       mono.clip.extents.x2 - mono.clip.extents.x1,
1575428d7b3dSmrg					       mono.clip.extents.y2 - mono.clip.extents.y1,
1576428d7b3dSmrg					       COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) {
1577428d7b3dSmrg			if (mono.clip.data == NULL && mono.op.damage == NULL)
1578428d7b3dSmrg				mono.span = mono_span__fast;
1579428d7b3dSmrg			else
1580428d7b3dSmrg				mono.span = mono_span;
1581428d7b3dSmrg			mono_render(&mono);
1582428d7b3dSmrg			mono.op.done(mono.sna, &mono.op);
1583428d7b3dSmrg		}
1584428d7b3dSmrg		mono_fini(&mono);
1585428d7b3dSmrg	}
1586428d7b3dSmrg
1587428d7b3dSmrg	mono_fini(&mono);
1588428d7b3dSmrg	REGION_UNINIT(NULL, &mono.clip);
1589428d7b3dSmrg	return true;
1590428d7b3dSmrg}
1591