142542f5fSchristos/*
242542f5fSchristos * Copyright (c) 2007  David Turner
342542f5fSchristos * Copyright (c) 2008  M Joonas Pihlaja
442542f5fSchristos * Copyright (c) 2011 Intel Corporation
542542f5fSchristos *
642542f5fSchristos * Permission is hereby granted, free of charge, to any person obtaining a
742542f5fSchristos * copy of this software and associated documentation files (the "Software"),
842542f5fSchristos * to deal in the Software without restriction, including without limitation
942542f5fSchristos * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1042542f5fSchristos * and/or sell copies of the Software, and to permit persons to whom the
1142542f5fSchristos * Software is furnished to do so, subject to the following conditions:
1242542f5fSchristos *
1342542f5fSchristos * The above copyright notice and this permission notice (including the next
1442542f5fSchristos * paragraph) shall be included in all copies or substantial portions of the
1542542f5fSchristos * Software.
1642542f5fSchristos *
1742542f5fSchristos * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1842542f5fSchristos * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1942542f5fSchristos * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2042542f5fSchristos * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2142542f5fSchristos * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2242542f5fSchristos * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2342542f5fSchristos * SOFTWARE.
2442542f5fSchristos *
2542542f5fSchristos * Authors:
2642542f5fSchristos *    Chris Wilson <chris@chris-wilson.co.uk>
2742542f5fSchristos *
2842542f5fSchristos */
2942542f5fSchristos
3042542f5fSchristos#ifdef HAVE_CONFIG_H
3142542f5fSchristos#include "config.h"
3242542f5fSchristos#endif
3342542f5fSchristos
3442542f5fSchristos#include "sna.h"
3542542f5fSchristos#include "sna_render.h"
3642542f5fSchristos#include "sna_render_inline.h"
3742542f5fSchristos#include "sna_trapezoids.h"
3842542f5fSchristos#include "fb/fbpict.h"
3942542f5fSchristos
4042542f5fSchristos#include <mipict.h>
4142542f5fSchristos
4242542f5fSchristos#undef FAST_SAMPLES_X
4342542f5fSchristos#undef FAST_SAMPLES_Y
4442542f5fSchristos
4542542f5fSchristos/* TODO: Emit unantialiased and MSAA triangles. */
4642542f5fSchristos
4742542f5fSchristos#ifndef MAX
4842542f5fSchristos#define MAX(x,y) ((x) >= (y) ? (x) : (y))
4942542f5fSchristos#endif
5042542f5fSchristos
5142542f5fSchristos#ifndef MIN
5242542f5fSchristos#define MIN(x,y) ((x) <= (y) ? (x) : (y))
5342542f5fSchristos#endif
5442542f5fSchristos
5542542f5fSchristos#define _GRID_TO_INT_FRAC(t, i, f, m) do {      \
5642542f5fSchristos	(i) = (t) / (m);                   \
5742542f5fSchristos	(f) = (t) % (m);                   \
5842542f5fSchristos	if ((f) < 0) {                     \
5942542f5fSchristos		--(i);                     \
6042542f5fSchristos		(f) += (m);                \
6142542f5fSchristos	}                                  \
6242542f5fSchristos} while (0)
6342542f5fSchristos
6442542f5fSchristos#define GRID_AREA (2*SAMPLES_X*SAMPLES_Y)
6542542f5fSchristos
6642542f5fSchristosstatic inline int pixman_fixed_to_grid_x(pixman_fixed_t v)
6742542f5fSchristos{
6813496ba1Ssnj	return ((int64_t)v * SAMPLES_X + (1<<15)) >> 16;
6942542f5fSchristos}
7042542f5fSchristos
7142542f5fSchristosstatic inline int pixman_fixed_to_grid_y(pixman_fixed_t v)
7242542f5fSchristos{
7313496ba1Ssnj	return ((int64_t)v * SAMPLES_Y + (1<<15)) >> 16;
7442542f5fSchristos}
7542542f5fSchristos
7642542f5fSchristostypedef void (*span_func_t)(struct sna *sna,
7742542f5fSchristos			    struct sna_composite_spans_op *op,
7842542f5fSchristos			    pixman_region16_t *clip,
7942542f5fSchristos			    const BoxRec *box,
8042542f5fSchristos			    int coverage);
8142542f5fSchristos
8242542f5fSchristos#if HAS_DEBUG_FULL
8342542f5fSchristosstatic void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function)
8442542f5fSchristos{
8542542f5fSchristos	if (box->x1 < 0 || box->y1 < 0 ||
8642542f5fSchristos	    box->x2 > pixmap->drawable.width ||
8742542f5fSchristos	    box->y2 > pixmap->drawable.height)
8842542f5fSchristos	{
8942542f5fSchristos		FatalError("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n",
9042542f5fSchristos			   function,
9142542f5fSchristos			   box->x1, box->y1, box->x2, box->y2,
9242542f5fSchristos			   pixmap->drawable.width,
9342542f5fSchristos			   pixmap->drawable.height);
9442542f5fSchristos	}
9542542f5fSchristos}
9642542f5fSchristos#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__)
9742542f5fSchristos#else
9842542f5fSchristos#define assert_pixmap_contains_box(p, b)
9942542f5fSchristos#endif
10042542f5fSchristos
10142542f5fSchristosstatic void apply_damage(struct sna_composite_op *op, RegionPtr region)
10242542f5fSchristos{
10342542f5fSchristos	DBG(("%s: damage=%p, region=%dx[(%d, %d), (%d, %d)]\n",
10442542f5fSchristos	     __FUNCTION__, op->damage,
10542542f5fSchristos	     region_num_rects(region),
10642542f5fSchristos	     region->extents.x1, region->extents.y1,
10742542f5fSchristos	     region->extents.x2, region->extents.y2));
10842542f5fSchristos
10942542f5fSchristos	if (op->damage == NULL)
11042542f5fSchristos		return;
11142542f5fSchristos
11242542f5fSchristos	RegionTranslate(region, op->dst.x, op->dst.y);
11342542f5fSchristos
11442542f5fSchristos	assert_pixmap_contains_box(op->dst.pixmap, RegionExtents(region));
11542542f5fSchristos	sna_damage_add(op->damage, region);
11642542f5fSchristos}
11742542f5fSchristos
11842542f5fSchristosstatic void _apply_damage_box(struct sna_composite_op *op, const BoxRec *box)
11942542f5fSchristos{
12042542f5fSchristos	BoxRec r;
12142542f5fSchristos
12242542f5fSchristos	r.x1 = box->x1 + op->dst.x;
12342542f5fSchristos	r.x2 = box->x2 + op->dst.x;
12442542f5fSchristos	r.y1 = box->y1 + op->dst.y;
12542542f5fSchristos	r.y2 = box->y2 + op->dst.y;
12642542f5fSchristos
12742542f5fSchristos	assert_pixmap_contains_box(op->dst.pixmap, &r);
12842542f5fSchristos	sna_damage_add_box(op->damage, &r);
12942542f5fSchristos}
13042542f5fSchristos
13142542f5fSchristosinline static void apply_damage_box(struct sna_composite_op *op, const BoxRec *box)
13242542f5fSchristos{
13342542f5fSchristos	if (op->damage)
13442542f5fSchristos		_apply_damage_box(op, box);
13542542f5fSchristos}
13642542f5fSchristos
13742542f5fSchristos#define SAMPLES_X_TO_INT_FRAC(x, i, f) \
13842542f5fSchristos	_GRID_TO_INT_FRAC(x, i, f, SAMPLES_X)
13942542f5fSchristos
14042542f5fSchristos#define AREA_TO_FLOAT(c)  ((c) / (float)GRID_AREA)
14142542f5fSchristos#define TO_ALPHA(c) (((c)+1) >> 1)
14242542f5fSchristos
14342542f5fSchristosstruct quorem {
14413496ba1Ssnj	int64_t quo;
14513496ba1Ssnj	int64_t rem;
14642542f5fSchristos};
14742542f5fSchristos
14842542f5fSchristosstruct edge {
14942542f5fSchristos	struct edge *next, *prev;
15042542f5fSchristos
15142542f5fSchristos	int dir;
15242542f5fSchristos
15342542f5fSchristos	int height_left;
15442542f5fSchristos
15513496ba1Ssnj	int cell;
15642542f5fSchristos	struct quorem x;
15742542f5fSchristos
15842542f5fSchristos	/* Advance of the current x when moving down a subsample line. */
15942542f5fSchristos	struct quorem dxdy;
16013496ba1Ssnj	int64_t dy;
16142542f5fSchristos
16242542f5fSchristos	/* The clipped y of the top of the edge. */
16342542f5fSchristos	int ytop;
16442542f5fSchristos
16542542f5fSchristos	/* y2-y1 after orienting the edge downwards.  */
16642542f5fSchristos};
16742542f5fSchristos
16842542f5fSchristos/* Number of subsample rows per y-bucket. Must be SAMPLES_Y. */
16942542f5fSchristos#define EDGE_Y_BUCKET_HEIGHT SAMPLES_Y
17042542f5fSchristos#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
17142542f5fSchristos
17242542f5fSchristos/* A collection of sorted and vertically clipped edges of the polygon.
17342542f5fSchristos * Edges are moved from the polygon to an active list while scan
17442542f5fSchristos * converting. */
17542542f5fSchristosstruct polygon {
17642542f5fSchristos	/* The vertical clip extents. */
17742542f5fSchristos	int ymin, ymax;
17842542f5fSchristos
17942542f5fSchristos	/* Array of edges all starting in the same bucket.	An edge is put
18042542f5fSchristos	 * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
18142542f5fSchristos	 * it is added to the polygon. */
18242542f5fSchristos	struct edge **y_buckets;
18342542f5fSchristos	struct edge *y_buckets_embedded[64];
18442542f5fSchristos
18542542f5fSchristos	struct edge edges_embedded[32];
18642542f5fSchristos	struct edge *edges;
18742542f5fSchristos	int num_edges;
18842542f5fSchristos};
18942542f5fSchristos
19042542f5fSchristos/* A cell records the effect on pixel coverage of polygon edges
19142542f5fSchristos * passing through a pixel.  It contains two accumulators of pixel
19242542f5fSchristos * coverage.
19342542f5fSchristos *
19442542f5fSchristos * Consider the effects of a polygon edge on the coverage of a pixel
19542542f5fSchristos * it intersects and that of the following one.  The coverage of the
19642542f5fSchristos * following pixel is the height of the edge multiplied by the width
19742542f5fSchristos * of the pixel, and the coverage of the pixel itself is the area of
19842542f5fSchristos * the trapezoid formed by the edge and the right side of the pixel.
19942542f5fSchristos *
20042542f5fSchristos * +-----------------------+-----------------------+
20142542f5fSchristos * |                       |                       |
20242542f5fSchristos * |                       |                       |
20342542f5fSchristos * |_______________________|_______________________|
20442542f5fSchristos * |   \...................|.......................|\
20542542f5fSchristos * |    \..................|.......................| |
20642542f5fSchristos * |     \.................|.......................| |
20742542f5fSchristos * |      \....covered.....|.......................| |
20842542f5fSchristos * |       \....area.......|.......................| } covered height
20942542f5fSchristos * |        \..............|.......................| |
21042542f5fSchristos * |uncovered\.............|.......................| |
21142542f5fSchristos * |  area    \............|.......................| |
21242542f5fSchristos * |___________\...........|.......................|/
21342542f5fSchristos * |                       |                       |
21442542f5fSchristos * |                       |                       |
21542542f5fSchristos * |                       |                       |
21642542f5fSchristos * +-----------------------+-----------------------+
21742542f5fSchristos *
21842542f5fSchristos * Since the coverage of the following pixel will always be a multiple
21942542f5fSchristos * of the width of the pixel, we can store the height of the covered
22042542f5fSchristos * area instead.  The coverage of the pixel itself is the total
22142542f5fSchristos * coverage minus the area of the uncovered area to the left of the
22242542f5fSchristos * edge.  As it's faster to compute the uncovered area we only store
22342542f5fSchristos * that and subtract it from the total coverage later when forming
22442542f5fSchristos * spans to blit.
22542542f5fSchristos *
22642542f5fSchristos * The heights and areas are signed, with left edges of the polygon
22742542f5fSchristos * having positive sign and right edges having negative sign.  When
22842542f5fSchristos * two edges intersect they swap their left/rightness so their
22942542f5fSchristos * contribution above and below the intersection point must be
23042542f5fSchristos * computed separately. */
23142542f5fSchristosstruct cell {
23242542f5fSchristos	struct cell *next;
23342542f5fSchristos	int x;
23442542f5fSchristos	int16_t uncovered_area;
23542542f5fSchristos	int16_t covered_height;
23642542f5fSchristos};
23742542f5fSchristos
23842542f5fSchristos/* A cell list represents the scan line sparsely as cells ordered by
23942542f5fSchristos * ascending x.  It is geared towards scanning the cells in order
24042542f5fSchristos * using an internal cursor. */
24142542f5fSchristosstruct cell_list {
24242542f5fSchristos	struct cell *cursor;
24342542f5fSchristos
24442542f5fSchristos	/* Points to the left-most cell in the scan line. */
24542542f5fSchristos	struct cell head, tail;
24642542f5fSchristos
24742542f5fSchristos	int16_t x1, x2;
24842542f5fSchristos	int16_t count, size;
24942542f5fSchristos	struct cell *cells;
25042542f5fSchristos	struct cell embedded[256];
25142542f5fSchristos};
25242542f5fSchristos
25342542f5fSchristos/* The active list contains edges in the current scan line ordered by
25442542f5fSchristos * the x-coordinate of the intercept of the edge and the scan line. */
25542542f5fSchristosstruct active_list {
25642542f5fSchristos	/* Leftmost edge on the current scan line. */
25742542f5fSchristos	struct edge head, tail;
25842542f5fSchristos};
25942542f5fSchristos
26042542f5fSchristosstruct tor {
26142542f5fSchristos    struct polygon	polygon[1];
26242542f5fSchristos    struct active_list	active[1];
26342542f5fSchristos    struct cell_list	coverages[1];
26442542f5fSchristos
26542542f5fSchristos    BoxRec extents;
26642542f5fSchristos};
26742542f5fSchristos
26842542f5fSchristos/* Rewinds the cell list's cursor to the beginning.  After rewinding
26942542f5fSchristos * we're good to cell_list_find() the cell any x coordinate. */
27042542f5fSchristosinline static void
27142542f5fSchristoscell_list_rewind(struct cell_list *cells)
27242542f5fSchristos{
27342542f5fSchristos	cells->cursor = &cells->head;
27442542f5fSchristos}
27542542f5fSchristos
27642542f5fSchristosstatic bool
27742542f5fSchristoscell_list_init(struct cell_list *cells, int x1, int x2)
27842542f5fSchristos{
27942542f5fSchristos	cells->tail.next = NULL;
28042542f5fSchristos	cells->tail.x = INT_MAX;
28142542f5fSchristos	cells->head.x = INT_MIN;
28242542f5fSchristos	cells->head.next = &cells->tail;
28342542f5fSchristos	cells->head.covered_height = 0;
28442542f5fSchristos	cell_list_rewind(cells);
28542542f5fSchristos	cells->count = 0;
28642542f5fSchristos	cells->x1 = x1;
28742542f5fSchristos	cells->x2 = x2;
28842542f5fSchristos	cells->size = x2 - x1 + 1;
28942542f5fSchristos	cells->cells = cells->embedded;
29042542f5fSchristos	if (cells->size > ARRAY_SIZE(cells->embedded))
29142542f5fSchristos		cells->cells = malloc(cells->size * sizeof(struct cell));
29242542f5fSchristos	return cells->cells != NULL;
29342542f5fSchristos}
29442542f5fSchristos
29542542f5fSchristosstatic void
29642542f5fSchristoscell_list_fini(struct cell_list *cells)
29742542f5fSchristos{
29842542f5fSchristos	if (cells->cells != cells->embedded)
29942542f5fSchristos		free(cells->cells);
30042542f5fSchristos}
30142542f5fSchristos
30242542f5fSchristosinline static void
30342542f5fSchristoscell_list_reset(struct cell_list *cells)
30442542f5fSchristos{
30542542f5fSchristos	cell_list_rewind(cells);
30642542f5fSchristos	cells->head.next = &cells->tail;
30742542f5fSchristos	cells->head.covered_height = 0;
30842542f5fSchristos	cells->count = 0;
30942542f5fSchristos}
31042542f5fSchristos
31142542f5fSchristosinline static struct cell *
31242542f5fSchristoscell_list_alloc(struct cell_list *cells,
31342542f5fSchristos		struct cell *tail,
31442542f5fSchristos		int x)
31542542f5fSchristos{
31642542f5fSchristos	struct cell *cell;
31742542f5fSchristos
31842542f5fSchristos	assert(cells->count < cells->size);
31942542f5fSchristos	cell = cells->cells + cells->count++;
32042542f5fSchristos	cell->next = tail->next;
32142542f5fSchristos	tail->next = cell;
32242542f5fSchristos
32342542f5fSchristos	cell->x = x;
32442542f5fSchristos	cell->covered_height = 0;
32542542f5fSchristos	cell->uncovered_area = 0;
32642542f5fSchristos	return cell;
32742542f5fSchristos}
32842542f5fSchristos
32942542f5fSchristos/* Find a cell at the given x-coordinate.  Returns %NULL if a new cell
33042542f5fSchristos * needed to be allocated but couldn't be.  Cells must be found with
33142542f5fSchristos * non-decreasing x-coordinate until the cell list is rewound using
33242542f5fSchristos * cell_list_rewind(). Ownership of the returned cell is retained by
33342542f5fSchristos * the cell list. */
33442542f5fSchristosinline static struct cell *
33542542f5fSchristoscell_list_find(struct cell_list *cells, int x)
33642542f5fSchristos{
33742542f5fSchristos	struct cell *tail;
33842542f5fSchristos
33942542f5fSchristos	if (x >= cells->x2)
34042542f5fSchristos		return &cells->tail;
34142542f5fSchristos
34242542f5fSchristos	if (x < cells->x1)
34342542f5fSchristos		return &cells->head;
34442542f5fSchristos
34542542f5fSchristos	tail = cells->cursor;
34642542f5fSchristos	if (tail->x == x)
34742542f5fSchristos		return tail;
34842542f5fSchristos
34942542f5fSchristos	do {
35042542f5fSchristos		if (tail->next->x > x)
35142542f5fSchristos			break;
35242542f5fSchristos
35342542f5fSchristos		tail = tail->next;
35442542f5fSchristos		if (tail->next->x > x)
35542542f5fSchristos			break;
35642542f5fSchristos
35742542f5fSchristos		tail = tail->next;
35842542f5fSchristos		if (tail->next->x > x)
35942542f5fSchristos			break;
36042542f5fSchristos
36142542f5fSchristos		tail = tail->next;
36242542f5fSchristos	} while (1);
36342542f5fSchristos
36442542f5fSchristos	if (tail->x != x)
36542542f5fSchristos		tail = cell_list_alloc(cells, tail, x);
36642542f5fSchristos
36742542f5fSchristos	return cells->cursor = tail;
36842542f5fSchristos}
36942542f5fSchristos
37042542f5fSchristos/* Add a subpixel span covering [x1, x2) to the coverage cells. */
37142542f5fSchristosinline static void
37242542f5fSchristoscell_list_add_subspan(struct cell_list *cells, int x1, int x2)
37342542f5fSchristos{
37442542f5fSchristos	struct cell *cell;
37542542f5fSchristos	int ix1, fx1;
37642542f5fSchristos	int ix2, fx2;
37742542f5fSchristos
37842542f5fSchristos	if (x1 == x2)
37942542f5fSchristos		return;
38042542f5fSchristos
38142542f5fSchristos	SAMPLES_X_TO_INT_FRAC(x1, ix1, fx1);
38242542f5fSchristos	SAMPLES_X_TO_INT_FRAC(x2, ix2, fx2);
38342542f5fSchristos
38442542f5fSchristos	__DBG(("%s: x1=%d (%d+%d), x2=%d (%d+%d)\n", __FUNCTION__,
38542542f5fSchristos	       x1, ix1, fx1, x2, ix2, fx2));
38642542f5fSchristos
38742542f5fSchristos	cell = cell_list_find(cells, ix1);
38842542f5fSchristos	if (ix1 != ix2) {
38942542f5fSchristos		cell->uncovered_area += 2*fx1;
39042542f5fSchristos		++cell->covered_height;
39142542f5fSchristos
39242542f5fSchristos		cell = cell_list_find(cells, ix2);
39342542f5fSchristos		cell->uncovered_area -= 2*fx2;
39442542f5fSchristos		--cell->covered_height;
39542542f5fSchristos	} else
39642542f5fSchristos		cell->uncovered_area += 2*(fx1-fx2);
39742542f5fSchristos}
39842542f5fSchristos
39942542f5fSchristosinline static void
40042542f5fSchristoscell_list_add_span(struct cell_list *cells, int x1, int x2)
40142542f5fSchristos{
40242542f5fSchristos	struct cell *cell;
40342542f5fSchristos	int ix1, fx1;
40442542f5fSchristos	int ix2, fx2;
40542542f5fSchristos
40642542f5fSchristos	SAMPLES_X_TO_INT_FRAC(x1, ix1, fx1);
40742542f5fSchristos	SAMPLES_X_TO_INT_FRAC(x2, ix2, fx2);
40842542f5fSchristos
40942542f5fSchristos	__DBG(("%s: x1=%d (%d+%d), x2=%d (%d+%d)\n", __FUNCTION__,
41042542f5fSchristos	       x1, ix1, fx1, x2, ix2, fx2));
41142542f5fSchristos
41242542f5fSchristos	cell = cell_list_find(cells, ix1);
41342542f5fSchristos	if (ix1 != ix2) {
41442542f5fSchristos		cell->uncovered_area += 2*fx1*SAMPLES_Y;
41542542f5fSchristos		cell->covered_height += SAMPLES_Y;
41642542f5fSchristos
41742542f5fSchristos		cell = cell_list_find(cells, ix2);
41842542f5fSchristos		cell->uncovered_area -= 2*fx2*SAMPLES_Y;
41942542f5fSchristos		cell->covered_height -= SAMPLES_Y;
42042542f5fSchristos	} else
42142542f5fSchristos		cell->uncovered_area += 2*(fx1-fx2)*SAMPLES_Y;
42242542f5fSchristos}
42342542f5fSchristos
42442542f5fSchristosstatic void
42542542f5fSchristospolygon_fini(struct polygon *polygon)
42642542f5fSchristos{
42742542f5fSchristos	if (polygon->y_buckets != polygon->y_buckets_embedded)
42842542f5fSchristos		free(polygon->y_buckets);
42942542f5fSchristos
43042542f5fSchristos	if (polygon->edges != polygon->edges_embedded)
43142542f5fSchristos		free(polygon->edges);
43242542f5fSchristos}
43342542f5fSchristos
43442542f5fSchristosstatic bool
43542542f5fSchristospolygon_init(struct polygon *polygon, int num_edges, int ymin, int ymax)
43642542f5fSchristos{
43742542f5fSchristos	unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax-1, ymin) + 1;
43842542f5fSchristos
43942542f5fSchristos	if (unlikely(ymax - ymin > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
44042542f5fSchristos		return false;
44142542f5fSchristos
44242542f5fSchristos	polygon->edges = polygon->edges_embedded;
44342542f5fSchristos	polygon->y_buckets = polygon->y_buckets_embedded;
44442542f5fSchristos
44542542f5fSchristos	polygon->num_edges = 0;
44642542f5fSchristos	if (num_edges > (int)ARRAY_SIZE(polygon->edges_embedded)) {
44742542f5fSchristos		polygon->edges = malloc(sizeof(struct edge)*num_edges);
44842542f5fSchristos		if (unlikely(NULL == polygon->edges))
44942542f5fSchristos			goto bail_no_mem;
45042542f5fSchristos	}
45142542f5fSchristos
45242542f5fSchristos	if (num_buckets >= ARRAY_SIZE(polygon->y_buckets_embedded)) {
45342542f5fSchristos		polygon->y_buckets = malloc((1+num_buckets)*sizeof(struct edge *));
45442542f5fSchristos		if (unlikely(NULL == polygon->y_buckets))
45542542f5fSchristos			goto bail_no_mem;
45642542f5fSchristos	}
45742542f5fSchristos	memset(polygon->y_buckets, 0, num_buckets * sizeof(struct edge *));
45842542f5fSchristos	polygon->y_buckets[num_buckets] = (void *)-1;
45942542f5fSchristos
46042542f5fSchristos	polygon->ymin = ymin;
46142542f5fSchristos	polygon->ymax = ymax;
46242542f5fSchristos	return true;
46342542f5fSchristos
46442542f5fSchristosbail_no_mem:
46542542f5fSchristos	polygon_fini(polygon);
46642542f5fSchristos	return false;
46742542f5fSchristos}
46842542f5fSchristos
46942542f5fSchristosstatic void
47042542f5fSchristos_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon, struct edge *e)
47142542f5fSchristos{
47242542f5fSchristos	unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
47342542f5fSchristos	struct edge **ptail = &polygon->y_buckets[ix];
47442542f5fSchristos	assert(e->ytop < polygon->ymax);
47542542f5fSchristos	e->next = *ptail;
47642542f5fSchristos	*ptail = e;
47742542f5fSchristos}
47842542f5fSchristos
47913496ba1Ssnjstatic inline int edge_to_cell(struct edge *e)
48013496ba1Ssnj{
48113496ba1Ssnj	int x = e->x.quo;
48213496ba1Ssnj	if (e->x.rem > e->dy/2)
48313496ba1Ssnj		x++;
48413496ba1Ssnj	__DBG(("%s: %lld.%lld -> %d\n",
48513496ba1Ssnj	       __FUNCTION__, e->x.quo, e->x.rem, x));
48613496ba1Ssnj	return x;
48713496ba1Ssnj}
48813496ba1Ssnj
48913496ba1Ssnjstatic inline int edge_advance(struct edge *e)
49013496ba1Ssnj{
49113496ba1Ssnj	__DBG(("%s: %lld.%lld + %lld.%lld\n",
49213496ba1Ssnj	       __FUNCTION__, e->x.quo, e->x.rem, e->dxdy.quo, e->dxdy.rem));
49313496ba1Ssnj
49413496ba1Ssnj	e->x.quo += e->dxdy.quo;
49513496ba1Ssnj	e->x.rem += e->dxdy.rem;
49613496ba1Ssnj	if (e->x.rem < 0) {
49713496ba1Ssnj		e->x.quo--;
49813496ba1Ssnj		e->x.rem += e->dy;
49913496ba1Ssnj	} else if (e->x.rem >= e->dy) {
50013496ba1Ssnj		e->x.quo++;
50113496ba1Ssnj		e->x.rem -= e->dy;
50213496ba1Ssnj	}
50313496ba1Ssnj	assert(e->x.rem >= 0 && e->x.rem < e->dy);
50413496ba1Ssnj	return edge_to_cell(e);
50513496ba1Ssnj}
50613496ba1Ssnj
50742542f5fSchristosinline static void
50842542f5fSchristospolygon_add_edge(struct polygon *polygon,
50913496ba1Ssnj		 const xTrapezoid *t,
51013496ba1Ssnj		 const xLineFixed *edge,
51113496ba1Ssnj		 int dir, int dx, int dy)
51242542f5fSchristos{
51342542f5fSchristos	struct edge *e = &polygon->edges[polygon->num_edges];
51413496ba1Ssnj	const int ymin = polygon->ymin;
51513496ba1Ssnj	const int ymax = polygon->ymax;
51642542f5fSchristos	int ytop, ybot;
51742542f5fSchristos
51813496ba1Ssnj	assert(t->bottom > t->top);
51913496ba1Ssnj	assert(edge->p2.y > edge->p1.y);
52042542f5fSchristos
52113496ba1Ssnj	ytop = pixman_fixed_to_grid_y(t->top) + dy;
52213496ba1Ssnj	if (ytop < ymin)
52313496ba1Ssnj		ytop = ymin;
52413496ba1Ssnj
52513496ba1Ssnj	ybot = pixman_fixed_to_grid_y(t->bottom) + dy;
52613496ba1Ssnj	if (ybot > ymax)
52713496ba1Ssnj		ybot = ymax;
52813496ba1Ssnj
52913496ba1Ssnj	__DBG(("%s: dx=(%d, %d), y=[%d, %d] +%d, -%d\n",
53013496ba1Ssnj	       __FUNCTION__, dx, dy, ytop, ybot,
53113496ba1Ssnj	       ((int64_t)(ytop - dy)<<16) / SAMPLES_Y - edge->p1.y,
53213496ba1Ssnj	       ((int64_t)(ybot - dy)<<16) / SAMPLES_Y - edge->p2.y));
53342542f5fSchristos
53442542f5fSchristos	e->ytop = ytop;
53542542f5fSchristos	e->height_left = ybot - ytop;
53642542f5fSchristos	if (e->height_left <= 0)
53742542f5fSchristos		return;
53842542f5fSchristos
53913496ba1Ssnj	if (pixman_fixed_to_grid_x(edge->p1.x) ==
54013496ba1Ssnj	    pixman_fixed_to_grid_x(edge->p2.x)) {
54113496ba1Ssnj		e->cell = pixman_fixed_to_grid_x(edge->p1.x) + dx;
54213496ba1Ssnj		e->x.quo = e->x.rem = 0;
54313496ba1Ssnj		e->dxdy.quo = e->dxdy.rem = 0;
54442542f5fSchristos		e->dy = 0;
54542542f5fSchristos	} else {
54613496ba1Ssnj		int64_t Ey, Ex, tmp;
54713496ba1Ssnj
54813496ba1Ssnj		__DBG(("%s: add diagonal edge (%d, %d) -> (%d, %d) [(%d, %d)]\n",
54913496ba1Ssnj
55013496ba1Ssnj		       __FUNCTION__,
55113496ba1Ssnj		       edge->p1.x, edge->p1.y,
55213496ba1Ssnj		       edge->p2.x, edge->p2.y,
55313496ba1Ssnj		       edge->p2.x - edge->p1.x,
55413496ba1Ssnj		       edge->p2.y - edge->p1.y));
55513496ba1Ssnj
55613496ba1Ssnj		Ex = ((int64_t)edge->p2.x - edge->p1.x) * SAMPLES_X;
55713496ba1Ssnj		Ey = ((int64_t)edge->p2.y - edge->p1.y) * SAMPLES_Y * (2 << 16);
55813496ba1Ssnj		assert(Ey > 0);
55913496ba1Ssnj		e->dxdy.quo = Ex * (2 << 16) / Ey;
56013496ba1Ssnj		e->dxdy.rem = Ex * (2 << 16) % Ey;
56113496ba1Ssnj
56213496ba1Ssnj		tmp = (int64_t)(2*(ytop - dy) + 1) << 16;
56313496ba1Ssnj		tmp -= (int64_t)edge->p1.y * SAMPLES_Y*2;
56413496ba1Ssnj		tmp *= Ex;
56513496ba1Ssnj		e->x.quo = tmp / Ey;
56613496ba1Ssnj		e->x.rem = tmp % Ey;
56713496ba1Ssnj
56813496ba1Ssnj		tmp = (int64_t)edge->p1.x * SAMPLES_X;
56913496ba1Ssnj		e->x.quo += (tmp >> 16) + dx;
57013496ba1Ssnj		tmp &= (1 << 16) - 1;
57113496ba1Ssnj		if (tmp) {
57213496ba1Ssnj			if (Ey < INT64_MAX >> 16)
57313496ba1Ssnj				tmp = (tmp * Ey) / (1 << 16);
57413496ba1Ssnj			else /* Handle overflow by losing precision */
57513496ba1Ssnj				tmp = tmp * (Ey / (1 << 16));
57613496ba1Ssnj			e->x.rem += tmp;
57713496ba1Ssnj		}
57813496ba1Ssnj
57913496ba1Ssnj		if (e->x.rem < 0) {
58013496ba1Ssnj			e->x.quo--;
58113496ba1Ssnj			e->x.rem += Ey;
58213496ba1Ssnj		} else if (e->x.rem >= Ey) {
58313496ba1Ssnj			e->x.quo++;
58413496ba1Ssnj			e->x.rem -= Ey;
58542542f5fSchristos		}
58613496ba1Ssnj		assert(e->x.rem >= 0 && e->x.rem < Ey);
58713496ba1Ssnj
58813496ba1Ssnj		e->dy = Ey;
58913496ba1Ssnj		e->cell = edge_to_cell(e);
59013496ba1Ssnj
59113496ba1Ssnj		__DBG(("%s: x=%lld.%lld + %lld.%lld %lld -> cell=%d\n",
59213496ba1Ssnj		       __FUNCTION__,
59313496ba1Ssnj		       (long long)e->x.quo,
59413496ba1Ssnj		       (long long)e->x.rem,
59513496ba1Ssnj		       (long long)e->dxdy.quo,
59613496ba1Ssnj		       (long long)e->dxdy.rem,
59713496ba1Ssnj		       (long long)Ey, e->cell));
59842542f5fSchristos	}
59913496ba1Ssnj
60013496ba1Ssnj	e->dir = dir;
60142542f5fSchristos
60242542f5fSchristos	_polygon_insert_edge_into_its_y_bucket(polygon, e);
60342542f5fSchristos	polygon->num_edges++;
60442542f5fSchristos}
60542542f5fSchristos
60642542f5fSchristosinline static void
60742542f5fSchristospolygon_add_line(struct polygon *polygon,
60842542f5fSchristos		 const xPointFixed *p1,
60913496ba1Ssnj		 const xPointFixed *p2,
61013496ba1Ssnj		 int dx, int dy)
61142542f5fSchristos{
61242542f5fSchristos	struct edge *e = &polygon->edges[polygon->num_edges];
61313496ba1Ssnj	int ytop, ybot;
61442542f5fSchristos
61513496ba1Ssnj	if (p1->y == p2->y)
61642542f5fSchristos		return;
61742542f5fSchristos
61842542f5fSchristos	__DBG(("%s: line=(%d, %d), (%d, %d)\n",
61942542f5fSchristos	       __FUNCTION__, (int)p1->x, (int)p1->y, (int)p2->x, (int)p2->y));
62042542f5fSchristos
62142542f5fSchristos	e->dir = 1;
62213496ba1Ssnj	if (p2->y < p1->y) {
62342542f5fSchristos		const xPointFixed *t;
62442542f5fSchristos
62542542f5fSchristos		e->dir = -1;
62642542f5fSchristos
62742542f5fSchristos		t = p1;
62842542f5fSchristos		p1 = p2;
62942542f5fSchristos		p2 = t;
63042542f5fSchristos	}
63142542f5fSchristos
63213496ba1Ssnj	ytop = pixman_fixed_to_grid_y(p1->y) + dy;
63313496ba1Ssnj	if (ytop < polygon->ymin)
63413496ba1Ssnj		ytop = polygon->ymin;
63513496ba1Ssnj
63613496ba1Ssnj	ybot = pixman_fixed_to_grid_y(p2->y) + dy;
63713496ba1Ssnj	if (ybot > polygon->ymax)
63813496ba1Ssnj		ybot = polygon->ymax;
63913496ba1Ssnj
64013496ba1Ssnj	if (ybot <= ytop)
64142542f5fSchristos		return;
64242542f5fSchristos
64313496ba1Ssnj	e->ytop = ytop;
64413496ba1Ssnj	e->height_left = ybot - ytop;
64542542f5fSchristos	if (e->height_left <= 0)
64642542f5fSchristos		return;
64742542f5fSchristos
64813496ba1Ssnj	__DBG(("%s: edge height=%d\n", __FUNCTION__, e->dir * e->height_left));
64913496ba1Ssnj
65013496ba1Ssnj	if (pixman_fixed_to_grid_x(p1->x) == pixman_fixed_to_grid_x(p2->x)) {
65113496ba1Ssnj		e->cell = pixman_fixed_to_grid_x(p1->x);
65213496ba1Ssnj		e->x.quo = e->x.rem = 0;
65313496ba1Ssnj		e->dxdy.quo = e->dxdy.rem = 0;
65442542f5fSchristos		e->dy = 0;
65542542f5fSchristos	} else {
65613496ba1Ssnj		int64_t Ey, Ex, tmp;
65713496ba1Ssnj
65813496ba1Ssnj		__DBG(("%s: add diagonal line (%d, %d) -> (%d, %d) [(%d, %d)]\n",
65913496ba1Ssnj
66013496ba1Ssnj		       __FUNCTION__,
66113496ba1Ssnj		       p1->x, p1->y,
66213496ba1Ssnj		       p2->x, p2->y,
66313496ba1Ssnj		       p2->x - p1->x,
66413496ba1Ssnj		       p2->y - p1->y));
66513496ba1Ssnj
66613496ba1Ssnj		Ex = ((int64_t)p2->x - p1->x) * SAMPLES_X;
66713496ba1Ssnj		Ey = ((int64_t)p2->y - p1->y) * SAMPLES_Y * (2 << 16);
66813496ba1Ssnj		e->dxdy.quo = Ex * (2 << 16) / Ey;
66913496ba1Ssnj		e->dxdy.rem = Ex * (2 << 16) % Ey;
67013496ba1Ssnj
67113496ba1Ssnj		tmp = (int64_t)(2*(ytop - dy) + 1) << 16;
67213496ba1Ssnj		tmp -= (int64_t)p1->y * SAMPLES_Y*2;
67313496ba1Ssnj		tmp *= Ex;
67413496ba1Ssnj		e->x.quo = tmp / Ey;
67513496ba1Ssnj		e->x.rem = tmp % Ey;
67613496ba1Ssnj
67713496ba1Ssnj		tmp = (int64_t)p1->x * SAMPLES_X;
67813496ba1Ssnj		e->x.quo += (tmp >> 16) + dx;
67913496ba1Ssnj		e->x.rem += ((tmp & ((1 << 16) - 1)) * Ey) / (1 << 16);
68013496ba1Ssnj
68113496ba1Ssnj		if (e->x.rem < 0) {
68213496ba1Ssnj			e->x.quo--;
68313496ba1Ssnj			e->x.rem += Ey;
68413496ba1Ssnj		} else if (e->x.rem >= Ey) {
68513496ba1Ssnj			e->x.quo++;
68613496ba1Ssnj			e->x.rem -= Ey;
68742542f5fSchristos		}
68813496ba1Ssnj		assert(e->x.rem >= 0 && e->x.rem < Ey);
68913496ba1Ssnj
69013496ba1Ssnj		e->dy = Ey;
69113496ba1Ssnj		e->cell = edge_to_cell(e);
69213496ba1Ssnj
69313496ba1Ssnj		__DBG(("%s: x=%lld.%lld + %lld.%lld %lld -> cell=%d\n",
69413496ba1Ssnj		       __FUNCTION__,
69513496ba1Ssnj		       (long long)e->x.quo,
69613496ba1Ssnj		       (long long)e->x.rem,
69713496ba1Ssnj		       (long long)e->dxdy.quo,
69813496ba1Ssnj		       (long long)e->dxdy.rem,
69913496ba1Ssnj		       (long long)Ey, e->cell));
70042542f5fSchristos	}
70142542f5fSchristos
70242542f5fSchristos	if (polygon->num_edges > 0) {
70342542f5fSchristos		struct edge *prev = &polygon->edges[polygon->num_edges-1];
70442542f5fSchristos		/* detect degenerate triangles inserted into tristrips */
70542542f5fSchristos		if (e->dir == -prev->dir &&
70642542f5fSchristos		    e->ytop == prev->ytop &&
70742542f5fSchristos		    e->height_left == prev->height_left &&
70813496ba1Ssnj		    e->cell == prev->cell &&
70942542f5fSchristos		    e->x.quo == prev->x.quo &&
71042542f5fSchristos		    e->x.rem == prev->x.rem &&
71142542f5fSchristos		    e->dxdy.quo == prev->dxdy.quo &&
71242542f5fSchristos		    e->dxdy.rem == prev->dxdy.rem) {
71342542f5fSchristos			unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop,
71442542f5fSchristos							  polygon->ymin);
71542542f5fSchristos			polygon->y_buckets[ix] = prev->next;
71613496ba1Ssnj			polygon->num_edges--;
71742542f5fSchristos			return;
71842542f5fSchristos		}
71942542f5fSchristos	}
72042542f5fSchristos
72142542f5fSchristos	_polygon_insert_edge_into_its_y_bucket(polygon, e);
72242542f5fSchristos	polygon->num_edges++;
72342542f5fSchristos}
72442542f5fSchristos
72542542f5fSchristosstatic void
72642542f5fSchristosactive_list_reset(struct active_list *active)
72742542f5fSchristos{
72842542f5fSchristos	active->head.height_left = INT_MAX;
72942542f5fSchristos	active->head.x.quo = INT_MIN;
73013496ba1Ssnj	active->head.cell = INT_MIN;
73142542f5fSchristos	active->head.dy = 0;
73242542f5fSchristos	active->head.prev = NULL;
73342542f5fSchristos	active->head.next = &active->tail;
73442542f5fSchristos	active->tail.prev = &active->head;
73542542f5fSchristos	active->tail.next = NULL;
73642542f5fSchristos	active->tail.x.quo = INT_MAX;
73713496ba1Ssnj	active->tail.cell = INT_MAX;
73842542f5fSchristos	active->tail.height_left = INT_MAX;
73942542f5fSchristos	active->tail.dy = 0;
74042542f5fSchristos}
74142542f5fSchristos
74242542f5fSchristosstatic struct edge *
74342542f5fSchristosmerge_sorted_edges(struct edge *head_a, struct edge *head_b)
74442542f5fSchristos{
74542542f5fSchristos	struct edge *head, **next, *prev;
74642542f5fSchristos	int32_t x;
74742542f5fSchristos
74842542f5fSchristos	if (head_b == NULL)
74942542f5fSchristos		return head_a;
75042542f5fSchristos
75142542f5fSchristos	prev = head_a->prev;
75242542f5fSchristos	next = &head;
75313496ba1Ssnj	if (head_a->cell <= head_b->cell) {
75442542f5fSchristos		head = head_a;
75542542f5fSchristos	} else {
75642542f5fSchristos		head = head_b;
75742542f5fSchristos		head_b->prev = prev;
75842542f5fSchristos		goto start_with_b;
75942542f5fSchristos	}
76042542f5fSchristos
76142542f5fSchristos	do {
76213496ba1Ssnj		x = head_b->cell;
76313496ba1Ssnj		while (head_a != NULL && head_a->cell <= x) {
76442542f5fSchristos			prev = head_a;
76542542f5fSchristos			next = &head_a->next;
76642542f5fSchristos			head_a = head_a->next;
76742542f5fSchristos		}
76842542f5fSchristos
76942542f5fSchristos		head_b->prev = prev;
77042542f5fSchristos		*next = head_b;
77142542f5fSchristos		if (head_a == NULL)
77242542f5fSchristos			return head;
77342542f5fSchristos
77442542f5fSchristosstart_with_b:
77513496ba1Ssnj		x = head_a->cell;
77613496ba1Ssnj		while (head_b != NULL && head_b->cell <= x) {
77742542f5fSchristos			prev = head_b;
77842542f5fSchristos			next = &head_b->next;
77942542f5fSchristos			head_b = head_b->next;
78042542f5fSchristos		}
78142542f5fSchristos
78242542f5fSchristos		head_a->prev = prev;
78342542f5fSchristos		*next = head_a;
78442542f5fSchristos		if (head_b == NULL)
78542542f5fSchristos			return head;
78642542f5fSchristos	} while (1);
78742542f5fSchristos}
78842542f5fSchristos
78942542f5fSchristosstatic struct edge *
79042542f5fSchristossort_edges(struct edge  *list,
79142542f5fSchristos	   unsigned int  level,
79242542f5fSchristos	   struct edge **head_out)
79342542f5fSchristos{
79442542f5fSchristos	struct edge *head_other, *remaining;
79542542f5fSchristos	unsigned int i;
79642542f5fSchristos
79742542f5fSchristos	head_other = list->next;
79842542f5fSchristos	if (head_other == NULL) {
79942542f5fSchristos		*head_out = list;
80042542f5fSchristos		return NULL;
80142542f5fSchristos	}
80242542f5fSchristos
80342542f5fSchristos	remaining = head_other->next;
80413496ba1Ssnj	if (list->cell <= head_other->cell) {
80542542f5fSchristos		*head_out = list;
80642542f5fSchristos		head_other->next = NULL;
80742542f5fSchristos	} else {
80842542f5fSchristos		*head_out = head_other;
80942542f5fSchristos		head_other->prev = list->prev;
81042542f5fSchristos		head_other->next = list;
81142542f5fSchristos		list->prev = head_other;
81242542f5fSchristos		list->next = NULL;
81342542f5fSchristos	}
81442542f5fSchristos
81542542f5fSchristos	for (i = 0; i < level && remaining; i++) {
81642542f5fSchristos		remaining = sort_edges(remaining, i, &head_other);
81742542f5fSchristos		*head_out = merge_sorted_edges(*head_out, head_other);
81842542f5fSchristos	}
81942542f5fSchristos
82042542f5fSchristos	return remaining;
82142542f5fSchristos}
82242542f5fSchristos
82342542f5fSchristosstatic struct edge *filter(struct edge *edges)
82442542f5fSchristos{
82542542f5fSchristos	struct edge *e;
82642542f5fSchristos
82742542f5fSchristos	e = edges;
82813496ba1Ssnj	while (e->next) {
82942542f5fSchristos		struct edge *n = e->next;
83042542f5fSchristos		if (e->dir == -n->dir &&
83142542f5fSchristos		    e->height_left == n->height_left &&
83213496ba1Ssnj		    e->cell == n->cell &&
83313496ba1Ssnj		    e->x.quo == n->x.quo &&
83413496ba1Ssnj		    e->x.rem == n->x.rem &&
83513496ba1Ssnj		    e->dxdy.quo == n->dxdy.quo &&
83613496ba1Ssnj		    e->dxdy.rem == n->dxdy.rem) {
83742542f5fSchristos			if (e->prev)
83842542f5fSchristos				e->prev->next = n->next;
83942542f5fSchristos			else
84042542f5fSchristos				edges = n->next;
84142542f5fSchristos			if (n->next)
84242542f5fSchristos				n->next->prev = e->prev;
84342542f5fSchristos			else
84442542f5fSchristos				break;
84542542f5fSchristos
84642542f5fSchristos			e = n->next;
84742542f5fSchristos		} else
84813496ba1Ssnj			e = n;
84913496ba1Ssnj	}
85042542f5fSchristos
85142542f5fSchristos	return edges;
85242542f5fSchristos}
85342542f5fSchristos
85442542f5fSchristosstatic struct edge *
85542542f5fSchristosmerge_unsorted_edges(struct edge *head, struct edge *unsorted)
85642542f5fSchristos{
85742542f5fSchristos	sort_edges(unsorted, UINT_MAX, &unsorted);
85842542f5fSchristos	return merge_sorted_edges(head, filter(unsorted));
85942542f5fSchristos}
86042542f5fSchristos
86142542f5fSchristos/* Test if the edges on the active list can be safely advanced by a
86242542f5fSchristos * full row without intersections or any edges ending. */
86342542f5fSchristosinline static int
86442542f5fSchristoscan_full_step(struct active_list *active)
86542542f5fSchristos{
86642542f5fSchristos	const struct edge *e;
86742542f5fSchristos	int min_height = INT_MAX;
86842542f5fSchristos
86942542f5fSchristos	assert(active->head.next != &active->tail);
87042542f5fSchristos	for (e = active->head.next; &active->tail != e; e = e->next) {
87142542f5fSchristos		assert(e->height_left > 0);
87242542f5fSchristos
87342542f5fSchristos		if (e->dy != 0)
87442542f5fSchristos			return 0;
87542542f5fSchristos
87642542f5fSchristos		if (e->height_left < min_height) {
87742542f5fSchristos			min_height = e->height_left;
87842542f5fSchristos			if (min_height < SAMPLES_Y)
87942542f5fSchristos				return 0;
88042542f5fSchristos		}
88142542f5fSchristos	}
88242542f5fSchristos
88342542f5fSchristos	return min_height;
88442542f5fSchristos}
88542542f5fSchristos
88642542f5fSchristosinline static void
88742542f5fSchristosmerge_edges(struct active_list *active, struct edge *edges)
88842542f5fSchristos{
88942542f5fSchristos	active->head.next = merge_unsorted_edges(active->head.next, edges);
89042542f5fSchristos}
89142542f5fSchristos
89242542f5fSchristosinline static void
89342542f5fSchristosfill_buckets(struct active_list *active,
89442542f5fSchristos	     struct edge *edge,
89542542f5fSchristos	     int ymin,
89642542f5fSchristos	     struct edge **buckets)
89742542f5fSchristos{
89842542f5fSchristos	while (edge) {
89942542f5fSchristos		struct edge *next = edge->next;
90042542f5fSchristos		struct edge **b = &buckets[edge->ytop - ymin];
90142542f5fSchristos		if (*b)
90242542f5fSchristos			(*b)->prev = edge;
90342542f5fSchristos		edge->next = *b;
90442542f5fSchristos		edge->prev = NULL;
90542542f5fSchristos		*b = edge;
90642542f5fSchristos		edge = next;
90742542f5fSchristos	}
90842542f5fSchristos}
90942542f5fSchristos
91042542f5fSchristosinline static void
91142542f5fSchristosnonzero_subrow(struct active_list *active, struct cell_list *coverages)
91242542f5fSchristos{
91342542f5fSchristos	struct edge *edge = active->head.next;
91442542f5fSchristos	int prev_x = INT_MIN;
91513496ba1Ssnj	int winding = 0, xstart = edge->cell;
91642542f5fSchristos
91742542f5fSchristos	cell_list_rewind(coverages);
91842542f5fSchristos
91942542f5fSchristos	while (&active->tail != edge) {
92042542f5fSchristos		struct edge *next = edge->next;
92142542f5fSchristos
92242542f5fSchristos		winding += edge->dir;
92313496ba1Ssnj		if (0 == winding && edge->next->cell != edge->cell) {
92413496ba1Ssnj			cell_list_add_subspan(coverages, xstart, edge->cell);
92513496ba1Ssnj			xstart = edge->next->cell;
92642542f5fSchristos		}
92742542f5fSchristos
92842542f5fSchristos		assert(edge->height_left > 0);
92942542f5fSchristos		if (--edge->height_left) {
93013496ba1Ssnj			if (edge->dy)
93113496ba1Ssnj				edge->cell = edge_advance(edge);
93242542f5fSchristos
93313496ba1Ssnj			if (edge->cell < prev_x) {
93442542f5fSchristos				struct edge *pos = edge->prev;
93542542f5fSchristos				pos->next = next;
93642542f5fSchristos				next->prev = pos;
93742542f5fSchristos				do {
93842542f5fSchristos					pos = pos->prev;
93913496ba1Ssnj				} while (edge->cell < pos->cell);
94042542f5fSchristos				pos->next->prev = edge;
94142542f5fSchristos				edge->next = pos->next;
94242542f5fSchristos				edge->prev = pos;
94342542f5fSchristos				pos->next = edge;
94442542f5fSchristos			} else
94513496ba1Ssnj				prev_x = edge->cell;
94642542f5fSchristos		} else {
94742542f5fSchristos			edge->prev->next = next;
94842542f5fSchristos			next->prev = edge->prev;
94942542f5fSchristos		}
95042542f5fSchristos
95142542f5fSchristos		edge = next;
95242542f5fSchristos	}
95342542f5fSchristos}
95442542f5fSchristos
95542542f5fSchristosstatic void
95642542f5fSchristosnonzero_row(struct active_list *active, struct cell_list *coverages)
95742542f5fSchristos{
95842542f5fSchristos	struct edge *left = active->head.next;
95942542f5fSchristos
96042542f5fSchristos	while (&active->tail != left) {
96142542f5fSchristos		struct edge *right;
96242542f5fSchristos		int winding = left->dir;
96342542f5fSchristos
96442542f5fSchristos		left->height_left -= SAMPLES_Y;
96542542f5fSchristos		assert(left->height_left >= 0);
96642542f5fSchristos		if (!left->height_left) {
96742542f5fSchristos			left->prev->next = left->next;
96842542f5fSchristos			left->next->prev = left->prev;
96942542f5fSchristos		}
97042542f5fSchristos
97142542f5fSchristos		right = left->next;
97242542f5fSchristos		do {
97342542f5fSchristos			right->height_left -= SAMPLES_Y;
97442542f5fSchristos			assert(right->height_left >= 0);
97542542f5fSchristos			if (!right->height_left) {
97642542f5fSchristos				right->prev->next = right->next;
97742542f5fSchristos				right->next->prev = right->prev;
97842542f5fSchristos			}
97942542f5fSchristos
98042542f5fSchristos			winding += right->dir;
98142542f5fSchristos			if (0 == winding)
98242542f5fSchristos				break;
98342542f5fSchristos
98442542f5fSchristos			right = right->next;
98542542f5fSchristos		} while (1);
98642542f5fSchristos
98713496ba1Ssnj		cell_list_add_span(coverages, left->cell, right->cell);
98842542f5fSchristos		left = right->next;
98942542f5fSchristos	}
99042542f5fSchristos}
99142542f5fSchristos
99242542f5fSchristosstatic void
99342542f5fSchristostor_fini(struct tor *converter)
99442542f5fSchristos{
99542542f5fSchristos	polygon_fini(converter->polygon);
99642542f5fSchristos	cell_list_fini(converter->coverages);
99742542f5fSchristos}
99842542f5fSchristos
99942542f5fSchristosstatic bool
100042542f5fSchristostor_init(struct tor *converter, const BoxRec *box, int num_edges)
100142542f5fSchristos{
100242542f5fSchristos	__DBG(("%s: (%d, %d),(%d, %d) x (%d, %d), num_edges=%d\n",
100342542f5fSchristos	       __FUNCTION__,
100442542f5fSchristos	       box->x1, box->y1, box->x2, box->y2,
100542542f5fSchristos	       SAMPLES_X, SAMPLES_Y,
100642542f5fSchristos	       num_edges));
100742542f5fSchristos
100842542f5fSchristos	converter->extents = *box;
100942542f5fSchristos
101042542f5fSchristos	if (!cell_list_init(converter->coverages, box->x1, box->x2))
101142542f5fSchristos		return false;
101242542f5fSchristos
101342542f5fSchristos	active_list_reset(converter->active);
101442542f5fSchristos	if (!polygon_init(converter->polygon, num_edges,
101542542f5fSchristos			  (int)box->y1 * SAMPLES_Y, (int)box->y2 * SAMPLES_Y)) {
101642542f5fSchristos		cell_list_fini(converter->coverages);
101742542f5fSchristos		return false;
101842542f5fSchristos	}
101942542f5fSchristos
102042542f5fSchristos	return true;
102142542f5fSchristos}
102242542f5fSchristos
102342542f5fSchristosstatic void
102413496ba1Ssnjtor_add_trapezoid(struct tor *tor, const xTrapezoid *t, int dx, int dy)
102542542f5fSchristos{
1026fe8aea9eSmrg	if (!xTrapezoidValid(t)) {
1027fe8aea9eSmrg		__DBG(("%s: skipping invalid trapezoid: top=%d, bottom=%d, left=(%d, %d), (%d, %d), right=(%d, %d), (%d, %d)\n",
1028fe8aea9eSmrg		       __FUNCTION__,
1029fe8aea9eSmrg		       t->top, t->bottom,
1030fe8aea9eSmrg		       t->left.p1.x, t->left.p1.y,
1031fe8aea9eSmrg		       t->left.p2.x, t->left.p2.y,
1032fe8aea9eSmrg		       t->right.p1.x, t->right.p1.y,
1033fe8aea9eSmrg		       t->right.p2.x, t->right.p2.y));
1034fe8aea9eSmrg		return;
1035fe8aea9eSmrg	}
103613496ba1Ssnj	polygon_add_edge(tor->polygon, t, &t->left, 1, dx, dy);
103713496ba1Ssnj	polygon_add_edge(tor->polygon, t, &t->right, -1, dx, dy);
103842542f5fSchristos}
103942542f5fSchristos
104042542f5fSchristosstatic void
104142542f5fSchristosstep_edges(struct active_list *active, int count)
104242542f5fSchristos{
104342542f5fSchristos	struct edge *edge;
104442542f5fSchristos
104542542f5fSchristos	count *= SAMPLES_Y;
104642542f5fSchristos	for (edge = active->head.next; edge != &active->tail; edge = edge->next) {
104742542f5fSchristos		edge->height_left -= count;
104842542f5fSchristos		assert(edge->height_left >= 0);
104942542f5fSchristos		if (!edge->height_left) {
105042542f5fSchristos			edge->prev->next = edge->next;
105142542f5fSchristos			edge->next->prev = edge->prev;
105242542f5fSchristos		}
105342542f5fSchristos	}
105442542f5fSchristos}
105542542f5fSchristos
105642542f5fSchristosstatic void
105742542f5fSchristostor_blt_span(struct sna *sna,
105842542f5fSchristos	     struct sna_composite_spans_op *op,
105942542f5fSchristos	     pixman_region16_t *clip,
106042542f5fSchristos	     const BoxRec *box,
106142542f5fSchristos	     int coverage)
106242542f5fSchristos{
106342542f5fSchristos	__DBG(("%s: %d -> %d @ %d\n", __FUNCTION__, box->x1, box->x2, coverage));
106442542f5fSchristos
106542542f5fSchristos	op->box(sna, op, box, AREA_TO_FLOAT(coverage));
106642542f5fSchristos	apply_damage_box(&op->base, box);
106742542f5fSchristos}
106842542f5fSchristos
106942542f5fSchristosstatic void
107042542f5fSchristostor_blt_span__no_damage(struct sna *sna,
107142542f5fSchristos			struct sna_composite_spans_op *op,
107242542f5fSchristos			pixman_region16_t *clip,
107342542f5fSchristos			const BoxRec *box,
107442542f5fSchristos			int coverage)
107542542f5fSchristos{
107642542f5fSchristos	__DBG(("%s: %d -> %d @ %d\n", __FUNCTION__, box->x1, box->x2, coverage));
107742542f5fSchristos
107842542f5fSchristos	op->box(sna, op, box, AREA_TO_FLOAT(coverage));
107942542f5fSchristos}
108042542f5fSchristos
108142542f5fSchristosstatic void
108242542f5fSchristostor_blt_span_clipped(struct sna *sna,
108342542f5fSchristos		     struct sna_composite_spans_op *op,
108442542f5fSchristos		     pixman_region16_t *clip,
108542542f5fSchristos		     const BoxRec *box,
108642542f5fSchristos		     int coverage)
108742542f5fSchristos{
108842542f5fSchristos	pixman_region16_t region;
108942542f5fSchristos	float opacity;
109042542f5fSchristos
109142542f5fSchristos	opacity = AREA_TO_FLOAT(coverage);
109242542f5fSchristos	__DBG(("%s: %d -> %d @ %f\n", __FUNCTION__, box->x1, box->x2, opacity));
109342542f5fSchristos
109442542f5fSchristos	pixman_region_init_rects(&region, box, 1);
109542542f5fSchristos	RegionIntersect(&region, &region, clip);
109642542f5fSchristos	if (region_num_rects(&region)) {
109742542f5fSchristos		op->boxes(sna, op,
109842542f5fSchristos			  region_rects(&region),
109942542f5fSchristos			  region_num_rects(&region),
110042542f5fSchristos			  opacity);
110142542f5fSchristos		apply_damage(&op->base, &region);
110242542f5fSchristos	}
110342542f5fSchristos	pixman_region_fini(&region);
110442542f5fSchristos}
110542542f5fSchristos
110642542f5fSchristosstatic void
110742542f5fSchristostor_blt(struct sna *sna,
110842542f5fSchristos	struct tor *converter,
110942542f5fSchristos	struct sna_composite_spans_op *op,
111042542f5fSchristos	pixman_region16_t *clip,
111142542f5fSchristos	void (*span)(struct sna *sna,
111242542f5fSchristos		     struct sna_composite_spans_op *op,
111342542f5fSchristos		     pixman_region16_t *clip,
111442542f5fSchristos		     const BoxRec *box,
111542542f5fSchristos		     int coverage),
111642542f5fSchristos	int y, int height,
111742542f5fSchristos	int unbounded)
111842542f5fSchristos{
111942542f5fSchristos	struct cell_list *cells = converter->coverages;
112042542f5fSchristos	struct cell *cell;
112142542f5fSchristos	BoxRec box;
112242542f5fSchristos	int cover;
112342542f5fSchristos
112442542f5fSchristos	box.y1 = y + converter->extents.y1;
112542542f5fSchristos	box.y2 = box.y1 + height;
112642542f5fSchristos	assert(box.y2 <= converter->extents.y2);
112742542f5fSchristos	box.x1 = converter->extents.x1;
112842542f5fSchristos
112942542f5fSchristos	/* Form the spans from the coverages and areas. */
113042542f5fSchristos	cover = cells->head.covered_height*SAMPLES_X*2;
113142542f5fSchristos	assert(cover >= 0);
113242542f5fSchristos	for (cell = cells->head.next; cell != &cells->tail; cell = cell->next) {
113342542f5fSchristos		int x = cell->x;
113442542f5fSchristos
113542542f5fSchristos		assert(x >= converter->extents.x1);
113642542f5fSchristos		assert(x < converter->extents.x2);
113713496ba1Ssnj		__DBG(("%s: cell=(%d, %d, %d), cover=%d\n", __FUNCTION__,
113842542f5fSchristos		       cell->x, cell->covered_height, cell->uncovered_area,
113913496ba1Ssnj		       cover));
114042542f5fSchristos
114142542f5fSchristos		if (cell->covered_height || cell->uncovered_area) {
114242542f5fSchristos			box.x2 = x;
114342542f5fSchristos			if (box.x2 > box.x1 && (unbounded || cover)) {
114413496ba1Ssnj				__DBG(("%s: end span (%d, %d)x(%d, %d) @ %d\n", __FUNCTION__,
114542542f5fSchristos				       box.x1, box.y1,
114642542f5fSchristos				       box.x2 - box.x1,
114742542f5fSchristos				       box.y2 - box.y1,
114842542f5fSchristos				       cover));
114942542f5fSchristos				span(sna, op, clip, &box, cover);
115042542f5fSchristos			}
115142542f5fSchristos			box.x1 = box.x2;
115242542f5fSchristos			cover += cell->covered_height*SAMPLES_X*2;
115342542f5fSchristos		}
115442542f5fSchristos
115542542f5fSchristos		if (cell->uncovered_area) {
115642542f5fSchristos			int area = cover - cell->uncovered_area;
115742542f5fSchristos			box.x2 = x + 1;
115842542f5fSchristos			if (unbounded || area) {
115913496ba1Ssnj				__DBG(("%s: new span (%d, %d)x(%d, %d) @ %d\n", __FUNCTION__,
116042542f5fSchristos				       box.x1, box.y1,
116142542f5fSchristos				       box.x2 - box.x1,
116242542f5fSchristos				       box.y2 - box.y1,
116342542f5fSchristos				       area));
116442542f5fSchristos				span(sna, op, clip, &box, area);
116542542f5fSchristos			}
116642542f5fSchristos			box.x1 = box.x2;
116742542f5fSchristos		}
116842542f5fSchristos	}
116942542f5fSchristos
117042542f5fSchristos	box.x2 = converter->extents.x2;
117142542f5fSchristos	if (box.x2 > box.x1 && (unbounded || cover)) {
117242542f5fSchristos		__DBG(("%s: span (%d, %d)x(%d, %d) @ %d\n", __FUNCTION__,
117342542f5fSchristos		       box.x1, box.y1,
117442542f5fSchristos		       box.x2 - box.x1,
117542542f5fSchristos		       box.y2 - box.y1,
117642542f5fSchristos		       cover));
117742542f5fSchristos		span(sna, op, clip, &box, cover);
117842542f5fSchristos	}
117942542f5fSchristos}
118042542f5fSchristos
118142542f5fSchristosflatten static void
118242542f5fSchristostor_render(struct sna *sna,
118342542f5fSchristos	   struct tor *converter,
118442542f5fSchristos	   struct sna_composite_spans_op *op,
118542542f5fSchristos	   pixman_region16_t *clip,
118642542f5fSchristos	   void (*span)(struct sna *sna,
118742542f5fSchristos			struct sna_composite_spans_op *op,
118842542f5fSchristos			pixman_region16_t *clip,
118942542f5fSchristos			const BoxRec *box,
119042542f5fSchristos			int coverage),
119142542f5fSchristos	   int unbounded)
119242542f5fSchristos{
119342542f5fSchristos	struct polygon *polygon = converter->polygon;
119442542f5fSchristos	struct cell_list *coverages = converter->coverages;
119542542f5fSchristos	struct active_list *active = converter->active;
119642542f5fSchristos	struct edge *buckets[SAMPLES_Y] = { 0 };
119742542f5fSchristos	int16_t i, j, h = converter->extents.y2 - converter->extents.y1;
119842542f5fSchristos
119942542f5fSchristos	__DBG(("%s: unbounded=%d\n", __FUNCTION__, unbounded));
120042542f5fSchristos
120142542f5fSchristos	/* Render each pixel row. */
120242542f5fSchristos	for (i = 0; i < h; i = j) {
120342542f5fSchristos		int do_full_step = 0;
120442542f5fSchristos
120542542f5fSchristos		j = i + 1;
120642542f5fSchristos
120742542f5fSchristos		/* Determine if we can ignore this row or use the full pixel
120842542f5fSchristos		 * stepper. */
120942542f5fSchristos		if (polygon->y_buckets[i] == NULL) {
121042542f5fSchristos			if (active->head.next == &active->tail) {
121142542f5fSchristos				for (; polygon->y_buckets[j] == NULL; j++)
121242542f5fSchristos					;
121342542f5fSchristos				__DBG(("%s: no new edges and no exisiting edges, skipping, %d -> %d\n",
121442542f5fSchristos				       __FUNCTION__, i, j));
121542542f5fSchristos
121642542f5fSchristos				assert(j <= h);
121742542f5fSchristos				if (unbounded) {
121842542f5fSchristos					BoxRec box;
121942542f5fSchristos
122042542f5fSchristos					box = converter->extents;
122142542f5fSchristos					box.y1 += i;
122242542f5fSchristos					box.y2 = converter->extents.y1 + j;
122342542f5fSchristos
122442542f5fSchristos					span(sna, op, clip, &box, 0);
122542542f5fSchristos				}
122642542f5fSchristos				continue;
122742542f5fSchristos			}
122842542f5fSchristos
122942542f5fSchristos			do_full_step = can_full_step(active);
123042542f5fSchristos		}
123142542f5fSchristos
123213496ba1Ssnj		__DBG(("%s: y=%d, do_full_step=%d, new edges=%d\n",
123313496ba1Ssnj		       __FUNCTION__, i, do_full_step,
123442542f5fSchristos		       polygon->y_buckets[i] != NULL));
123542542f5fSchristos		if (do_full_step) {
123642542f5fSchristos			nonzero_row(active, coverages);
123742542f5fSchristos
123842542f5fSchristos			while (polygon->y_buckets[j] == NULL &&
123942542f5fSchristos			       do_full_step >= 2*SAMPLES_Y) {
124042542f5fSchristos				do_full_step -= SAMPLES_Y;
124142542f5fSchristos				j++;
124242542f5fSchristos			}
124342542f5fSchristos			assert(j >= i + 1 && j <= h);
124442542f5fSchristos			if (j != i + 1)
124542542f5fSchristos				step_edges(active, j - (i + 1));
124642542f5fSchristos
124742542f5fSchristos			__DBG(("%s: vertical edges, full step (%d, %d)\n",
124842542f5fSchristos			       __FUNCTION__,  i, j));
124942542f5fSchristos		} else {
125042542f5fSchristos			int suby;
125142542f5fSchristos
125242542f5fSchristos			fill_buckets(active, polygon->y_buckets[i], (i+converter->extents.y1)*SAMPLES_Y, buckets);
125342542f5fSchristos
125442542f5fSchristos			/* Subsample this row. */
125542542f5fSchristos			for (suby = 0; suby < SAMPLES_Y; suby++) {
125642542f5fSchristos				if (buckets[suby]) {
125742542f5fSchristos					merge_edges(active, buckets[suby]);
125842542f5fSchristos					buckets[suby] = NULL;
125942542f5fSchristos				}
126042542f5fSchristos
126142542f5fSchristos				nonzero_subrow(active, coverages);
126242542f5fSchristos			}
126342542f5fSchristos		}
126442542f5fSchristos
126542542f5fSchristos		assert(j > i);
126642542f5fSchristos		tor_blt(sna, converter, op, clip, span, i, j-i, unbounded);
126742542f5fSchristos		cell_list_reset(coverages);
126842542f5fSchristos	}
126942542f5fSchristos}
127042542f5fSchristos
127142542f5fSchristosstatic void
127242542f5fSchristosinplace_row(struct active_list *active, uint8_t *row, int width)
127342542f5fSchristos{
127442542f5fSchristos	struct edge *left = active->head.next;
127542542f5fSchristos
127642542f5fSchristos	while (&active->tail != left) {
127742542f5fSchristos		struct edge *right;
127842542f5fSchristos		int winding = left->dir;
127942542f5fSchristos		int lfx, rfx;
128042542f5fSchristos		int lix, rix;
128142542f5fSchristos
128242542f5fSchristos		left->height_left -= SAMPLES_Y;
128342542f5fSchristos		assert(left->height_left >= 0);
128442542f5fSchristos		if (!left->height_left) {
128542542f5fSchristos			left->prev->next = left->next;
128642542f5fSchristos			left->next->prev = left->prev;
128742542f5fSchristos		}
128842542f5fSchristos
128942542f5fSchristos		right = left->next;
129042542f5fSchristos		do {
129142542f5fSchristos			right->height_left -= SAMPLES_Y;
129242542f5fSchristos			assert(right->height_left >= 0);
129342542f5fSchristos			if (!right->height_left) {
129442542f5fSchristos				right->prev->next = right->next;
129542542f5fSchristos				right->next->prev = right->prev;
129642542f5fSchristos			}
129742542f5fSchristos
129842542f5fSchristos			winding += right->dir;
129913496ba1Ssnj			if (0 == winding && right->cell != right->next->cell)
130042542f5fSchristos				break;
130142542f5fSchristos
130242542f5fSchristos			right = right->next;
130342542f5fSchristos		} while (1);
130442542f5fSchristos
130513496ba1Ssnj		if (left->cell < 0) {
130642542f5fSchristos			lix = lfx = 0;
130713496ba1Ssnj		} else if (left->cell >= width * SAMPLES_X) {
130842542f5fSchristos			lix = width;
130942542f5fSchristos			lfx = 0;
131042542f5fSchristos		} else
131113496ba1Ssnj			SAMPLES_X_TO_INT_FRAC(left->cell, lix, lfx);
131242542f5fSchristos
131313496ba1Ssnj		if (right->cell < 0) {
131442542f5fSchristos			rix = rfx = 0;
131513496ba1Ssnj		} else if (right->cell >= width * SAMPLES_X) {
131642542f5fSchristos			rix = width;
131742542f5fSchristos			rfx = 0;
131842542f5fSchristos		} else
131913496ba1Ssnj			SAMPLES_X_TO_INT_FRAC(right->cell, rix, rfx);
132042542f5fSchristos		if (lix == rix) {
132142542f5fSchristos			if (rfx != lfx) {
132242542f5fSchristos				assert(lix < width);
132342542f5fSchristos				row[lix] += (rfx-lfx) * SAMPLES_Y;
132442542f5fSchristos			}
132542542f5fSchristos		} else {
132642542f5fSchristos			assert(lix < width);
132742542f5fSchristos			if (lfx == 0)
132842542f5fSchristos				row[lix] = 0xff;
132942542f5fSchristos			else
133042542f5fSchristos				row[lix] += 255 - lfx * SAMPLES_Y;
133142542f5fSchristos
133242542f5fSchristos			assert(rix <= width);
133342542f5fSchristos			if (rfx) {
133442542f5fSchristos				assert(rix < width);
133542542f5fSchristos				row[rix] += rfx * SAMPLES_Y;
133642542f5fSchristos			}
133742542f5fSchristos
133842542f5fSchristos			if (rix > ++lix) {
133942542f5fSchristos				uint8_t *r = row + lix;
134042542f5fSchristos				rix -= lix;
134142542f5fSchristos#if 0
134242542f5fSchristos				if (rix == 1)
134342542f5fSchristos					*row = 0xff;
134442542f5fSchristos				else
134542542f5fSchristos					memset(row, 0xff, rix);
134642542f5fSchristos#else
134742542f5fSchristos				if ((uintptr_t)r & 1 && rix) {
134842542f5fSchristos					*r++ = 0xff;
134942542f5fSchristos					rix--;
135042542f5fSchristos				}
135142542f5fSchristos				if ((uintptr_t)r & 2 && rix >= 2) {
135242542f5fSchristos					*(uint16_t *)r = 0xffff;
135342542f5fSchristos					r += 2;
135442542f5fSchristos					rix -= 2;
135542542f5fSchristos				}
135642542f5fSchristos				if ((uintptr_t)r & 4 && rix >= 4) {
135742542f5fSchristos					*(uint32_t *)r = 0xffffffff;
135842542f5fSchristos					r += 4;
135942542f5fSchristos					rix -= 4;
136042542f5fSchristos				}
136142542f5fSchristos				while (rix >= 8) {
136242542f5fSchristos					*(uint64_t *)r = 0xffffffffffffffff;
136342542f5fSchristos					r += 8;
136442542f5fSchristos					rix -= 8;
136542542f5fSchristos				}
136642542f5fSchristos				if (rix & 4) {
136742542f5fSchristos					*(uint32_t *)r = 0xffffffff;
136842542f5fSchristos					r += 4;
136942542f5fSchristos				}
137042542f5fSchristos				if (rix & 2) {
137142542f5fSchristos					*(uint16_t *)r = 0xffff;
137242542f5fSchristos					r += 2;
137342542f5fSchristos				}
137442542f5fSchristos				if (rix & 1)
137542542f5fSchristos					*r = 0xff;
137642542f5fSchristos#endif
137742542f5fSchristos			}
137842542f5fSchristos		}
137942542f5fSchristos
138042542f5fSchristos		left = right->next;
138142542f5fSchristos	}
138242542f5fSchristos}
138342542f5fSchristos
138442542f5fSchristosinline static void
138542542f5fSchristosinplace_subrow(struct active_list *active, int8_t *row, int width)
138642542f5fSchristos{
138742542f5fSchristos	struct edge *edge = active->head.next;
138842542f5fSchristos	int prev_x = INT_MIN;
138942542f5fSchristos
139042542f5fSchristos	while (&active->tail != edge) {
139142542f5fSchristos		struct edge *next = edge->next;
139242542f5fSchristos		int winding = edge->dir;
139342542f5fSchristos		int lfx, rfx;
139442542f5fSchristos		int lix, rix;
139542542f5fSchristos
139613496ba1Ssnj		if (edge->cell < 0) {
139742542f5fSchristos			lix = lfx = 0;
139813496ba1Ssnj		} else if (edge->cell >= width * SAMPLES_X) {
139942542f5fSchristos			lix = width;
140042542f5fSchristos			lfx = 0;
140142542f5fSchristos		} else
140213496ba1Ssnj			SAMPLES_X_TO_INT_FRAC(edge->cell, lix, lfx);
140342542f5fSchristos
140442542f5fSchristos		assert(edge->height_left > 0);
140542542f5fSchristos		if (--edge->height_left) {
140613496ba1Ssnj			if (edge->dy)
140713496ba1Ssnj				edge->cell = edge_advance(edge);
140842542f5fSchristos
140913496ba1Ssnj			if (edge->cell < prev_x) {
141042542f5fSchristos				struct edge *pos = edge->prev;
141142542f5fSchristos				pos->next = next;
141242542f5fSchristos				next->prev = pos;
141342542f5fSchristos				do {
141442542f5fSchristos					pos = pos->prev;
141513496ba1Ssnj				} while (edge->cell < pos->cell);
141642542f5fSchristos				pos->next->prev = edge;
141742542f5fSchristos				edge->next = pos->next;
141842542f5fSchristos				edge->prev = pos;
141942542f5fSchristos				pos->next = edge;
142042542f5fSchristos			} else
142113496ba1Ssnj				prev_x = edge->cell;
142242542f5fSchristos		} else {
142342542f5fSchristos			edge->prev->next = next;
142442542f5fSchristos			next->prev = edge->prev;
142542542f5fSchristos		}
142642542f5fSchristos
142742542f5fSchristos		edge = next;
142842542f5fSchristos		do {
142942542f5fSchristos			next = edge->next;
143042542f5fSchristos			winding += edge->dir;
143113496ba1Ssnj			if (0 == winding && edge->cell != next->cell)
143242542f5fSchristos				break;
143342542f5fSchristos
143442542f5fSchristos			assert(edge->height_left > 0);
143542542f5fSchristos			if (--edge->height_left) {
143613496ba1Ssnj				if (edge->dy)
143713496ba1Ssnj					edge->cell = edge_advance(edge);
143842542f5fSchristos
143913496ba1Ssnj				if (edge->cell < prev_x) {
144042542f5fSchristos					struct edge *pos = edge->prev;
144142542f5fSchristos					pos->next = next;
144242542f5fSchristos					next->prev = pos;
144342542f5fSchristos					do {
144442542f5fSchristos						pos = pos->prev;
144513496ba1Ssnj					} while (edge->cell < pos->cell);
144642542f5fSchristos					pos->next->prev = edge;
144742542f5fSchristos					edge->next = pos->next;
144842542f5fSchristos					edge->prev = pos;
144942542f5fSchristos					pos->next = edge;
145042542f5fSchristos				} else
145113496ba1Ssnj					prev_x = edge->cell;
145242542f5fSchristos			} else {
145342542f5fSchristos				edge->prev->next = next;
145442542f5fSchristos				next->prev = edge->prev;
145542542f5fSchristos			}
145642542f5fSchristos
145742542f5fSchristos			edge = next;
145842542f5fSchristos		} while (1);
145942542f5fSchristos
146013496ba1Ssnj		if (edge->cell < 0) {
146142542f5fSchristos			rix = rfx = 0;
146213496ba1Ssnj		} else if (edge->cell >= width * SAMPLES_X) {
146342542f5fSchristos			rix = width;
146442542f5fSchristos			rfx = 0;
146542542f5fSchristos		} else
146613496ba1Ssnj			SAMPLES_X_TO_INT_FRAC(edge->cell, rix, rfx);
146742542f5fSchristos
146842542f5fSchristos		assert(edge->height_left > 0);
146942542f5fSchristos		if (--edge->height_left) {
147013496ba1Ssnj			if (edge->dy)
147113496ba1Ssnj				edge->cell = edge_advance(edge);
147242542f5fSchristos
147313496ba1Ssnj			if (edge->cell < prev_x) {
147442542f5fSchristos				struct edge *pos = edge->prev;
147542542f5fSchristos				pos->next = next;
147642542f5fSchristos				next->prev = pos;
147742542f5fSchristos				do {
147842542f5fSchristos					pos = pos->prev;
147913496ba1Ssnj				} while (edge->cell < pos->cell);
148042542f5fSchristos				pos->next->prev = edge;
148142542f5fSchristos				edge->next = pos->next;
148242542f5fSchristos				edge->prev = pos;
148342542f5fSchristos				pos->next = edge;
148442542f5fSchristos			} else
148513496ba1Ssnj				prev_x = edge->cell;
148642542f5fSchristos		} else {
148742542f5fSchristos			edge->prev->next = next;
148842542f5fSchristos			next->prev = edge->prev;
148942542f5fSchristos		}
149042542f5fSchristos
149142542f5fSchristos		edge = next;
149242542f5fSchristos
149313496ba1Ssnj		__DBG(("%s: left=%d.%d, right=%d.%d\n", __FUNCTION__,
149413496ba1Ssnj		       lix, lfx, rix, rfx));
149542542f5fSchristos		if (lix == rix) {
149642542f5fSchristos			if (rfx != lfx) {
149742542f5fSchristos				assert(lix < width);
149842542f5fSchristos				row[lix] += (rfx-lfx);
149942542f5fSchristos			}
150042542f5fSchristos		} else {
150142542f5fSchristos			assert(lix < width);
150242542f5fSchristos			row[lix] += SAMPLES_X - lfx;
150342542f5fSchristos
150442542f5fSchristos			assert(rix <= width);
150542542f5fSchristos			if (rfx) {
150642542f5fSchristos				assert(rix < width);
150742542f5fSchristos				row[rix] += rfx;
150842542f5fSchristos			}
150942542f5fSchristos
151042542f5fSchristos			while (++lix < rix)
151142542f5fSchristos				row[lix] += SAMPLES_X;
151242542f5fSchristos		}
151342542f5fSchristos	}
151442542f5fSchristos}
151542542f5fSchristos
151642542f5fSchristosflatten static void
151742542f5fSchristostor_inplace(struct tor *converter, PixmapPtr scratch)
151842542f5fSchristos{
151942542f5fSchristos	uint8_t buf[TOR_INPLACE_SIZE];
152042542f5fSchristos	int i, j, h = converter->extents.y2 - converter->extents.y1;
152142542f5fSchristos	struct polygon *polygon = converter->polygon;
152242542f5fSchristos	struct active_list *active = converter->active;
152342542f5fSchristos	struct edge *buckets[SAMPLES_Y] = { 0 };
152442542f5fSchristos	uint8_t *row = scratch->devPrivate.ptr;
152542542f5fSchristos	int stride = scratch->devKind;
152642542f5fSchristos	int width = scratch->drawable.width;
152742542f5fSchristos
152842542f5fSchristos	__DBG(("%s: buf?=%d\n", __FUNCTION__, buf != NULL));
152942542f5fSchristos	assert(converter->extents.x1 == 0);
153042542f5fSchristos	assert(scratch->drawable.depth == 8);
153142542f5fSchristos
153242542f5fSchristos	row += converter->extents.y1 * stride;
153342542f5fSchristos
153442542f5fSchristos	/* Render each pixel row. */
153542542f5fSchristos	for (i = 0; i < h; i = j) {
153642542f5fSchristos		int do_full_step = 0;
153742542f5fSchristos		void *ptr = scratch->usage_hint ? buf : row;
153842542f5fSchristos
153942542f5fSchristos		j = i + 1;
154042542f5fSchristos
154142542f5fSchristos		/* Determine if we can ignore this row or use the full pixel
154242542f5fSchristos		 * stepper. */
154342542f5fSchristos		if (!polygon->y_buckets[i]) {
154442542f5fSchristos			if (active->head.next == &active->tail) {
154542542f5fSchristos				for (; !polygon->y_buckets[j]; j++)
154642542f5fSchristos					;
154742542f5fSchristos				__DBG(("%s: no new edges and no exisiting edges, skipping, %d -> %d\n",
154842542f5fSchristos				       __FUNCTION__, i, j));
154942542f5fSchristos
155042542f5fSchristos				memset(row, 0, stride*(j-i));
155142542f5fSchristos				row += stride*(j-i);
155242542f5fSchristos				continue;
155342542f5fSchristos			}
155442542f5fSchristos
155542542f5fSchristos			do_full_step = can_full_step(active);
155642542f5fSchristos		}
155742542f5fSchristos
155842542f5fSchristos		__DBG(("%s: y=%d, do_full_step=%d, new edges=%d\n",
155942542f5fSchristos		       __FUNCTION__, i, do_full_step,
156042542f5fSchristos		       polygon->y_buckets[i] != NULL));
156142542f5fSchristos		if (do_full_step) {
156242542f5fSchristos			memset(ptr, 0, width);
156342542f5fSchristos			inplace_row(active, ptr, width);
156442542f5fSchristos			if (row != ptr)
156542542f5fSchristos				memcpy(row, ptr, width);
156642542f5fSchristos
156742542f5fSchristos			while (polygon->y_buckets[j] == NULL &&
156842542f5fSchristos			       do_full_step >= 2*SAMPLES_Y) {
156942542f5fSchristos				do_full_step -= SAMPLES_Y;
157042542f5fSchristos				row += stride;
157142542f5fSchristos				memcpy(row, ptr, width);
157242542f5fSchristos				j++;
157342542f5fSchristos			}
157442542f5fSchristos			if (j != i + 1)
157542542f5fSchristos				step_edges(active, j - (i + 1));
157642542f5fSchristos
157742542f5fSchristos			__DBG(("%s: vertical edges, full step (%d, %d)\n",
157842542f5fSchristos			       __FUNCTION__,  i, j));
157942542f5fSchristos		} else {
158042542f5fSchristos			int suby;
158142542f5fSchristos
158242542f5fSchristos			fill_buckets(active, polygon->y_buckets[i], (i+converter->extents.y1)*SAMPLES_Y, buckets);
158342542f5fSchristos
158442542f5fSchristos			/* Subsample this row. */
158542542f5fSchristos			memset(ptr, 0, width);
158642542f5fSchristos			for (suby = 0; suby < SAMPLES_Y; suby++) {
158742542f5fSchristos				if (buckets[suby]) {
158842542f5fSchristos					merge_edges(active, buckets[suby]);
158942542f5fSchristos					buckets[suby] = NULL;
159042542f5fSchristos				}
159142542f5fSchristos
159242542f5fSchristos				inplace_subrow(active, ptr, width);
159342542f5fSchristos			}
159442542f5fSchristos			if (row != ptr)
159542542f5fSchristos				memcpy(row, ptr, width);
159642542f5fSchristos		}
159742542f5fSchristos
159842542f5fSchristos		row += stride;
159942542f5fSchristos	}
160042542f5fSchristos}
160142542f5fSchristos
160242542f5fSchristosstatic int operator_is_bounded(uint8_t op)
160342542f5fSchristos{
160442542f5fSchristos	switch (op) {
160542542f5fSchristos	case PictOpOver:
160642542f5fSchristos	case PictOpOutReverse:
160742542f5fSchristos	case PictOpAdd:
160842542f5fSchristos		return true;
160942542f5fSchristos	default:
161042542f5fSchristos		return false;
161142542f5fSchristos	}
161242542f5fSchristos}
161342542f5fSchristos
161442542f5fSchristosstatic span_func_t
161542542f5fSchristoschoose_span(struct sna_composite_spans_op *tmp,
161642542f5fSchristos	    PicturePtr dst,
161742542f5fSchristos	    PictFormatPtr maskFormat,
161842542f5fSchristos	    RegionPtr clip)
161942542f5fSchristos{
162042542f5fSchristos	span_func_t span;
162142542f5fSchristos
162242542f5fSchristos	assert(!is_mono(dst, maskFormat));
162342542f5fSchristos	if (clip->data)
162442542f5fSchristos		span = tor_blt_span_clipped;
162542542f5fSchristos	else if (tmp->base.damage == NULL)
162642542f5fSchristos		span = tor_blt_span__no_damage;
162742542f5fSchristos	else
162842542f5fSchristos		span = tor_blt_span;
162942542f5fSchristos
163042542f5fSchristos	return span;
163142542f5fSchristos}
163242542f5fSchristos
163342542f5fSchristosstruct span_thread {
163442542f5fSchristos	struct sna *sna;
163542542f5fSchristos	const struct sna_composite_spans_op *op;
163642542f5fSchristos	const xTrapezoid *traps;
163742542f5fSchristos	RegionPtr clip;
163842542f5fSchristos	span_func_t span;
163942542f5fSchristos	BoxRec extents;
164042542f5fSchristos	int dx, dy, draw_y;
164142542f5fSchristos	int ntrap;
164242542f5fSchristos	bool unbounded;
164342542f5fSchristos};
164442542f5fSchristos
164542542f5fSchristos#define SPAN_THREAD_MAX_BOXES (8192/sizeof(struct sna_opacity_box))
164642542f5fSchristosstruct span_thread_boxes {
164742542f5fSchristos	const struct sna_composite_spans_op *op;
1648fe8aea9eSmrg	const BoxRec *clip_start, *clip_end;
164942542f5fSchristos	int num_boxes;
165042542f5fSchristos	struct sna_opacity_box boxes[SPAN_THREAD_MAX_BOXES];
165142542f5fSchristos};
165242542f5fSchristos
1653fe8aea9eSmrgstatic void span_thread_add_box(struct sna *sna, void *data,
1654fe8aea9eSmrg				const BoxRec *box, float alpha)
165542542f5fSchristos{
165642542f5fSchristos	struct span_thread_boxes *b = data;
165742542f5fSchristos
1658fe8aea9eSmrg	__DBG(("%s: adding box with alpha=%f\n", __FUNCTION__, alpha));
165942542f5fSchristos
1660fe8aea9eSmrg	if (unlikely(b->num_boxes == SPAN_THREAD_MAX_BOXES)) {
1661fe8aea9eSmrg		DBG(("%s: flushing %d boxes\n", __FUNCTION__, b->num_boxes));
166242542f5fSchristos		b->op->thread_boxes(sna, b->op, b->boxes, b->num_boxes);
166342542f5fSchristos		b->num_boxes = 0;
166442542f5fSchristos	}
166542542f5fSchristos
1666fe8aea9eSmrg	b->boxes[b->num_boxes].box = *box++;
1667fe8aea9eSmrg	b->boxes[b->num_boxes].alpha = alpha;
1668fe8aea9eSmrg	b->num_boxes++;
166942542f5fSchristos	assert(b->num_boxes <= SPAN_THREAD_MAX_BOXES);
167042542f5fSchristos}
167142542f5fSchristos
167242542f5fSchristosstatic void
167342542f5fSchristosspan_thread_box(struct sna *sna,
167442542f5fSchristos		struct sna_composite_spans_op *op,
167542542f5fSchristos		pixman_region16_t *clip,
167642542f5fSchristos		const BoxRec *box,
167742542f5fSchristos		int coverage)
167842542f5fSchristos{
1679fe8aea9eSmrg	struct span_thread_boxes *b = (struct span_thread_boxes *)op;
1680fe8aea9eSmrg
168142542f5fSchristos	__DBG(("%s: %d -> %d @ %d\n", __FUNCTION__, box->x1, box->x2, coverage));
1682fe8aea9eSmrg	if (b->num_boxes) {
1683fe8aea9eSmrg		struct sna_opacity_box *bb = &b->boxes[b->num_boxes-1];
1684fe8aea9eSmrg		if (bb->box.x1 == box->x1 &&
1685fe8aea9eSmrg		    bb->box.x2 == box->x2 &&
1686fe8aea9eSmrg		    bb->box.y2 == box->y1 &&
1687fe8aea9eSmrg		    bb->alpha == AREA_TO_FLOAT(coverage)) {
1688fe8aea9eSmrg			bb->box.y2 = box->y2;
1689fe8aea9eSmrg			__DBG(("%s: contracted double row: %d -> %d\n", __func__, bb->box.y1, bb->box.y2));
1690fe8aea9eSmrg			return;
1691fe8aea9eSmrg		}
1692fe8aea9eSmrg	}
1693fe8aea9eSmrg
1694fe8aea9eSmrg	span_thread_add_box(sna, op, box, AREA_TO_FLOAT(coverage));
169542542f5fSchristos}
169642542f5fSchristos
169742542f5fSchristosstatic void
169842542f5fSchristosspan_thread_clipped_box(struct sna *sna,
169942542f5fSchristos			struct sna_composite_spans_op *op,
170042542f5fSchristos			pixman_region16_t *clip,
170142542f5fSchristos			const BoxRec *box,
170242542f5fSchristos			int coverage)
170342542f5fSchristos{
1704fe8aea9eSmrg	struct span_thread_boxes *b = (struct span_thread_boxes *)op;
1705fe8aea9eSmrg	const BoxRec *c;
170642542f5fSchristos
170742542f5fSchristos	__DBG(("%s: %d -> %d @ %f\n", __FUNCTION__, box->x1, box->x2,
170842542f5fSchristos	       AREA_TO_FLOAT(coverage)));
170942542f5fSchristos
1710fe8aea9eSmrg	b->clip_start =
1711fe8aea9eSmrg		find_clip_box_for_y(b->clip_start, b->clip_end, box->y1);
1712fe8aea9eSmrg
1713fe8aea9eSmrg	c = b->clip_start;
1714fe8aea9eSmrg	while (c != b->clip_end) {
1715fe8aea9eSmrg		BoxRec clipped;
1716fe8aea9eSmrg
1717fe8aea9eSmrg		if (box->y2 <= c->y1)
1718fe8aea9eSmrg			break;
1719fe8aea9eSmrg
1720fe8aea9eSmrg		clipped = *box;
1721fe8aea9eSmrg		if (!box_intersect(&clipped, c++))
1722fe8aea9eSmrg			continue;
1723fe8aea9eSmrg
1724fe8aea9eSmrg		span_thread_add_box(sna, op, &clipped, AREA_TO_FLOAT(coverage));
172542542f5fSchristos	}
172642542f5fSchristos}
172742542f5fSchristos
172842542f5fSchristosstatic span_func_t
172942542f5fSchristosthread_choose_span(struct sna_composite_spans_op *tmp,
173042542f5fSchristos		   PicturePtr dst,
173142542f5fSchristos		   PictFormatPtr maskFormat,
173242542f5fSchristos		   RegionPtr clip)
173342542f5fSchristos{
173442542f5fSchristos	span_func_t span;
173542542f5fSchristos
173642542f5fSchristos	if (tmp->base.damage) {
173742542f5fSchristos		DBG(("%s: damaged -> no thread support\n", __FUNCTION__));
173842542f5fSchristos		return NULL;
173942542f5fSchristos	}
174042542f5fSchristos
174142542f5fSchristos	assert(!is_mono(dst, maskFormat));
174242542f5fSchristos	assert(tmp->thread_boxes);
1743fe8aea9eSmrg	DBG(("%s: clipped? %d x %d\n", __FUNCTION__, clip->data != NULL, region_num_rects(clip)));
174442542f5fSchristos	if (clip->data)
174542542f5fSchristos		span = span_thread_clipped_box;
174642542f5fSchristos	else
174742542f5fSchristos		span = span_thread_box;
174842542f5fSchristos
174942542f5fSchristos	return span;
175042542f5fSchristos}
175142542f5fSchristos
1752fe8aea9eSmrginline static void
1753fe8aea9eSmrgspan_thread_boxes_init(struct span_thread_boxes *boxes,
1754fe8aea9eSmrg		       const struct sna_composite_spans_op *op,
1755fe8aea9eSmrg		       const RegionRec *clip)
1756fe8aea9eSmrg{
1757fe8aea9eSmrg	boxes->op = op;
1758fe8aea9eSmrg	boxes->clip_start = region_rects(clip);
1759fe8aea9eSmrg	boxes->clip_end = boxes->clip_start + region_num_rects(clip);
1760fe8aea9eSmrg	boxes->num_boxes = 0;
1761fe8aea9eSmrg}
1762fe8aea9eSmrg
176342542f5fSchristosstatic void
176442542f5fSchristosspan_thread(void *arg)
176542542f5fSchristos{
176642542f5fSchristos	struct span_thread *thread = arg;
176742542f5fSchristos	struct span_thread_boxes boxes;
176842542f5fSchristos	struct tor tor;
176942542f5fSchristos	const xTrapezoid *t;
177042542f5fSchristos	int n, y1, y2;
177142542f5fSchristos
177242542f5fSchristos	if (!tor_init(&tor, &thread->extents, 2*thread->ntrap))
177342542f5fSchristos		return;
177442542f5fSchristos
1775fe8aea9eSmrg	span_thread_boxes_init(&boxes, thread->op, thread->clip);
177642542f5fSchristos
177742542f5fSchristos	y1 = thread->extents.y1 - thread->draw_y;
177842542f5fSchristos	y2 = thread->extents.y2 - thread->draw_y;
177942542f5fSchristos	for (n = thread->ntrap, t = thread->traps; n--; t++) {
178042542f5fSchristos		if (pixman_fixed_integer_floor(t->top) >= y2 ||
178142542f5fSchristos		    pixman_fixed_integer_ceil(t->bottom) <= y1)
178242542f5fSchristos			continue;
178342542f5fSchristos
178413496ba1Ssnj		tor_add_trapezoid(&tor, t, thread->dx, thread->dy);
178542542f5fSchristos	}
178642542f5fSchristos
178742542f5fSchristos	tor_render(thread->sna, &tor,
178842542f5fSchristos		   (struct sna_composite_spans_op *)&boxes, thread->clip,
178942542f5fSchristos		   thread->span, thread->unbounded);
179042542f5fSchristos
179142542f5fSchristos	tor_fini(&tor);
179242542f5fSchristos
179342542f5fSchristos	if (boxes.num_boxes) {
179442542f5fSchristos		DBG(("%s: flushing %d boxes\n", __FUNCTION__, boxes.num_boxes));
179542542f5fSchristos		assert(boxes.num_boxes <= SPAN_THREAD_MAX_BOXES);
179642542f5fSchristos		thread->op->thread_boxes(thread->sna, thread->op,
179742542f5fSchristos					 boxes.boxes, boxes.num_boxes);
179842542f5fSchristos	}
179942542f5fSchristos}
180042542f5fSchristos
180142542f5fSchristosbool
180242542f5fSchristosprecise_trapezoid_span_converter(struct sna *sna,
180342542f5fSchristos				 CARD8 op, PicturePtr src, PicturePtr dst,
180442542f5fSchristos				 PictFormatPtr maskFormat, unsigned int flags,
180542542f5fSchristos				 INT16 src_x, INT16 src_y,
180642542f5fSchristos				 int ntrap, xTrapezoid *traps)
180742542f5fSchristos{
180842542f5fSchristos	struct sna_composite_spans_op tmp;
180942542f5fSchristos	pixman_region16_t clip;
181042542f5fSchristos	int16_t dst_x, dst_y;
181142542f5fSchristos	bool was_clear;
181242542f5fSchristos	int dx, dy, n;
181342542f5fSchristos	int num_threads;
181442542f5fSchristos
181542542f5fSchristos	if (NO_PRECISE)
181642542f5fSchristos		return false;
181742542f5fSchristos
181842542f5fSchristos	if (!sna->render.check_composite_spans(sna, op, src, dst, 0, 0, flags)) {
181942542f5fSchristos		DBG(("%s: fallback -- composite spans not supported\n",
182042542f5fSchristos		     __FUNCTION__));
182142542f5fSchristos		return false;
182242542f5fSchristos	}
182342542f5fSchristos
182442542f5fSchristos	if (!trapezoids_bounds(ntrap, traps, &clip.extents))
182542542f5fSchristos		return true;
182642542f5fSchristos
182742542f5fSchristos#if 1
182842542f5fSchristos	if (((clip.extents.y2 - clip.extents.y1) | (clip.extents.x2 - clip.extents.x1)) < 32) {
182942542f5fSchristos		DBG(("%s: fallback -- traps extents too small %dx%d\n", __FUNCTION__,
183042542f5fSchristos		     clip.extents.y2 - clip.extents.y1,
183142542f5fSchristos		     clip.extents.x2 - clip.extents.x1));
183242542f5fSchristos		return false;
183342542f5fSchristos	}
183442542f5fSchristos#endif
183542542f5fSchristos
183642542f5fSchristos	DBG(("%s: extents (%d, %d), (%d, %d)\n",
183742542f5fSchristos	     __FUNCTION__,
183842542f5fSchristos	     clip.extents.x1, clip.extents.y1,
183942542f5fSchristos	     clip.extents.x2, clip.extents.y2));
184042542f5fSchristos
184142542f5fSchristos	trapezoid_origin(&traps[0].left, &dst_x, &dst_y);
184242542f5fSchristos
184342542f5fSchristos	if (!sna_compute_composite_region(&clip,
184442542f5fSchristos					  src, NULL, dst,
184542542f5fSchristos					  src_x + clip.extents.x1 - dst_x,
184642542f5fSchristos					  src_y + clip.extents.y1 - dst_y,
184742542f5fSchristos					  0, 0,
184842542f5fSchristos					  clip.extents.x1, clip.extents.y1,
184942542f5fSchristos					  clip.extents.x2 - clip.extents.x1,
185042542f5fSchristos					  clip.extents.y2 - clip.extents.y1)) {
185142542f5fSchristos		DBG(("%s: trapezoids do not intersect drawable clips\n",
185242542f5fSchristos		     __FUNCTION__)) ;
185342542f5fSchristos		return true;
185442542f5fSchristos	}
185542542f5fSchristos
185642542f5fSchristos	if (!sna->render.check_composite_spans(sna, op, src, dst,
185742542f5fSchristos					       clip.extents.x2 - clip.extents.x1,
185842542f5fSchristos					       clip.extents.y2 - clip.extents.y1,
185942542f5fSchristos					       flags)) {
186042542f5fSchristos		DBG(("%s: fallback -- composite spans not supported\n",
186142542f5fSchristos		     __FUNCTION__));
186242542f5fSchristos		return false;
186342542f5fSchristos	}
186442542f5fSchristos
186542542f5fSchristos	dx = dst->pDrawable->x;
186642542f5fSchristos	dy = dst->pDrawable->y;
186742542f5fSchristos
186842542f5fSchristos	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
186942542f5fSchristos	     __FUNCTION__,
187042542f5fSchristos	     clip.extents.x1, clip.extents.y1,
187142542f5fSchristos	     clip.extents.x2, clip.extents.y2,
187242542f5fSchristos	     dx, dy,
187342542f5fSchristos	     src_x + clip.extents.x1 - dst_x - dx,
187442542f5fSchristos	     src_y + clip.extents.y1 - dst_y - dy));
187542542f5fSchristos
187642542f5fSchristos	was_clear = sna_drawable_is_clear(dst->pDrawable);
187742542f5fSchristos	switch (op) {
187842542f5fSchristos	case PictOpAdd:
187942542f5fSchristos	case PictOpOver:
188042542f5fSchristos		if (was_clear)
188142542f5fSchristos			op = PictOpSrc;
188242542f5fSchristos		break;
188342542f5fSchristos	case PictOpIn:
188442542f5fSchristos		if (was_clear)
188542542f5fSchristos			return true;
188642542f5fSchristos		break;
188742542f5fSchristos	}
188842542f5fSchristos
188942542f5fSchristos	if (!sna->render.composite_spans(sna, op, src, dst,
189042542f5fSchristos					 src_x + clip.extents.x1 - dst_x - dx,
189142542f5fSchristos					 src_y + clip.extents.y1 - dst_y - dy,
189242542f5fSchristos					 clip.extents.x1,  clip.extents.y1,
189342542f5fSchristos					 clip.extents.x2 - clip.extents.x1,
189442542f5fSchristos					 clip.extents.y2 - clip.extents.y1,
189542542f5fSchristos					 flags, memset(&tmp, 0, sizeof(tmp)))) {
189642542f5fSchristos		DBG(("%s: fallback -- composite spans render op not supported\n",
189742542f5fSchristos		     __FUNCTION__));
189842542f5fSchristos		return false;
189942542f5fSchristos	}
190042542f5fSchristos
190142542f5fSchristos	dx *= SAMPLES_X;
190242542f5fSchristos	dy *= SAMPLES_Y;
190342542f5fSchristos
190442542f5fSchristos	num_threads = 1;
190542542f5fSchristos	if (!NO_GPU_THREADS &&
190642542f5fSchristos	    (flags & COMPOSITE_SPANS_RECTILINEAR) == 0 &&
190742542f5fSchristos	    tmp.thread_boxes &&
190842542f5fSchristos	    thread_choose_span(&tmp, dst, maskFormat, &clip))
190942542f5fSchristos		num_threads = sna_use_threads(clip.extents.x2-clip.extents.x1,
191042542f5fSchristos					      clip.extents.y2-clip.extents.y1,
191142542f5fSchristos					      8);
191242542f5fSchristos	DBG(("%s: using %d threads\n", __FUNCTION__, num_threads));
191342542f5fSchristos	if (num_threads == 1) {
191442542f5fSchristos		struct tor tor;
191542542f5fSchristos
191642542f5fSchristos		if (!tor_init(&tor, &clip.extents, 2*ntrap))
191742542f5fSchristos			goto skip;
191842542f5fSchristos
191942542f5fSchristos		for (n = 0; n < ntrap; n++) {
192042542f5fSchristos			if (pixman_fixed_integer_floor(traps[n].top) + dst->pDrawable->y >= clip.extents.y2 ||
192142542f5fSchristos			    pixman_fixed_integer_ceil(traps[n].bottom) + dst->pDrawable->y <= clip.extents.y1)
192242542f5fSchristos				continue;
192342542f5fSchristos
192413496ba1Ssnj			tor_add_trapezoid(&tor, &traps[n], dx, dy);
192542542f5fSchristos		}
192642542f5fSchristos
192742542f5fSchristos		tor_render(sna, &tor, &tmp, &clip,
192842542f5fSchristos			   choose_span(&tmp, dst, maskFormat, &clip),
192942542f5fSchristos			   !was_clear && maskFormat && !operator_is_bounded(op));
193042542f5fSchristos
193142542f5fSchristos		tor_fini(&tor);
193242542f5fSchristos	} else {
193342542f5fSchristos		struct span_thread threads[num_threads];
193442542f5fSchristos		int y, h;
193542542f5fSchristos
193642542f5fSchristos		DBG(("%s: using %d threads for span compositing %dx%d\n",
193742542f5fSchristos		     __FUNCTION__, num_threads,
193842542f5fSchristos		     clip.extents.x2 - clip.extents.x1,
193942542f5fSchristos		     clip.extents.y2 - clip.extents.y1));
194042542f5fSchristos
194142542f5fSchristos		threads[0].sna = sna;
194242542f5fSchristos		threads[0].op = &tmp;
194342542f5fSchristos		threads[0].traps = traps;
194442542f5fSchristos		threads[0].ntrap = ntrap;
194542542f5fSchristos		threads[0].extents = clip.extents;
194642542f5fSchristos		threads[0].clip = &clip;
194742542f5fSchristos		threads[0].dx = dx;
194842542f5fSchristos		threads[0].dy = dy;
194942542f5fSchristos		threads[0].draw_y = dst->pDrawable->y;
195042542f5fSchristos		threads[0].unbounded = !was_clear && maskFormat && !operator_is_bounded(op);
195142542f5fSchristos		threads[0].span = thread_choose_span(&tmp, dst, maskFormat, &clip);
195242542f5fSchristos
195342542f5fSchristos		y = clip.extents.y1;
195442542f5fSchristos		h = clip.extents.y2 - clip.extents.y1;
195542542f5fSchristos		h = (h + num_threads - 1) / num_threads;
195642542f5fSchristos		num_threads -= (num_threads-1) * h >= clip.extents.y2 - clip.extents.y1;
195742542f5fSchristos
195842542f5fSchristos		for (n = 1; n < num_threads; n++) {
195942542f5fSchristos			threads[n] = threads[0];
196042542f5fSchristos			threads[n].extents.y1 = y;
196142542f5fSchristos			threads[n].extents.y2 = y += h;
196242542f5fSchristos
196342542f5fSchristos			sna_threads_run(n, span_thread, &threads[n]);
196442542f5fSchristos		}
196542542f5fSchristos
196642542f5fSchristos		assert(y < threads[0].extents.y2);
196742542f5fSchristos		threads[0].extents.y1 = y;
196842542f5fSchristos		span_thread(&threads[0]);
196942542f5fSchristos
197042542f5fSchristos		sna_threads_wait();
197142542f5fSchristos	}
197242542f5fSchristosskip:
197342542f5fSchristos	tmp.done(sna, &tmp);
197442542f5fSchristos
197542542f5fSchristos	REGION_UNINIT(NULL, &clip);
197642542f5fSchristos	return true;
197742542f5fSchristos}
197842542f5fSchristos
197942542f5fSchristosstatic void
198042542f5fSchristostor_blt_mask(struct sna *sna,
198142542f5fSchristos	     struct sna_composite_spans_op *op,
198242542f5fSchristos	     pixman_region16_t *clip,
198342542f5fSchristos	     const BoxRec *box,
198442542f5fSchristos	     int coverage)
198542542f5fSchristos{
198642542f5fSchristos	uint8_t *ptr = (uint8_t *)op;
198742542f5fSchristos	int stride = (intptr_t)clip;
198842542f5fSchristos	int h, w;
198942542f5fSchristos
199042542f5fSchristos	coverage = TO_ALPHA(coverage);
199142542f5fSchristos	ptr += box->y1 * stride + box->x1;
199242542f5fSchristos
199342542f5fSchristos	h = box->y2 - box->y1;
199442542f5fSchristos	w = box->x2 - box->x1;
199542542f5fSchristos	if ((w | h) == 1) {
199642542f5fSchristos		*ptr = coverage;
199742542f5fSchristos	} else if (w == 1) {
199842542f5fSchristos		do {
199942542f5fSchristos			*ptr = coverage;
200042542f5fSchristos			ptr += stride;
200142542f5fSchristos		} while (--h);
200242542f5fSchristos	} else do {
200342542f5fSchristos		memset(ptr, coverage, w);
200442542f5fSchristos		ptr += stride;
200542542f5fSchristos	} while (--h);
200642542f5fSchristos}
200742542f5fSchristos
200842542f5fSchristosstruct mask_thread {
200942542f5fSchristos	PixmapPtr scratch;
201042542f5fSchristos	const xTrapezoid *traps;
201142542f5fSchristos	BoxRec extents;
201242542f5fSchristos	int dx, dy, dst_y;
201342542f5fSchristos	int ntrap;
201442542f5fSchristos};
201542542f5fSchristos
201642542f5fSchristosstatic void
201742542f5fSchristosmask_thread(void *arg)
201842542f5fSchristos{
201942542f5fSchristos	struct mask_thread *thread = arg;
202042542f5fSchristos	struct tor tor;
202142542f5fSchristos	const xTrapezoid *t;
202242542f5fSchristos	int n, y1, y2;
202342542f5fSchristos
202442542f5fSchristos	if (!tor_init(&tor, &thread->extents, 2*thread->ntrap))
202542542f5fSchristos		return;
202642542f5fSchristos
202742542f5fSchristos	y1 = thread->extents.y1 + thread->dst_y;
202842542f5fSchristos	y2 = thread->extents.y2 + thread->dst_y;
202942542f5fSchristos	for (n = thread->ntrap, t = thread->traps; n--; t++) {
203042542f5fSchristos		if (pixman_fixed_integer_floor(t->top) >= y2 ||
203142542f5fSchristos		    pixman_fixed_integer_ceil(t->bottom) <= y1)
203242542f5fSchristos			continue;
203342542f5fSchristos
203413496ba1Ssnj		tor_add_trapezoid(&tor, t, thread->dx, thread->dy);
203542542f5fSchristos	}
203642542f5fSchristos
203742542f5fSchristos	if (thread->extents.x2 <= TOR_INPLACE_SIZE) {
203842542f5fSchristos		tor_inplace(&tor, thread->scratch);
203942542f5fSchristos	} else {
204042542f5fSchristos		tor_render(NULL, &tor,
204142542f5fSchristos			   thread->scratch->devPrivate.ptr,
204242542f5fSchristos			   (void *)(intptr_t)thread->scratch->devKind,
204342542f5fSchristos			   tor_blt_mask,
204442542f5fSchristos			   true);
204542542f5fSchristos	}
204642542f5fSchristos
204742542f5fSchristos	tor_fini(&tor);
204842542f5fSchristos}
204942542f5fSchristos
205042542f5fSchristosbool
205142542f5fSchristosprecise_trapezoid_mask_converter(CARD8 op, PicturePtr src, PicturePtr dst,
205242542f5fSchristos				 PictFormatPtr maskFormat, unsigned flags,
205342542f5fSchristos				 INT16 src_x, INT16 src_y,
205442542f5fSchristos				 int ntrap, xTrapezoid *traps)
205542542f5fSchristos{
205642542f5fSchristos	ScreenPtr screen = dst->pDrawable->pScreen;
205742542f5fSchristos	PixmapPtr scratch;
205842542f5fSchristos	PicturePtr mask;
205942542f5fSchristos	BoxRec extents;
206042542f5fSchristos	int num_threads;
206142542f5fSchristos	int16_t dst_x, dst_y;
206242542f5fSchristos	int dx, dy;
206342542f5fSchristos	int error, n;
206442542f5fSchristos
206542542f5fSchristos	if (NO_PRECISE)
206642542f5fSchristos		return false;
206742542f5fSchristos
206842542f5fSchristos	if (maskFormat == NULL && ntrap > 1) {
206942542f5fSchristos		DBG(("%s: individual rasterisation requested\n",
207042542f5fSchristos		     __FUNCTION__));
207142542f5fSchristos		do {
207242542f5fSchristos			/* XXX unwind errors? */
207342542f5fSchristos			if (!precise_trapezoid_mask_converter(op, src, dst, NULL, flags,
207442542f5fSchristos							      src_x, src_y, 1, traps++))
207542542f5fSchristos				return false;
207642542f5fSchristos		} while (--ntrap);
207742542f5fSchristos		return true;
207842542f5fSchristos	}
207942542f5fSchristos
208042542f5fSchristos	if (!trapezoids_bounds(ntrap, traps, &extents))
208142542f5fSchristos		return true;
208242542f5fSchristos
208342542f5fSchristos	DBG(("%s: ntraps=%d, extents (%d, %d), (%d, %d)\n",
208442542f5fSchristos	     __FUNCTION__, ntrap, extents.x1, extents.y1, extents.x2, extents.y2));
208542542f5fSchristos
208642542f5fSchristos	if (!sna_compute_composite_extents(&extents,
208742542f5fSchristos					   src, NULL, dst,
208842542f5fSchristos					   src_x, src_y,
208942542f5fSchristos					   0, 0,
209042542f5fSchristos					   extents.x1, extents.y1,
209142542f5fSchristos					   extents.x2 - extents.x1,
209242542f5fSchristos					   extents.y2 - extents.y1))
209342542f5fSchristos		return true;
209442542f5fSchristos
209542542f5fSchristos	DBG(("%s: extents (%d, %d), (%d, %d)\n",
209642542f5fSchristos	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
209742542f5fSchristos
209842542f5fSchristos	extents.y2 -= extents.y1;
209942542f5fSchristos	extents.x2 -= extents.x1;
210042542f5fSchristos	extents.x1 -= dst->pDrawable->x;
210142542f5fSchristos	extents.y1 -= dst->pDrawable->y;
210242542f5fSchristos	dst_x = extents.x1;
210342542f5fSchristos	dst_y = extents.y1;
210442542f5fSchristos	dx = -extents.x1 * SAMPLES_X;
210542542f5fSchristos	dy = -extents.y1 * SAMPLES_Y;
210642542f5fSchristos	extents.x1 = extents.y1 = 0;
210742542f5fSchristos
210842542f5fSchristos	DBG(("%s: mask (%dx%d), dx=(%d, %d)\n",
210942542f5fSchristos	     __FUNCTION__, extents.x2, extents.y2, dx, dy));
211042542f5fSchristos	scratch = sna_pixmap_create_upload(screen,
211142542f5fSchristos					   extents.x2, extents.y2, 8,
211242542f5fSchristos					   KGEM_BUFFER_WRITE_INPLACE);
211342542f5fSchristos	if (!scratch)
211442542f5fSchristos		return true;
211542542f5fSchristos
211642542f5fSchristos	DBG(("%s: created buffer %p, stride %d\n",
211742542f5fSchristos	     __FUNCTION__, scratch->devPrivate.ptr, scratch->devKind));
211842542f5fSchristos
211942542f5fSchristos	num_threads = 1;
212013496ba1Ssnj	if (!NO_GPU_THREADS &&
212113496ba1Ssnj	    (flags & COMPOSITE_SPANS_RECTILINEAR) == 0)
212242542f5fSchristos		num_threads = sna_use_threads(extents.x2 - extents.x1,
212342542f5fSchristos					      extents.y2 - extents.y1,
212442542f5fSchristos					      4);
212542542f5fSchristos	if (num_threads == 1) {
212642542f5fSchristos		struct tor tor;
212742542f5fSchristos
212842542f5fSchristos		if (!tor_init(&tor, &extents, 2*ntrap)) {
212942542f5fSchristos			sna_pixmap_destroy(scratch);
213042542f5fSchristos			return true;
213142542f5fSchristos		}
213242542f5fSchristos
213342542f5fSchristos		for (n = 0; n < ntrap; n++) {
213442542f5fSchristos			if (pixman_fixed_to_int(traps[n].top) - dst_y >= extents.y2 ||
213542542f5fSchristos			    pixman_fixed_to_int(traps[n].bottom) - dst_y < 0)
213642542f5fSchristos				continue;
213742542f5fSchristos
213813496ba1Ssnj			tor_add_trapezoid(&tor, &traps[n], dx, dy);
213942542f5fSchristos		}
214042542f5fSchristos
214142542f5fSchristos		if (extents.x2 <= TOR_INPLACE_SIZE) {
214242542f5fSchristos			tor_inplace(&tor, scratch);
214342542f5fSchristos		} else {
214442542f5fSchristos			tor_render(NULL, &tor,
214542542f5fSchristos				   scratch->devPrivate.ptr,
214642542f5fSchristos				   (void *)(intptr_t)scratch->devKind,
214742542f5fSchristos				   tor_blt_mask,
214842542f5fSchristos				   true);
214942542f5fSchristos		}
215042542f5fSchristos		tor_fini(&tor);
215142542f5fSchristos	} else {
215242542f5fSchristos		struct mask_thread threads[num_threads];
215342542f5fSchristos		int y, h;
215442542f5fSchristos
215542542f5fSchristos		DBG(("%s: using %d threads for mask compositing %dx%d\n",
215642542f5fSchristos		     __FUNCTION__, num_threads,
215742542f5fSchristos		     extents.x2 - extents.x1,
215842542f5fSchristos		     extents.y2 - extents.y1));
215942542f5fSchristos
216042542f5fSchristos		threads[0].scratch = scratch;
216142542f5fSchristos		threads[0].traps = traps;
216242542f5fSchristos		threads[0].ntrap = ntrap;
216342542f5fSchristos		threads[0].extents = extents;
216442542f5fSchristos		threads[0].dx = dx;
216542542f5fSchristos		threads[0].dy = dy;
216642542f5fSchristos		threads[0].dst_y = dst_y;
216742542f5fSchristos
216842542f5fSchristos		y = extents.y1;
216942542f5fSchristos		h = extents.y2 - extents.y1;
217042542f5fSchristos		h = (h + num_threads - 1) / num_threads;
217142542f5fSchristos		num_threads -= (num_threads-1) * h >= extents.y2 - extents.y1;
217242542f5fSchristos
217342542f5fSchristos		for (n = 1; n < num_threads; n++) {
217442542f5fSchristos			threads[n] = threads[0];
217542542f5fSchristos			threads[n].extents.y1 = y;
217642542f5fSchristos			threads[n].extents.y2 = y += h;
217742542f5fSchristos
217842542f5fSchristos			sna_threads_run(n, mask_thread, &threads[n]);
217942542f5fSchristos		}
218042542f5fSchristos
218142542f5fSchristos		assert(y < threads[0].extents.y2);
218242542f5fSchristos		threads[0].extents.y1 = y;
218342542f5fSchristos		mask_thread(&threads[0]);
218442542f5fSchristos
218542542f5fSchristos		sna_threads_wait();
218642542f5fSchristos	}
218742542f5fSchristos
218842542f5fSchristos	mask = CreatePicture(0, &scratch->drawable,
218942542f5fSchristos			     PictureMatchFormat(screen, 8, PICT_a8),
219042542f5fSchristos			     0, 0, serverClient, &error);
219142542f5fSchristos	if (mask) {
219242542f5fSchristos		int16_t x0, y0;
219342542f5fSchristos
219442542f5fSchristos		trapezoid_origin(&traps[0].left, &x0, &y0);
219542542f5fSchristos
219642542f5fSchristos		CompositePicture(op, src, mask, dst,
219742542f5fSchristos				 src_x + dst_x - x0,
219842542f5fSchristos				 src_y + dst_y - y0,
219942542f5fSchristos				 0, 0,
220042542f5fSchristos				 dst_x, dst_y,
220142542f5fSchristos				 extents.x2, extents.y2);
220242542f5fSchristos		FreePicture(mask, 0);
220342542f5fSchristos	}
220442542f5fSchristos	sna_pixmap_destroy(scratch);
220542542f5fSchristos
220642542f5fSchristos	return true;
220742542f5fSchristos}
220842542f5fSchristos
220942542f5fSchristosstruct inplace {
221042542f5fSchristos	uint8_t *ptr;
221142542f5fSchristos	uint32_t stride;
221242542f5fSchristos	union {
221342542f5fSchristos		uint8_t opacity;
221442542f5fSchristos		uint32_t color;
221542542f5fSchristos	};
221642542f5fSchristos};
221742542f5fSchristos
221842542f5fSchristosstatic force_inline uint8_t coverage_opacity(int coverage, uint8_t opacity)
221942542f5fSchristos{
222042542f5fSchristos	coverage = TO_ALPHA(coverage);
222142542f5fSchristos	return opacity == 255 ? coverage : mul_8_8(coverage, opacity);
222242542f5fSchristos}
222342542f5fSchristos
2224fe8aea9eSmrgstruct clipped_span {
2225fe8aea9eSmrg	span_func_t span;
2226fe8aea9eSmrg	const BoxRec *clip_start, *clip_end;
2227fe8aea9eSmrg};
2228fe8aea9eSmrg
2229fe8aea9eSmrgstatic void
2230fe8aea9eSmrgtor_blt_clipped(struct sna *sna,
2231fe8aea9eSmrg		struct sna_composite_spans_op *op,
2232fe8aea9eSmrg		pixman_region16_t *clip,
2233fe8aea9eSmrg		const BoxRec *box,
2234fe8aea9eSmrg		int coverage)
2235fe8aea9eSmrg{
2236fe8aea9eSmrg	struct clipped_span *cs = (struct clipped_span *)clip;
2237fe8aea9eSmrg	const BoxRec *c;
2238fe8aea9eSmrg
2239fe8aea9eSmrg	cs->clip_start =
2240fe8aea9eSmrg		find_clip_box_for_y(cs->clip_start, cs->clip_end, box->y1);
2241fe8aea9eSmrg
2242fe8aea9eSmrg	c = cs->clip_start;
2243fe8aea9eSmrg	while (c != cs->clip_end) {
2244fe8aea9eSmrg		BoxRec clipped;
2245fe8aea9eSmrg
2246fe8aea9eSmrg		if (box->y2 <= c->y1)
2247fe8aea9eSmrg			break;
2248fe8aea9eSmrg
2249fe8aea9eSmrg		clipped = *box;
2250fe8aea9eSmrg		if (!box_intersect(&clipped, c++))
2251fe8aea9eSmrg			continue;
2252fe8aea9eSmrg
2253fe8aea9eSmrg		cs->span(sna, op, NULL, &clipped, coverage);
2254fe8aea9eSmrg	}
2255fe8aea9eSmrg}
2256fe8aea9eSmrg
2257fe8aea9eSmrginline static span_func_t
2258fe8aea9eSmrgclipped_span(struct clipped_span *cs,
2259fe8aea9eSmrg	     span_func_t span,
2260fe8aea9eSmrg	     const RegionRec *clip)
2261fe8aea9eSmrg{
2262fe8aea9eSmrg	if (clip->data) {
2263fe8aea9eSmrg		cs->span = span;
2264fe8aea9eSmrg		region_get_boxes(clip, &cs->clip_start, &cs->clip_end);
2265fe8aea9eSmrg		span = tor_blt_clipped;
2266fe8aea9eSmrg	}
2267fe8aea9eSmrg	return span;
2268fe8aea9eSmrg}
2269fe8aea9eSmrg
227042542f5fSchristosstatic void _tor_blt_src(struct inplace *in, const BoxRec *box, uint8_t v)
227142542f5fSchristos{
227242542f5fSchristos	uint8_t *ptr = in->ptr;
227342542f5fSchristos	int h, w;
227442542f5fSchristos
227542542f5fSchristos	ptr += box->y1 * in->stride + box->x1;
227642542f5fSchristos
227742542f5fSchristos	h = box->y2 - box->y1;
227842542f5fSchristos	w = box->x2 - box->x1;
227942542f5fSchristos	if ((w | h) == 1) {
228042542f5fSchristos		*ptr = v;
228142542f5fSchristos	} else if (w == 1) {
228242542f5fSchristos		do {
228342542f5fSchristos			*ptr = v;
228442542f5fSchristos			ptr += in->stride;
228542542f5fSchristos		} while (--h);
228642542f5fSchristos	} else do {
228742542f5fSchristos		memset(ptr, v, w);
228842542f5fSchristos		ptr += in->stride;
228942542f5fSchristos	} while (--h);
229042542f5fSchristos}
229142542f5fSchristos
229242542f5fSchristosstatic void
229342542f5fSchristostor_blt_src(struct sna *sna,
229442542f5fSchristos	    struct sna_composite_spans_op *op,
229542542f5fSchristos	    pixman_region16_t *clip,
229642542f5fSchristos	    const BoxRec *box,
229742542f5fSchristos	    int coverage)
229842542f5fSchristos{
229942542f5fSchristos	struct inplace *in = (struct inplace *)op;
230042542f5fSchristos
230142542f5fSchristos	_tor_blt_src(in, box, coverage_opacity(coverage, in->opacity));
230242542f5fSchristos}
230342542f5fSchristos
230442542f5fSchristosstatic void
230542542f5fSchristostor_blt_in(struct sna *sna,
230642542f5fSchristos	   struct sna_composite_spans_op *op,
230742542f5fSchristos	   pixman_region16_t *clip,
230842542f5fSchristos	   const BoxRec *box,
230942542f5fSchristos	   int coverage)
231042542f5fSchristos{
231142542f5fSchristos	struct inplace *in = (struct inplace *)op;
231242542f5fSchristos	uint8_t *ptr = in->ptr;
231342542f5fSchristos	int h, w, i;
231442542f5fSchristos
231542542f5fSchristos	if (coverage == 0 || in->opacity == 0) {
231642542f5fSchristos		_tor_blt_src(in, box, 0);
231742542f5fSchristos		return;
231842542f5fSchristos	}
231942542f5fSchristos
232042542f5fSchristos	coverage = coverage_opacity(coverage, in->opacity);
232142542f5fSchristos	if (coverage == 0xff)
232242542f5fSchristos		return;
232342542f5fSchristos
232442542f5fSchristos	ptr += box->y1 * in->stride + box->x1;
232542542f5fSchristos
232642542f5fSchristos	h = box->y2 - box->y1;
232742542f5fSchristos	w = box->x2 - box->x1;
232842542f5fSchristos	do {
232942542f5fSchristos		for (i = 0; i < w; i++)
233042542f5fSchristos			ptr[i] = mul_8_8(ptr[i], coverage);
233142542f5fSchristos		ptr += in->stride;
233242542f5fSchristos	} while (--h);
233342542f5fSchristos}
233442542f5fSchristos
233542542f5fSchristosstatic void
233642542f5fSchristostor_blt_add(struct sna *sna,
233742542f5fSchristos	    struct sna_composite_spans_op *op,
233842542f5fSchristos	    pixman_region16_t *clip,
233942542f5fSchristos	    const BoxRec *box,
234042542f5fSchristos	    int coverage)
234142542f5fSchristos{
234242542f5fSchristos	struct inplace *in = (struct inplace *)op;
234342542f5fSchristos	uint8_t *ptr = in->ptr;
234442542f5fSchristos	int h, w, v, i;
234542542f5fSchristos
234642542f5fSchristos	if (coverage == 0)
234742542f5fSchristos		return;
234842542f5fSchristos
234942542f5fSchristos	coverage = coverage_opacity(coverage, in->opacity);
235042542f5fSchristos	if (coverage == 0xff) {
235142542f5fSchristos		_tor_blt_src(in, box, 0xff);
235242542f5fSchristos		return;
235342542f5fSchristos	}
235442542f5fSchristos
235542542f5fSchristos	ptr += box->y1 * in->stride + box->x1;
235642542f5fSchristos
235742542f5fSchristos	h = box->y2 - box->y1;
235842542f5fSchristos	w = box->x2 - box->x1;
235942542f5fSchristos	if ((w | h) == 1) {
236042542f5fSchristos		v = coverage + *ptr;
236142542f5fSchristos		*ptr = v >= 255 ? 255 : v;
236242542f5fSchristos	} else {
236342542f5fSchristos		do {
236442542f5fSchristos			for (i = 0; i < w; i++) {
236542542f5fSchristos				v = coverage + ptr[i];
236642542f5fSchristos				ptr[i] = v >= 255 ? 255 : v;
236742542f5fSchristos			}
236842542f5fSchristos			ptr += in->stride;
236942542f5fSchristos		} while (--h);
237042542f5fSchristos	}
237142542f5fSchristos}
237242542f5fSchristos
237342542f5fSchristosstatic void
237442542f5fSchristostor_blt_lerp32(struct sna *sna,
237542542f5fSchristos	       struct sna_composite_spans_op *op,
237642542f5fSchristos	       pixman_region16_t *clip,
237742542f5fSchristos	       const BoxRec *box,
237842542f5fSchristos	       int coverage)
237942542f5fSchristos{
238042542f5fSchristos	struct inplace *in = (struct inplace *)op;
238142542f5fSchristos	uint32_t *ptr = (uint32_t *)in->ptr;
238242542f5fSchristos	int stride = in->stride / sizeof(uint32_t);
238342542f5fSchristos	int h, w, i;
238442542f5fSchristos
238542542f5fSchristos	if (coverage == 0)
238642542f5fSchristos		return;
238742542f5fSchristos
2388fe8aea9eSmrg	sigtrap_assert_active();
238942542f5fSchristos	ptr += box->y1 * stride + box->x1;
239042542f5fSchristos
239142542f5fSchristos	h = box->y2 - box->y1;
239242542f5fSchristos	w = box->x2 - box->x1;
239342542f5fSchristos	if (coverage == GRID_AREA) {
239442542f5fSchristos		if ((w | h) == 1) {
239542542f5fSchristos			*ptr = in->color;
239642542f5fSchristos		} else {
239742542f5fSchristos			if (w < 16) {
239842542f5fSchristos				do {
239942542f5fSchristos					for (i = 0; i < w; i++)
240042542f5fSchristos						ptr[i] = in->color;
240142542f5fSchristos					ptr += stride;
240242542f5fSchristos				} while (--h);
240342542f5fSchristos			} else {
240442542f5fSchristos				pixman_fill(ptr, stride, 32,
240542542f5fSchristos					    0, 0, w, h, in->color);
240642542f5fSchristos			}
240742542f5fSchristos		}
240842542f5fSchristos	} else {
240942542f5fSchristos		coverage = TO_ALPHA(coverage);
241042542f5fSchristos		if ((w | h) == 1) {
241142542f5fSchristos			*ptr = lerp8x4(in->color, coverage, *ptr);
241242542f5fSchristos		} else if (w == 1) {
241342542f5fSchristos			do {
241442542f5fSchristos				*ptr = lerp8x4(in->color, coverage, *ptr);
241542542f5fSchristos				ptr += stride;
241642542f5fSchristos			} while (--h);
241742542f5fSchristos		} else{
241842542f5fSchristos			do {
241942542f5fSchristos				for (i = 0; i < w; i++)
242042542f5fSchristos					ptr[i] = lerp8x4(in->color, coverage, ptr[i]);
242142542f5fSchristos				ptr += stride;
242242542f5fSchristos			} while (--h);
242342542f5fSchristos		}
242442542f5fSchristos	}
242542542f5fSchristos}
242642542f5fSchristos
242742542f5fSchristosstruct pixman_inplace {
242842542f5fSchristos	pixman_image_t *image, *source, *mask;
242942542f5fSchristos	uint32_t color;
243042542f5fSchristos	uint32_t *bits;
243142542f5fSchristos	int dx, dy;
243242542f5fSchristos	int sx, sy;
243342542f5fSchristos	uint8_t op;
243442542f5fSchristos};
243542542f5fSchristos
243642542f5fSchristosstatic void
243742542f5fSchristospixmask_span_solid(struct sna *sna,
243842542f5fSchristos		   struct sna_composite_spans_op *op,
243942542f5fSchristos		   pixman_region16_t *clip,
244042542f5fSchristos		   const BoxRec *box,
244142542f5fSchristos		   int coverage)
244242542f5fSchristos{
244342542f5fSchristos	struct pixman_inplace *pi = (struct pixman_inplace *)op;
244442542f5fSchristos	if (coverage != GRID_AREA)
244542542f5fSchristos		*pi->bits = mul_4x8_8(pi->color, TO_ALPHA(coverage));
244642542f5fSchristos	else
244742542f5fSchristos		*pi->bits = pi->color;
244842542f5fSchristos	pixman_image_composite(pi->op, pi->source, NULL, pi->image,
244942542f5fSchristos			       box->x1, box->y1,
245042542f5fSchristos			       0, 0,
245142542f5fSchristos			       pi->dx + box->x1, pi->dy + box->y1,
245242542f5fSchristos			       box->x2 - box->x1, box->y2 - box->y1);
245342542f5fSchristos}
245442542f5fSchristos
245542542f5fSchristosstatic void
245642542f5fSchristospixmask_span(struct sna *sna,
245742542f5fSchristos	     struct sna_composite_spans_op *op,
245842542f5fSchristos	     pixman_region16_t *clip,
245942542f5fSchristos	     const BoxRec *box,
246042542f5fSchristos	     int coverage)
246142542f5fSchristos{
246242542f5fSchristos	struct pixman_inplace *pi = (struct pixman_inplace *)op;
246342542f5fSchristos	pixman_image_t *mask = NULL;
246442542f5fSchristos	if (coverage != GRID_AREA) {
246542542f5fSchristos		*pi->bits = TO_ALPHA(coverage);
246642542f5fSchristos		mask = pi->mask;
246742542f5fSchristos	}
246842542f5fSchristos	pixman_image_composite(pi->op, pi->source, mask, pi->image,
246942542f5fSchristos			       pi->sx + box->x1, pi->sy + box->y1,
247042542f5fSchristos			       0, 0,
247142542f5fSchristos			       pi->dx + box->x1, pi->dy + box->y1,
247242542f5fSchristos			       box->x2 - box->x1, box->y2 - box->y1);
247342542f5fSchristos}
247442542f5fSchristos
247542542f5fSchristosstruct inplace_x8r8g8b8_thread {
247642542f5fSchristos	xTrapezoid *traps;
247742542f5fSchristos	PicturePtr dst, src;
247842542f5fSchristos	BoxRec extents;
247942542f5fSchristos	int dx, dy;
248042542f5fSchristos	int ntrap;
248142542f5fSchristos	bool lerp, is_solid;
248242542f5fSchristos	uint32_t color;
248342542f5fSchristos	int16_t src_x, src_y;
248442542f5fSchristos	uint8_t op;
248542542f5fSchristos};
248642542f5fSchristos
248742542f5fSchristosstatic void inplace_x8r8g8b8_thread(void *arg)
248842542f5fSchristos{
248942542f5fSchristos	struct inplace_x8r8g8b8_thread *thread = arg;
249042542f5fSchristos	struct tor tor;
249142542f5fSchristos	span_func_t span;
2492fe8aea9eSmrg	struct clipped_span clipped;
249342542f5fSchristos	RegionPtr clip;
249442542f5fSchristos	int y1, y2, n;
249542542f5fSchristos
249642542f5fSchristos	if (!tor_init(&tor, &thread->extents, 2*thread->ntrap))
249742542f5fSchristos		return;
249842542f5fSchristos
249942542f5fSchristos	y1 = thread->extents.y1 - thread->dst->pDrawable->y;
250042542f5fSchristos	y2 = thread->extents.y2 - thread->dst->pDrawable->y;
250142542f5fSchristos	for (n = 0; n < thread->ntrap; n++) {
250242542f5fSchristos		if (pixman_fixed_to_int(thread->traps[n].top) >= y2 ||
250342542f5fSchristos		    pixman_fixed_to_int(thread->traps[n].bottom) < y1)
250442542f5fSchristos			continue;
250542542f5fSchristos
250613496ba1Ssnj		tor_add_trapezoid(&tor, &thread->traps[n], thread->dx, thread->dy);
250742542f5fSchristos	}
250842542f5fSchristos
250942542f5fSchristos	clip = thread->dst->pCompositeClip;
251042542f5fSchristos	if (thread->lerp) {
251142542f5fSchristos		struct inplace inplace;
251242542f5fSchristos		int16_t dst_x, dst_y;
251342542f5fSchristos		PixmapPtr pixmap;
251442542f5fSchristos
251542542f5fSchristos		pixmap = get_drawable_pixmap(thread->dst->pDrawable);
251642542f5fSchristos
251742542f5fSchristos		inplace.ptr = pixmap->devPrivate.ptr;
251842542f5fSchristos		if (get_drawable_deltas(thread->dst->pDrawable, pixmap, &dst_x, &dst_y))
251942542f5fSchristos			inplace.ptr += dst_y * pixmap->devKind + dst_x * 4;
252042542f5fSchristos		inplace.stride = pixmap->devKind;
252142542f5fSchristos		inplace.color = thread->color;
252242542f5fSchristos
2523fe8aea9eSmrg		span = clipped_span(&clipped, tor_blt_lerp32, clip);
252442542f5fSchristos
2525fe8aea9eSmrg		tor_render(NULL, &tor,
2526fe8aea9eSmrg			   (void*)&inplace, (void *)&clipped,
2527fe8aea9eSmrg			   span, false);
252842542f5fSchristos	} else if (thread->is_solid) {
252942542f5fSchristos		struct pixman_inplace pi;
253042542f5fSchristos
253142542f5fSchristos		pi.image = image_from_pict(thread->dst, false, &pi.dx, &pi.dy);
253242542f5fSchristos		pi.op = thread->op;
253342542f5fSchristos		pi.color = thread->color;
253442542f5fSchristos
253542542f5fSchristos		pi.bits = (uint32_t *)&pi.sx;
253642542f5fSchristos		pi.source = pixman_image_create_bits(PIXMAN_a8r8g8b8,
253742542f5fSchristos						     1, 1, pi.bits, 0);
253842542f5fSchristos		pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL);
253942542f5fSchristos
2540fe8aea9eSmrg		span = clipped_span(&clipped, pixmask_span_solid, clip);
254142542f5fSchristos
254242542f5fSchristos		tor_render(NULL, &tor, (void*)&pi, clip, span, false);
254342542f5fSchristos
254442542f5fSchristos		pixman_image_unref(pi.source);
254542542f5fSchristos		pixman_image_unref(pi.image);
254642542f5fSchristos	} else {
254742542f5fSchristos		struct pixman_inplace pi;
254842542f5fSchristos		int16_t x0, y0;
254942542f5fSchristos
255042542f5fSchristos		trapezoid_origin(&thread->traps[0].left, &x0, &y0);
255142542f5fSchristos
255242542f5fSchristos		pi.image = image_from_pict(thread->dst, false, &pi.dx, &pi.dy);
255342542f5fSchristos		pi.source = image_from_pict(thread->src, false, &pi.sx, &pi.sy);
255442542f5fSchristos		pi.sx += thread->src_x - x0;
255542542f5fSchristos		pi.sy += thread->src_y - y0;
255642542f5fSchristos		pi.mask = pixman_image_create_bits(PIXMAN_a8, 1, 1, NULL, 0);
255742542f5fSchristos		pixman_image_set_repeat(pi.mask, PIXMAN_REPEAT_NORMAL);
255842542f5fSchristos		pi.bits = pixman_image_get_data(pi.mask);
255942542f5fSchristos		pi.op = thread->op;
256042542f5fSchristos
2561fe8aea9eSmrg		span = clipped_span(&clipped, pixmask_span, clip);
256242542f5fSchristos
2563fe8aea9eSmrg		tor_render(NULL, &tor,
2564fe8aea9eSmrg			   (void*)&pi, (void *)&clipped,
2565fe8aea9eSmrg			   span, false);
256642542f5fSchristos
256742542f5fSchristos		pixman_image_unref(pi.mask);
256842542f5fSchristos		pixman_image_unref(pi.source);
256942542f5fSchristos		pixman_image_unref(pi.image);
257042542f5fSchristos	}
257142542f5fSchristos
257242542f5fSchristos	tor_fini(&tor);
257342542f5fSchristos}
257442542f5fSchristos
257542542f5fSchristosstatic bool
257642542f5fSchristostrapezoid_span_inplace__x8r8g8b8(CARD8 op,
257742542f5fSchristos				 PicturePtr dst,
257842542f5fSchristos				 PicturePtr src, int16_t src_x, int16_t src_y,
257942542f5fSchristos				 PictFormatPtr maskFormat, unsigned flags,
258042542f5fSchristos				 int ntrap, xTrapezoid *traps)
258142542f5fSchristos{
258242542f5fSchristos	uint32_t color;
258342542f5fSchristos	bool lerp, is_solid;
258442542f5fSchristos	RegionRec region;
258542542f5fSchristos	int dx, dy;
258642542f5fSchristos	int num_threads, n;
258742542f5fSchristos
258842542f5fSchristos	lerp = false;
258942542f5fSchristos	is_solid = sna_picture_is_solid(src, &color);
259042542f5fSchristos	if (is_solid) {
259142542f5fSchristos		if (op == PictOpOver && (color >> 24) == 0xff)
259242542f5fSchristos			op = PictOpSrc;
259342542f5fSchristos		if (op == PictOpOver && sna_drawable_is_clear(dst->pDrawable))
259442542f5fSchristos			op = PictOpSrc;
259542542f5fSchristos		lerp = op == PictOpSrc;
259642542f5fSchristos	}
259742542f5fSchristos	if (!lerp) {
259842542f5fSchristos		switch (op) {
259942542f5fSchristos		case PictOpOver:
260042542f5fSchristos		case PictOpAdd:
260142542f5fSchristos		case PictOpOutReverse:
260242542f5fSchristos			break;
260342542f5fSchristos		case PictOpSrc:
260442542f5fSchristos			if (!sna_drawable_is_clear(dst->pDrawable))
260542542f5fSchristos				return false;
260642542f5fSchristos			break;
260742542f5fSchristos		default:
260842542f5fSchristos			return false;
260942542f5fSchristos		}
261042542f5fSchristos	}
261142542f5fSchristos
261242542f5fSchristos	if (maskFormat == NULL && ntrap > 1) {
261342542f5fSchristos		DBG(("%s: individual rasterisation requested\n",
261442542f5fSchristos		     __FUNCTION__));
261542542f5fSchristos		do {
261642542f5fSchristos			/* XXX unwind errors? */
261742542f5fSchristos			if (!trapezoid_span_inplace__x8r8g8b8(op, dst,
261842542f5fSchristos							      src, src_x, src_y,
261942542f5fSchristos							      NULL, flags,
262042542f5fSchristos							      1, traps++))
262142542f5fSchristos				return false;
262242542f5fSchristos		} while (--ntrap);
262342542f5fSchristos		return true;
262442542f5fSchristos	}
262542542f5fSchristos
262642542f5fSchristos	if (!trapezoids_bounds(ntrap, traps, &region.extents))
262742542f5fSchristos		return true;
262842542f5fSchristos
262942542f5fSchristos	DBG(("%s: extents (%d, %d), (%d, %d)\n",
263042542f5fSchristos	     __FUNCTION__,
263142542f5fSchristos	     region.extents.x1, region.extents.y1,
263242542f5fSchristos	     region.extents.x2, region.extents.y2));
263342542f5fSchristos
263442542f5fSchristos	if (!sna_compute_composite_extents(&region.extents,
263542542f5fSchristos					   src, NULL, dst,
263642542f5fSchristos					   src_x, src_y,
263742542f5fSchristos					   0, 0,
263842542f5fSchristos					   region.extents.x1, region.extents.y1,
263942542f5fSchristos					   region.extents.x2 - region.extents.x1,
264042542f5fSchristos					   region.extents.y2 - region.extents.y1))
264142542f5fSchristos		return true;
264242542f5fSchristos
264342542f5fSchristos	DBG(("%s: clipped extents (%d, %d), (%d, %d)\n",
264442542f5fSchristos	     __FUNCTION__,
264542542f5fSchristos	     region.extents.x1, region.extents.y1,
264642542f5fSchristos	     region.extents.x2, region.extents.y2));
264742542f5fSchristos
264842542f5fSchristos	region.data = NULL;
264942542f5fSchristos	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &region,
265042542f5fSchristos					    MOVE_WRITE | MOVE_READ))
265142542f5fSchristos		return true;
265242542f5fSchristos
265342542f5fSchristos	if (!is_solid && src->pDrawable) {
265442542f5fSchristos		if (!sna_drawable_move_to_cpu(src->pDrawable,
265542542f5fSchristos					      MOVE_READ))
265642542f5fSchristos			return true;
265742542f5fSchristos
265842542f5fSchristos		if (src->alphaMap &&
265942542f5fSchristos		    !sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
266042542f5fSchristos					      MOVE_READ))
266142542f5fSchristos			return true;
266242542f5fSchristos	}
266342542f5fSchristos
266442542f5fSchristos	dx = dst->pDrawable->x * SAMPLES_X;
266542542f5fSchristos	dy = dst->pDrawable->y * SAMPLES_Y;
266642542f5fSchristos
266742542f5fSchristos	num_threads = 1;
266813496ba1Ssnj	if (!NO_GPU_THREADS &&
266913496ba1Ssnj	    (flags & COMPOSITE_SPANS_RECTILINEAR) == 0 &&
267013496ba1Ssnj	    (lerp || is_solid))
267142542f5fSchristos		num_threads = sna_use_threads(4*(region.extents.x2 - region.extents.x1),
267242542f5fSchristos					      region.extents.y2 - region.extents.y1,
267342542f5fSchristos					      4);
267442542f5fSchristos
267542542f5fSchristos	DBG(("%s: %dx%d, format=%x, op=%d, lerp?=%d, num_threads=%d\n",
267642542f5fSchristos	     __FUNCTION__,
267742542f5fSchristos	     region.extents.x2 - region.extents.x1,
267842542f5fSchristos	     region.extents.y2 - region.extents.y1,
267942542f5fSchristos	     dst->format, op, lerp, num_threads));
268042542f5fSchristos
268142542f5fSchristos	if (num_threads == 1) {
268242542f5fSchristos		struct tor tor;
268342542f5fSchristos		span_func_t span;
2684fe8aea9eSmrg		struct clipped_span clipped;
268542542f5fSchristos
268642542f5fSchristos		if (!tor_init(&tor, &region.extents, 2*ntrap))
268742542f5fSchristos			return true;
268842542f5fSchristos
268942542f5fSchristos		for (n = 0; n < ntrap; n++) {
269042542f5fSchristos			if (pixman_fixed_to_int(traps[n].top) >= region.extents.y2 - dst->pDrawable->y ||
269142542f5fSchristos			    pixman_fixed_to_int(traps[n].bottom) < region.extents.y1 - dst->pDrawable->y)
269242542f5fSchristos				continue;
269342542f5fSchristos
269413496ba1Ssnj			tor_add_trapezoid(&tor, &traps[n], dx, dy);
269542542f5fSchristos		}
269642542f5fSchristos
269742542f5fSchristos		if (lerp) {
269842542f5fSchristos			struct inplace inplace;
269942542f5fSchristos			PixmapPtr pixmap;
270042542f5fSchristos			int16_t dst_x, dst_y;
270142542f5fSchristos
270242542f5fSchristos			pixmap = get_drawable_pixmap(dst->pDrawable);
270342542f5fSchristos
270442542f5fSchristos			inplace.ptr = pixmap->devPrivate.ptr;
270542542f5fSchristos			if (get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y))
270642542f5fSchristos				inplace.ptr += dst_y * pixmap->devKind + dst_x * 4;
270742542f5fSchristos			inplace.stride = pixmap->devKind;
270842542f5fSchristos			inplace.color = color;
270942542f5fSchristos
2710fe8aea9eSmrg			span = clipped_span(&clipped, tor_blt_lerp32, dst->pCompositeClip);
271142542f5fSchristos			DBG(("%s: render inplace op=%d, color=%08x\n",
271242542f5fSchristos			     __FUNCTION__, op, color));
271342542f5fSchristos
271442542f5fSchristos			if (sigtrap_get() == 0) {
2715fe8aea9eSmrg				tor_render(NULL, &tor,
2716fe8aea9eSmrg					   (void*)&inplace, (void*)&clipped,
2717fe8aea9eSmrg					   span, false);
271842542f5fSchristos				sigtrap_put();
271942542f5fSchristos			}
272042542f5fSchristos		} else if (is_solid) {
272142542f5fSchristos			struct pixman_inplace pi;
272242542f5fSchristos
272342542f5fSchristos			pi.image = image_from_pict(dst, false, &pi.dx, &pi.dy);
272442542f5fSchristos			pi.op = op;
272542542f5fSchristos			pi.color = color;
272642542f5fSchristos
272742542f5fSchristos			pi.bits = (uint32_t *)&pi.sx;
272842542f5fSchristos			pi.source = pixman_image_create_bits(PIXMAN_a8r8g8b8,
272942542f5fSchristos							     1, 1, pi.bits, 0);
273042542f5fSchristos			pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL);
273142542f5fSchristos
2732fe8aea9eSmrg			span = clipped_span(&clipped, pixmask_span_solid, dst->pCompositeClip);
273342542f5fSchristos			if (sigtrap_get() == 0) {
2734fe8aea9eSmrg				tor_render(NULL, &tor,
2735fe8aea9eSmrg					   (void*)&pi, (void*)&clipped,
2736fe8aea9eSmrg					    span, false);
273742542f5fSchristos				sigtrap_put();
273842542f5fSchristos			}
273942542f5fSchristos
274042542f5fSchristos			pixman_image_unref(pi.source);
274142542f5fSchristos			pixman_image_unref(pi.image);
274242542f5fSchristos		} else {
274342542f5fSchristos			struct pixman_inplace pi;
274442542f5fSchristos			int16_t x0, y0;
274542542f5fSchristos
274642542f5fSchristos			trapezoid_origin(&traps[0].left, &x0, &y0);
274742542f5fSchristos
274842542f5fSchristos			pi.image = image_from_pict(dst, false, &pi.dx, &pi.dy);
274942542f5fSchristos			pi.source = image_from_pict(src, false, &pi.sx, &pi.sy);
275042542f5fSchristos			pi.sx += src_x - x0;
275142542f5fSchristos			pi.sy += src_y - y0;
275242542f5fSchristos			pi.mask = pixman_image_create_bits(PIXMAN_a8, 1, 1, NULL, 0);
275342542f5fSchristos			pixman_image_set_repeat(pi.mask, PIXMAN_REPEAT_NORMAL);
275442542f5fSchristos			pi.bits = pixman_image_get_data(pi.mask);
275542542f5fSchristos			pi.op = op;
275642542f5fSchristos
2757fe8aea9eSmrg			span = clipped_span(&clipped, pixmask_span, dst->pCompositeClip);
275842542f5fSchristos			if (sigtrap_get() == 0) {
2759fe8aea9eSmrg				tor_render(NULL, &tor,
2760fe8aea9eSmrg					   (void*)&pi, (void *)&clipped,
2761fe8aea9eSmrg					   span, false);
276242542f5fSchristos				sigtrap_put();
276342542f5fSchristos			}
276442542f5fSchristos
276542542f5fSchristos			pixman_image_unref(pi.mask);
276642542f5fSchristos			pixman_image_unref(pi.source);
276742542f5fSchristos			pixman_image_unref(pi.image);
276842542f5fSchristos		}
276942542f5fSchristos
277042542f5fSchristos		tor_fini(&tor);
277142542f5fSchristos	} else {
277242542f5fSchristos		struct inplace_x8r8g8b8_thread threads[num_threads];
277342542f5fSchristos		int y, h;
277442542f5fSchristos
277542542f5fSchristos		DBG(("%s: using %d threads for inplace compositing %dx%d\n",
277642542f5fSchristos		     __FUNCTION__, num_threads,
277742542f5fSchristos		     region.extents.x2 - region.extents.x1,
277842542f5fSchristos		     region.extents.y2 - region.extents.y1));
277942542f5fSchristos
278042542f5fSchristos		threads[0].traps = traps;
278142542f5fSchristos		threads[0].ntrap = ntrap;
278242542f5fSchristos		threads[0].extents = region.extents;
278342542f5fSchristos		threads[0].lerp = lerp;
278442542f5fSchristos		threads[0].is_solid = is_solid;
278542542f5fSchristos		threads[0].color = color;
278642542f5fSchristos		threads[0].dx = dx;
278742542f5fSchristos		threads[0].dy = dy;
278842542f5fSchristos		threads[0].dst = dst;
278942542f5fSchristos		threads[0].src = src;
279042542f5fSchristos		threads[0].op = op;
279142542f5fSchristos		threads[0].src_x = src_x;
279242542f5fSchristos		threads[0].src_y = src_y;
279342542f5fSchristos
279442542f5fSchristos		y = region.extents.y1;
279542542f5fSchristos		h = region.extents.y2 - region.extents.y1;
279642542f5fSchristos		h = (h + num_threads - 1) / num_threads;
279742542f5fSchristos		num_threads -= (num_threads-1) * h >= region.extents.y2 - region.extents.y1;
279842542f5fSchristos
279942542f5fSchristos		if (sigtrap_get() == 0) {
280042542f5fSchristos			for (n = 1; n < num_threads; n++) {
280142542f5fSchristos				threads[n] = threads[0];
280242542f5fSchristos				threads[n].extents.y1 = y;
280342542f5fSchristos				threads[n].extents.y2 = y += h;
280442542f5fSchristos
280542542f5fSchristos				sna_threads_run(n, inplace_x8r8g8b8_thread, &threads[n]);
280642542f5fSchristos			}
280742542f5fSchristos
280842542f5fSchristos			assert(y < threads[0].extents.y2);
280942542f5fSchristos			threads[0].extents.y1 = y;
281042542f5fSchristos			inplace_x8r8g8b8_thread(&threads[0]);
281142542f5fSchristos
281242542f5fSchristos			sna_threads_wait();
281342542f5fSchristos			sigtrap_put();
281442542f5fSchristos		} else
281542542f5fSchristos			sna_threads_kill(); /* leaks thread allocations */
281642542f5fSchristos	}
281742542f5fSchristos
281842542f5fSchristos	return true;
281942542f5fSchristos}
282042542f5fSchristos
282142542f5fSchristosstruct inplace_thread {
282242542f5fSchristos	xTrapezoid *traps;
282342542f5fSchristos	span_func_t span;
282442542f5fSchristos	struct inplace inplace;
2825fe8aea9eSmrg	struct clipped_span clipped;
282642542f5fSchristos	BoxRec extents;
282742542f5fSchristos	int dx, dy;
282842542f5fSchristos	int draw_x, draw_y;
282942542f5fSchristos	bool unbounded;
283042542f5fSchristos	int ntrap;
283142542f5fSchristos};
283242542f5fSchristos
283342542f5fSchristosstatic void inplace_thread(void *arg)
283442542f5fSchristos{
283542542f5fSchristos	struct inplace_thread *thread = arg;
283642542f5fSchristos	struct tor tor;
283742542f5fSchristos	int n;
283842542f5fSchristos
283942542f5fSchristos	if (!tor_init(&tor, &thread->extents, 2*thread->ntrap))
284042542f5fSchristos		return;
284142542f5fSchristos
284242542f5fSchristos	for (n = 0; n < thread->ntrap; n++) {
284342542f5fSchristos		if (pixman_fixed_to_int(thread->traps[n].top) >= thread->extents.y2 - thread->draw_y ||
284442542f5fSchristos		    pixman_fixed_to_int(thread->traps[n].bottom) < thread->extents.y1 - thread->draw_y)
284542542f5fSchristos			continue;
284642542f5fSchristos
284713496ba1Ssnj		tor_add_trapezoid(&tor, &thread->traps[n], thread->dx, thread->dy);
284842542f5fSchristos	}
284942542f5fSchristos
2850fe8aea9eSmrg	tor_render(NULL, &tor,
2851fe8aea9eSmrg		   (void*)&thread->inplace, (void*)&thread->clipped,
2852fe8aea9eSmrg		   thread->span, thread->unbounded);
285342542f5fSchristos
285442542f5fSchristos	tor_fini(&tor);
285542542f5fSchristos}
285642542f5fSchristos
285742542f5fSchristosbool
285842542f5fSchristosprecise_trapezoid_span_inplace(struct sna *sna,
285942542f5fSchristos			       CARD8 op, PicturePtr src, PicturePtr dst,
286042542f5fSchristos			       PictFormatPtr maskFormat, unsigned flags,
286142542f5fSchristos			       INT16 src_x, INT16 src_y,
286242542f5fSchristos			       int ntrap, xTrapezoid *traps,
286342542f5fSchristos			       bool fallback)
286442542f5fSchristos{
286542542f5fSchristos	struct inplace inplace;
2866fe8aea9eSmrg	struct clipped_span clipped;
286742542f5fSchristos	span_func_t span;
286842542f5fSchristos	PixmapPtr pixmap;
286942542f5fSchristos	struct sna_pixmap *priv;
287042542f5fSchristos	RegionRec region;
287142542f5fSchristos	uint32_t color;
287242542f5fSchristos	bool unbounded;
287342542f5fSchristos	int16_t dst_x, dst_y;
287442542f5fSchristos	int dx, dy;
287542542f5fSchristos	int num_threads, n;
287642542f5fSchristos
287742542f5fSchristos	if (NO_PRECISE)
287842542f5fSchristos		return false;
287942542f5fSchristos
288042542f5fSchristos	if (dst->format == PICT_a8r8g8b8 || dst->format == PICT_x8r8g8b8)
288142542f5fSchristos		return trapezoid_span_inplace__x8r8g8b8(op, dst,
288242542f5fSchristos							src, src_x, src_y,
288342542f5fSchristos							maskFormat, flags,
288442542f5fSchristos							ntrap, traps);
288542542f5fSchristos
288642542f5fSchristos	if (!sna_picture_is_solid(src, &color)) {
288742542f5fSchristos		DBG(("%s: fallback -- can not perform operation in place, requires solid source\n",
288842542f5fSchristos		     __FUNCTION__));
288942542f5fSchristos		return false;
289042542f5fSchristos	}
289142542f5fSchristos
289242542f5fSchristos	if (dst->format != PICT_a8) {
289342542f5fSchristos		DBG(("%s: fallback -- can not perform operation in place, format=%x\n",
289442542f5fSchristos		     __FUNCTION__, dst->format));
289542542f5fSchristos		return false;
289642542f5fSchristos	}
289742542f5fSchristos
289842542f5fSchristos	pixmap = get_drawable_pixmap(dst->pDrawable);
289942542f5fSchristos
290042542f5fSchristos	unbounded = false;
290142542f5fSchristos	priv = sna_pixmap(pixmap);
290242542f5fSchristos	if (priv) {
290342542f5fSchristos		switch (op) {
290442542f5fSchristos		case PictOpAdd:
290542542f5fSchristos			if (priv->clear && priv->clear_color == 0) {
290642542f5fSchristos				unbounded = true;
290742542f5fSchristos				op = PictOpSrc;
290842542f5fSchristos			}
290942542f5fSchristos			if ((color >> 24) == 0)
291042542f5fSchristos				return true;
291142542f5fSchristos			break;
291242542f5fSchristos		case PictOpIn:
291342542f5fSchristos			if (priv->clear && priv->clear_color == 0)
291442542f5fSchristos				return true;
291542542f5fSchristos			if (priv->clear && priv->clear_color == 0xff)
291642542f5fSchristos				op = PictOpSrc;
291742542f5fSchristos			unbounded = true;
291842542f5fSchristos			break;
291942542f5fSchristos		case PictOpSrc:
292042542f5fSchristos			unbounded = true;
292142542f5fSchristos			break;
292242542f5fSchristos		default:
292342542f5fSchristos			DBG(("%s: fallback -- can not perform op [%d] in place\n",
292442542f5fSchristos			     __FUNCTION__, op));
292542542f5fSchristos			return false;
292642542f5fSchristos		}
292742542f5fSchristos	} else {
292842542f5fSchristos		switch (op) {
292942542f5fSchristos		case PictOpAdd:
293042542f5fSchristos			if ((color >> 24) == 0)
293142542f5fSchristos				return true;
293242542f5fSchristos			break;
293342542f5fSchristos		case PictOpIn:
293442542f5fSchristos		case PictOpSrc:
293542542f5fSchristos			unbounded = true;
293642542f5fSchristos			break;
293742542f5fSchristos		default:
293842542f5fSchristos			DBG(("%s: fallback -- can not perform op [%d] in place\n",
293942542f5fSchristos			     __FUNCTION__, op));
294042542f5fSchristos			return false;
294142542f5fSchristos		}
294242542f5fSchristos	}
294342542f5fSchristos
294442542f5fSchristos	DBG(("%s: format=%x, op=%d, color=%x\n",
294542542f5fSchristos	     __FUNCTION__, dst->format, op, color));
294642542f5fSchristos
294742542f5fSchristos	if (maskFormat == NULL && ntrap > 1) {
294842542f5fSchristos		DBG(("%s: individual rasterisation requested\n",
294942542f5fSchristos		     __FUNCTION__));
295042542f5fSchristos		do {
295142542f5fSchristos			/* XXX unwind errors? */
295242542f5fSchristos			if (!precise_trapezoid_span_inplace(sna, op, src, dst, NULL, flags,
295342542f5fSchristos							    src_x, src_y, 1, traps++,
295442542f5fSchristos							    fallback))
295542542f5fSchristos				return false;
295642542f5fSchristos		} while (--ntrap);
295742542f5fSchristos		return true;
295842542f5fSchristos	}
295942542f5fSchristos
296042542f5fSchristos	if (!trapezoids_bounds(ntrap, traps, &region.extents))
296142542f5fSchristos		return true;
296242542f5fSchristos
296342542f5fSchristos	DBG(("%s: extents (%d, %d), (%d, %d)\n",
296442542f5fSchristos	     __FUNCTION__,
296542542f5fSchristos	     region.extents.x1, region.extents.y1,
296642542f5fSchristos	     region.extents.x2, region.extents.y2));
296742542f5fSchristos
296842542f5fSchristos	if (!sna_compute_composite_extents(&region.extents,
296942542f5fSchristos					   NULL, NULL, dst,
297042542f5fSchristos					   0, 0,
297142542f5fSchristos					   0, 0,
297242542f5fSchristos					   region.extents.x1, region.extents.y1,
297342542f5fSchristos					   region.extents.x2 - region.extents.x1,
297442542f5fSchristos					   region.extents.y2 - region.extents.y1))
297542542f5fSchristos		return true;
297642542f5fSchristos
297742542f5fSchristos	DBG(("%s: clipped extents (%d, %d), (%d, %d) [complex clip? %d]\n",
297842542f5fSchristos	     __FUNCTION__,
297942542f5fSchristos	     region.extents.x1, region.extents.y1,
298042542f5fSchristos	     region.extents.x2, region.extents.y2,
298142542f5fSchristos	     dst->pCompositeClip->data != NULL));
298242542f5fSchristos
298342542f5fSchristos	if (op == PictOpSrc) {
2984fe8aea9eSmrg		span = tor_blt_src;
298542542f5fSchristos	} else if (op == PictOpIn) {
2986fe8aea9eSmrg		span = tor_blt_in;
298742542f5fSchristos	} else {
298842542f5fSchristos		assert(op == PictOpAdd);
2989fe8aea9eSmrg		span = tor_blt_add;
299042542f5fSchristos	}
299142542f5fSchristos
299242542f5fSchristos	DBG(("%s: move-to-cpu(dst)\n", __FUNCTION__));
299342542f5fSchristos	region.data = NULL;
299442542f5fSchristos	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &region,
299542542f5fSchristos					     op == PictOpSrc ? MOVE_WRITE | MOVE_INPLACE_HINT : MOVE_WRITE | MOVE_READ))
299642542f5fSchristos		return true;
299742542f5fSchristos
299842542f5fSchristos	dx = dst->pDrawable->x * SAMPLES_X;
299942542f5fSchristos	dy = dst->pDrawable->y * SAMPLES_Y;
300042542f5fSchristos
300142542f5fSchristos	inplace.ptr = pixmap->devPrivate.ptr;
300242542f5fSchristos	if (get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y))
300342542f5fSchristos		inplace.ptr += dst_y * pixmap->devKind + dst_x;
300442542f5fSchristos	inplace.stride = pixmap->devKind;
300542542f5fSchristos	inplace.opacity = color >> 24;
300642542f5fSchristos
3007fe8aea9eSmrg	span = clipped_span(&clipped, span, dst->pCompositeClip);
3008fe8aea9eSmrg
300942542f5fSchristos	num_threads = 1;
301013496ba1Ssnj	if (!NO_GPU_THREADS &&
301113496ba1Ssnj	    (flags & COMPOSITE_SPANS_RECTILINEAR) == 0)
301242542f5fSchristos		num_threads = sna_use_threads(region.extents.x2 - region.extents.x1,
301342542f5fSchristos					      region.extents.y2 - region.extents.y1,
301442542f5fSchristos					      4);
301542542f5fSchristos	if (num_threads == 1) {
301642542f5fSchristos		struct tor tor;
301742542f5fSchristos
301842542f5fSchristos		if (!tor_init(&tor, &region.extents, 2*ntrap))
301942542f5fSchristos			return true;
302042542f5fSchristos
302142542f5fSchristos		for (n = 0; n < ntrap; n++) {
302242542f5fSchristos
302342542f5fSchristos			if (pixman_fixed_to_int(traps[n].top) >= region.extents.y2 - dst->pDrawable->y ||
302442542f5fSchristos			    pixman_fixed_to_int(traps[n].bottom) < region.extents.y1 - dst->pDrawable->y)
302542542f5fSchristos				continue;
302642542f5fSchristos
302713496ba1Ssnj			tor_add_trapezoid(&tor, &traps[n], dx, dy);
302842542f5fSchristos		}
302942542f5fSchristos
303042542f5fSchristos		if (sigtrap_get() == 0) {
3031fe8aea9eSmrg			tor_render(NULL, &tor,
3032fe8aea9eSmrg				   (void*)&inplace, (void *)&clipped,
3033fe8aea9eSmrg				   span, unbounded);
303442542f5fSchristos			sigtrap_put();
303542542f5fSchristos		}
303642542f5fSchristos
303742542f5fSchristos		tor_fini(&tor);
303842542f5fSchristos	} else {
303942542f5fSchristos		struct inplace_thread threads[num_threads];
304042542f5fSchristos		int y, h;
304142542f5fSchristos
304242542f5fSchristos		DBG(("%s: using %d threads for inplace compositing %dx%d\n",
304342542f5fSchristos		     __FUNCTION__, num_threads,
304442542f5fSchristos		     region.extents.x2 - region.extents.x1,
304542542f5fSchristos		     region.extents.y2 - region.extents.y1));
304642542f5fSchristos
304742542f5fSchristos		threads[0].traps = traps;
304842542f5fSchristos		threads[0].ntrap = ntrap;
304942542f5fSchristos		threads[0].inplace = inplace;
305042542f5fSchristos		threads[0].extents = region.extents;
3051fe8aea9eSmrg		threads[0].clipped = clipped;
305242542f5fSchristos		threads[0].span = span;
305342542f5fSchristos		threads[0].unbounded = unbounded;
305442542f5fSchristos		threads[0].dx = dx;
305542542f5fSchristos		threads[0].dy = dy;
305642542f5fSchristos		threads[0].draw_x = dst->pDrawable->x;
305742542f5fSchristos		threads[0].draw_y = dst->pDrawable->y;
305842542f5fSchristos
305942542f5fSchristos		y = region.extents.y1;
306042542f5fSchristos		h = region.extents.y2 - region.extents.y1;
306142542f5fSchristos		h = (h + num_threads - 1) / num_threads;
306242542f5fSchristos		num_threads -= (num_threads-1) * h >= region.extents.y2 - region.extents.y1;
306342542f5fSchristos
306442542f5fSchristos		if (sigtrap_get() == 0) {
306542542f5fSchristos			for (n = 1; n < num_threads; n++) {
306642542f5fSchristos				threads[n] = threads[0];
306742542f5fSchristos				threads[n].extents.y1 = y;
306842542f5fSchristos				threads[n].extents.y2 = y += h;
306942542f5fSchristos
307042542f5fSchristos				sna_threads_run(n, inplace_thread, &threads[n]);
307142542f5fSchristos			}
307242542f5fSchristos
307342542f5fSchristos			assert(y < threads[0].extents.y2);
307442542f5fSchristos			threads[0].extents.y1 = y;
307542542f5fSchristos			inplace_thread(&threads[0]);
307642542f5fSchristos
307742542f5fSchristos			sna_threads_wait();
307842542f5fSchristos			sigtrap_put();
307942542f5fSchristos		} else
308042542f5fSchristos			sna_threads_kill(); /* leaks thread allocations */
308142542f5fSchristos	}
308242542f5fSchristos
308342542f5fSchristos	return true;
308442542f5fSchristos}
308542542f5fSchristos
308642542f5fSchristosbool
308742542f5fSchristosprecise_trapezoid_span_fallback(CARD8 op, PicturePtr src, PicturePtr dst,
308842542f5fSchristos				PictFormatPtr maskFormat, unsigned flags,
308942542f5fSchristos				INT16 src_x, INT16 src_y,
309042542f5fSchristos				int ntrap, xTrapezoid *traps)
309142542f5fSchristos{
309242542f5fSchristos	ScreenPtr screen = dst->pDrawable->pScreen;
309342542f5fSchristos	PixmapPtr scratch;
309442542f5fSchristos	PicturePtr mask;
309542542f5fSchristos	BoxRec extents;
309642542f5fSchristos	int16_t dst_x, dst_y;
309742542f5fSchristos	int dx, dy, num_threads;
309842542f5fSchristos	int error, n;
309942542f5fSchristos
310042542f5fSchristos	if (NO_PRECISE)
310142542f5fSchristos		return false;
310242542f5fSchristos
310342542f5fSchristos	if (maskFormat == NULL && ntrap > 1) {
310442542f5fSchristos		DBG(("%s: individual rasterisation requested\n",
310542542f5fSchristos		     __FUNCTION__));
310642542f5fSchristos		do {
310742542f5fSchristos			/* XXX unwind errors? */
310842542f5fSchristos			if (!precise_trapezoid_span_fallback(op, src, dst, NULL, flags,
310942542f5fSchristos							     src_x, src_y, 1, traps++))
311042542f5fSchristos				return false;
311142542f5fSchristos		} while (--ntrap);
311242542f5fSchristos		return true;
311342542f5fSchristos	}
311442542f5fSchristos
311542542f5fSchristos	if (!trapezoids_bounds(ntrap, traps, &extents))
311642542f5fSchristos		return true;
311742542f5fSchristos
311842542f5fSchristos	DBG(("%s: ntraps=%d, extents (%d, %d), (%d, %d)\n",
311942542f5fSchristos	     __FUNCTION__, ntrap, extents.x1, extents.y1, extents.x2, extents.y2));
312042542f5fSchristos
312142542f5fSchristos	if (!sna_compute_composite_extents(&extents,
312242542f5fSchristos					   src, NULL, dst,
312342542f5fSchristos					   src_x, src_y,
312442542f5fSchristos					   0, 0,
312542542f5fSchristos					   extents.x1, extents.y1,
312642542f5fSchristos					   extents.x2 - extents.x1,
312742542f5fSchristos					   extents.y2 - extents.y1))
312842542f5fSchristos		return true;
312942542f5fSchristos
313042542f5fSchristos	DBG(("%s: extents (%d, %d), (%d, %d)\n",
313142542f5fSchristos	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
313242542f5fSchristos
313342542f5fSchristos	extents.y2 -= extents.y1;
313442542f5fSchristos	extents.x2 -= extents.x1;
313542542f5fSchristos	extents.x1 -= dst->pDrawable->x;
313642542f5fSchristos	extents.y1 -= dst->pDrawable->y;
313742542f5fSchristos	dst_x = extents.x1;
313842542f5fSchristos	dst_y = extents.y1;
313942542f5fSchristos	dx = -extents.x1 * SAMPLES_X;
314042542f5fSchristos	dy = -extents.y1 * SAMPLES_Y;
314142542f5fSchristos	extents.x1 = extents.y1 = 0;
314242542f5fSchristos
314342542f5fSchristos	DBG(("%s: mask (%dx%d), dx=(%d, %d)\n",
314442542f5fSchristos	     __FUNCTION__, extents.x2, extents.y2, dx, dy));
314542542f5fSchristos	scratch = sna_pixmap_create_unattached(screen,
314642542f5fSchristos					       extents.x2, extents.y2, 8);
314742542f5fSchristos	if (!scratch)
314842542f5fSchristos		return true;
314942542f5fSchristos
315042542f5fSchristos	DBG(("%s: created buffer %p, stride %d\n",
315142542f5fSchristos	     __FUNCTION__, scratch->devPrivate.ptr, scratch->devKind));
315242542f5fSchristos
315342542f5fSchristos	num_threads = 1;
315413496ba1Ssnj	if (!NO_GPU_THREADS &&
315513496ba1Ssnj	    (flags & COMPOSITE_SPANS_RECTILINEAR) == 0)
315642542f5fSchristos		num_threads = sna_use_threads(extents.x2 - extents.x1,
315742542f5fSchristos					      extents.y2 - extents.y1,
315842542f5fSchristos					      4);
315942542f5fSchristos	if (num_threads == 1) {
316042542f5fSchristos		struct tor tor;
316142542f5fSchristos
316242542f5fSchristos		if (!tor_init(&tor, &extents, 2*ntrap)) {
316342542f5fSchristos			sna_pixmap_destroy(scratch);
316442542f5fSchristos			return true;
316542542f5fSchristos		}
316642542f5fSchristos
316742542f5fSchristos		for (n = 0; n < ntrap; n++) {
316842542f5fSchristos			if (pixman_fixed_to_int(traps[n].top) - dst_y >= extents.y2 ||
316942542f5fSchristos			    pixman_fixed_to_int(traps[n].bottom) - dst_y < 0)
317042542f5fSchristos				continue;
317142542f5fSchristos
317213496ba1Ssnj			tor_add_trapezoid(&tor, &traps[n], dx, dy);
317342542f5fSchristos		}
317442542f5fSchristos
317542542f5fSchristos		if (extents.x2 <= TOR_INPLACE_SIZE) {
317642542f5fSchristos			tor_inplace(&tor, scratch);
317742542f5fSchristos		} else {
317842542f5fSchristos			tor_render(NULL, &tor,
317942542f5fSchristos				   scratch->devPrivate.ptr,
318042542f5fSchristos				   (void *)(intptr_t)scratch->devKind,
318142542f5fSchristos				   tor_blt_mask,
318242542f5fSchristos				   true);
318342542f5fSchristos		}
318442542f5fSchristos		tor_fini(&tor);
318542542f5fSchristos	} else {
318642542f5fSchristos		struct mask_thread threads[num_threads];
318742542f5fSchristos		int y, h;
318842542f5fSchristos
318942542f5fSchristos		DBG(("%s: using %d threads for mask compositing %dx%d\n",
319042542f5fSchristos		     __FUNCTION__, num_threads,
319142542f5fSchristos		     extents.x2 - extents.x1,
319242542f5fSchristos		     extents.y2 - extents.y1));
319342542f5fSchristos
319442542f5fSchristos		threads[0].scratch = scratch;
319542542f5fSchristos		threads[0].traps = traps;
319642542f5fSchristos		threads[0].ntrap = ntrap;
319742542f5fSchristos		threads[0].extents = extents;
319842542f5fSchristos		threads[0].dx = dx;
319942542f5fSchristos		threads[0].dy = dy;
320042542f5fSchristos		threads[0].dst_y = dst_y;
320142542f5fSchristos
320242542f5fSchristos		y = extents.y1;
320342542f5fSchristos		h = extents.y2 - extents.y1;
320442542f5fSchristos		h = (h + num_threads - 1) / num_threads;
320542542f5fSchristos		num_threads -= (num_threads-1) * h >= extents.y2 - extents.y1;
320642542f5fSchristos
320742542f5fSchristos		for (n = 1; n < num_threads; n++) {
320842542f5fSchristos			threads[n] = threads[0];
320942542f5fSchristos			threads[n].extents.y1 = y;
321042542f5fSchristos			threads[n].extents.y2 = y += h;
321142542f5fSchristos
321242542f5fSchristos			sna_threads_run(n, mask_thread, &threads[n]);
321342542f5fSchristos		}
321442542f5fSchristos
321542542f5fSchristos		assert(y < threads[0].extents.y2);
321642542f5fSchristos		threads[0].extents.y1 = y;
321742542f5fSchristos		mask_thread(&threads[0]);
321842542f5fSchristos
321942542f5fSchristos		sna_threads_wait();
322042542f5fSchristos	}
322142542f5fSchristos
322242542f5fSchristos	mask = CreatePicture(0, &scratch->drawable,
322342542f5fSchristos			     PictureMatchFormat(screen, 8, PICT_a8),
322442542f5fSchristos			     0, 0, serverClient, &error);
322542542f5fSchristos	if (mask) {
322642542f5fSchristos		RegionRec region;
322742542f5fSchristos		int16_t x0, y0;
322842542f5fSchristos
322942542f5fSchristos		region.extents.x1 = dst_x + dst->pDrawable->x;
323042542f5fSchristos		region.extents.y1 = dst_y + dst->pDrawable->y;
323142542f5fSchristos		region.extents.x2 = region.extents.x1 + extents.x2;
323242542f5fSchristos		region.extents.y2 = region.extents.y1 + extents.y2;
323342542f5fSchristos		region.data = NULL;
323442542f5fSchristos
323542542f5fSchristos		trapezoid_origin(&traps[0].left, &x0, &y0);
323642542f5fSchristos
323742542f5fSchristos		DBG(("%s: fbComposite()\n", __FUNCTION__));
323842542f5fSchristos		sna_composite_fb(op, src, mask, dst, &region,
323942542f5fSchristos				 src_x + dst_x - x0, src_y + dst_y - y0,
324042542f5fSchristos				 0, 0,
324142542f5fSchristos				 dst_x, dst_y,
324242542f5fSchristos				 extents.x2, extents.y2);
324342542f5fSchristos
324442542f5fSchristos		FreePicture(mask, 0);
324542542f5fSchristos	}
324642542f5fSchristos	sna_pixmap_destroy(scratch);
324742542f5fSchristos
324842542f5fSchristos	return true;
324942542f5fSchristos}
325013496ba1Ssnj
325113496ba1Ssnjstruct tristrip_thread {
325213496ba1Ssnj	struct sna *sna;
325313496ba1Ssnj	const struct sna_composite_spans_op *op;
325413496ba1Ssnj	const xPointFixed *points;
325513496ba1Ssnj	RegionPtr clip;
325613496ba1Ssnj	span_func_t span;
325713496ba1Ssnj	BoxRec extents;
325813496ba1Ssnj	int dx, dy, draw_y;
325913496ba1Ssnj	int count;
326013496ba1Ssnj	bool unbounded;
326113496ba1Ssnj};
326213496ba1Ssnj
326313496ba1Ssnjstatic void
326413496ba1Ssnjtristrip_thread(void *arg)
326513496ba1Ssnj{
326613496ba1Ssnj	struct tristrip_thread *thread = arg;
326713496ba1Ssnj	struct span_thread_boxes boxes;
326813496ba1Ssnj	struct tor tor;
326913496ba1Ssnj	int n, cw, ccw;
327013496ba1Ssnj
327113496ba1Ssnj	if (!tor_init(&tor, &thread->extents, 2*thread->count))
327213496ba1Ssnj		return;
327313496ba1Ssnj
3274fe8aea9eSmrg	span_thread_boxes_init(&boxes, thread->op, thread->clip);
327513496ba1Ssnj
327613496ba1Ssnj	cw = 0; ccw = 1;
327713496ba1Ssnj	polygon_add_line(tor.polygon,
327813496ba1Ssnj			 &thread->points[ccw], &thread->points[cw],
327913496ba1Ssnj			 thread->dx, thread->dy);
328013496ba1Ssnj	n = 2;
328113496ba1Ssnj	do {
328213496ba1Ssnj		polygon_add_line(tor.polygon,
328313496ba1Ssnj				 &thread->points[cw], &thread->points[n],
328413496ba1Ssnj				 thread->dx, thread->dy);
328513496ba1Ssnj		cw = n;
328613496ba1Ssnj		if (++n == thread->count)
328713496ba1Ssnj			break;
328813496ba1Ssnj
328913496ba1Ssnj		polygon_add_line(tor.polygon,
329013496ba1Ssnj				 &thread->points[n], &thread->points[ccw],
329113496ba1Ssnj				 thread->dx, thread->dy);
329213496ba1Ssnj		ccw = n;
329313496ba1Ssnj		if (++n == thread->count)
329413496ba1Ssnj			break;
329513496ba1Ssnj	} while (1);
329613496ba1Ssnj	polygon_add_line(tor.polygon,
329713496ba1Ssnj			 &thread->points[cw], &thread->points[ccw],
329813496ba1Ssnj			 thread->dx, thread->dy);
329913496ba1Ssnj	assert(tor.polygon->num_edges <= 2*thread->count);
330013496ba1Ssnj
330113496ba1Ssnj	tor_render(thread->sna, &tor,
330213496ba1Ssnj		   (struct sna_composite_spans_op *)&boxes, thread->clip,
330313496ba1Ssnj		   thread->span, thread->unbounded);
330413496ba1Ssnj
330513496ba1Ssnj	tor_fini(&tor);
330613496ba1Ssnj
330713496ba1Ssnj	if (boxes.num_boxes) {
330813496ba1Ssnj		DBG(("%s: flushing %d boxes\n", __FUNCTION__, boxes.num_boxes));
330913496ba1Ssnj		assert(boxes.num_boxes <= SPAN_THREAD_MAX_BOXES);
331013496ba1Ssnj		thread->op->thread_boxes(thread->sna, thread->op,
331113496ba1Ssnj					 boxes.boxes, boxes.num_boxes);
331213496ba1Ssnj	}
331313496ba1Ssnj}
331413496ba1Ssnj
331513496ba1Ssnjbool
331613496ba1Ssnjprecise_tristrip_span_converter(struct sna *sna,
331713496ba1Ssnj				CARD8 op, PicturePtr src, PicturePtr dst,
331813496ba1Ssnj				PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
331913496ba1Ssnj				int count, xPointFixed *points)
332013496ba1Ssnj{
332113496ba1Ssnj	struct sna_composite_spans_op tmp;
332213496ba1Ssnj	BoxRec extents;
332313496ba1Ssnj	pixman_region16_t clip;
332413496ba1Ssnj	int16_t dst_x, dst_y;
332513496ba1Ssnj	int dx, dy, num_threads;
332613496ba1Ssnj	bool was_clear;
332713496ba1Ssnj
332813496ba1Ssnj	if (!sna->render.check_composite_spans(sna, op, src, dst, 0, 0, 0)) {
332913496ba1Ssnj		DBG(("%s: fallback -- composite spans not supported\n",
333013496ba1Ssnj		     __FUNCTION__));
333113496ba1Ssnj		return false;
333213496ba1Ssnj	}
333313496ba1Ssnj
333413496ba1Ssnj	dst_x = pixman_fixed_to_int(points[0].x);
333513496ba1Ssnj	dst_y = pixman_fixed_to_int(points[0].y);
333613496ba1Ssnj
333713496ba1Ssnj	miPointFixedBounds(count, points, &extents);
333813496ba1Ssnj	DBG(("%s: extents (%d, %d), (%d, %d)\n",
333913496ba1Ssnj	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
334013496ba1Ssnj
334113496ba1Ssnj	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
334213496ba1Ssnj		return true;
334313496ba1Ssnj
334413496ba1Ssnj#if 0
334513496ba1Ssnj	if (extents.y2 - extents.y1 < 64 && extents.x2 - extents.x1 < 64) {
334613496ba1Ssnj		DBG(("%s: fallback -- traps extents too small %dx%d\n",
334713496ba1Ssnj		     __FUNCTION__, extents.y2 - extents.y1, extents.x2 - extents.x1));
334813496ba1Ssnj		return false;
334913496ba1Ssnj	}
335013496ba1Ssnj#endif
335113496ba1Ssnj
335213496ba1Ssnj	if (!sna_compute_composite_region(&clip,
335313496ba1Ssnj					  src, NULL, dst,
335413496ba1Ssnj					  src_x + extents.x1 - dst_x,
335513496ba1Ssnj					  src_y + extents.y1 - dst_y,
335613496ba1Ssnj					  0, 0,
335713496ba1Ssnj					  extents.x1, extents.y1,
335813496ba1Ssnj					  extents.x2 - extents.x1,
335913496ba1Ssnj					  extents.y2 - extents.y1)) {
336013496ba1Ssnj		DBG(("%s: triangles do not intersect drawable clips\n",
336113496ba1Ssnj		     __FUNCTION__)) ;
336213496ba1Ssnj		return true;
336313496ba1Ssnj	}
336413496ba1Ssnj
336513496ba1Ssnj	if (!sna->render.check_composite_spans(sna, op, src, dst,
336613496ba1Ssnj					       clip.extents.x2 - clip.extents.x1,
336713496ba1Ssnj					       clip.extents.y2 - clip.extents.y1,
336813496ba1Ssnj					       0)) {
336913496ba1Ssnj		DBG(("%s: fallback -- composite spans not supported\n",
337013496ba1Ssnj		     __FUNCTION__));
337113496ba1Ssnj		return false;
337213496ba1Ssnj	}
337313496ba1Ssnj
337413496ba1Ssnj	extents = *RegionExtents(&clip);
337513496ba1Ssnj	dx = dst->pDrawable->x;
337613496ba1Ssnj	dy = dst->pDrawable->y;
337713496ba1Ssnj
337813496ba1Ssnj	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
337913496ba1Ssnj	     __FUNCTION__,
338013496ba1Ssnj	     extents.x1, extents.y1,
338113496ba1Ssnj	     extents.x2, extents.y2,
338213496ba1Ssnj	     dx, dy,
338313496ba1Ssnj	     src_x + extents.x1 - dst_x - dx,
338413496ba1Ssnj	     src_y + extents.y1 - dst_y - dy));
338513496ba1Ssnj
338613496ba1Ssnj	was_clear = sna_drawable_is_clear(dst->pDrawable);
338713496ba1Ssnj
338813496ba1Ssnj	memset(&tmp, 0, sizeof(tmp));
338913496ba1Ssnj	if (!sna->render.composite_spans(sna, op, src, dst,
339013496ba1Ssnj					 src_x + extents.x1 - dst_x - dx,
339113496ba1Ssnj					 src_y + extents.y1 - dst_y - dy,
339213496ba1Ssnj					 extents.x1,  extents.y1,
339313496ba1Ssnj					 extents.x2 - extents.x1,
339413496ba1Ssnj					 extents.y2 - extents.y1,
339513496ba1Ssnj					 0,
339613496ba1Ssnj					 &tmp)) {
339713496ba1Ssnj		DBG(("%s: fallback -- composite spans render op not supported\n",
339813496ba1Ssnj		     __FUNCTION__));
339913496ba1Ssnj		return false;
340013496ba1Ssnj	}
340113496ba1Ssnj
340213496ba1Ssnj	dx *= SAMPLES_X;
340313496ba1Ssnj	dy *= SAMPLES_Y;
340413496ba1Ssnj
340513496ba1Ssnj	num_threads = 1;
340613496ba1Ssnj	if (!NO_GPU_THREADS &&
340713496ba1Ssnj	    tmp.thread_boxes &&
340813496ba1Ssnj	    thread_choose_span(&tmp, dst, maskFormat, &clip))
340913496ba1Ssnj		num_threads = sna_use_threads(extents.x2 - extents.x1,
341013496ba1Ssnj					      extents.y2 - extents.y1,
341113496ba1Ssnj					      16);
341213496ba1Ssnj	if (num_threads == 1) {
341313496ba1Ssnj		struct tor tor;
341413496ba1Ssnj		int cw, ccw, n;
341513496ba1Ssnj
341613496ba1Ssnj		if (!tor_init(&tor, &extents, 2*count))
341713496ba1Ssnj			goto skip;
341813496ba1Ssnj
341913496ba1Ssnj		cw = 0; ccw = 1;
342013496ba1Ssnj		polygon_add_line(tor.polygon,
342113496ba1Ssnj				 &points[ccw], &points[cw],
342213496ba1Ssnj				 dx, dy);
342313496ba1Ssnj		n = 2;
342413496ba1Ssnj		do {
342513496ba1Ssnj			polygon_add_line(tor.polygon,
342613496ba1Ssnj					 &points[cw], &points[n],
342713496ba1Ssnj					 dx, dy);
342813496ba1Ssnj			cw = n;
342913496ba1Ssnj			if (++n == count)
343013496ba1Ssnj				break;
343113496ba1Ssnj
343213496ba1Ssnj			polygon_add_line(tor.polygon,
343313496ba1Ssnj					 &points[n], &points[ccw],
343413496ba1Ssnj					 dx, dy);
343513496ba1Ssnj			ccw = n;
343613496ba1Ssnj			if (++n == count)
343713496ba1Ssnj				break;
343813496ba1Ssnj		} while (1);
343913496ba1Ssnj		polygon_add_line(tor.polygon,
344013496ba1Ssnj				 &points[cw], &points[ccw],
344113496ba1Ssnj				 dx, dy);
344213496ba1Ssnj		assert(tor.polygon->num_edges <= 2*count);
344313496ba1Ssnj
344413496ba1Ssnj		tor_render(sna, &tor, &tmp, &clip,
344513496ba1Ssnj			   choose_span(&tmp, dst, maskFormat, &clip),
344613496ba1Ssnj			   !was_clear && maskFormat && !operator_is_bounded(op));
344713496ba1Ssnj
344813496ba1Ssnj		tor_fini(&tor);
344913496ba1Ssnj	} else {
345013496ba1Ssnj		struct tristrip_thread threads[num_threads];
345113496ba1Ssnj		int y, h, n;
345213496ba1Ssnj
345313496ba1Ssnj		DBG(("%s: using %d threads for tristrip compositing %dx%d\n",
345413496ba1Ssnj		     __FUNCTION__, num_threads,
345513496ba1Ssnj		     clip.extents.x2 - clip.extents.x1,
345613496ba1Ssnj		     clip.extents.y2 - clip.extents.y1));
345713496ba1Ssnj
345813496ba1Ssnj		threads[0].sna = sna;
345913496ba1Ssnj		threads[0].op = &tmp;
346013496ba1Ssnj		threads[0].points = points;
346113496ba1Ssnj		threads[0].count = count;
346213496ba1Ssnj		threads[0].extents = clip.extents;
346313496ba1Ssnj		threads[0].clip = &clip;
346413496ba1Ssnj		threads[0].dx = dx;
346513496ba1Ssnj		threads[0].dy = dy;
346613496ba1Ssnj		threads[0].draw_y = dst->pDrawable->y;
346713496ba1Ssnj		threads[0].unbounded = !was_clear && maskFormat && !operator_is_bounded(op);
346813496ba1Ssnj		threads[0].span = thread_choose_span(&tmp, dst, maskFormat, &clip);
346913496ba1Ssnj
347013496ba1Ssnj		y = clip.extents.y1;
347113496ba1Ssnj		h = clip.extents.y2 - clip.extents.y1;
347213496ba1Ssnj		h = (h + num_threads - 1) / num_threads;
347313496ba1Ssnj		num_threads -= (num_threads-1) * h >= clip.extents.y2 - clip.extents.y1;
347413496ba1Ssnj
347513496ba1Ssnj		for (n = 1; n < num_threads; n++) {
347613496ba1Ssnj			threads[n] = threads[0];
347713496ba1Ssnj			threads[n].extents.y1 = y;
347813496ba1Ssnj			threads[n].extents.y2 = y += h;
347913496ba1Ssnj
348013496ba1Ssnj			sna_threads_run(n, tristrip_thread, &threads[n]);
348113496ba1Ssnj		}
348213496ba1Ssnj
348313496ba1Ssnj		assert(y < threads[0].extents.y2);
348413496ba1Ssnj		threads[0].extents.y1 = y;
348513496ba1Ssnj		tristrip_thread(&threads[0]);
348613496ba1Ssnj
348713496ba1Ssnj		sna_threads_wait();
348813496ba1Ssnj	}
348913496ba1Ssnjskip:
349013496ba1Ssnj	tmp.done(sna, &tmp);
349113496ba1Ssnj
349213496ba1Ssnj	REGION_UNINIT(NULL, &clip);
349313496ba1Ssnj	return true;
349413496ba1Ssnj}
349513496ba1Ssnj
349613496ba1Ssnjbool
349713496ba1Ssnjprecise_trap_span_converter(struct sna *sna,
349813496ba1Ssnj			    PicturePtr dst,
349913496ba1Ssnj			    INT16 src_x, INT16 src_y,
350013496ba1Ssnj			    int ntrap, xTrap *trap)
350113496ba1Ssnj{
350213496ba1Ssnj	struct sna_composite_spans_op tmp;
350313496ba1Ssnj	struct tor tor;
350413496ba1Ssnj	BoxRec extents;
350513496ba1Ssnj	pixman_region16_t *clip;
350613496ba1Ssnj	int dx, dy, n;
350713496ba1Ssnj
350813496ba1Ssnj	if (dst->pDrawable->depth < 8)
350913496ba1Ssnj		return false;
351013496ba1Ssnj
351113496ba1Ssnj	if (!sna->render.check_composite_spans(sna, PictOpAdd, sna->render.white_picture, dst,
351213496ba1Ssnj					       dst->pCompositeClip->extents.x2 - dst->pCompositeClip->extents.x1,
351313496ba1Ssnj					       dst->pCompositeClip->extents.y2 - dst->pCompositeClip->extents.y1,
351413496ba1Ssnj					       0)) {
351513496ba1Ssnj		DBG(("%s: fallback -- composite spans not supported\n",
351613496ba1Ssnj		     __FUNCTION__));
351713496ba1Ssnj		return false;
351813496ba1Ssnj	}
351913496ba1Ssnj
352013496ba1Ssnj	clip = dst->pCompositeClip;
352113496ba1Ssnj	extents = *RegionExtents(clip);
352213496ba1Ssnj	dx = dst->pDrawable->x;
352313496ba1Ssnj	dy = dst->pDrawable->y;
352413496ba1Ssnj
352513496ba1Ssnj	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d)\n",
352613496ba1Ssnj	     __FUNCTION__,
352713496ba1Ssnj	     extents.x1, extents.y1,
352813496ba1Ssnj	     extents.x2, extents.y2,
352913496ba1Ssnj	     dx, dy));
353013496ba1Ssnj
353113496ba1Ssnj	memset(&tmp, 0, sizeof(tmp));
353213496ba1Ssnj	if (!sna->render.composite_spans(sna, PictOpAdd, sna->render.white_picture, dst,
353313496ba1Ssnj					 0, 0,
353413496ba1Ssnj					 extents.x1,  extents.y1,
353513496ba1Ssnj					 extents.x2 - extents.x1,
353613496ba1Ssnj					 extents.y2 - extents.y1,
353713496ba1Ssnj					 0,
353813496ba1Ssnj					 &tmp)) {
353913496ba1Ssnj		DBG(("%s: fallback -- composite spans render op not supported\n",
354013496ba1Ssnj		     __FUNCTION__));
354113496ba1Ssnj		return false;
354213496ba1Ssnj	}
354313496ba1Ssnj
354413496ba1Ssnj	dx *= SAMPLES_X;
354513496ba1Ssnj	dy *= SAMPLES_Y;
354613496ba1Ssnj	if (!tor_init(&tor, &extents, 2*ntrap))
354713496ba1Ssnj		goto skip;
354813496ba1Ssnj
354913496ba1Ssnj	for (n = 0; n < ntrap; n++) {
355013496ba1Ssnj		xPointFixed p1, p2;
355113496ba1Ssnj
355213496ba1Ssnj		if (pixman_fixed_to_int(trap[n].top.y) + dst->pDrawable->y >= extents.y2 ||
355313496ba1Ssnj		    pixman_fixed_to_int(trap[n].bot.y) + dst->pDrawable->y < extents.y1)
355413496ba1Ssnj			continue;
355513496ba1Ssnj
355613496ba1Ssnj		p1.y = trap[n].top.y;
355713496ba1Ssnj		p2.y = trap[n].bot.y;
355813496ba1Ssnj		p1.x = trap[n].top.l;
355913496ba1Ssnj		p2.x = trap[n].bot.l;
356013496ba1Ssnj		polygon_add_line(tor.polygon, &p1, &p2, dx, dy);
356113496ba1Ssnj
356213496ba1Ssnj		p1.y = trap[n].bot.y;
356313496ba1Ssnj		p2.y = trap[n].top.y;
356413496ba1Ssnj		p1.x = trap[n].top.r;
356513496ba1Ssnj		p2.x = trap[n].bot.r;
356613496ba1Ssnj		polygon_add_line(tor.polygon, &p1, &p2, dx, dy);
356713496ba1Ssnj	}
356813496ba1Ssnj
356913496ba1Ssnj	tor_render(sna, &tor, &tmp, clip,
357013496ba1Ssnj		   choose_span(&tmp, dst, NULL, clip), false);
357113496ba1Ssnj
357213496ba1Ssnj	tor_fini(&tor);
357313496ba1Ssnjskip:
357413496ba1Ssnj	tmp.done(sna, &tmp);
357513496ba1Ssnj	return true;
357613496ba1Ssnj}
3577