sna_trapezoids.c revision 03b705cf
103b705cfSriastradh/*
203b705cfSriastradh * Copyright (c) 2007  David Turner
303b705cfSriastradh * Copyright (c) 2008  M Joonas Pihlaja
403b705cfSriastradh * Copyright (c) 2011 Intel Corporation
503b705cfSriastradh *
603b705cfSriastradh * Permission is hereby granted, free of charge, to any person obtaining a
703b705cfSriastradh * copy of this software and associated documentation files (the "Software"),
803b705cfSriastradh * to deal in the Software without restriction, including without limitation
903b705cfSriastradh * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1003b705cfSriastradh * and/or sell copies of the Software, and to permit persons to whom the
1103b705cfSriastradh * Software is furnished to do so, subject to the following conditions:
1203b705cfSriastradh *
1303b705cfSriastradh * The above copyright notice and this permission notice (including the next
1403b705cfSriastradh * paragraph) shall be included in all copies or substantial portions of the
1503b705cfSriastradh * Software.
1603b705cfSriastradh *
1703b705cfSriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1803b705cfSriastradh * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1903b705cfSriastradh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2003b705cfSriastradh * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2103b705cfSriastradh * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2203b705cfSriastradh * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2303b705cfSriastradh * SOFTWARE.
2403b705cfSriastradh *
2503b705cfSriastradh * Authors:
2603b705cfSriastradh *    Chris Wilson <chris@chris-wilson.co.uk>
2703b705cfSriastradh *
2803b705cfSriastradh */
2903b705cfSriastradh
3003b705cfSriastradh#ifdef HAVE_CONFIG_H
3103b705cfSriastradh#include "config.h"
3203b705cfSriastradh#endif
3303b705cfSriastradh
3403b705cfSriastradh#include "sna.h"
3503b705cfSriastradh#include "sna_render.h"
3603b705cfSriastradh#include "sna_render_inline.h"
3703b705cfSriastradh#include "fb/fbpict.h"
3803b705cfSriastradh
3903b705cfSriastradh#include <mipict.h>
4003b705cfSriastradh
4103b705cfSriastradh#if 0
4203b705cfSriastradh#define __DBG(x) ErrorF x
4303b705cfSriastradh#else
4403b705cfSriastradh#define __DBG(x)
4503b705cfSriastradh#endif
4603b705cfSriastradh
4703b705cfSriastradh#define NO_ACCEL 0
4803b705cfSriastradh#define FORCE_FALLBACK 0
4903b705cfSriastradh#define NO_ALIGNED_BOXES 0
5003b705cfSriastradh#define NO_UNALIGNED_BOXES 0
5103b705cfSriastradh#define NO_SCAN_CONVERTER 0
5203b705cfSriastradh#define NO_GPU_THREADS 0
5303b705cfSriastradh
5403b705cfSriastradh/* TODO: Emit unantialiased and MSAA triangles. */
5503b705cfSriastradh
5603b705cfSriastradh#ifndef MAX
5703b705cfSriastradh#define MAX(x,y) ((x) >= (y) ? (x) : (y))
5803b705cfSriastradh#endif
5903b705cfSriastradh
6003b705cfSriastradh#ifndef MIN
6103b705cfSriastradh#define MIN(x,y) ((x) <= (y) ? (x) : (y))
6203b705cfSriastradh#endif
6303b705cfSriastradh
6403b705cfSriastradh#define SAMPLES_X 17
6503b705cfSriastradh#define SAMPLES_Y 15
6603b705cfSriastradh
6703b705cfSriastradh#define FAST_SAMPLES_shift 2
6803b705cfSriastradh#define FAST_SAMPLES_X (1<<FAST_SAMPLES_shift)
6903b705cfSriastradh#define FAST_SAMPLES_Y (1<<FAST_SAMPLES_shift)
7003b705cfSriastradh#define FAST_SAMPLES_mask ((1<<FAST_SAMPLES_shift)-1)
7103b705cfSriastradh
7203b705cfSriastradh#define region_count(r) ((r)->data ? (r)->data->numRects : 1)
7303b705cfSriastradh#define region_boxes(r) ((r)->data ? (BoxPtr)((r)->data + 1) : &(r)->extents)
7403b705cfSriastradh
7503b705cfSriastradhtypedef void (*span_func_t)(struct sna *sna,
7603b705cfSriastradh			    struct sna_composite_spans_op *op,
7703b705cfSriastradh			    pixman_region16_t *clip,
7803b705cfSriastradh			    const BoxRec *box,
7903b705cfSriastradh			    int coverage);
8003b705cfSriastradh
8103b705cfSriastradh#if HAS_DEBUG_FULL
8203b705cfSriastradhstatic void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function)
8303b705cfSriastradh{
8403b705cfSriastradh	if (box->x1 < 0 || box->y1 < 0 ||
8503b705cfSriastradh	    box->x2 > pixmap->drawable.width ||
8603b705cfSriastradh	    box->y2 > pixmap->drawable.height)
8703b705cfSriastradh	{
8803b705cfSriastradh		ErrorF("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n",
8903b705cfSriastradh		       __FUNCTION__,
9003b705cfSriastradh		       box->x1, box->y1, box->x2, box->y2,
9103b705cfSriastradh		       pixmap->drawable.width,
9203b705cfSriastradh		       pixmap->drawable.height);
9303b705cfSriastradh		assert(0);
9403b705cfSriastradh	}
9503b705cfSriastradh}
9603b705cfSriastradh#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__)
9703b705cfSriastradh#else
9803b705cfSriastradh#define assert_pixmap_contains_box(p, b)
9903b705cfSriastradh#endif
10003b705cfSriastradh
10103b705cfSriastradhstatic void apply_damage(struct sna_composite_op *op, RegionPtr region)
10203b705cfSriastradh{
10303b705cfSriastradh	DBG(("%s: damage=%p, region=%ldx[(%d, %d), (%d, %d)]\n",
10403b705cfSriastradh	     __FUNCTION__, op->damage,
10503b705cfSriastradh	     (long)REGION_NUM_RECTS(region),
10603b705cfSriastradh	     region->extents.x1, region->extents.y1,
10703b705cfSriastradh	     region->extents.x2, region->extents.y2));
10803b705cfSriastradh
10903b705cfSriastradh	if (op->damage == NULL)
11003b705cfSriastradh		return;
11103b705cfSriastradh
11203b705cfSriastradh	RegionTranslate(region, op->dst.x, op->dst.y);
11303b705cfSriastradh
11403b705cfSriastradh	assert_pixmap_contains_box(op->dst.pixmap, RegionExtents(region));
11503b705cfSriastradh	sna_damage_add(op->damage, region);
11603b705cfSriastradh}
11703b705cfSriastradh
11803b705cfSriastradhstatic void _apply_damage_box(struct sna_composite_op *op, const BoxRec *box)
11903b705cfSriastradh{
12003b705cfSriastradh	BoxRec r;
12103b705cfSriastradh
12203b705cfSriastradh	r.x1 = box->x1 + op->dst.x;
12303b705cfSriastradh	r.x2 = box->x2 + op->dst.x;
12403b705cfSriastradh	r.y1 = box->y1 + op->dst.y;
12503b705cfSriastradh	r.y2 = box->y2 + op->dst.y;
12603b705cfSriastradh
12703b705cfSriastradh	assert_pixmap_contains_box(op->dst.pixmap, &r);
12803b705cfSriastradh	sna_damage_add_box(op->damage, &r);
12903b705cfSriastradh}
13003b705cfSriastradh
13103b705cfSriastradhinline static void apply_damage_box(struct sna_composite_op *op, const BoxRec *box)
13203b705cfSriastradh{
13303b705cfSriastradh	if (op->damage)
13403b705cfSriastradh		_apply_damage_box(op, box);
13503b705cfSriastradh}
13603b705cfSriastradh
13703b705cfSriastradhtypedef int grid_scaled_x_t;
13803b705cfSriastradhtypedef int grid_scaled_y_t;
13903b705cfSriastradh
14003b705cfSriastradh#define FAST_SAMPLES_X_TO_INT_FRAC(x, i, f) \
14103b705cfSriastradh	_GRID_TO_INT_FRAC_shift(x, i, f, FAST_SAMPLES_shift)
14203b705cfSriastradh
14303b705cfSriastradh#define FAST_SAMPLES_INT(x) ((x) >> (FAST_SAMPLES_shift))
14403b705cfSriastradh#define FAST_SAMPLES_FRAC(x) ((x) & (FAST_SAMPLES_mask))
14503b705cfSriastradh
14603b705cfSriastradh#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do {	\
14703b705cfSriastradh    (f) = FAST_SAMPLES_FRAC(t);				\
14803b705cfSriastradh    (i) = FAST_SAMPLES_INT(t);				\
14903b705cfSriastradh} while (0)
15003b705cfSriastradh
15103b705cfSriastradh/* A grid area is a real in [0,1] scaled by 2*SAMPLES_X*SAMPLES_Y.  We want
15203b705cfSriastradh * to be able to represent exactly areas of subpixel trapezoids whose
15303b705cfSriastradh * vertices are given in grid scaled coordinates.  The scale factor
15403b705cfSriastradh * comes from needing to accurately represent the area 0.5*dx*dy of a
15503b705cfSriastradh * triangle with base dx and height dy in grid scaled numbers. */
15603b705cfSriastradhtypedef int grid_area_t;
15703b705cfSriastradh#define FAST_SAMPLES_XY (2*FAST_SAMPLES_X*FAST_SAMPLES_Y) /* Unit area on the grid. */
15803b705cfSriastradh
15903b705cfSriastradh#define AREA_TO_ALPHA(c)  ((c) / (float)FAST_SAMPLES_XY)
16003b705cfSriastradh
16103b705cfSriastradhstruct quorem {
16203b705cfSriastradh	int32_t quo;
16303b705cfSriastradh	int32_t rem;
16403b705cfSriastradh};
16503b705cfSriastradh
16603b705cfSriastradhstruct edge {
16703b705cfSriastradh	struct edge *next, *prev;
16803b705cfSriastradh
16903b705cfSriastradh	int dir;
17003b705cfSriastradh
17103b705cfSriastradh	grid_scaled_y_t height_left;
17203b705cfSriastradh
17303b705cfSriastradh	/* Current x coordinate while the edge is on the active
17403b705cfSriastradh	 * list. Initialised to the x coordinate of the top of the
17503b705cfSriastradh	 * edge. The quotient is in grid_scaled_x_t units and the
17603b705cfSriastradh	 * remainder is mod dy in grid_scaled_y_t units.*/
17703b705cfSriastradh	struct quorem x;
17803b705cfSriastradh
17903b705cfSriastradh	/* Advance of the current x when moving down a subsample line. */
18003b705cfSriastradh	struct quorem dxdy;
18103b705cfSriastradh	grid_scaled_y_t dy;
18203b705cfSriastradh
18303b705cfSriastradh	/* The clipped y of the top of the edge. */
18403b705cfSriastradh	grid_scaled_y_t ytop;
18503b705cfSriastradh
18603b705cfSriastradh	/* y2-y1 after orienting the edge downwards.  */
18703b705cfSriastradh};
18803b705cfSriastradh
18903b705cfSriastradh/* Number of subsample rows per y-bucket. Must be SAMPLES_Y. */
19003b705cfSriastradh#define EDGE_Y_BUCKET_HEIGHT FAST_SAMPLES_Y
19103b705cfSriastradh#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
19203b705cfSriastradh
19303b705cfSriastradh/* A collection of sorted and vertically clipped edges of the polygon.
19403b705cfSriastradh * Edges are moved from the polygon to an active list while scan
19503b705cfSriastradh * converting. */
19603b705cfSriastradhstruct polygon {
19703b705cfSriastradh	/* The vertical clip extents. */
19803b705cfSriastradh	grid_scaled_y_t ymin, ymax;
19903b705cfSriastradh
20003b705cfSriastradh	/* Array of edges all starting in the same bucket.	An edge is put
20103b705cfSriastradh	 * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
20203b705cfSriastradh	 * it is added to the polygon. */
20303b705cfSriastradh	struct edge **y_buckets;
20403b705cfSriastradh	struct edge *y_buckets_embedded[64];
20503b705cfSriastradh
20603b705cfSriastradh	struct edge edges_embedded[32];
20703b705cfSriastradh	struct edge *edges;
20803b705cfSriastradh	int num_edges;
20903b705cfSriastradh};
21003b705cfSriastradh
21103b705cfSriastradh/* A cell records the effect on pixel coverage of polygon edges
21203b705cfSriastradh * passing through a pixel.  It contains two accumulators of pixel
21303b705cfSriastradh * coverage.
21403b705cfSriastradh *
21503b705cfSriastradh * Consider the effects of a polygon edge on the coverage of a pixel
21603b705cfSriastradh * it intersects and that of the following one.  The coverage of the
21703b705cfSriastradh * following pixel is the height of the edge multiplied by the width
21803b705cfSriastradh * of the pixel, and the coverage of the pixel itself is the area of
21903b705cfSriastradh * the trapezoid formed by the edge and the right side of the pixel.
22003b705cfSriastradh *
22103b705cfSriastradh * +-----------------------+-----------------------+
22203b705cfSriastradh * |                       |                       |
22303b705cfSriastradh * |                       |                       |
22403b705cfSriastradh * |_______________________|_______________________|
22503b705cfSriastradh * |   \...................|.......................|\
22603b705cfSriastradh * |    \..................|.......................| |
22703b705cfSriastradh * |     \.................|.......................| |
22803b705cfSriastradh * |      \....covered.....|.......................| |
22903b705cfSriastradh * |       \....area.......|.......................| } covered height
23003b705cfSriastradh * |        \..............|.......................| |
23103b705cfSriastradh * |uncovered\.............|.......................| |
23203b705cfSriastradh * |  area    \............|.......................| |
23303b705cfSriastradh * |___________\...........|.......................|/
23403b705cfSriastradh * |                       |                       |
23503b705cfSriastradh * |                       |                       |
23603b705cfSriastradh * |                       |                       |
23703b705cfSriastradh * +-----------------------+-----------------------+
23803b705cfSriastradh *
23903b705cfSriastradh * Since the coverage of the following pixel will always be a multiple
24003b705cfSriastradh * of the width of the pixel, we can store the height of the covered
24103b705cfSriastradh * area instead.  The coverage of the pixel itself is the total
24203b705cfSriastradh * coverage minus the area of the uncovered area to the left of the
24303b705cfSriastradh * edge.  As it's faster to compute the uncovered area we only store
24403b705cfSriastradh * that and subtract it from the total coverage later when forming
24503b705cfSriastradh * spans to blit.
24603b705cfSriastradh *
24703b705cfSriastradh * The heights and areas are signed, with left edges of the polygon
24803b705cfSriastradh * having positive sign and right edges having negative sign.  When
24903b705cfSriastradh * two edges intersect they swap their left/rightness so their
25003b705cfSriastradh * contribution above and below the intersection point must be
25103b705cfSriastradh * computed separately. */
25203b705cfSriastradhstruct cell {
25303b705cfSriastradh	struct cell *next;
25403b705cfSriastradh	int x;
25503b705cfSriastradh	grid_area_t uncovered_area;
25603b705cfSriastradh	grid_scaled_y_t covered_height;
25703b705cfSriastradh};
25803b705cfSriastradh
25903b705cfSriastradh/* A cell list represents the scan line sparsely as cells ordered by
26003b705cfSriastradh * ascending x.  It is geared towards scanning the cells in order
26103b705cfSriastradh * using an internal cursor. */
26203b705cfSriastradhstruct cell_list {
26303b705cfSriastradh	struct cell *cursor;
26403b705cfSriastradh
26503b705cfSriastradh	/* Points to the left-most cell in the scan line. */
26603b705cfSriastradh	struct cell head, tail;
26703b705cfSriastradh
26803b705cfSriastradh	int16_t x1, x2;
26903b705cfSriastradh	int16_t count, size;
27003b705cfSriastradh	struct cell *cells;
27103b705cfSriastradh	struct cell embedded[256];
27203b705cfSriastradh};
27303b705cfSriastradh
27403b705cfSriastradh/* The active list contains edges in the current scan line ordered by
27503b705cfSriastradh * the x-coordinate of the intercept of the edge and the scan line. */
27603b705cfSriastradhstruct active_list {
27703b705cfSriastradh	/* Leftmost edge on the current scan line. */
27803b705cfSriastradh	struct edge head, tail;
27903b705cfSriastradh
28003b705cfSriastradh	/* A lower bound on the height of the active edges is used to
28103b705cfSriastradh	 * estimate how soon some active edge ends.	 We can't advance the
28203b705cfSriastradh	 * scan conversion by a full pixel row if an edge ends somewhere
28303b705cfSriastradh	 * within it. */
28403b705cfSriastradh	grid_scaled_y_t min_height;
28503b705cfSriastradh	int is_vertical;
28603b705cfSriastradh};
28703b705cfSriastradh
28803b705cfSriastradhstruct tor {
28903b705cfSriastradh    struct polygon	polygon[1];
29003b705cfSriastradh    struct active_list	active[1];
29103b705cfSriastradh    struct cell_list	coverages[1];
29203b705cfSriastradh
29303b705cfSriastradh    /* Clip box. */
29403b705cfSriastradh    grid_scaled_x_t xmin, xmax;
29503b705cfSriastradh    grid_scaled_y_t ymin, ymax;
29603b705cfSriastradh};
29703b705cfSriastradh
29803b705cfSriastradh/* Compute the floored division a/b. Assumes / and % perform symmetric
29903b705cfSriastradh * division. */
30003b705cfSriastradhinline static struct quorem
30103b705cfSriastradhfloored_divrem(int a, int b)
30203b705cfSriastradh{
30303b705cfSriastradh	struct quorem qr;
30403b705cfSriastradh	qr.quo = a/b;
30503b705cfSriastradh	qr.rem = a%b;
30603b705cfSriastradh	if (qr.rem && (a^b)<0) {
30703b705cfSriastradh		qr.quo -= 1;
30803b705cfSriastradh		qr.rem += b;
30903b705cfSriastradh	}
31003b705cfSriastradh	return qr;
31103b705cfSriastradh}
31203b705cfSriastradh
31303b705cfSriastradh/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
31403b705cfSriastradh * division. */
31503b705cfSriastradhstatic struct quorem
31603b705cfSriastradhfloored_muldivrem(int32_t x, int32_t a, int32_t b)
31703b705cfSriastradh{
31803b705cfSriastradh	struct quorem qr;
31903b705cfSriastradh	int64_t xa = (int64_t)x*a;
32003b705cfSriastradh	qr.quo = xa/b;
32103b705cfSriastradh	qr.rem = xa%b;
32203b705cfSriastradh	if (qr.rem && (xa>=0) != (b>=0)) {
32303b705cfSriastradh		qr.quo -= 1;
32403b705cfSriastradh		qr.rem += b;
32503b705cfSriastradh	}
32603b705cfSriastradh	return qr;
32703b705cfSriastradh}
32803b705cfSriastradh
32903b705cfSriastradh/* Rewinds the cell list's cursor to the beginning.  After rewinding
33003b705cfSriastradh * we're good to cell_list_find() the cell any x coordinate. */
33103b705cfSriastradhinline static void
33203b705cfSriastradhcell_list_rewind(struct cell_list *cells)
33303b705cfSriastradh{
33403b705cfSriastradh	cells->cursor = &cells->head;
33503b705cfSriastradh}
33603b705cfSriastradh
33703b705cfSriastradhstatic bool
33803b705cfSriastradhcell_list_init(struct cell_list *cells, int x1, int x2)
33903b705cfSriastradh{
34003b705cfSriastradh	cells->tail.next = NULL;
34103b705cfSriastradh	cells->tail.x = INT_MAX;
34203b705cfSriastradh	cells->head.x = INT_MIN;
34303b705cfSriastradh	cells->head.next = &cells->tail;
34403b705cfSriastradh	cell_list_rewind(cells);
34503b705cfSriastradh	cells->count = 0;
34603b705cfSriastradh	cells->x1 = x1;
34703b705cfSriastradh	cells->x2 = x2;
34803b705cfSriastradh	cells->size = x2 - x1 + 1;
34903b705cfSriastradh	cells->cells = cells->embedded;
35003b705cfSriastradh	if (cells->size > ARRAY_SIZE(cells->embedded))
35103b705cfSriastradh		cells->cells = malloc(cells->size * sizeof(struct cell));
35203b705cfSriastradh	return cells->cells != NULL;
35303b705cfSriastradh}
35403b705cfSriastradh
35503b705cfSriastradhstatic void
35603b705cfSriastradhcell_list_fini(struct cell_list *cells)
35703b705cfSriastradh{
35803b705cfSriastradh	if (cells->cells != cells->embedded)
35903b705cfSriastradh		free(cells->cells);
36003b705cfSriastradh}
36103b705cfSriastradh
36203b705cfSriastradhinline static void
36303b705cfSriastradhcell_list_reset(struct cell_list *cells)
36403b705cfSriastradh{
36503b705cfSriastradh	cell_list_rewind(cells);
36603b705cfSriastradh	cells->head.next = &cells->tail;
36703b705cfSriastradh	cells->count = 0;
36803b705cfSriastradh}
36903b705cfSriastradh
37003b705cfSriastradhinline static struct cell *
37103b705cfSriastradhcell_list_alloc(struct cell_list *cells,
37203b705cfSriastradh		struct cell *tail,
37303b705cfSriastradh		int x)
37403b705cfSriastradh{
37503b705cfSriastradh	struct cell *cell;
37603b705cfSriastradh
37703b705cfSriastradh	assert(cells->count < cells->size);
37803b705cfSriastradh	cell = cells->cells + cells->count++;
37903b705cfSriastradh	cell->next = tail->next;
38003b705cfSriastradh	tail->next = cell;
38103b705cfSriastradh
38203b705cfSriastradh	cell->x = x;
38303b705cfSriastradh	cell->uncovered_area = 0;
38403b705cfSriastradh	cell->covered_height = 0;
38503b705cfSriastradh	return cell;
38603b705cfSriastradh}
38703b705cfSriastradh
38803b705cfSriastradh/* Find a cell at the given x-coordinate.  Returns %NULL if a new cell
38903b705cfSriastradh * needed to be allocated but couldn't be.  Cells must be found with
39003b705cfSriastradh * non-decreasing x-coordinate until the cell list is rewound using
39103b705cfSriastradh * cell_list_rewind(). Ownership of the returned cell is retained by
39203b705cfSriastradh * the cell list. */
39303b705cfSriastradhinline static struct cell *
39403b705cfSriastradhcell_list_find(struct cell_list *cells, int x)
39503b705cfSriastradh{
39603b705cfSriastradh	struct cell *tail = cells->cursor;
39703b705cfSriastradh
39803b705cfSriastradh	if (tail->x == x)
39903b705cfSriastradh		return tail;
40003b705cfSriastradh
40103b705cfSriastradh	if (x >= cells->x2)
40203b705cfSriastradh		return &cells->tail;
40303b705cfSriastradh
40403b705cfSriastradh	if (x < cells->x1)
40503b705cfSriastradh		x = cells->x1;
40603b705cfSriastradh
40703b705cfSriastradh	if (tail->x == x)
40803b705cfSriastradh		return tail;
40903b705cfSriastradh
41003b705cfSriastradh	do {
41103b705cfSriastradh		if (tail->next->x > x)
41203b705cfSriastradh			break;
41303b705cfSriastradh
41403b705cfSriastradh		tail = tail->next;
41503b705cfSriastradh		if (tail->next->x > x)
41603b705cfSriastradh			break;
41703b705cfSriastradh
41803b705cfSriastradh		tail = tail->next;
41903b705cfSriastradh		if (tail->next->x > x)
42003b705cfSriastradh			break;
42103b705cfSriastradh
42203b705cfSriastradh		tail = tail->next;
42303b705cfSriastradh	} while (1);
42403b705cfSriastradh
42503b705cfSriastradh	if (tail->x != x)
42603b705cfSriastradh		tail = cell_list_alloc (cells, tail, x);
42703b705cfSriastradh
42803b705cfSriastradh	return cells->cursor = tail;
42903b705cfSriastradh}
43003b705cfSriastradh
43103b705cfSriastradh/* Add a subpixel span covering [x1, x2) to the coverage cells. */
43203b705cfSriastradhinline static void
43303b705cfSriastradhcell_list_add_subspan(struct cell_list *cells,
43403b705cfSriastradh		      grid_scaled_x_t x1,
43503b705cfSriastradh		      grid_scaled_x_t x2)
43603b705cfSriastradh{
43703b705cfSriastradh	struct cell *cell;
43803b705cfSriastradh	int ix1, fx1;
43903b705cfSriastradh	int ix2, fx2;
44003b705cfSriastradh
44103b705cfSriastradh	if (x1 == x2)
44203b705cfSriastradh		return;
44303b705cfSriastradh
44403b705cfSriastradh	FAST_SAMPLES_X_TO_INT_FRAC(x1, ix1, fx1);
44503b705cfSriastradh	FAST_SAMPLES_X_TO_INT_FRAC(x2, ix2, fx2);
44603b705cfSriastradh
44703b705cfSriastradh	__DBG(("%s: x1=%d (%d+%d), x2=%d (%d+%d)\n", __FUNCTION__,
44803b705cfSriastradh	       x1, ix1, fx1, x2, ix2, fx2));
44903b705cfSriastradh
45003b705cfSriastradh	cell = cell_list_find(cells, ix1);
45103b705cfSriastradh	if (ix1 != ix2) {
45203b705cfSriastradh		cell->uncovered_area += 2*fx1;
45303b705cfSriastradh		++cell->covered_height;
45403b705cfSriastradh
45503b705cfSriastradh		cell = cell_list_find(cells, ix2);
45603b705cfSriastradh		cell->uncovered_area -= 2*fx2;
45703b705cfSriastradh		--cell->covered_height;
45803b705cfSriastradh	} else
45903b705cfSriastradh		cell->uncovered_area += 2*(fx1-fx2);
46003b705cfSriastradh}
46103b705cfSriastradh
46203b705cfSriastradhinline static void
46303b705cfSriastradhcell_list_add_span(struct cell_list *cells,
46403b705cfSriastradh		   grid_scaled_x_t x1,
46503b705cfSriastradh		   grid_scaled_x_t x2)
46603b705cfSriastradh{
46703b705cfSriastradh	struct cell *cell;
46803b705cfSriastradh	int ix1, fx1;
46903b705cfSriastradh	int ix2, fx2;
47003b705cfSriastradh
47103b705cfSriastradh	FAST_SAMPLES_X_TO_INT_FRAC(x1, ix1, fx1);
47203b705cfSriastradh	FAST_SAMPLES_X_TO_INT_FRAC(x2, ix2, fx2);
47303b705cfSriastradh
47403b705cfSriastradh	__DBG(("%s: x1=%d (%d+%d), x2=%d (%d+%d)\n", __FUNCTION__,
47503b705cfSriastradh	       x1, ix1, fx1, x2, ix2, fx2));
47603b705cfSriastradh
47703b705cfSriastradh	cell = cell_list_find(cells, ix1);
47803b705cfSriastradh	if (ix1 != ix2) {
47903b705cfSriastradh		cell->uncovered_area += 2*fx1*FAST_SAMPLES_Y;
48003b705cfSriastradh		cell->covered_height += FAST_SAMPLES_Y;
48103b705cfSriastradh
48203b705cfSriastradh		cell = cell_list_find(cells, ix2);
48303b705cfSriastradh		cell->uncovered_area -= 2*fx2*FAST_SAMPLES_Y;
48403b705cfSriastradh		cell->covered_height -= FAST_SAMPLES_Y;
48503b705cfSriastradh	} else
48603b705cfSriastradh		cell->uncovered_area += 2*(fx1-fx2)*FAST_SAMPLES_Y;
48703b705cfSriastradh}
48803b705cfSriastradh
48903b705cfSriastradhstatic void
49003b705cfSriastradhpolygon_fini(struct polygon *polygon)
49103b705cfSriastradh{
49203b705cfSriastradh	if (polygon->y_buckets != polygon->y_buckets_embedded)
49303b705cfSriastradh		free(polygon->y_buckets);
49403b705cfSriastradh
49503b705cfSriastradh	if (polygon->edges != polygon->edges_embedded)
49603b705cfSriastradh		free(polygon->edges);
49703b705cfSriastradh}
49803b705cfSriastradh
49903b705cfSriastradhstatic bool
50003b705cfSriastradhpolygon_init(struct polygon *polygon,
50103b705cfSriastradh	     int num_edges,
50203b705cfSriastradh	     grid_scaled_y_t ymin,
50303b705cfSriastradh	     grid_scaled_y_t ymax)
50403b705cfSriastradh{
50503b705cfSriastradh	unsigned h = ymax - ymin;
50603b705cfSriastradh	unsigned num_buckets =
50703b705cfSriastradh		EDGE_Y_BUCKET_INDEX(ymax+EDGE_Y_BUCKET_HEIGHT-1, ymin);
50803b705cfSriastradh
50903b705cfSriastradh	if (unlikely(h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
51003b705cfSriastradh		goto bail_no_mem; /* even if you could, you wouldn't want to. */
51103b705cfSriastradh
51203b705cfSriastradh	polygon->edges = polygon->edges_embedded;
51303b705cfSriastradh	polygon->y_buckets = polygon->y_buckets_embedded;
51403b705cfSriastradh
51503b705cfSriastradh	polygon->num_edges = 0;
51603b705cfSriastradh	if (num_edges > (int)ARRAY_SIZE(polygon->edges_embedded)) {
51703b705cfSriastradh		polygon->edges = malloc(sizeof(struct edge)*num_edges);
51803b705cfSriastradh		if (unlikely(NULL == polygon->edges))
51903b705cfSriastradh			goto bail_no_mem;
52003b705cfSriastradh	}
52103b705cfSriastradh
52203b705cfSriastradh	if (num_buckets >= ARRAY_SIZE(polygon->y_buckets_embedded)) {
52303b705cfSriastradh		polygon->y_buckets = malloc((1+num_buckets)*sizeof(struct edge *));
52403b705cfSriastradh		if (unlikely(NULL == polygon->y_buckets))
52503b705cfSriastradh			goto bail_no_mem;
52603b705cfSriastradh	}
52703b705cfSriastradh	memset(polygon->y_buckets, 0, num_buckets * sizeof(struct edge *));
52803b705cfSriastradh	polygon->y_buckets[num_buckets] = (void *)-1;
52903b705cfSriastradh
53003b705cfSriastradh	polygon->ymin = ymin;
53103b705cfSriastradh	polygon->ymax = ymax;
53203b705cfSriastradh	return true;
53303b705cfSriastradh
53403b705cfSriastradhbail_no_mem:
53503b705cfSriastradh	polygon_fini(polygon);
53603b705cfSriastradh	return false;
53703b705cfSriastradh}
53803b705cfSriastradh
53903b705cfSriastradhstatic void
54003b705cfSriastradh_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon, struct edge *e)
54103b705cfSriastradh{
54203b705cfSriastradh	unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
54303b705cfSriastradh	struct edge **ptail = &polygon->y_buckets[ix];
54403b705cfSriastradh	e->next = *ptail;
54503b705cfSriastradh	*ptail = e;
54603b705cfSriastradh}
54703b705cfSriastradh
54803b705cfSriastradhinline static void
54903b705cfSriastradhpolygon_add_edge(struct polygon *polygon,
55003b705cfSriastradh		 grid_scaled_x_t x1,
55103b705cfSriastradh		 grid_scaled_x_t x2,
55203b705cfSriastradh		 grid_scaled_y_t y1,
55303b705cfSriastradh		 grid_scaled_y_t y2,
55403b705cfSriastradh		 grid_scaled_y_t top,
55503b705cfSriastradh		 grid_scaled_y_t bottom,
55603b705cfSriastradh		 int dir)
55703b705cfSriastradh{
55803b705cfSriastradh	struct edge *e = &polygon->edges[polygon->num_edges++];
55903b705cfSriastradh	grid_scaled_x_t dx = x2 - x1;
56003b705cfSriastradh	grid_scaled_y_t dy = y2 - y1;
56103b705cfSriastradh	grid_scaled_y_t ytop, ybot;
56203b705cfSriastradh	grid_scaled_y_t ymin = polygon->ymin;
56303b705cfSriastradh	grid_scaled_y_t ymax = polygon->ymax;
56403b705cfSriastradh
56503b705cfSriastradh	__DBG(("%s: edge=(%d [%d.%d], %d [%d.%d]), (%d [%d.%d], %d [%d.%d]), top=%d [%d.%d], bottom=%d [%d.%d], dir=%d\n",
56603b705cfSriastradh	       __FUNCTION__,
56703b705cfSriastradh	       x1, FAST_SAMPLES_INT(x1), FAST_SAMPLES_FRAC(x1),
56803b705cfSriastradh	       y1, FAST_SAMPLES_INT(y1), FAST_SAMPLES_FRAC(y1),
56903b705cfSriastradh	       x2, FAST_SAMPLES_INT(x2), FAST_SAMPLES_FRAC(x2),
57003b705cfSriastradh	       y2, FAST_SAMPLES_INT(y2), FAST_SAMPLES_FRAC(y2),
57103b705cfSriastradh	       top, FAST_SAMPLES_INT(top), FAST_SAMPLES_FRAC(top),
57203b705cfSriastradh	       bottom, FAST_SAMPLES_INT(bottom), FAST_SAMPLES_FRAC(bottom),
57303b705cfSriastradh	       dir));
57403b705cfSriastradh	assert (dy > 0);
57503b705cfSriastradh
57603b705cfSriastradh	e->dy = dy;
57703b705cfSriastradh	e->dir = dir;
57803b705cfSriastradh
57903b705cfSriastradh	ytop = top >= ymin ? top : ymin;
58003b705cfSriastradh	ybot = bottom <= ymax ? bottom : ymax;
58103b705cfSriastradh	e->ytop = ytop;
58203b705cfSriastradh	e->height_left = ybot - ytop;
58303b705cfSriastradh	if (e->height_left <= 0)
58403b705cfSriastradh		return;
58503b705cfSriastradh
58603b705cfSriastradh	if (dx == 0) {
58703b705cfSriastradh		e->x.quo = x1;
58803b705cfSriastradh		e->x.rem = 0;
58903b705cfSriastradh		e->dy = 0;
59003b705cfSriastradh		e->dxdy.quo = 0;
59103b705cfSriastradh		e->dxdy.rem = 0;
59203b705cfSriastradh	} else {
59303b705cfSriastradh		e->dxdy = floored_divrem(dx, dy);
59403b705cfSriastradh		if (ytop == y1) {
59503b705cfSriastradh			e->x.quo = x1;
59603b705cfSriastradh			e->x.rem = 0;
59703b705cfSriastradh		} else {
59803b705cfSriastradh			e->x = floored_muldivrem(ytop - y1, dx, dy);
59903b705cfSriastradh			e->x.quo += x1;
60003b705cfSriastradh		}
60103b705cfSriastradh	}
60203b705cfSriastradh
60303b705cfSriastradh	_polygon_insert_edge_into_its_y_bucket(polygon, e);
60403b705cfSriastradh
60503b705cfSriastradh	e->x.rem -= dy; /* Bias the remainder for faster edge advancement. */
60603b705cfSriastradh}
60703b705cfSriastradh
60803b705cfSriastradhinline static void
60903b705cfSriastradhpolygon_add_line(struct polygon *polygon,
61003b705cfSriastradh		 const xPointFixed *p1,
61103b705cfSriastradh		 const xPointFixed *p2)
61203b705cfSriastradh{
61303b705cfSriastradh	struct edge *e = &polygon->edges[polygon->num_edges];
61403b705cfSriastradh	grid_scaled_x_t dx = p2->x - p1->x;
61503b705cfSriastradh	grid_scaled_y_t dy = p2->y - p1->y;
61603b705cfSriastradh	grid_scaled_y_t top, bot;
61703b705cfSriastradh
61803b705cfSriastradh	if (dy == 0)
61903b705cfSriastradh		return;
62003b705cfSriastradh
62103b705cfSriastradh	__DBG(("%s: line=(%d, %d), (%d, %d)\n",
62203b705cfSriastradh	       __FUNCTION__, (int)p1->x, (int)p1->y, (int)p2->x, (int)p2->y));
62303b705cfSriastradh
62403b705cfSriastradh	e->dir = 1;
62503b705cfSriastradh	if (dy < 0) {
62603b705cfSriastradh		const xPointFixed *t;
62703b705cfSriastradh
62803b705cfSriastradh		dx = -dx;
62903b705cfSriastradh		dy = -dy;
63003b705cfSriastradh
63103b705cfSriastradh		e->dir = -1;
63203b705cfSriastradh
63303b705cfSriastradh		t = p1;
63403b705cfSriastradh		p1 = p2;
63503b705cfSriastradh		p2 = t;
63603b705cfSriastradh	}
63703b705cfSriastradh	assert (dy > 0);
63803b705cfSriastradh	e->dy = dy;
63903b705cfSriastradh
64003b705cfSriastradh	top = MAX(p1->y, polygon->ymin);
64103b705cfSriastradh	bot = MIN(p2->y, polygon->ymax);
64203b705cfSriastradh	if (bot <= top)
64303b705cfSriastradh		return;
64403b705cfSriastradh
64503b705cfSriastradh	e->ytop = top;
64603b705cfSriastradh	e->height_left = bot - top;
64703b705cfSriastradh	if (e->height_left <= 0)
64803b705cfSriastradh		return;
64903b705cfSriastradh
65003b705cfSriastradh	if (dx == 0) {
65103b705cfSriastradh		e->x.quo = p1->x;
65203b705cfSriastradh		e->x.rem = -dy;
65303b705cfSriastradh		e->dxdy.quo = 0;
65403b705cfSriastradh		e->dxdy.rem = 0;
65503b705cfSriastradh		e->dy = 0;
65603b705cfSriastradh	} else {
65703b705cfSriastradh		e->dxdy = floored_divrem(dx, dy);
65803b705cfSriastradh		if (top == p1->y) {
65903b705cfSriastradh			e->x.quo = p1->x;
66003b705cfSriastradh			e->x.rem = -dy;
66103b705cfSriastradh		} else {
66203b705cfSriastradh			e->x = floored_muldivrem(top - p1->y, dx, dy);
66303b705cfSriastradh			e->x.quo += p1->x;
66403b705cfSriastradh			e->x.rem -= dy;
66503b705cfSriastradh		}
66603b705cfSriastradh	}
66703b705cfSriastradh
66803b705cfSriastradh	if (polygon->num_edges > 0) {
66903b705cfSriastradh		struct edge *prev = &polygon->edges[polygon->num_edges-1];
67003b705cfSriastradh		/* detect degenerate triangles inserted into tristrips */
67103b705cfSriastradh		if (e->dir == -prev->dir &&
67203b705cfSriastradh		    e->ytop == prev->ytop &&
67303b705cfSriastradh		    e->height_left == prev->height_left &&
67403b705cfSriastradh		    e->x.quo == prev->x.quo &&
67503b705cfSriastradh		    e->x.rem == prev->x.rem &&
67603b705cfSriastradh		    e->dxdy.quo == prev->dxdy.quo &&
67703b705cfSriastradh		    e->dxdy.rem == prev->dxdy.rem) {
67803b705cfSriastradh			unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop,
67903b705cfSriastradh							  polygon->ymin);
68003b705cfSriastradh			polygon->y_buckets[ix] = prev->next;
68103b705cfSriastradh			polygon->num_edges--;
68203b705cfSriastradh			return;
68303b705cfSriastradh		}
68403b705cfSriastradh	}
68503b705cfSriastradh
68603b705cfSriastradh	_polygon_insert_edge_into_its_y_bucket(polygon, e);
68703b705cfSriastradh	polygon->num_edges++;
68803b705cfSriastradh}
68903b705cfSriastradh
69003b705cfSriastradhstatic void
69103b705cfSriastradhactive_list_reset(struct active_list *active)
69203b705cfSriastradh{
69303b705cfSriastradh	active->head.height_left = INT_MAX;
69403b705cfSriastradh	active->head.x.quo = INT_MIN;
69503b705cfSriastradh	active->head.dy = 0;
69603b705cfSriastradh	active->head.prev = NULL;
69703b705cfSriastradh	active->head.next = &active->tail;
69803b705cfSriastradh	active->tail.prev = &active->head;
69903b705cfSriastradh	active->tail.next = NULL;
70003b705cfSriastradh	active->tail.x.quo = INT_MAX;
70103b705cfSriastradh	active->tail.height_left = INT_MAX;
70203b705cfSriastradh	active->tail.dy = 0;
70303b705cfSriastradh	active->min_height = INT_MAX;
70403b705cfSriastradh	active->is_vertical = 1;
70503b705cfSriastradh}
70603b705cfSriastradh
70703b705cfSriastradhstatic struct edge *
70803b705cfSriastradhmerge_sorted_edges(struct edge *head_a, struct edge *head_b)
70903b705cfSriastradh{
71003b705cfSriastradh	struct edge *head, **next, *prev;
71103b705cfSriastradh	int32_t x;
71203b705cfSriastradh
71303b705cfSriastradh	if (head_b == NULL)
71403b705cfSriastradh		return head_a;
71503b705cfSriastradh
71603b705cfSriastradh	prev = head_a->prev;
71703b705cfSriastradh	next = &head;
71803b705cfSriastradh	if (head_a->x.quo <= head_b->x.quo) {
71903b705cfSriastradh		head = head_a;
72003b705cfSriastradh	} else {
72103b705cfSriastradh		head = head_b;
72203b705cfSriastradh		head_b->prev = prev;
72303b705cfSriastradh		goto start_with_b;
72403b705cfSriastradh	}
72503b705cfSriastradh
72603b705cfSriastradh	do {
72703b705cfSriastradh		x = head_b->x.quo;
72803b705cfSriastradh		while (head_a != NULL && head_a->x.quo <= x) {
72903b705cfSriastradh			prev = head_a;
73003b705cfSriastradh			next = &head_a->next;
73103b705cfSriastradh			head_a = head_a->next;
73203b705cfSriastradh		}
73303b705cfSriastradh
73403b705cfSriastradh		head_b->prev = prev;
73503b705cfSriastradh		*next = head_b;
73603b705cfSriastradh		if (head_a == NULL)
73703b705cfSriastradh			return head;
73803b705cfSriastradh
73903b705cfSriastradhstart_with_b:
74003b705cfSriastradh		x = head_a->x.quo;
74103b705cfSriastradh		while (head_b != NULL && head_b->x.quo <= x) {
74203b705cfSriastradh			prev = head_b;
74303b705cfSriastradh			next = &head_b->next;
74403b705cfSriastradh			head_b = head_b->next;
74503b705cfSriastradh		}
74603b705cfSriastradh
74703b705cfSriastradh		head_a->prev = prev;
74803b705cfSriastradh		*next = head_a;
74903b705cfSriastradh		if (head_b == NULL)
75003b705cfSriastradh			return head;
75103b705cfSriastradh	} while (1);
75203b705cfSriastradh}
75303b705cfSriastradh
75403b705cfSriastradhstatic struct edge *
75503b705cfSriastradhsort_edges(struct edge  *list,
75603b705cfSriastradh	   unsigned int  level,
75703b705cfSriastradh	   struct edge **head_out)
75803b705cfSriastradh{
75903b705cfSriastradh	struct edge *head_other, *remaining;
76003b705cfSriastradh	unsigned int i;
76103b705cfSriastradh
76203b705cfSriastradh	head_other = list->next;
76303b705cfSriastradh	if (head_other == NULL) {
76403b705cfSriastradh		*head_out = list;
76503b705cfSriastradh		return NULL;
76603b705cfSriastradh	}
76703b705cfSriastradh
76803b705cfSriastradh	remaining = head_other->next;
76903b705cfSriastradh	if (list->x.quo <= head_other->x.quo) {
77003b705cfSriastradh		*head_out = list;
77103b705cfSriastradh		head_other->next = NULL;
77203b705cfSriastradh	} else {
77303b705cfSriastradh		*head_out = head_other;
77403b705cfSriastradh		head_other->prev = list->prev;
77503b705cfSriastradh		head_other->next = list;
77603b705cfSriastradh		list->prev = head_other;
77703b705cfSriastradh		list->next = NULL;
77803b705cfSriastradh	}
77903b705cfSriastradh
78003b705cfSriastradh	for (i = 0; i < level && remaining; i++) {
78103b705cfSriastradh		remaining = sort_edges(remaining, i, &head_other);
78203b705cfSriastradh		*head_out = merge_sorted_edges(*head_out, head_other);
78303b705cfSriastradh	}
78403b705cfSriastradh
78503b705cfSriastradh	return remaining;
78603b705cfSriastradh}
78703b705cfSriastradh
78803b705cfSriastradhstatic struct edge *filter(struct edge *edges)
78903b705cfSriastradh{
79003b705cfSriastradh	struct edge *e;
79103b705cfSriastradh
79203b705cfSriastradh	e = edges;
79303b705cfSriastradh	do {
79403b705cfSriastradh		struct edge *n = e->next;
79503b705cfSriastradh		if (e->dir == -n->dir &&
79603b705cfSriastradh		    e->height_left == n->height_left &&
79703b705cfSriastradh		    *(uint64_t *)&e->x == *(uint64_t *)&n->x &&
79803b705cfSriastradh		    *(uint64_t *)&e->dxdy == *(uint64_t *)&n->dxdy) {
79903b705cfSriastradh			if (e->prev)
80003b705cfSriastradh				e->prev->next = n->next;
80103b705cfSriastradh			else
80203b705cfSriastradh				edges = n->next;
80303b705cfSriastradh			if (n->next)
80403b705cfSriastradh				n->next->prev = e->prev;
80503b705cfSriastradh			else
80603b705cfSriastradh				break;
80703b705cfSriastradh
80803b705cfSriastradh			e = n->next;
80903b705cfSriastradh		} else
81003b705cfSriastradh			e = e->next;
81103b705cfSriastradh	} while (e->next);
81203b705cfSriastradh
81303b705cfSriastradh	return edges;
81403b705cfSriastradh}
81503b705cfSriastradh
81603b705cfSriastradhstatic struct edge *
81703b705cfSriastradhmerge_unsorted_edges (struct edge *head, struct edge *unsorted)
81803b705cfSriastradh{
81903b705cfSriastradh	sort_edges (unsorted, UINT_MAX, &unsorted);
82003b705cfSriastradh	return merge_sorted_edges (head, filter(unsorted));
82103b705cfSriastradh}
82203b705cfSriastradh
82303b705cfSriastradh/* Test if the edges on the active list can be safely advanced by a
82403b705cfSriastradh * full row without intersections or any edges ending. */
82503b705cfSriastradhinline static bool
82603b705cfSriastradhcan_full_step(struct active_list *active)
82703b705cfSriastradh{
82803b705cfSriastradh	/* Recomputes the minimum height of all edges on the active
82903b705cfSriastradh	 * list if we have been dropping edges. */
83003b705cfSriastradh	if (active->min_height <= 0) {
83103b705cfSriastradh		const struct edge *e;
83203b705cfSriastradh		int min_height = INT_MAX;
83303b705cfSriastradh		int is_vertical = 1;
83403b705cfSriastradh
83503b705cfSriastradh		for (e = active->head.next; &active->tail != e; e = e->next) {
83603b705cfSriastradh			if (e->height_left < min_height)
83703b705cfSriastradh				min_height = e->height_left;
83803b705cfSriastradh			if (is_vertical)
83903b705cfSriastradh				is_vertical = e->dy == 0;
84003b705cfSriastradh		}
84103b705cfSriastradh
84203b705cfSriastradh		active->is_vertical = is_vertical;
84303b705cfSriastradh		active->min_height = min_height;
84403b705cfSriastradh	}
84503b705cfSriastradh
84603b705cfSriastradh	if (active->min_height < FAST_SAMPLES_Y)
84703b705cfSriastradh		return false;
84803b705cfSriastradh
84903b705cfSriastradh	return active->is_vertical;
85003b705cfSriastradh}
85103b705cfSriastradh
85203b705cfSriastradhinline static void
85303b705cfSriastradhmerge_edges(struct active_list *active, struct edge *edges)
85403b705cfSriastradh{
85503b705cfSriastradh	active->head.next = merge_unsorted_edges (active->head.next, edges);
85603b705cfSriastradh}
85703b705cfSriastradh
85803b705cfSriastradhinline static void
85903b705cfSriastradhfill_buckets(struct active_list *active,
86003b705cfSriastradh	     struct edge *edge,
86103b705cfSriastradh	     struct edge **buckets)
86203b705cfSriastradh{
86303b705cfSriastradh	int min_height = active->min_height;
86403b705cfSriastradh	int is_vertical = active->is_vertical;
86503b705cfSriastradh
86603b705cfSriastradh	while (edge) {
86703b705cfSriastradh		struct edge *next = edge->next;
86803b705cfSriastradh		struct edge **b = &buckets[edge->ytop & (FAST_SAMPLES_Y-1)];
86903b705cfSriastradh		if (*b)
87003b705cfSriastradh			(*b)->prev = edge;
87103b705cfSriastradh		edge->next = *b;
87203b705cfSriastradh		edge->prev = NULL;
87303b705cfSriastradh		*b = edge;
87403b705cfSriastradh		if (edge->height_left < min_height)
87503b705cfSriastradh			min_height = edge->height_left;
87603b705cfSriastradh		if (is_vertical)
87703b705cfSriastradh			is_vertical = edge->dy == 0;
87803b705cfSriastradh		edge = next;
87903b705cfSriastradh	}
88003b705cfSriastradh
88103b705cfSriastradh	active->is_vertical = is_vertical;
88203b705cfSriastradh	active->min_height = min_height;
88303b705cfSriastradh}
88403b705cfSriastradh
88503b705cfSriastradhinline static void
88603b705cfSriastradhnonzero_subrow(struct active_list *active, struct cell_list *coverages)
88703b705cfSriastradh{
88803b705cfSriastradh	struct edge *edge = active->head.next;
88903b705cfSriastradh	grid_scaled_x_t prev_x = INT_MIN;
89003b705cfSriastradh	int winding = 0, xstart = INT_MIN;
89103b705cfSriastradh
89203b705cfSriastradh	cell_list_rewind (coverages);
89303b705cfSriastradh
89403b705cfSriastradh	while (&active->tail != edge) {
89503b705cfSriastradh		struct edge *next = edge->next;
89603b705cfSriastradh
89703b705cfSriastradh		winding += edge->dir;
89803b705cfSriastradh		if (0 == winding) {
89903b705cfSriastradh			if (edge->next->x.quo != edge->x.quo) {
90003b705cfSriastradh				cell_list_add_subspan(coverages,
90103b705cfSriastradh						      xstart, edge->x.quo);
90203b705cfSriastradh				xstart = INT_MIN;
90303b705cfSriastradh			}
90403b705cfSriastradh		} else if (xstart < 0)
90503b705cfSriastradh			xstart = edge->x.quo;
90603b705cfSriastradh
90703b705cfSriastradh		if (--edge->height_left) {
90803b705cfSriastradh			if (edge->dy) {
90903b705cfSriastradh				edge->x.quo += edge->dxdy.quo;
91003b705cfSriastradh				edge->x.rem += edge->dxdy.rem;
91103b705cfSriastradh				if (edge->x.rem >= 0) {
91203b705cfSriastradh					++edge->x.quo;
91303b705cfSriastradh					edge->x.rem -= edge->dy;
91403b705cfSriastradh				}
91503b705cfSriastradh			}
91603b705cfSriastradh
91703b705cfSriastradh			if (edge->x.quo < prev_x) {
91803b705cfSriastradh				struct edge *pos = edge->prev;
91903b705cfSriastradh				pos->next = next;
92003b705cfSriastradh				next->prev = pos;
92103b705cfSriastradh				do {
92203b705cfSriastradh					pos = pos->prev;
92303b705cfSriastradh				} while (edge->x.quo < pos->x.quo);
92403b705cfSriastradh				pos->next->prev = edge;
92503b705cfSriastradh				edge->next = pos->next;
92603b705cfSriastradh				edge->prev = pos;
92703b705cfSriastradh				pos->next = edge;
92803b705cfSriastradh			} else
92903b705cfSriastradh				prev_x = edge->x.quo;
93003b705cfSriastradh		} else {
93103b705cfSriastradh			edge->prev->next = next;
93203b705cfSriastradh			next->prev = edge->prev;
93303b705cfSriastradh			active->min_height = -1;
93403b705cfSriastradh		}
93503b705cfSriastradh
93603b705cfSriastradh		edge = next;
93703b705cfSriastradh	}
93803b705cfSriastradh}
93903b705cfSriastradh
94003b705cfSriastradhstatic void
94103b705cfSriastradhnonzero_row(struct active_list *active, struct cell_list *coverages)
94203b705cfSriastradh{
94303b705cfSriastradh	struct edge *left = active->head.next;
94403b705cfSriastradh
94503b705cfSriastradh	assert(active->is_vertical);
94603b705cfSriastradh
94703b705cfSriastradh	while (&active->tail != left) {
94803b705cfSriastradh		struct edge *right;
94903b705cfSriastradh		int winding = left->dir;
95003b705cfSriastradh
95103b705cfSriastradh		left->height_left -= FAST_SAMPLES_Y;
95203b705cfSriastradh		if (! left->height_left) {
95303b705cfSriastradh			left->prev->next = left->next;
95403b705cfSriastradh			left->next->prev = left->prev;
95503b705cfSriastradh		}
95603b705cfSriastradh
95703b705cfSriastradh		right = left->next;
95803b705cfSriastradh		do {
95903b705cfSriastradh			right->height_left -= FAST_SAMPLES_Y;
96003b705cfSriastradh			if (!right->height_left) {
96103b705cfSriastradh				right->prev->next = right->next;
96203b705cfSriastradh				right->next->prev = right->prev;
96303b705cfSriastradh			}
96403b705cfSriastradh
96503b705cfSriastradh			winding += right->dir;
96603b705cfSriastradh			if (0 == winding)
96703b705cfSriastradh				break;
96803b705cfSriastradh
96903b705cfSriastradh			right = right->next;
97003b705cfSriastradh		} while (1);
97103b705cfSriastradh
97203b705cfSriastradh		cell_list_add_span(coverages, left->x.quo, right->x.quo);
97303b705cfSriastradh		left = right->next;
97403b705cfSriastradh	}
97503b705cfSriastradh}
97603b705cfSriastradh
97703b705cfSriastradhstatic void
97803b705cfSriastradhtor_fini(struct tor *converter)
97903b705cfSriastradh{
98003b705cfSriastradh	polygon_fini(converter->polygon);
98103b705cfSriastradh	cell_list_fini(converter->coverages);
98203b705cfSriastradh}
98303b705cfSriastradh
98403b705cfSriastradhstatic bool
98503b705cfSriastradhtor_init(struct tor *converter, const BoxRec *box, int num_edges)
98603b705cfSriastradh{
98703b705cfSriastradh	__DBG(("%s: (%d, %d),(%d, %d) x (%d, %d), num_edges=%d\n",
98803b705cfSriastradh	       __FUNCTION__,
98903b705cfSriastradh	       box->x1, box->y1, box->x2, box->y2,
99003b705cfSriastradh	       FAST_SAMPLES_X, FAST_SAMPLES_Y,
99103b705cfSriastradh	       num_edges));
99203b705cfSriastradh
99303b705cfSriastradh	converter->xmin = box->x1;
99403b705cfSriastradh	converter->ymin = box->y1;
99503b705cfSriastradh	converter->xmax = box->x2;
99603b705cfSriastradh	converter->ymax = box->y2;
99703b705cfSriastradh
99803b705cfSriastradh	if (!cell_list_init(converter->coverages, box->x1, box->x2))
99903b705cfSriastradh		return false;
100003b705cfSriastradh
100103b705cfSriastradh	active_list_reset(converter->active);
100203b705cfSriastradh	if (!polygon_init(converter->polygon,
100303b705cfSriastradh			    num_edges,
100403b705cfSriastradh			    box->y1 * FAST_SAMPLES_Y,
100503b705cfSriastradh			    box->y2 * FAST_SAMPLES_Y)) {
100603b705cfSriastradh		cell_list_fini(converter->coverages);
100703b705cfSriastradh		return false;
100803b705cfSriastradh	}
100903b705cfSriastradh
101003b705cfSriastradh	return true;
101103b705cfSriastradh}
101203b705cfSriastradh
101303b705cfSriastradhstatic void
101403b705cfSriastradhtor_add_edge(struct tor *converter,
101503b705cfSriastradh	     const xTrapezoid *t,
101603b705cfSriastradh	     const xLineFixed *edge,
101703b705cfSriastradh	     int dir)
101803b705cfSriastradh{
101903b705cfSriastradh	polygon_add_edge(converter->polygon,
102003b705cfSriastradh			 edge->p1.x, edge->p2.x,
102103b705cfSriastradh			 edge->p1.y, edge->p2.y,
102203b705cfSriastradh			 t->top, t->bottom,
102303b705cfSriastradh			 dir);
102403b705cfSriastradh}
102503b705cfSriastradh
102603b705cfSriastradhstatic void
102703b705cfSriastradhstep_edges(struct active_list *active, int count)
102803b705cfSriastradh{
102903b705cfSriastradh	struct edge *edge;
103003b705cfSriastradh
103103b705cfSriastradh	count *= FAST_SAMPLES_Y;
103203b705cfSriastradh	for (edge = active->head.next; edge != &active->tail; edge = edge->next) {
103303b705cfSriastradh		edge->height_left -= count;
103403b705cfSriastradh		if (! edge->height_left) {
103503b705cfSriastradh			edge->prev->next = edge->next;
103603b705cfSriastradh			edge->next->prev = edge->prev;
103703b705cfSriastradh		}
103803b705cfSriastradh	}
103903b705cfSriastradh}
104003b705cfSriastradh
104103b705cfSriastradhstatic void
104203b705cfSriastradhtor_blt_span(struct sna *sna,
104303b705cfSriastradh	     struct sna_composite_spans_op *op,
104403b705cfSriastradh	     pixman_region16_t *clip,
104503b705cfSriastradh	     const BoxRec *box,
104603b705cfSriastradh	     int coverage)
104703b705cfSriastradh{
104803b705cfSriastradh	__DBG(("%s: %d -> %d @ %d\n", __FUNCTION__, box->x1, box->x2, coverage));
104903b705cfSriastradh
105003b705cfSriastradh	op->box(sna, op, box, AREA_TO_ALPHA(coverage));
105103b705cfSriastradh	apply_damage_box(&op->base, box);
105203b705cfSriastradh}
105303b705cfSriastradh
105403b705cfSriastradhstatic void
105503b705cfSriastradhtor_blt_span__no_damage(struct sna *sna,
105603b705cfSriastradh			struct sna_composite_spans_op *op,
105703b705cfSriastradh			pixman_region16_t *clip,
105803b705cfSriastradh			const BoxRec *box,
105903b705cfSriastradh			int coverage)
106003b705cfSriastradh{
106103b705cfSriastradh	__DBG(("%s: %d -> %d @ %d\n", __FUNCTION__, box->x1, box->x2, coverage));
106203b705cfSriastradh
106303b705cfSriastradh	op->box(sna, op, box, AREA_TO_ALPHA(coverage));
106403b705cfSriastradh}
106503b705cfSriastradh
106603b705cfSriastradhstatic void
106703b705cfSriastradhtor_blt_span_clipped(struct sna *sna,
106803b705cfSriastradh		     struct sna_composite_spans_op *op,
106903b705cfSriastradh		     pixman_region16_t *clip,
107003b705cfSriastradh		     const BoxRec *box,
107103b705cfSriastradh		     int coverage)
107203b705cfSriastradh{
107303b705cfSriastradh	pixman_region16_t region;
107403b705cfSriastradh	float opacity;
107503b705cfSriastradh
107603b705cfSriastradh	opacity = AREA_TO_ALPHA(coverage);
107703b705cfSriastradh	__DBG(("%s: %d -> %d @ %f\n", __FUNCTION__, box->x1, box->x2, opacity));
107803b705cfSriastradh
107903b705cfSriastradh	pixman_region_init_rects(&region, box, 1);
108003b705cfSriastradh	RegionIntersect(&region, &region, clip);
108103b705cfSriastradh	if (REGION_NUM_RECTS(&region)) {
108203b705cfSriastradh		op->boxes(sna, op,
108303b705cfSriastradh			  REGION_RECTS(&region),
108403b705cfSriastradh			  REGION_NUM_RECTS(&region),
108503b705cfSriastradh			  opacity);
108603b705cfSriastradh		apply_damage(&op->base, &region);
108703b705cfSriastradh	}
108803b705cfSriastradh	pixman_region_fini(&region);
108903b705cfSriastradh}
109003b705cfSriastradh
109103b705cfSriastradhstatic void
109203b705cfSriastradhtor_blt_span_mono(struct sna *sna,
109303b705cfSriastradh		  struct sna_composite_spans_op *op,
109403b705cfSriastradh		  pixman_region16_t *clip,
109503b705cfSriastradh		  const BoxRec *box,
109603b705cfSriastradh		  int coverage)
109703b705cfSriastradh{
109803b705cfSriastradh	if (coverage < FAST_SAMPLES_XY/2)
109903b705cfSriastradh		return;
110003b705cfSriastradh
110103b705cfSriastradh	tor_blt_span(sna, op, clip, box, FAST_SAMPLES_XY);
110203b705cfSriastradh}
110303b705cfSriastradh
110403b705cfSriastradhstatic void
110503b705cfSriastradhtor_blt_span_mono_clipped(struct sna *sna,
110603b705cfSriastradh			  struct sna_composite_spans_op *op,
110703b705cfSriastradh			  pixman_region16_t *clip,
110803b705cfSriastradh			  const BoxRec *box,
110903b705cfSriastradh			  int coverage)
111003b705cfSriastradh{
111103b705cfSriastradh	if (coverage < FAST_SAMPLES_XY/2)
111203b705cfSriastradh		return;
111303b705cfSriastradh
111403b705cfSriastradh	tor_blt_span_clipped(sna, op, clip, box, FAST_SAMPLES_XY);
111503b705cfSriastradh}
111603b705cfSriastradh
111703b705cfSriastradhstatic void
111803b705cfSriastradhtor_blt_span_mono_unbounded(struct sna *sna,
111903b705cfSriastradh			    struct sna_composite_spans_op *op,
112003b705cfSriastradh			    pixman_region16_t *clip,
112103b705cfSriastradh			    const BoxRec *box,
112203b705cfSriastradh			    int coverage)
112303b705cfSriastradh{
112403b705cfSriastradh	tor_blt_span(sna, op, clip, box,
112503b705cfSriastradh		     coverage < FAST_SAMPLES_XY/2 ? 0 : FAST_SAMPLES_XY);
112603b705cfSriastradh}
112703b705cfSriastradh
112803b705cfSriastradhstatic void
112903b705cfSriastradhtor_blt_span_mono_unbounded_clipped(struct sna *sna,
113003b705cfSriastradh				    struct sna_composite_spans_op *op,
113103b705cfSriastradh				    pixman_region16_t *clip,
113203b705cfSriastradh				    const BoxRec *box,
113303b705cfSriastradh				    int coverage)
113403b705cfSriastradh{
113503b705cfSriastradh	tor_blt_span_clipped(sna, op, clip, box,
113603b705cfSriastradh			     coverage < FAST_SAMPLES_XY/2 ? 0 : FAST_SAMPLES_XY);
113703b705cfSriastradh}
113803b705cfSriastradh
113903b705cfSriastradhstatic void
114003b705cfSriastradhtor_blt(struct sna *sna,
114103b705cfSriastradh	struct sna_composite_spans_op *op,
114203b705cfSriastradh	pixman_region16_t *clip,
114303b705cfSriastradh	void (*span)(struct sna *sna,
114403b705cfSriastradh		     struct sna_composite_spans_op *op,
114503b705cfSriastradh		     pixman_region16_t *clip,
114603b705cfSriastradh		     const BoxRec *box,
114703b705cfSriastradh		     int coverage),
114803b705cfSriastradh	struct cell_list *cells,
114903b705cfSriastradh	int y, int height,
115003b705cfSriastradh	int xmin, int xmax,
115103b705cfSriastradh	int unbounded)
115203b705cfSriastradh{
115303b705cfSriastradh	struct cell *cell;
115403b705cfSriastradh	BoxRec box;
115503b705cfSriastradh	int cover;
115603b705cfSriastradh
115703b705cfSriastradh	box.y1 = y;
115803b705cfSriastradh	box.y2 = y + height;
115903b705cfSriastradh	box.x1 = xmin;
116003b705cfSriastradh
116103b705cfSriastradh	/* Form the spans from the coverages and areas. */
116203b705cfSriastradh	cover = 0;
116303b705cfSriastradh	for (cell = cells->head.next; cell != &cells->tail; cell = cell->next) {
116403b705cfSriastradh		int x = cell->x;
116503b705cfSriastradh
116603b705cfSriastradh		assert(x >= xmin);
116703b705cfSriastradh		assert(x < xmax);
116803b705cfSriastradh		__DBG(("%s: cell=(%d, %d, %d), cover=%d, max=%d\n", __FUNCTION__,
116903b705cfSriastradh		       cell->x, cell->covered_height, cell->uncovered_area,
117003b705cfSriastradh		       cover, xmax));
117103b705cfSriastradh
117203b705cfSriastradh		if (cell->covered_height || cell->uncovered_area) {
117303b705cfSriastradh			box.x2 = x;
117403b705cfSriastradh			if (box.x2 > box.x1 && (unbounded || cover)) {
117503b705cfSriastradh				__DBG(("%s: span (%d, %d)x(%d, %d) @ %d\n", __FUNCTION__,
117603b705cfSriastradh				       box.x1, box.y1,
117703b705cfSriastradh				       box.x2 - box.x1,
117803b705cfSriastradh				       box.y2 - box.y1,
117903b705cfSriastradh				       cover));
118003b705cfSriastradh				span(sna, op, clip, &box, cover);
118103b705cfSriastradh			}
118203b705cfSriastradh			box.x1 = box.x2;
118303b705cfSriastradh			cover += cell->covered_height*FAST_SAMPLES_X*2;
118403b705cfSriastradh		}
118503b705cfSriastradh
118603b705cfSriastradh		if (cell->uncovered_area) {
118703b705cfSriastradh			int area = cover - cell->uncovered_area;
118803b705cfSriastradh			box.x2 = x + 1;
118903b705cfSriastradh			if (unbounded || area) {
119003b705cfSriastradh				__DBG(("%s: span (%d, %d)x(%d, %d) @ %d\n", __FUNCTION__,
119103b705cfSriastradh				       box.x1, box.y1,
119203b705cfSriastradh				       box.x2 - box.x1,
119303b705cfSriastradh				       box.y2 - box.y1,
119403b705cfSriastradh				       area));
119503b705cfSriastradh				span(sna, op, clip, &box, area);
119603b705cfSriastradh			}
119703b705cfSriastradh			box.x1 = box.x2;
119803b705cfSriastradh		}
119903b705cfSriastradh	}
120003b705cfSriastradh
120103b705cfSriastradh	box.x2 = xmax;
120203b705cfSriastradh	if (box.x2 > box.x1 && (unbounded || cover)) {
120303b705cfSriastradh		__DBG(("%s: span (%d, %d)x(%d, %d) @ %d\n", __FUNCTION__,
120403b705cfSriastradh		       box.x1, box.y1,
120503b705cfSriastradh		       box.x2 - box.x1,
120603b705cfSriastradh		       box.y2 - box.y1,
120703b705cfSriastradh		       cover));
120803b705cfSriastradh		span(sna, op, clip, &box, cover);
120903b705cfSriastradh	}
121003b705cfSriastradh}
121103b705cfSriastradh
121203b705cfSriastradhstatic void
121303b705cfSriastradhtor_blt_empty(struct sna *sna,
121403b705cfSriastradh	      struct sna_composite_spans_op *op,
121503b705cfSriastradh	      pixman_region16_t *clip,
121603b705cfSriastradh	      void (*span)(struct sna *sna,
121703b705cfSriastradh			   struct sna_composite_spans_op *op,
121803b705cfSriastradh			   pixman_region16_t *clip,
121903b705cfSriastradh			   const BoxRec *box,
122003b705cfSriastradh			   int coverage),
122103b705cfSriastradh	      int y, int height,
122203b705cfSriastradh	      int xmin, int xmax)
122303b705cfSriastradh{
122403b705cfSriastradh	BoxRec box;
122503b705cfSriastradh
122603b705cfSriastradh	box.x1 = xmin;
122703b705cfSriastradh	box.x2 = xmax;
122803b705cfSriastradh	box.y1 = y;
122903b705cfSriastradh	box.y2 = y + height;
123003b705cfSriastradh
123103b705cfSriastradh	span(sna, op, clip, &box, 0);
123203b705cfSriastradh}
123303b705cfSriastradh
123403b705cfSriastradhflatten static void
123503b705cfSriastradhtor_render(struct sna *sna,
123603b705cfSriastradh	   struct tor *converter,
123703b705cfSriastradh	   struct sna_composite_spans_op *op,
123803b705cfSriastradh	   pixman_region16_t *clip,
123903b705cfSriastradh	   void (*span)(struct sna *sna,
124003b705cfSriastradh			struct sna_composite_spans_op *op,
124103b705cfSriastradh			pixman_region16_t *clip,
124203b705cfSriastradh			const BoxRec *box,
124303b705cfSriastradh			int coverage),
124403b705cfSriastradh	   int unbounded)
124503b705cfSriastradh{
124603b705cfSriastradh	int ymin = converter->ymin;
124703b705cfSriastradh	int xmin = converter->xmin;
124803b705cfSriastradh	int xmax = converter->xmax;
124903b705cfSriastradh	int i, j, h = converter->ymax - ymin;
125003b705cfSriastradh	struct polygon *polygon = converter->polygon;
125103b705cfSriastradh	struct cell_list *coverages = converter->coverages;
125203b705cfSriastradh	struct active_list *active = converter->active;
125303b705cfSriastradh	struct edge *buckets[FAST_SAMPLES_Y] = { 0 };
125403b705cfSriastradh
125503b705cfSriastradh	__DBG(("%s: unbounded=%d\n", __FUNCTION__, unbounded));
125603b705cfSriastradh
125703b705cfSriastradh	/* Render each pixel row. */
125803b705cfSriastradh	for (i = 0; i < h; i = j) {
125903b705cfSriastradh		int do_full_step = 0;
126003b705cfSriastradh
126103b705cfSriastradh		j = i + 1;
126203b705cfSriastradh
126303b705cfSriastradh		/* Determine if we can ignore this row or use the full pixel
126403b705cfSriastradh		 * stepper. */
126503b705cfSriastradh		if (!polygon->y_buckets[i]) {
126603b705cfSriastradh			if (active->head.next == &active->tail) {
126703b705cfSriastradh				active->min_height = INT_MAX;
126803b705cfSriastradh				active->is_vertical = 1;
126903b705cfSriastradh				for (; !polygon->y_buckets[j]; j++)
127003b705cfSriastradh					;
127103b705cfSriastradh				__DBG(("%s: no new edges and no exisiting edges, skipping, %d -> %d\n",
127203b705cfSriastradh				       __FUNCTION__, i, j));
127303b705cfSriastradh
127403b705cfSriastradh				if (unbounded)
127503b705cfSriastradh					tor_blt_empty(sna, op, clip, span, i+ymin, j-i, xmin, xmax);
127603b705cfSriastradh				continue;
127703b705cfSriastradh			}
127803b705cfSriastradh
127903b705cfSriastradh			do_full_step = can_full_step(active);
128003b705cfSriastradh		}
128103b705cfSriastradh
128203b705cfSriastradh		__DBG(("%s: y=%d [%d], do_full_step=%d, new edges=%d, min_height=%d, vertical=%d\n",
128303b705cfSriastradh		       __FUNCTION__,
128403b705cfSriastradh		       i, i+ymin, do_full_step,
128503b705cfSriastradh		       polygon->y_buckets[i] != NULL,
128603b705cfSriastradh		       active->min_height,
128703b705cfSriastradh		       active->is_vertical));
128803b705cfSriastradh		if (do_full_step) {
128903b705cfSriastradh			assert(active->is_vertical);
129003b705cfSriastradh			nonzero_row(active, coverages);
129103b705cfSriastradh
129203b705cfSriastradh			while (polygon->y_buckets[j] == NULL &&
129303b705cfSriastradh			       active->min_height >= 2*FAST_SAMPLES_Y)
129403b705cfSriastradh			{
129503b705cfSriastradh				active->min_height -= FAST_SAMPLES_Y;
129603b705cfSriastradh				j++;
129703b705cfSriastradh			}
129803b705cfSriastradh			if (j != i + 1)
129903b705cfSriastradh				step_edges(active, j - (i + 1));
130003b705cfSriastradh
130103b705cfSriastradh			__DBG(("%s: vertical edges, full step (%d, %d)\n",
130203b705cfSriastradh			       __FUNCTION__,  i, j));
130303b705cfSriastradh		} else {
130403b705cfSriastradh			grid_scaled_y_t suby;
130503b705cfSriastradh
130603b705cfSriastradh			fill_buckets(active, polygon->y_buckets[i], buckets);
130703b705cfSriastradh
130803b705cfSriastradh			/* Subsample this row. */
130903b705cfSriastradh			for (suby = 0; suby < FAST_SAMPLES_Y; suby++) {
131003b705cfSriastradh				if (buckets[suby]) {
131103b705cfSriastradh					merge_edges(active, buckets[suby]);
131203b705cfSriastradh					buckets[suby] = NULL;
131303b705cfSriastradh				}
131403b705cfSriastradh
131503b705cfSriastradh				nonzero_subrow(active, coverages);
131603b705cfSriastradh			}
131703b705cfSriastradh		}
131803b705cfSriastradh
131903b705cfSriastradh		if (coverages->head.next != &coverages->tail) {
132003b705cfSriastradh			tor_blt(sna, op, clip, span, coverages,
132103b705cfSriastradh				i+ymin, j-i, xmin, xmax,
132203b705cfSriastradh				unbounded);
132303b705cfSriastradh			cell_list_reset(coverages);
132403b705cfSriastradh		} else if (unbounded)
132503b705cfSriastradh			tor_blt_empty(sna, op, clip, span, i+ymin, j-i, xmin, xmax);
132603b705cfSriastradh
132703b705cfSriastradh		active->min_height -= FAST_SAMPLES_Y;
132803b705cfSriastradh	}
132903b705cfSriastradh}
133003b705cfSriastradh
133103b705cfSriastradhstatic void
133203b705cfSriastradhinplace_row(struct active_list *active, uint8_t *row, int width)
133303b705cfSriastradh{
133403b705cfSriastradh	struct edge *left = active->head.next;
133503b705cfSriastradh
133603b705cfSriastradh	assert(active->is_vertical);
133703b705cfSriastradh
133803b705cfSriastradh	while (&active->tail != left) {
133903b705cfSriastradh		struct edge *right;
134003b705cfSriastradh		int winding = left->dir;
134103b705cfSriastradh		grid_scaled_x_t lfx, rfx;
134203b705cfSriastradh		int lix, rix;
134303b705cfSriastradh
134403b705cfSriastradh		left->height_left -= FAST_SAMPLES_Y;
134503b705cfSriastradh		if (!left->height_left) {
134603b705cfSriastradh			left->prev->next = left->next;
134703b705cfSriastradh			left->next->prev = left->prev;
134803b705cfSriastradh		}
134903b705cfSriastradh
135003b705cfSriastradh		right = left->next;
135103b705cfSriastradh		do {
135203b705cfSriastradh			right->height_left -= FAST_SAMPLES_Y;
135303b705cfSriastradh			if (!right->height_left) {
135403b705cfSriastradh				right->prev->next = right->next;
135503b705cfSriastradh				right->next->prev = right->prev;
135603b705cfSriastradh			}
135703b705cfSriastradh
135803b705cfSriastradh			winding += right->dir;
135903b705cfSriastradh			if (0 == winding && right->x.quo != right->next->x.quo)
136003b705cfSriastradh				break;
136103b705cfSriastradh
136203b705cfSriastradh			right = right->next;
136303b705cfSriastradh		} while (1);
136403b705cfSriastradh
136503b705cfSriastradh		if (left->x.quo < 0) {
136603b705cfSriastradh			lix = lfx = 0;
136703b705cfSriastradh		} else if (left->x.quo >= width * FAST_SAMPLES_X) {
136803b705cfSriastradh			lix = width;
136903b705cfSriastradh			lfx = 0;
137003b705cfSriastradh		} else
137103b705cfSriastradh			FAST_SAMPLES_X_TO_INT_FRAC(left->x.quo, lix, lfx);
137203b705cfSriastradh
137303b705cfSriastradh		if (right->x.quo < 0) {
137403b705cfSriastradh			rix = rfx = 0;
137503b705cfSriastradh		} else if (right->x.quo >= width * FAST_SAMPLES_X) {
137603b705cfSriastradh			rix = width;
137703b705cfSriastradh			rfx = 0;
137803b705cfSriastradh		} else
137903b705cfSriastradh			FAST_SAMPLES_X_TO_INT_FRAC(right->x.quo, rix, rfx);
138003b705cfSriastradh		if (lix == rix) {
138103b705cfSriastradh			if (rfx != lfx) {
138203b705cfSriastradh				assert(lix < width);
138303b705cfSriastradh				row[lix] += (rfx-lfx) * 256 / FAST_SAMPLES_X;
138403b705cfSriastradh			}
138503b705cfSriastradh		} else {
138603b705cfSriastradh			assert(lix < width);
138703b705cfSriastradh			if (lfx == 0)
138803b705cfSriastradh				row[lix] = 0xff;
138903b705cfSriastradh			else
139003b705cfSriastradh				row[lix] += 256 - lfx * 256 / FAST_SAMPLES_X;
139103b705cfSriastradh
139203b705cfSriastradh			assert(rix <= width);
139303b705cfSriastradh			if (rfx) {
139403b705cfSriastradh				assert(rix < width);
139503b705cfSriastradh				row[rix] += rfx * 256 / FAST_SAMPLES_X;
139603b705cfSriastradh			}
139703b705cfSriastradh
139803b705cfSriastradh			if (rix > ++lix) {
139903b705cfSriastradh				uint8_t *r = row + lix;
140003b705cfSriastradh				rix -= lix;
140103b705cfSriastradh#if 0
140203b705cfSriastradh				if (rix == 1)
140303b705cfSriastradh					*row = 0xff;
140403b705cfSriastradh				else
140503b705cfSriastradh					memset(row, 0xff, rix);
140603b705cfSriastradh#else
140703b705cfSriastradh				if ((uintptr_t)r & 1 && rix) {
140803b705cfSriastradh					*r++ = 0xff;
140903b705cfSriastradh					rix--;
141003b705cfSriastradh				}
141103b705cfSriastradh				if ((uintptr_t)r & 2 && rix >= 2) {
141203b705cfSriastradh					*(uint16_t *)r = 0xffff;
141303b705cfSriastradh					r += 2;
141403b705cfSriastradh					rix -= 2;
141503b705cfSriastradh				}
141603b705cfSriastradh				if ((uintptr_t)r & 4 && rix >= 4) {
141703b705cfSriastradh					*(uint32_t *)r = 0xffffffff;
141803b705cfSriastradh					r += 4;
141903b705cfSriastradh					rix -= 4;
142003b705cfSriastradh				}
142103b705cfSriastradh				while (rix >= 8) {
142203b705cfSriastradh					*(uint64_t *)r = 0xffffffffffffffff;
142303b705cfSriastradh					r += 8;
142403b705cfSriastradh					rix -= 8;
142503b705cfSriastradh				}
142603b705cfSriastradh				if (rix & 4) {
142703b705cfSriastradh					*(uint32_t *)r = 0xffffffff;
142803b705cfSriastradh					r += 4;
142903b705cfSriastradh				}
143003b705cfSriastradh				if (rix & 2) {
143103b705cfSriastradh					*(uint16_t *)r = 0xffff;
143203b705cfSriastradh					r += 2;
143303b705cfSriastradh				}
143403b705cfSriastradh				if (rix & 1)
143503b705cfSriastradh					*r = 0xff;
143603b705cfSriastradh#endif
143703b705cfSriastradh			}
143803b705cfSriastradh		}
143903b705cfSriastradh
144003b705cfSriastradh		left = right->next;
144103b705cfSriastradh	}
144203b705cfSriastradh}
144303b705cfSriastradh
144403b705cfSriastradhinline static void
144503b705cfSriastradhinplace_subrow(struct active_list *active, int8_t *row,
144603b705cfSriastradh	       int width, int *min, int *max)
144703b705cfSriastradh{
144803b705cfSriastradh	struct edge *edge = active->head.next;
144903b705cfSriastradh	grid_scaled_x_t prev_x = INT_MIN;
145003b705cfSriastradh	int winding = 0, xstart = INT_MIN;
145103b705cfSriastradh
145203b705cfSriastradh	while (&active->tail != edge) {
145303b705cfSriastradh		struct edge *next = edge->next;
145403b705cfSriastradh
145503b705cfSriastradh		winding += edge->dir;
145603b705cfSriastradh		if (0 == winding) {
145703b705cfSriastradh			if (edge->next->x.quo != edge->x.quo) {
145803b705cfSriastradh				if (edge->x.quo <= xstart) {
145903b705cfSriastradh					xstart = INT_MIN;
146003b705cfSriastradh				} else  {
146103b705cfSriastradh					grid_scaled_x_t fx;
146203b705cfSriastradh					int ix;
146303b705cfSriastradh
146403b705cfSriastradh					if (xstart < FAST_SAMPLES_X * width) {
146503b705cfSriastradh						FAST_SAMPLES_X_TO_INT_FRAC(xstart, ix, fx);
146603b705cfSriastradh						if (ix < *min)
146703b705cfSriastradh							*min = ix;
146803b705cfSriastradh
146903b705cfSriastradh						row[ix++] += FAST_SAMPLES_X - fx;
147003b705cfSriastradh						if (fx && ix < width)
147103b705cfSriastradh							row[ix] += fx;
147203b705cfSriastradh					}
147303b705cfSriastradh
147403b705cfSriastradh					xstart = edge->x.quo;
147503b705cfSriastradh					if (xstart < FAST_SAMPLES_X * width) {
147603b705cfSriastradh						FAST_SAMPLES_X_TO_INT_FRAC(xstart, ix, fx);
147703b705cfSriastradh						row[ix] -= FAST_SAMPLES_X - fx;
147803b705cfSriastradh						if (fx && ix + 1 < width)
147903b705cfSriastradh							row[++ix] -= fx;
148003b705cfSriastradh
148103b705cfSriastradh						if (ix >= *max)
148203b705cfSriastradh							*max = ix + 1;
148303b705cfSriastradh
148403b705cfSriastradh						xstart = INT_MIN;
148503b705cfSriastradh					} else
148603b705cfSriastradh						*max = width;
148703b705cfSriastradh				}
148803b705cfSriastradh			}
148903b705cfSriastradh		} else if (xstart < 0) {
149003b705cfSriastradh			xstart = MAX(edge->x.quo, 0);
149103b705cfSriastradh		}
149203b705cfSriastradh
149303b705cfSriastradh		if (--edge->height_left) {
149403b705cfSriastradh			if (edge->dy) {
149503b705cfSriastradh				edge->x.quo += edge->dxdy.quo;
149603b705cfSriastradh				edge->x.rem += edge->dxdy.rem;
149703b705cfSriastradh				if (edge->x.rem >= 0) {
149803b705cfSriastradh					++edge->x.quo;
149903b705cfSriastradh					edge->x.rem -= edge->dy;
150003b705cfSriastradh				}
150103b705cfSriastradh			}
150203b705cfSriastradh
150303b705cfSriastradh			if (edge->x.quo < prev_x) {
150403b705cfSriastradh				struct edge *pos = edge->prev;
150503b705cfSriastradh				pos->next = next;
150603b705cfSriastradh				next->prev = pos;
150703b705cfSriastradh				do {
150803b705cfSriastradh					pos = pos->prev;
150903b705cfSriastradh				} while (edge->x.quo < pos->x.quo);
151003b705cfSriastradh				pos->next->prev = edge;
151103b705cfSriastradh				edge->next = pos->next;
151203b705cfSriastradh				edge->prev = pos;
151303b705cfSriastradh				pos->next = edge;
151403b705cfSriastradh			} else
151503b705cfSriastradh				prev_x = edge->x.quo;
151603b705cfSriastradh		} else {
151703b705cfSriastradh			edge->prev->next = next;
151803b705cfSriastradh			next->prev = edge->prev;
151903b705cfSriastradh			active->min_height = -1;
152003b705cfSriastradh		}
152103b705cfSriastradh
152203b705cfSriastradh		edge = next;
152303b705cfSriastradh	}
152403b705cfSriastradh}
152503b705cfSriastradh
152603b705cfSriastradhinline static void
152703b705cfSriastradhinplace_end_subrows(struct active_list *active, uint8_t *row,
152803b705cfSriastradh		    int8_t *buf, int width)
152903b705cfSriastradh{
153003b705cfSriastradh	int cover = 0;
153103b705cfSriastradh
153203b705cfSriastradh	while (width >= 4) {
153303b705cfSriastradh		uint32_t dw;
153403b705cfSriastradh		int v;
153503b705cfSriastradh
153603b705cfSriastradh		dw = *(uint32_t *)buf;
153703b705cfSriastradh		buf += 4;
153803b705cfSriastradh
153903b705cfSriastradh		if (dw == 0) {
154003b705cfSriastradh			v = cover * 256 / (FAST_SAMPLES_X * FAST_SAMPLES_Y);
154103b705cfSriastradh			v -= v >> 8;
154203b705cfSriastradh			v |= v << 8;
154303b705cfSriastradh			dw = v | v << 16;
154403b705cfSriastradh		} else {
154503b705cfSriastradh			cover += (int8_t)(dw & 0xff);
154603b705cfSriastradh			if (cover) {
154703b705cfSriastradh				assert(cover > 0);
154803b705cfSriastradh				v = cover * 256 / (FAST_SAMPLES_X * FAST_SAMPLES_Y);
154903b705cfSriastradh				v -= v >> 8;
155003b705cfSriastradh				dw >>= 8;
155103b705cfSriastradh				dw |= v << 24;
155203b705cfSriastradh			} else
155303b705cfSriastradh				dw >>= 8;
155403b705cfSriastradh
155503b705cfSriastradh			cover += (int8_t)(dw & 0xff);
155603b705cfSriastradh			if (cover) {
155703b705cfSriastradh				assert(cover > 0);
155803b705cfSriastradh				v = cover * 256 / (FAST_SAMPLES_X * FAST_SAMPLES_Y);
155903b705cfSriastradh				v -= v >> 8;
156003b705cfSriastradh				dw >>= 8;
156103b705cfSriastradh				dw |= v << 24;
156203b705cfSriastradh			} else
156303b705cfSriastradh				dw >>= 8;
156403b705cfSriastradh
156503b705cfSriastradh			cover += (int8_t)(dw & 0xff);
156603b705cfSriastradh			if (cover) {
156703b705cfSriastradh				assert(cover > 0);
156803b705cfSriastradh				v = cover * 256 / (FAST_SAMPLES_X * FAST_SAMPLES_Y);
156903b705cfSriastradh				v -= v >> 8;
157003b705cfSriastradh				dw >>= 8;
157103b705cfSriastradh				dw |= v << 24;
157203b705cfSriastradh			} else
157303b705cfSriastradh				dw >>= 8;
157403b705cfSriastradh
157503b705cfSriastradh			cover += (int8_t)(dw & 0xff);
157603b705cfSriastradh			if (cover) {
157703b705cfSriastradh				assert(cover > 0);
157803b705cfSriastradh				v = cover * 256 / (FAST_SAMPLES_X * FAST_SAMPLES_Y);
157903b705cfSriastradh				v -= v >> 8;
158003b705cfSriastradh				dw >>= 8;
158103b705cfSriastradh				dw |= v << 24;
158203b705cfSriastradh			} else
158303b705cfSriastradh				dw >>= 8;
158403b705cfSriastradh		}
158503b705cfSriastradh
158603b705cfSriastradh		*(uint32_t *)row = dw;
158703b705cfSriastradh		row += 4;
158803b705cfSriastradh		width -= 4;
158903b705cfSriastradh	}
159003b705cfSriastradh
159103b705cfSriastradh	while (width--) {
159203b705cfSriastradh		int v;
159303b705cfSriastradh
159403b705cfSriastradh		cover += *buf++;
159503b705cfSriastradh		assert(cover >= 0);
159603b705cfSriastradh
159703b705cfSriastradh		v = cover * 256 / (FAST_SAMPLES_X * FAST_SAMPLES_Y);
159803b705cfSriastradh		v -= v >> 8;
159903b705cfSriastradh		*row++ = v;
160003b705cfSriastradh	}
160103b705cfSriastradh}
160203b705cfSriastradh
160303b705cfSriastradh#define TOR_INPLACE_SIZE 128
160403b705cfSriastradhstatic void
160503b705cfSriastradhtor_inplace(struct tor *converter, PixmapPtr scratch, int mono, uint8_t *buf)
160603b705cfSriastradh{
160703b705cfSriastradh	int i, j, h = converter->ymax;
160803b705cfSriastradh	struct polygon *polygon = converter->polygon;
160903b705cfSriastradh	struct active_list *active = converter->active;
161003b705cfSriastradh	struct edge *buckets[FAST_SAMPLES_Y] = { 0 };
161103b705cfSriastradh	uint8_t *row = scratch->devPrivate.ptr;
161203b705cfSriastradh	int stride = scratch->devKind;
161303b705cfSriastradh	int width = scratch->drawable.width;
161403b705cfSriastradh
161503b705cfSriastradh	__DBG(("%s: mono=%d, buf?=%d\n", __FUNCTION__, mono, buf != NULL));
161603b705cfSriastradh	assert(!mono);
161703b705cfSriastradh	assert(converter->ymin == 0);
161803b705cfSriastradh	assert(converter->xmin == 0);
161903b705cfSriastradh	assert(scratch->drawable.depth == 8);
162003b705cfSriastradh
162103b705cfSriastradh	/* Render each pixel row. */
162203b705cfSriastradh	for (i = 0; i < h; i = j) {
162303b705cfSriastradh		int do_full_step = 0;
162403b705cfSriastradh		void *ptr = buf ?: row;
162503b705cfSriastradh
162603b705cfSriastradh		j = i + 1;
162703b705cfSriastradh
162803b705cfSriastradh		/* Determine if we can ignore this row or use the full pixel
162903b705cfSriastradh		 * stepper. */
163003b705cfSriastradh		if (!polygon->y_buckets[i]) {
163103b705cfSriastradh			if (active->head.next == &active->tail) {
163203b705cfSriastradh				active->min_height = INT_MAX;
163303b705cfSriastradh				active->is_vertical = 1;
163403b705cfSriastradh				for (; !polygon->y_buckets[j]; j++)
163503b705cfSriastradh					;
163603b705cfSriastradh				__DBG(("%s: no new edges and no exisiting edges, skipping, %d -> %d\n",
163703b705cfSriastradh				       __FUNCTION__, i, j));
163803b705cfSriastradh
163903b705cfSriastradh				memset(row, 0, stride*(j-i));
164003b705cfSriastradh				row += stride*(j-i);
164103b705cfSriastradh				continue;
164203b705cfSriastradh			}
164303b705cfSriastradh
164403b705cfSriastradh			do_full_step = can_full_step(active);
164503b705cfSriastradh		}
164603b705cfSriastradh
164703b705cfSriastradh		__DBG(("%s: y=%d, do_full_step=%d, new edges=%d, min_height=%d, vertical=%d\n",
164803b705cfSriastradh		       __FUNCTION__,
164903b705cfSriastradh		       i, do_full_step,
165003b705cfSriastradh		       polygon->y_buckets[i] != NULL,
165103b705cfSriastradh		       active->min_height,
165203b705cfSriastradh		       active->is_vertical));
165303b705cfSriastradh		if (do_full_step) {
165403b705cfSriastradh			assert(active->is_vertical);
165503b705cfSriastradh
165603b705cfSriastradh			memset(ptr, 0, width);
165703b705cfSriastradh			inplace_row(active, ptr, width);
165803b705cfSriastradh			if (row != ptr)
165903b705cfSriastradh				memcpy(row, ptr, width);
166003b705cfSriastradh
166103b705cfSriastradh			while (polygon->y_buckets[j] == NULL &&
166203b705cfSriastradh			       active->min_height >= 2*FAST_SAMPLES_Y)
166303b705cfSriastradh			{
166403b705cfSriastradh				active->min_height -= FAST_SAMPLES_Y;
166503b705cfSriastradh				row += stride;
166603b705cfSriastradh				memcpy(row, ptr, width);
166703b705cfSriastradh				j++;
166803b705cfSriastradh			}
166903b705cfSriastradh			if (j != i + 1)
167003b705cfSriastradh				step_edges(active, j - (i + 1));
167103b705cfSriastradh
167203b705cfSriastradh			__DBG(("%s: vertical edges, full step (%d, %d)\n",
167303b705cfSriastradh			       __FUNCTION__,  i, j));
167403b705cfSriastradh		} else {
167503b705cfSriastradh			grid_scaled_y_t suby;
167603b705cfSriastradh			int min = width, max = 0;
167703b705cfSriastradh
167803b705cfSriastradh			fill_buckets(active, polygon->y_buckets[i], buckets);
167903b705cfSriastradh
168003b705cfSriastradh			/* Subsample this row. */
168103b705cfSriastradh			memset(ptr, 0, width);
168203b705cfSriastradh			for (suby = 0; suby < FAST_SAMPLES_Y; suby++) {
168303b705cfSriastradh				if (buckets[suby]) {
168403b705cfSriastradh					merge_edges(active, buckets[suby]);
168503b705cfSriastradh					buckets[suby] = NULL;
168603b705cfSriastradh				}
168703b705cfSriastradh
168803b705cfSriastradh				inplace_subrow(active, ptr, width, &min, &max);
168903b705cfSriastradh			}
169003b705cfSriastradh			assert(min >= 0 && max <= width);
169103b705cfSriastradh			memset(row, 0, min);
169203b705cfSriastradh			if (max > min)
169303b705cfSriastradh				inplace_end_subrows(active, row+min, (int8_t*)ptr+min, max-min);
169403b705cfSriastradh			if (max < width)
169503b705cfSriastradh				memset(row+max, 0, width-max);
169603b705cfSriastradh		}
169703b705cfSriastradh
169803b705cfSriastradh		active->min_height -= FAST_SAMPLES_Y;
169903b705cfSriastradh		row += stride;
170003b705cfSriastradh	}
170103b705cfSriastradh}
170203b705cfSriastradh
170303b705cfSriastradhstruct mono_edge {
170403b705cfSriastradh	struct mono_edge *next, *prev;
170503b705cfSriastradh
170603b705cfSriastradh	int32_t height_left;
170703b705cfSriastradh	int32_t dir;
170803b705cfSriastradh
170903b705cfSriastradh	int32_t dy;
171003b705cfSriastradh	struct quorem x;
171103b705cfSriastradh	struct quorem dxdy;
171203b705cfSriastradh};
171303b705cfSriastradh
171403b705cfSriastradhstruct mono_polygon {
171503b705cfSriastradh	int num_edges;
171603b705cfSriastradh	struct mono_edge *edges;
171703b705cfSriastradh	struct mono_edge **y_buckets;
171803b705cfSriastradh
171903b705cfSriastradh	struct mono_edge *y_buckets_embedded[64];
172003b705cfSriastradh	struct mono_edge edges_embedded[32];
172103b705cfSriastradh};
172203b705cfSriastradh
172303b705cfSriastradhstruct mono {
172403b705cfSriastradh	/* Leftmost edge on the current scan line. */
172503b705cfSriastradh	struct mono_edge head, tail;
172603b705cfSriastradh	int is_vertical;
172703b705cfSriastradh
172803b705cfSriastradh	struct sna *sna;
172903b705cfSriastradh	struct sna_composite_op op;
173003b705cfSriastradh	pixman_region16_t clip;
173103b705cfSriastradh
173203b705cfSriastradh	fastcall void (*span)(struct mono *, int, int, BoxPtr);
173303b705cfSriastradh
173403b705cfSriastradh	struct mono_polygon polygon;
173503b705cfSriastradh};
173603b705cfSriastradh
173703b705cfSriastradh#define I(x) pixman_fixed_to_int ((x) + pixman_fixed_1_minus_e/2)
173803b705cfSriastradh
173903b705cfSriastradhstatic bool
174003b705cfSriastradhmono_polygon_init(struct mono_polygon *polygon, BoxPtr box, int num_edges)
174103b705cfSriastradh{
174203b705cfSriastradh	unsigned h = box->y2 - box->y1;
174303b705cfSriastradh
174403b705cfSriastradh	polygon->y_buckets = polygon->y_buckets_embedded;
174503b705cfSriastradh	if (h > ARRAY_SIZE (polygon->y_buckets_embedded)) {
174603b705cfSriastradh		polygon->y_buckets = malloc (h * sizeof (struct mono_edge *));
174703b705cfSriastradh		if (unlikely (NULL == polygon->y_buckets))
174803b705cfSriastradh			return false;
174903b705cfSriastradh	}
175003b705cfSriastradh
175103b705cfSriastradh	polygon->num_edges = 0;
175203b705cfSriastradh	polygon->edges = polygon->edges_embedded;
175303b705cfSriastradh	if (num_edges > (int)ARRAY_SIZE (polygon->edges_embedded)) {
175403b705cfSriastradh		polygon->edges = malloc (num_edges * sizeof (struct mono_edge));
175503b705cfSriastradh		if (unlikely (polygon->edges == NULL)) {
175603b705cfSriastradh			if (polygon->y_buckets != polygon->y_buckets_embedded)
175703b705cfSriastradh				free(polygon->y_buckets);
175803b705cfSriastradh			return false;
175903b705cfSriastradh		}
176003b705cfSriastradh	}
176103b705cfSriastradh
176203b705cfSriastradh	memset(polygon->y_buckets, 0, h * sizeof (struct edge *));
176303b705cfSriastradh	return true;
176403b705cfSriastradh}
176503b705cfSriastradh
176603b705cfSriastradhstatic void
176703b705cfSriastradhmono_polygon_fini(struct mono_polygon *polygon)
176803b705cfSriastradh{
176903b705cfSriastradh	if (polygon->y_buckets != polygon->y_buckets_embedded)
177003b705cfSriastradh		free(polygon->y_buckets);
177103b705cfSriastradh
177203b705cfSriastradh	if (polygon->edges != polygon->edges_embedded)
177303b705cfSriastradh		free(polygon->edges);
177403b705cfSriastradh}
177503b705cfSriastradh
177603b705cfSriastradhstatic void
177703b705cfSriastradhmono_add_line(struct mono *mono,
177803b705cfSriastradh	      int dst_x, int dst_y,
177903b705cfSriastradh	      xFixed top, xFixed bottom,
178003b705cfSriastradh	      const xPointFixed *p1, const xPointFixed *p2,
178103b705cfSriastradh	      int dir)
178203b705cfSriastradh{
178303b705cfSriastradh	struct mono_polygon *polygon = &mono->polygon;
178403b705cfSriastradh	struct mono_edge *e;
178503b705cfSriastradh	pixman_fixed_t dx;
178603b705cfSriastradh	pixman_fixed_t dy;
178703b705cfSriastradh	int y, ytop, ybot;
178803b705cfSriastradh
178903b705cfSriastradh	__DBG(("%s: top=%d, bottom=%d, line=(%d, %d), (%d, %d) delta=%dx%d, dir=%d\n",
179003b705cfSriastradh	       __FUNCTION__,
179103b705cfSriastradh	       (int)top, (int)bottom,
179203b705cfSriastradh	       (int)p1->x, (int)p1->y, (int)p2->x, (int)p2->y,
179303b705cfSriastradh	       dst_x, dst_y,
179403b705cfSriastradh	       dir));
179503b705cfSriastradh
179603b705cfSriastradh	if (top > bottom) {
179703b705cfSriastradh		const xPointFixed *t;
179803b705cfSriastradh
179903b705cfSriastradh		y = top;
180003b705cfSriastradh		top = bottom;
180103b705cfSriastradh		bottom = y;
180203b705cfSriastradh
180303b705cfSriastradh		t = p1;
180403b705cfSriastradh		p1 = p2;
180503b705cfSriastradh		p2 = t;
180603b705cfSriastradh
180703b705cfSriastradh		dir = -dir;
180803b705cfSriastradh	}
180903b705cfSriastradh
181003b705cfSriastradh	y = I(top) + dst_y;
181103b705cfSriastradh	ytop = MAX(y, mono->clip.extents.y1);
181203b705cfSriastradh
181303b705cfSriastradh	y = I(bottom) + dst_y;
181403b705cfSriastradh	ybot = MIN(y, mono->clip.extents.y2);
181503b705cfSriastradh
181603b705cfSriastradh	if (ybot <= ytop) {
181703b705cfSriastradh		__DBG(("discard clipped line\n"));
181803b705cfSriastradh		return;
181903b705cfSriastradh	}
182003b705cfSriastradh
182103b705cfSriastradh	e = polygon->edges + polygon->num_edges++;
182203b705cfSriastradh	e->height_left = ybot - ytop;
182303b705cfSriastradh	e->dir = dir;
182403b705cfSriastradh
182503b705cfSriastradh	dx = p2->x - p1->x;
182603b705cfSriastradh	dy = p2->y - p1->y;
182703b705cfSriastradh
182803b705cfSriastradh	if (dx == 0) {
182903b705cfSriastradh		e->x.quo = p1->x;
183003b705cfSriastradh		e->x.rem = 0;
183103b705cfSriastradh		e->dxdy.quo = 0;
183203b705cfSriastradh		e->dxdy.rem = 0;
183303b705cfSriastradh		e->dy = 0;
183403b705cfSriastradh	} else {
183503b705cfSriastradh		e->dxdy = floored_muldivrem (dx, pixman_fixed_1, dy);
183603b705cfSriastradh		e->dy = dy;
183703b705cfSriastradh
183803b705cfSriastradh		e->x = floored_muldivrem ((ytop-dst_y) * pixman_fixed_1 + pixman_fixed_1_minus_e/2 - p1->y,
183903b705cfSriastradh					  dx, dy);
184003b705cfSriastradh		e->x.quo += p1->x;
184103b705cfSriastradh		e->x.rem -= dy;
184203b705cfSriastradh	}
184303b705cfSriastradh	e->x.quo += dst_x*pixman_fixed_1;
184403b705cfSriastradh
184503b705cfSriastradh	{
184603b705cfSriastradh		struct mono_edge **ptail = &polygon->y_buckets[ytop - mono->clip.extents.y1];
184703b705cfSriastradh		if (*ptail)
184803b705cfSriastradh			(*ptail)->prev = e;
184903b705cfSriastradh		e->next = *ptail;
185003b705cfSriastradh		e->prev = NULL;
185103b705cfSriastradh		*ptail = e;
185203b705cfSriastradh	}
185303b705cfSriastradh}
185403b705cfSriastradh
185503b705cfSriastradhstatic struct mono_edge *
185603b705cfSriastradhmono_merge_sorted_edges(struct mono_edge *head_a, struct mono_edge *head_b)
185703b705cfSriastradh{
185803b705cfSriastradh	struct mono_edge *head, **next, *prev;
185903b705cfSriastradh	int32_t x;
186003b705cfSriastradh
186103b705cfSriastradh	if (head_b == NULL)
186203b705cfSriastradh		return head_a;
186303b705cfSriastradh
186403b705cfSriastradh	prev = head_a->prev;
186503b705cfSriastradh	next = &head;
186603b705cfSriastradh	if (head_a->x.quo <= head_b->x.quo) {
186703b705cfSriastradh		head = head_a;
186803b705cfSriastradh	} else {
186903b705cfSriastradh		head = head_b;
187003b705cfSriastradh		head_b->prev = prev;
187103b705cfSriastradh		goto start_with_b;
187203b705cfSriastradh	}
187303b705cfSriastradh
187403b705cfSriastradh	do {
187503b705cfSriastradh		x = head_b->x.quo;
187603b705cfSriastradh		while (head_a != NULL && head_a->x.quo <= x) {
187703b705cfSriastradh			prev = head_a;
187803b705cfSriastradh			next = &head_a->next;
187903b705cfSriastradh			head_a = head_a->next;
188003b705cfSriastradh		}
188103b705cfSriastradh
188203b705cfSriastradh		head_b->prev = prev;
188303b705cfSriastradh		*next = head_b;
188403b705cfSriastradh		if (head_a == NULL)
188503b705cfSriastradh			return head;
188603b705cfSriastradh
188703b705cfSriastradhstart_with_b:
188803b705cfSriastradh		x = head_a->x.quo;
188903b705cfSriastradh		while (head_b != NULL && head_b->x.quo <= x) {
189003b705cfSriastradh			prev = head_b;
189103b705cfSriastradh			next = &head_b->next;
189203b705cfSriastradh			head_b = head_b->next;
189303b705cfSriastradh		}
189403b705cfSriastradh
189503b705cfSriastradh		head_a->prev = prev;
189603b705cfSriastradh		*next = head_a;
189703b705cfSriastradh		if (head_b == NULL)
189803b705cfSriastradh			return head;
189903b705cfSriastradh	} while (1);
190003b705cfSriastradh}
190103b705cfSriastradh
190203b705cfSriastradhstatic struct mono_edge *
190303b705cfSriastradhmono_sort_edges(struct mono_edge *list,
190403b705cfSriastradh		unsigned int level,
190503b705cfSriastradh		struct mono_edge **head_out)
190603b705cfSriastradh{
190703b705cfSriastradh	struct mono_edge *head_other, *remaining;
190803b705cfSriastradh	unsigned int i;
190903b705cfSriastradh
191003b705cfSriastradh	head_other = list->next;
191103b705cfSriastradh
191203b705cfSriastradh	if (head_other == NULL) {
191303b705cfSriastradh		*head_out = list;
191403b705cfSriastradh		return NULL;
191503b705cfSriastradh	}
191603b705cfSriastradh
191703b705cfSriastradh	remaining = head_other->next;
191803b705cfSriastradh	if (list->x.quo <= head_other->x.quo) {
191903b705cfSriastradh		*head_out = list;
192003b705cfSriastradh		head_other->next = NULL;
192103b705cfSriastradh	} else {
192203b705cfSriastradh		*head_out = head_other;
192303b705cfSriastradh		head_other->prev = list->prev;
192403b705cfSriastradh		head_other->next = list;
192503b705cfSriastradh		list->prev = head_other;
192603b705cfSriastradh		list->next = NULL;
192703b705cfSriastradh	}
192803b705cfSriastradh
192903b705cfSriastradh	for (i = 0; i < level && remaining; i++) {
193003b705cfSriastradh		remaining = mono_sort_edges(remaining, i, &head_other);
193103b705cfSriastradh		*head_out = mono_merge_sorted_edges(*head_out, head_other);
193203b705cfSriastradh	}
193303b705cfSriastradh
193403b705cfSriastradh	return remaining;
193503b705cfSriastradh}
193603b705cfSriastradh
193703b705cfSriastradhstatic struct mono_edge *mono_filter(struct mono_edge *edges)
193803b705cfSriastradh{
193903b705cfSriastradh	struct mono_edge *e;
194003b705cfSriastradh
194103b705cfSriastradh	e = edges;
194203b705cfSriastradh	do {
194303b705cfSriastradh		struct mono_edge *n = e->next;
194403b705cfSriastradh		if (e->dir == -n->dir &&
194503b705cfSriastradh		    e->height_left == n->height_left &&
194603b705cfSriastradh		    *(uint64_t *)&e->x == *(uint64_t *)&n->x &&
194703b705cfSriastradh		    *(uint64_t *)&e->dxdy == *(uint64_t *)&n->dxdy) {
194803b705cfSriastradh			if (e->prev)
194903b705cfSriastradh				e->prev->next = n->next;
195003b705cfSriastradh			else
195103b705cfSriastradh				edges = n->next;
195203b705cfSriastradh			if (n->next)
195303b705cfSriastradh				n->next->prev = e->prev;
195403b705cfSriastradh			else
195503b705cfSriastradh				break;
195603b705cfSriastradh
195703b705cfSriastradh			e = n->next;
195803b705cfSriastradh		} else
195903b705cfSriastradh			e = e->next;
196003b705cfSriastradh	} while (e->next);
196103b705cfSriastradh
196203b705cfSriastradh	return edges;
196303b705cfSriastradh}
196403b705cfSriastradh
196503b705cfSriastradhstatic struct mono_edge *
196603b705cfSriastradhmono_merge_unsorted_edges(struct mono_edge *head, struct mono_edge *unsorted)
196703b705cfSriastradh{
196803b705cfSriastradh	mono_sort_edges(unsorted, UINT_MAX, &unsorted);
196903b705cfSriastradh	return mono_merge_sorted_edges(head, mono_filter(unsorted));
197003b705cfSriastradh}
197103b705cfSriastradh
197203b705cfSriastradh#if 0
197303b705cfSriastradhstatic inline void
197403b705cfSriastradh__dbg_mono_edges(const char *function, struct mono_edge *edges)
197503b705cfSriastradh{
197603b705cfSriastradh	ErrorF("%s: ", function);
197703b705cfSriastradh	while (edges) {
197803b705cfSriastradh		if (edges->x.quo < INT16_MAX << 16) {
197903b705cfSriastradh			ErrorF("(%d.%06d)+(%d.%06d)x%d, ",
198003b705cfSriastradh			       edges->x.quo, edges->x.rem,
198103b705cfSriastradh			       edges->dxdy.quo, edges->dxdy.rem,
198203b705cfSriastradh			       edges->dy*edges->dir);
198303b705cfSriastradh		}
198403b705cfSriastradh		edges = edges->next;
198503b705cfSriastradh	}
198603b705cfSriastradh	ErrorF("\n");
198703b705cfSriastradh}
198803b705cfSriastradh#define DBG_MONO_EDGES(x) __dbg_mono_edges(__FUNCTION__, x)
198903b705cfSriastradhstatic inline void
199003b705cfSriastradhVALIDATE_MONO_EDGES(struct mono_edge *edges)
199103b705cfSriastradh{
199203b705cfSriastradh	int prev_x = edges->x.quo;
199303b705cfSriastradh	while ((edges = edges->next)) {
199403b705cfSriastradh		assert(edges->x.quo >= prev_x);
199503b705cfSriastradh		prev_x = edges->x.quo;
199603b705cfSriastradh	}
199703b705cfSriastradh}
199803b705cfSriastradh
199903b705cfSriastradh#else
200003b705cfSriastradh#define DBG_MONO_EDGES(x)
200103b705cfSriastradh#define VALIDATE_MONO_EDGES(x)
200203b705cfSriastradh#endif
200303b705cfSriastradh
200403b705cfSriastradhinline static void
200503b705cfSriastradhmono_merge_edges(struct mono *c, struct mono_edge *edges)
200603b705cfSriastradh{
200703b705cfSriastradh	struct mono_edge *e;
200803b705cfSriastradh
200903b705cfSriastradh	DBG_MONO_EDGES(edges);
201003b705cfSriastradh
201103b705cfSriastradh	for (e = edges; c->is_vertical && e; e = e->next)
201203b705cfSriastradh		c->is_vertical = e->dy == 0;
201303b705cfSriastradh
201403b705cfSriastradh	c->head.next = mono_merge_unsorted_edges(c->head.next, edges);
201503b705cfSriastradh}
201603b705cfSriastradh
201703b705cfSriastradhfastcall static void
201803b705cfSriastradhmono_span(struct mono *c, int x1, int x2, BoxPtr box)
201903b705cfSriastradh{
202003b705cfSriastradh	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
202103b705cfSriastradh
202203b705cfSriastradh	box->x1 = x1;
202303b705cfSriastradh	box->x2 = x2;
202403b705cfSriastradh
202503b705cfSriastradh	if (c->clip.data) {
202603b705cfSriastradh		pixman_region16_t region;
202703b705cfSriastradh
202803b705cfSriastradh		pixman_region_init_rects(&region, box, 1);
202903b705cfSriastradh		RegionIntersect(&region, &region, &c->clip);
203003b705cfSriastradh		if (REGION_NUM_RECTS(&region)) {
203103b705cfSriastradh			c->op.boxes(c->sna, &c->op,
203203b705cfSriastradh				    REGION_RECTS(&region),
203303b705cfSriastradh				    REGION_NUM_RECTS(&region));
203403b705cfSriastradh			apply_damage(&c->op, &region);
203503b705cfSriastradh		}
203603b705cfSriastradh		pixman_region_fini(&region);
203703b705cfSriastradh	} else {
203803b705cfSriastradh		c->op.box(c->sna, &c->op, box);
203903b705cfSriastradh		apply_damage_box(&c->op, box);
204003b705cfSriastradh	}
204103b705cfSriastradh}
204203b705cfSriastradh
204303b705cfSriastradhfastcall static void
204403b705cfSriastradhmono_span__fast(struct mono *c, int x1, int x2, BoxPtr box)
204503b705cfSriastradh{
204603b705cfSriastradh	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
204703b705cfSriastradh
204803b705cfSriastradh	box->x1 = x1;
204903b705cfSriastradh	box->x2 = x2;
205003b705cfSriastradh
205103b705cfSriastradh	c->op.box(c->sna, &c->op, box);
205203b705cfSriastradh}
205303b705cfSriastradh
205403b705cfSriastradhstruct mono_span_thread_boxes {
205503b705cfSriastradh	const struct sna_composite_op *op;
205603b705cfSriastradh#define MONO_SPAN_MAX_BOXES (8192/sizeof(BoxRec))
205703b705cfSriastradh	BoxRec boxes[MONO_SPAN_MAX_BOXES];
205803b705cfSriastradh	int num_boxes;
205903b705cfSriastradh};
206003b705cfSriastradh
206103b705cfSriastradhinline static void
206203b705cfSriastradhthread_mono_span_add_boxes(struct mono *c, const BoxRec *box, int count)
206303b705cfSriastradh{
206403b705cfSriastradh	struct mono_span_thread_boxes *b = c->op.priv;
206503b705cfSriastradh
206603b705cfSriastradh	assert(count > 0 && count <= MONO_SPAN_MAX_BOXES);
206703b705cfSriastradh	if (unlikely(b->num_boxes + count > MONO_SPAN_MAX_BOXES)) {
206803b705cfSriastradh		b->op->thread_boxes(c->sna, b->op, b->boxes, b->num_boxes);
206903b705cfSriastradh		b->num_boxes = 0;
207003b705cfSriastradh	}
207103b705cfSriastradh
207203b705cfSriastradh	memcpy(b->boxes + b->num_boxes, box, count*sizeof(BoxRec));
207303b705cfSriastradh	b->num_boxes += count;
207403b705cfSriastradh	assert(b->num_boxes <= MONO_SPAN_MAX_BOXES);
207503b705cfSriastradh}
207603b705cfSriastradh
207703b705cfSriastradhfastcall static void
207803b705cfSriastradhthread_mono_span_clipped(struct mono *c, int x1, int x2, BoxPtr box)
207903b705cfSriastradh{
208003b705cfSriastradh	pixman_region16_t region;
208103b705cfSriastradh
208203b705cfSriastradh	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
208303b705cfSriastradh
208403b705cfSriastradh	box->x1 = x1;
208503b705cfSriastradh	box->x2 = x2;
208603b705cfSriastradh
208703b705cfSriastradh	assert(c->clip.data);
208803b705cfSriastradh
208903b705cfSriastradh	pixman_region_init_rects(&region, box, 1);
209003b705cfSriastradh	RegionIntersect(&region, &region, &c->clip);
209103b705cfSriastradh	if (REGION_NUM_RECTS(&region))
209203b705cfSriastradh		thread_mono_span_add_boxes(c,
209303b705cfSriastradh					   REGION_RECTS(&region),
209403b705cfSriastradh					   REGION_NUM_RECTS(&region));
209503b705cfSriastradh	pixman_region_fini(&region);
209603b705cfSriastradh}
209703b705cfSriastradh
209803b705cfSriastradhfastcall static void
209903b705cfSriastradhthread_mono_span(struct mono *c, int x1, int x2, BoxPtr box)
210003b705cfSriastradh{
210103b705cfSriastradh	__DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2));
210203b705cfSriastradh
210303b705cfSriastradh	box->x1 = x1;
210403b705cfSriastradh	box->x2 = x2;
210503b705cfSriastradh	thread_mono_span_add_boxes(c, box, 1);
210603b705cfSriastradh}
210703b705cfSriastradh
210803b705cfSriastradhinline static void
210903b705cfSriastradhmono_row(struct mono *c, int16_t y, int16_t h)
211003b705cfSriastradh{
211103b705cfSriastradh	struct mono_edge *edge = c->head.next;
211203b705cfSriastradh	int prev_x = INT_MIN;
211303b705cfSriastradh	int16_t xstart = INT16_MIN;
211403b705cfSriastradh	int winding = 0;
211503b705cfSriastradh	BoxRec box;
211603b705cfSriastradh
211703b705cfSriastradh	DBG_MONO_EDGES(edge);
211803b705cfSriastradh	VALIDATE_MONO_EDGES(&c->head);
211903b705cfSriastradh
212003b705cfSriastradh	box.y1 = c->clip.extents.y1 + y;
212103b705cfSriastradh	box.y2 = box.y1 + h;
212203b705cfSriastradh
212303b705cfSriastradh	while (&c->tail != edge) {
212403b705cfSriastradh		struct mono_edge *next = edge->next;
212503b705cfSriastradh		int16_t xend = I(edge->x.quo);
212603b705cfSriastradh
212703b705cfSriastradh		if (--edge->height_left) {
212803b705cfSriastradh			if (edge->dy) {
212903b705cfSriastradh				edge->x.quo += edge->dxdy.quo;
213003b705cfSriastradh				edge->x.rem += edge->dxdy.rem;
213103b705cfSriastradh				if (edge->x.rem >= 0) {
213203b705cfSriastradh					++edge->x.quo;
213303b705cfSriastradh					edge->x.rem -= edge->dy;
213403b705cfSriastradh				}
213503b705cfSriastradh			}
213603b705cfSriastradh
213703b705cfSriastradh			if (edge->x.quo < prev_x) {
213803b705cfSriastradh				struct mono_edge *pos = edge->prev;
213903b705cfSriastradh				pos->next = next;
214003b705cfSriastradh				next->prev = pos;
214103b705cfSriastradh				do {
214203b705cfSriastradh					pos = pos->prev;
214303b705cfSriastradh				} while (edge->x.quo < pos->x.quo);
214403b705cfSriastradh				pos->next->prev = edge;
214503b705cfSriastradh				edge->next = pos->next;
214603b705cfSriastradh				edge->prev = pos;
214703b705cfSriastradh				pos->next = edge;
214803b705cfSriastradh			} else
214903b705cfSriastradh				prev_x = edge->x.quo;
215003b705cfSriastradh		} else {
215103b705cfSriastradh			edge->prev->next = next;
215203b705cfSriastradh			next->prev = edge->prev;
215303b705cfSriastradh		}
215403b705cfSriastradh
215503b705cfSriastradh		winding += edge->dir;
215603b705cfSriastradh		if (winding == 0) {
215703b705cfSriastradh			assert(I(next->x.quo) >= xend);
215803b705cfSriastradh			if (I(next->x.quo) > xend + 1) {
215903b705cfSriastradh				if (xstart < c->clip.extents.x1)
216003b705cfSriastradh					xstart = c->clip.extents.x1;
216103b705cfSriastradh				if (xend > c->clip.extents.x2)
216203b705cfSriastradh					xend = c->clip.extents.x2;
216303b705cfSriastradh				if (xend > xstart)
216403b705cfSriastradh					c->span(c, xstart, xend, &box);
216503b705cfSriastradh				xstart = INT16_MIN;
216603b705cfSriastradh			}
216703b705cfSriastradh		} else if (xstart == INT16_MIN)
216803b705cfSriastradh			xstart = xend;
216903b705cfSriastradh
217003b705cfSriastradh		edge = next;
217103b705cfSriastradh	}
217203b705cfSriastradh
217303b705cfSriastradh	DBG_MONO_EDGES(c->head.next);
217403b705cfSriastradh	VALIDATE_MONO_EDGES(&c->head);
217503b705cfSriastradh}
217603b705cfSriastradh
217703b705cfSriastradhstatic bool
217803b705cfSriastradhmono_init(struct mono *c, int num_edges)
217903b705cfSriastradh{
218003b705cfSriastradh	if (!mono_polygon_init(&c->polygon, &c->clip.extents, num_edges))
218103b705cfSriastradh		return false;
218203b705cfSriastradh
218303b705cfSriastradh	c->head.dy = 0;
218403b705cfSriastradh	c->head.height_left = INT_MAX;
218503b705cfSriastradh	c->head.x.quo = INT16_MIN << 16;
218603b705cfSriastradh	c->head.prev = NULL;
218703b705cfSriastradh	c->head.next = &c->tail;
218803b705cfSriastradh	c->tail.prev = &c->head;
218903b705cfSriastradh	c->tail.next = NULL;
219003b705cfSriastradh	c->tail.x.quo = INT16_MAX << 16;
219103b705cfSriastradh	c->tail.height_left = INT_MAX;
219203b705cfSriastradh	c->tail.dy = 0;
219303b705cfSriastradh
219403b705cfSriastradh	c->is_vertical = 1;
219503b705cfSriastradh
219603b705cfSriastradh	return true;
219703b705cfSriastradh}
219803b705cfSriastradh
219903b705cfSriastradhstatic void
220003b705cfSriastradhmono_fini(struct mono *mono)
220103b705cfSriastradh{
220203b705cfSriastradh	mono_polygon_fini(&mono->polygon);
220303b705cfSriastradh}
220403b705cfSriastradh
220503b705cfSriastradhstatic void
220603b705cfSriastradhmono_step_edges(struct mono *c, int count)
220703b705cfSriastradh{
220803b705cfSriastradh	struct mono_edge *edge;
220903b705cfSriastradh
221003b705cfSriastradh	for (edge = c->head.next; edge != &c->tail; edge = edge->next) {
221103b705cfSriastradh		edge->height_left -= count;
221203b705cfSriastradh		if (! edge->height_left) {
221303b705cfSriastradh			edge->prev->next = edge->next;
221403b705cfSriastradh			edge->next->prev = edge->prev;
221503b705cfSriastradh		}
221603b705cfSriastradh	}
221703b705cfSriastradh}
221803b705cfSriastradh
221903b705cfSriastradhflatten static void
222003b705cfSriastradhmono_render(struct mono *mono)
222103b705cfSriastradh{
222203b705cfSriastradh	struct mono_polygon *polygon = &mono->polygon;
222303b705cfSriastradh	int i, j, h = mono->clip.extents.y2 - mono->clip.extents.y1;
222403b705cfSriastradh
222503b705cfSriastradh	assert(mono->span);
222603b705cfSriastradh
222703b705cfSriastradh	for (i = 0; i < h; i = j) {
222803b705cfSriastradh		j = i + 1;
222903b705cfSriastradh
223003b705cfSriastradh		if (polygon->y_buckets[i])
223103b705cfSriastradh			mono_merge_edges(mono, polygon->y_buckets[i]);
223203b705cfSriastradh
223303b705cfSriastradh		if (mono->is_vertical) {
223403b705cfSriastradh			struct mono_edge *e = mono->head.next;
223503b705cfSriastradh			int min_height = h - i;
223603b705cfSriastradh
223703b705cfSriastradh			while (e != &mono->tail) {
223803b705cfSriastradh				if (e->height_left < min_height)
223903b705cfSriastradh					min_height = e->height_left;
224003b705cfSriastradh				e = e->next;
224103b705cfSriastradh			}
224203b705cfSriastradh
224303b705cfSriastradh			while (--min_height >= 1 && polygon->y_buckets[j] == NULL)
224403b705cfSriastradh				j++;
224503b705cfSriastradh			if (j != i + 1)
224603b705cfSriastradh				mono_step_edges(mono, j - (i + 1));
224703b705cfSriastradh		}
224803b705cfSriastradh
224903b705cfSriastradh		mono_row(mono, i, j-i);
225003b705cfSriastradh
225103b705cfSriastradh		/* XXX recompute after dropping edges? */
225203b705cfSriastradh		if (mono->head.next == &mono->tail)
225303b705cfSriastradh			mono->is_vertical = 1;
225403b705cfSriastradh	}
225503b705cfSriastradh}
225603b705cfSriastradh
225703b705cfSriastradhstatic int operator_is_bounded(uint8_t op)
225803b705cfSriastradh{
225903b705cfSriastradh	switch (op) {
226003b705cfSriastradh	case PictOpOver:
226103b705cfSriastradh	case PictOpOutReverse:
226203b705cfSriastradh	case PictOpAdd:
226303b705cfSriastradh		return true;
226403b705cfSriastradh	default:
226503b705cfSriastradh		return false;
226603b705cfSriastradh	}
226703b705cfSriastradh}
226803b705cfSriastradh
226903b705cfSriastradhinline static xFixed
227003b705cfSriastradhline_x_for_y(const xLineFixed *l, xFixed y, bool ceil)
227103b705cfSriastradh{
227203b705cfSriastradh	xFixed_32_32 ex = (xFixed_32_32)(y - l->p1.y) * (l->p2.x - l->p1.x);
227303b705cfSriastradh	xFixed d = l->p2.y - l->p1.y;
227403b705cfSriastradh
227503b705cfSriastradh	if (ceil)
227603b705cfSriastradh		ex += (d - 1);
227703b705cfSriastradh
227803b705cfSriastradh	return l->p1.x + (xFixed) (ex / d);
227903b705cfSriastradh}
228003b705cfSriastradh
228103b705cfSriastradh#define pixman_fixed_integer_floor(V) pixman_fixed_to_int(V)
228203b705cfSriastradh#define pixman_fixed_integer_ceil(V) pixman_fixed_to_int(pixman_fixed_ceil(V))
228303b705cfSriastradh
228403b705cfSriastradhstatic void
228503b705cfSriastradhtrapezoids_bounds(int n, const xTrapezoid *t, BoxPtr box)
228603b705cfSriastradh{
228703b705cfSriastradh	xFixed x1, y1, x2, y2;
228803b705cfSriastradh
228903b705cfSriastradh	/* XXX need 33 bits... */
229003b705cfSriastradh	x1 = y1 = INT_MAX / 2;
229103b705cfSriastradh	x2 = y2 = INT_MIN / 2;
229203b705cfSriastradh
229303b705cfSriastradh	do {
229403b705cfSriastradh		xFixed fx1, fx2, v;
229503b705cfSriastradh
229603b705cfSriastradh		if (!xTrapezoidValid(t))
229703b705cfSriastradh			continue;
229803b705cfSriastradh
229903b705cfSriastradh		if (t->top < y1)
230003b705cfSriastradh			y1 = t->top;
230103b705cfSriastradh		if (t->bottom > y2)
230203b705cfSriastradh			y2 = t->bottom;
230303b705cfSriastradh
230403b705cfSriastradh		if (((t->left.p1.x - x1) | (t->left.p2.x - x1)) < 0) {
230503b705cfSriastradh			if (pixman_fixed_floor(t->left.p1.x) == pixman_fixed_floor(t->left.p2.x)) {
230603b705cfSriastradh				x1 = pixman_fixed_floor(t->left.p1.x);
230703b705cfSriastradh			} else {
230803b705cfSriastradh				if (t->left.p1.y == t->top)
230903b705cfSriastradh					fx1 = t->left.p1.x;
231003b705cfSriastradh				else
231103b705cfSriastradh					fx1 = line_x_for_y(&t->left, t->top, false);
231203b705cfSriastradh
231303b705cfSriastradh				if (t->left.p2.y == t->bottom)
231403b705cfSriastradh					fx2 = t->left.p2.x;
231503b705cfSriastradh				else
231603b705cfSriastradh					fx2 = line_x_for_y(&t->left, t->bottom, false);
231703b705cfSriastradh
231803b705cfSriastradh				v = min(fx1, fx2);
231903b705cfSriastradh				if (v < x1)
232003b705cfSriastradh					x1 = pixman_fixed_floor(v);
232103b705cfSriastradh			}
232203b705cfSriastradh		}
232303b705cfSriastradh
232403b705cfSriastradh		if (((x2 - t->right.p1.x) | (x2 - t->right.p2.x)) < 0) {
232503b705cfSriastradh			if (pixman_fixed_floor(t->right.p1.x) == pixman_fixed_floor(t->right.p2.x)) {
232603b705cfSriastradh				x2 = pixman_fixed_ceil(t->right.p1.x);
232703b705cfSriastradh			} else {
232803b705cfSriastradh				if (t->right.p1.y == t->top)
232903b705cfSriastradh					fx1 = t->right.p1.x;
233003b705cfSriastradh				else
233103b705cfSriastradh					fx1 = line_x_for_y(&t->right, t->top, true);
233203b705cfSriastradh
233303b705cfSriastradh				if (t->right.p2.y == t->bottom)
233403b705cfSriastradh					fx2 = t->right.p2.x;
233503b705cfSriastradh				else
233603b705cfSriastradh					fx2 = line_x_for_y(&t->right, t->bottom, true);
233703b705cfSriastradh
233803b705cfSriastradh				v = max(fx1, fx2);
233903b705cfSriastradh				if (v > x2)
234003b705cfSriastradh					x2 = pixman_fixed_ceil(v);
234103b705cfSriastradh			}
234203b705cfSriastradh		}
234303b705cfSriastradh	} while (t++, --n);
234403b705cfSriastradh
234503b705cfSriastradh	box->x1 = pixman_fixed_to_int(x1);
234603b705cfSriastradh	box->x2 = pixman_fixed_to_int(x2);
234703b705cfSriastradh	box->y1 = pixman_fixed_integer_floor(y1);
234803b705cfSriastradh	box->y2 = pixman_fixed_integer_ceil(y2);
234903b705cfSriastradh}
235003b705cfSriastradh
235103b705cfSriastradhstatic bool
235203b705cfSriastradhis_mono(PicturePtr dst, PictFormatPtr mask)
235303b705cfSriastradh{
235403b705cfSriastradh	return mask ? mask->depth < 8 : dst->polyEdge==PolyEdgeSharp;
235503b705cfSriastradh}
235603b705cfSriastradh
235703b705cfSriastradhstatic bool
235803b705cfSriastradhtrapezoids_inplace_fallback(struct sna *sna,
235903b705cfSriastradh			    CARD8 op,
236003b705cfSriastradh			    PicturePtr src, PicturePtr dst, PictFormatPtr mask,
236103b705cfSriastradh			    int ntrap, xTrapezoid *traps)
236203b705cfSriastradh{
236303b705cfSriastradh	pixman_image_t *image;
236403b705cfSriastradh	BoxRec box;
236503b705cfSriastradh	uint32_t color;
236603b705cfSriastradh	int dx, dy;
236703b705cfSriastradh
236803b705cfSriastradh	if (op != PictOpAdd)
236903b705cfSriastradh		return false;
237003b705cfSriastradh
237103b705cfSriastradh	if (is_mono(dst, mask)) {
237203b705cfSriastradh		if (dst->format != PICT_a1)
237303b705cfSriastradh			return false;
237403b705cfSriastradh	} else {
237503b705cfSriastradh		if (dst->format != PICT_a8)
237603b705cfSriastradh			return false;
237703b705cfSriastradh	}
237803b705cfSriastradh
237903b705cfSriastradh	if (!sna_picture_is_solid(src, &color) || (color >> 24) != 0xff) {
238003b705cfSriastradh		DBG(("%s: not an opaque solid source\n", __FUNCTION__));
238103b705cfSriastradh		return false;
238203b705cfSriastradh	}
238303b705cfSriastradh
238403b705cfSriastradh	box.x1 = dst->pDrawable->x;
238503b705cfSriastradh	box.y1 = dst->pDrawable->y;
238603b705cfSriastradh	box.x2 = dst->pDrawable->width;
238703b705cfSriastradh	box.y2 = dst->pDrawable->height;
238803b705cfSriastradh	if (pixman_region_contains_rectangle(dst->pCompositeClip,
238903b705cfSriastradh					     &box) != PIXMAN_REGION_IN) {
239003b705cfSriastradh		DBG(("%s: requires clipping, drawable (%d,%d), (%d, %d), clip (%d, %d), (%d, %d)\n", __FUNCTION__,
239103b705cfSriastradh		     box.x1, box.y1, box.x2, box.y2,
239203b705cfSriastradh		     dst->pCompositeClip->extents.x1,
239303b705cfSriastradh		     dst->pCompositeClip->extents.y1,
239403b705cfSriastradh		     dst->pCompositeClip->extents.x2,
239503b705cfSriastradh		     dst->pCompositeClip->extents.y2));
239603b705cfSriastradh		return false;
239703b705cfSriastradh	}
239803b705cfSriastradh
239903b705cfSriastradh	if (is_gpu(sna, dst->pDrawable, PREFER_GPU_SPANS)) {
240003b705cfSriastradh		DBG(("%s: not performing inplace as dst is already on the GPU\n",
240103b705cfSriastradh		     __FUNCTION__));
240203b705cfSriastradh		return false;
240303b705cfSriastradh	}
240403b705cfSriastradh
240503b705cfSriastradh	DBG(("%s\n", __FUNCTION__));
240603b705cfSriastradh
240703b705cfSriastradh	image = NULL;
240803b705cfSriastradh	if (sna_drawable_move_to_cpu(dst->pDrawable, MOVE_READ | MOVE_WRITE))
240903b705cfSriastradh		image = image_from_pict(dst, false, &dx, &dy);
241003b705cfSriastradh	if (image) {
241103b705cfSriastradh		dx += dst->pDrawable->x;
241203b705cfSriastradh		dy += dst->pDrawable->y;
241303b705cfSriastradh
241403b705cfSriastradh		for (; ntrap; ntrap--, traps++)
241503b705cfSriastradh			pixman_rasterize_trapezoid(image,
241603b705cfSriastradh						   (pixman_trapezoid_t *)traps,
241703b705cfSriastradh						   dx, dy);
241803b705cfSriastradh
241903b705cfSriastradh		pixman_image_unref(image);
242003b705cfSriastradh	}
242103b705cfSriastradh
242203b705cfSriastradh	return true;
242303b705cfSriastradh}
242403b705cfSriastradh
242503b705cfSriastradhstruct rasterize_traps_thread {
242603b705cfSriastradh	xTrapezoid *traps;
242703b705cfSriastradh	char *ptr;
242803b705cfSriastradh	int stride;
242903b705cfSriastradh	BoxRec bounds;
243003b705cfSriastradh	pixman_format_code_t format;
243103b705cfSriastradh	int ntrap;
243203b705cfSriastradh};
243303b705cfSriastradh
243403b705cfSriastradhstatic void rasterize_traps_thread(void *arg)
243503b705cfSriastradh{
243603b705cfSriastradh	struct rasterize_traps_thread *thread = arg;
243703b705cfSriastradh	pixman_image_t *image;
243803b705cfSriastradh	int width, height, n;
243903b705cfSriastradh
244003b705cfSriastradh	width = thread->bounds.x2 - thread->bounds.x1;
244103b705cfSriastradh	height = thread->bounds.y2 - thread->bounds.y1;
244203b705cfSriastradh
244303b705cfSriastradh	memset(thread->ptr, 0, thread->stride*height);
244403b705cfSriastradh	if (PIXMAN_FORMAT_DEPTH(thread->format) < 8)
244503b705cfSriastradh		image = pixman_image_create_bits(thread->format,
244603b705cfSriastradh						 width, height,
244703b705cfSriastradh						 NULL, 0);
244803b705cfSriastradh	else
244903b705cfSriastradh		image = pixman_image_create_bits(thread->format,
245003b705cfSriastradh						 width, height,
245103b705cfSriastradh						 (uint32_t *)thread->ptr,
245203b705cfSriastradh						 thread->stride);
245303b705cfSriastradh	if (image == NULL)
245403b705cfSriastradh		return;
245503b705cfSriastradh
245603b705cfSriastradh	for (n = 0; n < thread->ntrap; n++)
245703b705cfSriastradh		pixman_rasterize_trapezoid(image,
245803b705cfSriastradh					   (pixman_trapezoid_t *)&thread->traps[n],
245903b705cfSriastradh					   -thread->bounds.x1, -thread->bounds.y1);
246003b705cfSriastradh
246103b705cfSriastradh	if (PIXMAN_FORMAT_DEPTH(thread->format) < 8) {
246203b705cfSriastradh		pixman_image_t *a8;
246303b705cfSriastradh
246403b705cfSriastradh		a8 = pixman_image_create_bits(PIXMAN_a8,
246503b705cfSriastradh					      width, height,
246603b705cfSriastradh					      (uint32_t *)thread->ptr,
246703b705cfSriastradh					      thread->stride);
246803b705cfSriastradh		if (a8) {
246903b705cfSriastradh			pixman_image_composite(PIXMAN_OP_SRC,
247003b705cfSriastradh					       image, NULL, a8,
247103b705cfSriastradh					       0, 0,
247203b705cfSriastradh					       0, 0,
247303b705cfSriastradh					       0, 0,
247403b705cfSriastradh					       width, height);
247503b705cfSriastradh			pixman_image_unref(a8);
247603b705cfSriastradh		}
247703b705cfSriastradh	}
247803b705cfSriastradh
247903b705cfSriastradh	pixman_image_unref(image);
248003b705cfSriastradh}
248103b705cfSriastradh
248203b705cfSriastradhinline static void trapezoid_origin(const xLineFixed *l, int16_t *x, int16_t *y)
248303b705cfSriastradh{
248403b705cfSriastradh	if (l->p1.y < l->p2.y) {
248503b705cfSriastradh		*x = pixman_fixed_to_int(l->p1.x);
248603b705cfSriastradh		*y = pixman_fixed_to_int(l->p1.y);
248703b705cfSriastradh	} else {
248803b705cfSriastradh		*x = pixman_fixed_to_int(l->p2.x);
248903b705cfSriastradh		*y = pixman_fixed_to_int(l->p2.y);
249003b705cfSriastradh	}
249103b705cfSriastradh}
249203b705cfSriastradh
249303b705cfSriastradhstatic void
249403b705cfSriastradhtrapezoids_fallback(struct sna *sna,
249503b705cfSriastradh		    CARD8 op, PicturePtr src, PicturePtr dst,
249603b705cfSriastradh		    PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc,
249703b705cfSriastradh		    int ntrap, xTrapezoid * traps)
249803b705cfSriastradh{
249903b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
250003b705cfSriastradh
250103b705cfSriastradh	if (maskFormat) {
250203b705cfSriastradh		PixmapPtr scratch;
250303b705cfSriastradh		PicturePtr mask;
250403b705cfSriastradh		INT16 dst_x, dst_y;
250503b705cfSriastradh		BoxRec bounds;
250603b705cfSriastradh		int width, height, depth;
250703b705cfSriastradh		pixman_image_t *image;
250803b705cfSriastradh		pixman_format_code_t format;
250903b705cfSriastradh		int error;
251003b705cfSriastradh
251103b705cfSriastradh		trapezoid_origin(&traps[0].left, &dst_x, &dst_y);
251203b705cfSriastradh
251303b705cfSriastradh		trapezoids_bounds(ntrap, traps, &bounds);
251403b705cfSriastradh		if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2)
251503b705cfSriastradh			return;
251603b705cfSriastradh
251703b705cfSriastradh		DBG(("%s: bounds (%d, %d), (%d, %d)\n", __FUNCTION__,
251803b705cfSriastradh		     bounds.x1, bounds.y1, bounds.x2, bounds.y2));
251903b705cfSriastradh
252003b705cfSriastradh		if (!sna_compute_composite_extents(&bounds,
252103b705cfSriastradh						   src, NULL, dst,
252203b705cfSriastradh						   xSrc, ySrc,
252303b705cfSriastradh						   0, 0,
252403b705cfSriastradh						   bounds.x1, bounds.y1,
252503b705cfSriastradh						   bounds.x2 - bounds.x1,
252603b705cfSriastradh						   bounds.y2 - bounds.y1))
252703b705cfSriastradh			return;
252803b705cfSriastradh
252903b705cfSriastradh		DBG(("%s: extents (%d, %d), (%d, %d)\n", __FUNCTION__,
253003b705cfSriastradh		     bounds.x1, bounds.y1, bounds.x2, bounds.y2));
253103b705cfSriastradh
253203b705cfSriastradh		width  = bounds.x2 - bounds.x1;
253303b705cfSriastradh		height = bounds.y2 - bounds.y1;
253403b705cfSriastradh		bounds.x1 -= dst->pDrawable->x;
253503b705cfSriastradh		bounds.y1 -= dst->pDrawable->y;
253603b705cfSriastradh		bounds.x2 -= dst->pDrawable->x;
253703b705cfSriastradh		bounds.y2 -= dst->pDrawable->y;
253803b705cfSriastradh		depth = maskFormat->depth;
253903b705cfSriastradh		if (depth == 1) {
254003b705cfSriastradh			format = PIXMAN_a1;
254103b705cfSriastradh		} else if (depth <= 4) {
254203b705cfSriastradh			format = PIXMAN_a4;
254303b705cfSriastradh			depth = 4;
254403b705cfSriastradh		} else
254503b705cfSriastradh			format = PIXMAN_a8;
254603b705cfSriastradh
254703b705cfSriastradh		DBG(("%s: mask (%dx%d) depth=%d, format=%08x\n",
254803b705cfSriastradh		     __FUNCTION__, width, height, depth, format));
254903b705cfSriastradh		if (is_gpu(sna, dst->pDrawable, PREFER_GPU_RENDER) ||
255003b705cfSriastradh		    picture_is_gpu(sna, src)) {
255103b705cfSriastradh			int num_threads;
255203b705cfSriastradh
255303b705cfSriastradh			scratch = sna_pixmap_create_upload(screen,
255403b705cfSriastradh							   width, height, 8,
255503b705cfSriastradh							   KGEM_BUFFER_WRITE);
255603b705cfSriastradh			if (!scratch)
255703b705cfSriastradh				return;
255803b705cfSriastradh
255903b705cfSriastradh			num_threads = sna_use_threads(width, height, 8);
256003b705cfSriastradh			if (num_threads == 1) {
256103b705cfSriastradh				if (depth < 8) {
256203b705cfSriastradh					image = pixman_image_create_bits(format, width, height,
256303b705cfSriastradh									 NULL, 0);
256403b705cfSriastradh				} else {
256503b705cfSriastradh					memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
256603b705cfSriastradh
256703b705cfSriastradh					image = pixman_image_create_bits(format, width, height,
256803b705cfSriastradh									 scratch->devPrivate.ptr,
256903b705cfSriastradh									 scratch->devKind);
257003b705cfSriastradh				}
257103b705cfSriastradh				if (image) {
257203b705cfSriastradh					for (; ntrap; ntrap--, traps++)
257303b705cfSriastradh						pixman_rasterize_trapezoid(image,
257403b705cfSriastradh									   (pixman_trapezoid_t *)traps,
257503b705cfSriastradh									   -bounds.x1, -bounds.y1);
257603b705cfSriastradh					if (depth < 8) {
257703b705cfSriastradh						pixman_image_t *a8;
257803b705cfSriastradh
257903b705cfSriastradh						a8 = pixman_image_create_bits(PIXMAN_a8, width, height,
258003b705cfSriastradh									      scratch->devPrivate.ptr,
258103b705cfSriastradh									      scratch->devKind);
258203b705cfSriastradh						if (a8) {
258303b705cfSriastradh							pixman_image_composite(PIXMAN_OP_SRC,
258403b705cfSriastradh									       image, NULL, a8,
258503b705cfSriastradh									       0, 0,
258603b705cfSriastradh									       0, 0,
258703b705cfSriastradh									       0, 0,
258803b705cfSriastradh									       width, height);
258903b705cfSriastradh							format = PIXMAN_a8;
259003b705cfSriastradh							depth = 8;
259103b705cfSriastradh							pixman_image_unref(a8);
259203b705cfSriastradh						}
259303b705cfSriastradh					}
259403b705cfSriastradh
259503b705cfSriastradh					pixman_image_unref(image);
259603b705cfSriastradh				}
259703b705cfSriastradh				if (format != PIXMAN_a8) {
259803b705cfSriastradh					sna_pixmap_destroy(scratch);
259903b705cfSriastradh					return;
260003b705cfSriastradh				}
260103b705cfSriastradh			} else {
260203b705cfSriastradh				struct rasterize_traps_thread threads[num_threads];
260303b705cfSriastradh				int y, dy, n;
260403b705cfSriastradh
260503b705cfSriastradh				threads[0].ptr = scratch->devPrivate.ptr;
260603b705cfSriastradh				threads[0].stride = scratch->devKind;
260703b705cfSriastradh				threads[0].traps = traps;
260803b705cfSriastradh				threads[0].ntrap = ntrap;
260903b705cfSriastradh				threads[0].bounds = bounds;
261003b705cfSriastradh				threads[0].format = format;
261103b705cfSriastradh
261203b705cfSriastradh				y = bounds.y1;
261303b705cfSriastradh				dy = (height + num_threads - 1) / num_threads;
261403b705cfSriastradh
261503b705cfSriastradh				for (n = 1; n < num_threads; n++) {
261603b705cfSriastradh					threads[n] = threads[0];
261703b705cfSriastradh					threads[n].ptr += (y - bounds.y1) * threads[n].stride;
261803b705cfSriastradh					threads[n].bounds.y1 = y;
261903b705cfSriastradh					threads[n].bounds.y2 = y += dy;
262003b705cfSriastradh
262103b705cfSriastradh					sna_threads_run(rasterize_traps_thread, &threads[n]);
262203b705cfSriastradh				}
262303b705cfSriastradh
262403b705cfSriastradh				threads[0].ptr += (y - bounds.y1) * threads[0].stride;
262503b705cfSriastradh				threads[0].bounds.y1 = y;
262603b705cfSriastradh				threads[0].bounds.y2 = bounds.y2;
262703b705cfSriastradh				rasterize_traps_thread(&threads[0]);
262803b705cfSriastradh
262903b705cfSriastradh				sna_threads_wait();
263003b705cfSriastradh
263103b705cfSriastradh				format = PIXMAN_a8;
263203b705cfSriastradh				depth = 8;
263303b705cfSriastradh			}
263403b705cfSriastradh		} else {
263503b705cfSriastradh			scratch = sna_pixmap_create_unattached(screen,
263603b705cfSriastradh							       width, height,
263703b705cfSriastradh							       depth);
263803b705cfSriastradh			if (!scratch)
263903b705cfSriastradh				return;
264003b705cfSriastradh
264103b705cfSriastradh			memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
264203b705cfSriastradh			image = pixman_image_create_bits(format, width, height,
264303b705cfSriastradh							 scratch->devPrivate.ptr,
264403b705cfSriastradh							 scratch->devKind);
264503b705cfSriastradh			if (image) {
264603b705cfSriastradh				for (; ntrap; ntrap--, traps++)
264703b705cfSriastradh					pixman_rasterize_trapezoid(image,
264803b705cfSriastradh								   (pixman_trapezoid_t *)traps,
264903b705cfSriastradh								   -bounds.x1, -bounds.y1);
265003b705cfSriastradh				pixman_image_unref(image);
265103b705cfSriastradh			}
265203b705cfSriastradh		}
265303b705cfSriastradh
265403b705cfSriastradh		mask = CreatePicture(0, &scratch->drawable,
265503b705cfSriastradh				     PictureMatchFormat(screen, depth, format),
265603b705cfSriastradh				     0, 0, serverClient, &error);
265703b705cfSriastradh		if (mask) {
265803b705cfSriastradh			CompositePicture(op, src, mask, dst,
265903b705cfSriastradh					 xSrc + bounds.x1 - dst_x,
266003b705cfSriastradh					 ySrc + bounds.y1 - dst_y,
266103b705cfSriastradh					 0, 0,
266203b705cfSriastradh					 bounds.x1, bounds.y1,
266303b705cfSriastradh					 width, height);
266403b705cfSriastradh			FreePicture(mask, 0);
266503b705cfSriastradh		}
266603b705cfSriastradh		sna_pixmap_destroy(scratch);
266703b705cfSriastradh	} else {
266803b705cfSriastradh		if (dst->polyEdge == PolyEdgeSharp)
266903b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 1, PICT_a1);
267003b705cfSriastradh		else
267103b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 8, PICT_a8);
267203b705cfSriastradh
267303b705cfSriastradh		for (; ntrap; ntrap--, traps++)
267403b705cfSriastradh			trapezoids_fallback(sna, op,
267503b705cfSriastradh					    src, dst, maskFormat,
267603b705cfSriastradh					    xSrc, ySrc, 1, traps);
267703b705cfSriastradh	}
267803b705cfSriastradh}
267903b705cfSriastradh
268003b705cfSriastradhstatic bool
268103b705cfSriastradhcomposite_aligned_boxes(struct sna *sna,
268203b705cfSriastradh			CARD8 op,
268303b705cfSriastradh			PicturePtr src,
268403b705cfSriastradh			PicturePtr dst,
268503b705cfSriastradh			PictFormatPtr maskFormat,
268603b705cfSriastradh			INT16 src_x, INT16 src_y,
268703b705cfSriastradh			int ntrap, const xTrapezoid *traps,
268803b705cfSriastradh			bool force_fallback)
268903b705cfSriastradh{
269003b705cfSriastradh	BoxRec stack_boxes[64], *boxes;
269103b705cfSriastradh	pixman_region16_t region, clip;
269203b705cfSriastradh	struct sna_composite_op tmp;
269303b705cfSriastradh	bool ret = true;
269403b705cfSriastradh	int dx, dy, n, num_boxes;
269503b705cfSriastradh
269603b705cfSriastradh	if (NO_ALIGNED_BOXES)
269703b705cfSriastradh		return false;
269803b705cfSriastradh
269903b705cfSriastradh	DBG(("%s\n", __FUNCTION__));
270003b705cfSriastradh
270103b705cfSriastradh	boxes = stack_boxes;
270203b705cfSriastradh	if (ntrap > (int)ARRAY_SIZE(stack_boxes)) {
270303b705cfSriastradh		boxes = malloc(sizeof(BoxRec)*ntrap);
270403b705cfSriastradh		if (boxes == NULL)
270503b705cfSriastradh			return false;
270603b705cfSriastradh	}
270703b705cfSriastradh
270803b705cfSriastradh	dx = dst->pDrawable->x;
270903b705cfSriastradh	dy = dst->pDrawable->y;
271003b705cfSriastradh
271103b705cfSriastradh	region.extents.x1 = region.extents.y1 = 32767;
271203b705cfSriastradh	region.extents.x2 = region.extents.y2 = -32767;
271303b705cfSriastradh	num_boxes = 0;
271403b705cfSriastradh	for (n = 0; n < ntrap; n++) {
271503b705cfSriastradh		boxes[num_boxes].x1 = dx + pixman_fixed_to_int(traps[n].left.p1.x + pixman_fixed_1_minus_e/2);
271603b705cfSriastradh		boxes[num_boxes].y1 = dy + pixman_fixed_to_int(traps[n].top + pixman_fixed_1_minus_e/2);
271703b705cfSriastradh		boxes[num_boxes].x2 = dx + pixman_fixed_to_int(traps[n].right.p2.x + pixman_fixed_1_minus_e/2);
271803b705cfSriastradh		boxes[num_boxes].y2 = dy + pixman_fixed_to_int(traps[n].bottom + pixman_fixed_1_minus_e/2);
271903b705cfSriastradh
272003b705cfSriastradh		if (boxes[num_boxes].x1 >= boxes[num_boxes].x2)
272103b705cfSriastradh			continue;
272203b705cfSriastradh		if (boxes[num_boxes].y1 >= boxes[num_boxes].y2)
272303b705cfSriastradh			continue;
272403b705cfSriastradh
272503b705cfSriastradh		if (boxes[num_boxes].x1 < region.extents.x1)
272603b705cfSriastradh			region.extents.x1 = boxes[num_boxes].x1;
272703b705cfSriastradh		if (boxes[num_boxes].x2 > region.extents.x2)
272803b705cfSriastradh			region.extents.x2 = boxes[num_boxes].x2;
272903b705cfSriastradh
273003b705cfSriastradh		if (boxes[num_boxes].y1 < region.extents.y1)
273103b705cfSriastradh			region.extents.y1 = boxes[num_boxes].y1;
273203b705cfSriastradh		if (boxes[num_boxes].y2 > region.extents.y2)
273303b705cfSriastradh			region.extents.y2 = boxes[num_boxes].y2;
273403b705cfSriastradh
273503b705cfSriastradh		num_boxes++;
273603b705cfSriastradh	}
273703b705cfSriastradh
273803b705cfSriastradh	if (num_boxes == 0)
273903b705cfSriastradh		goto free_boxes;
274003b705cfSriastradh
274103b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d) offset of (%d, %d)\n",
274203b705cfSriastradh	     __FUNCTION__,
274303b705cfSriastradh	     region.extents.x1, region.extents.y1,
274403b705cfSriastradh	     region.extents.x2, region.extents.y2,
274503b705cfSriastradh	     region.extents.x1 - boxes[0].x1,
274603b705cfSriastradh	     region.extents.y1 - boxes[0].y1));
274703b705cfSriastradh
274803b705cfSriastradh	src_x += region.extents.x1 - boxes[0].x1;
274903b705cfSriastradh	src_y += region.extents.y1 - boxes[0].y1;
275003b705cfSriastradh
275103b705cfSriastradh	if (!sna_compute_composite_region(&clip,
275203b705cfSriastradh					  src, NULL, dst,
275303b705cfSriastradh					  src_x,  src_y,
275403b705cfSriastradh					  0, 0,
275503b705cfSriastradh					  region.extents.x1 - dx, region.extents.y1 - dy,
275603b705cfSriastradh					  region.extents.x2 - region.extents.x1,
275703b705cfSriastradh					  region.extents.y2 - region.extents.y1)) {
275803b705cfSriastradh		DBG(("%s: trapezoids do not intersect drawable clips\n",
275903b705cfSriastradh		     __FUNCTION__)) ;
276003b705cfSriastradh		goto done;
276103b705cfSriastradh	}
276203b705cfSriastradh
276303b705cfSriastradh	if (force_fallback ||
276403b705cfSriastradh	    !sna->render.composite(sna, op, src, NULL, dst,
276503b705cfSriastradh				   src_x,  src_y,
276603b705cfSriastradh				   0, 0,
276703b705cfSriastradh				   clip.extents.x1,  clip.extents.y1,
276803b705cfSriastradh				   clip.extents.x2 - clip.extents.x1,
276903b705cfSriastradh				   clip.extents.y2 - clip.extents.y1,
277003b705cfSriastradh				   memset(&tmp, 0, sizeof(tmp)))) {
277103b705cfSriastradh		unsigned int flags;
277203b705cfSriastradh		pixman_box16_t *b;
277303b705cfSriastradh		int i, count;
277403b705cfSriastradh
277503b705cfSriastradh		DBG(("%s: composite render op not supported\n",
277603b705cfSriastradh		     __FUNCTION__));
277703b705cfSriastradh
277803b705cfSriastradh		flags = MOVE_READ | MOVE_WRITE;
277903b705cfSriastradh		if (n == 1 && op <= PictOpSrc)
278003b705cfSriastradh			flags = MOVE_WRITE | MOVE_INPLACE_HINT;
278103b705cfSriastradh
278203b705cfSriastradh		if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &clip, flags))
278303b705cfSriastradh			goto done;
278403b705cfSriastradh		if (dst->alphaMap  &&
278503b705cfSriastradh		    !sna_drawable_move_to_cpu(dst->alphaMap->pDrawable,
278603b705cfSriastradh					      MOVE_READ | MOVE_WRITE))
278703b705cfSriastradh			goto done;
278803b705cfSriastradh		if (src->pDrawable) {
278903b705cfSriastradh			if (!sna_drawable_move_to_cpu(src->pDrawable,
279003b705cfSriastradh						      MOVE_READ))
279103b705cfSriastradh				goto done;
279203b705cfSriastradh			if (src->alphaMap &&
279303b705cfSriastradh			    !sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
279403b705cfSriastradh						      MOVE_READ))
279503b705cfSriastradh				goto done;
279603b705cfSriastradh		}
279703b705cfSriastradh
279803b705cfSriastradh		DBG(("%s: fbComposite()\n", __FUNCTION__));
279903b705cfSriastradh		if (maskFormat) {
280003b705cfSriastradh			pixman_region_init_rects(&region, boxes, num_boxes);
280103b705cfSriastradh			RegionIntersect(&region, &region, &clip);
280203b705cfSriastradh
280303b705cfSriastradh			b = REGION_RECTS(&region);
280403b705cfSriastradh			count = REGION_NUM_RECTS(&region);
280503b705cfSriastradh			for (i = 0; i < count; i++) {
280603b705cfSriastradh				fbComposite(op, src, NULL, dst,
280703b705cfSriastradh					    src_x + b[i].x1 - boxes[0].x1,
280803b705cfSriastradh					    src_y + b[i].y1 - boxes[0].y1,
280903b705cfSriastradh					    0, 0,
281003b705cfSriastradh					    b[i].x1, b[i].y1,
281103b705cfSriastradh					    b[i].x2 - b[i].x1, b[i].y2 - b[i].y1);
281203b705cfSriastradh			}
281303b705cfSriastradh			pixman_region_fini(&region);
281403b705cfSriastradh		} else {
281503b705cfSriastradh			for (n = 0; n < num_boxes; n++) {
281603b705cfSriastradh				pixman_region_init_rects(&region, &boxes[n], 1);
281703b705cfSriastradh				RegionIntersect(&region, &region, &clip);
281803b705cfSriastradh				b = REGION_RECTS(&region);
281903b705cfSriastradh				count = REGION_NUM_RECTS(&region);
282003b705cfSriastradh				for (i = 0; i < count; i++) {
282103b705cfSriastradh					fbComposite(op, src, NULL, dst,
282203b705cfSriastradh						    src_x + b[i].x1 - boxes[0].x1,
282303b705cfSriastradh						    src_y + b[i].y1 - boxes[0].y1,
282403b705cfSriastradh						    0, 0,
282503b705cfSriastradh						    b[i].x1, b[i].y1,
282603b705cfSriastradh						    b[i].x2 - b[i].x1, b[i].y2 - b[i].y1);
282703b705cfSriastradh				}
282803b705cfSriastradh				pixman_region_fini(&region);
282903b705cfSriastradh				pixman_region_fini(&region);
283003b705cfSriastradh			}
283103b705cfSriastradh		}
283203b705cfSriastradh		ret = true;
283303b705cfSriastradh		goto done;
283403b705cfSriastradh	}
283503b705cfSriastradh
283603b705cfSriastradh	if (maskFormat ||
283703b705cfSriastradh	    (op == PictOpSrc || op == PictOpClear) ||
283803b705cfSriastradh	    num_boxes == 1) {
283903b705cfSriastradh		pixman_region_init_rects(&region, boxes, num_boxes);
284003b705cfSriastradh		RegionIntersect(&region, &region, &clip);
284103b705cfSriastradh		if (REGION_NUM_RECTS(&region)) {
284203b705cfSriastradh			tmp.boxes(sna, &tmp,
284303b705cfSriastradh				  REGION_RECTS(&region),
284403b705cfSriastradh				  REGION_NUM_RECTS(&region));
284503b705cfSriastradh			apply_damage(&tmp, &region);
284603b705cfSriastradh		}
284703b705cfSriastradh		pixman_region_fini(&region);
284803b705cfSriastradh	} else {
284903b705cfSriastradh		for (n = 0; n < num_boxes; n++) {
285003b705cfSriastradh			pixman_region_init_rects(&region, &boxes[n], 1);
285103b705cfSriastradh			RegionIntersect(&region, &region, &clip);
285203b705cfSriastradh			if (REGION_NUM_RECTS(&region)) {
285303b705cfSriastradh				tmp.boxes(sna, &tmp,
285403b705cfSriastradh					  REGION_RECTS(&region),
285503b705cfSriastradh					  REGION_NUM_RECTS(&region));
285603b705cfSriastradh				apply_damage(&tmp, &region);
285703b705cfSriastradh			}
285803b705cfSriastradh			pixman_region_fini(&region);
285903b705cfSriastradh		}
286003b705cfSriastradh	}
286103b705cfSriastradh	tmp.done(sna, &tmp);
286203b705cfSriastradh
286303b705cfSriastradhdone:
286403b705cfSriastradh	REGION_UNINIT(NULL, &clip);
286503b705cfSriastradhfree_boxes:
286603b705cfSriastradh	if (boxes != stack_boxes)
286703b705cfSriastradh		free(boxes);
286803b705cfSriastradh
286903b705cfSriastradh	return ret;
287003b705cfSriastradh}
287103b705cfSriastradh
287203b705cfSriastradhstatic inline int grid_coverage(int samples, pixman_fixed_t f)
287303b705cfSriastradh{
287403b705cfSriastradh	return (samples * pixman_fixed_frac(f) + pixman_fixed_1/2) / pixman_fixed_1;
287503b705cfSriastradh}
287603b705cfSriastradh
287703b705cfSriastradhinline static void
287803b705cfSriastradhcomposite_unaligned_box(struct sna *sna,
287903b705cfSriastradh			struct sna_composite_spans_op *tmp,
288003b705cfSriastradh			const BoxRec *box,
288103b705cfSriastradh			float opacity,
288203b705cfSriastradh			pixman_region16_t *clip)
288303b705cfSriastradh{
288403b705cfSriastradh	assert(opacity != 0.);
288503b705cfSriastradh
288603b705cfSriastradh	if (clip) {
288703b705cfSriastradh		pixman_region16_t region;
288803b705cfSriastradh
288903b705cfSriastradh		pixman_region_init_rects(&region, box, 1);
289003b705cfSriastradh		RegionIntersect(&region, &region, clip);
289103b705cfSriastradh		if (REGION_NUM_RECTS(&region))
289203b705cfSriastradh			tmp->boxes(sna, tmp,
289303b705cfSriastradh				   REGION_RECTS(&region),
289403b705cfSriastradh				   REGION_NUM_RECTS(&region),
289503b705cfSriastradh				   opacity);
289603b705cfSriastradh		pixman_region_fini(&region);
289703b705cfSriastradh	} else
289803b705cfSriastradh		tmp->box(sna, tmp, box, opacity);
289903b705cfSriastradh}
290003b705cfSriastradh
290103b705cfSriastradhinline static void
290203b705cfSriastradhcomposite_unaligned_trap_row(struct sna *sna,
290303b705cfSriastradh			     struct sna_composite_spans_op *tmp,
290403b705cfSriastradh			     const xTrapezoid *trap, int dx,
290503b705cfSriastradh			     int y1, int y2, int covered,
290603b705cfSriastradh			     pixman_region16_t *clip)
290703b705cfSriastradh{
290803b705cfSriastradh	BoxRec box;
290903b705cfSriastradh	int opacity;
291003b705cfSriastradh	int x1, x2;
291103b705cfSriastradh#define u8_to_float(x) ((x) * (1.f/255))
291203b705cfSriastradh
291303b705cfSriastradh	if (covered == 0)
291403b705cfSriastradh		return;
291503b705cfSriastradh
291603b705cfSriastradh	x1 = dx + pixman_fixed_to_int(trap->left.p1.x);
291703b705cfSriastradh	x2 = dx + pixman_fixed_to_int(trap->right.p1.x);
291803b705cfSriastradh	if (clip) {
291903b705cfSriastradh		if (y2 > clip->extents.y2)
292003b705cfSriastradh			y2 = clip->extents.y2;
292103b705cfSriastradh		if (y1 < clip->extents.y1)
292203b705cfSriastradh			y1 = clip->extents.y1;
292303b705cfSriastradh		if (y1 >= y2)
292403b705cfSriastradh			return;
292503b705cfSriastradh
292603b705cfSriastradh		if (x2 < clip->extents.x1 || x1 > clip->extents.x2)
292703b705cfSriastradh			return;
292803b705cfSriastradh	}
292903b705cfSriastradh
293003b705cfSriastradh	box.y1 = y1;
293103b705cfSriastradh	box.y2 = y2;
293203b705cfSriastradh
293303b705cfSriastradh	if (x1 == x2) {
293403b705cfSriastradh		box.x1 = x1;
293503b705cfSriastradh		box.x2 = x2 + 1;
293603b705cfSriastradh
293703b705cfSriastradh		opacity = covered;
293803b705cfSriastradh		opacity *= grid_coverage(SAMPLES_X, trap->right.p1.x) - grid_coverage(SAMPLES_X, trap->left.p1.x);
293903b705cfSriastradh
294003b705cfSriastradh		if (opacity)
294103b705cfSriastradh			composite_unaligned_box(sna, tmp, &box,
294203b705cfSriastradh						u8_to_float(opacity), clip);
294303b705cfSriastradh	} else {
294403b705cfSriastradh		if (pixman_fixed_frac(trap->left.p1.x)) {
294503b705cfSriastradh			box.x1 = x1;
294603b705cfSriastradh			box.x2 = ++x1;
294703b705cfSriastradh
294803b705cfSriastradh			opacity = covered;
294903b705cfSriastradh			opacity *= SAMPLES_X - grid_coverage(SAMPLES_X, trap->left.p1.x);
295003b705cfSriastradh
295103b705cfSriastradh			if (opacity)
295203b705cfSriastradh				composite_unaligned_box(sna, tmp, &box,
295303b705cfSriastradh							u8_to_float(opacity), clip);
295403b705cfSriastradh		}
295503b705cfSriastradh
295603b705cfSriastradh		if (x2 > x1) {
295703b705cfSriastradh			box.x1 = x1;
295803b705cfSriastradh			box.x2 = x2;
295903b705cfSriastradh
296003b705cfSriastradh			composite_unaligned_box(sna, tmp, &box,
296103b705cfSriastradh						covered == SAMPLES_Y ? 1. : u8_to_float(covered*SAMPLES_X),
296203b705cfSriastradh						clip);
296303b705cfSriastradh		}
296403b705cfSriastradh
296503b705cfSriastradh		if (pixman_fixed_frac(trap->right.p1.x)) {
296603b705cfSriastradh			box.x1 = x2;
296703b705cfSriastradh			box.x2 = x2 + 1;
296803b705cfSriastradh
296903b705cfSriastradh			opacity = covered;
297003b705cfSriastradh			opacity *= grid_coverage(SAMPLES_X, trap->right.p1.x);
297103b705cfSriastradh
297203b705cfSriastradh			if (opacity)
297303b705cfSriastradh				composite_unaligned_box(sna, tmp, &box,
297403b705cfSriastradh							u8_to_float(opacity), clip);
297503b705cfSriastradh		}
297603b705cfSriastradh	}
297703b705cfSriastradh}
297803b705cfSriastradh
297903b705cfSriastradhflatten static void
298003b705cfSriastradhcomposite_unaligned_trap(struct sna *sna,
298103b705cfSriastradh			struct sna_composite_spans_op *tmp,
298203b705cfSriastradh			const xTrapezoid *trap,
298303b705cfSriastradh			int dx, int dy,
298403b705cfSriastradh			pixman_region16_t *clip)
298503b705cfSriastradh{
298603b705cfSriastradh	int y1, y2;
298703b705cfSriastradh
298803b705cfSriastradh	y1 = dy + pixman_fixed_to_int(trap->top);
298903b705cfSriastradh	y2 = dy + pixman_fixed_to_int(trap->bottom);
299003b705cfSriastradh
299103b705cfSriastradh	DBG(("%s: y1=%d, y2=%d\n", __FUNCTION__, y1, y2));
299203b705cfSriastradh
299303b705cfSriastradh	if (y1 == y2) {
299403b705cfSriastradh		composite_unaligned_trap_row(sna, tmp, trap, dx,
299503b705cfSriastradh					     y1, y1 + 1,
299603b705cfSriastradh					     grid_coverage(SAMPLES_Y, trap->bottom) - grid_coverage(SAMPLES_Y, trap->top),
299703b705cfSriastradh					     clip);
299803b705cfSriastradh	} else {
299903b705cfSriastradh		if (pixman_fixed_frac(trap->top)) {
300003b705cfSriastradh			composite_unaligned_trap_row(sna, tmp, trap, dx,
300103b705cfSriastradh						     y1, y1 + 1,
300203b705cfSriastradh						     SAMPLES_Y - grid_coverage(SAMPLES_Y, trap->top),
300303b705cfSriastradh						     clip);
300403b705cfSriastradh			y1++;
300503b705cfSriastradh		}
300603b705cfSriastradh
300703b705cfSriastradh		if (y2 > y1)
300803b705cfSriastradh			composite_unaligned_trap_row(sna, tmp, trap, dx,
300903b705cfSriastradh						     y1, y2,
301003b705cfSriastradh						     SAMPLES_Y,
301103b705cfSriastradh						     clip);
301203b705cfSriastradh
301303b705cfSriastradh		if (pixman_fixed_frac(trap->bottom))
301403b705cfSriastradh			composite_unaligned_trap_row(sna, tmp, trap, dx,
301503b705cfSriastradh						     y2, y2 + 1,
301603b705cfSriastradh						     grid_coverage(SAMPLES_Y, trap->bottom),
301703b705cfSriastradh						     clip);
301803b705cfSriastradh	}
301903b705cfSriastradh
302003b705cfSriastradh	if (tmp->base.damage) {
302103b705cfSriastradh		BoxRec box;
302203b705cfSriastradh
302303b705cfSriastradh		box.x1 = dx + pixman_fixed_to_int(trap->left.p1.x);
302403b705cfSriastradh		box.x2 = dx + pixman_fixed_to_int(trap->right.p1.x + pixman_fixed_1_minus_e);
302503b705cfSriastradh		box.y1 = dy + pixman_fixed_to_int(trap->top);
302603b705cfSriastradh		box.y2 = dy + pixman_fixed_to_int(trap->bottom + pixman_fixed_1_minus_e);
302703b705cfSriastradh
302803b705cfSriastradh		if (clip) {
302903b705cfSriastradh			pixman_region16_t region;
303003b705cfSriastradh
303103b705cfSriastradh			pixman_region_init_rects(&region, &box, 1);
303203b705cfSriastradh			RegionIntersect(&region, &region, clip);
303303b705cfSriastradh			if (REGION_NUM_RECTS(&region))
303403b705cfSriastradh				apply_damage(&tmp->base, &region);
303503b705cfSriastradh			RegionUninit(&region);
303603b705cfSriastradh		} else
303703b705cfSriastradh			apply_damage_box(&tmp->base, &box);
303803b705cfSriastradh	}
303903b705cfSriastradh}
304003b705cfSriastradh
304103b705cfSriastradhinline static void
304203b705cfSriastradhblt_opacity(PixmapPtr scratch,
304303b705cfSriastradh	    int x1, int x2,
304403b705cfSriastradh	    int y, int h,
304503b705cfSriastradh	    uint8_t opacity)
304603b705cfSriastradh{
304703b705cfSriastradh	uint8_t *ptr;
304803b705cfSriastradh
304903b705cfSriastradh	if (opacity == 0xff)
305003b705cfSriastradh		return;
305103b705cfSriastradh
305203b705cfSriastradh	if (x1 < 0)
305303b705cfSriastradh		x1 = 0;
305403b705cfSriastradh	if (x2 > scratch->drawable.width)
305503b705cfSriastradh		x2 = scratch->drawable.width;
305603b705cfSriastradh	if (x1 >= x2)
305703b705cfSriastradh		return;
305803b705cfSriastradh
305903b705cfSriastradh	x2 -= x1;
306003b705cfSriastradh
306103b705cfSriastradh	ptr = scratch->devPrivate.ptr;
306203b705cfSriastradh	ptr += scratch->devKind * y;
306303b705cfSriastradh	ptr += x1;
306403b705cfSriastradh	do {
306503b705cfSriastradh		if (x2 == 1)
306603b705cfSriastradh			*ptr = opacity;
306703b705cfSriastradh		else
306803b705cfSriastradh			memset(ptr, opacity, x2);
306903b705cfSriastradh		ptr += scratch->devKind;
307003b705cfSriastradh	} while (--h);
307103b705cfSriastradh}
307203b705cfSriastradh
307303b705cfSriastradhstatic void
307403b705cfSriastradhblt_unaligned_box_row(PixmapPtr scratch,
307503b705cfSriastradh		      BoxPtr extents,
307603b705cfSriastradh		      const xTrapezoid *trap,
307703b705cfSriastradh		      int y1, int y2,
307803b705cfSriastradh		      int covered)
307903b705cfSriastradh{
308003b705cfSriastradh	int x1, x2;
308103b705cfSriastradh
308203b705cfSriastradh	if (y2 > scratch->drawable.height)
308303b705cfSriastradh		y2 = scratch->drawable.height;
308403b705cfSriastradh	if (y1 < 0)
308503b705cfSriastradh		y1 = 0;
308603b705cfSriastradh	if (y1 >= y2)
308703b705cfSriastradh		return;
308803b705cfSriastradh
308903b705cfSriastradh	y2 -= y1;
309003b705cfSriastradh
309103b705cfSriastradh	x1 = pixman_fixed_to_int(trap->left.p1.x);
309203b705cfSriastradh	x2 = pixman_fixed_to_int(trap->right.p1.x);
309303b705cfSriastradh
309403b705cfSriastradh	x1 -= extents->x1;
309503b705cfSriastradh	x2 -= extents->x1;
309603b705cfSriastradh
309703b705cfSriastradh	if (x1 == x2) {
309803b705cfSriastradh		blt_opacity(scratch,
309903b705cfSriastradh			    x1, x1+1,
310003b705cfSriastradh			    y1, y2,
310103b705cfSriastradh			    covered * (grid_coverage(SAMPLES_X, trap->right.p1.x) - grid_coverage(SAMPLES_X, trap->left.p1.x)));
310203b705cfSriastradh	} else {
310303b705cfSriastradh		if (pixman_fixed_frac(trap->left.p1.x)) {
310403b705cfSriastradh			blt_opacity(scratch,
310503b705cfSriastradh				    x1, x1 + 1,
310603b705cfSriastradh				    y1, y2,
310703b705cfSriastradh				    covered * (SAMPLES_X - grid_coverage(SAMPLES_X, trap->left.p1.x)));
310803b705cfSriastradh			x1++;
310903b705cfSriastradh		}
311003b705cfSriastradh
311103b705cfSriastradh		if (x2 > x1) {
311203b705cfSriastradh			blt_opacity(scratch,
311303b705cfSriastradh				    x1, x2,
311403b705cfSriastradh				    y1, y2,
311503b705cfSriastradh				    covered*SAMPLES_X);
311603b705cfSriastradh		}
311703b705cfSriastradh
311803b705cfSriastradh		if (pixman_fixed_frac(trap->right.p1.x))
311903b705cfSriastradh			blt_opacity(scratch,
312003b705cfSriastradh				    x2, x2 + 1,
312103b705cfSriastradh				    y1, y2,
312203b705cfSriastradh				    covered * grid_coverage(SAMPLES_X, trap->right.p1.x));
312303b705cfSriastradh	}
312403b705cfSriastradh}
312503b705cfSriastradh
312603b705cfSriastradh#define ONE_HALF 0x7f
312703b705cfSriastradh#define RB_MASK 0x00ff00ff
312803b705cfSriastradh#define RB_ONE_HALF 0x007f007f
312903b705cfSriastradh#define RB_MASK_PLUS_ONE 0x01000100
313003b705cfSriastradh#define G_SHIFT 8
313103b705cfSriastradh
313203b705cfSriastradhstatic force_inline uint32_t
313303b705cfSriastradhmul8x2_8 (uint32_t a, uint8_t b)
313403b705cfSriastradh{
313503b705cfSriastradh	uint32_t t = (a & RB_MASK) * b + RB_ONE_HALF;
313603b705cfSriastradh	return ((t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT) & RB_MASK;
313703b705cfSriastradh}
313803b705cfSriastradh
313903b705cfSriastradhstatic force_inline uint32_t
314003b705cfSriastradhadd8x2_8x2(uint32_t a, uint32_t b)
314103b705cfSriastradh{
314203b705cfSriastradh	uint32_t t = a + b;
314303b705cfSriastradh	t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK);
314403b705cfSriastradh	return t & RB_MASK;
314503b705cfSriastradh}
314603b705cfSriastradh
314703b705cfSriastradhstatic force_inline uint32_t
314803b705cfSriastradhlerp8x4(uint32_t src, uint8_t a, uint32_t dst)
314903b705cfSriastradh{
315003b705cfSriastradh	return (add8x2_8x2(mul8x2_8(src, a),
315103b705cfSriastradh			   mul8x2_8(dst, ~a)) |
315203b705cfSriastradh		add8x2_8x2(mul8x2_8(src >> G_SHIFT, a),
315303b705cfSriastradh			   mul8x2_8(dst >> G_SHIFT, ~a)) << G_SHIFT);
315403b705cfSriastradh}
315503b705cfSriastradh
315603b705cfSriastradhinline static void
315703b705cfSriastradhlerp32_opacity(PixmapPtr scratch,
315803b705cfSriastradh	       uint32_t color,
315903b705cfSriastradh	       int16_t x, int16_t w,
316003b705cfSriastradh	       int16_t y, int16_t h,
316103b705cfSriastradh	       uint8_t opacity)
316203b705cfSriastradh{
316303b705cfSriastradh	uint32_t *ptr;
316403b705cfSriastradh	int stride, i;
316503b705cfSriastradh
316603b705cfSriastradh	ptr = (uint32_t*)((uint8_t *)scratch->devPrivate.ptr + scratch->devKind * y);
316703b705cfSriastradh	ptr += x;
316803b705cfSriastradh	stride = scratch->devKind / 4;
316903b705cfSriastradh
317003b705cfSriastradh	if (opacity == 0xff) {
317103b705cfSriastradh		if ((w | h) == 1) {
317203b705cfSriastradh			*ptr = color;
317303b705cfSriastradh		} else {
317403b705cfSriastradh			if (w < 16) {
317503b705cfSriastradh				do {
317603b705cfSriastradh					for (i = 0; i < w; i++)
317703b705cfSriastradh						ptr[i] = color;
317803b705cfSriastradh					ptr += stride;
317903b705cfSriastradh				} while (--h);
318003b705cfSriastradh			} else {
318103b705cfSriastradh				pixman_fill(ptr, stride, 32,
318203b705cfSriastradh					    0, 0, w, h, color);
318303b705cfSriastradh			}
318403b705cfSriastradh		}
318503b705cfSriastradh	} else {
318603b705cfSriastradh		if ((w | h) == 1) {
318703b705cfSriastradh			*ptr = lerp8x4(color, opacity, *ptr);
318803b705cfSriastradh		} else if (w == 1) {
318903b705cfSriastradh			do {
319003b705cfSriastradh				*ptr = lerp8x4(color, opacity, *ptr);
319103b705cfSriastradh				ptr += stride;
319203b705cfSriastradh			} while (--h);
319303b705cfSriastradh		} else{
319403b705cfSriastradh			do {
319503b705cfSriastradh				for (i = 0; i < w; i++)
319603b705cfSriastradh					ptr[i] = lerp8x4(color, opacity, ptr[i]);
319703b705cfSriastradh				ptr += stride;
319803b705cfSriastradh			} while (--h);
319903b705cfSriastradh		}
320003b705cfSriastradh	}
320103b705cfSriastradh}
320203b705cfSriastradh
320303b705cfSriastradhstatic void
320403b705cfSriastradhlerp32_unaligned_box_row(PixmapPtr scratch, uint32_t color,
320503b705cfSriastradh			 const BoxRec *extents,
320603b705cfSriastradh			 const xTrapezoid *trap, int16_t dx,
320703b705cfSriastradh			 int16_t y, int16_t h,
320803b705cfSriastradh			 uint8_t covered)
320903b705cfSriastradh{
321003b705cfSriastradh	int16_t x1 = pixman_fixed_to_int(trap->left.p1.x) + dx;
321103b705cfSriastradh	uint16_t fx1 = grid_coverage(SAMPLES_X, trap->left.p1.x);
321203b705cfSriastradh	int16_t x2 = pixman_fixed_to_int(trap->right.p2.x) + dx;
321303b705cfSriastradh	uint16_t fx2 = grid_coverage(SAMPLES_X, trap->right.p2.x);
321403b705cfSriastradh
321503b705cfSriastradh	if (x1 < extents->x1)
321603b705cfSriastradh		x1 = extents->x1, fx1 = 0;
321703b705cfSriastradh	if (x2 >= extents->x2)
321803b705cfSriastradh		x2 = extents->x2, fx2 = 0;
321903b705cfSriastradh
322003b705cfSriastradh	DBG(("%s: x=(%d.%d, %d.%d), y=%dx%d, covered=%d\n", __FUNCTION__,
322103b705cfSriastradh	     x1, fx1, x2, fx2, y, h, covered));
322203b705cfSriastradh
322303b705cfSriastradh	if (x1 < x2) {
322403b705cfSriastradh		if (fx1) {
322503b705cfSriastradh			lerp32_opacity(scratch, color,
322603b705cfSriastradh				       x1, 1,
322703b705cfSriastradh				       y, h,
322803b705cfSriastradh				       covered * (SAMPLES_X - fx1));
322903b705cfSriastradh			x1++;
323003b705cfSriastradh		}
323103b705cfSriastradh
323203b705cfSriastradh		if (x2 > x1) {
323303b705cfSriastradh			lerp32_opacity(scratch, color,
323403b705cfSriastradh				       x1, x2-x1,
323503b705cfSriastradh				       y, h,
323603b705cfSriastradh				       covered*SAMPLES_X);
323703b705cfSriastradh		}
323803b705cfSriastradh
323903b705cfSriastradh		if (fx2) {
324003b705cfSriastradh			lerp32_opacity(scratch, color,
324103b705cfSriastradh				       x2, 1,
324203b705cfSriastradh				       y, h,
324303b705cfSriastradh				       covered * fx2);
324403b705cfSriastradh		}
324503b705cfSriastradh	} else if (x1 == x2 && fx2 > fx1) {
324603b705cfSriastradh		lerp32_opacity(scratch, color,
324703b705cfSriastradh			       x1, 1,
324803b705cfSriastradh			       y, h,
324903b705cfSriastradh			       covered * (fx2 - fx1));
325003b705cfSriastradh	}
325103b705cfSriastradh}
325203b705cfSriastradh
325303b705cfSriastradhstruct pixman_inplace {
325403b705cfSriastradh	pixman_image_t *image, *source, *mask;
325503b705cfSriastradh	uint32_t color;
325603b705cfSriastradh	uint32_t *bits;
325703b705cfSriastradh	int dx, dy;
325803b705cfSriastradh	int sx, sy;
325903b705cfSriastradh	uint8_t op;
326003b705cfSriastradh};
326103b705cfSriastradh
326203b705cfSriastradhstatic force_inline uint8_t
326303b705cfSriastradhmul_8_8(uint8_t a, uint8_t b)
326403b705cfSriastradh{
326503b705cfSriastradh    uint16_t t = a * (uint16_t)b + 0x7f;
326603b705cfSriastradh    return ((t >> 8) + t) >> 8;
326703b705cfSriastradh}
326803b705cfSriastradh
326903b705cfSriastradhstatic inline uint32_t multa(uint32_t s, uint8_t a, int shift)
327003b705cfSriastradh{
327103b705cfSriastradh	return mul_8_8((s >> shift) & 0xff, a) << shift;
327203b705cfSriastradh}
327303b705cfSriastradh
327403b705cfSriastradhstatic inline uint32_t mul_4x8_8(uint32_t color, uint8_t alpha)
327503b705cfSriastradh{
327603b705cfSriastradh	uint32_t v;
327703b705cfSriastradh
327803b705cfSriastradh	v = 0;
327903b705cfSriastradh	v |= multa(color, alpha, 24);
328003b705cfSriastradh	v |= multa(color, alpha, 16);
328103b705cfSriastradh	v |= multa(color, alpha, 8);
328203b705cfSriastradh	v |= multa(color, alpha, 0);
328303b705cfSriastradh
328403b705cfSriastradh	return v;
328503b705cfSriastradh}
328603b705cfSriastradh
328703b705cfSriastradhinline static void
328803b705cfSriastradhpixsolid_opacity(struct pixman_inplace *pi,
328903b705cfSriastradh		 int16_t x, int16_t w,
329003b705cfSriastradh		 int16_t y, int16_t h,
329103b705cfSriastradh		 uint8_t opacity)
329203b705cfSriastradh{
329303b705cfSriastradh	if (opacity == 0xff)
329403b705cfSriastradh		*pi->bits = pi->color;
329503b705cfSriastradh	else
329603b705cfSriastradh		*pi->bits = mul_4x8_8(pi->color, opacity);
329703b705cfSriastradh	pixman_image_composite(pi->op, pi->source, NULL, pi->image,
329803b705cfSriastradh			       0, 0, 0, 0, pi->dx + x, pi->dy + y, w, h);
329903b705cfSriastradh}
330003b705cfSriastradh
330103b705cfSriastradhstatic void
330203b705cfSriastradhpixsolid_unaligned_box_row(struct pixman_inplace *pi,
330303b705cfSriastradh			   const BoxRec *extents,
330403b705cfSriastradh			   const xTrapezoid *trap,
330503b705cfSriastradh			   int16_t y, int16_t h,
330603b705cfSriastradh			   uint8_t covered)
330703b705cfSriastradh{
330803b705cfSriastradh	int16_t x1 = pixman_fixed_to_int(trap->left.p1.x);
330903b705cfSriastradh	uint16_t fx1 = grid_coverage(SAMPLES_X, trap->left.p1.x);
331003b705cfSriastradh	int16_t x2 = pixman_fixed_to_int(trap->right.p1.x);
331103b705cfSriastradh	uint16_t fx2 = grid_coverage(SAMPLES_X, trap->right.p1.x);
331203b705cfSriastradh
331303b705cfSriastradh	if (x1 < extents->x1)
331403b705cfSriastradh		x1 = extents->x1, fx1 = 0;
331503b705cfSriastradh	if (x2 >= extents->x2)
331603b705cfSriastradh		x2 = extents->x2, fx2 = 0;
331703b705cfSriastradh
331803b705cfSriastradh	if (x1 < x2) {
331903b705cfSriastradh		if (fx1) {
332003b705cfSriastradh			pixsolid_opacity(pi, x1, 1, y, h,
332103b705cfSriastradh					 covered * (SAMPLES_X - fx1));
332203b705cfSriastradh			x1++;
332303b705cfSriastradh		}
332403b705cfSriastradh
332503b705cfSriastradh		if (x2 > x1)
332603b705cfSriastradh			pixsolid_opacity(pi, x1, x2-x1, y, h, covered*SAMPLES_X);
332703b705cfSriastradh
332803b705cfSriastradh		if (fx2)
332903b705cfSriastradh			pixsolid_opacity(pi, x2, 1, y, h, covered * fx2);
333003b705cfSriastradh	} else if (x1 == x2 && fx2 > fx1) {
333103b705cfSriastradh		pixsolid_opacity(pi, x1, 1, y, h, covered * (fx2 - fx1));
333203b705cfSriastradh	}
333303b705cfSriastradh}
333403b705cfSriastradh
333503b705cfSriastradhstatic bool
333603b705cfSriastradhcomposite_unaligned_boxes_inplace__solid(struct sna *sna,
333703b705cfSriastradh					 CARD8 op, uint32_t color,
333803b705cfSriastradh					 PicturePtr dst,
333903b705cfSriastradh					 int n, const xTrapezoid *t,
334003b705cfSriastradh					 bool force_fallback)
334103b705cfSriastradh{
334203b705cfSriastradh	PixmapPtr pixmap;
334303b705cfSriastradh	int16_t dx, dy;
334403b705cfSriastradh
334503b705cfSriastradh	DBG(("%s: force=%d, is_gpu=%d, op=%d, color=%x\n", __FUNCTION__,
334603b705cfSriastradh	     force_fallback, is_gpu(sna, dst->pDrawable, PREFER_GPU_SPANS), op, color));
334703b705cfSriastradh
334803b705cfSriastradh	if (!force_fallback && is_gpu(sna, dst->pDrawable, PREFER_GPU_SPANS)) {
334903b705cfSriastradh		DBG(("%s: fallback -- can not perform operation in place, destination busy\n",
335003b705cfSriastradh		     __FUNCTION__));
335103b705cfSriastradh
335203b705cfSriastradh		return false;
335303b705cfSriastradh	}
335403b705cfSriastradh
335503b705cfSriastradh	/* XXX a8 boxes */
335603b705cfSriastradh	if (!(dst->format == PICT_a8r8g8b8 || dst->format == PICT_x8r8g8b8)) {
335703b705cfSriastradh		DBG(("%s: fallback -- can not perform operation in place, unhanbled format %08lx\n",
335803b705cfSriastradh		     __FUNCTION__, (long)dst->format));
335903b705cfSriastradh
336003b705cfSriastradh		goto pixman;
336103b705cfSriastradh	}
336203b705cfSriastradh
336303b705cfSriastradh	pixmap = get_drawable_pixmap(dst->pDrawable);
336403b705cfSriastradh	get_drawable_deltas(dst->pDrawable, pixmap, &dx, &dy);
336503b705cfSriastradh
336603b705cfSriastradh	if (op == PictOpOver && (color >> 24) == 0xff)
336703b705cfSriastradh		op = PictOpSrc;
336803b705cfSriastradh	if (op == PictOpOver || op == PictOpAdd) {
336903b705cfSriastradh		struct sna_pixmap *priv = sna_pixmap(pixmap);
337003b705cfSriastradh		if (priv && priv->clear && priv->clear_color == 0)
337103b705cfSriastradh			op = PictOpSrc;
337203b705cfSriastradh	}
337303b705cfSriastradh
337403b705cfSriastradh	switch (op) {
337503b705cfSriastradh	case PictOpSrc:
337603b705cfSriastradh		break;
337703b705cfSriastradh	default:
337803b705cfSriastradh		DBG(("%s: fallback -- can not perform op [%d] in place\n",
337903b705cfSriastradh		     __FUNCTION__, op));
338003b705cfSriastradh		goto pixman;
338103b705cfSriastradh	}
338203b705cfSriastradh
338303b705cfSriastradh	DBG(("%s: inplace operation on argb32 destination x %d\n",
338403b705cfSriastradh	     __FUNCTION__, n));
338503b705cfSriastradh	do {
338603b705cfSriastradh		RegionRec clip;
338703b705cfSriastradh		BoxPtr extents;
338803b705cfSriastradh		int count;
338903b705cfSriastradh
339003b705cfSriastradh		clip.extents.x1 = pixman_fixed_to_int(t->left.p1.x);
339103b705cfSriastradh		clip.extents.x2 = pixman_fixed_to_int(t->right.p1.x + pixman_fixed_1_minus_e);
339203b705cfSriastradh		clip.extents.y1 = pixman_fixed_to_int(t->top);
339303b705cfSriastradh		clip.extents.y2 = pixman_fixed_to_int(t->bottom + pixman_fixed_1_minus_e);
339403b705cfSriastradh		clip.data = NULL;
339503b705cfSriastradh
339603b705cfSriastradh		if (!sna_compute_composite_region(&clip,
339703b705cfSriastradh						   NULL, NULL, dst,
339803b705cfSriastradh						   0, 0,
339903b705cfSriastradh						   0, 0,
340003b705cfSriastradh						   clip.extents.x1, clip.extents.y1,
340103b705cfSriastradh						   clip.extents.x2 - clip.extents.x1,
340203b705cfSriastradh						   clip.extents.y2 - clip.extents.y1))
340303b705cfSriastradh			continue;
340403b705cfSriastradh
340503b705cfSriastradh		if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &clip,
340603b705cfSriastradh						     MOVE_WRITE | MOVE_READ)) {
340703b705cfSriastradh			RegionUninit(&clip);
340803b705cfSriastradh			continue;
340903b705cfSriastradh		}
341003b705cfSriastradh
341103b705cfSriastradh		RegionTranslate(&clip, dx, dy);
341203b705cfSriastradh		count = REGION_NUM_RECTS(&clip);
341303b705cfSriastradh		extents = REGION_RECTS(&clip);
341403b705cfSriastradh		while (count--) {
341503b705cfSriastradh			int16_t y1 = dy + pixman_fixed_to_int(t->top);
341603b705cfSriastradh			uint16_t fy1 = pixman_fixed_frac(t->top);
341703b705cfSriastradh			int16_t y2 = dy + pixman_fixed_to_int(t->bottom);
341803b705cfSriastradh			uint16_t fy2 = pixman_fixed_frac(t->bottom);
341903b705cfSriastradh
342003b705cfSriastradh			DBG(("%s: t=(%d, %d), (%d, %d), extents (%d, %d), (%d, %d)\n",
342103b705cfSriastradh			     __FUNCTION__,
342203b705cfSriastradh			     pixman_fixed_to_int(t->left.p1.x),
342303b705cfSriastradh			     pixman_fixed_to_int(t->top),
342403b705cfSriastradh			     pixman_fixed_to_int(t->right.p2.x),
342503b705cfSriastradh			     pixman_fixed_to_int(t->bottom),
342603b705cfSriastradh			     extents->x1, extents->y1,
342703b705cfSriastradh			     extents->x2, extents->y2));
342803b705cfSriastradh
342903b705cfSriastradh			if (y1 < extents->y1)
343003b705cfSriastradh				y1 = extents->y1, fy1 = 0;
343103b705cfSriastradh			if (y2 >= extents->y2)
343203b705cfSriastradh				y2 = extents->y2, fy2 = 0;
343303b705cfSriastradh
343403b705cfSriastradh			if (y1 < y2) {
343503b705cfSriastradh				if (fy1) {
343603b705cfSriastradh					lerp32_unaligned_box_row(pixmap, color, extents,
343703b705cfSriastradh								 t, dx, y1, 1,
343803b705cfSriastradh								 SAMPLES_Y - grid_coverage(SAMPLES_Y, fy1));
343903b705cfSriastradh					y1++;
344003b705cfSriastradh				}
344103b705cfSriastradh
344203b705cfSriastradh				if (y2 > y1)
344303b705cfSriastradh					lerp32_unaligned_box_row(pixmap, color, extents,
344403b705cfSriastradh								 t, dx, y1, y2 - y1,
344503b705cfSriastradh								 SAMPLES_Y);
344603b705cfSriastradh
344703b705cfSriastradh				if (fy2)
344803b705cfSriastradh					lerp32_unaligned_box_row(pixmap, color,  extents,
344903b705cfSriastradh								 t, dx, y2, 1,
345003b705cfSriastradh								 grid_coverage(SAMPLES_Y, fy2));
345103b705cfSriastradh			} else if (y1 == y2 && fy2 > fy1) {
345203b705cfSriastradh				lerp32_unaligned_box_row(pixmap, color, extents,
345303b705cfSriastradh							 t, dx, y1, 1,
345403b705cfSriastradh							 grid_coverage(SAMPLES_Y, fy2) - grid_coverage(SAMPLES_Y, fy1));
345503b705cfSriastradh			}
345603b705cfSriastradh			extents++;
345703b705cfSriastradh		}
345803b705cfSriastradh
345903b705cfSriastradh		RegionUninit(&clip);
346003b705cfSriastradh	} while (--n && t++);
346103b705cfSriastradh
346203b705cfSriastradh	return true;
346303b705cfSriastradh
346403b705cfSriastradhpixman:
346503b705cfSriastradh	do {
346603b705cfSriastradh		struct pixman_inplace pi;
346703b705cfSriastradh		RegionRec clip;
346803b705cfSriastradh		BoxPtr extents;
346903b705cfSriastradh		int count;
347003b705cfSriastradh
347103b705cfSriastradh		clip.extents.x1 = pixman_fixed_to_int(t->left.p1.x);
347203b705cfSriastradh		clip.extents.x2 = pixman_fixed_to_int(t->right.p1.x + pixman_fixed_1_minus_e);
347303b705cfSriastradh		clip.extents.y1 = pixman_fixed_to_int(t->top);
347403b705cfSriastradh		clip.extents.y2 = pixman_fixed_to_int(t->bottom + pixman_fixed_1_minus_e);
347503b705cfSriastradh		clip.data = NULL;
347603b705cfSriastradh
347703b705cfSriastradh		if (!sna_compute_composite_region(&clip,
347803b705cfSriastradh						   NULL, NULL, dst,
347903b705cfSriastradh						   0, 0,
348003b705cfSriastradh						   0, 0,
348103b705cfSriastradh						   clip.extents.x1, clip.extents.y1,
348203b705cfSriastradh						   clip.extents.x2 - clip.extents.x1,
348303b705cfSriastradh						   clip.extents.y2 - clip.extents.y1))
348403b705cfSriastradh			continue;
348503b705cfSriastradh
348603b705cfSriastradh		if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &clip,
348703b705cfSriastradh						     MOVE_WRITE | MOVE_READ)) {
348803b705cfSriastradh			RegionUninit(&clip);
348903b705cfSriastradh			continue;
349003b705cfSriastradh		}
349103b705cfSriastradh
349203b705cfSriastradh		pi.image = image_from_pict(dst, false, &pi.dx, &pi.dy);
349303b705cfSriastradh		pi.source = pixman_image_create_bits(PIXMAN_a8r8g8b8, 1, 1, NULL, 0);
349403b705cfSriastradh		pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL);
349503b705cfSriastradh		pi.bits = pixman_image_get_data(pi.source);
349603b705cfSriastradh		pi.color = color;
349703b705cfSriastradh		pi.op = op;
349803b705cfSriastradh
349903b705cfSriastradh		count = REGION_NUM_RECTS(&clip);
350003b705cfSriastradh		extents = REGION_RECTS(&clip);
350103b705cfSriastradh		while (count--) {
350203b705cfSriastradh			int16_t y1 = pixman_fixed_to_int(t->top);
350303b705cfSriastradh			uint16_t fy1 = pixman_fixed_frac(t->top);
350403b705cfSriastradh			int16_t y2 = pixman_fixed_to_int(t->bottom);
350503b705cfSriastradh			uint16_t fy2 = pixman_fixed_frac(t->bottom);
350603b705cfSriastradh
350703b705cfSriastradh			if (y1 < extents->y1)
350803b705cfSriastradh				y1 = extents->y1, fy1 = 0;
350903b705cfSriastradh			if (y2 >= extents->y2)
351003b705cfSriastradh				y2 = extents->y2, fy2 = 0;
351103b705cfSriastradh			if (y1 < y2) {
351203b705cfSriastradh				if (fy1) {
351303b705cfSriastradh					pixsolid_unaligned_box_row(&pi, extents, t, y1, 1,
351403b705cfSriastradh								   SAMPLES_Y - grid_coverage(SAMPLES_Y, fy1));
351503b705cfSriastradh					y1++;
351603b705cfSriastradh				}
351703b705cfSriastradh
351803b705cfSriastradh				if (y2 > y1)
351903b705cfSriastradh					pixsolid_unaligned_box_row(&pi, extents, t, y1, y2 - y1,
352003b705cfSriastradh								   SAMPLES_Y);
352103b705cfSriastradh
352203b705cfSriastradh				if (fy2)
352303b705cfSriastradh					pixsolid_unaligned_box_row(&pi, extents, t, y2, 1,
352403b705cfSriastradh								   grid_coverage(SAMPLES_Y, fy2));
352503b705cfSriastradh			} else if (y1 == y2 && fy2 > fy1) {
352603b705cfSriastradh				pixsolid_unaligned_box_row(&pi, extents, t, y1, 1,
352703b705cfSriastradh							   grid_coverage(SAMPLES_Y, fy2) - grid_coverage(SAMPLES_Y, fy1));
352803b705cfSriastradh			}
352903b705cfSriastradh			extents++;
353003b705cfSriastradh		}
353103b705cfSriastradh
353203b705cfSriastradh		RegionUninit(&clip);
353303b705cfSriastradh		pixman_image_unref(pi.image);
353403b705cfSriastradh		pixman_image_unref(pi.source);
353503b705cfSriastradh	} while (--n && t++);
353603b705cfSriastradh	return true;
353703b705cfSriastradh}
353803b705cfSriastradh
353903b705cfSriastradhinline static void
354003b705cfSriastradhpixmask_opacity(struct pixman_inplace *pi,
354103b705cfSriastradh		int16_t x, int16_t w,
354203b705cfSriastradh		int16_t y, int16_t h,
354303b705cfSriastradh		uint8_t opacity)
354403b705cfSriastradh{
354503b705cfSriastradh	if (opacity == 0xff) {
354603b705cfSriastradh		pixman_image_composite(pi->op, pi->source, NULL, pi->image,
354703b705cfSriastradh				       pi->sx + x, pi->sy + y,
354803b705cfSriastradh				       0, 0,
354903b705cfSriastradh				       pi->dx + x, pi->dy + y,
355003b705cfSriastradh				       w, h);
355103b705cfSriastradh	} else {
355203b705cfSriastradh		*pi->bits = opacity;
355303b705cfSriastradh		pixman_image_composite(pi->op, pi->source, pi->mask, pi->image,
355403b705cfSriastradh				       pi->sx + x, pi->sy + y,
355503b705cfSriastradh				       0, 0,
355603b705cfSriastradh				       pi->dx + x, pi->dy + y,
355703b705cfSriastradh				       w, h);
355803b705cfSriastradh	}
355903b705cfSriastradh}
356003b705cfSriastradh
356103b705cfSriastradhstatic void
356203b705cfSriastradhpixmask_unaligned_box_row(struct pixman_inplace *pi,
356303b705cfSriastradh			  const BoxRec *extents,
356403b705cfSriastradh			  const xTrapezoid *trap,
356503b705cfSriastradh			  int16_t y, int16_t h,
356603b705cfSriastradh			  uint8_t covered)
356703b705cfSriastradh{
356803b705cfSriastradh	int16_t x1 = pixman_fixed_to_int(trap->left.p1.x);
356903b705cfSriastradh	uint16_t fx1 = grid_coverage(SAMPLES_X, trap->left.p1.x);
357003b705cfSriastradh	int16_t x2 = pixman_fixed_to_int(trap->right.p1.x);
357103b705cfSriastradh	uint16_t fx2 = grid_coverage(SAMPLES_X, trap->right.p1.x);
357203b705cfSriastradh
357303b705cfSriastradh	if (x1 < extents->x1)
357403b705cfSriastradh		x1 = extents->x1, fx1 = 0;
357503b705cfSriastradh	if (x2 >= extents->x2)
357603b705cfSriastradh		x2 = extents->x2, fx2 = 0;
357703b705cfSriastradh
357803b705cfSriastradh	if (x1 < x2) {
357903b705cfSriastradh		if (fx1) {
358003b705cfSriastradh			pixmask_opacity(pi, x1, 1, y, h,
358103b705cfSriastradh					 covered * (SAMPLES_X - fx1));
358203b705cfSriastradh			x1++;
358303b705cfSriastradh		}
358403b705cfSriastradh
358503b705cfSriastradh		if (x2 > x1)
358603b705cfSriastradh			pixmask_opacity(pi, x1, x2-x1, y, h, covered*SAMPLES_X);
358703b705cfSriastradh
358803b705cfSriastradh		if (fx2)
358903b705cfSriastradh			pixmask_opacity(pi, x2, 1, y, h, covered * fx2);
359003b705cfSriastradh	} else if (x1 == x2 && fx2 > fx1) {
359103b705cfSriastradh		pixmask_opacity(pi, x1, 1, y, h, covered * (fx2 - fx1));
359203b705cfSriastradh	}
359303b705cfSriastradh}
359403b705cfSriastradh
359503b705cfSriastradhstruct rectilinear_inplace_thread {
359603b705cfSriastradh	pixman_image_t *dst, *src;
359703b705cfSriastradh	const RegionRec *clip;
359803b705cfSriastradh	const xTrapezoid *trap;
359903b705cfSriastradh	int dx, dy, sx, sy;
360003b705cfSriastradh	int y1, y2;
360103b705cfSriastradh	CARD8 op;
360203b705cfSriastradh};
360303b705cfSriastradh
360403b705cfSriastradhstatic void rectilinear_inplace_thread(void *arg)
360503b705cfSriastradh{
360603b705cfSriastradh	struct rectilinear_inplace_thread *thread = arg;
360703b705cfSriastradh	const xTrapezoid *t = thread->trap;
360803b705cfSriastradh	struct pixman_inplace pi;
360903b705cfSriastradh	const BoxRec *extents;
361003b705cfSriastradh	int count;
361103b705cfSriastradh
361203b705cfSriastradh	pi.image = thread->dst;
361303b705cfSriastradh	pi.dx = thread->dx;
361403b705cfSriastradh	pi.dy = thread->dy;
361503b705cfSriastradh
361603b705cfSriastradh	pi.source = thread->src;
361703b705cfSriastradh	pi.sx = thread->sx;
361803b705cfSriastradh	pi.sy = thread->sy;
361903b705cfSriastradh
362003b705cfSriastradh	pi.mask = pixman_image_create_bits(PIXMAN_a8, 1, 1, &pi.color, 4);
362103b705cfSriastradh	pixman_image_set_repeat(pi.mask, PIXMAN_REPEAT_NORMAL);
362203b705cfSriastradh	pi.bits = pixman_image_get_data(pi.mask);
362303b705cfSriastradh	pi.op = thread->op;
362403b705cfSriastradh
362503b705cfSriastradh	count = region_count(thread->clip);
362603b705cfSriastradh	extents = region_boxes(thread->clip);
362703b705cfSriastradh	while (count--) {
362803b705cfSriastradh		int16_t y1 = pixman_fixed_to_int(t->top);
362903b705cfSriastradh		uint16_t fy1 = pixman_fixed_frac(t->top);
363003b705cfSriastradh		int16_t y2 = pixman_fixed_to_int(t->bottom);
363103b705cfSriastradh		uint16_t fy2 = pixman_fixed_frac(t->bottom);
363203b705cfSriastradh
363303b705cfSriastradh		if (y1 < MAX(thread->y1, extents->y1))
363403b705cfSriastradh			y1 = MAX(thread->y1, extents->y1), fy1 = 0;
363503b705cfSriastradh		if (y2 > MIN(thread->y2, extents->y2))
363603b705cfSriastradh			y2 = MIN(thread->y2, extents->y2), fy2 = 0;
363703b705cfSriastradh		if (y1 < y2) {
363803b705cfSriastradh			if (fy1) {
363903b705cfSriastradh				pixmask_unaligned_box_row(&pi, extents, t, y1, 1,
364003b705cfSriastradh							  SAMPLES_Y - grid_coverage(SAMPLES_Y, fy1));
364103b705cfSriastradh				y1++;
364203b705cfSriastradh			}
364303b705cfSriastradh
364403b705cfSriastradh			if (y2 > y1)
364503b705cfSriastradh				pixmask_unaligned_box_row(&pi, extents, t, y1, y2 - y1,
364603b705cfSriastradh							  SAMPLES_Y);
364703b705cfSriastradh
364803b705cfSriastradh			if (fy2)
364903b705cfSriastradh				pixmask_unaligned_box_row(&pi, extents, t, y2, 1,
365003b705cfSriastradh							  grid_coverage(SAMPLES_Y, fy2));
365103b705cfSriastradh		} else if (y1 == y2 && fy2 > fy1) {
365203b705cfSriastradh			pixmask_unaligned_box_row(&pi, extents, t, y1, 1,
365303b705cfSriastradh						  grid_coverage(SAMPLES_Y, fy2) - grid_coverage(SAMPLES_Y, fy1));
365403b705cfSriastradh		}
365503b705cfSriastradh		extents++;
365603b705cfSriastradh	}
365703b705cfSriastradh
365803b705cfSriastradh	pixman_image_unref(pi.mask);
365903b705cfSriastradh}
366003b705cfSriastradh
366103b705cfSriastradhstatic bool
366203b705cfSriastradhcomposite_unaligned_boxes_inplace(struct sna *sna,
366303b705cfSriastradh				  CARD8 op,
366403b705cfSriastradh				  PicturePtr src, int16_t src_x, int16_t src_y,
366503b705cfSriastradh				  PicturePtr dst, int n, const xTrapezoid *t,
366603b705cfSriastradh				  bool force_fallback)
366703b705cfSriastradh{
366803b705cfSriastradh	if (!force_fallback &&
366903b705cfSriastradh	    (is_gpu(sna, dst->pDrawable, PREFER_GPU_SPANS) ||
367003b705cfSriastradh	     picture_is_gpu(sna, src))) {
367103b705cfSriastradh		DBG(("%s: fallback -- not forcing\n", __FUNCTION__));
367203b705cfSriastradh		return false;
367303b705cfSriastradh	}
367403b705cfSriastradh
367503b705cfSriastradh	DBG(("%s\n", __FUNCTION__));
367603b705cfSriastradh
367703b705cfSriastradh	src_x -= pixman_fixed_to_int(t[0].left.p1.x);
367803b705cfSriastradh	src_y -= pixman_fixed_to_int(t[0].left.p1.y);
367903b705cfSriastradh	do {
368003b705cfSriastradh		RegionRec clip;
368103b705cfSriastradh		BoxPtr extents;
368203b705cfSriastradh		int count;
368303b705cfSriastradh		int num_threads;
368403b705cfSriastradh
368503b705cfSriastradh		clip.extents.x1 = pixman_fixed_to_int(t->left.p1.x);
368603b705cfSriastradh		clip.extents.x2 = pixman_fixed_to_int(t->right.p1.x + pixman_fixed_1_minus_e);
368703b705cfSriastradh		clip.extents.y1 = pixman_fixed_to_int(t->top);
368803b705cfSriastradh		clip.extents.y2 = pixman_fixed_to_int(t->bottom + pixman_fixed_1_minus_e);
368903b705cfSriastradh		clip.data = NULL;
369003b705cfSriastradh
369103b705cfSriastradh		if (!sna_compute_composite_region(&clip,
369203b705cfSriastradh						   src, NULL, dst,
369303b705cfSriastradh						   clip.extents.x1 + src_x,
369403b705cfSriastradh						   clip.extents.y1 + src_y,
369503b705cfSriastradh						   0, 0,
369603b705cfSriastradh						   clip.extents.x1, clip.extents.y1,
369703b705cfSriastradh						   clip.extents.x2 - clip.extents.x1,
369803b705cfSriastradh						   clip.extents.y2 - clip.extents.y1))
369903b705cfSriastradh			continue;
370003b705cfSriastradh
370103b705cfSriastradh		if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &clip,
370203b705cfSriastradh						     MOVE_WRITE | MOVE_READ)) {
370303b705cfSriastradh			RegionUninit(&clip);
370403b705cfSriastradh			continue;
370503b705cfSriastradh		}
370603b705cfSriastradh
370703b705cfSriastradh		if (src->pDrawable) {
370803b705cfSriastradh			if (!sna_drawable_move_to_cpu(src->pDrawable,
370903b705cfSriastradh						      MOVE_READ)) {
371003b705cfSriastradh				RegionUninit(&clip);
371103b705cfSriastradh				continue;
371203b705cfSriastradh			}
371303b705cfSriastradh			if (src->alphaMap) {
371403b705cfSriastradh				if (!sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
371503b705cfSriastradh							      MOVE_READ)) {
371603b705cfSriastradh					RegionUninit(&clip);
371703b705cfSriastradh					continue;
371803b705cfSriastradh				}
371903b705cfSriastradh			}
372003b705cfSriastradh		}
372103b705cfSriastradh
372203b705cfSriastradh		num_threads = sna_use_threads(clip.extents.x2 - clip.extents.x1,
372303b705cfSriastradh					      clip.extents.y2 - clip.extents.y1,
372403b705cfSriastradh					      32);
372503b705cfSriastradh		if (num_threads == 1) {
372603b705cfSriastradh			struct pixman_inplace pi;
372703b705cfSriastradh
372803b705cfSriastradh			pi.image = image_from_pict(dst, false, &pi.dx, &pi.dy);
372903b705cfSriastradh			pi.source = image_from_pict(src, false, &pi.sx, &pi.sy);
373003b705cfSriastradh			pi.sx += src_x;
373103b705cfSriastradh			pi.sy += src_y;
373203b705cfSriastradh			pi.mask = pixman_image_create_bits(PIXMAN_a8, 1, 1, &pi.color, 4);
373303b705cfSriastradh			pixman_image_set_repeat(pi.mask, PIXMAN_REPEAT_NORMAL);
373403b705cfSriastradh			pi.bits = pixman_image_get_data(pi.mask);
373503b705cfSriastradh			pi.op = op;
373603b705cfSriastradh
373703b705cfSriastradh			count = REGION_NUM_RECTS(&clip);
373803b705cfSriastradh			extents = REGION_RECTS(&clip);
373903b705cfSriastradh			while (count--) {
374003b705cfSriastradh				int16_t y1 = pixman_fixed_to_int(t->top);
374103b705cfSriastradh				uint16_t fy1 = pixman_fixed_frac(t->top);
374203b705cfSriastradh				int16_t y2 = pixman_fixed_to_int(t->bottom);
374303b705cfSriastradh				uint16_t fy2 = pixman_fixed_frac(t->bottom);
374403b705cfSriastradh
374503b705cfSriastradh				if (y1 < extents->y1)
374603b705cfSriastradh					y1 = extents->y1, fy1 = 0;
374703b705cfSriastradh				if (y2 > extents->y2)
374803b705cfSriastradh					y2 = extents->y2, fy2 = 0;
374903b705cfSriastradh				if (y1 < y2) {
375003b705cfSriastradh					if (fy1) {
375103b705cfSriastradh						pixmask_unaligned_box_row(&pi, extents, t, y1, 1,
375203b705cfSriastradh									  SAMPLES_Y - grid_coverage(SAMPLES_Y, fy1));
375303b705cfSriastradh						y1++;
375403b705cfSriastradh					}
375503b705cfSriastradh
375603b705cfSriastradh					if (y2 > y1)
375703b705cfSriastradh						pixmask_unaligned_box_row(&pi, extents, t, y1, y2 - y1,
375803b705cfSriastradh									  SAMPLES_Y);
375903b705cfSriastradh
376003b705cfSriastradh					if (fy2)
376103b705cfSriastradh						pixmask_unaligned_box_row(&pi, extents, t, y2, 1,
376203b705cfSriastradh									  grid_coverage(SAMPLES_Y, fy2));
376303b705cfSriastradh				} else if (y1 == y2 && fy2 > fy1) {
376403b705cfSriastradh					pixmask_unaligned_box_row(&pi, extents, t, y1, 1,
376503b705cfSriastradh								  grid_coverage(SAMPLES_Y, fy2) - grid_coverage(SAMPLES_Y, fy1));
376603b705cfSriastradh				}
376703b705cfSriastradh				extents++;
376803b705cfSriastradh			}
376903b705cfSriastradh
377003b705cfSriastradh			pixman_image_unref(pi.image);
377103b705cfSriastradh			pixman_image_unref(pi.source);
377203b705cfSriastradh			pixman_image_unref(pi.mask);
377303b705cfSriastradh		} else {
377403b705cfSriastradh			struct rectilinear_inplace_thread thread[num_threads];
377503b705cfSriastradh			int i, y, dy;
377603b705cfSriastradh
377703b705cfSriastradh
377803b705cfSriastradh			thread[0].trap = t;
377903b705cfSriastradh			thread[0].dst = image_from_pict(dst, false, &thread[0].dx, &thread[0].dy);
378003b705cfSriastradh			thread[0].src = image_from_pict(src, false, &thread[0].sx, &thread[0].sy);
378103b705cfSriastradh			thread[0].sx += src_x;
378203b705cfSriastradh			thread[0].sy += src_y;
378303b705cfSriastradh
378403b705cfSriastradh			thread[0].clip = &clip;
378503b705cfSriastradh			thread[0].op = op;
378603b705cfSriastradh
378703b705cfSriastradh			y = clip.extents.y1;
378803b705cfSriastradh			dy = (clip.extents.y2 - clip.extents.y1 + num_threads - 1) / num_threads;
378903b705cfSriastradh
379003b705cfSriastradh			for (i = 1; i < num_threads; i++) {
379103b705cfSriastradh				thread[i] = thread[0];
379203b705cfSriastradh				thread[i].y1 = y;
379303b705cfSriastradh				thread[i].y2 = y += dy;
379403b705cfSriastradh				sna_threads_run(rectilinear_inplace_thread, &thread[i]);
379503b705cfSriastradh			}
379603b705cfSriastradh
379703b705cfSriastradh			thread[0].y1 = y;
379803b705cfSriastradh			thread[0].y2 = clip.extents.y2;
379903b705cfSriastradh			rectilinear_inplace_thread(&thread[0]);
380003b705cfSriastradh
380103b705cfSriastradh			sna_threads_wait();
380203b705cfSriastradh
380303b705cfSriastradh			pixman_image_unref(thread[0].dst);
380403b705cfSriastradh			pixman_image_unref(thread[0].src);
380503b705cfSriastradh		}
380603b705cfSriastradh
380703b705cfSriastradh		RegionUninit(&clip);
380803b705cfSriastradh	} while (--n && t++);
380903b705cfSriastradh
381003b705cfSriastradh	return true;
381103b705cfSriastradh}
381203b705cfSriastradh
381303b705cfSriastradhstatic bool
381403b705cfSriastradhcomposite_unaligned_boxes_fallback(struct sna *sna,
381503b705cfSriastradh				   CARD8 op,
381603b705cfSriastradh				   PicturePtr src,
381703b705cfSriastradh				   PicturePtr dst,
381803b705cfSriastradh				   INT16 src_x, INT16 src_y,
381903b705cfSriastradh				   int ntrap, const xTrapezoid *traps,
382003b705cfSriastradh				   bool force_fallback)
382103b705cfSriastradh{
382203b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
382303b705cfSriastradh	uint32_t color;
382403b705cfSriastradh	int16_t dst_x, dst_y;
382503b705cfSriastradh	int16_t dx, dy;
382603b705cfSriastradh	int n;
382703b705cfSriastradh
382803b705cfSriastradh	if (sna_picture_is_solid(src, &color) &&
382903b705cfSriastradh	    composite_unaligned_boxes_inplace__solid(sna, op, color, dst,
383003b705cfSriastradh						     ntrap, traps,
383103b705cfSriastradh						     force_fallback))
383203b705cfSriastradh		return true;
383303b705cfSriastradh
383403b705cfSriastradh	if (composite_unaligned_boxes_inplace(sna, op, src, src_x, src_y,
383503b705cfSriastradh					      dst, ntrap, traps,
383603b705cfSriastradh					      force_fallback))
383703b705cfSriastradh		return true;
383803b705cfSriastradh
383903b705cfSriastradh	trapezoid_origin(&traps[0].left, &dst_x, &dst_y);
384003b705cfSriastradh	dx = dst->pDrawable->x;
384103b705cfSriastradh	dy = dst->pDrawable->y;
384203b705cfSriastradh	for (n = 0; n < ntrap; n++) {
384303b705cfSriastradh		const xTrapezoid *t = &traps[n];
384403b705cfSriastradh		PixmapPtr scratch;
384503b705cfSriastradh		PicturePtr mask;
384603b705cfSriastradh		BoxRec extents;
384703b705cfSriastradh		int error;
384803b705cfSriastradh		int y1, y2;
384903b705cfSriastradh
385003b705cfSriastradh		extents.x1 = pixman_fixed_to_int(t->left.p1.x);
385103b705cfSriastradh		extents.x2 = pixman_fixed_to_int(t->right.p1.x + pixman_fixed_1_minus_e);
385203b705cfSriastradh		extents.y1 = pixman_fixed_to_int(t->top);
385303b705cfSriastradh		extents.y2 = pixman_fixed_to_int(t->bottom + pixman_fixed_1_minus_e);
385403b705cfSriastradh
385503b705cfSriastradh		if (!sna_compute_composite_extents(&extents,
385603b705cfSriastradh						   src, NULL, dst,
385703b705cfSriastradh						   src_x, src_y,
385803b705cfSriastradh						   0, 0,
385903b705cfSriastradh						   extents.x1, extents.y1,
386003b705cfSriastradh						   extents.x2 - extents.x1,
386103b705cfSriastradh						   extents.y2 - extents.y1))
386203b705cfSriastradh			continue;
386303b705cfSriastradh
386403b705cfSriastradh		if (force_fallback)
386503b705cfSriastradh			scratch = sna_pixmap_create_unattached(screen,
386603b705cfSriastradh							       extents.x2 - extents.x1,
386703b705cfSriastradh							       extents.y2 - extents.y1,
386803b705cfSriastradh							       8);
386903b705cfSriastradh		else
387003b705cfSriastradh			scratch = sna_pixmap_create_upload(screen,
387103b705cfSriastradh							   extents.x2 - extents.x1,
387203b705cfSriastradh							   extents.y2 - extents.y1,
387303b705cfSriastradh							   8, KGEM_BUFFER_WRITE_INPLACE);
387403b705cfSriastradh		if (!scratch)
387503b705cfSriastradh			continue;
387603b705cfSriastradh
387703b705cfSriastradh		memset(scratch->devPrivate.ptr, 0xff,
387803b705cfSriastradh		       scratch->devKind * (extents.y2 - extents.y1));
387903b705cfSriastradh
388003b705cfSriastradh		extents.x1 -= dx;
388103b705cfSriastradh		extents.x2 -= dx;
388203b705cfSriastradh		extents.y1 -= dy;
388303b705cfSriastradh		extents.y2 -= dy;
388403b705cfSriastradh
388503b705cfSriastradh		y1 = pixman_fixed_to_int(t->top) - extents.y1;
388603b705cfSriastradh		y2 = pixman_fixed_to_int(t->bottom) - extents.y1;
388703b705cfSriastradh
388803b705cfSriastradh		if (y1 == y2) {
388903b705cfSriastradh			blt_unaligned_box_row(scratch, &extents, t, y1, y1 + 1,
389003b705cfSriastradh					      grid_coverage(SAMPLES_Y, t->bottom) - grid_coverage(SAMPLES_Y, t->top));
389103b705cfSriastradh		} else {
389203b705cfSriastradh			if (pixman_fixed_frac(t->top)) {
389303b705cfSriastradh				blt_unaligned_box_row(scratch, &extents, t, y1, y1 + 1,
389403b705cfSriastradh						      SAMPLES_Y - grid_coverage(SAMPLES_Y, t->top));
389503b705cfSriastradh				y1++;
389603b705cfSriastradh			}
389703b705cfSriastradh
389803b705cfSriastradh			if (y2 > y1)
389903b705cfSriastradh				blt_unaligned_box_row(scratch, &extents, t, y1, y2,
390003b705cfSriastradh						      SAMPLES_Y);
390103b705cfSriastradh
390203b705cfSriastradh			if (pixman_fixed_frac(t->bottom))
390303b705cfSriastradh				blt_unaligned_box_row(scratch, &extents, t, y2, y2+1,
390403b705cfSriastradh						      grid_coverage(SAMPLES_Y, t->bottom));
390503b705cfSriastradh		}
390603b705cfSriastradh
390703b705cfSriastradh		mask = CreatePicture(0, &scratch->drawable,
390803b705cfSriastradh				     PictureMatchFormat(screen, 8, PICT_a8),
390903b705cfSriastradh				     0, 0, serverClient, &error);
391003b705cfSriastradh		if (mask) {
391103b705cfSriastradh			CompositePicture(op, src, mask, dst,
391203b705cfSriastradh					 src_x + extents.x1 - dst_x,
391303b705cfSriastradh					 src_y + extents.y1 - dst_y,
391403b705cfSriastradh					 0, 0,
391503b705cfSriastradh					 extents.x1, extents.y1,
391603b705cfSriastradh					 extents.x2 - extents.x1,
391703b705cfSriastradh					 extents.y2 - extents.y1);
391803b705cfSriastradh			FreePicture(mask, 0);
391903b705cfSriastradh		}
392003b705cfSriastradh		sna_pixmap_destroy(scratch);
392103b705cfSriastradh	}
392203b705cfSriastradh
392303b705cfSriastradh	return true;
392403b705cfSriastradh}
392503b705cfSriastradh
392603b705cfSriastradhstatic bool
392703b705cfSriastradhcomposite_unaligned_boxes(struct sna *sna,
392803b705cfSriastradh			  CARD8 op,
392903b705cfSriastradh			  PicturePtr src,
393003b705cfSriastradh			  PicturePtr dst,
393103b705cfSriastradh			  PictFormatPtr maskFormat,
393203b705cfSriastradh			  INT16 src_x, INT16 src_y,
393303b705cfSriastradh			  int ntrap, const xTrapezoid *traps,
393403b705cfSriastradh			  bool force_fallback)
393503b705cfSriastradh{
393603b705cfSriastradh	BoxRec extents;
393703b705cfSriastradh	struct sna_composite_spans_op tmp;
393803b705cfSriastradh	struct sna_pixmap *priv;
393903b705cfSriastradh	pixman_region16_t clip, *c;
394003b705cfSriastradh	int16_t dst_x, dst_y;
394103b705cfSriastradh	int dx, dy, n;
394203b705cfSriastradh
394303b705cfSriastradh	if (NO_UNALIGNED_BOXES)
394403b705cfSriastradh		return false;
394503b705cfSriastradh
394603b705cfSriastradh	DBG(("%s: force_fallback=%d, mask=%x, n=%d, op=%d\n",
394703b705cfSriastradh	     __FUNCTION__, force_fallback, maskFormat ? (int)maskFormat->format : 0, ntrap, op));
394803b705cfSriastradh
394903b705cfSriastradh	/* need a span converter to handle overlapping traps */
395003b705cfSriastradh	if (ntrap > 1 && maskFormat)
395103b705cfSriastradh		return false;
395203b705cfSriastradh
395303b705cfSriastradh	if (force_fallback ||
395403b705cfSriastradh	    !sna->render.check_composite_spans(sna, op, src, dst, 0, 0,
395503b705cfSriastradh					       COMPOSITE_SPANS_RECTILINEAR)) {
395603b705cfSriastradhfallback:
395703b705cfSriastradh		return composite_unaligned_boxes_fallback(sna, op, src, dst,
395803b705cfSriastradh							  src_x, src_y,
395903b705cfSriastradh							  ntrap, traps,
396003b705cfSriastradh							  force_fallback);
396103b705cfSriastradh	}
396203b705cfSriastradh
396303b705cfSriastradh	trapezoid_origin(&traps[0].left, &dst_x, &dst_y);
396403b705cfSriastradh
396503b705cfSriastradh	extents.x1 = pixman_fixed_to_int(traps[0].left.p1.x);
396603b705cfSriastradh	extents.x2 = pixman_fixed_to_int(traps[0].right.p1.x + pixman_fixed_1_minus_e);
396703b705cfSriastradh	extents.y1 = pixman_fixed_to_int(traps[0].top);
396803b705cfSriastradh	extents.y2 = pixman_fixed_to_int(traps[0].bottom + pixman_fixed_1_minus_e);
396903b705cfSriastradh
397003b705cfSriastradh	DBG(("%s: src=(%d, %d), dst=(%d, %d)\n",
397103b705cfSriastradh	     __FUNCTION__, src_x, src_y, dst_x, dst_y));
397203b705cfSriastradh
397303b705cfSriastradh	for (n = 1; n < ntrap; n++) {
397403b705cfSriastradh		int x1 = pixman_fixed_to_int(traps[n].left.p1.x);
397503b705cfSriastradh		int x2 = pixman_fixed_to_int(traps[n].right.p1.x + pixman_fixed_1_minus_e);
397603b705cfSriastradh		int y1 = pixman_fixed_to_int(traps[n].top);
397703b705cfSriastradh		int y2 = pixman_fixed_to_int(traps[n].bottom + pixman_fixed_1_minus_e);
397803b705cfSriastradh
397903b705cfSriastradh		if (x1 < extents.x1)
398003b705cfSriastradh			extents.x1 = x1;
398103b705cfSriastradh		if (x2 > extents.x2)
398203b705cfSriastradh			extents.x2 = x2;
398303b705cfSriastradh		if (y1 < extents.y1)
398403b705cfSriastradh			extents.y1 = y1;
398503b705cfSriastradh		if (y2 > extents.y2)
398603b705cfSriastradh			extents.y2 = y2;
398703b705cfSriastradh	}
398803b705cfSriastradh
398903b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n", __FUNCTION__,
399003b705cfSriastradh	     extents.x1, extents.y1, extents.x2, extents.y2));
399103b705cfSriastradh
399203b705cfSriastradh	if (!sna_compute_composite_region(&clip,
399303b705cfSriastradh					  src, NULL, dst,
399403b705cfSriastradh					  src_x + extents.x1 - dst_x,
399503b705cfSriastradh					  src_y + extents.y1 - dst_y,
399603b705cfSriastradh					  0, 0,
399703b705cfSriastradh					  extents.x1, extents.y1,
399803b705cfSriastradh					  extents.x2 - extents.x1,
399903b705cfSriastradh					  extents.y2 - extents.y1)) {
400003b705cfSriastradh		DBG(("%s: trapezoids do not intersect drawable clips\n",
400103b705cfSriastradh		     __FUNCTION__)) ;
400203b705cfSriastradh		return true;
400303b705cfSriastradh	}
400403b705cfSriastradh
400503b705cfSriastradh	if (!sna->render.check_composite_spans(sna, op, src, dst,
400603b705cfSriastradh					       clip.extents.x2 - clip.extents.x1,
400703b705cfSriastradh					       clip.extents.y2 - clip.extents.y1,
400803b705cfSriastradh					       COMPOSITE_SPANS_RECTILINEAR)) {
400903b705cfSriastradh		DBG(("%s: fallback -- composite spans not supported\n",
401003b705cfSriastradh		     __FUNCTION__));
401103b705cfSriastradh		goto fallback;
401203b705cfSriastradh	}
401303b705cfSriastradh
401403b705cfSriastradh	c = NULL;
401503b705cfSriastradh	if (extents.x2 - extents.x1 > clip.extents.x2 - clip.extents.x1 ||
401603b705cfSriastradh	    extents.y2 - extents.y1 > clip.extents.y2 - clip.extents.y1) {
401703b705cfSriastradh		DBG(("%s: forcing clip\n", __FUNCTION__));
401803b705cfSriastradh		c = &clip;
401903b705cfSriastradh	}
402003b705cfSriastradh
402103b705cfSriastradh	extents = *RegionExtents(&clip);
402203b705cfSriastradh	dx = dst->pDrawable->x;
402303b705cfSriastradh	dy = dst->pDrawable->y;
402403b705cfSriastradh
402503b705cfSriastradh	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
402603b705cfSriastradh	     __FUNCTION__,
402703b705cfSriastradh	     extents.x1, extents.y1,
402803b705cfSriastradh	     extents.x2, extents.y2,
402903b705cfSriastradh	     dx, dy,
403003b705cfSriastradh	     src_x + extents.x1 - dst_x - dx,
403103b705cfSriastradh	     src_y + extents.y1 - dst_y - dy));
403203b705cfSriastradh
403303b705cfSriastradh	switch (op) {
403403b705cfSriastradh	case PictOpAdd:
403503b705cfSriastradh	case PictOpOver:
403603b705cfSriastradh		priv = sna_pixmap(get_drawable_pixmap(dst->pDrawable));
403703b705cfSriastradh		assert(priv != NULL);
403803b705cfSriastradh		if (priv->clear && priv->clear_color == 0) {
403903b705cfSriastradh			DBG(("%s: converting %d to PictOpSrc\n",
404003b705cfSriastradh			     __FUNCTION__, op));
404103b705cfSriastradh			op = PictOpSrc;
404203b705cfSriastradh		}
404303b705cfSriastradh		break;
404403b705cfSriastradh	case PictOpIn:
404503b705cfSriastradh		priv = sna_pixmap(get_drawable_pixmap(dst->pDrawable));
404603b705cfSriastradh		assert(priv != NULL);
404703b705cfSriastradh		if (priv->clear && priv->clear_color == 0) {
404803b705cfSriastradh			DBG(("%s: clear destination using In, skipping\n",
404903b705cfSriastradh			     __FUNCTION__));
405003b705cfSriastradh			return true;
405103b705cfSriastradh		}
405203b705cfSriastradh		break;
405303b705cfSriastradh	}
405403b705cfSriastradh
405503b705cfSriastradh	if (!sna->render.composite_spans(sna, op, src, dst,
405603b705cfSriastradh					 src_x + extents.x1 - dst_x - dx,
405703b705cfSriastradh					 src_y + extents.y1 - dst_y - dy,
405803b705cfSriastradh					 extents.x1,  extents.y1,
405903b705cfSriastradh					 extents.x2 - extents.x1,
406003b705cfSriastradh					 extents.y2 - extents.y1,
406103b705cfSriastradh					 COMPOSITE_SPANS_RECTILINEAR,
406203b705cfSriastradh					 memset(&tmp, 0, sizeof(tmp)))) {
406303b705cfSriastradh		DBG(("%s: composite spans render op not supported\n",
406403b705cfSriastradh		     __FUNCTION__));
406503b705cfSriastradh		REGION_UNINIT(NULL, &clip);
406603b705cfSriastradh		goto fallback;
406703b705cfSriastradh	}
406803b705cfSriastradh
406903b705cfSriastradh	for (n = 0; n < ntrap; n++)
407003b705cfSriastradh		composite_unaligned_trap(sna, &tmp, &traps[n], dx, dy, c);
407103b705cfSriastradh	tmp.done(sna, &tmp);
407203b705cfSriastradh	REGION_UNINIT(NULL, &clip);
407303b705cfSriastradh	return true;
407403b705cfSriastradh}
407503b705cfSriastradh
407603b705cfSriastradhstatic inline int pixman_fixed_to_grid (pixman_fixed_t v)
407703b705cfSriastradh{
407803b705cfSriastradh	return (v + ((1<<(16-FAST_SAMPLES_shift))-1)/2) >> (16 - FAST_SAMPLES_shift);
407903b705cfSriastradh}
408003b705cfSriastradh
408103b705cfSriastradhstatic inline bool
408203b705cfSriastradhproject_trapezoid_onto_grid(const xTrapezoid *in,
408303b705cfSriastradh			    int dx, int dy,
408403b705cfSriastradh			    xTrapezoid *out)
408503b705cfSriastradh{
408603b705cfSriastradh	__DBG(("%s: in: L:(%d, %d), (%d, %d); R:(%d, %d), (%d, %d), [%d, %d]\n",
408703b705cfSriastradh	       __FUNCTION__,
408803b705cfSriastradh	       in->left.p1.x, in->left.p1.y, in->left.p2.x, in->left.p2.y,
408903b705cfSriastradh	       in->right.p1.x, in->right.p1.y, in->right.p2.x, in->right.p2.y,
409003b705cfSriastradh	       in->top, in->bottom));
409103b705cfSriastradh
409203b705cfSriastradh	out->left.p1.x = dx + pixman_fixed_to_grid(in->left.p1.x);
409303b705cfSriastradh	out->left.p1.y = dy + pixman_fixed_to_grid(in->left.p1.y);
409403b705cfSriastradh	out->left.p2.x = dx + pixman_fixed_to_grid(in->left.p2.x);
409503b705cfSriastradh	out->left.p2.y = dy + pixman_fixed_to_grid(in->left.p2.y);
409603b705cfSriastradh
409703b705cfSriastradh	out->right.p1.x = dx + pixman_fixed_to_grid(in->right.p1.x);
409803b705cfSriastradh	out->right.p1.y = dy + pixman_fixed_to_grid(in->right.p1.y);
409903b705cfSriastradh	out->right.p2.x = dx + pixman_fixed_to_grid(in->right.p2.x);
410003b705cfSriastradh	out->right.p2.y = dy + pixman_fixed_to_grid(in->right.p2.y);
410103b705cfSriastradh
410203b705cfSriastradh	out->top = dy + pixman_fixed_to_grid(in->top);
410303b705cfSriastradh	out->bottom = dy + pixman_fixed_to_grid(in->bottom);
410403b705cfSriastradh
410503b705cfSriastradh	__DBG(("%s: out: L:(%d, %d), (%d, %d); R:(%d, %d), (%d, %d), [%d, %d]\n",
410603b705cfSriastradh	       __FUNCTION__,
410703b705cfSriastradh	       out->left.p1.x, out->left.p1.y, out->left.p2.x, out->left.p2.y,
410803b705cfSriastradh	       out->right.p1.x, out->right.p1.y, out->right.p2.x, out->right.p2.y,
410903b705cfSriastradh	       out->top, out->bottom));
411003b705cfSriastradh
411103b705cfSriastradh	return xTrapezoidValid(out);
411203b705cfSriastradh}
411303b705cfSriastradh
411403b705cfSriastradhstatic span_func_t
411503b705cfSriastradhchoose_span(struct sna_composite_spans_op *tmp,
411603b705cfSriastradh	    PicturePtr dst,
411703b705cfSriastradh	    PictFormatPtr maskFormat,
411803b705cfSriastradh	    RegionPtr clip)
411903b705cfSriastradh{
412003b705cfSriastradh	span_func_t span;
412103b705cfSriastradh
412203b705cfSriastradh	if (is_mono(dst, maskFormat)) {
412303b705cfSriastradh		/* XXX An imprecise approximation */
412403b705cfSriastradh		if (maskFormat && !operator_is_bounded(tmp->base.op)) {
412503b705cfSriastradh			span = tor_blt_span_mono_unbounded;
412603b705cfSriastradh			if (clip->data)
412703b705cfSriastradh				span = tor_blt_span_mono_unbounded_clipped;
412803b705cfSriastradh		} else {
412903b705cfSriastradh			span = tor_blt_span_mono;
413003b705cfSriastradh			if (clip->data)
413103b705cfSriastradh				span = tor_blt_span_mono_clipped;
413203b705cfSriastradh		}
413303b705cfSriastradh	} else {
413403b705cfSriastradh		if (clip->data)
413503b705cfSriastradh			span = tor_blt_span_clipped;
413603b705cfSriastradh		else if (tmp->base.damage == NULL)
413703b705cfSriastradh			span = tor_blt_span__no_damage;
413803b705cfSriastradh		else
413903b705cfSriastradh			span = tor_blt_span;
414003b705cfSriastradh	}
414103b705cfSriastradh
414203b705cfSriastradh	return span;
414303b705cfSriastradh}
414403b705cfSriastradh
414503b705cfSriastradhstruct mono_span_thread {
414603b705cfSriastradh	struct sna *sna;
414703b705cfSriastradh	const xTrapezoid *traps;
414803b705cfSriastradh	const struct sna_composite_op *op;
414903b705cfSriastradh	RegionPtr clip;
415003b705cfSriastradh	int ntrap;
415103b705cfSriastradh	BoxRec extents;
415203b705cfSriastradh	int dx, dy;
415303b705cfSriastradh};
415403b705cfSriastradh
415503b705cfSriastradhstatic void
415603b705cfSriastradhmono_span_thread(void *arg)
415703b705cfSriastradh{
415803b705cfSriastradh	struct mono_span_thread *thread = arg;
415903b705cfSriastradh	struct mono mono;
416003b705cfSriastradh	struct mono_span_thread_boxes boxes;
416103b705cfSriastradh	const xTrapezoid *t;
416203b705cfSriastradh	int n;
416303b705cfSriastradh
416403b705cfSriastradh	mono.sna = thread->sna;
416503b705cfSriastradh
416603b705cfSriastradh	mono.clip.extents = thread->extents;
416703b705cfSriastradh	mono.clip.data = NULL;
416803b705cfSriastradh	if (thread->clip->data) {
416903b705cfSriastradh		RegionIntersect(&mono.clip, &mono.clip, thread->clip);
417003b705cfSriastradh		if (RegionNil(&mono.clip))
417103b705cfSriastradh			return;
417203b705cfSriastradh	}
417303b705cfSriastradh
417403b705cfSriastradh	boxes.op = thread->op;
417503b705cfSriastradh	boxes.num_boxes = 0;
417603b705cfSriastradh	mono.op.priv = &boxes;
417703b705cfSriastradh
417803b705cfSriastradh	if (!mono_init(&mono, 2*thread->ntrap)) {
417903b705cfSriastradh		RegionUninit(&mono.clip);
418003b705cfSriastradh		return;
418103b705cfSriastradh	}
418203b705cfSriastradh
418303b705cfSriastradh	for (n = thread->ntrap, t = thread->traps; n--; t++) {
418403b705cfSriastradh		if (!xTrapezoidValid(t))
418503b705cfSriastradh			continue;
418603b705cfSriastradh
418703b705cfSriastradh		if (pixman_fixed_to_int(t->top) + thread->dy >= thread->extents.y2 ||
418803b705cfSriastradh		    pixman_fixed_to_int(t->bottom) + thread->dy <= thread->extents.y1)
418903b705cfSriastradh			continue;
419003b705cfSriastradh
419103b705cfSriastradh		mono_add_line(&mono, thread->dx, thread->dy,
419203b705cfSriastradh			      t->top, t->bottom,
419303b705cfSriastradh			      &t->left.p1, &t->left.p2, 1);
419403b705cfSriastradh		mono_add_line(&mono, thread->dx, thread->dy,
419503b705cfSriastradh			      t->top, t->bottom,
419603b705cfSriastradh			      &t->right.p1, &t->right.p2, -1);
419703b705cfSriastradh	}
419803b705cfSriastradh
419903b705cfSriastradh	if (mono.clip.data == NULL)
420003b705cfSriastradh		mono.span = thread_mono_span;
420103b705cfSriastradh	else
420203b705cfSriastradh		mono.span = thread_mono_span_clipped;
420303b705cfSriastradh
420403b705cfSriastradh	mono_render(&mono);
420503b705cfSriastradh	mono_fini(&mono);
420603b705cfSriastradh
420703b705cfSriastradh	if (boxes.num_boxes)
420803b705cfSriastradh		thread->op->thread_boxes(thread->sna, thread->op,
420903b705cfSriastradh					 boxes.boxes, boxes.num_boxes);
421003b705cfSriastradh	RegionUninit(&mono.clip);
421103b705cfSriastradh}
421203b705cfSriastradh
421303b705cfSriastradhstatic bool
421403b705cfSriastradhmono_trapezoids_span_converter(struct sna *sna,
421503b705cfSriastradh			       CARD8 op, PicturePtr src, PicturePtr dst,
421603b705cfSriastradh			       INT16 src_x, INT16 src_y,
421703b705cfSriastradh			       int ntrap, xTrapezoid *traps)
421803b705cfSriastradh{
421903b705cfSriastradh	struct mono mono;
422003b705cfSriastradh	BoxRec extents;
422103b705cfSriastradh	int16_t dst_x, dst_y;
422203b705cfSriastradh	int16_t dx, dy;
422303b705cfSriastradh	bool unbounded;
422403b705cfSriastradh	int num_threads, n;
422503b705cfSriastradh
422603b705cfSriastradh	if (NO_SCAN_CONVERTER)
422703b705cfSriastradh		return false;
422803b705cfSriastradh
422903b705cfSriastradh	trapezoid_origin(&traps[0].left, &dst_x, &dst_y);
423003b705cfSriastradh
423103b705cfSriastradh	trapezoids_bounds(ntrap, traps, &extents);
423203b705cfSriastradh	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
423303b705cfSriastradh		return true;
423403b705cfSriastradh
423503b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
423603b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
423703b705cfSriastradh
423803b705cfSriastradh	if (!sna_compute_composite_region(&mono.clip,
423903b705cfSriastradh					  src, NULL, dst,
424003b705cfSriastradh					  src_x + extents.x1 - dst_x,
424103b705cfSriastradh					  src_y + extents.y1 - dst_y,
424203b705cfSriastradh					  0, 0,
424303b705cfSriastradh					  extents.x1, extents.y1,
424403b705cfSriastradh					  extents.x2 - extents.x1,
424503b705cfSriastradh					  extents.y2 - extents.y1)) {
424603b705cfSriastradh		DBG(("%s: trapezoids do not intersect drawable clips\n",
424703b705cfSriastradh		     __FUNCTION__)) ;
424803b705cfSriastradh		return true;
424903b705cfSriastradh	}
425003b705cfSriastradh
425103b705cfSriastradh	dx = dst->pDrawable->x;
425203b705cfSriastradh	dy = dst->pDrawable->y;
425303b705cfSriastradh
425403b705cfSriastradh	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
425503b705cfSriastradh	     __FUNCTION__,
425603b705cfSriastradh	     mono.clip.extents.x1, mono.clip.extents.y1,
425703b705cfSriastradh	     mono.clip.extents.x2, mono.clip.extents.y2,
425803b705cfSriastradh	     dx, dy,
425903b705cfSriastradh	     src_x + mono.clip.extents.x1 - dst_x - dx,
426003b705cfSriastradh	     src_y + mono.clip.extents.y1 - dst_y - dy));
426103b705cfSriastradh
426203b705cfSriastradh	unbounded = (!sna_drawable_is_clear(dst->pDrawable) &&
426303b705cfSriastradh		     !operator_is_bounded(op));
426403b705cfSriastradh
426503b705cfSriastradh	mono.sna = sna;
426603b705cfSriastradh	if (!mono.sna->render.composite(mono.sna, op, src, NULL, dst,
426703b705cfSriastradh				       src_x + mono.clip.extents.x1 - dst_x - dx,
426803b705cfSriastradh				       src_y + mono.clip.extents.y1 - dst_y - dy,
426903b705cfSriastradh				       0, 0,
427003b705cfSriastradh				       mono.clip.extents.x1,  mono.clip.extents.y1,
427103b705cfSriastradh				       mono.clip.extents.x2 - mono.clip.extents.x1,
427203b705cfSriastradh				       mono.clip.extents.y2 - mono.clip.extents.y1,
427303b705cfSriastradh				       memset(&mono.op, 0, sizeof(mono.op))))
427403b705cfSriastradh		return false;
427503b705cfSriastradh
427603b705cfSriastradh	num_threads = 1;
427703b705cfSriastradh	if (!NO_GPU_THREADS &&
427803b705cfSriastradh	    mono.op.thread_boxes &&
427903b705cfSriastradh	    mono.op.damage == NULL &&
428003b705cfSriastradh	    !unbounded)
428103b705cfSriastradh		num_threads = sna_use_threads(mono.clip.extents.x2 - mono.clip.extents.x1,
428203b705cfSriastradh					      mono.clip.extents.y2 - mono.clip.extents.y1,
428303b705cfSriastradh					      32);
428403b705cfSriastradh	if (num_threads > 1) {
428503b705cfSriastradh		struct mono_span_thread threads[num_threads];
428603b705cfSriastradh		int y, h;
428703b705cfSriastradh
428803b705cfSriastradh		DBG(("%s: using %d threads for mono span compositing %dx%d\n",
428903b705cfSriastradh		     __FUNCTION__, num_threads,
429003b705cfSriastradh		     mono.clip.extents.x2 - mono.clip.extents.x1,
429103b705cfSriastradh		     mono.clip.extents.y2 - mono.clip.extents.y1));
429203b705cfSriastradh
429303b705cfSriastradh		threads[0].sna = mono.sna;
429403b705cfSriastradh		threads[0].op = &mono.op;
429503b705cfSriastradh		threads[0].traps = traps;
429603b705cfSriastradh		threads[0].ntrap = ntrap;
429703b705cfSriastradh		threads[0].extents = mono.clip.extents;
429803b705cfSriastradh		threads[0].clip = &mono.clip;
429903b705cfSriastradh		threads[0].dx = dx;
430003b705cfSriastradh		threads[0].dy = dy;
430103b705cfSriastradh
430203b705cfSriastradh		y = extents.y1;
430303b705cfSriastradh		h = extents.y2 - extents.y1;
430403b705cfSriastradh		h = (h + num_threads - 1) / num_threads;
430503b705cfSriastradh
430603b705cfSriastradh		for (n = 1; n < num_threads; n++) {
430703b705cfSriastradh			threads[n] = threads[0];
430803b705cfSriastradh			threads[n].extents.y1 = y;
430903b705cfSriastradh			threads[n].extents.y2 = y += h;
431003b705cfSriastradh
431103b705cfSriastradh			sna_threads_run(mono_span_thread, &threads[n]);
431203b705cfSriastradh		}
431303b705cfSriastradh
431403b705cfSriastradh		threads[0].extents.y1 = y;
431503b705cfSriastradh		threads[0].extents.y2 = extents.y2;
431603b705cfSriastradh		mono_span_thread(&threads[0]);
431703b705cfSriastradh
431803b705cfSriastradh		sna_threads_wait();
431903b705cfSriastradh		mono.op.done(mono.sna, &mono.op);
432003b705cfSriastradh		return true;
432103b705cfSriastradh	}
432203b705cfSriastradh
432303b705cfSriastradh	if (!mono_init(&mono, 2*ntrap))
432403b705cfSriastradh		return false;
432503b705cfSriastradh
432603b705cfSriastradh	for (n = 0; n < ntrap; n++) {
432703b705cfSriastradh		if (!xTrapezoidValid(&traps[n]))
432803b705cfSriastradh			continue;
432903b705cfSriastradh
433003b705cfSriastradh		if (pixman_fixed_to_int(traps[n].top) + dy >= mono.clip.extents.y2 ||
433103b705cfSriastradh		    pixman_fixed_to_int(traps[n].bottom) + dy < mono.clip.extents.y1)
433203b705cfSriastradh			continue;
433303b705cfSriastradh
433403b705cfSriastradh		mono_add_line(&mono, dx, dy,
433503b705cfSriastradh			      traps[n].top, traps[n].bottom,
433603b705cfSriastradh			      &traps[n].left.p1, &traps[n].left.p2, 1);
433703b705cfSriastradh		mono_add_line(&mono, dx, dy,
433803b705cfSriastradh			      traps[n].top, traps[n].bottom,
433903b705cfSriastradh			      &traps[n].right.p1, &traps[n].right.p2, -1);
434003b705cfSriastradh	}
434103b705cfSriastradh
434203b705cfSriastradh	if (mono.clip.data == NULL && mono.op.damage == NULL)
434303b705cfSriastradh		mono.span = mono_span__fast;
434403b705cfSriastradh	else
434503b705cfSriastradh		mono.span = mono_span;
434603b705cfSriastradh
434703b705cfSriastradh	mono_render(&mono);
434803b705cfSriastradh	mono.op.done(mono.sna, &mono.op);
434903b705cfSriastradh	mono_fini(&mono);
435003b705cfSriastradh
435103b705cfSriastradh	if (unbounded) {
435203b705cfSriastradh		xPointFixed p1, p2;
435303b705cfSriastradh
435403b705cfSriastradh		if (!mono_init(&mono, 2+2*ntrap))
435503b705cfSriastradh			return false;
435603b705cfSriastradh
435703b705cfSriastradh		p1.y = mono.clip.extents.y1 * pixman_fixed_1;
435803b705cfSriastradh		p2.y = mono.clip.extents.y2 * pixman_fixed_1;
435903b705cfSriastradh
436003b705cfSriastradh		p1.x = mono.clip.extents.x1 * pixman_fixed_1;
436103b705cfSriastradh		p2.x = mono.clip.extents.x1 * pixman_fixed_1;
436203b705cfSriastradh		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
436303b705cfSriastradh
436403b705cfSriastradh		p1.x = mono.clip.extents.x2 * pixman_fixed_1;
436503b705cfSriastradh		p2.x = mono.clip.extents.x2 * pixman_fixed_1;
436603b705cfSriastradh		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
436703b705cfSriastradh
436803b705cfSriastradh		for (n = 0; n < ntrap; n++) {
436903b705cfSriastradh			if (!xTrapezoidValid(&traps[n]))
437003b705cfSriastradh				continue;
437103b705cfSriastradh
437203b705cfSriastradh			if (pixman_fixed_to_int(traps[n].top) + dy >= mono.clip.extents.y2 ||
437303b705cfSriastradh			    pixman_fixed_to_int(traps[n].bottom) + dy < mono.clip.extents.y1)
437403b705cfSriastradh				continue;
437503b705cfSriastradh
437603b705cfSriastradh			mono_add_line(&mono, dx, dy,
437703b705cfSriastradh				      traps[n].top, traps[n].bottom,
437803b705cfSriastradh				      &traps[n].left.p1, &traps[n].left.p2, 1);
437903b705cfSriastradh			mono_add_line(&mono, dx, dy,
438003b705cfSriastradh				      traps[n].top, traps[n].bottom,
438103b705cfSriastradh				      &traps[n].right.p1, &traps[n].right.p2, -1);
438203b705cfSriastradh		}
438303b705cfSriastradh		memset(&mono.op, 0, sizeof(mono.op));
438403b705cfSriastradh		if (mono.sna->render.composite(mono.sna,
438503b705cfSriastradh					       PictOpClear,
438603b705cfSriastradh					       mono.sna->clear, NULL, dst,
438703b705cfSriastradh					       0, 0,
438803b705cfSriastradh					       0, 0,
438903b705cfSriastradh					       mono.clip.extents.x1,  mono.clip.extents.y1,
439003b705cfSriastradh					       mono.clip.extents.x2 - mono.clip.extents.x1,
439103b705cfSriastradh					       mono.clip.extents.y2 - mono.clip.extents.y1,
439203b705cfSriastradh					       &mono.op)) {
439303b705cfSriastradh			mono_render(&mono);
439403b705cfSriastradh			mono.op.done(mono.sna, &mono.op);
439503b705cfSriastradh		}
439603b705cfSriastradh		mono_fini(&mono);
439703b705cfSriastradh	}
439803b705cfSriastradh
439903b705cfSriastradh	REGION_UNINIT(NULL, &mono.clip);
440003b705cfSriastradh	return true;
440103b705cfSriastradh}
440203b705cfSriastradh
440303b705cfSriastradhstruct span_thread {
440403b705cfSriastradh	struct sna *sna;
440503b705cfSriastradh	const struct sna_composite_spans_op *op;
440603b705cfSriastradh	const xTrapezoid *traps;
440703b705cfSriastradh	RegionPtr clip;
440803b705cfSriastradh	span_func_t span;
440903b705cfSriastradh	BoxRec extents;
441003b705cfSriastradh	int dx, dy, draw_y;
441103b705cfSriastradh	int ntrap;
441203b705cfSriastradh	bool unbounded;
441303b705cfSriastradh};
441403b705cfSriastradh
441503b705cfSriastradh#define SPAN_THREAD_MAX_BOXES (8192/sizeof(struct sna_opacity_box))
441603b705cfSriastradhstruct span_thread_boxes {
441703b705cfSriastradh	const struct sna_composite_spans_op *op;
441803b705cfSriastradh	struct sna_opacity_box boxes[SPAN_THREAD_MAX_BOXES];
441903b705cfSriastradh	int num_boxes;
442003b705cfSriastradh};
442103b705cfSriastradh
442203b705cfSriastradhstatic void span_thread_add_boxes(struct sna *sna, void *data,
442303b705cfSriastradh				  const BoxRec *box, int count, float alpha)
442403b705cfSriastradh{
442503b705cfSriastradh	struct span_thread_boxes *b = data;
442603b705cfSriastradh
442703b705cfSriastradh	__DBG(("%s: adding %d boxes with alpha=%f\n",
442803b705cfSriastradh	       __FUNCTION__, count, alpha));
442903b705cfSriastradh
443003b705cfSriastradh	assert(count > 0 && count <= SPAN_THREAD_MAX_BOXES);
443103b705cfSriastradh	if (unlikely(b->num_boxes + count > SPAN_THREAD_MAX_BOXES)) {
443203b705cfSriastradh		DBG(("%s: flushing %d boxes, adding %d\n", __FUNCTION__, b->num_boxes, count));
443303b705cfSriastradh		assert(b->num_boxes <= SPAN_THREAD_MAX_BOXES);
443403b705cfSriastradh		b->op->thread_boxes(sna, b->op, b->boxes, b->num_boxes);
443503b705cfSriastradh		b->num_boxes = 0;
443603b705cfSriastradh	}
443703b705cfSriastradh
443803b705cfSriastradh	do {
443903b705cfSriastradh		b->boxes[b->num_boxes].box = *box++;
444003b705cfSriastradh		b->boxes[b->num_boxes].alpha = alpha;
444103b705cfSriastradh		b->num_boxes++;
444203b705cfSriastradh	} while (--count);
444303b705cfSriastradh	assert(b->num_boxes <= SPAN_THREAD_MAX_BOXES);
444403b705cfSriastradh}
444503b705cfSriastradh
444603b705cfSriastradhstatic void
444703b705cfSriastradhspan_thread_box(struct sna *sna,
444803b705cfSriastradh		struct sna_composite_spans_op *op,
444903b705cfSriastradh		pixman_region16_t *clip,
445003b705cfSriastradh		const BoxRec *box,
445103b705cfSriastradh		int coverage)
445203b705cfSriastradh{
445303b705cfSriastradh	__DBG(("%s: %d -> %d @ %d\n", __FUNCTION__, box->x1, box->x2, coverage));
445403b705cfSriastradh	span_thread_add_boxes(sna, op, box, 1, AREA_TO_ALPHA(coverage));
445503b705cfSriastradh}
445603b705cfSriastradh
445703b705cfSriastradhstatic void
445803b705cfSriastradhspan_thread_clipped_box(struct sna *sna,
445903b705cfSriastradh			struct sna_composite_spans_op *op,
446003b705cfSriastradh			pixman_region16_t *clip,
446103b705cfSriastradh			const BoxRec *box,
446203b705cfSriastradh			int coverage)
446303b705cfSriastradh{
446403b705cfSriastradh	pixman_region16_t region;
446503b705cfSriastradh
446603b705cfSriastradh	__DBG(("%s: %d -> %d @ %f\n", __FUNCTION__, box->x1, box->x2,
446703b705cfSriastradh	       AREA_TO_ALPHA(coverage)));
446803b705cfSriastradh
446903b705cfSriastradh	pixman_region_init_rects(&region, box, 1);
447003b705cfSriastradh	RegionIntersect(&region, &region, clip);
447103b705cfSriastradh	if (REGION_NUM_RECTS(&region)) {
447203b705cfSriastradh		span_thread_add_boxes(sna, op,
447303b705cfSriastradh				      REGION_RECTS(&region),
447403b705cfSriastradh				      REGION_NUM_RECTS(&region),
447503b705cfSriastradh				      AREA_TO_ALPHA(coverage));
447603b705cfSriastradh	}
447703b705cfSriastradh	pixman_region_fini(&region);
447803b705cfSriastradh}
447903b705cfSriastradh
448003b705cfSriastradhstatic span_func_t
448103b705cfSriastradhthread_choose_span(struct sna_composite_spans_op *tmp,
448203b705cfSriastradh		   PicturePtr dst,
448303b705cfSriastradh		   PictFormatPtr maskFormat,
448403b705cfSriastradh		   RegionPtr clip)
448503b705cfSriastradh{
448603b705cfSriastradh	span_func_t span;
448703b705cfSriastradh
448803b705cfSriastradh	if (tmp->base.damage) {
448903b705cfSriastradh		DBG(("%s: damaged -> no thread support\n", __FUNCTION__));
449003b705cfSriastradh		return NULL;
449103b705cfSriastradh	}
449203b705cfSriastradh
449303b705cfSriastradh	if (is_mono(dst, maskFormat)) {
449403b705cfSriastradh		DBG(("%s: mono rendering -> no thread support\n", __FUNCTION__));
449503b705cfSriastradh		return NULL;
449603b705cfSriastradh	} else {
449703b705cfSriastradh		assert(tmp->thread_boxes);
449803b705cfSriastradh		DBG(("%s: clipped? %d\n", __FUNCTION__, clip->data != NULL));
449903b705cfSriastradh		if (clip->data)
450003b705cfSriastradh			span = span_thread_clipped_box;
450103b705cfSriastradh		else
450203b705cfSriastradh			span = span_thread_box;
450303b705cfSriastradh	}
450403b705cfSriastradh
450503b705cfSriastradh	return span;
450603b705cfSriastradh}
450703b705cfSriastradh
450803b705cfSriastradhstatic void
450903b705cfSriastradhspan_thread(void *arg)
451003b705cfSriastradh{
451103b705cfSriastradh	struct span_thread *thread = arg;
451203b705cfSriastradh	struct span_thread_boxes boxes;
451303b705cfSriastradh	struct tor tor;
451403b705cfSriastradh	const xTrapezoid *t;
451503b705cfSriastradh	int n, y1, y2;
451603b705cfSriastradh
451703b705cfSriastradh	if (!tor_init(&tor, &thread->extents, 2*thread->ntrap))
451803b705cfSriastradh		return;
451903b705cfSriastradh
452003b705cfSriastradh	boxes.op = thread->op;
452103b705cfSriastradh	boxes.num_boxes = 0;
452203b705cfSriastradh
452303b705cfSriastradh	y1 = thread->extents.y1 - thread->draw_y;
452403b705cfSriastradh	y2 = thread->extents.y2 - thread->draw_y;
452503b705cfSriastradh	for (n = thread->ntrap, t = thread->traps; n--; t++) {
452603b705cfSriastradh		xTrapezoid tt;
452703b705cfSriastradh
452803b705cfSriastradh		if (pixman_fixed_to_int(t->top) >= y2 ||
452903b705cfSriastradh		    pixman_fixed_to_int(t->bottom) < y1)
453003b705cfSriastradh			continue;
453103b705cfSriastradh
453203b705cfSriastradh		if (!project_trapezoid_onto_grid(t, thread->dx, thread->dy, &tt))
453303b705cfSriastradh			continue;
453403b705cfSriastradh
453503b705cfSriastradh		tor_add_edge(&tor, &tt, &tt.left, 1);
453603b705cfSriastradh		tor_add_edge(&tor, &tt, &tt.right, -1);
453703b705cfSriastradh	}
453803b705cfSriastradh
453903b705cfSriastradh	tor_render(thread->sna, &tor,
454003b705cfSriastradh		   (struct sna_composite_spans_op *)&boxes, thread->clip,
454103b705cfSriastradh		   thread->span, thread->unbounded);
454203b705cfSriastradh
454303b705cfSriastradh	tor_fini(&tor);
454403b705cfSriastradh
454503b705cfSriastradh	if (boxes.num_boxes) {
454603b705cfSriastradh		DBG(("%s: flushing %d boxes\n", __FUNCTION__, boxes.num_boxes));
454703b705cfSriastradh		assert(boxes.num_boxes <= SPAN_THREAD_MAX_BOXES);
454803b705cfSriastradh		thread->op->thread_boxes(thread->sna, thread->op,
454903b705cfSriastradh					 boxes.boxes, boxes.num_boxes);
455003b705cfSriastradh	}
455103b705cfSriastradh}
455203b705cfSriastradh
455303b705cfSriastradhstatic bool
455403b705cfSriastradhtrapezoid_span_converter(struct sna *sna,
455503b705cfSriastradh			 CARD8 op, PicturePtr src, PicturePtr dst,
455603b705cfSriastradh			 PictFormatPtr maskFormat, unsigned int flags,
455703b705cfSriastradh			 INT16 src_x, INT16 src_y,
455803b705cfSriastradh			 int ntrap, xTrapezoid *traps)
455903b705cfSriastradh{
456003b705cfSriastradh	struct sna_composite_spans_op tmp;
456103b705cfSriastradh	BoxRec extents;
456203b705cfSriastradh	pixman_region16_t clip;
456303b705cfSriastradh	int16_t dst_x, dst_y;
456403b705cfSriastradh	bool was_clear;
456503b705cfSriastradh	int dx, dy, n;
456603b705cfSriastradh	int num_threads;
456703b705cfSriastradh
456803b705cfSriastradh	if (NO_SCAN_CONVERTER)
456903b705cfSriastradh		return false;
457003b705cfSriastradh
457103b705cfSriastradh	if (is_mono(dst, maskFormat))
457203b705cfSriastradh		return mono_trapezoids_span_converter(sna, op, src, dst,
457303b705cfSriastradh						      src_x, src_y,
457403b705cfSriastradh						      ntrap, traps);
457503b705cfSriastradh
457603b705cfSriastradh	/* XXX strict adherence to the Render specification */
457703b705cfSriastradh	if (dst->polyMode == PolyModePrecise) {
457803b705cfSriastradh		DBG(("%s: fallback -- precise rasterisation requested\n",
457903b705cfSriastradh		     __FUNCTION__));
458003b705cfSriastradh		return false;
458103b705cfSriastradh	}
458203b705cfSriastradh
458303b705cfSriastradh	if (!sna->render.check_composite_spans(sna, op, src, dst, 0, 0, flags)) {
458403b705cfSriastradh		DBG(("%s: fallback -- composite spans not supported\n",
458503b705cfSriastradh		     __FUNCTION__));
458603b705cfSriastradh		return false;
458703b705cfSriastradh	}
458803b705cfSriastradh
458903b705cfSriastradh	trapezoid_origin(&traps[0].left, &dst_x, &dst_y);
459003b705cfSriastradh
459103b705cfSriastradh	trapezoids_bounds(ntrap, traps, &extents);
459203b705cfSriastradh	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
459303b705cfSriastradh		return true;
459403b705cfSriastradh
459503b705cfSriastradh#if 0
459603b705cfSriastradh	if (extents.y2 - extents.y1 < 64 && extents.x2 - extents.x1 < 64) {
459703b705cfSriastradh		DBG(("%s: fallback -- traps extents too small %dx%d\n",
459803b705cfSriastradh		     __FUNCTION__, extents.y2 - extents.y1, extents.x2 - extents.x1));
459903b705cfSriastradh		return false;
460003b705cfSriastradh	}
460103b705cfSriastradh#endif
460203b705cfSriastradh
460303b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
460403b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
460503b705cfSriastradh
460603b705cfSriastradh	if (!sna_compute_composite_region(&clip,
460703b705cfSriastradh					  src, NULL, dst,
460803b705cfSriastradh					  src_x + extents.x1 - dst_x,
460903b705cfSriastradh					  src_y + extents.y1 - dst_y,
461003b705cfSriastradh					  0, 0,
461103b705cfSriastradh					  extents.x1, extents.y1,
461203b705cfSriastradh					  extents.x2 - extents.x1,
461303b705cfSriastradh					  extents.y2 - extents.y1)) {
461403b705cfSriastradh		DBG(("%s: trapezoids do not intersect drawable clips\n",
461503b705cfSriastradh		     __FUNCTION__)) ;
461603b705cfSriastradh		return true;
461703b705cfSriastradh	}
461803b705cfSriastradh
461903b705cfSriastradh	if (!sna->render.check_composite_spans(sna, op, src, dst,
462003b705cfSriastradh					       clip.extents.x2 - clip.extents.x1,
462103b705cfSriastradh					       clip.extents.y2 - clip.extents.y1,
462203b705cfSriastradh					       flags)) {
462303b705cfSriastradh		DBG(("%s: fallback -- composite spans not supported\n",
462403b705cfSriastradh		     __FUNCTION__));
462503b705cfSriastradh		return false;
462603b705cfSriastradh	}
462703b705cfSriastradh
462803b705cfSriastradh	extents = *RegionExtents(&clip);
462903b705cfSriastradh	dx = dst->pDrawable->x;
463003b705cfSriastradh	dy = dst->pDrawable->y;
463103b705cfSriastradh
463203b705cfSriastradh	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
463303b705cfSriastradh	     __FUNCTION__,
463403b705cfSriastradh	     extents.x1, extents.y1,
463503b705cfSriastradh	     extents.x2, extents.y2,
463603b705cfSriastradh	     dx, dy,
463703b705cfSriastradh	     src_x + extents.x1 - dst_x - dx,
463803b705cfSriastradh	     src_y + extents.y1 - dst_y - dy));
463903b705cfSriastradh
464003b705cfSriastradh	was_clear = sna_drawable_is_clear(dst->pDrawable);
464103b705cfSriastradh	switch (op) {
464203b705cfSriastradh	case PictOpAdd:
464303b705cfSriastradh	case PictOpOver:
464403b705cfSriastradh		if (was_clear)
464503b705cfSriastradh			op = PictOpSrc;
464603b705cfSriastradh		break;
464703b705cfSriastradh	case PictOpIn:
464803b705cfSriastradh		if (was_clear)
464903b705cfSriastradh			return true;
465003b705cfSriastradh		break;
465103b705cfSriastradh	}
465203b705cfSriastradh
465303b705cfSriastradh	memset(&tmp, 0, sizeof(tmp));
465403b705cfSriastradh	if (!sna->render.composite_spans(sna, op, src, dst,
465503b705cfSriastradh					 src_x + extents.x1 - dst_x - dx,
465603b705cfSriastradh					 src_y + extents.y1 - dst_y - dy,
465703b705cfSriastradh					 extents.x1,  extents.y1,
465803b705cfSriastradh					 extents.x2 - extents.x1,
465903b705cfSriastradh					 extents.y2 - extents.y1,
466003b705cfSriastradh					 flags, &tmp)) {
466103b705cfSriastradh		DBG(("%s: fallback -- composite spans render op not supported\n",
466203b705cfSriastradh		     __FUNCTION__));
466303b705cfSriastradh		return false;
466403b705cfSriastradh	}
466503b705cfSriastradh
466603b705cfSriastradh	dx *= FAST_SAMPLES_X;
466703b705cfSriastradh	dy *= FAST_SAMPLES_Y;
466803b705cfSriastradh
466903b705cfSriastradh	num_threads = 1;
467003b705cfSriastradh	if (!NO_GPU_THREADS && tmp.thread_boxes &&
467103b705cfSriastradh	    thread_choose_span(&tmp, dst, maskFormat, &clip))
467203b705cfSriastradh		num_threads = sna_use_threads(extents.x2-extents.x1,
467303b705cfSriastradh					      extents.y2-extents.y1,
467403b705cfSriastradh					      16);
467503b705cfSriastradh	DBG(("%s: using %d threads\n", __FUNCTION__, num_threads));
467603b705cfSriastradh	if (num_threads == 1) {
467703b705cfSriastradh		struct tor tor;
467803b705cfSriastradh
467903b705cfSriastradh		if (!tor_init(&tor, &extents, 2*ntrap))
468003b705cfSriastradh			goto skip;
468103b705cfSriastradh
468203b705cfSriastradh		for (n = 0; n < ntrap; n++) {
468303b705cfSriastradh			xTrapezoid t;
468403b705cfSriastradh
468503b705cfSriastradh			if (!project_trapezoid_onto_grid(&traps[n], dx, dy, &t))
468603b705cfSriastradh				continue;
468703b705cfSriastradh
468803b705cfSriastradh			if (pixman_fixed_to_int(traps[n].top) + dst->pDrawable->y >= extents.y2 ||
468903b705cfSriastradh			    pixman_fixed_to_int(traps[n].bottom) + dst->pDrawable->y < extents.y1)
469003b705cfSriastradh				continue;
469103b705cfSriastradh
469203b705cfSriastradh			tor_add_edge(&tor, &t, &t.left, 1);
469303b705cfSriastradh			tor_add_edge(&tor, &t, &t.right, -1);
469403b705cfSriastradh		}
469503b705cfSriastradh
469603b705cfSriastradh		tor_render(sna, &tor, &tmp, &clip,
469703b705cfSriastradh			   choose_span(&tmp, dst, maskFormat, &clip),
469803b705cfSriastradh			   !was_clear && maskFormat && !operator_is_bounded(op));
469903b705cfSriastradh
470003b705cfSriastradh		tor_fini(&tor);
470103b705cfSriastradh	} else {
470203b705cfSriastradh		struct span_thread threads[num_threads];
470303b705cfSriastradh		int y, h;
470403b705cfSriastradh
470503b705cfSriastradh		DBG(("%s: using %d threads for span compositing %dx%d\n",
470603b705cfSriastradh		     __FUNCTION__, num_threads,
470703b705cfSriastradh		     extents.x2 - extents.x1,
470803b705cfSriastradh		     extents.y2 - extents.y1));
470903b705cfSriastradh
471003b705cfSriastradh		threads[0].sna = sna;
471103b705cfSriastradh		threads[0].op = &tmp;
471203b705cfSriastradh		threads[0].traps = traps;
471303b705cfSriastradh		threads[0].ntrap = ntrap;
471403b705cfSriastradh		threads[0].extents = extents;
471503b705cfSriastradh		threads[0].clip = &clip;
471603b705cfSriastradh		threads[0].dx = dx;
471703b705cfSriastradh		threads[0].dy = dy;
471803b705cfSriastradh		threads[0].draw_y = dst->pDrawable->y;
471903b705cfSriastradh		threads[0].unbounded = !was_clear && maskFormat && !operator_is_bounded(op);
472003b705cfSriastradh		threads[0].span = thread_choose_span(&tmp, dst, maskFormat, &clip);
472103b705cfSriastradh
472203b705cfSriastradh		y = extents.y1;
472303b705cfSriastradh		h = extents.y2 - extents.y1;
472403b705cfSriastradh		h = (h + num_threads - 1) / num_threads;
472503b705cfSriastradh
472603b705cfSriastradh		for (n = 1; n < num_threads; n++) {
472703b705cfSriastradh			threads[n] = threads[0];
472803b705cfSriastradh			threads[n].extents.y1 = y;
472903b705cfSriastradh			threads[n].extents.y2 = y += h;
473003b705cfSriastradh
473103b705cfSriastradh			sna_threads_run(span_thread, &threads[n]);
473203b705cfSriastradh		}
473303b705cfSriastradh
473403b705cfSriastradh		threads[0].extents.y1 = y;
473503b705cfSriastradh		threads[0].extents.y2 = extents.y2;
473603b705cfSriastradh		span_thread(&threads[0]);
473703b705cfSriastradh
473803b705cfSriastradh		sna_threads_wait();
473903b705cfSriastradh	}
474003b705cfSriastradhskip:
474103b705cfSriastradh	tmp.done(sna, &tmp);
474203b705cfSriastradh
474303b705cfSriastradh	REGION_UNINIT(NULL, &clip);
474403b705cfSriastradh	return true;
474503b705cfSriastradh}
474603b705cfSriastradh
474703b705cfSriastradhstatic void
474803b705cfSriastradhtor_blt_mask(struct sna *sna,
474903b705cfSriastradh	     struct sna_composite_spans_op *op,
475003b705cfSriastradh	     pixman_region16_t *clip,
475103b705cfSriastradh	     const BoxRec *box,
475203b705cfSriastradh	     int coverage)
475303b705cfSriastradh{
475403b705cfSriastradh	uint8_t *ptr = (uint8_t *)op;
475503b705cfSriastradh	int stride = (intptr_t)clip;
475603b705cfSriastradh	int h, w;
475703b705cfSriastradh
475803b705cfSriastradh	coverage = 256 * coverage / FAST_SAMPLES_XY;
475903b705cfSriastradh	coverage -= coverage >> 8;
476003b705cfSriastradh
476103b705cfSriastradh	ptr += box->y1 * stride + box->x1;
476203b705cfSriastradh
476303b705cfSriastradh	h = box->y2 - box->y1;
476403b705cfSriastradh	w = box->x2 - box->x1;
476503b705cfSriastradh	if ((w | h) == 1) {
476603b705cfSriastradh		*ptr = coverage;
476703b705cfSriastradh	} else if (w == 1) {
476803b705cfSriastradh		do {
476903b705cfSriastradh			*ptr = coverage;
477003b705cfSriastradh			ptr += stride;
477103b705cfSriastradh		} while (--h);
477203b705cfSriastradh	} else do {
477303b705cfSriastradh		memset(ptr, coverage, w);
477403b705cfSriastradh		ptr += stride;
477503b705cfSriastradh	} while (--h);
477603b705cfSriastradh}
477703b705cfSriastradh
477803b705cfSriastradhstatic void
477903b705cfSriastradhtor_blt_mask_mono(struct sna *sna,
478003b705cfSriastradh		  struct sna_composite_spans_op *op,
478103b705cfSriastradh		  pixman_region16_t *clip,
478203b705cfSriastradh		  const BoxRec *box,
478303b705cfSriastradh		  int coverage)
478403b705cfSriastradh{
478503b705cfSriastradh	tor_blt_mask(sna, op, clip, box,
478603b705cfSriastradh		     coverage < FAST_SAMPLES_XY/2 ? 0 : FAST_SAMPLES_XY);
478703b705cfSriastradh}
478803b705cfSriastradh
478903b705cfSriastradhstatic bool
479003b705cfSriastradhtrapezoid_mask_converter(CARD8 op, PicturePtr src, PicturePtr dst,
479103b705cfSriastradh			 PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
479203b705cfSriastradh			 int ntrap, xTrapezoid *traps)
479303b705cfSriastradh{
479403b705cfSriastradh	struct tor tor;
479503b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
479603b705cfSriastradh	PixmapPtr scratch;
479703b705cfSriastradh	PicturePtr mask;
479803b705cfSriastradh	BoxRec extents;
479903b705cfSriastradh	int16_t dst_x, dst_y;
480003b705cfSriastradh	int dx, dy;
480103b705cfSriastradh	int error, n;
480203b705cfSriastradh
480303b705cfSriastradh	if (NO_SCAN_CONVERTER)
480403b705cfSriastradh		return false;
480503b705cfSriastradh
480603b705cfSriastradh	if (dst->polyMode == PolyModePrecise && !is_mono(dst, maskFormat)) {
480703b705cfSriastradh		DBG(("%s: fallback -- precise rasterisation requested\n",
480803b705cfSriastradh		     __FUNCTION__));
480903b705cfSriastradh		return false;
481003b705cfSriastradh	}
481103b705cfSriastradh
481203b705cfSriastradh	if (maskFormat == NULL && ntrap > 1) {
481303b705cfSriastradh		DBG(("%s: individual rasterisation requested\n",
481403b705cfSriastradh		     __FUNCTION__));
481503b705cfSriastradh		do {
481603b705cfSriastradh			/* XXX unwind errors? */
481703b705cfSriastradh			if (!trapezoid_mask_converter(op, src, dst, NULL,
481803b705cfSriastradh						 src_x, src_y, 1, traps++))
481903b705cfSriastradh				return false;
482003b705cfSriastradh		} while (--ntrap);
482103b705cfSriastradh		return true;
482203b705cfSriastradh	}
482303b705cfSriastradh
482403b705cfSriastradh	trapezoids_bounds(ntrap, traps, &extents);
482503b705cfSriastradh	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
482603b705cfSriastradh		return true;
482703b705cfSriastradh
482803b705cfSriastradh	DBG(("%s: ntraps=%d, extents (%d, %d), (%d, %d)\n",
482903b705cfSriastradh	     __FUNCTION__, ntrap, extents.x1, extents.y1, extents.x2, extents.y2));
483003b705cfSriastradh
483103b705cfSriastradh	if (!sna_compute_composite_extents(&extents,
483203b705cfSriastradh					   src, NULL, dst,
483303b705cfSriastradh					   src_x, src_y,
483403b705cfSriastradh					   0, 0,
483503b705cfSriastradh					   extents.x1, extents.y1,
483603b705cfSriastradh					   extents.x2 - extents.x1,
483703b705cfSriastradh					   extents.y2 - extents.y1))
483803b705cfSriastradh		return true;
483903b705cfSriastradh
484003b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
484103b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
484203b705cfSriastradh
484303b705cfSriastradh	extents.y2 -= extents.y1;
484403b705cfSriastradh	extents.x2 -= extents.x1;
484503b705cfSriastradh	extents.x1 -= dst->pDrawable->x;
484603b705cfSriastradh	extents.y1 -= dst->pDrawable->y;
484703b705cfSriastradh	dst_x = extents.x1;
484803b705cfSriastradh	dst_y = extents.y1;
484903b705cfSriastradh	dx = -extents.x1 * FAST_SAMPLES_X;
485003b705cfSriastradh	dy = -extents.y1 * FAST_SAMPLES_Y;
485103b705cfSriastradh	extents.x1 = extents.y1 = 0;
485203b705cfSriastradh
485303b705cfSriastradh	DBG(("%s: mask (%dx%d), dx=(%d, %d)\n",
485403b705cfSriastradh	     __FUNCTION__, extents.x2, extents.y2, dx, dy));
485503b705cfSriastradh	scratch = sna_pixmap_create_upload(screen,
485603b705cfSriastradh					   extents.x2, extents.y2, 8,
485703b705cfSriastradh					   KGEM_BUFFER_WRITE_INPLACE);
485803b705cfSriastradh	if (!scratch)
485903b705cfSriastradh		return true;
486003b705cfSriastradh
486103b705cfSriastradh	DBG(("%s: created buffer %p, stride %d\n",
486203b705cfSriastradh	     __FUNCTION__, scratch->devPrivate.ptr, scratch->devKind));
486303b705cfSriastradh
486403b705cfSriastradh	if (!tor_init(&tor, &extents, 2*ntrap)) {
486503b705cfSriastradh		sna_pixmap_destroy(scratch);
486603b705cfSriastradh		return true;
486703b705cfSriastradh	}
486803b705cfSriastradh
486903b705cfSriastradh	for (n = 0; n < ntrap; n++) {
487003b705cfSriastradh		xTrapezoid t;
487103b705cfSriastradh
487203b705cfSriastradh		if (!project_trapezoid_onto_grid(&traps[n], dx, dy, &t))
487303b705cfSriastradh			continue;
487403b705cfSriastradh
487503b705cfSriastradh		if (pixman_fixed_to_int(traps[n].top) - dst_y >= extents.y2 ||
487603b705cfSriastradh		    pixman_fixed_to_int(traps[n].bottom) - dst_y < 0)
487703b705cfSriastradh			continue;
487803b705cfSriastradh
487903b705cfSriastradh		tor_add_edge(&tor, &t, &t.left, 1);
488003b705cfSriastradh		tor_add_edge(&tor, &t, &t.right, -1);
488103b705cfSriastradh	}
488203b705cfSriastradh
488303b705cfSriastradh	if (extents.x2 <= TOR_INPLACE_SIZE) {
488403b705cfSriastradh		uint8_t buf[TOR_INPLACE_SIZE];
488503b705cfSriastradh		tor_inplace(&tor, scratch, is_mono(dst, maskFormat),
488603b705cfSriastradh			    scratch->usage_hint ? NULL : buf);
488703b705cfSriastradh	} else {
488803b705cfSriastradh		tor_render(NULL, &tor,
488903b705cfSriastradh			   scratch->devPrivate.ptr,
489003b705cfSriastradh			   (void *)(intptr_t)scratch->devKind,
489103b705cfSriastradh			   is_mono(dst, maskFormat) ? tor_blt_mask_mono : tor_blt_mask,
489203b705cfSriastradh			   true);
489303b705cfSriastradh	}
489403b705cfSriastradh	tor_fini(&tor);
489503b705cfSriastradh
489603b705cfSriastradh	mask = CreatePicture(0, &scratch->drawable,
489703b705cfSriastradh			     PictureMatchFormat(screen, 8, PICT_a8),
489803b705cfSriastradh			     0, 0, serverClient, &error);
489903b705cfSriastradh	if (mask) {
490003b705cfSriastradh		CompositePicture(op, src, mask, dst,
490103b705cfSriastradh				 src_x + dst_x - pixman_fixed_to_int(traps[0].left.p1.x),
490203b705cfSriastradh				 src_y + dst_y - pixman_fixed_to_int(traps[0].left.p1.y),
490303b705cfSriastradh				 0, 0,
490403b705cfSriastradh				 dst_x, dst_y,
490503b705cfSriastradh				 extents.x2, extents.y2);
490603b705cfSriastradh		FreePicture(mask, 0);
490703b705cfSriastradh	}
490803b705cfSriastradh	sna_pixmap_destroy(scratch);
490903b705cfSriastradh
491003b705cfSriastradh	return true;
491103b705cfSriastradh}
491203b705cfSriastradh
491303b705cfSriastradhstruct inplace {
491403b705cfSriastradh	uint32_t stride;
491503b705cfSriastradh	uint8_t *ptr;
491603b705cfSriastradh	union {
491703b705cfSriastradh		uint8_t opacity;
491803b705cfSriastradh		uint32_t color;
491903b705cfSriastradh	};
492003b705cfSriastradh};
492103b705cfSriastradh
492203b705cfSriastradhstatic force_inline uint8_t coverage_opacity(int coverage, uint8_t opacity)
492303b705cfSriastradh{
492403b705cfSriastradh	coverage = coverage * 256 / FAST_SAMPLES_XY;
492503b705cfSriastradh	coverage -= coverage >> 8;
492603b705cfSriastradh	return opacity == 255 ? coverage : mul_8_8(coverage, opacity);
492703b705cfSriastradh}
492803b705cfSriastradh
492903b705cfSriastradhstatic void
493003b705cfSriastradhtor_blt_src(struct sna *sna,
493103b705cfSriastradh	    struct sna_composite_spans_op *op,
493203b705cfSriastradh	    pixman_region16_t *clip,
493303b705cfSriastradh	    const BoxRec *box,
493403b705cfSriastradh	    int coverage)
493503b705cfSriastradh{
493603b705cfSriastradh	struct inplace *in = (struct inplace *)op;
493703b705cfSriastradh	uint8_t *ptr = in->ptr;
493803b705cfSriastradh	int h, w;
493903b705cfSriastradh
494003b705cfSriastradh	coverage = coverage_opacity(coverage, in->opacity);
494103b705cfSriastradh
494203b705cfSriastradh	ptr += box->y1 * in->stride + box->x1;
494303b705cfSriastradh
494403b705cfSriastradh	h = box->y2 - box->y1;
494503b705cfSriastradh	w = box->x2 - box->x1;
494603b705cfSriastradh	if ((w | h) == 1) {
494703b705cfSriastradh		*ptr = coverage;
494803b705cfSriastradh	} else if (w == 1) {
494903b705cfSriastradh		do {
495003b705cfSriastradh			*ptr = coverage;
495103b705cfSriastradh			ptr += in->stride;
495203b705cfSriastradh		} while (--h);
495303b705cfSriastradh	} else do {
495403b705cfSriastradh		memset(ptr, coverage, w);
495503b705cfSriastradh		ptr += in->stride;
495603b705cfSriastradh	} while (--h);
495703b705cfSriastradh}
495803b705cfSriastradh
495903b705cfSriastradhstatic void
496003b705cfSriastradhtor_blt_src_clipped(struct sna *sna,
496103b705cfSriastradh		    struct sna_composite_spans_op *op,
496203b705cfSriastradh		    pixman_region16_t *clip,
496303b705cfSriastradh		    const BoxRec *box,
496403b705cfSriastradh		    int coverage)
496503b705cfSriastradh{
496603b705cfSriastradh	pixman_region16_t region;
496703b705cfSriastradh	int n;
496803b705cfSriastradh
496903b705cfSriastradh	pixman_region_init_rects(&region, box, 1);
497003b705cfSriastradh	RegionIntersect(&region, &region, clip);
497103b705cfSriastradh	n = REGION_NUM_RECTS(&region);
497203b705cfSriastradh	box = REGION_RECTS(&region);
497303b705cfSriastradh	while (n--)
497403b705cfSriastradh		tor_blt_src(sna, op, NULL, box++, coverage);
497503b705cfSriastradh	pixman_region_fini(&region);
497603b705cfSriastradh}
497703b705cfSriastradh
497803b705cfSriastradhstatic void
497903b705cfSriastradhtor_blt_in(struct sna *sna,
498003b705cfSriastradh	   struct sna_composite_spans_op *op,
498103b705cfSriastradh	   pixman_region16_t *clip,
498203b705cfSriastradh	   const BoxRec *box,
498303b705cfSriastradh	   int coverage)
498403b705cfSriastradh{
498503b705cfSriastradh	struct inplace *in = (struct inplace *)op;
498603b705cfSriastradh	uint8_t *ptr = in->ptr;
498703b705cfSriastradh	int h, w, i;
498803b705cfSriastradh
498903b705cfSriastradh	if (coverage == 0) {
499003b705cfSriastradh		tor_blt_src(sna, op, clip, box, 0);
499103b705cfSriastradh		return;
499203b705cfSriastradh	}
499303b705cfSriastradh
499403b705cfSriastradh	coverage = coverage_opacity(coverage, in->opacity);
499503b705cfSriastradh	if (coverage == 0xff)
499603b705cfSriastradh		return;
499703b705cfSriastradh
499803b705cfSriastradh	ptr += box->y1 * in->stride + box->x1;
499903b705cfSriastradh
500003b705cfSriastradh	h = box->y2 - box->y1;
500103b705cfSriastradh	w = box->x2 - box->x1;
500203b705cfSriastradh	do {
500303b705cfSriastradh		for (i = 0; i < w; i++)
500403b705cfSriastradh			ptr[i] = mul_8_8(ptr[i], coverage);
500503b705cfSriastradh		ptr += in->stride;
500603b705cfSriastradh	} while (--h);
500703b705cfSriastradh}
500803b705cfSriastradh
500903b705cfSriastradhstatic void
501003b705cfSriastradhtor_blt_in_clipped(struct sna *sna,
501103b705cfSriastradh		   struct sna_composite_spans_op *op,
501203b705cfSriastradh		   pixman_region16_t *clip,
501303b705cfSriastradh		   const BoxRec *box,
501403b705cfSriastradh		   int coverage)
501503b705cfSriastradh{
501603b705cfSriastradh	pixman_region16_t region;
501703b705cfSriastradh	int n;
501803b705cfSriastradh
501903b705cfSriastradh	pixman_region_init_rects(&region, box, 1);
502003b705cfSriastradh	RegionIntersect(&region, &region, clip);
502103b705cfSriastradh	n = REGION_NUM_RECTS(&region);
502203b705cfSriastradh	box = REGION_RECTS(&region);
502303b705cfSriastradh	while (n--)
502403b705cfSriastradh		tor_blt_in(sna, op, NULL, box++, coverage);
502503b705cfSriastradh	pixman_region_fini(&region);
502603b705cfSriastradh}
502703b705cfSriastradh
502803b705cfSriastradhstatic void
502903b705cfSriastradhtor_blt_add(struct sna *sna,
503003b705cfSriastradh	    struct sna_composite_spans_op *op,
503103b705cfSriastradh	    pixman_region16_t *clip,
503203b705cfSriastradh	    const BoxRec *box,
503303b705cfSriastradh	    int coverage)
503403b705cfSriastradh{
503503b705cfSriastradh	struct inplace *in = (struct inplace *)op;
503603b705cfSriastradh	uint8_t *ptr = in->ptr;
503703b705cfSriastradh	int h, w, v, i;
503803b705cfSriastradh
503903b705cfSriastradh	if (coverage == 0)
504003b705cfSriastradh		return;
504103b705cfSriastradh
504203b705cfSriastradh	coverage = coverage_opacity(coverage, in->opacity);
504303b705cfSriastradh	if (coverage == 0xff) {
504403b705cfSriastradh		tor_blt_src(sna, op, clip, box, 0xff);
504503b705cfSriastradh		return;
504603b705cfSriastradh	}
504703b705cfSriastradh
504803b705cfSriastradh	ptr += box->y1 * in->stride + box->x1;
504903b705cfSriastradh
505003b705cfSriastradh	h = box->y2 - box->y1;
505103b705cfSriastradh	w = box->x2 - box->x1;
505203b705cfSriastradh	if ((w | h) == 1) {
505303b705cfSriastradh		v = coverage + *ptr;
505403b705cfSriastradh		*ptr = v >= 255 ? 255 : v;
505503b705cfSriastradh	} else {
505603b705cfSriastradh		do {
505703b705cfSriastradh			for (i = 0; i < w; i++) {
505803b705cfSriastradh				v = coverage + ptr[i];
505903b705cfSriastradh				ptr[i] = v >= 255 ? 255 : v;
506003b705cfSriastradh			}
506103b705cfSriastradh			ptr += in->stride;
506203b705cfSriastradh		} while (--h);
506303b705cfSriastradh	}
506403b705cfSriastradh}
506503b705cfSriastradh
506603b705cfSriastradhstatic void
506703b705cfSriastradhtor_blt_add_clipped(struct sna *sna,
506803b705cfSriastradh		    struct sna_composite_spans_op *op,
506903b705cfSriastradh		    pixman_region16_t *clip,
507003b705cfSriastradh		    const BoxRec *box,
507103b705cfSriastradh		    int coverage)
507203b705cfSriastradh{
507303b705cfSriastradh	pixman_region16_t region;
507403b705cfSriastradh	int n;
507503b705cfSriastradh
507603b705cfSriastradh	pixman_region_init_rects(&region, box, 1);
507703b705cfSriastradh	RegionIntersect(&region, &region, clip);
507803b705cfSriastradh	n = REGION_NUM_RECTS(&region);
507903b705cfSriastradh	box = REGION_RECTS(&region);
508003b705cfSriastradh	while (n--)
508103b705cfSriastradh		tor_blt_add(sna, op, NULL, box++, coverage);
508203b705cfSriastradh	pixman_region_fini(&region);
508303b705cfSriastradh}
508403b705cfSriastradh
508503b705cfSriastradhstatic void
508603b705cfSriastradhtor_blt_lerp32(struct sna *sna,
508703b705cfSriastradh	       struct sna_composite_spans_op *op,
508803b705cfSriastradh	       pixman_region16_t *clip,
508903b705cfSriastradh	       const BoxRec *box,
509003b705cfSriastradh	       int coverage)
509103b705cfSriastradh{
509203b705cfSriastradh	struct inplace *in = (struct inplace *)op;
509303b705cfSriastradh	uint32_t *ptr = (uint32_t *)in->ptr;
509403b705cfSriastradh	int stride = in->stride / sizeof(uint32_t);
509503b705cfSriastradh	int h, w, i;
509603b705cfSriastradh
509703b705cfSriastradh	if (coverage == 0)
509803b705cfSriastradh		return;
509903b705cfSriastradh
510003b705cfSriastradh	ptr += box->y1 * stride + box->x1;
510103b705cfSriastradh
510203b705cfSriastradh	h = box->y2 - box->y1;
510303b705cfSriastradh	w = box->x2 - box->x1;
510403b705cfSriastradh	if (coverage == FAST_SAMPLES_XY) {
510503b705cfSriastradh		if ((w | h) == 1) {
510603b705cfSriastradh			*ptr = in->color;
510703b705cfSriastradh		} else {
510803b705cfSriastradh			if (w < 16) {
510903b705cfSriastradh				do {
511003b705cfSriastradh					for (i = 0; i < w; i++)
511103b705cfSriastradh						ptr[i] = in->color;
511203b705cfSriastradh					ptr += stride;
511303b705cfSriastradh				} while (--h);
511403b705cfSriastradh			} else {
511503b705cfSriastradh				pixman_fill(ptr, stride, 32,
511603b705cfSriastradh					    0, 0, w, h, in->color);
511703b705cfSriastradh			}
511803b705cfSriastradh		}
511903b705cfSriastradh	} else {
512003b705cfSriastradh		coverage = coverage * 256 / FAST_SAMPLES_XY;
512103b705cfSriastradh		coverage -= coverage >> 8;
512203b705cfSriastradh
512303b705cfSriastradh		if ((w | h) == 1) {
512403b705cfSriastradh			*ptr = lerp8x4(in->color, coverage, *ptr);
512503b705cfSriastradh		} else if (w == 1) {
512603b705cfSriastradh			do {
512703b705cfSriastradh				*ptr = lerp8x4(in->color, coverage, *ptr);
512803b705cfSriastradh				ptr += stride;
512903b705cfSriastradh			} while (--h);
513003b705cfSriastradh		} else{
513103b705cfSriastradh			do {
513203b705cfSriastradh				for (i = 0; i < w; i++)
513303b705cfSriastradh					ptr[i] = lerp8x4(in->color, coverage, ptr[i]);
513403b705cfSriastradh				ptr += stride;
513503b705cfSriastradh			} while (--h);
513603b705cfSriastradh		}
513703b705cfSriastradh	}
513803b705cfSriastradh}
513903b705cfSriastradh
514003b705cfSriastradhstatic void
514103b705cfSriastradhtor_blt_lerp32_clipped(struct sna *sna,
514203b705cfSriastradh		       struct sna_composite_spans_op *op,
514303b705cfSriastradh		       pixman_region16_t *clip,
514403b705cfSriastradh		       const BoxRec *box,
514503b705cfSriastradh		       int coverage)
514603b705cfSriastradh{
514703b705cfSriastradh	pixman_region16_t region;
514803b705cfSriastradh	int n;
514903b705cfSriastradh
515003b705cfSriastradh	pixman_region_init_rects(&region, box, 1);
515103b705cfSriastradh	RegionIntersect(&region, &region, clip);
515203b705cfSriastradh	n = REGION_NUM_RECTS(&region);
515303b705cfSriastradh	box = REGION_RECTS(&region);
515403b705cfSriastradh	while (n--)
515503b705cfSriastradh		tor_blt_lerp32(sna, op, NULL, box++, coverage);
515603b705cfSriastradh	pixman_region_fini(&region);
515703b705cfSriastradh}
515803b705cfSriastradh
515903b705cfSriastradhstruct mono_inplace_composite {
516003b705cfSriastradh	pixman_image_t *src, *dst;
516103b705cfSriastradh	int dx, dy;
516203b705cfSriastradh	int sx, sy;
516303b705cfSriastradh	int op;
516403b705cfSriastradh};
516503b705cfSriastradhstruct mono_inplace_fill {
516603b705cfSriastradh	uint32_t *data, stride;
516703b705cfSriastradh	uint32_t color;
516803b705cfSriastradh	int bpp;
516903b705cfSriastradh};
517003b705cfSriastradh
517103b705cfSriastradhfastcall static void
517203b705cfSriastradhmono_inplace_fill_box(struct sna *sna,
517303b705cfSriastradh		      const struct sna_composite_op *op,
517403b705cfSriastradh		      const BoxRec *box)
517503b705cfSriastradh{
517603b705cfSriastradh	struct mono_inplace_fill *fill = op->priv;
517703b705cfSriastradh
517803b705cfSriastradh	DBG(("(%s: (%d, %d)x(%d, %d):%08x\n",
517903b705cfSriastradh	     __FUNCTION__,
518003b705cfSriastradh	     box->x1, box->y1,
518103b705cfSriastradh	     box->x2 - box->x1,
518203b705cfSriastradh	     box->y2 - box->y1,
518303b705cfSriastradh	     fill->color));
518403b705cfSriastradh	pixman_fill(fill->data, fill->stride, fill->bpp,
518503b705cfSriastradh		    box->x1, box->y1,
518603b705cfSriastradh		    box->x2 - box->x1,
518703b705cfSriastradh		    box->y2 - box->y1,
518803b705cfSriastradh		    fill->color);
518903b705cfSriastradh}
519003b705cfSriastradh
519103b705cfSriastradhstatic void
519203b705cfSriastradhmono_inplace_fill_boxes(struct sna *sna,
519303b705cfSriastradh			const struct sna_composite_op *op,
519403b705cfSriastradh			const BoxRec *box, int nbox)
519503b705cfSriastradh{
519603b705cfSriastradh	struct mono_inplace_fill *fill = op->priv;
519703b705cfSriastradh
519803b705cfSriastradh	do {
519903b705cfSriastradh		DBG(("(%s: (%d, %d)x(%d, %d):%08x\n",
520003b705cfSriastradh		     __FUNCTION__,
520103b705cfSriastradh		     box->x1, box->y1,
520203b705cfSriastradh		     box->x2 - box->x1,
520303b705cfSriastradh		     box->y2 - box->y1,
520403b705cfSriastradh		     fill->color));
520503b705cfSriastradh		pixman_fill(fill->data, fill->stride, fill->bpp,
520603b705cfSriastradh			    box->x1, box->y1,
520703b705cfSriastradh			    box->x2 - box->x1,
520803b705cfSriastradh			    box->y2 - box->y1,
520903b705cfSriastradh			    fill->color);
521003b705cfSriastradh		box++;
521103b705cfSriastradh	} while (--nbox);
521203b705cfSriastradh}
521303b705cfSriastradh
521403b705cfSriastradhfastcall static void
521503b705cfSriastradhmono_inplace_composite_box(struct sna *sna,
521603b705cfSriastradh			   const struct sna_composite_op *op,
521703b705cfSriastradh			   const BoxRec *box)
521803b705cfSriastradh{
521903b705cfSriastradh	struct mono_inplace_composite *c = op->priv;
522003b705cfSriastradh
522103b705cfSriastradh	pixman_image_composite(c->op, c->src, NULL, c->dst,
522203b705cfSriastradh			       box->x1 + c->sx, box->y1 + c->sy,
522303b705cfSriastradh			       0, 0,
522403b705cfSriastradh			       box->x1 + c->dx, box->y1 + c->dy,
522503b705cfSriastradh			       box->x2 - box->x1,
522603b705cfSriastradh			       box->y2 - box->y1);
522703b705cfSriastradh}
522803b705cfSriastradh
522903b705cfSriastradhstatic void
523003b705cfSriastradhmono_inplace_composite_boxes(struct sna *sna,
523103b705cfSriastradh			     const struct sna_composite_op *op,
523203b705cfSriastradh			     const BoxRec *box, int nbox)
523303b705cfSriastradh{
523403b705cfSriastradh	struct mono_inplace_composite *c = op->priv;
523503b705cfSriastradh
523603b705cfSriastradh	do {
523703b705cfSriastradh		pixman_image_composite(c->op, c->src, NULL, c->dst,
523803b705cfSriastradh				       box->x1 + c->sx, box->y1 + c->sy,
523903b705cfSriastradh				       0, 0,
524003b705cfSriastradh				       box->x1 + c->dx, box->y1 + c->dy,
524103b705cfSriastradh				       box->x2 - box->x1,
524203b705cfSriastradh				       box->y2 - box->y1);
524303b705cfSriastradh		box++;
524403b705cfSriastradh	} while (--nbox);
524503b705cfSriastradh}
524603b705cfSriastradh
524703b705cfSriastradhstatic bool
524803b705cfSriastradhtrapezoid_spans_maybe_inplace(struct sna *sna,
524903b705cfSriastradh			      CARD8 op, PicturePtr src, PicturePtr dst,
525003b705cfSriastradh			      PictFormatPtr maskFormat)
525103b705cfSriastradh{
525203b705cfSriastradh	struct sna_pixmap *priv;
525303b705cfSriastradh
525403b705cfSriastradh	if (NO_SCAN_CONVERTER)
525503b705cfSriastradh		return false;
525603b705cfSriastradh
525703b705cfSriastradh	if (dst->polyMode == PolyModePrecise && !is_mono(dst, maskFormat))
525803b705cfSriastradh		return false;
525903b705cfSriastradh	if (dst->alphaMap)
526003b705cfSriastradh		return false;
526103b705cfSriastradh
526203b705cfSriastradh	if (is_mono(dst, maskFormat))
526303b705cfSriastradh		goto out;
526403b705cfSriastradh
526503b705cfSriastradh	switch ((int)dst->format) {
526603b705cfSriastradh	case PICT_a8:
526703b705cfSriastradh		if (!sna_picture_is_solid(src, NULL))
526803b705cfSriastradh			return false;
526903b705cfSriastradh
527003b705cfSriastradh		switch (op) {
527103b705cfSriastradh		case PictOpIn:
527203b705cfSriastradh		case PictOpAdd:
527303b705cfSriastradh		case PictOpSrc:
527403b705cfSriastradh			break;
527503b705cfSriastradh		default:
527603b705cfSriastradh			return false;
527703b705cfSriastradh		}
527803b705cfSriastradh		break;
527903b705cfSriastradh
528003b705cfSriastradh	case PICT_x8r8g8b8:
528103b705cfSriastradh	case PICT_a8r8g8b8:
528203b705cfSriastradh		if (picture_is_gpu(sna, src))
528303b705cfSriastradh			return false;
528403b705cfSriastradh
528503b705cfSriastradh		switch (op) {
528603b705cfSriastradh		case PictOpOver:
528703b705cfSriastradh		case PictOpAdd:
528803b705cfSriastradh		case PictOpOutReverse:
528903b705cfSriastradh			break;
529003b705cfSriastradh		case PictOpSrc:
529103b705cfSriastradh			if (sna_picture_is_solid(src, NULL))
529203b705cfSriastradh				break;
529303b705cfSriastradh
529403b705cfSriastradh			if (!sna_drawable_is_clear(dst->pDrawable))
529503b705cfSriastradh				return false;
529603b705cfSriastradh			break;
529703b705cfSriastradh		default:
529803b705cfSriastradh			return false;
529903b705cfSriastradh		}
530003b705cfSriastradh		break;
530103b705cfSriastradh	default:
530203b705cfSriastradh		return false;
530303b705cfSriastradh	}
530403b705cfSriastradh
530503b705cfSriastradhout:
530603b705cfSriastradh	priv = sna_pixmap_from_drawable(dst->pDrawable);
530703b705cfSriastradh	if (priv == NULL)
530803b705cfSriastradh		return true;
530903b705cfSriastradh
531003b705cfSriastradh	if (priv->cpu_bo && kgem_bo_is_busy(priv->cpu_bo))
531103b705cfSriastradh		return false;
531203b705cfSriastradh
531303b705cfSriastradh	if (DAMAGE_IS_ALL(priv->cpu_damage) || priv->gpu_damage == NULL)
531403b705cfSriastradh		return true;
531503b705cfSriastradh
531603b705cfSriastradh	if (priv->clear)
531703b705cfSriastradh		return dst->pDrawable->width <= TOR_INPLACE_SIZE;
531803b705cfSriastradh
531903b705cfSriastradh	if (kgem_bo_is_busy(priv->gpu_bo))
532003b705cfSriastradh		return false;
532103b705cfSriastradh
532203b705cfSriastradh	if (priv->cpu_damage)
532303b705cfSriastradh		return true;
532403b705cfSriastradh
532503b705cfSriastradh	return dst->pDrawable->width <= TOR_INPLACE_SIZE;
532603b705cfSriastradh}
532703b705cfSriastradh
532803b705cfSriastradhstatic bool
532903b705cfSriastradhtrapezoid_span_mono_inplace(struct sna *sna,
533003b705cfSriastradh			    CARD8 op,
533103b705cfSriastradh			    PicturePtr src,
533203b705cfSriastradh			    PicturePtr dst,
533303b705cfSriastradh			    INT16 src_x, INT16 src_y,
533403b705cfSriastradh			    int ntrap, xTrapezoid *traps)
533503b705cfSriastradh{
533603b705cfSriastradh	struct mono mono;
533703b705cfSriastradh	union {
533803b705cfSriastradh		struct mono_inplace_fill fill;
533903b705cfSriastradh		struct mono_inplace_composite composite;
534003b705cfSriastradh	} inplace;
534103b705cfSriastradh	int was_clear;
534203b705cfSriastradh	int x, y, n;
534303b705cfSriastradh
534403b705cfSriastradh	trapezoids_bounds(ntrap, traps, &mono.clip.extents);
534503b705cfSriastradh	if (mono.clip.extents.y1 >= mono.clip.extents.y2 ||
534603b705cfSriastradh	    mono.clip.extents.x1 >= mono.clip.extents.x2)
534703b705cfSriastradh		return true;
534803b705cfSriastradh
534903b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
535003b705cfSriastradh	     __FUNCTION__,
535103b705cfSriastradh	     mono.clip.extents.x1, mono.clip.extents.y1,
535203b705cfSriastradh	     mono.clip.extents.x2, mono.clip.extents.y2));
535303b705cfSriastradh
535403b705cfSriastradh	if (!sna_compute_composite_region(&mono.clip,
535503b705cfSriastradh					  src, NULL, dst,
535603b705cfSriastradh					  src_x, src_y,
535703b705cfSriastradh					  0, 0,
535803b705cfSriastradh					  mono.clip.extents.x1, mono.clip.extents.y1,
535903b705cfSriastradh					  mono.clip.extents.x2 - mono.clip.extents.x1,
536003b705cfSriastradh					  mono.clip.extents.y2 - mono.clip.extents.y1)) {
536103b705cfSriastradh		DBG(("%s: trapezoids do not intersect drawable clips\n",
536203b705cfSriastradh		     __FUNCTION__)) ;
536303b705cfSriastradh		return true;
536403b705cfSriastradh	}
536503b705cfSriastradh
536603b705cfSriastradh	DBG(("%s: clipped extents (%d, %d), (%d, %d)\n",
536703b705cfSriastradh	     __FUNCTION__,
536803b705cfSriastradh	     mono.clip.extents.x1, mono.clip.extents.y1,
536903b705cfSriastradh	     mono.clip.extents.x2, mono.clip.extents.y2));
537003b705cfSriastradh
537103b705cfSriastradh	was_clear = sna_drawable_is_clear(dst->pDrawable);
537203b705cfSriastradh	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &mono.clip,
537303b705cfSriastradh					     MOVE_WRITE | MOVE_READ))
537403b705cfSriastradh		return true;
537503b705cfSriastradh
537603b705cfSriastradh	mono.sna = sna;
537703b705cfSriastradh	if (!mono_init(&mono, 2*ntrap))
537803b705cfSriastradh		return false;
537903b705cfSriastradh
538003b705cfSriastradh	mono.op.damage = NULL;
538103b705cfSriastradh
538203b705cfSriastradh	x = dst->pDrawable->x;
538303b705cfSriastradh	y = dst->pDrawable->y;
538403b705cfSriastradh
538503b705cfSriastradh	for (n = 0; n < ntrap; n++) {
538603b705cfSriastradh		if (!xTrapezoidValid(&traps[n]))
538703b705cfSriastradh			continue;
538803b705cfSriastradh
538903b705cfSriastradh		if (pixman_fixed_to_int(traps[n].top) + y >= mono.clip.extents.y2 ||
539003b705cfSriastradh		    pixman_fixed_to_int(traps[n].bottom) + y < mono.clip.extents.y1)
539103b705cfSriastradh			continue;
539203b705cfSriastradh
539303b705cfSriastradh		mono_add_line(&mono, x, y,
539403b705cfSriastradh			      traps[n].top, traps[n].bottom,
539503b705cfSriastradh			      &traps[n].left.p1, &traps[n].left.p2, 1);
539603b705cfSriastradh		mono_add_line(&mono, x, y,
539703b705cfSriastradh			      traps[n].top, traps[n].bottom,
539803b705cfSriastradh			      &traps[n].right.p1, &traps[n].right.p2, -1);
539903b705cfSriastradh	}
540003b705cfSriastradh
540103b705cfSriastradh	if (sna_picture_is_solid(src, &inplace.fill.color) &&
540203b705cfSriastradh	    (op == PictOpSrc || op == PictOpClear ||
540303b705cfSriastradh	     (was_clear && (op == PictOpOver || op == PictOpAdd)) ||
540403b705cfSriastradh	     (op == PictOpOver && inplace.fill.color >> 24 == 0xff))) {
540503b705cfSriastradh		PixmapPtr pixmap;
540603b705cfSriastradh		int16_t dx, dy;
540703b705cfSriastradh		uint8_t *ptr;
540803b705cfSriastradh
540903b705cfSriastradhunbounded_pass:
541003b705cfSriastradh		pixmap = get_drawable_pixmap(dst->pDrawable);
541103b705cfSriastradh
541203b705cfSriastradh		ptr = pixmap->devPrivate.ptr;
541303b705cfSriastradh		if (get_drawable_deltas(dst->pDrawable, pixmap, &dx, &dy))
541403b705cfSriastradh			ptr += dy * pixmap->devKind + dx * pixmap->drawable.bitsPerPixel / 8;
541503b705cfSriastradh		inplace.fill.data = (uint32_t *)ptr;
541603b705cfSriastradh		inplace.fill.stride = pixmap->devKind / sizeof(uint32_t);
541703b705cfSriastradh		inplace.fill.bpp = pixmap->drawable.bitsPerPixel;
541803b705cfSriastradh
541903b705cfSriastradh		if (op == PictOpClear)
542003b705cfSriastradh			inplace.fill.color = 0;
542103b705cfSriastradh		else if (dst->format != PICT_a8r8g8b8)
542203b705cfSriastradh			inplace.fill.color = sna_rgba_to_color(inplace.fill.color, dst->format);
542303b705cfSriastradh
542403b705cfSriastradh		DBG(("%s: fill %x\n", __FUNCTION__, inplace.fill.color));
542503b705cfSriastradh
542603b705cfSriastradh		mono.op.priv = &inplace.fill;
542703b705cfSriastradh		mono.op.box = mono_inplace_fill_box;
542803b705cfSriastradh		mono.op.boxes = mono_inplace_fill_boxes;
542903b705cfSriastradh
543003b705cfSriastradh		op = 0;
543103b705cfSriastradh	} else {
543203b705cfSriastradh		if (src->pDrawable) {
543303b705cfSriastradh			if (!sna_drawable_move_to_cpu(src->pDrawable,
543403b705cfSriastradh						      MOVE_READ)) {
543503b705cfSriastradh				mono_fini(&mono);
543603b705cfSriastradh				return false;
543703b705cfSriastradh			}
543803b705cfSriastradh			if (src->alphaMap &&
543903b705cfSriastradh			    !sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
544003b705cfSriastradh						      MOVE_READ)) {
544103b705cfSriastradh				mono_fini(&mono);
544203b705cfSriastradh				return false;
544303b705cfSriastradh			}
544403b705cfSriastradh		}
544503b705cfSriastradh
544603b705cfSriastradh		inplace.composite.dst = image_from_pict(dst, false,
544703b705cfSriastradh							&inplace.composite.dx,
544803b705cfSriastradh							&inplace.composite.dy);
544903b705cfSriastradh		inplace.composite.src = image_from_pict(src, false,
545003b705cfSriastradh							&inplace.composite.sx,
545103b705cfSriastradh							&inplace.composite.sy);
545203b705cfSriastradh		inplace.composite.sx +=
545303b705cfSriastradh			src_x - pixman_fixed_to_int(traps[0].left.p1.x),
545403b705cfSriastradh		inplace.composite.sy +=
545503b705cfSriastradh			src_y - pixman_fixed_to_int(traps[0].left.p1.y),
545603b705cfSriastradh		inplace.composite.op = op;
545703b705cfSriastradh
545803b705cfSriastradh		mono.op.priv = &inplace.composite;
545903b705cfSriastradh		mono.op.box = mono_inplace_composite_box;
546003b705cfSriastradh		mono.op.boxes = mono_inplace_composite_boxes;
546103b705cfSriastradh	}
546203b705cfSriastradh
546303b705cfSriastradh	if (mono.clip.data == NULL && mono.op.damage == NULL)
546403b705cfSriastradh		mono.span = mono_span__fast;
546503b705cfSriastradh	else
546603b705cfSriastradh		mono.span = mono_span;
546703b705cfSriastradh	mono_render(&mono);
546803b705cfSriastradh	mono_fini(&mono);
546903b705cfSriastradh
547003b705cfSriastradh	if (op) {
547103b705cfSriastradh		free_pixman_pict(src, inplace.composite.src);
547203b705cfSriastradh		free_pixman_pict(dst, inplace.composite.dst);
547303b705cfSriastradh
547403b705cfSriastradh		if (!was_clear && !operator_is_bounded(op)) {
547503b705cfSriastradh			xPointFixed p1, p2;
547603b705cfSriastradh
547703b705cfSriastradh			DBG(("%s: unbounded fixup\n", __FUNCTION__));
547803b705cfSriastradh
547903b705cfSriastradh			if (!mono_init(&mono, 2+2*ntrap))
548003b705cfSriastradh				return false;
548103b705cfSriastradh
548203b705cfSriastradh			p1.y = mono.clip.extents.y1 * pixman_fixed_1;
548303b705cfSriastradh			p2.y = mono.clip.extents.y2 * pixman_fixed_1;
548403b705cfSriastradh
548503b705cfSriastradh			p1.x = mono.clip.extents.x1 * pixman_fixed_1;
548603b705cfSriastradh			p2.x = mono.clip.extents.x1 * pixman_fixed_1;
548703b705cfSriastradh			mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
548803b705cfSriastradh
548903b705cfSriastradh			p1.x = mono.clip.extents.x2 * pixman_fixed_1;
549003b705cfSriastradh			p2.x = mono.clip.extents.x2 * pixman_fixed_1;
549103b705cfSriastradh			mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
549203b705cfSriastradh
549303b705cfSriastradh			for (n = 0; n < ntrap; n++) {
549403b705cfSriastradh				if (!xTrapezoidValid(&traps[n]))
549503b705cfSriastradh					continue;
549603b705cfSriastradh
549703b705cfSriastradh				if (pixman_fixed_to_int(traps[n].top) + x >= mono.clip.extents.y2 ||
549803b705cfSriastradh				    pixman_fixed_to_int(traps[n].bottom) + y < mono.clip.extents.y1)
549903b705cfSriastradh					continue;
550003b705cfSriastradh
550103b705cfSriastradh				mono_add_line(&mono, x, y,
550203b705cfSriastradh					      traps[n].top, traps[n].bottom,
550303b705cfSriastradh					      &traps[n].left.p1, &traps[n].left.p2, 1);
550403b705cfSriastradh				mono_add_line(&mono, x, y,
550503b705cfSriastradh					      traps[n].top, traps[n].bottom,
550603b705cfSriastradh					      &traps[n].right.p1, &traps[n].right.p2, -1);
550703b705cfSriastradh			}
550803b705cfSriastradh
550903b705cfSriastradh			op = PictOpClear;
551003b705cfSriastradh			goto unbounded_pass;
551103b705cfSriastradh		}
551203b705cfSriastradh	}
551303b705cfSriastradh
551403b705cfSriastradh	return true;
551503b705cfSriastradh}
551603b705cfSriastradh
551703b705cfSriastradhstatic void
551803b705cfSriastradhpixmask_span_solid(struct sna *sna,
551903b705cfSriastradh		   struct sna_composite_spans_op *op,
552003b705cfSriastradh		   pixman_region16_t *clip,
552103b705cfSriastradh		   const BoxRec *box,
552203b705cfSriastradh		   int coverage)
552303b705cfSriastradh{
552403b705cfSriastradh	struct pixman_inplace *pi = (struct pixman_inplace *)op;
552503b705cfSriastradh	if (coverage != FAST_SAMPLES_XY) {
552603b705cfSriastradh		coverage = coverage * 256 / FAST_SAMPLES_XY;
552703b705cfSriastradh		coverage -= coverage >> 8;
552803b705cfSriastradh		*pi->bits = mul_4x8_8(pi->color, coverage);
552903b705cfSriastradh	} else
553003b705cfSriastradh		*pi->bits = pi->color;
553103b705cfSriastradh	pixman_image_composite(pi->op, pi->source, NULL, pi->image,
553203b705cfSriastradh			       box->x1, box->y1,
553303b705cfSriastradh			       0, 0,
553403b705cfSriastradh			       pi->dx + box->x1, pi->dy + box->y1,
553503b705cfSriastradh			       box->x2 - box->x1, box->y2 - box->y1);
553603b705cfSriastradh}
553703b705cfSriastradhstatic void
553803b705cfSriastradhpixmask_span_solid__clipped(struct sna *sna,
553903b705cfSriastradh			    struct sna_composite_spans_op *op,
554003b705cfSriastradh			    pixman_region16_t *clip,
554103b705cfSriastradh			    const BoxRec *box,
554203b705cfSriastradh			    int coverage)
554303b705cfSriastradh{
554403b705cfSriastradh	pixman_region16_t region;
554503b705cfSriastradh	int n;
554603b705cfSriastradh
554703b705cfSriastradh	pixman_region_init_rects(&region, box, 1);
554803b705cfSriastradh	RegionIntersect(&region, &region, clip);
554903b705cfSriastradh	n = REGION_NUM_RECTS(&region);
555003b705cfSriastradh	box = REGION_RECTS(&region);
555103b705cfSriastradh	while (n--)
555203b705cfSriastradh		pixmask_span_solid(sna, op, NULL, box++, coverage);
555303b705cfSriastradh	pixman_region_fini(&region);
555403b705cfSriastradh}
555503b705cfSriastradh
555603b705cfSriastradhstatic void
555703b705cfSriastradhpixmask_span(struct sna *sna,
555803b705cfSriastradh	     struct sna_composite_spans_op *op,
555903b705cfSriastradh	     pixman_region16_t *clip,
556003b705cfSriastradh	     const BoxRec *box,
556103b705cfSriastradh	     int coverage)
556203b705cfSriastradh{
556303b705cfSriastradh	struct pixman_inplace *pi = (struct pixman_inplace *)op;
556403b705cfSriastradh	pixman_image_t *mask = NULL;
556503b705cfSriastradh	if (coverage != FAST_SAMPLES_XY) {
556603b705cfSriastradh		coverage = coverage * 256 / FAST_SAMPLES_XY;
556703b705cfSriastradh		coverage -= coverage >> 8;
556803b705cfSriastradh		*pi->bits = coverage;
556903b705cfSriastradh		mask = pi->mask;
557003b705cfSriastradh	}
557103b705cfSriastradh	pixman_image_composite(pi->op, pi->source, mask, pi->image,
557203b705cfSriastradh			       pi->sx + box->x1, pi->sy + box->y1,
557303b705cfSriastradh			       0, 0,
557403b705cfSriastradh			       pi->dx + box->x1, pi->dy + box->y1,
557503b705cfSriastradh			       box->x2 - box->x1, box->y2 - box->y1);
557603b705cfSriastradh}
557703b705cfSriastradhstatic void
557803b705cfSriastradhpixmask_span__clipped(struct sna *sna,
557903b705cfSriastradh		      struct sna_composite_spans_op *op,
558003b705cfSriastradh		      pixman_region16_t *clip,
558103b705cfSriastradh		      const BoxRec *box,
558203b705cfSriastradh		      int coverage)
558303b705cfSriastradh{
558403b705cfSriastradh	pixman_region16_t region;
558503b705cfSriastradh	int n;
558603b705cfSriastradh
558703b705cfSriastradh	pixman_region_init_rects(&region, box, 1);
558803b705cfSriastradh	RegionIntersect(&region, &region, clip);
558903b705cfSriastradh	n = REGION_NUM_RECTS(&region);
559003b705cfSriastradh	box = REGION_RECTS(&region);
559103b705cfSriastradh	while (n--)
559203b705cfSriastradh		pixmask_span(sna, op, NULL, box++, coverage);
559303b705cfSriastradh	pixman_region_fini(&region);
559403b705cfSriastradh}
559503b705cfSriastradh
559603b705cfSriastradhstruct inplace_x8r8g8b8_thread {
559703b705cfSriastradh	xTrapezoid *traps;
559803b705cfSriastradh	PicturePtr dst, src;
559903b705cfSriastradh	BoxRec extents;
560003b705cfSriastradh	int dx, dy;
560103b705cfSriastradh	int ntrap;
560203b705cfSriastradh	bool lerp, is_solid;
560303b705cfSriastradh	uint32_t color;
560403b705cfSriastradh	int16_t src_x, src_y;
560503b705cfSriastradh	uint8_t op;
560603b705cfSriastradh};
560703b705cfSriastradh
560803b705cfSriastradhstatic void inplace_x8r8g8b8_thread(void *arg)
560903b705cfSriastradh{
561003b705cfSriastradh	struct inplace_x8r8g8b8_thread *thread = arg;
561103b705cfSriastradh	struct tor tor;
561203b705cfSriastradh	span_func_t span;
561303b705cfSriastradh	RegionPtr clip;
561403b705cfSriastradh	int y1, y2, n;
561503b705cfSriastradh
561603b705cfSriastradh	if (!tor_init(&tor, &thread->extents, 2*thread->ntrap))
561703b705cfSriastradh		return;
561803b705cfSriastradh
561903b705cfSriastradh	y1 = thread->extents.y1 - thread->dst->pDrawable->y;
562003b705cfSriastradh	y2 = thread->extents.y2 - thread->dst->pDrawable->y;
562103b705cfSriastradh	for (n = 0; n < thread->ntrap; n++) {
562203b705cfSriastradh		xTrapezoid t;
562303b705cfSriastradh
562403b705cfSriastradh		if (!project_trapezoid_onto_grid(&thread->traps[n], thread->dx, thread->dy, &t))
562503b705cfSriastradh			continue;
562603b705cfSriastradh
562703b705cfSriastradh		if (pixman_fixed_to_int(thread->traps[n].top) >= y2 ||
562803b705cfSriastradh		    pixman_fixed_to_int(thread->traps[n].bottom) < y1)
562903b705cfSriastradh			continue;
563003b705cfSriastradh
563103b705cfSriastradh		tor_add_edge(&tor, &t, &t.left, 1);
563203b705cfSriastradh		tor_add_edge(&tor, &t, &t.right, -1);
563303b705cfSriastradh	}
563403b705cfSriastradh
563503b705cfSriastradh	clip = thread->dst->pCompositeClip;
563603b705cfSriastradh	if (thread->lerp) {
563703b705cfSriastradh		struct inplace inplace;
563803b705cfSriastradh		int16_t dst_x, dst_y;
563903b705cfSriastradh		PixmapPtr pixmap;
564003b705cfSriastradh
564103b705cfSriastradh		pixmap = get_drawable_pixmap(thread->dst->pDrawable);
564203b705cfSriastradh
564303b705cfSriastradh		inplace.ptr = pixmap->devPrivate.ptr;
564403b705cfSriastradh		if (get_drawable_deltas(thread->dst->pDrawable, pixmap, &dst_x, &dst_y))
564503b705cfSriastradh			inplace.ptr += dst_y * pixmap->devKind + dst_x * 4;
564603b705cfSriastradh		inplace.stride = pixmap->devKind;
564703b705cfSriastradh		inplace.color = thread->color;
564803b705cfSriastradh
564903b705cfSriastradh		if (clip->data)
565003b705cfSriastradh			span = tor_blt_lerp32_clipped;
565103b705cfSriastradh		else
565203b705cfSriastradh			span = tor_blt_lerp32;
565303b705cfSriastradh
565403b705cfSriastradh		tor_render(NULL, &tor, (void*)&inplace, clip, span, false);
565503b705cfSriastradh	} else if (thread->is_solid) {
565603b705cfSriastradh		struct pixman_inplace pi;
565703b705cfSriastradh
565803b705cfSriastradh		pi.image = image_from_pict(thread->dst, false, &pi.dx, &pi.dy);
565903b705cfSriastradh		pi.op = thread->op;
566003b705cfSriastradh		pi.color = thread->color;
566103b705cfSriastradh
566203b705cfSriastradh		pi.bits = (uint32_t *)&pi.sx;
566303b705cfSriastradh		pi.source = pixman_image_create_bits(PIXMAN_a8r8g8b8,
566403b705cfSriastradh						     1, 1, pi.bits, 0);
566503b705cfSriastradh		pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL);
566603b705cfSriastradh
566703b705cfSriastradh		if (clip->data)
566803b705cfSriastradh			span = pixmask_span_solid__clipped;
566903b705cfSriastradh		else
567003b705cfSriastradh			span = pixmask_span_solid;
567103b705cfSriastradh
567203b705cfSriastradh		tor_render(NULL, &tor, (void*)&pi, clip, span, false);
567303b705cfSriastradh
567403b705cfSriastradh		pixman_image_unref(pi.source);
567503b705cfSriastradh		pixman_image_unref(pi.image);
567603b705cfSriastradh	} else {
567703b705cfSriastradh		struct pixman_inplace pi;
567803b705cfSriastradh
567903b705cfSriastradh		pi.image = image_from_pict(thread->dst, false, &pi.dx, &pi.dy);
568003b705cfSriastradh		pi.source = image_from_pict(thread->src, false, &pi.sx, &pi.sy);
568103b705cfSriastradh		pi.sx += thread->src_x - pixman_fixed_to_int(thread->traps[0].left.p1.x);
568203b705cfSriastradh		pi.sy += thread->src_y - pixman_fixed_to_int(thread->traps[0].left.p1.y);
568303b705cfSriastradh		pi.mask = pixman_image_create_bits(PIXMAN_a8, 1, 1, NULL, 0);
568403b705cfSriastradh		pixman_image_set_repeat(pi.mask, PIXMAN_REPEAT_NORMAL);
568503b705cfSriastradh		pi.bits = pixman_image_get_data(pi.mask);
568603b705cfSriastradh		pi.op = thread->op;
568703b705cfSriastradh
568803b705cfSriastradh		if (clip->data)
568903b705cfSriastradh			span = pixmask_span__clipped;
569003b705cfSriastradh		else
569103b705cfSriastradh			span = pixmask_span;
569203b705cfSriastradh
569303b705cfSriastradh		tor_render(NULL, &tor, (void*)&pi, clip, span, false);
569403b705cfSriastradh
569503b705cfSriastradh		pixman_image_unref(pi.mask);
569603b705cfSriastradh		pixman_image_unref(pi.source);
569703b705cfSriastradh		pixman_image_unref(pi.image);
569803b705cfSriastradh	}
569903b705cfSriastradh
570003b705cfSriastradh	tor_fini(&tor);
570103b705cfSriastradh}
570203b705cfSriastradh
570303b705cfSriastradhstatic bool
570403b705cfSriastradhtrapezoid_span_inplace__x8r8g8b8(CARD8 op,
570503b705cfSriastradh				 PicturePtr dst,
570603b705cfSriastradh				 PicturePtr src, int16_t src_x, int16_t src_y,
570703b705cfSriastradh				 PictFormatPtr maskFormat,
570803b705cfSriastradh				 int ntrap, xTrapezoid *traps)
570903b705cfSriastradh{
571003b705cfSriastradh	uint32_t color;
571103b705cfSriastradh	bool lerp, is_solid;
571203b705cfSriastradh	RegionRec region;
571303b705cfSriastradh	int dx, dy;
571403b705cfSriastradh	int num_threads, n;
571503b705cfSriastradh
571603b705cfSriastradh	lerp = false;
571703b705cfSriastradh	is_solid = sna_picture_is_solid(src, &color);
571803b705cfSriastradh	if (is_solid) {
571903b705cfSriastradh		if (op == PictOpOver && (color >> 24) == 0xff)
572003b705cfSriastradh			op = PictOpSrc;
572103b705cfSriastradh		if (op == PictOpOver && sna_drawable_is_clear(dst->pDrawable))
572203b705cfSriastradh			op = PictOpSrc;
572303b705cfSriastradh		lerp = op == PictOpSrc;
572403b705cfSriastradh	}
572503b705cfSriastradh	if (!lerp) {
572603b705cfSriastradh		switch (op) {
572703b705cfSriastradh		case PictOpOver:
572803b705cfSriastradh		case PictOpAdd:
572903b705cfSriastradh		case PictOpOutReverse:
573003b705cfSriastradh			break;
573103b705cfSriastradh		case PictOpSrc:
573203b705cfSriastradh			if (!sna_drawable_is_clear(dst->pDrawable))
573303b705cfSriastradh				return false;
573403b705cfSriastradh			break;
573503b705cfSriastradh		default:
573603b705cfSriastradh			return false;
573703b705cfSriastradh		}
573803b705cfSriastradh	}
573903b705cfSriastradh
574003b705cfSriastradh	if (maskFormat == NULL && ntrap > 1) {
574103b705cfSriastradh		DBG(("%s: individual rasterisation requested\n",
574203b705cfSriastradh		     __FUNCTION__));
574303b705cfSriastradh		do {
574403b705cfSriastradh			/* XXX unwind errors? */
574503b705cfSriastradh			if (!trapezoid_span_inplace__x8r8g8b8(op, dst,
574603b705cfSriastradh							      src, src_x, src_y,
574703b705cfSriastradh							      NULL, 1, traps++))
574803b705cfSriastradh				return false;
574903b705cfSriastradh		} while (--ntrap);
575003b705cfSriastradh		return true;
575103b705cfSriastradh	}
575203b705cfSriastradh
575303b705cfSriastradh	trapezoids_bounds(ntrap, traps, &region.extents);
575403b705cfSriastradh	if (region.extents.y1 >= region.extents.y2 ||
575503b705cfSriastradh	    region.extents.x1 >= region.extents.x2)
575603b705cfSriastradh		return true;
575703b705cfSriastradh
575803b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
575903b705cfSriastradh	     __FUNCTION__,
576003b705cfSriastradh	     region.extents.x1, region.extents.y1,
576103b705cfSriastradh	     region.extents.x2, region.extents.y2));
576203b705cfSriastradh
576303b705cfSriastradh	if (!sna_compute_composite_extents(&region.extents,
576403b705cfSriastradh					   src, NULL, dst,
576503b705cfSriastradh					   src_x, src_y,
576603b705cfSriastradh					   0, 0,
576703b705cfSriastradh					   region.extents.x1, region.extents.y1,
576803b705cfSriastradh					   region.extents.x2 - region.extents.x1,
576903b705cfSriastradh					   region.extents.y2 - region.extents.y1))
577003b705cfSriastradh		return true;
577103b705cfSriastradh
577203b705cfSriastradh	DBG(("%s: clipped extents (%d, %d), (%d, %d)\n",
577303b705cfSriastradh	     __FUNCTION__,
577403b705cfSriastradh	     region.extents.x1, region.extents.y1,
577503b705cfSriastradh	     region.extents.x2, region.extents.y2));
577603b705cfSriastradh
577703b705cfSriastradh	region.data = NULL;
577803b705cfSriastradh	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &region,
577903b705cfSriastradh					    MOVE_WRITE | MOVE_READ))
578003b705cfSriastradh		return true;
578103b705cfSriastradh
578203b705cfSriastradh	if (!is_solid && src->pDrawable) {
578303b705cfSriastradh		if (!sna_drawable_move_to_cpu(src->pDrawable,
578403b705cfSriastradh					      MOVE_READ))
578503b705cfSriastradh			return true;
578603b705cfSriastradh
578703b705cfSriastradh		if (src->alphaMap &&
578803b705cfSriastradh		    !sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
578903b705cfSriastradh					      MOVE_READ))
579003b705cfSriastradh			return true;
579103b705cfSriastradh	}
579203b705cfSriastradh
579303b705cfSriastradh	dx = dst->pDrawable->x * FAST_SAMPLES_X;
579403b705cfSriastradh	dy = dst->pDrawable->y * FAST_SAMPLES_Y;
579503b705cfSriastradh
579603b705cfSriastradh	num_threads = sna_use_threads(4*(region.extents.x2 - region.extents.x1),
579703b705cfSriastradh				      region.extents.y2 - region.extents.y1,
579803b705cfSriastradh				      16);
579903b705cfSriastradh
580003b705cfSriastradh	DBG(("%s: %dx%d, format=%x, op=%d, lerp?=%d, num_threads=%d\n",
580103b705cfSriastradh	     __FUNCTION__,
580203b705cfSriastradh	     region.extents.x2 - region.extents.x1,
580303b705cfSriastradh	     region.extents.y2 - region.extents.y1,
580403b705cfSriastradh	     dst->format, op, lerp, num_threads));
580503b705cfSriastradh
580603b705cfSriastradh	if (num_threads == 1) {
580703b705cfSriastradh		struct tor tor;
580803b705cfSriastradh		span_func_t span;
580903b705cfSriastradh
581003b705cfSriastradh		if (!tor_init(&tor, &region.extents, 2*ntrap))
581103b705cfSriastradh			return true;
581203b705cfSriastradh
581303b705cfSriastradh		for (n = 0; n < ntrap; n++) {
581403b705cfSriastradh			xTrapezoid t;
581503b705cfSriastradh
581603b705cfSriastradh			if (!project_trapezoid_onto_grid(&traps[n], dx, dy, &t))
581703b705cfSriastradh				continue;
581803b705cfSriastradh
581903b705cfSriastradh			if (pixman_fixed_to_int(traps[n].top) >= region.extents.y2 - dst->pDrawable->y ||
582003b705cfSriastradh			    pixman_fixed_to_int(traps[n].bottom) < region.extents.y1 - dst->pDrawable->y)
582103b705cfSriastradh				continue;
582203b705cfSriastradh
582303b705cfSriastradh			tor_add_edge(&tor, &t, &t.left, 1);
582403b705cfSriastradh			tor_add_edge(&tor, &t, &t.right, -1);
582503b705cfSriastradh		}
582603b705cfSriastradh
582703b705cfSriastradh		if (lerp) {
582803b705cfSriastradh			struct inplace inplace;
582903b705cfSriastradh			PixmapPtr pixmap;
583003b705cfSriastradh			int16_t dst_x, dst_y;
583103b705cfSriastradh
583203b705cfSriastradh			pixmap = get_drawable_pixmap(dst->pDrawable);
583303b705cfSriastradh
583403b705cfSriastradh			inplace.ptr = pixmap->devPrivate.ptr;
583503b705cfSriastradh			if (get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y))
583603b705cfSriastradh				inplace.ptr += dst_y * pixmap->devKind + dst_x * 4;
583703b705cfSriastradh			inplace.stride = pixmap->devKind;
583803b705cfSriastradh			inplace.color = color;
583903b705cfSriastradh
584003b705cfSriastradh			if (dst->pCompositeClip->data)
584103b705cfSriastradh				span = tor_blt_lerp32_clipped;
584203b705cfSriastradh			else
584303b705cfSriastradh				span = tor_blt_lerp32;
584403b705cfSriastradh
584503b705cfSriastradh			DBG(("%s: render inplace op=%d, color=%08x\n",
584603b705cfSriastradh			     __FUNCTION__, op, color));
584703b705cfSriastradh
584803b705cfSriastradh			tor_render(NULL, &tor, (void*)&inplace,
584903b705cfSriastradh				   dst->pCompositeClip, span, false);
585003b705cfSriastradh		} else if (is_solid) {
585103b705cfSriastradh			struct pixman_inplace pi;
585203b705cfSriastradh
585303b705cfSriastradh			pi.image = image_from_pict(dst, false, &pi.dx, &pi.dy);
585403b705cfSriastradh			pi.op = op;
585503b705cfSriastradh			pi.color = color;
585603b705cfSriastradh
585703b705cfSriastradh			pi.bits = (uint32_t *)&pi.sx;
585803b705cfSriastradh			pi.source = pixman_image_create_bits(PIXMAN_a8r8g8b8,
585903b705cfSriastradh							     1, 1, pi.bits, 0);
586003b705cfSriastradh			pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL);
586103b705cfSriastradh
586203b705cfSriastradh			if (dst->pCompositeClip->data)
586303b705cfSriastradh				span = pixmask_span_solid__clipped;
586403b705cfSriastradh			else
586503b705cfSriastradh				span = pixmask_span_solid;
586603b705cfSriastradh
586703b705cfSriastradh			tor_render(NULL, &tor, (void*)&pi,
586803b705cfSriastradh				   dst->pCompositeClip, span,
586903b705cfSriastradh				   false);
587003b705cfSriastradh
587103b705cfSriastradh			pixman_image_unref(pi.source);
587203b705cfSriastradh			pixman_image_unref(pi.image);
587303b705cfSriastradh		} else {
587403b705cfSriastradh			struct pixman_inplace pi;
587503b705cfSriastradh
587603b705cfSriastradh			pi.image = image_from_pict(dst, false, &pi.dx, &pi.dy);
587703b705cfSriastradh			pi.source = image_from_pict(src, false, &pi.sx, &pi.sy);
587803b705cfSriastradh			pi.sx += src_x - pixman_fixed_to_int(traps[0].left.p1.x);
587903b705cfSriastradh			pi.sy += src_y - pixman_fixed_to_int(traps[0].left.p1.y);
588003b705cfSriastradh			pi.mask = pixman_image_create_bits(PIXMAN_a8, 1, 1, NULL, 0);
588103b705cfSriastradh			pixman_image_set_repeat(pi.mask, PIXMAN_REPEAT_NORMAL);
588203b705cfSriastradh			pi.bits = pixman_image_get_data(pi.mask);
588303b705cfSriastradh			pi.op = op;
588403b705cfSriastradh
588503b705cfSriastradh			if (dst->pCompositeClip->data)
588603b705cfSriastradh				span = pixmask_span__clipped;
588703b705cfSriastradh			else
588803b705cfSriastradh				span = pixmask_span;
588903b705cfSriastradh
589003b705cfSriastradh			tor_render(NULL, &tor, (void*)&pi,
589103b705cfSriastradh				   dst->pCompositeClip, span,
589203b705cfSriastradh				   false);
589303b705cfSriastradh
589403b705cfSriastradh			pixman_image_unref(pi.mask);
589503b705cfSriastradh			pixman_image_unref(pi.source);
589603b705cfSriastradh			pixman_image_unref(pi.image);
589703b705cfSriastradh		}
589803b705cfSriastradh
589903b705cfSriastradh		tor_fini(&tor);
590003b705cfSriastradh	} else {
590103b705cfSriastradh		struct inplace_x8r8g8b8_thread threads[num_threads];
590203b705cfSriastradh		int y, h;
590303b705cfSriastradh
590403b705cfSriastradh		DBG(("%s: using %d threads for inplace compositing %dx%d\n",
590503b705cfSriastradh		     __FUNCTION__, num_threads,
590603b705cfSriastradh		     region.extents.x2 - region.extents.x1,
590703b705cfSriastradh		     region.extents.y2 - region.extents.y1));
590803b705cfSriastradh
590903b705cfSriastradh		threads[0].traps = traps;
591003b705cfSriastradh		threads[0].ntrap = ntrap;
591103b705cfSriastradh		threads[0].extents = region.extents;
591203b705cfSriastradh		threads[0].lerp = lerp;
591303b705cfSriastradh		threads[0].is_solid = is_solid;
591403b705cfSriastradh		threads[0].color = color;
591503b705cfSriastradh		threads[0].dx = dx;
591603b705cfSriastradh		threads[0].dy = dy;
591703b705cfSriastradh		threads[0].dst = dst;
591803b705cfSriastradh		threads[0].src = src;
591903b705cfSriastradh		threads[0].op = op;
592003b705cfSriastradh		threads[0].src_x = src_x;
592103b705cfSriastradh		threads[0].src_y = src_y;
592203b705cfSriastradh
592303b705cfSriastradh		y = region.extents.y1;
592403b705cfSriastradh		h = region.extents.y2 - region.extents.y1;
592503b705cfSriastradh		h = (h + num_threads - 1) / num_threads;
592603b705cfSriastradh
592703b705cfSriastradh		for (n = 1; n < num_threads; n++) {
592803b705cfSriastradh			threads[n] = threads[0];
592903b705cfSriastradh			threads[n].extents.y1 = y;
593003b705cfSriastradh			threads[n].extents.y2 = y += h;
593103b705cfSriastradh
593203b705cfSriastradh			sna_threads_run(inplace_x8r8g8b8_thread, &threads[n]);
593303b705cfSriastradh		}
593403b705cfSriastradh
593503b705cfSriastradh		threads[0].extents.y1 = y;
593603b705cfSriastradh		threads[0].extents.y2 = region.extents.y2;
593703b705cfSriastradh		inplace_x8r8g8b8_thread(&threads[0]);
593803b705cfSriastradh
593903b705cfSriastradh		sna_threads_wait();
594003b705cfSriastradh	}
594103b705cfSriastradh
594203b705cfSriastradh	return true;
594303b705cfSriastradh}
594403b705cfSriastradh
594503b705cfSriastradhstruct inplace_thread {
594603b705cfSriastradh	xTrapezoid *traps;
594703b705cfSriastradh	RegionPtr clip;
594803b705cfSriastradh	span_func_t span;
594903b705cfSriastradh	struct inplace inplace;
595003b705cfSriastradh	BoxRec extents;
595103b705cfSriastradh	int dx, dy;
595203b705cfSriastradh	int draw_x, draw_y;
595303b705cfSriastradh	bool unbounded;
595403b705cfSriastradh	int ntrap;
595503b705cfSriastradh};
595603b705cfSriastradh
595703b705cfSriastradhstatic void inplace_thread(void *arg)
595803b705cfSriastradh{
595903b705cfSriastradh	struct inplace_thread *thread = arg;
596003b705cfSriastradh	struct tor tor;
596103b705cfSriastradh	int n;
596203b705cfSriastradh
596303b705cfSriastradh	if (!tor_init(&tor, &thread->extents, 2*thread->ntrap))
596403b705cfSriastradh		return;
596503b705cfSriastradh
596603b705cfSriastradh	for (n = 0; n < thread->ntrap; n++) {
596703b705cfSriastradh		xTrapezoid t;
596803b705cfSriastradh
596903b705cfSriastradh		if (!project_trapezoid_onto_grid(&thread->traps[n], thread->dx, thread->dy, &t))
597003b705cfSriastradh			continue;
597103b705cfSriastradh
597203b705cfSriastradh		if (pixman_fixed_to_int(thread->traps[n].top) >= thread->extents.y2 - thread->draw_y ||
597303b705cfSriastradh		    pixman_fixed_to_int(thread->traps[n].bottom) < thread->extents.y1 - thread->draw_y)
597403b705cfSriastradh			continue;
597503b705cfSriastradh
597603b705cfSriastradh		tor_add_edge(&tor, &t, &t.left, 1);
597703b705cfSriastradh		tor_add_edge(&tor, &t, &t.right, -1);
597803b705cfSriastradh	}
597903b705cfSriastradh
598003b705cfSriastradh	tor_render(NULL, &tor, (void*)&thread->inplace,
598103b705cfSriastradh		   thread->clip, thread->span, thread->unbounded);
598203b705cfSriastradh
598303b705cfSriastradh	tor_fini(&tor);
598403b705cfSriastradh}
598503b705cfSriastradh
598603b705cfSriastradhstatic bool
598703b705cfSriastradhtrapezoid_span_inplace(struct sna *sna,
598803b705cfSriastradh		       CARD8 op, PicturePtr src, PicturePtr dst,
598903b705cfSriastradh		       PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
599003b705cfSriastradh		       int ntrap, xTrapezoid *traps,
599103b705cfSriastradh		       bool fallback)
599203b705cfSriastradh{
599303b705cfSriastradh	struct inplace inplace;
599403b705cfSriastradh	span_func_t span;
599503b705cfSriastradh	PixmapPtr pixmap;
599603b705cfSriastradh	struct sna_pixmap *priv;
599703b705cfSriastradh	RegionRec region;
599803b705cfSriastradh	uint32_t color;
599903b705cfSriastradh	bool unbounded;
600003b705cfSriastradh	int16_t dst_x, dst_y;
600103b705cfSriastradh	int dx, dy;
600203b705cfSriastradh	int num_threads, n;
600303b705cfSriastradh
600403b705cfSriastradh	if (NO_SCAN_CONVERTER)
600503b705cfSriastradh		return false;
600603b705cfSriastradh
600703b705cfSriastradh	if (dst->polyMode == PolyModePrecise && !is_mono(dst, maskFormat)) {
600803b705cfSriastradh		DBG(("%s: fallback -- precise rasterisation requested\n",
600903b705cfSriastradh		     __FUNCTION__));
601003b705cfSriastradh		return false;
601103b705cfSriastradh	}
601203b705cfSriastradh	if (dst->alphaMap) {
601303b705cfSriastradh		DBG(("%s: fallback -- dst alphamap\n",
601403b705cfSriastradh		     __FUNCTION__));
601503b705cfSriastradh		return false;
601603b705cfSriastradh	}
601703b705cfSriastradh
601803b705cfSriastradh	if (!fallback && is_gpu(sna, dst->pDrawable, PREFER_GPU_SPANS)) {
601903b705cfSriastradh		DBG(("%s: fallback -- can not perform operation in place, destination busy\n",
602003b705cfSriastradh		     __FUNCTION__));
602103b705cfSriastradh
602203b705cfSriastradh		return false;
602303b705cfSriastradh	}
602403b705cfSriastradh
602503b705cfSriastradh	if (is_mono(dst, maskFormat))
602603b705cfSriastradh		return trapezoid_span_mono_inplace(sna, op, src, dst,
602703b705cfSriastradh						   src_x, src_y, ntrap, traps);
602803b705cfSriastradh
602903b705cfSriastradh	if (dst->format == PICT_a8r8g8b8 || dst->format == PICT_x8r8g8b8)
603003b705cfSriastradh		return trapezoid_span_inplace__x8r8g8b8(op, dst,
603103b705cfSriastradh							src, src_x, src_y,
603203b705cfSriastradh							maskFormat,
603303b705cfSriastradh							ntrap, traps);
603403b705cfSriastradh
603503b705cfSriastradh	if (!sna_picture_is_solid(src, &color)) {
603603b705cfSriastradh		DBG(("%s: fallback -- can not perform operation in place, requires solid source\n",
603703b705cfSriastradh		     __FUNCTION__));
603803b705cfSriastradh		return false;
603903b705cfSriastradh	}
604003b705cfSriastradh
604103b705cfSriastradh	if (dst->format != PICT_a8) {
604203b705cfSriastradh		DBG(("%s: fallback -- can not perform operation in place, format=%x\n",
604303b705cfSriastradh		     __FUNCTION__, dst->format));
604403b705cfSriastradh		return false;
604503b705cfSriastradh	}
604603b705cfSriastradh
604703b705cfSriastradh	pixmap = get_drawable_pixmap(dst->pDrawable);
604803b705cfSriastradh
604903b705cfSriastradh	unbounded = false;
605003b705cfSriastradh	priv = sna_pixmap(pixmap);
605103b705cfSriastradh	if (priv) {
605203b705cfSriastradh		switch (op) {
605303b705cfSriastradh		case PictOpAdd:
605403b705cfSriastradh			if (priv->clear && priv->clear_color == 0) {
605503b705cfSriastradh				unbounded = true;
605603b705cfSriastradh				op = PictOpSrc;
605703b705cfSriastradh			}
605803b705cfSriastradh			if ((color >> 24) == 0)
605903b705cfSriastradh				return true;
606003b705cfSriastradh			break;
606103b705cfSriastradh		case PictOpIn:
606203b705cfSriastradh			if (priv->clear && priv->clear_color == 0)
606303b705cfSriastradh				return true;
606403b705cfSriastradh			if (priv->clear && priv->clear_color == 0xff)
606503b705cfSriastradh				op = PictOpSrc;
606603b705cfSriastradh			unbounded = true;
606703b705cfSriastradh			break;
606803b705cfSriastradh		case PictOpSrc:
606903b705cfSriastradh			unbounded = true;
607003b705cfSriastradh			break;
607103b705cfSriastradh		default:
607203b705cfSriastradh			DBG(("%s: fallback -- can not perform op [%d] in place\n",
607303b705cfSriastradh			     __FUNCTION__, op));
607403b705cfSriastradh			return false;
607503b705cfSriastradh		}
607603b705cfSriastradh	} else {
607703b705cfSriastradh		switch (op) {
607803b705cfSriastradh		case PictOpAdd:
607903b705cfSriastradh			if ((color >> 24) == 0)
608003b705cfSriastradh				return true;
608103b705cfSriastradh			break;
608203b705cfSriastradh		case PictOpIn:
608303b705cfSriastradh		case PictOpSrc:
608403b705cfSriastradh			unbounded = true;
608503b705cfSriastradh			break;
608603b705cfSriastradh		default:
608703b705cfSriastradh			DBG(("%s: fallback -- can not perform op [%d] in place\n",
608803b705cfSriastradh			     __FUNCTION__, op));
608903b705cfSriastradh			return false;
609003b705cfSriastradh		}
609103b705cfSriastradh	}
609203b705cfSriastradh
609303b705cfSriastradh	DBG(("%s: format=%x, op=%d, color=%x\n",
609403b705cfSriastradh	     __FUNCTION__, dst->format, op, color));
609503b705cfSriastradh
609603b705cfSriastradh	if (maskFormat == NULL && ntrap > 1) {
609703b705cfSriastradh		DBG(("%s: individual rasterisation requested\n",
609803b705cfSriastradh		     __FUNCTION__));
609903b705cfSriastradh		do {
610003b705cfSriastradh			/* XXX unwind errors? */
610103b705cfSriastradh			if (!trapezoid_span_inplace(sna, op, src, dst, NULL,
610203b705cfSriastradh						    src_x, src_y, 1, traps++,
610303b705cfSriastradh						    fallback))
610403b705cfSriastradh				return false;
610503b705cfSriastradh		} while (--ntrap);
610603b705cfSriastradh		return true;
610703b705cfSriastradh	}
610803b705cfSriastradh
610903b705cfSriastradh	trapezoids_bounds(ntrap, traps, &region.extents);
611003b705cfSriastradh	if (region.extents.y1 >= region.extents.y2 ||
611103b705cfSriastradh	    region.extents.x1 >= region.extents.x2)
611203b705cfSriastradh		return true;
611303b705cfSriastradh
611403b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
611503b705cfSriastradh	     __FUNCTION__,
611603b705cfSriastradh	     region.extents.x1, region.extents.y1,
611703b705cfSriastradh	     region.extents.x2, region.extents.y2));
611803b705cfSriastradh
611903b705cfSriastradh	if (!sna_compute_composite_extents(&region.extents,
612003b705cfSriastradh					   NULL, NULL, dst,
612103b705cfSriastradh					   0, 0,
612203b705cfSriastradh					   0, 0,
612303b705cfSriastradh					   region.extents.x1, region.extents.y1,
612403b705cfSriastradh					   region.extents.x2 - region.extents.x1,
612503b705cfSriastradh					   region.extents.y2 - region.extents.y1))
612603b705cfSriastradh		return true;
612703b705cfSriastradh
612803b705cfSriastradh	DBG(("%s: clipped extents (%d, %d), (%d, %d)\n",
612903b705cfSriastradh	     __FUNCTION__,
613003b705cfSriastradh	     region.extents.x1, region.extents.y1,
613103b705cfSriastradh	     region.extents.x2, region.extents.y2));
613203b705cfSriastradh
613303b705cfSriastradh	if (op == PictOpSrc) {
613403b705cfSriastradh		if (dst->pCompositeClip->data)
613503b705cfSriastradh			span = tor_blt_src_clipped;
613603b705cfSriastradh		else
613703b705cfSriastradh			span = tor_blt_src;
613803b705cfSriastradh	} else if (op == PictOpIn) {
613903b705cfSriastradh		if (dst->pCompositeClip->data)
614003b705cfSriastradh			span = tor_blt_in_clipped;
614103b705cfSriastradh		else
614203b705cfSriastradh			span = tor_blt_in;
614303b705cfSriastradh	} else {
614403b705cfSriastradh		assert(op == PictOpAdd);
614503b705cfSriastradh		if (dst->pCompositeClip->data)
614603b705cfSriastradh			span = tor_blt_add_clipped;
614703b705cfSriastradh		else
614803b705cfSriastradh			span = tor_blt_add;
614903b705cfSriastradh	}
615003b705cfSriastradh
615103b705cfSriastradh	DBG(("%s: move-to-cpu\n", __FUNCTION__));
615203b705cfSriastradh	region.data = NULL;
615303b705cfSriastradh	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &region,
615403b705cfSriastradh					     op == PictOpSrc ? MOVE_WRITE | MOVE_INPLACE_HINT : MOVE_WRITE | MOVE_READ))
615503b705cfSriastradh		return true;
615603b705cfSriastradh
615703b705cfSriastradh	dx = dst->pDrawable->x * FAST_SAMPLES_X;
615803b705cfSriastradh	dy = dst->pDrawable->y * FAST_SAMPLES_Y;
615903b705cfSriastradh
616003b705cfSriastradh
616103b705cfSriastradh	inplace.ptr = pixmap->devPrivate.ptr;
616203b705cfSriastradh	if (get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y))
616303b705cfSriastradh		inplace.ptr += dst_y * pixmap->devKind + dst_x;
616403b705cfSriastradh	inplace.stride = pixmap->devKind;
616503b705cfSriastradh	inplace.opacity = color >> 24;
616603b705cfSriastradh
616703b705cfSriastradh	num_threads = sna_use_threads(region.extents.x2 - region.extents.x1,
616803b705cfSriastradh				      region.extents.y2 - region.extents.y1,
616903b705cfSriastradh				      16);
617003b705cfSriastradh	if (num_threads == 1) {
617103b705cfSriastradh		struct tor tor;
617203b705cfSriastradh
617303b705cfSriastradh		if (!tor_init(&tor, &region.extents, 2*ntrap))
617403b705cfSriastradh			return true;
617503b705cfSriastradh
617603b705cfSriastradh		for (n = 0; n < ntrap; n++) {
617703b705cfSriastradh			xTrapezoid t;
617803b705cfSriastradh
617903b705cfSriastradh			if (!project_trapezoid_onto_grid(&traps[n], dx, dy, &t))
618003b705cfSriastradh				continue;
618103b705cfSriastradh
618203b705cfSriastradh			if (pixman_fixed_to_int(traps[n].top) >= region.extents.y2 - dst->pDrawable->y ||
618303b705cfSriastradh			    pixman_fixed_to_int(traps[n].bottom) < region.extents.y1 - dst->pDrawable->y)
618403b705cfSriastradh				continue;
618503b705cfSriastradh
618603b705cfSriastradh			tor_add_edge(&tor, &t, &t.left, 1);
618703b705cfSriastradh			tor_add_edge(&tor, &t, &t.right, -1);
618803b705cfSriastradh		}
618903b705cfSriastradh
619003b705cfSriastradh		tor_render(NULL, &tor, (void*)&inplace,
619103b705cfSriastradh			   dst->pCompositeClip, span, unbounded);
619203b705cfSriastradh
619303b705cfSriastradh		tor_fini(&tor);
619403b705cfSriastradh	} else {
619503b705cfSriastradh		struct inplace_thread threads[num_threads];
619603b705cfSriastradh		int y, h;
619703b705cfSriastradh
619803b705cfSriastradh		DBG(("%s: using %d threads for inplace compositing %dx%d\n",
619903b705cfSriastradh		     __FUNCTION__, num_threads,
620003b705cfSriastradh		     region.extents.x2 - region.extents.x1,
620103b705cfSriastradh		     region.extents.y2 - region.extents.y1));
620203b705cfSriastradh
620303b705cfSriastradh		threads[0].traps = traps;
620403b705cfSriastradh		threads[0].ntrap = ntrap;
620503b705cfSriastradh		threads[0].inplace = inplace;
620603b705cfSriastradh		threads[0].extents = region.extents;
620703b705cfSriastradh		threads[0].clip = dst->pCompositeClip;
620803b705cfSriastradh		threads[0].span = span;
620903b705cfSriastradh		threads[0].unbounded = unbounded;
621003b705cfSriastradh		threads[0].dx = dx;
621103b705cfSriastradh		threads[0].dy = dy;
621203b705cfSriastradh		threads[0].draw_x = dst->pDrawable->x;
621303b705cfSriastradh		threads[0].draw_y = dst->pDrawable->y;
621403b705cfSriastradh
621503b705cfSriastradh		y = region.extents.y1;
621603b705cfSriastradh		h = region.extents.y2 - region.extents.y1;
621703b705cfSriastradh		h = (h + num_threads - 1) / num_threads;
621803b705cfSriastradh
621903b705cfSriastradh		for (n = 1; n < num_threads; n++) {
622003b705cfSriastradh			threads[n] = threads[0];
622103b705cfSriastradh			threads[n].extents.y1 = y;
622203b705cfSriastradh			threads[n].extents.y2 = y += h;
622303b705cfSriastradh
622403b705cfSriastradh			sna_threads_run(inplace_thread, &threads[n]);
622503b705cfSriastradh		}
622603b705cfSriastradh
622703b705cfSriastradh		threads[0].extents.y1 = y;
622803b705cfSriastradh		threads[0].extents.y2 = region.extents.y2;
622903b705cfSriastradh		inplace_thread(&threads[0]);
623003b705cfSriastradh
623103b705cfSriastradh		sna_threads_wait();
623203b705cfSriastradh	}
623303b705cfSriastradh
623403b705cfSriastradh	return true;
623503b705cfSriastradh}
623603b705cfSriastradh
623703b705cfSriastradhstatic bool
623803b705cfSriastradhtrapezoid_span_fallback(CARD8 op, PicturePtr src, PicturePtr dst,
623903b705cfSriastradh			PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
624003b705cfSriastradh			int ntrap, xTrapezoid *traps)
624103b705cfSriastradh{
624203b705cfSriastradh	struct tor tor;
624303b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
624403b705cfSriastradh	PixmapPtr scratch;
624503b705cfSriastradh	PicturePtr mask;
624603b705cfSriastradh	BoxRec extents;
624703b705cfSriastradh	int16_t dst_x, dst_y;
624803b705cfSriastradh	int dx, dy;
624903b705cfSriastradh	int error, n;
625003b705cfSriastradh
625103b705cfSriastradh	if (NO_SCAN_CONVERTER)
625203b705cfSriastradh		return false;
625303b705cfSriastradh
625403b705cfSriastradh	if (dst->polyMode == PolyModePrecise && !is_mono(dst, maskFormat)) {
625503b705cfSriastradh		DBG(("%s: fallback -- precise rasterisation requested\n",
625603b705cfSriastradh		     __FUNCTION__));
625703b705cfSriastradh		return false;
625803b705cfSriastradh	}
625903b705cfSriastradh
626003b705cfSriastradh	if (maskFormat == NULL && ntrap > 1) {
626103b705cfSriastradh		DBG(("%s: individual rasterisation requested\n",
626203b705cfSriastradh		     __FUNCTION__));
626303b705cfSriastradh		do {
626403b705cfSriastradh			/* XXX unwind errors? */
626503b705cfSriastradh			if (!trapezoid_span_fallback(op, src, dst, NULL,
626603b705cfSriastradh						     src_x, src_y, 1, traps++))
626703b705cfSriastradh				return false;
626803b705cfSriastradh		} while (--ntrap);
626903b705cfSriastradh		return true;
627003b705cfSriastradh	}
627103b705cfSriastradh
627203b705cfSriastradh	trapezoids_bounds(ntrap, traps, &extents);
627303b705cfSriastradh	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
627403b705cfSriastradh		return true;
627503b705cfSriastradh
627603b705cfSriastradh	DBG(("%s: ntraps=%d, extents (%d, %d), (%d, %d)\n",
627703b705cfSriastradh	     __FUNCTION__, ntrap, extents.x1, extents.y1, extents.x2, extents.y2));
627803b705cfSriastradh
627903b705cfSriastradh	if (!sna_compute_composite_extents(&extents,
628003b705cfSriastradh					   src, NULL, dst,
628103b705cfSriastradh					   src_x, src_y,
628203b705cfSriastradh					   0, 0,
628303b705cfSriastradh					   extents.x1, extents.y1,
628403b705cfSriastradh					   extents.x2 - extents.x1,
628503b705cfSriastradh					   extents.y2 - extents.y1))
628603b705cfSriastradh		return true;
628703b705cfSriastradh
628803b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
628903b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
629003b705cfSriastradh
629103b705cfSriastradh	extents.y2 -= extents.y1;
629203b705cfSriastradh	extents.x2 -= extents.x1;
629303b705cfSriastradh	extents.x1 -= dst->pDrawable->x;
629403b705cfSriastradh	extents.y1 -= dst->pDrawable->y;
629503b705cfSriastradh	dst_x = extents.x1;
629603b705cfSriastradh	dst_y = extents.y1;
629703b705cfSriastradh	dx = -extents.x1 * FAST_SAMPLES_X;
629803b705cfSriastradh	dy = -extents.y1 * FAST_SAMPLES_Y;
629903b705cfSriastradh	extents.x1 = extents.y1 = 0;
630003b705cfSriastradh
630103b705cfSriastradh	DBG(("%s: mask (%dx%d), dx=(%d, %d)\n",
630203b705cfSriastradh	     __FUNCTION__, extents.x2, extents.y2, dx, dy));
630303b705cfSriastradh	scratch = sna_pixmap_create_unattached(screen,
630403b705cfSriastradh					       extents.x2, extents.y2, 8);
630503b705cfSriastradh	if (!scratch)
630603b705cfSriastradh		return true;
630703b705cfSriastradh
630803b705cfSriastradh	DBG(("%s: created buffer %p, stride %d\n",
630903b705cfSriastradh	     __FUNCTION__, scratch->devPrivate.ptr, scratch->devKind));
631003b705cfSriastradh
631103b705cfSriastradh	if (!tor_init(&tor, &extents, 2*ntrap)) {
631203b705cfSriastradh		sna_pixmap_destroy(scratch);
631303b705cfSriastradh		return true;
631403b705cfSriastradh	}
631503b705cfSriastradh
631603b705cfSriastradh	for (n = 0; n < ntrap; n++) {
631703b705cfSriastradh		xTrapezoid t;
631803b705cfSriastradh
631903b705cfSriastradh		if (!project_trapezoid_onto_grid(&traps[n], dx, dy, &t))
632003b705cfSriastradh			continue;
632103b705cfSriastradh
632203b705cfSriastradh		if (pixman_fixed_to_int(traps[n].top) - dst_y >= extents.y2 ||
632303b705cfSriastradh		    pixman_fixed_to_int(traps[n].bottom) - dst_y < 0)
632403b705cfSriastradh			continue;
632503b705cfSriastradh
632603b705cfSriastradh		tor_add_edge(&tor, &t, &t.left, 1);
632703b705cfSriastradh		tor_add_edge(&tor, &t, &t.right, -1);
632803b705cfSriastradh	}
632903b705cfSriastradh
633003b705cfSriastradh	if (extents.x2 <= TOR_INPLACE_SIZE) {
633103b705cfSriastradh		tor_inplace(&tor, scratch, is_mono(dst, maskFormat), NULL);
633203b705cfSriastradh	} else {
633303b705cfSriastradh		tor_render(NULL, &tor,
633403b705cfSriastradh			   scratch->devPrivate.ptr,
633503b705cfSriastradh			   (void *)(intptr_t)scratch->devKind,
633603b705cfSriastradh			   is_mono(dst, maskFormat) ? tor_blt_mask_mono : tor_blt_mask,
633703b705cfSriastradh			   true);
633803b705cfSriastradh	}
633903b705cfSriastradh	tor_fini(&tor);
634003b705cfSriastradh
634103b705cfSriastradh	mask = CreatePicture(0, &scratch->drawable,
634203b705cfSriastradh			     PictureMatchFormat(screen, 8, PICT_a8),
634303b705cfSriastradh			     0, 0, serverClient, &error);
634403b705cfSriastradh	if (mask) {
634503b705cfSriastradh		RegionRec region;
634603b705cfSriastradh
634703b705cfSriastradh		region.extents.x1 = dst_x + dst->pDrawable->x;
634803b705cfSriastradh		region.extents.y1 = dst_y + dst->pDrawable->y;
634903b705cfSriastradh		region.extents.x2 = region.extents.x1 + extents.x2;
635003b705cfSriastradh		region.extents.y2 = region.extents.y1 + extents.y2;
635103b705cfSriastradh		region.data = NULL;
635203b705cfSriastradh
635303b705cfSriastradh		DBG(("%s: fbComposite()\n", __FUNCTION__));
635403b705cfSriastradh		sna_composite_fb(op, src, mask, dst, &region,
635503b705cfSriastradh				 src_x + dst_x - pixman_fixed_to_int(traps[0].left.p1.x),
635603b705cfSriastradh				 src_y + dst_y - pixman_fixed_to_int(traps[0].left.p1.y),
635703b705cfSriastradh				 0, 0,
635803b705cfSriastradh				 dst_x, dst_y,
635903b705cfSriastradh				 extents.x2, extents.y2);
636003b705cfSriastradh
636103b705cfSriastradh		FreePicture(mask, 0);
636203b705cfSriastradh	}
636303b705cfSriastradh	sna_pixmap_destroy(scratch);
636403b705cfSriastradh
636503b705cfSriastradh	return true;
636603b705cfSriastradh}
636703b705cfSriastradh
636803b705cfSriastradhvoid
636903b705cfSriastradhsna_composite_trapezoids(CARD8 op,
637003b705cfSriastradh			 PicturePtr src,
637103b705cfSriastradh			 PicturePtr dst,
637203b705cfSriastradh			 PictFormatPtr maskFormat,
637303b705cfSriastradh			 INT16 xSrc, INT16 ySrc,
637403b705cfSriastradh			 int ntrap, xTrapezoid *traps)
637503b705cfSriastradh{
637603b705cfSriastradh	PixmapPtr pixmap = get_drawable_pixmap(dst->pDrawable);
637703b705cfSriastradh	struct sna *sna = to_sna_from_pixmap(pixmap);
637803b705cfSriastradh	struct sna_pixmap *priv;
637903b705cfSriastradh	bool rectilinear, pixel_aligned, force_fallback;
638003b705cfSriastradh	unsigned flags;
638103b705cfSriastradh	int n;
638203b705cfSriastradh
638303b705cfSriastradh	DBG(("%s(op=%d, src=(%d, %d), mask=%08x, ntrap=%d)\n", __FUNCTION__,
638403b705cfSriastradh	     op, xSrc, ySrc,
638503b705cfSriastradh	     maskFormat ? (int)maskFormat->format : 0,
638603b705cfSriastradh	     ntrap));
638703b705cfSriastradh
638803b705cfSriastradh	if (ntrap == 0)
638903b705cfSriastradh		return;
639003b705cfSriastradh
639103b705cfSriastradh	if (NO_ACCEL)
639203b705cfSriastradh		goto fallback;
639303b705cfSriastradh
639403b705cfSriastradh	if (wedged(sna)) {
639503b705cfSriastradh		DBG(("%s: fallback -- wedged\n", __FUNCTION__));
639603b705cfSriastradh		goto fallback;
639703b705cfSriastradh	}
639803b705cfSriastradh
639903b705cfSriastradh	if (dst->alphaMap) {
640003b705cfSriastradh		DBG(("%s: fallback -- dst alpha map\n", __FUNCTION__));
640103b705cfSriastradh		goto fallback;
640203b705cfSriastradh	}
640303b705cfSriastradh
640403b705cfSriastradh	priv = sna_pixmap(pixmap);
640503b705cfSriastradh	if (priv == NULL) {
640603b705cfSriastradh		DBG(("%s: fallback -- dst is unattached\n", __FUNCTION__));
640703b705cfSriastradh		goto fallback;
640803b705cfSriastradh	}
640903b705cfSriastradh
641003b705cfSriastradh	force_fallback = FORCE_FALLBACK > 0;
641103b705cfSriastradh	if ((too_small(priv) || DAMAGE_IS_ALL(priv->cpu_damage)) &&
641203b705cfSriastradh	    !picture_is_gpu(sna, src) && untransformed(src)) {
641303b705cfSriastradh		DBG(("%s: force fallbacks --too small, %dx%d? %d, all-cpu? %d, src-is-cpu? %d\n",
641403b705cfSriastradh		     __FUNCTION__,
641503b705cfSriastradh		     dst->pDrawable->width,
641603b705cfSriastradh		     dst->pDrawable->height,
641703b705cfSriastradh		     too_small(priv),
641803b705cfSriastradh		     (int)DAMAGE_IS_ALL(priv->cpu_damage),
641903b705cfSriastradh		     !picture_is_gpu(sna, src)));
642003b705cfSriastradh		force_fallback = true;
642103b705cfSriastradh	}
642203b705cfSriastradh	if (FORCE_FALLBACK < 0)
642303b705cfSriastradh		force_fallback = false;
642403b705cfSriastradh
642503b705cfSriastradh	/* scan through for fast rectangles */
642603b705cfSriastradh	rectilinear = pixel_aligned = true;
642703b705cfSriastradh	if (is_mono(dst, maskFormat)) {
642803b705cfSriastradh		for (n = 0; n < ntrap && rectilinear; n++) {
642903b705cfSriastradh			int lx1 = pixman_fixed_to_int(traps[n].left.p1.x + pixman_fixed_1_minus_e/2);
643003b705cfSriastradh			int lx2 = pixman_fixed_to_int(traps[n].left.p2.x + pixman_fixed_1_minus_e/2);
643103b705cfSriastradh			int rx1 = pixman_fixed_to_int(traps[n].right.p1.x + pixman_fixed_1_minus_e/2);
643203b705cfSriastradh			int rx2 = pixman_fixed_to_int(traps[n].right.p2.x + pixman_fixed_1_minus_e/2);
643303b705cfSriastradh			rectilinear &= lx1 == lx2 && rx1 == rx2;
643403b705cfSriastradh		}
643503b705cfSriastradh	} else if (dst->polyMode != PolyModePrecise) {
643603b705cfSriastradh		for (n = 0; n < ntrap && rectilinear; n++) {
643703b705cfSriastradh			int lx1 = pixman_fixed_to_grid(traps[n].left.p1.x);
643803b705cfSriastradh			int lx2 = pixman_fixed_to_grid(traps[n].left.p2.x);
643903b705cfSriastradh			int rx1 = pixman_fixed_to_grid(traps[n].right.p1.x);
644003b705cfSriastradh			int rx2 = pixman_fixed_to_grid(traps[n].right.p2.x);
644103b705cfSriastradh			int top = pixman_fixed_to_grid(traps[n].top);
644203b705cfSriastradh			int bot = pixman_fixed_to_grid(traps[n].bottom);
644303b705cfSriastradh
644403b705cfSriastradh			rectilinear &= lx1 == lx2 && rx1 == rx2;
644503b705cfSriastradh			pixel_aligned &= ((top | bot | lx1 | lx2 | rx1 | rx2) & FAST_SAMPLES_mask) == 0;
644603b705cfSriastradh		}
644703b705cfSriastradh	} else {
644803b705cfSriastradh		for (n = 0; n < ntrap && rectilinear; n++) {
644903b705cfSriastradh			rectilinear &=
645003b705cfSriastradh				traps[n].left.p1.x == traps[n].left.p2.x &&
645103b705cfSriastradh				traps[n].right.p1.x == traps[n].right.p2.x;
645203b705cfSriastradh			pixel_aligned &=
645303b705cfSriastradh				((traps[n].top | traps[n].bottom |
645403b705cfSriastradh				  traps[n].left.p1.x | traps[n].left.p2.x |
645503b705cfSriastradh				  traps[n].right.p1.x | traps[n].right.p2.x)
645603b705cfSriastradh				 & pixman_fixed_1_minus_e) == 0;
645703b705cfSriastradh		}
645803b705cfSriastradh	}
645903b705cfSriastradh
646003b705cfSriastradh	DBG(("%s: rectilinear? %d, pixel-aligned? %d\n",
646103b705cfSriastradh	     __FUNCTION__, rectilinear, pixel_aligned));
646203b705cfSriastradh	flags = 0;
646303b705cfSriastradh	if (rectilinear) {
646403b705cfSriastradh		if (pixel_aligned) {
646503b705cfSriastradh			if (composite_aligned_boxes(sna, op, src, dst,
646603b705cfSriastradh						    maskFormat,
646703b705cfSriastradh						    xSrc, ySrc,
646803b705cfSriastradh						    ntrap, traps,
646903b705cfSriastradh						    force_fallback))
647003b705cfSriastradh			    return;
647103b705cfSriastradh		} else {
647203b705cfSriastradh			if (composite_unaligned_boxes(sna, op, src, dst,
647303b705cfSriastradh						      maskFormat,
647403b705cfSriastradh						      xSrc, ySrc,
647503b705cfSriastradh						      ntrap, traps,
647603b705cfSriastradh						      force_fallback))
647703b705cfSriastradh				return;
647803b705cfSriastradh		}
647903b705cfSriastradh		flags |= COMPOSITE_SPANS_RECTILINEAR;
648003b705cfSriastradh	}
648103b705cfSriastradh
648203b705cfSriastradh	if (force_fallback)
648303b705cfSriastradh		goto fallback;
648403b705cfSriastradh
648503b705cfSriastradh	if (is_mono(dst, maskFormat) &&
648603b705cfSriastradh	    mono_trapezoids_span_converter(sna, op, src, dst,
648703b705cfSriastradh					   xSrc, ySrc,
648803b705cfSriastradh					   ntrap, traps))
648903b705cfSriastradh		return;
649003b705cfSriastradh
649103b705cfSriastradh	if (trapezoid_spans_maybe_inplace(sna, op, src, dst, maskFormat)) {
649203b705cfSriastradh		flags |= COMPOSITE_SPANS_INPLACE_HINT;
649303b705cfSriastradh		if (trapezoid_span_inplace(sna, op, src, dst, maskFormat,
649403b705cfSriastradh					   xSrc, ySrc, ntrap, traps,
649503b705cfSriastradh					   false))
649603b705cfSriastradh			return;
649703b705cfSriastradh	}
649803b705cfSriastradh
649903b705cfSriastradh	if (trapezoid_span_converter(sna, op, src, dst, maskFormat, flags,
650003b705cfSriastradh				     xSrc, ySrc, ntrap, traps))
650103b705cfSriastradh		return;
650203b705cfSriastradh
650303b705cfSriastradh	if (trapezoid_span_inplace(sna, op, src, dst, maskFormat,
650403b705cfSriastradh				   xSrc, ySrc, ntrap, traps,
650503b705cfSriastradh				   false))
650603b705cfSriastradh		return;
650703b705cfSriastradh
650803b705cfSriastradh	if (trapezoid_mask_converter(op, src, dst, maskFormat,
650903b705cfSriastradh				     xSrc, ySrc, ntrap, traps))
651003b705cfSriastradh		return;
651103b705cfSriastradh
651203b705cfSriastradhfallback:
651303b705cfSriastradh	if (trapezoid_span_inplace(sna, op, src, dst, maskFormat,
651403b705cfSriastradh				   xSrc, ySrc, ntrap, traps,
651503b705cfSriastradh				   true))
651603b705cfSriastradh		return;
651703b705cfSriastradh
651803b705cfSriastradh	if (trapezoid_span_fallback(op, src, dst, maskFormat,
651903b705cfSriastradh				    xSrc, ySrc, ntrap, traps))
652003b705cfSriastradh		return;
652103b705cfSriastradh
652203b705cfSriastradh	if (trapezoids_inplace_fallback(sna, op, src, dst, maskFormat,
652303b705cfSriastradh					ntrap, traps))
652403b705cfSriastradh		return;
652503b705cfSriastradh
652603b705cfSriastradh	DBG(("%s: fallback mask=%08x, ntrap=%d\n", __FUNCTION__,
652703b705cfSriastradh	     maskFormat ? (unsigned)maskFormat->format : 0, ntrap));
652803b705cfSriastradh	trapezoids_fallback(sna, op, src, dst, maskFormat,
652903b705cfSriastradh			    xSrc, ySrc,
653003b705cfSriastradh			    ntrap, traps);
653103b705cfSriastradh}
653203b705cfSriastradh
653303b705cfSriastradhstatic inline bool
653403b705cfSriastradhproject_trap_onto_grid(const xTrap *in,
653503b705cfSriastradh		       int dx, int dy,
653603b705cfSriastradh		       xTrap *out)
653703b705cfSriastradh{
653803b705cfSriastradh	out->top.l = dx + pixman_fixed_to_grid(in->top.l);
653903b705cfSriastradh	out->top.r = dx + pixman_fixed_to_grid(in->top.r);
654003b705cfSriastradh	out->top.y = dy + pixman_fixed_to_grid(in->top.y);
654103b705cfSriastradh
654203b705cfSriastradh	out->bot.l = dx + pixman_fixed_to_grid(in->bot.l);
654303b705cfSriastradh	out->bot.r = dx + pixman_fixed_to_grid(in->bot.r);
654403b705cfSriastradh	out->bot.y = dy + pixman_fixed_to_grid(in->bot.y);
654503b705cfSriastradh
654603b705cfSriastradh	return out->bot.y > out->top.y;
654703b705cfSriastradh}
654803b705cfSriastradh
654903b705cfSriastradhstatic bool
655003b705cfSriastradhmono_trap_span_converter(struct sna *sna,
655103b705cfSriastradh			 PicturePtr dst,
655203b705cfSriastradh			 INT16 x, INT16 y,
655303b705cfSriastradh			 int ntrap, xTrap *traps)
655403b705cfSriastradh{
655503b705cfSriastradh	struct mono mono;
655603b705cfSriastradh	xRenderColor white;
655703b705cfSriastradh	PicturePtr src;
655803b705cfSriastradh	int error;
655903b705cfSriastradh	int n;
656003b705cfSriastradh
656103b705cfSriastradh	white.red = white.green = white.blue = white.alpha = 0xffff;
656203b705cfSriastradh	src = CreateSolidPicture(0, &white, &error);
656303b705cfSriastradh	if (src == NULL)
656403b705cfSriastradh		return true;
656503b705cfSriastradh
656603b705cfSriastradh	mono.clip = *dst->pCompositeClip;
656703b705cfSriastradh	x += dst->pDrawable->x;
656803b705cfSriastradh	y += dst->pDrawable->y;
656903b705cfSriastradh
657003b705cfSriastradh	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d)\n",
657103b705cfSriastradh	     __FUNCTION__,
657203b705cfSriastradh	     mono.clip.extents.x1, mono.clip.extents.y1,
657303b705cfSriastradh	     mono.clip.extents.x2, mono.clip.extents.y2,
657403b705cfSriastradh	     x, y));
657503b705cfSriastradh
657603b705cfSriastradh	mono.sna = sna;
657703b705cfSriastradh	if (!mono_init(&mono, 2*ntrap))
657803b705cfSriastradh		return false;
657903b705cfSriastradh
658003b705cfSriastradh	for (n = 0; n < ntrap; n++) {
658103b705cfSriastradh		xPointFixed p1, p2;
658203b705cfSriastradh
658303b705cfSriastradh		if (pixman_fixed_to_int(traps[n].top.y) + y >= mono.clip.extents.y2 ||
658403b705cfSriastradh		    pixman_fixed_to_int(traps[n].bot.y) + y < mono.clip.extents.y1)
658503b705cfSriastradh			continue;
658603b705cfSriastradh
658703b705cfSriastradh		p1.y = traps[n].top.y;
658803b705cfSriastradh		p2.y = traps[n].bot.y;
658903b705cfSriastradh
659003b705cfSriastradh		p1.x = traps[n].top.l;
659103b705cfSriastradh		p2.x = traps[n].bot.l;
659203b705cfSriastradh		mono_add_line(&mono, x, y,
659303b705cfSriastradh			      traps[n].top.y, traps[n].bot.y,
659403b705cfSriastradh			      &p1, &p2, 1);
659503b705cfSriastradh
659603b705cfSriastradh		p1.x = traps[n].top.r;
659703b705cfSriastradh		p2.x = traps[n].bot.r;
659803b705cfSriastradh		mono_add_line(&mono, x, y,
659903b705cfSriastradh			      traps[n].top.y, traps[n].bot.y,
660003b705cfSriastradh			      &p1, &p2, -1);
660103b705cfSriastradh	}
660203b705cfSriastradh
660303b705cfSriastradh	memset(&mono.op, 0, sizeof(mono.op));
660403b705cfSriastradh	if (mono.sna->render.composite(mono.sna, PictOpAdd, src, NULL, dst,
660503b705cfSriastradh					0, 0,
660603b705cfSriastradh					0, 0,
660703b705cfSriastradh					mono.clip.extents.x1,  mono.clip.extents.y1,
660803b705cfSriastradh					mono.clip.extents.x2 - mono.clip.extents.x1,
660903b705cfSriastradh					mono.clip.extents.y2 - mono.clip.extents.y1,
661003b705cfSriastradh					&mono.op)) {
661103b705cfSriastradh		mono_render(&mono);
661203b705cfSriastradh		mono.op.done(mono.sna, &mono.op);
661303b705cfSriastradh	}
661403b705cfSriastradh
661503b705cfSriastradh	mono_fini(&mono);
661603b705cfSriastradh	FreePicture(src, 0);
661703b705cfSriastradh	return true;
661803b705cfSriastradh}
661903b705cfSriastradh
662003b705cfSriastradhstatic bool
662103b705cfSriastradhtrap_span_converter(struct sna *sna,
662203b705cfSriastradh		    PicturePtr dst,
662303b705cfSriastradh		    INT16 src_x, INT16 src_y,
662403b705cfSriastradh		    int ntrap, xTrap *trap)
662503b705cfSriastradh{
662603b705cfSriastradh	struct sna_composite_spans_op tmp;
662703b705cfSriastradh	struct tor tor;
662803b705cfSriastradh	BoxRec extents;
662903b705cfSriastradh	pixman_region16_t *clip;
663003b705cfSriastradh	int dx, dy, n;
663103b705cfSriastradh
663203b705cfSriastradh	if (NO_SCAN_CONVERTER)
663303b705cfSriastradh		return false;
663403b705cfSriastradh
663503b705cfSriastradh	if (dst->pDrawable->depth < 8)
663603b705cfSriastradh		return false;
663703b705cfSriastradh
663803b705cfSriastradh	if (dst->polyEdge == PolyEdgeSharp)
663903b705cfSriastradh		return mono_trap_span_converter(sna, dst, src_x, src_y, ntrap, trap);
664003b705cfSriastradh
664103b705cfSriastradh	if (!sna->render.check_composite_spans(sna, PictOpAdd, sna->render.white_picture, dst,
664203b705cfSriastradh					       dst->pCompositeClip->extents.x2 - dst->pCompositeClip->extents.x1,
664303b705cfSriastradh					       dst->pCompositeClip->extents.y2 - dst->pCompositeClip->extents.y1,
664403b705cfSriastradh					       0)) {
664503b705cfSriastradh		DBG(("%s: fallback -- composite spans not supported\n",
664603b705cfSriastradh		     __FUNCTION__));
664703b705cfSriastradh		return false;
664803b705cfSriastradh	}
664903b705cfSriastradh
665003b705cfSriastradh	clip = dst->pCompositeClip;
665103b705cfSriastradh	extents = *RegionExtents(clip);
665203b705cfSriastradh	dx = dst->pDrawable->x;
665303b705cfSriastradh	dy = dst->pDrawable->y;
665403b705cfSriastradh
665503b705cfSriastradh	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d)\n",
665603b705cfSriastradh	     __FUNCTION__,
665703b705cfSriastradh	     extents.x1, extents.y1,
665803b705cfSriastradh	     extents.x2, extents.y2,
665903b705cfSriastradh	     dx, dy));
666003b705cfSriastradh
666103b705cfSriastradh	memset(&tmp, 0, sizeof(tmp));
666203b705cfSriastradh	if (!sna->render.composite_spans(sna, PictOpAdd, sna->render.white_picture, dst,
666303b705cfSriastradh					 0, 0,
666403b705cfSriastradh					 extents.x1,  extents.y1,
666503b705cfSriastradh					 extents.x2 - extents.x1,
666603b705cfSriastradh					 extents.y2 - extents.y1,
666703b705cfSriastradh					 0,
666803b705cfSriastradh					 &tmp)) {
666903b705cfSriastradh		DBG(("%s: fallback -- composite spans render op not supported\n",
667003b705cfSriastradh		     __FUNCTION__));
667103b705cfSriastradh		return false;
667203b705cfSriastradh	}
667303b705cfSriastradh
667403b705cfSriastradh	dx *= FAST_SAMPLES_X;
667503b705cfSriastradh	dy *= FAST_SAMPLES_Y;
667603b705cfSriastradh	if (!tor_init(&tor, &extents, 2*ntrap))
667703b705cfSriastradh		goto skip;
667803b705cfSriastradh
667903b705cfSriastradh	for (n = 0; n < ntrap; n++) {
668003b705cfSriastradh		xTrap t;
668103b705cfSriastradh		xPointFixed p1, p2;
668203b705cfSriastradh
668303b705cfSriastradh		if (!project_trap_onto_grid(&trap[n], dx, dy, &t))
668403b705cfSriastradh			continue;
668503b705cfSriastradh
668603b705cfSriastradh		if (pixman_fixed_to_int(trap[n].top.y) + dst->pDrawable->y >= extents.y2 ||
668703b705cfSriastradh		    pixman_fixed_to_int(trap[n].bot.y) + dst->pDrawable->y < extents.y1)
668803b705cfSriastradh			continue;
668903b705cfSriastradh
669003b705cfSriastradh		p1.y = t.top.y;
669103b705cfSriastradh		p2.y = t.bot.y;
669203b705cfSriastradh		p1.x = t.top.l;
669303b705cfSriastradh		p2.x = t.bot.l;
669403b705cfSriastradh		polygon_add_line(tor.polygon, &p1, &p2);
669503b705cfSriastradh
669603b705cfSriastradh		p1.y = t.bot.y;
669703b705cfSriastradh		p2.y = t.top.y;
669803b705cfSriastradh		p1.x = t.top.r;
669903b705cfSriastradh		p2.x = t.bot.r;
670003b705cfSriastradh		polygon_add_line(tor.polygon, &p1, &p2);
670103b705cfSriastradh	}
670203b705cfSriastradh
670303b705cfSriastradh	tor_render(sna, &tor, &tmp, clip,
670403b705cfSriastradh		   choose_span(&tmp, dst, NULL, clip), false);
670503b705cfSriastradh
670603b705cfSriastradh	tor_fini(&tor);
670703b705cfSriastradhskip:
670803b705cfSriastradh	tmp.done(sna, &tmp);
670903b705cfSriastradh	return true;
671003b705cfSriastradh}
671103b705cfSriastradh
671203b705cfSriastradhstatic void mark_damaged(PixmapPtr pixmap, struct sna_pixmap *priv,
671303b705cfSriastradh			 BoxPtr box, int16_t x, int16_t y)
671403b705cfSriastradh{
671503b705cfSriastradh	box->x1 += x; box->x2 += x;
671603b705cfSriastradh	box->y1 += y; box->y2 += y;
671703b705cfSriastradh	if (box->x1 <= 0 && box->y1 <= 0 &&
671803b705cfSriastradh	    box->x2 >= pixmap->drawable.width &&
671903b705cfSriastradh	    box->y2 >= pixmap->drawable.height) {
672003b705cfSriastradh		sna_damage_destroy(&priv->cpu_damage);
672103b705cfSriastradh		sna_damage_all(&priv->gpu_damage,
672203b705cfSriastradh			       pixmap->drawable.width,
672303b705cfSriastradh			       pixmap->drawable.height);
672403b705cfSriastradh		list_del(&priv->flush_list);
672503b705cfSriastradh	} else {
672603b705cfSriastradh		sna_damage_add_box(&priv->gpu_damage, box);
672703b705cfSriastradh		sna_damage_subtract_box(&priv->cpu_damage, box);
672803b705cfSriastradh	}
672903b705cfSriastradh}
673003b705cfSriastradh
673103b705cfSriastradhstatic bool
673203b705cfSriastradhtrap_mask_converter(struct sna *sna,
673303b705cfSriastradh		    PicturePtr picture,
673403b705cfSriastradh		    INT16 x, INT16 y,
673503b705cfSriastradh		    int ntrap, xTrap *trap)
673603b705cfSriastradh{
673703b705cfSriastradh	struct tor tor;
673803b705cfSriastradh	ScreenPtr screen = picture->pDrawable->pScreen;
673903b705cfSriastradh	PixmapPtr scratch, pixmap;
674003b705cfSriastradh	struct sna_pixmap *priv;
674103b705cfSriastradh	BoxRec extents;
674203b705cfSriastradh	span_func_t span;
674303b705cfSriastradh	int dx, dy, n;
674403b705cfSriastradh
674503b705cfSriastradh	if (NO_SCAN_CONVERTER)
674603b705cfSriastradh		return false;
674703b705cfSriastradh
674803b705cfSriastradh	pixmap = get_drawable_pixmap(picture->pDrawable);
674903b705cfSriastradh	priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_WRITE);
675003b705cfSriastradh	if (priv == NULL)
675103b705cfSriastradh		return false;
675203b705cfSriastradh
675303b705cfSriastradh	/* XXX strict adherence to the Render specification */
675403b705cfSriastradh	if (picture->polyMode == PolyModePrecise &&
675503b705cfSriastradh	    picture->polyEdge != PolyEdgeSharp) {
675603b705cfSriastradh		DBG(("%s: fallback -- precise rasterisation requested\n",
675703b705cfSriastradh		     __FUNCTION__));
675803b705cfSriastradh		return false;
675903b705cfSriastradh	}
676003b705cfSriastradh
676103b705cfSriastradh	extents = *RegionExtents(picture->pCompositeClip);
676203b705cfSriastradh	for (n = 0; n < ntrap; n++) {
676303b705cfSriastradh		int v;
676403b705cfSriastradh
676503b705cfSriastradh		v = x + pixman_fixed_integer_floor (MIN(trap[n].top.l, trap[n].bot.l));
676603b705cfSriastradh		if (v < extents.x1)
676703b705cfSriastradh			extents.x1 = v;
676803b705cfSriastradh
676903b705cfSriastradh		v = x + pixman_fixed_integer_ceil (MAX(trap[n].top.r, trap[n].bot.r));
677003b705cfSriastradh		if (v > extents.x2)
677103b705cfSriastradh			extents.x2 = v;
677203b705cfSriastradh
677303b705cfSriastradh		v = y + pixman_fixed_integer_floor (trap[n].top.y);
677403b705cfSriastradh		if (v < extents.y1)
677503b705cfSriastradh			extents.y1 = v;
677603b705cfSriastradh
677703b705cfSriastradh		v = y + pixman_fixed_integer_ceil (trap[n].bot.y);
677803b705cfSriastradh		if (v > extents.y2)
677903b705cfSriastradh			extents.y2 = v;
678003b705cfSriastradh	}
678103b705cfSriastradh
678203b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
678303b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
678403b705cfSriastradh
678503b705cfSriastradh	scratch = sna_pixmap_create_upload(screen,
678603b705cfSriastradh					   extents.x2-extents.x1,
678703b705cfSriastradh					   extents.y2-extents.y1,
678803b705cfSriastradh					   8, KGEM_BUFFER_WRITE_INPLACE);
678903b705cfSriastradh	if (!scratch)
679003b705cfSriastradh		return true;
679103b705cfSriastradh
679203b705cfSriastradh	dx = picture->pDrawable->x;
679303b705cfSriastradh	dy = picture->pDrawable->y;
679403b705cfSriastradh	dx *= FAST_SAMPLES_X;
679503b705cfSriastradh	dy *= FAST_SAMPLES_Y;
679603b705cfSriastradh	if (!tor_init(&tor, &extents, 2*ntrap)) {
679703b705cfSriastradh		sna_pixmap_destroy(scratch);
679803b705cfSriastradh		return true;
679903b705cfSriastradh	}
680003b705cfSriastradh
680103b705cfSriastradh	for (n = 0; n < ntrap; n++) {
680203b705cfSriastradh		xTrap t;
680303b705cfSriastradh		xPointFixed p1, p2;
680403b705cfSriastradh
680503b705cfSriastradh		if (!project_trap_onto_grid(&trap[n], dx, dy, &t))
680603b705cfSriastradh			continue;
680703b705cfSriastradh
680803b705cfSriastradh		if (pixman_fixed_to_int(trap[n].top.y) + picture->pDrawable->y >= extents.y2 ||
680903b705cfSriastradh		    pixman_fixed_to_int(trap[n].bot.y) + picture->pDrawable->y < extents.y1)
681003b705cfSriastradh			continue;
681103b705cfSriastradh
681203b705cfSriastradh		p1.y = t.top.y;
681303b705cfSriastradh		p2.y = t.bot.y;
681403b705cfSriastradh		p1.x = t.top.l;
681503b705cfSriastradh		p2.x = t.bot.l;
681603b705cfSriastradh		polygon_add_line(tor.polygon, &p1, &p2);
681703b705cfSriastradh
681803b705cfSriastradh		p1.y = t.bot.y;
681903b705cfSriastradh		p2.y = t.top.y;
682003b705cfSriastradh		p1.x = t.top.r;
682103b705cfSriastradh		p2.x = t.bot.r;
682203b705cfSriastradh		polygon_add_line(tor.polygon, &p1, &p2);
682303b705cfSriastradh	}
682403b705cfSriastradh
682503b705cfSriastradh	if (picture->polyEdge == PolyEdgeSharp)
682603b705cfSriastradh		span = tor_blt_mask_mono;
682703b705cfSriastradh	else
682803b705cfSriastradh		span = tor_blt_mask;
682903b705cfSriastradh
683003b705cfSriastradh	tor_render(NULL, &tor,
683103b705cfSriastradh		   scratch->devPrivate.ptr,
683203b705cfSriastradh		   (void *)(intptr_t)scratch->devKind,
683303b705cfSriastradh		   span, true);
683403b705cfSriastradh
683503b705cfSriastradh	tor_fini(&tor);
683603b705cfSriastradh
683703b705cfSriastradh	/* XXX clip boxes */
683803b705cfSriastradh	get_drawable_deltas(picture->pDrawable, pixmap, &x, &y);
683903b705cfSriastradh	sna = to_sna_from_screen(screen);
684003b705cfSriastradh	sna->render.copy_boxes(sna, GXcopy,
684103b705cfSriastradh			       scratch, __sna_pixmap_get_bo(scratch), -extents.x1, -extents.x1,
684203b705cfSriastradh			       pixmap, priv->gpu_bo, x, y,
684303b705cfSriastradh			       &extents, 1, 0);
684403b705cfSriastradh	mark_damaged(pixmap, priv, &extents ,x, y);
684503b705cfSriastradh	sna_pixmap_destroy(scratch);
684603b705cfSriastradh	return true;
684703b705cfSriastradh}
684803b705cfSriastradh
684903b705cfSriastradhstatic bool
685003b705cfSriastradhtrap_upload(PicturePtr picture,
685103b705cfSriastradh	    INT16 x, INT16 y,
685203b705cfSriastradh	    int ntrap, xTrap *trap)
685303b705cfSriastradh{
685403b705cfSriastradh	ScreenPtr screen = picture->pDrawable->pScreen;
685503b705cfSriastradh	struct sna *sna = to_sna_from_screen(screen);
685603b705cfSriastradh	PixmapPtr pixmap = get_drawable_pixmap(picture->pDrawable);
685703b705cfSriastradh	PixmapPtr scratch;
685803b705cfSriastradh	struct sna_pixmap *priv;
685903b705cfSriastradh	BoxRec extents;
686003b705cfSriastradh	pixman_image_t *image;
686103b705cfSriastradh	int width, height, depth;
686203b705cfSriastradh	int n;
686303b705cfSriastradh
686403b705cfSriastradh	priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_WRITE);
686503b705cfSriastradh	if (priv == NULL)
686603b705cfSriastradh		return false;
686703b705cfSriastradh
686803b705cfSriastradh	extents = *RegionExtents(picture->pCompositeClip);
686903b705cfSriastradh	for (n = 0; n < ntrap; n++) {
687003b705cfSriastradh		int v;
687103b705cfSriastradh
687203b705cfSriastradh		v = x + pixman_fixed_integer_floor (MIN(trap[n].top.l, trap[n].bot.l));
687303b705cfSriastradh		if (v < extents.x1)
687403b705cfSriastradh			extents.x1 = v;
687503b705cfSriastradh
687603b705cfSriastradh		v = x + pixman_fixed_integer_ceil (MAX(trap[n].top.r, trap[n].bot.r));
687703b705cfSriastradh		if (v > extents.x2)
687803b705cfSriastradh			extents.x2 = v;
687903b705cfSriastradh
688003b705cfSriastradh		v = y + pixman_fixed_integer_floor (trap[n].top.y);
688103b705cfSriastradh		if (v < extents.y1)
688203b705cfSriastradh			extents.y1 = v;
688303b705cfSriastradh
688403b705cfSriastradh		v = y + pixman_fixed_integer_ceil (trap[n].bot.y);
688503b705cfSriastradh		if (v > extents.y2)
688603b705cfSriastradh			extents.y2 = v;
688703b705cfSriastradh	}
688803b705cfSriastradh
688903b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
689003b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
689103b705cfSriastradh
689203b705cfSriastradh	width  = extents.x2 - extents.x1;
689303b705cfSriastradh	height = extents.y2 - extents.y1;
689403b705cfSriastradh	depth = picture->pDrawable->depth;
689503b705cfSriastradh
689603b705cfSriastradh	DBG(("%s: tmp (%dx%d) depth=%d\n",
689703b705cfSriastradh	     __FUNCTION__, width, height, depth));
689803b705cfSriastradh	scratch = sna_pixmap_create_upload(screen,
689903b705cfSriastradh					   width, height, depth,
690003b705cfSriastradh					   KGEM_BUFFER_WRITE);
690103b705cfSriastradh	if (!scratch)
690203b705cfSriastradh		return true;
690303b705cfSriastradh
690403b705cfSriastradh	memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
690503b705cfSriastradh	image = pixman_image_create_bits(picture->format, width, height,
690603b705cfSriastradh					 scratch->devPrivate.ptr,
690703b705cfSriastradh					 scratch->devKind);
690803b705cfSriastradh	if (image) {
690903b705cfSriastradh		pixman_add_traps (image, -extents.x1, -extents.y1,
691003b705cfSriastradh				  ntrap, (pixman_trap_t *)trap);
691103b705cfSriastradh
691203b705cfSriastradh		pixman_image_unref(image);
691303b705cfSriastradh	}
691403b705cfSriastradh
691503b705cfSriastradh	/* XXX clip boxes */
691603b705cfSriastradh	get_drawable_deltas(picture->pDrawable, pixmap, &x, &y);
691703b705cfSriastradh	sna->render.copy_boxes(sna, GXcopy,
691803b705cfSriastradh			       scratch, __sna_pixmap_get_bo(scratch), -extents.x1, -extents.x1,
691903b705cfSriastradh			       pixmap, priv->gpu_bo, x, y,
692003b705cfSriastradh			       &extents, 1, 0);
692103b705cfSriastradh	mark_damaged(pixmap, priv, &extents, x, y);
692203b705cfSriastradh
692303b705cfSriastradh	sna_pixmap_destroy(scratch);
692403b705cfSriastradh	return true;
692503b705cfSriastradh}
692603b705cfSriastradh
692703b705cfSriastradhvoid
692803b705cfSriastradhsna_add_traps(PicturePtr picture, INT16 x, INT16 y, int n, xTrap *t)
692903b705cfSriastradh{
693003b705cfSriastradh	struct sna *sna;
693103b705cfSriastradh
693203b705cfSriastradh	DBG(("%s (%d, %d) x %d\n", __FUNCTION__, x, y, n));
693303b705cfSriastradh
693403b705cfSriastradh	sna = to_sna_from_drawable(picture->pDrawable);
693503b705cfSriastradh	if (is_gpu(sna, picture->pDrawable, PREFER_GPU_SPANS)) {
693603b705cfSriastradh		if (trap_span_converter(sna, picture, x, y, n, t))
693703b705cfSriastradh			return;
693803b705cfSriastradh	}
693903b705cfSriastradh
694003b705cfSriastradh	if (is_gpu(sna, picture->pDrawable, PREFER_GPU_RENDER)) {
694103b705cfSriastradh		if (trap_mask_converter(sna, picture, x, y, n, t))
694203b705cfSriastradh			return;
694303b705cfSriastradh
694403b705cfSriastradh		if (trap_upload(picture, x, y, n, t))
694503b705cfSriastradh			return;
694603b705cfSriastradh	}
694703b705cfSriastradh
694803b705cfSriastradh	DBG(("%s -- fallback\n", __FUNCTION__));
694903b705cfSriastradh	if (sna_drawable_move_to_cpu(picture->pDrawable,
695003b705cfSriastradh				     MOVE_READ | MOVE_WRITE)) {
695103b705cfSriastradh		pixman_image_t *image;
695203b705cfSriastradh		int dx, dy;
695303b705cfSriastradh
695403b705cfSriastradh		if (!(image = image_from_pict(picture, false, &dx, &dy)))
695503b705cfSriastradh			return;
695603b705cfSriastradh
695703b705cfSriastradh		pixman_add_traps(image, x + dx, y + dy, n, (pixman_trap_t *)t);
695803b705cfSriastradh
695903b705cfSriastradh		free_pixman_pict(picture, image);
696003b705cfSriastradh	}
696103b705cfSriastradh}
696203b705cfSriastradh
696303b705cfSriastradhstatic inline void
696403b705cfSriastradhproject_point_onto_grid(const xPointFixed *in,
696503b705cfSriastradh			int dx, int dy,
696603b705cfSriastradh			xPointFixed *out)
696703b705cfSriastradh{
696803b705cfSriastradh	out->x = dx + pixman_fixed_to_grid(in->x);
696903b705cfSriastradh	out->y = dy + pixman_fixed_to_grid(in->y);
697003b705cfSriastradh}
697103b705cfSriastradh
697203b705cfSriastradh#if HAS_PIXMAN_TRIANGLES
697303b705cfSriastradhstatic inline bool
697403b705cfSriastradhxTriangleValid(const xTriangle *t)
697503b705cfSriastradh{
697603b705cfSriastradh	xPointFixed v1, v2;
697703b705cfSriastradh
697803b705cfSriastradh	v1.x = t->p2.x - t->p1.x;
697903b705cfSriastradh	v1.y = t->p2.y - t->p1.y;
698003b705cfSriastradh
698103b705cfSriastradh	v2.x = t->p3.x - t->p1.x;
698203b705cfSriastradh	v2.y = t->p3.y - t->p1.y;
698303b705cfSriastradh
698403b705cfSriastradh	/* if the length of any edge is zero, the area must be zero */
698503b705cfSriastradh	if (v1.x == 0 && v1.y == 0)
698603b705cfSriastradh		return false;
698703b705cfSriastradh	if (v2.x == 0 && v2.y == 0)
698803b705cfSriastradh		return false;
698903b705cfSriastradh
699003b705cfSriastradh	/* if the cross-product is zero, so it the size */
699103b705cfSriastradh	return v2.y * v1.x != v1.y * v2.x;
699203b705cfSriastradh}
699303b705cfSriastradh
699403b705cfSriastradhstatic inline bool
699503b705cfSriastradhproject_triangle_onto_grid(const xTriangle *in,
699603b705cfSriastradh			   int dx, int dy,
699703b705cfSriastradh			   xTriangle *out)
699803b705cfSriastradh{
699903b705cfSriastradh	project_point_onto_grid(&in->p1, dx, dy, &out->p1);
700003b705cfSriastradh	project_point_onto_grid(&in->p2, dx, dy, &out->p2);
700103b705cfSriastradh	project_point_onto_grid(&in->p3, dx, dy, &out->p3);
700203b705cfSriastradh
700303b705cfSriastradh	return xTriangleValid(out);
700403b705cfSriastradh}
700503b705cfSriastradh
700603b705cfSriastradhstatic bool
700703b705cfSriastradhmono_triangles_span_converter(struct sna *sna,
700803b705cfSriastradh			      CARD8 op, PicturePtr src, PicturePtr dst,
700903b705cfSriastradh			      INT16 src_x, INT16 src_y,
701003b705cfSriastradh			      int count, xTriangle *tri)
701103b705cfSriastradh{
701203b705cfSriastradh	struct mono mono;
701303b705cfSriastradh	BoxRec extents;
701403b705cfSriastradh	int16_t dst_x, dst_y;
701503b705cfSriastradh	int16_t dx, dy;
701603b705cfSriastradh	bool was_clear;
701703b705cfSriastradh	int n;
701803b705cfSriastradh
701903b705cfSriastradh	mono.sna = sna;
702003b705cfSriastradh
702103b705cfSriastradh	dst_x = pixman_fixed_to_int(tri[0].p1.x);
702203b705cfSriastradh	dst_y = pixman_fixed_to_int(tri[0].p1.y);
702303b705cfSriastradh
702403b705cfSriastradh	miTriangleBounds(count, tri, &extents);
702503b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
702603b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
702703b705cfSriastradh
702803b705cfSriastradh	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
702903b705cfSriastradh		return true;
703003b705cfSriastradh
703103b705cfSriastradh	if (!sna_compute_composite_region(&mono.clip,
703203b705cfSriastradh					  src, NULL, dst,
703303b705cfSriastradh					  src_x + extents.x1 - dst_x,
703403b705cfSriastradh					  src_y + extents.y1 - dst_y,
703503b705cfSriastradh					  0, 0,
703603b705cfSriastradh					  extents.x1, extents.y1,
703703b705cfSriastradh					  extents.x2 - extents.x1,
703803b705cfSriastradh					  extents.y2 - extents.y1)) {
703903b705cfSriastradh		DBG(("%s: triangles do not intersect drawable clips\n",
704003b705cfSriastradh		     __FUNCTION__)) ;
704103b705cfSriastradh		return true;
704203b705cfSriastradh	}
704303b705cfSriastradh
704403b705cfSriastradh	dx = dst->pDrawable->x;
704503b705cfSriastradh	dy = dst->pDrawable->y;
704603b705cfSriastradh
704703b705cfSriastradh	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
704803b705cfSriastradh	     __FUNCTION__,
704903b705cfSriastradh	     mono.clip.extents.x1, mono.clip.extents.y1,
705003b705cfSriastradh	     mono.clip.extents.x2, mono.clip.extents.y2,
705103b705cfSriastradh	     dx, dy,
705203b705cfSriastradh	     src_x + mono.clip.extents.x1 - dst_x - dx,
705303b705cfSriastradh	     src_y + mono.clip.extents.y1 - dst_y - dy));
705403b705cfSriastradh
705503b705cfSriastradh	was_clear = sna_drawable_is_clear(dst->pDrawable);
705603b705cfSriastradh
705703b705cfSriastradh	if (mono_init(&mono, 3*count))
705803b705cfSriastradh		return false;
705903b705cfSriastradh
706003b705cfSriastradh	for (n = 0; n < count; n++) {
706103b705cfSriastradh		mono_add_line(&mono, dx, dy,
706203b705cfSriastradh			      tri[n].p1.y, tri[n].p2.y,
706303b705cfSriastradh			      &tri[n].p1, &tri[n].p2, 1);
706403b705cfSriastradh		mono_add_line(&mono, dx, dy,
706503b705cfSriastradh			      tri[n].p2.y, tri[n].p3.y,
706603b705cfSriastradh			      &tri[n].p2, &tri[n].p3, 1);
706703b705cfSriastradh		mono_add_line(&mono, dx, dy,
706803b705cfSriastradh			      tri[n].p3.y, tri[n].p1.y,
706903b705cfSriastradh			      &tri[n].p3, &tri[n].p1, 1);
707003b705cfSriastradh	}
707103b705cfSriastradh
707203b705cfSriastradh	memset(&mono.op, 0, sizeof(mono.op));
707303b705cfSriastradh	if (mono.sna->render.composite(mono.sna, op, src, NULL, dst,
707403b705cfSriastradh				       src_x + mono.clip.extents.x1 - dst_x - dx,
707503b705cfSriastradh				       src_y + mono.clip.extents.y1 - dst_y - dy,
707603b705cfSriastradh				       0, 0,
707703b705cfSriastradh				       mono.clip.extents.x1,  mono.clip.extents.y1,
707803b705cfSriastradh				       mono.clip.extents.x2 - mono.clip.extents.x1,
707903b705cfSriastradh				       mono.clip.extents.y2 - mono.clip.extents.y1,
708003b705cfSriastradh				       &mono.op)) {
708103b705cfSriastradh		if (mono.clip.data == NULL && mono.op.damage == NULL)
708203b705cfSriastradh			mono.span = mono_span__fast;
708303b705cfSriastradh		else
708403b705cfSriastradh			mono.span = mono_span;
708503b705cfSriastradh		mono_render(&mono);
708603b705cfSriastradh		mono.op.done(mono.sna, &mono.op);
708703b705cfSriastradh	}
708803b705cfSriastradh
708903b705cfSriastradh	if (!was_clear && !operator_is_bounded(op)) {
709003b705cfSriastradh		xPointFixed p1, p2;
709103b705cfSriastradh
709203b705cfSriastradh		if (!mono_init(&mono, 2+3*count))
709303b705cfSriastradh			return false;
709403b705cfSriastradh
709503b705cfSriastradh		p1.y = mono.clip.extents.y1 * pixman_fixed_1;
709603b705cfSriastradh		p2.y = mono.clip.extents.y2 * pixman_fixed_1;
709703b705cfSriastradh
709803b705cfSriastradh		p1.x = mono.clip.extents.x1 * pixman_fixed_1;
709903b705cfSriastradh		p2.x = mono.clip.extents.x1 * pixman_fixed_1;
710003b705cfSriastradh		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, -1);
710103b705cfSriastradh
710203b705cfSriastradh		p1.x = mono.clip.extents.x2 * pixman_fixed_1;
710303b705cfSriastradh		p2.x = mono.clip.extents.x2 * pixman_fixed_1;
710403b705cfSriastradh		mono_add_line(&mono, 0, 0, p1.y, p2.y, &p1, &p2, 1);
710503b705cfSriastradh
710603b705cfSriastradh		for (n = 0; n < count; n++) {
710703b705cfSriastradh			mono_add_line(&mono, dx, dy,
710803b705cfSriastradh				      tri[n].p1.y, tri[n].p2.y,
710903b705cfSriastradh				      &tri[n].p1, &tri[n].p2, 1);
711003b705cfSriastradh			mono_add_line(&mono, dx, dy,
711103b705cfSriastradh				      tri[n].p2.y, tri[n].p3.y,
711203b705cfSriastradh				      &tri[n].p2, &tri[n].p3, 1);
711303b705cfSriastradh			mono_add_line(&mono, dx, dy,
711403b705cfSriastradh				      tri[n].p3.y, tri[n].p1.y,
711503b705cfSriastradh				      &tri[n].p3, &tri[n].p1, 1);
711603b705cfSriastradh		}
711703b705cfSriastradh
711803b705cfSriastradh		memset(&mono.op, 0, sizeof(mono.op));
711903b705cfSriastradh		if (mono.sna->render.composite(mono.sna,
712003b705cfSriastradh					       PictOpClear,
712103b705cfSriastradh					       mono.sna->clear, NULL, dst,
712203b705cfSriastradh					       0, 0,
712303b705cfSriastradh					       0, 0,
712403b705cfSriastradh					       mono.clip.extents.x1,  mono.clip.extents.y1,
712503b705cfSriastradh					       mono.clip.extents.x2 - mono.clip.extents.x1,
712603b705cfSriastradh					       mono.clip.extents.y2 - mono.clip.extents.y1,
712703b705cfSriastradh					       &mono.op)) {
712803b705cfSriastradh			if (mono.clip.data == NULL && mono.op.damage == NULL)
712903b705cfSriastradh				mono.span = mono_span__fast;
713003b705cfSriastradh			else
713103b705cfSriastradh				mono.span = mono_span;
713203b705cfSriastradh			mono_render(&mono);
713303b705cfSriastradh			mono.op.done(mono.sna, &mono.op);
713403b705cfSriastradh		}
713503b705cfSriastradh		mono_fini(&mono);
713603b705cfSriastradh	}
713703b705cfSriastradh
713803b705cfSriastradh	mono_fini(&mono);
713903b705cfSriastradh	REGION_UNINIT(NULL, &mono.clip);
714003b705cfSriastradh	return true;
714103b705cfSriastradh}
714203b705cfSriastradh
714303b705cfSriastradhstatic bool
714403b705cfSriastradhtriangles_span_converter(struct sna *sna,
714503b705cfSriastradh			 CARD8 op, PicturePtr src, PicturePtr dst,
714603b705cfSriastradh			 PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
714703b705cfSriastradh			 int count, xTriangle *tri)
714803b705cfSriastradh{
714903b705cfSriastradh	struct sna_composite_spans_op tmp;
715003b705cfSriastradh	struct tor tor;
715103b705cfSriastradh	BoxRec extents;
715203b705cfSriastradh	pixman_region16_t clip;
715303b705cfSriastradh	int16_t dst_x, dst_y;
715403b705cfSriastradh	int dx, dy, n;
715503b705cfSriastradh	bool was_clear;
715603b705cfSriastradh
715703b705cfSriastradh	if (NO_SCAN_CONVERTER)
715803b705cfSriastradh		return false;
715903b705cfSriastradh
716003b705cfSriastradh	if (is_mono(dst, maskFormat))
716103b705cfSriastradh		return mono_triangles_span_converter(sna, op, src, dst,
716203b705cfSriastradh						     src_x, src_y,
716303b705cfSriastradh						     count, tri);
716403b705cfSriastradh
716503b705cfSriastradh	/* XXX strict adherence to the Render specification */
716603b705cfSriastradh	if (dst->polyMode == PolyModePrecise) {
716703b705cfSriastradh		DBG(("%s: fallback -- precise rasterisation requested\n",
716803b705cfSriastradh		     __FUNCTION__));
716903b705cfSriastradh		return false;
717003b705cfSriastradh	}
717103b705cfSriastradh
717203b705cfSriastradh	if (!sna->render.check_composite_spans(sna, op, src, dst, 0, 0, 0)) {
717303b705cfSriastradh		DBG(("%s: fallback -- composite spans not supported\n",
717403b705cfSriastradh		     __FUNCTION__));
717503b705cfSriastradh		return false;
717603b705cfSriastradh	}
717703b705cfSriastradh
717803b705cfSriastradh	dst_x = pixman_fixed_to_int(tri[0].p1.x);
717903b705cfSriastradh	dst_y = pixman_fixed_to_int(tri[0].p1.y);
718003b705cfSriastradh
718103b705cfSriastradh	miTriangleBounds(count, tri, &extents);
718203b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
718303b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
718403b705cfSriastradh
718503b705cfSriastradh	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
718603b705cfSriastradh		return true;
718703b705cfSriastradh
718803b705cfSriastradh#if 0
718903b705cfSriastradh	if (extents.y2 - extents.y1 < 64 && extents.x2 - extents.x1 < 64) {
719003b705cfSriastradh		DBG(("%s: fallback -- traps extents too small %dx%d\n",
719103b705cfSriastradh		     __FUNCTION__, extents.y2 - extents.y1, extents.x2 - extents.x1));
719203b705cfSriastradh		return false;
719303b705cfSriastradh	}
719403b705cfSriastradh#endif
719503b705cfSriastradh
719603b705cfSriastradh	if (!sna_compute_composite_region(&clip,
719703b705cfSriastradh					  src, NULL, dst,
719803b705cfSriastradh					  src_x + extents.x1 - dst_x,
719903b705cfSriastradh					  src_y + extents.y1 - dst_y,
720003b705cfSriastradh					  0, 0,
720103b705cfSriastradh					  extents.x1, extents.y1,
720203b705cfSriastradh					  extents.x2 - extents.x1,
720303b705cfSriastradh					  extents.y2 - extents.y1)) {
720403b705cfSriastradh		DBG(("%s: triangles do not intersect drawable clips\n",
720503b705cfSriastradh		     __FUNCTION__)) ;
720603b705cfSriastradh		return true;
720703b705cfSriastradh	}
720803b705cfSriastradh
720903b705cfSriastradh	if (!sna->render.check_composite_spans(sna, op, src, dst,
721003b705cfSriastradh					       clip.extents.x2 - clip.extents.x1,
721103b705cfSriastradh					       clip.extents.y2 - clip.extents.y1,
721203b705cfSriastradh					       0)) {
721303b705cfSriastradh		DBG(("%s: fallback -- composite spans not supported\n",
721403b705cfSriastradh		     __FUNCTION__));
721503b705cfSriastradh		return false;
721603b705cfSriastradh	}
721703b705cfSriastradh
721803b705cfSriastradh	extents = *RegionExtents(&clip);
721903b705cfSriastradh	dx = dst->pDrawable->x;
722003b705cfSriastradh	dy = dst->pDrawable->y;
722103b705cfSriastradh
722203b705cfSriastradh	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
722303b705cfSriastradh	     __FUNCTION__,
722403b705cfSriastradh	     extents.x1, extents.y1,
722503b705cfSriastradh	     extents.x2, extents.y2,
722603b705cfSriastradh	     dx, dy,
722703b705cfSriastradh	     src_x + extents.x1 - dst_x - dx,
722803b705cfSriastradh	     src_y + extents.y1 - dst_y - dy));
722903b705cfSriastradh
723003b705cfSriastradh	was_clear = sna_drawable_is_clear(dst->pDrawable);
723103b705cfSriastradh
723203b705cfSriastradh	memset(&tmp, 0, sizeof(tmp));
723303b705cfSriastradh	if (!sna->render.composite_spans(sna, op, src, dst,
723403b705cfSriastradh					 src_x + extents.x1 - dst_x - dx,
723503b705cfSriastradh					 src_y + extents.y1 - dst_y - dy,
723603b705cfSriastradh					 extents.x1,  extents.y1,
723703b705cfSriastradh					 extents.x2 - extents.x1,
723803b705cfSriastradh					 extents.y2 - extents.y1,
723903b705cfSriastradh					 0,
724003b705cfSriastradh					 &tmp)) {
724103b705cfSriastradh		DBG(("%s: fallback -- composite spans render op not supported\n",
724203b705cfSriastradh		     __FUNCTION__));
724303b705cfSriastradh		return false;
724403b705cfSriastradh	}
724503b705cfSriastradh
724603b705cfSriastradh	dx *= FAST_SAMPLES_X;
724703b705cfSriastradh	dy *= FAST_SAMPLES_Y;
724803b705cfSriastradh	if (!tor_init(&tor, &extents, 3*count))
724903b705cfSriastradh		goto skip;
725003b705cfSriastradh
725103b705cfSriastradh	for (n = 0; n < count; n++) {
725203b705cfSriastradh		xTriangle t;
725303b705cfSriastradh
725403b705cfSriastradh		if (!project_triangle_onto_grid(&tri[n], dx, dy, &t))
725503b705cfSriastradh			continue;
725603b705cfSriastradh
725703b705cfSriastradh		polygon_add_line(tor.polygon, &t.p1, &t.p2);
725803b705cfSriastradh		polygon_add_line(tor.polygon, &t.p2, &t.p3);
725903b705cfSriastradh		polygon_add_line(tor.polygon, &t.p3, &t.p1);
726003b705cfSriastradh	}
726103b705cfSriastradh
726203b705cfSriastradh	tor_render(sna, &tor, &tmp, &clip,
726303b705cfSriastradh		   choose_span(&tmp, dst, maskFormat, &clip),
726403b705cfSriastradh		   !was_clear && maskFormat && !operator_is_bounded(op));
726503b705cfSriastradh
726603b705cfSriastradh	tor_fini(&tor);
726703b705cfSriastradhskip:
726803b705cfSriastradh	tmp.done(sna, &tmp);
726903b705cfSriastradh
727003b705cfSriastradh	REGION_UNINIT(NULL, &clip);
727103b705cfSriastradh	return true;
727203b705cfSriastradh}
727303b705cfSriastradh
727403b705cfSriastradhstatic bool
727503b705cfSriastradhtriangles_mask_converter(CARD8 op, PicturePtr src, PicturePtr dst,
727603b705cfSriastradh			 PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
727703b705cfSriastradh			 int count, xTriangle *tri)
727803b705cfSriastradh{
727903b705cfSriastradh	struct tor tor;
728003b705cfSriastradh	void (*span)(struct sna *sna,
728103b705cfSriastradh		     struct sna_composite_spans_op *op,
728203b705cfSriastradh		     pixman_region16_t *clip,
728303b705cfSriastradh		     const BoxRec *box,
728403b705cfSriastradh		     int coverage);
728503b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
728603b705cfSriastradh	PixmapPtr scratch;
728703b705cfSriastradh	PicturePtr mask;
728803b705cfSriastradh	BoxRec extents;
728903b705cfSriastradh	int16_t dst_x, dst_y;
729003b705cfSriastradh	int dx, dy;
729103b705cfSriastradh	int error, n;
729203b705cfSriastradh
729303b705cfSriastradh	if (NO_SCAN_CONVERTER)
729403b705cfSriastradh		return false;
729503b705cfSriastradh
729603b705cfSriastradh	if (dst->polyMode == PolyModePrecise && !is_mono(dst, maskFormat)) {
729703b705cfSriastradh		DBG(("%s: fallback -- precise rasterisation requested\n",
729803b705cfSriastradh		     __FUNCTION__));
729903b705cfSriastradh		return false;
730003b705cfSriastradh	}
730103b705cfSriastradh
730203b705cfSriastradh	if (maskFormat == NULL && count > 1) {
730303b705cfSriastradh		DBG(("%s: fallback -- individual rasterisation requested\n",
730403b705cfSriastradh		     __FUNCTION__));
730503b705cfSriastradh		return false;
730603b705cfSriastradh	}
730703b705cfSriastradh
730803b705cfSriastradh	miTriangleBounds(count, tri, &extents);
730903b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
731003b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
731103b705cfSriastradh
731203b705cfSriastradh	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
731303b705cfSriastradh		return true;
731403b705cfSriastradh
731503b705cfSriastradh	if (!sna_compute_composite_extents(&extents,
731603b705cfSriastradh					   src, NULL, dst,
731703b705cfSriastradh					   src_x, src_y,
731803b705cfSriastradh					   0, 0,
731903b705cfSriastradh					   extents.x1, extents.y1,
732003b705cfSriastradh					   extents.x2 - extents.x1,
732103b705cfSriastradh					   extents.y2 - extents.y1))
732203b705cfSriastradh		return true;
732303b705cfSriastradh
732403b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
732503b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
732603b705cfSriastradh
732703b705cfSriastradh	extents.y2 -= extents.y1;
732803b705cfSriastradh	extents.x2 -= extents.x1;
732903b705cfSriastradh	extents.x1 -= dst->pDrawable->x;
733003b705cfSriastradh	extents.y1 -= dst->pDrawable->y;
733103b705cfSriastradh	dst_x = extents.x1;
733203b705cfSriastradh	dst_y = extents.y1;
733303b705cfSriastradh	dx = -extents.x1 * FAST_SAMPLES_X;
733403b705cfSriastradh	dy = -extents.y1 * FAST_SAMPLES_Y;
733503b705cfSriastradh	extents.x1 = extents.y1 = 0;
733603b705cfSriastradh
733703b705cfSriastradh	DBG(("%s: mask (%dx%d)\n",
733803b705cfSriastradh	     __FUNCTION__, extents.x2, extents.y2));
733903b705cfSriastradh	scratch = sna_pixmap_create_upload(screen,
734003b705cfSriastradh					   extents.x2, extents.y2, 8,
734103b705cfSriastradh					   KGEM_BUFFER_WRITE_INPLACE);
734203b705cfSriastradh	if (!scratch)
734303b705cfSriastradh		return true;
734403b705cfSriastradh
734503b705cfSriastradh	DBG(("%s: created buffer %p, stride %d\n",
734603b705cfSriastradh	     __FUNCTION__, scratch->devPrivate.ptr, scratch->devKind));
734703b705cfSriastradh
734803b705cfSriastradh	if (!tor_init(&tor, &extents, 3*count)) {
734903b705cfSriastradh		sna_pixmap_destroy(scratch);
735003b705cfSriastradh		return true;
735103b705cfSriastradh	}
735203b705cfSriastradh
735303b705cfSriastradh	for (n = 0; n < count; n++) {
735403b705cfSriastradh		xTriangle t;
735503b705cfSriastradh
735603b705cfSriastradh		if (!project_triangle_onto_grid(&tri[n], dx, dy, &t))
735703b705cfSriastradh			continue;
735803b705cfSriastradh
735903b705cfSriastradh		polygon_add_line(tor.polygon, &t.p1, &t.p2);
736003b705cfSriastradh		polygon_add_line(tor.polygon, &t.p2, &t.p3);
736103b705cfSriastradh		polygon_add_line(tor.polygon, &t.p3, &t.p1);
736203b705cfSriastradh	}
736303b705cfSriastradh
736403b705cfSriastradh	if (maskFormat ? maskFormat->depth < 8 : dst->polyEdge == PolyEdgeSharp)
736503b705cfSriastradh		span = tor_blt_mask_mono;
736603b705cfSriastradh	else
736703b705cfSriastradh		span = tor_blt_mask;
736803b705cfSriastradh
736903b705cfSriastradh	tor_render(NULL, &tor,
737003b705cfSriastradh		   scratch->devPrivate.ptr,
737103b705cfSriastradh		   (void *)(intptr_t)scratch->devKind,
737203b705cfSriastradh		   span, true);
737303b705cfSriastradh
737403b705cfSriastradh	mask = CreatePicture(0, &scratch->drawable,
737503b705cfSriastradh			     PictureMatchFormat(screen, 8, PICT_a8),
737603b705cfSriastradh			     0, 0, serverClient, &error);
737703b705cfSriastradh	if (mask) {
737803b705cfSriastradh		CompositePicture(op, src, mask, dst,
737903b705cfSriastradh				 src_x + dst_x - pixman_fixed_to_int(tri[0].p1.x),
738003b705cfSriastradh				 src_y + dst_y - pixman_fixed_to_int(tri[0].p1.y),
738103b705cfSriastradh				 0, 0,
738203b705cfSriastradh				 dst_x, dst_y,
738303b705cfSriastradh				 extents.x2, extents.y2);
738403b705cfSriastradh		FreePicture(mask, 0);
738503b705cfSriastradh	}
738603b705cfSriastradh	tor_fini(&tor);
738703b705cfSriastradh	sna_pixmap_destroy(scratch);
738803b705cfSriastradh
738903b705cfSriastradh	return true;
739003b705cfSriastradh}
739103b705cfSriastradh
739203b705cfSriastradhstatic void
739303b705cfSriastradhtriangles_fallback(CARD8 op,
739403b705cfSriastradh		   PicturePtr src,
739503b705cfSriastradh		   PicturePtr dst,
739603b705cfSriastradh		   PictFormatPtr maskFormat,
739703b705cfSriastradh		   INT16 xSrc, INT16 ySrc,
739803b705cfSriastradh		   int n, xTriangle *tri)
739903b705cfSriastradh{
740003b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
740103b705cfSriastradh
740203b705cfSriastradh	DBG(("%s op=%d, count=%d\n", __FUNCTION__, op, n));
740303b705cfSriastradh
740403b705cfSriastradh	if (maskFormat) {
740503b705cfSriastradh		PixmapPtr scratch;
740603b705cfSriastradh		PicturePtr mask;
740703b705cfSriastradh		INT16 dst_x, dst_y;
740803b705cfSriastradh		BoxRec bounds;
740903b705cfSriastradh		int width, height, depth;
741003b705cfSriastradh		pixman_image_t *image;
741103b705cfSriastradh		pixman_format_code_t format;
741203b705cfSriastradh		int error;
741303b705cfSriastradh
741403b705cfSriastradh		dst_x = pixman_fixed_to_int(tri[0].p1.x);
741503b705cfSriastradh		dst_y = pixman_fixed_to_int(tri[0].p1.y);
741603b705cfSriastradh
741703b705cfSriastradh		miTriangleBounds(n, tri, &bounds);
741803b705cfSriastradh		DBG(("%s: bounds (%d, %d), (%d, %d)\n",
741903b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
742003b705cfSriastradh
742103b705cfSriastradh		if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2)
742203b705cfSriastradh			return;
742303b705cfSriastradh
742403b705cfSriastradh		if (!sna_compute_composite_extents(&bounds,
742503b705cfSriastradh						   src, NULL, dst,
742603b705cfSriastradh						   xSrc, ySrc,
742703b705cfSriastradh						   0, 0,
742803b705cfSriastradh						   bounds.x1, bounds.y1,
742903b705cfSriastradh						   bounds.x2 - bounds.x1,
743003b705cfSriastradh						   bounds.y2 - bounds.y1))
743103b705cfSriastradh			return;
743203b705cfSriastradh
743303b705cfSriastradh		DBG(("%s: extents (%d, %d), (%d, %d)\n",
743403b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
743503b705cfSriastradh
743603b705cfSriastradh		width  = bounds.x2 - bounds.x1;
743703b705cfSriastradh		height = bounds.y2 - bounds.y1;
743803b705cfSriastradh		bounds.x1 -= dst->pDrawable->x;
743903b705cfSriastradh		bounds.y1 -= dst->pDrawable->y;
744003b705cfSriastradh		depth = maskFormat->depth;
744103b705cfSriastradh		format = maskFormat->format | (BitsPerPixel(depth) << 24);
744203b705cfSriastradh
744303b705cfSriastradh		DBG(("%s: mask (%dx%d) depth=%d, format=%08x\n",
744403b705cfSriastradh		     __FUNCTION__, width, height, depth, format));
744503b705cfSriastradh		scratch = sna_pixmap_create_upload(screen,
744603b705cfSriastradh						   width, height, depth,
744703b705cfSriastradh						   KGEM_BUFFER_WRITE);
744803b705cfSriastradh		if (!scratch)
744903b705cfSriastradh			return;
745003b705cfSriastradh
745103b705cfSriastradh		memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
745203b705cfSriastradh		image = pixman_image_create_bits(format, width, height,
745303b705cfSriastradh						 scratch->devPrivate.ptr,
745403b705cfSriastradh						 scratch->devKind);
745503b705cfSriastradh		if (image) {
745603b705cfSriastradh			pixman_add_triangles(image,
745703b705cfSriastradh					     -bounds.x1, -bounds.y1,
745803b705cfSriastradh					     n, (pixman_triangle_t *)tri);
745903b705cfSriastradh			pixman_image_unref(image);
746003b705cfSriastradh		}
746103b705cfSriastradh
746203b705cfSriastradh		mask = CreatePicture(0, &scratch->drawable,
746303b705cfSriastradh				     PictureMatchFormat(screen, depth, format),
746403b705cfSriastradh				     0, 0, serverClient, &error);
746503b705cfSriastradh		if (mask) {
746603b705cfSriastradh			CompositePicture(op, src, mask, dst,
746703b705cfSriastradh					 xSrc + bounds.x1 - dst_x,
746803b705cfSriastradh					 ySrc + bounds.y1 - dst_y,
746903b705cfSriastradh					 0, 0,
747003b705cfSriastradh					 bounds.x1, bounds.y1,
747103b705cfSriastradh					 width, height);
747203b705cfSriastradh			FreePicture(mask, 0);
747303b705cfSriastradh		}
747403b705cfSriastradh		sna_pixmap_destroy(scratch);
747503b705cfSriastradh	} else {
747603b705cfSriastradh		if (dst->polyEdge == PolyEdgeSharp)
747703b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 1, PICT_a1);
747803b705cfSriastradh		else
747903b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 8, PICT_a8);
748003b705cfSriastradh
748103b705cfSriastradh		for (; n--; tri++)
748203b705cfSriastradh			triangles_fallback(op,
748303b705cfSriastradh					   src, dst, maskFormat,
748403b705cfSriastradh					   xSrc, ySrc, 1, tri);
748503b705cfSriastradh	}
748603b705cfSriastradh}
748703b705cfSriastradh
748803b705cfSriastradhvoid
748903b705cfSriastradhsna_composite_triangles(CARD8 op,
749003b705cfSriastradh			 PicturePtr src,
749103b705cfSriastradh			 PicturePtr dst,
749203b705cfSriastradh			 PictFormatPtr maskFormat,
749303b705cfSriastradh			 INT16 xSrc, INT16 ySrc,
749403b705cfSriastradh			 int n, xTriangle *tri)
749503b705cfSriastradh{
749603b705cfSriastradh	struct sna *sna = to_sna_from_drawable(dst->pDrawable);
749703b705cfSriastradh
749803b705cfSriastradh	if (triangles_span_converter(sna, op, src, dst, maskFormat,
749903b705cfSriastradh				     xSrc, ySrc,
750003b705cfSriastradh				     n, tri))
750103b705cfSriastradh		return;
750203b705cfSriastradh
750303b705cfSriastradh	if (triangles_mask_converter(op, src, dst, maskFormat,
750403b705cfSriastradh				     xSrc, ySrc,
750503b705cfSriastradh				     n, tri))
750603b705cfSriastradh		return;
750703b705cfSriastradh
750803b705cfSriastradh	triangles_fallback(op, src, dst, maskFormat, xSrc, ySrc, n, tri);
750903b705cfSriastradh}
751003b705cfSriastradh
751103b705cfSriastradhstatic bool
751203b705cfSriastradhtristrip_span_converter(struct sna *sna,
751303b705cfSriastradh			CARD8 op, PicturePtr src, PicturePtr dst,
751403b705cfSriastradh			PictFormatPtr maskFormat, INT16 src_x, INT16 src_y,
751503b705cfSriastradh			int count, xPointFixed *points)
751603b705cfSriastradh{
751703b705cfSriastradh	struct sna_composite_spans_op tmp;
751803b705cfSriastradh	struct tor tor;
751903b705cfSriastradh	BoxRec extents;
752003b705cfSriastradh	pixman_region16_t clip;
752103b705cfSriastradh	xPointFixed p[4];
752203b705cfSriastradh	int16_t dst_x, dst_y;
752303b705cfSriastradh	int dx, dy;
752403b705cfSriastradh	int cw, ccw, n;
752503b705cfSriastradh	bool was_clear;
752603b705cfSriastradh
752703b705cfSriastradh	if (NO_SCAN_CONVERTER)
752803b705cfSriastradh		return false;
752903b705cfSriastradh
753003b705cfSriastradh	/* XXX strict adherence to the Render specification */
753103b705cfSriastradh	if (dst->polyMode == PolyModePrecise && !is_mono(dst, maskFormat)) {
753203b705cfSriastradh		DBG(("%s: fallback -- precise rasterisation requested\n",
753303b705cfSriastradh		     __FUNCTION__));
753403b705cfSriastradh		return false;
753503b705cfSriastradh	}
753603b705cfSriastradh
753703b705cfSriastradh	if (!sna->render.check_composite_spans(sna, op, src, dst, 0, 0, 0)) {
753803b705cfSriastradh		DBG(("%s: fallback -- composite spans not supported\n",
753903b705cfSriastradh		     __FUNCTION__));
754003b705cfSriastradh		return false;
754103b705cfSriastradh	}
754203b705cfSriastradh
754303b705cfSriastradh	dst_x = pixman_fixed_to_int(points[0].x);
754403b705cfSriastradh	dst_y = pixman_fixed_to_int(points[0].y);
754503b705cfSriastradh
754603b705cfSriastradh	miPointFixedBounds(count, points, &extents);
754703b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
754803b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
754903b705cfSriastradh
755003b705cfSriastradh	if (extents.y1 >= extents.y2 || extents.x1 >= extents.x2)
755103b705cfSriastradh		return true;
755203b705cfSriastradh
755303b705cfSriastradh#if 0
755403b705cfSriastradh	if (extents.y2 - extents.y1 < 64 && extents.x2 - extents.x1 < 64) {
755503b705cfSriastradh		DBG(("%s: fallback -- traps extents too small %dx%d\n",
755603b705cfSriastradh		     __FUNCTION__, extents.y2 - extents.y1, extents.x2 - extents.x1));
755703b705cfSriastradh		return false;
755803b705cfSriastradh	}
755903b705cfSriastradh#endif
756003b705cfSriastradh
756103b705cfSriastradh	if (!sna_compute_composite_region(&clip,
756203b705cfSriastradh					  src, NULL, dst,
756303b705cfSriastradh					  src_x + extents.x1 - dst_x,
756403b705cfSriastradh					  src_y + extents.y1 - dst_y,
756503b705cfSriastradh					  0, 0,
756603b705cfSriastradh					  extents.x1, extents.y1,
756703b705cfSriastradh					  extents.x2 - extents.x1,
756803b705cfSriastradh					  extents.y2 - extents.y1)) {
756903b705cfSriastradh		DBG(("%s: triangles do not intersect drawable clips\n",
757003b705cfSriastradh		     __FUNCTION__)) ;
757103b705cfSriastradh		return true;
757203b705cfSriastradh	}
757303b705cfSriastradh
757403b705cfSriastradh	if (!sna->render.check_composite_spans(sna, op, src, dst,
757503b705cfSriastradh					       clip.extents.x2 - clip.extents.x1,
757603b705cfSriastradh					       clip.extents.y2 - clip.extents.y1,
757703b705cfSriastradh					       0)) {
757803b705cfSriastradh		DBG(("%s: fallback -- composite spans not supported\n",
757903b705cfSriastradh		     __FUNCTION__));
758003b705cfSriastradh		return false;
758103b705cfSriastradh	}
758203b705cfSriastradh
758303b705cfSriastradh	extents = *RegionExtents(&clip);
758403b705cfSriastradh	dx = dst->pDrawable->x;
758503b705cfSriastradh	dy = dst->pDrawable->y;
758603b705cfSriastradh
758703b705cfSriastradh	DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n",
758803b705cfSriastradh	     __FUNCTION__,
758903b705cfSriastradh	     extents.x1, extents.y1,
759003b705cfSriastradh	     extents.x2, extents.y2,
759103b705cfSriastradh	     dx, dy,
759203b705cfSriastradh	     src_x + extents.x1 - dst_x - dx,
759303b705cfSriastradh	     src_y + extents.y1 - dst_y - dy));
759403b705cfSriastradh
759503b705cfSriastradh	was_clear = sna_drawable_is_clear(dst->pDrawable);
759603b705cfSriastradh
759703b705cfSriastradh	memset(&tmp, 0, sizeof(tmp));
759803b705cfSriastradh	if (!sna->render.composite_spans(sna, op, src, dst,
759903b705cfSriastradh					 src_x + extents.x1 - dst_x - dx,
760003b705cfSriastradh					 src_y + extents.y1 - dst_y - dy,
760103b705cfSriastradh					 extents.x1,  extents.y1,
760203b705cfSriastradh					 extents.x2 - extents.x1,
760303b705cfSriastradh					 extents.y2 - extents.y1,
760403b705cfSriastradh					 0,
760503b705cfSriastradh					 &tmp)) {
760603b705cfSriastradh		DBG(("%s: fallback -- composite spans render op not supported\n",
760703b705cfSriastradh		     __FUNCTION__));
760803b705cfSriastradh		return false;
760903b705cfSriastradh	}
761003b705cfSriastradh
761103b705cfSriastradh	dx *= FAST_SAMPLES_X;
761203b705cfSriastradh	dy *= FAST_SAMPLES_Y;
761303b705cfSriastradh	if (!tor_init(&tor, &extents, 2*count))
761403b705cfSriastradh		goto skip;
761503b705cfSriastradh
761603b705cfSriastradh	cw = ccw = 0;
761703b705cfSriastradh	project_point_onto_grid(&points[0], dx, dy, &p[cw]);
761803b705cfSriastradh	project_point_onto_grid(&points[1], dx, dy, &p[2+ccw]);
761903b705cfSriastradh	polygon_add_line(tor.polygon, &p[cw], &p[2+ccw]);
762003b705cfSriastradh	n = 2;
762103b705cfSriastradh	do {
762203b705cfSriastradh		cw = !cw;
762303b705cfSriastradh		project_point_onto_grid(&points[n], dx, dy, &p[cw]);
762403b705cfSriastradh		polygon_add_line(tor.polygon, &p[!cw], &p[cw]);
762503b705cfSriastradh		if (++n == count)
762603b705cfSriastradh			break;
762703b705cfSriastradh
762803b705cfSriastradh		ccw = !ccw;
762903b705cfSriastradh		project_point_onto_grid(&points[n], dx, dy, &p[2+ccw]);
763003b705cfSriastradh		polygon_add_line(tor.polygon, &p[2+ccw], &p[2+!ccw]);
763103b705cfSriastradh		if (++n == count)
763203b705cfSriastradh			break;
763303b705cfSriastradh	} while (1);
763403b705cfSriastradh	polygon_add_line(tor.polygon, &p[2+ccw], &p[cw]);
763503b705cfSriastradh	assert(tor.polygon->num_edges <= 2*count);
763603b705cfSriastradh
763703b705cfSriastradh	tor_render(sna, &tor, &tmp, &clip,
763803b705cfSriastradh		   choose_span(&tmp, dst, maskFormat, &clip),
763903b705cfSriastradh		   !was_clear && maskFormat && !operator_is_bounded(op));
764003b705cfSriastradh
764103b705cfSriastradh	tor_fini(&tor);
764203b705cfSriastradhskip:
764303b705cfSriastradh	tmp.done(sna, &tmp);
764403b705cfSriastradh
764503b705cfSriastradh	REGION_UNINIT(NULL, &clip);
764603b705cfSriastradh	return true;
764703b705cfSriastradh}
764803b705cfSriastradh
764903b705cfSriastradhstatic void
765003b705cfSriastradhtristrip_fallback(CARD8 op,
765103b705cfSriastradh		  PicturePtr src,
765203b705cfSriastradh		  PicturePtr dst,
765303b705cfSriastradh		  PictFormatPtr maskFormat,
765403b705cfSriastradh		  INT16 xSrc, INT16 ySrc,
765503b705cfSriastradh		  int n, xPointFixed *points)
765603b705cfSriastradh{
765703b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
765803b705cfSriastradh
765903b705cfSriastradh	if (maskFormat) {
766003b705cfSriastradh		PixmapPtr scratch;
766103b705cfSriastradh		PicturePtr mask;
766203b705cfSriastradh		INT16 dst_x, dst_y;
766303b705cfSriastradh		BoxRec bounds;
766403b705cfSriastradh		int width, height, depth;
766503b705cfSriastradh		pixman_image_t *image;
766603b705cfSriastradh		pixman_format_code_t format;
766703b705cfSriastradh		int error;
766803b705cfSriastradh
766903b705cfSriastradh		dst_x = pixman_fixed_to_int(points->x);
767003b705cfSriastradh		dst_y = pixman_fixed_to_int(points->y);
767103b705cfSriastradh
767203b705cfSriastradh		miPointFixedBounds(n, points, &bounds);
767303b705cfSriastradh		DBG(("%s: bounds (%d, %d), (%d, %d)\n",
767403b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
767503b705cfSriastradh
767603b705cfSriastradh		if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2)
767703b705cfSriastradh			return;
767803b705cfSriastradh
767903b705cfSriastradh		if (!sna_compute_composite_extents(&bounds,
768003b705cfSriastradh						   src, NULL, dst,
768103b705cfSriastradh						   xSrc, ySrc,
768203b705cfSriastradh						   0, 0,
768303b705cfSriastradh						   bounds.x1, bounds.y1,
768403b705cfSriastradh						   bounds.x2 - bounds.x1,
768503b705cfSriastradh						   bounds.y2 - bounds.y1))
768603b705cfSriastradh			return;
768703b705cfSriastradh
768803b705cfSriastradh		DBG(("%s: extents (%d, %d), (%d, %d)\n",
768903b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
769003b705cfSriastradh
769103b705cfSriastradh		width  = bounds.x2 - bounds.x1;
769203b705cfSriastradh		height = bounds.y2 - bounds.y1;
769303b705cfSriastradh		bounds.x1 -= dst->pDrawable->x;
769403b705cfSriastradh		bounds.y1 -= dst->pDrawable->y;
769503b705cfSriastradh		depth = maskFormat->depth;
769603b705cfSriastradh		format = maskFormat->format | (BitsPerPixel(depth) << 24);
769703b705cfSriastradh
769803b705cfSriastradh		DBG(("%s: mask (%dx%d) depth=%d, format=%08x\n",
769903b705cfSriastradh		     __FUNCTION__, width, height, depth, format));
770003b705cfSriastradh		scratch = sna_pixmap_create_upload(screen,
770103b705cfSriastradh						   width, height, depth,
770203b705cfSriastradh						   KGEM_BUFFER_WRITE);
770303b705cfSriastradh		if (!scratch)
770403b705cfSriastradh			return;
770503b705cfSriastradh
770603b705cfSriastradh		memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
770703b705cfSriastradh		image = pixman_image_create_bits(format, width, height,
770803b705cfSriastradh						 scratch->devPrivate.ptr,
770903b705cfSriastradh						 scratch->devKind);
771003b705cfSriastradh		if (image) {
771103b705cfSriastradh			xTriangle tri;
771203b705cfSriastradh			xPointFixed *p[3] = { &tri.p1, &tri.p2, &tri.p3 };
771303b705cfSriastradh			int i;
771403b705cfSriastradh
771503b705cfSriastradh			*p[0] = points[0];
771603b705cfSriastradh			*p[1] = points[1];
771703b705cfSriastradh			*p[2] = points[2];
771803b705cfSriastradh			pixman_add_triangles(image,
771903b705cfSriastradh					     -bounds.x1, -bounds.y1,
772003b705cfSriastradh					     1, (pixman_triangle_t *)&tri);
772103b705cfSriastradh			for (i = 3; i < n; i++) {
772203b705cfSriastradh				*p[i%3] = points[i];
772303b705cfSriastradh				pixman_add_triangles(image,
772403b705cfSriastradh						     -bounds.x1, -bounds.y1,
772503b705cfSriastradh						     1, (pixman_triangle_t *)&tri);
772603b705cfSriastradh			}
772703b705cfSriastradh			pixman_image_unref(image);
772803b705cfSriastradh		}
772903b705cfSriastradh
773003b705cfSriastradh		mask = CreatePicture(0, &scratch->drawable,
773103b705cfSriastradh				     PictureMatchFormat(screen, depth, format),
773203b705cfSriastradh				     0, 0, serverClient, &error);
773303b705cfSriastradh		if (mask) {
773403b705cfSriastradh			CompositePicture(op, src, mask, dst,
773503b705cfSriastradh					 xSrc + bounds.x1 - dst_x,
773603b705cfSriastradh					 ySrc + bounds.y1 - dst_y,
773703b705cfSriastradh					 0, 0,
773803b705cfSriastradh					 bounds.x1, bounds.y1,
773903b705cfSriastradh					 width, height);
774003b705cfSriastradh			FreePicture(mask, 0);
774103b705cfSriastradh		}
774203b705cfSriastradh		sna_pixmap_destroy(scratch);
774303b705cfSriastradh	} else {
774403b705cfSriastradh		xTriangle tri;
774503b705cfSriastradh		xPointFixed *p[3] = { &tri.p1, &tri.p2, &tri.p3 };
774603b705cfSriastradh		int i;
774703b705cfSriastradh
774803b705cfSriastradh		if (dst->polyEdge == PolyEdgeSharp)
774903b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 1, PICT_a1);
775003b705cfSriastradh		else
775103b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 8, PICT_a8);
775203b705cfSriastradh
775303b705cfSriastradh		*p[0] = points[0];
775403b705cfSriastradh		*p[1] = points[1];
775503b705cfSriastradh		*p[2] = points[2];
775603b705cfSriastradh		triangles_fallback(op,
775703b705cfSriastradh				   src, dst, maskFormat,
775803b705cfSriastradh				   xSrc, ySrc, 1, &tri);
775903b705cfSriastradh		for (i = 3; i < n; i++) {
776003b705cfSriastradh			*p[i%3] = points[i];
776103b705cfSriastradh			/* Should xSrc,ySrc be updated? */
776203b705cfSriastradh			triangles_fallback(op,
776303b705cfSriastradh					   src, dst, maskFormat,
776403b705cfSriastradh					   xSrc, ySrc, 1, &tri);
776503b705cfSriastradh		}
776603b705cfSriastradh	}
776703b705cfSriastradh}
776803b705cfSriastradh
776903b705cfSriastradhvoid
777003b705cfSriastradhsna_composite_tristrip(CARD8 op,
777103b705cfSriastradh		       PicturePtr src,
777203b705cfSriastradh		       PicturePtr dst,
777303b705cfSriastradh		       PictFormatPtr maskFormat,
777403b705cfSriastradh		       INT16 xSrc, INT16 ySrc,
777503b705cfSriastradh		       int n, xPointFixed *points)
777603b705cfSriastradh{
777703b705cfSriastradh	struct sna *sna = to_sna_from_drawable(dst->pDrawable);
777803b705cfSriastradh
777903b705cfSriastradh	if (tristrip_span_converter(sna, op, src, dst, maskFormat, xSrc, ySrc, n, points))
778003b705cfSriastradh		return;
778103b705cfSriastradh
778203b705cfSriastradh	tristrip_fallback(op, src, dst, maskFormat, xSrc, ySrc, n, points);
778303b705cfSriastradh}
778403b705cfSriastradh
778503b705cfSriastradhstatic void
778603b705cfSriastradhtrifan_fallback(CARD8 op,
778703b705cfSriastradh		PicturePtr src,
778803b705cfSriastradh		PicturePtr dst,
778903b705cfSriastradh		PictFormatPtr maskFormat,
779003b705cfSriastradh		INT16 xSrc, INT16 ySrc,
779103b705cfSriastradh		int n, xPointFixed *points)
779203b705cfSriastradh{
779303b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
779403b705cfSriastradh
779503b705cfSriastradh	if (maskFormat) {
779603b705cfSriastradh		PixmapPtr scratch;
779703b705cfSriastradh		PicturePtr mask;
779803b705cfSriastradh		INT16 dst_x, dst_y;
779903b705cfSriastradh		BoxRec bounds;
780003b705cfSriastradh		int width, height, depth;
780103b705cfSriastradh		pixman_image_t *image;
780203b705cfSriastradh		pixman_format_code_t format;
780303b705cfSriastradh		int error;
780403b705cfSriastradh
780503b705cfSriastradh		dst_x = pixman_fixed_to_int(points->x);
780603b705cfSriastradh		dst_y = pixman_fixed_to_int(points->y);
780703b705cfSriastradh
780803b705cfSriastradh		miPointFixedBounds(n, points, &bounds);
780903b705cfSriastradh		DBG(("%s: bounds (%d, %d), (%d, %d)\n",
781003b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
781103b705cfSriastradh
781203b705cfSriastradh		if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2)
781303b705cfSriastradh			return;
781403b705cfSriastradh
781503b705cfSriastradh		if (!sna_compute_composite_extents(&bounds,
781603b705cfSriastradh						   src, NULL, dst,
781703b705cfSriastradh						   xSrc, ySrc,
781803b705cfSriastradh						   0, 0,
781903b705cfSriastradh						   bounds.x1, bounds.y1,
782003b705cfSriastradh						   bounds.x2 - bounds.x1,
782103b705cfSriastradh						   bounds.y2 - bounds.y1))
782203b705cfSriastradh			return;
782303b705cfSriastradh
782403b705cfSriastradh		DBG(("%s: extents (%d, %d), (%d, %d)\n",
782503b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
782603b705cfSriastradh
782703b705cfSriastradh		width  = bounds.x2 - bounds.x1;
782803b705cfSriastradh		height = bounds.y2 - bounds.y1;
782903b705cfSriastradh		bounds.x1 -= dst->pDrawable->x;
783003b705cfSriastradh		bounds.y1 -= dst->pDrawable->y;
783103b705cfSriastradh		depth = maskFormat->depth;
783203b705cfSriastradh		format = maskFormat->format | (BitsPerPixel(depth) << 24);
783303b705cfSriastradh
783403b705cfSriastradh		DBG(("%s: mask (%dx%d) depth=%d, format=%08x\n",
783503b705cfSriastradh		     __FUNCTION__, width, height, depth, format));
783603b705cfSriastradh		scratch = sna_pixmap_create_upload(screen,
783703b705cfSriastradh						   width, height, depth,
783803b705cfSriastradh						   KGEM_BUFFER_WRITE);
783903b705cfSriastradh		if (!scratch)
784003b705cfSriastradh			return;
784103b705cfSriastradh
784203b705cfSriastradh		memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
784303b705cfSriastradh		image = pixman_image_create_bits(format, width, height,
784403b705cfSriastradh						 scratch->devPrivate.ptr,
784503b705cfSriastradh						 scratch->devKind);
784603b705cfSriastradh		if (image) {
784703b705cfSriastradh			xTriangle tri;
784803b705cfSriastradh			xPointFixed *p[3] = { &tri.p1, &tri.p2, &tri.p3 };
784903b705cfSriastradh			int i;
785003b705cfSriastradh
785103b705cfSriastradh			*p[0] = points[0];
785203b705cfSriastradh			*p[1] = points[1];
785303b705cfSriastradh			*p[2] = points[2];
785403b705cfSriastradh			pixman_add_triangles(image,
785503b705cfSriastradh					     -bounds.x1, -bounds.y1,
785603b705cfSriastradh					     1, (pixman_triangle_t *)&tri);
785703b705cfSriastradh			for (i = 3; i < n; i++) {
785803b705cfSriastradh				*p[2 - (i&1)] = points[i];
785903b705cfSriastradh				pixman_add_triangles(image,
786003b705cfSriastradh						     -bounds.x1, -bounds.y1,
786103b705cfSriastradh						     1, (pixman_triangle_t *)&tri);
786203b705cfSriastradh			}
786303b705cfSriastradh			pixman_image_unref(image);
786403b705cfSriastradh		}
786503b705cfSriastradh
786603b705cfSriastradh		mask = CreatePicture(0, &scratch->drawable,
786703b705cfSriastradh				     PictureMatchFormat(screen, depth, format),
786803b705cfSriastradh				     0, 0, serverClient, &error);
786903b705cfSriastradh		if (mask) {
787003b705cfSriastradh			CompositePicture(op, src, mask, dst,
787103b705cfSriastradh					 xSrc + bounds.x1 - dst_x,
787203b705cfSriastradh					 ySrc + bounds.y1 - dst_y,
787303b705cfSriastradh					 0, 0,
787403b705cfSriastradh					 bounds.x1, bounds.y1,
787503b705cfSriastradh					 width, height);
787603b705cfSriastradh			FreePicture(mask, 0);
787703b705cfSriastradh		}
787803b705cfSriastradh		sna_pixmap_destroy(scratch);
787903b705cfSriastradh	} else {
788003b705cfSriastradh		xTriangle tri;
788103b705cfSriastradh		xPointFixed *p[3] = { &tri.p1, &tri.p2, &tri.p3 };
788203b705cfSriastradh		int i;
788303b705cfSriastradh
788403b705cfSriastradh		if (dst->polyEdge == PolyEdgeSharp)
788503b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 1, PICT_a1);
788603b705cfSriastradh		else
788703b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 8, PICT_a8);
788803b705cfSriastradh
788903b705cfSriastradh		*p[0] = points[0];
789003b705cfSriastradh		*p[1] = points[1];
789103b705cfSriastradh		*p[2] = points[2];
789203b705cfSriastradh		triangles_fallback(op,
789303b705cfSriastradh				   src, dst, maskFormat,
789403b705cfSriastradh				   xSrc, ySrc, 1, &tri);
789503b705cfSriastradh		for (i = 3; i < n; i++) {
789603b705cfSriastradh			*p[2 - (i&1)] = points[i];
789703b705cfSriastradh			/* Should xSrc,ySrc be updated? */
789803b705cfSriastradh			triangles_fallback(op,
789903b705cfSriastradh					   src, dst, maskFormat,
790003b705cfSriastradh					   xSrc, ySrc, 1, &tri);
790103b705cfSriastradh		}
790203b705cfSriastradh	}
790303b705cfSriastradh}
790403b705cfSriastradh
790503b705cfSriastradhvoid
790603b705cfSriastradhsna_composite_trifan(CARD8 op,
790703b705cfSriastradh		     PicturePtr src,
790803b705cfSriastradh		     PicturePtr dst,
790903b705cfSriastradh		     PictFormatPtr maskFormat,
791003b705cfSriastradh		     INT16 xSrc, INT16 ySrc,
791103b705cfSriastradh		     int n, xPointFixed *points)
791203b705cfSriastradh{
791303b705cfSriastradh	trifan_fallback(op, src, dst, maskFormat, xSrc, ySrc, n, points);
791403b705cfSriastradh}
791503b705cfSriastradh#endif
7916