sna_trapezoids.c revision 42542f5f
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"
3742542f5fSchristos#include "sna_trapezoids.h"
3803b705cfSriastradh#include "fb/fbpict.h"
3903b705cfSriastradh
4003b705cfSriastradh#include <mipict.h>
4103b705cfSriastradh
4203b705cfSriastradh/* TODO: Emit unantialiased and MSAA triangles. */
4303b705cfSriastradh
4403b705cfSriastradh#ifndef MAX
4503b705cfSriastradh#define MAX(x,y) ((x) >= (y) ? (x) : (y))
4603b705cfSriastradh#endif
4703b705cfSriastradh
4803b705cfSriastradh#ifndef MIN
4903b705cfSriastradh#define MIN(x,y) ((x) <= (y) ? (x) : (y))
5003b705cfSriastradh#endif
5103b705cfSriastradh
5203b705cfSriastradh#define region_count(r) ((r)->data ? (r)->data->numRects : 1)
5303b705cfSriastradh#define region_boxes(r) ((r)->data ? (BoxPtr)((r)->data + 1) : &(r)->extents)
5403b705cfSriastradh
5542542f5fSchristosinline static xFixed
5642542f5fSchristosline_x_for_y(const xLineFixed *l, xFixed y, bool ceil)
5703b705cfSriastradh{
5842542f5fSchristos	xFixed_32_32 ex = (xFixed_32_32)(y - l->p1.y) * (l->p2.x - l->p1.x);
5942542f5fSchristos	xFixed d = l->p2.y - l->p1.y;
6003b705cfSriastradh
6142542f5fSchristos	if (ceil)
6242542f5fSchristos		ex += (d - 1);
6303b705cfSriastradh
6442542f5fSchristos	return l->p1.x + (xFixed) (ex / d);
6503b705cfSriastradh}
6603b705cfSriastradh
6742542f5fSchristosbool trapezoids_bounds(int n, const xTrapezoid *t, BoxPtr box)
6803b705cfSriastradh{
6942542f5fSchristos	xFixed x1, y1, x2, y2;
7003b705cfSriastradh
7142542f5fSchristos	/* XXX need 33 bits... */
7242542f5fSchristos	x1 = y1 = INT_MAX / 2;
7342542f5fSchristos	x2 = y2 = INT_MIN / 2;
7403b705cfSriastradh
7542542f5fSchristos	do {
7642542f5fSchristos		xFixed fx1, fx2, v;
7703b705cfSriastradh
7842542f5fSchristos		if (!xTrapezoidValid(t))
7942542f5fSchristos			continue;
8003b705cfSriastradh
8142542f5fSchristos		if (t->top < y1)
8242542f5fSchristos			y1 = t->top;
8342542f5fSchristos		if (t->bottom > y2)
8442542f5fSchristos			y2 = t->bottom;
8503b705cfSriastradh
8642542f5fSchristos		if (((t->left.p1.x - x1) | (t->left.p2.x - x1)) < 0) {
8742542f5fSchristos			if (pixman_fixed_floor(t->left.p1.x) == pixman_fixed_floor(t->left.p2.x)) {
8842542f5fSchristos				x1 = pixman_fixed_floor(t->left.p1.x);
8942542f5fSchristos			} else {
9042542f5fSchristos				if (t->left.p1.y == t->top)
9142542f5fSchristos					fx1 = t->left.p1.x;
9242542f5fSchristos				else
9342542f5fSchristos					fx1 = line_x_for_y(&t->left, t->top, false);
9403b705cfSriastradh
9542542f5fSchristos				if (t->left.p2.y == t->bottom)
9642542f5fSchristos					fx2 = t->left.p2.x;
9742542f5fSchristos				else
9842542f5fSchristos					fx2 = line_x_for_y(&t->left, t->bottom, false);
9903b705cfSriastradh
10042542f5fSchristos				v = min(fx1, fx2);
10142542f5fSchristos				if (v < x1)
10242542f5fSchristos					x1 = pixman_fixed_floor(v);
10342542f5fSchristos			}
10442542f5fSchristos		}
10503b705cfSriastradh
10642542f5fSchristos		if (((x2 - t->right.p1.x) | (x2 - t->right.p2.x)) < 0) {
10742542f5fSchristos			if (pixman_fixed_floor(t->right.p1.x) == pixman_fixed_floor(t->right.p2.x)) {
10842542f5fSchristos				x2 = pixman_fixed_ceil(t->right.p1.x);
10942542f5fSchristos			} else {
11042542f5fSchristos				if (t->right.p1.y == t->top)
11142542f5fSchristos					fx1 = t->right.p1.x;
11242542f5fSchristos				else
11342542f5fSchristos					fx1 = line_x_for_y(&t->right, t->top, true);
11403b705cfSriastradh
11542542f5fSchristos				if (t->right.p2.y == t->bottom)
11642542f5fSchristos					fx2 = t->right.p2.x;
11742542f5fSchristos				else
11842542f5fSchristos					fx2 = line_x_for_y(&t->right, t->bottom, true);
11903b705cfSriastradh
12042542f5fSchristos				v = max(fx1, fx2);
12142542f5fSchristos				if (v > x2)
12242542f5fSchristos					x2 = pixman_fixed_ceil(v);
12342542f5fSchristos			}
12442542f5fSchristos		}
12542542f5fSchristos	} while (t++, --n);
12603b705cfSriastradh
12742542f5fSchristos	box->x1 = pixman_fixed_to_int(x1);
12842542f5fSchristos	box->x2 = pixman_fixed_to_int(x2);
12942542f5fSchristos	box->y1 = pixman_fixed_integer_floor(y1);
13042542f5fSchristos	box->y2 = pixman_fixed_integer_ceil(y2);
13103b705cfSriastradh
13242542f5fSchristos	return box->x2 > box->x1 && box->y2 > box->y1;
13342542f5fSchristos}
13403b705cfSriastradh
13542542f5fSchristosstatic bool
13642542f5fSchristostrapezoids_inplace_fallback(struct sna *sna,
13742542f5fSchristos			    CARD8 op,
13842542f5fSchristos			    PicturePtr src, PicturePtr dst, PictFormatPtr mask,
13942542f5fSchristos			    int ntrap, xTrapezoid *traps)
14042542f5fSchristos{
14142542f5fSchristos	pixman_image_t *image;
14242542f5fSchristos	BoxRec box;
14342542f5fSchristos	uint32_t color;
14442542f5fSchristos	int dx, dy;
14503b705cfSriastradh
14642542f5fSchristos	if (op != PictOpAdd)
14742542f5fSchristos		return false;
14803b705cfSriastradh
14942542f5fSchristos	if (is_mono(dst, mask)) {
15042542f5fSchristos		if (dst->format != PICT_a1)
15142542f5fSchristos			return false;
15242542f5fSchristos	} else {
15342542f5fSchristos		if (dst->format != PICT_a8)
15442542f5fSchristos			return false;
15542542f5fSchristos	}
15603b705cfSriastradh
15742542f5fSchristos	if (!sna_picture_is_solid(src, &color) || (color >> 24) != 0xff) {
15842542f5fSchristos		DBG(("%s: not an opaque solid source\n", __FUNCTION__));
15942542f5fSchristos		return false;
16042542f5fSchristos	}
16103b705cfSriastradh
16242542f5fSchristos	box.x1 = dst->pDrawable->x;
16342542f5fSchristos	box.y1 = dst->pDrawable->y;
16442542f5fSchristos	box.x2 = dst->pDrawable->width;
16542542f5fSchristos	box.y2 = dst->pDrawable->height;
16642542f5fSchristos	if (pixman_region_contains_rectangle(dst->pCompositeClip,
16742542f5fSchristos					     &box) != PIXMAN_REGION_IN) {
16842542f5fSchristos		DBG(("%s: requires clipping, drawable (%d,%d), (%d, %d), clip (%d, %d), (%d, %d)\n", __FUNCTION__,
16942542f5fSchristos		     box.x1, box.y1, box.x2, box.y2,
17042542f5fSchristos		     dst->pCompositeClip->extents.x1,
17142542f5fSchristos		     dst->pCompositeClip->extents.y1,
17242542f5fSchristos		     dst->pCompositeClip->extents.x2,
17342542f5fSchristos		     dst->pCompositeClip->extents.y2));
17442542f5fSchristos		return false;
17542542f5fSchristos	}
17603b705cfSriastradh
17742542f5fSchristos	if (is_gpu(sna, dst->pDrawable, PREFER_GPU_SPANS)) {
17842542f5fSchristos		DBG(("%s: not performing inplace as dst is already on the GPU\n",
17942542f5fSchristos		     __FUNCTION__));
18042542f5fSchristos		return false;
18142542f5fSchristos	}
18203b705cfSriastradh
18342542f5fSchristos	DBG(("%s\n", __FUNCTION__));
18403b705cfSriastradh
18542542f5fSchristos	image = NULL;
18642542f5fSchristos	if (sna_drawable_move_to_cpu(dst->pDrawable, MOVE_READ | MOVE_WRITE))
18742542f5fSchristos		image = image_from_pict(dst, false, &dx, &dy);
18842542f5fSchristos	if (image) {
18942542f5fSchristos		dx += dst->pDrawable->x;
19042542f5fSchristos		dy += dst->pDrawable->y;
19103b705cfSriastradh
19242542f5fSchristos		if (sigtrap_get() == 0) {
19342542f5fSchristos			for (; ntrap; ntrap--, traps++)
19442542f5fSchristos				if (xTrapezoidValid(traps))
19542542f5fSchristos					pixman_rasterize_trapezoid(image,
19642542f5fSchristos								   (pixman_trapezoid_t *)traps,
19742542f5fSchristos								   dx, dy);
19842542f5fSchristos			sigtrap_put();
19942542f5fSchristos		}
20003b705cfSriastradh
20142542f5fSchristos		pixman_image_unref(image);
20203b705cfSriastradh	}
20303b705cfSriastradh
20442542f5fSchristos	return true;
20503b705cfSriastradh}
20603b705cfSriastradh
20742542f5fSchristosstruct rasterize_traps_thread {
20842542f5fSchristos	xTrapezoid *traps;
20942542f5fSchristos	char *ptr;
21042542f5fSchristos	int stride;
21142542f5fSchristos	BoxRec bounds;
21242542f5fSchristos	pixman_format_code_t format;
21342542f5fSchristos	int ntrap;
21442542f5fSchristos};
21503b705cfSriastradh
21642542f5fSchristosstatic void rasterize_traps_thread(void *arg)
21703b705cfSriastradh{
21842542f5fSchristos	struct rasterize_traps_thread *thread = arg;
21942542f5fSchristos	pixman_image_t *image;
22042542f5fSchristos	int width, height, n;
22103b705cfSriastradh
22242542f5fSchristos	width = thread->bounds.x2 - thread->bounds.x1;
22342542f5fSchristos	height = thread->bounds.y2 - thread->bounds.y1;
22403b705cfSriastradh
22542542f5fSchristos	memset(thread->ptr, 0, thread->stride*height);
22642542f5fSchristos	if (PIXMAN_FORMAT_DEPTH(thread->format) < 8)
22742542f5fSchristos		image = pixman_image_create_bits(thread->format,
22842542f5fSchristos						 width, height,
22942542f5fSchristos						 NULL, 0);
23042542f5fSchristos	else
23142542f5fSchristos		image = pixman_image_create_bits(thread->format,
23242542f5fSchristos						 width, height,
23342542f5fSchristos						 (uint32_t *)thread->ptr,
23442542f5fSchristos						 thread->stride);
23542542f5fSchristos	if (image == NULL)
23642542f5fSchristos		return;
23703b705cfSriastradh
23842542f5fSchristos	for (n = 0; n < thread->ntrap; n++)
23942542f5fSchristos		if (xTrapezoidValid(&thread->traps[n]))
24042542f5fSchristos			pixman_rasterize_trapezoid(image,
24142542f5fSchristos						   (pixman_trapezoid_t *)&thread->traps[n],
24242542f5fSchristos						   -thread->bounds.x1, -thread->bounds.y1);
24342542f5fSchristos
24442542f5fSchristos	if (PIXMAN_FORMAT_DEPTH(thread->format) < 8) {
24542542f5fSchristos		pixman_image_t *a8;
24603b705cfSriastradh
24742542f5fSchristos		a8 = pixman_image_create_bits(PIXMAN_a8,
24842542f5fSchristos					      width, height,
24942542f5fSchristos					      (uint32_t *)thread->ptr,
25042542f5fSchristos					      thread->stride);
25142542f5fSchristos		if (a8) {
25242542f5fSchristos			pixman_image_composite(PIXMAN_OP_SRC,
25342542f5fSchristos					       image, NULL, a8,
25442542f5fSchristos					       0, 0,
25542542f5fSchristos					       0, 0,
25642542f5fSchristos					       0, 0,
25742542f5fSchristos					       width, height);
25842542f5fSchristos			pixman_image_unref(a8);
25942542f5fSchristos		}
26042542f5fSchristos	}
26103b705cfSriastradh
26242542f5fSchristos	pixman_image_unref(image);
26303b705cfSriastradh}
26403b705cfSriastradh
26542542f5fSchristosstatic void
26642542f5fSchristostrapezoids_fallback(struct sna *sna,
26742542f5fSchristos		    CARD8 op, PicturePtr src, PicturePtr dst,
26842542f5fSchristos		    PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc,
26942542f5fSchristos		    int ntrap, xTrapezoid * traps)
27003b705cfSriastradh{
27142542f5fSchristos	ScreenPtr screen = dst->pDrawable->pScreen;
27203b705cfSriastradh
27342542f5fSchristos	if (maskFormat) {
27442542f5fSchristos		PixmapPtr scratch;
27542542f5fSchristos		PicturePtr mask;
27642542f5fSchristos		INT16 dst_x, dst_y;
27742542f5fSchristos		BoxRec bounds;
27842542f5fSchristos		int width, height, depth;
27942542f5fSchristos		pixman_image_t *image;
28042542f5fSchristos		pixman_format_code_t format;
28142542f5fSchristos		int error;
28203b705cfSriastradh
28342542f5fSchristos		trapezoid_origin(&traps[0].left, &dst_x, &dst_y);
28403b705cfSriastradh
28542542f5fSchristos		if (!trapezoids_bounds(ntrap, traps, &bounds))
28642542f5fSchristos			return;
28703b705cfSriastradh
28842542f5fSchristos		DBG(("%s: bounds (%d, %d), (%d, %d)\n", __FUNCTION__,
28942542f5fSchristos		     bounds.x1, bounds.y1, bounds.x2, bounds.y2));
29003b705cfSriastradh
29142542f5fSchristos		if (!sna_compute_composite_extents(&bounds,
29242542f5fSchristos						   src, NULL, dst,
29342542f5fSchristos						   xSrc, ySrc,
29442542f5fSchristos						   0, 0,
29542542f5fSchristos						   bounds.x1, bounds.y1,
29642542f5fSchristos						   bounds.x2 - bounds.x1,
29742542f5fSchristos						   bounds.y2 - bounds.y1))
29842542f5fSchristos			return;
29903b705cfSriastradh
30042542f5fSchristos		DBG(("%s: extents (%d, %d), (%d, %d)\n", __FUNCTION__,
30142542f5fSchristos		     bounds.x1, bounds.y1, bounds.x2, bounds.y2));
30203b705cfSriastradh
30342542f5fSchristos		width  = bounds.x2 - bounds.x1;
30442542f5fSchristos		height = bounds.y2 - bounds.y1;
30542542f5fSchristos		bounds.x1 -= dst->pDrawable->x;
30642542f5fSchristos		bounds.y1 -= dst->pDrawable->y;
30742542f5fSchristos		bounds.x2 -= dst->pDrawable->x;
30842542f5fSchristos		bounds.y2 -= dst->pDrawable->y;
30942542f5fSchristos		depth = maskFormat->depth;
31042542f5fSchristos		if (depth == 1) {
31142542f5fSchristos			format = PIXMAN_a1;
31242542f5fSchristos		} else if (depth <= 4) {
31342542f5fSchristos			format = PIXMAN_a4;
31442542f5fSchristos			depth = 4;
31542542f5fSchristos		} else
31642542f5fSchristos			format = PIXMAN_a8;
31703b705cfSriastradh
31842542f5fSchristos		DBG(("%s: mask (%dx%d) depth=%d, format=%08x\n",
31942542f5fSchristos		     __FUNCTION__, width, height, depth, format));
32042542f5fSchristos		if (is_gpu(sna, dst->pDrawable, PREFER_GPU_RENDER) ||
32142542f5fSchristos		    picture_is_gpu(sna, src, PREFER_GPU_RENDER)) {
32242542f5fSchristos			int num_threads;
32303b705cfSriastradh
32442542f5fSchristos			scratch = sna_pixmap_create_upload(screen,
32542542f5fSchristos							   width, height, 8,
32642542f5fSchristos							   KGEM_BUFFER_WRITE);
32742542f5fSchristos			if (!scratch)
32842542f5fSchristos				return;
32903b705cfSriastradh
33042542f5fSchristos			num_threads = sna_use_threads(width, height, 8);
33142542f5fSchristos			if (num_threads == 1) {
33242542f5fSchristos				if (depth < 8) {
33342542f5fSchristos					image = pixman_image_create_bits(format, width, height,
33442542f5fSchristos									 NULL, 0);
33542542f5fSchristos				} else {
33642542f5fSchristos					memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
33703b705cfSriastradh
33842542f5fSchristos					image = pixman_image_create_bits(format, width, height,
33942542f5fSchristos									 scratch->devPrivate.ptr,
34042542f5fSchristos									 scratch->devKind);
34142542f5fSchristos				}
34242542f5fSchristos				if (image) {
34342542f5fSchristos					for (; ntrap; ntrap--, traps++)
34442542f5fSchristos						if (xTrapezoidValid(traps))
34542542f5fSchristos							pixman_rasterize_trapezoid(image,
34642542f5fSchristos										   (pixman_trapezoid_t *)traps,
34742542f5fSchristos										   -bounds.x1, -bounds.y1);
34842542f5fSchristos					if (depth < 8) {
34942542f5fSchristos						pixman_image_t *a8;
35003b705cfSriastradh
35142542f5fSchristos						a8 = pixman_image_create_bits(PIXMAN_a8, width, height,
35242542f5fSchristos									      scratch->devPrivate.ptr,
35342542f5fSchristos									      scratch->devKind);
35442542f5fSchristos						if (a8) {
35542542f5fSchristos							pixman_image_composite(PIXMAN_OP_SRC,
35642542f5fSchristos									       image, NULL, a8,
35742542f5fSchristos									       0, 0,
35842542f5fSchristos									       0, 0,
35942542f5fSchristos									       0, 0,
36042542f5fSchristos									       width, height);
36142542f5fSchristos							format = PIXMAN_a8;
36242542f5fSchristos							depth = 8;
36342542f5fSchristos							pixman_image_unref(a8);
36442542f5fSchristos						}
36542542f5fSchristos					}
36603b705cfSriastradh
36742542f5fSchristos					pixman_image_unref(image);
36842542f5fSchristos				}
36942542f5fSchristos				if (format != PIXMAN_a8) {
37042542f5fSchristos					sna_pixmap_destroy(scratch);
37142542f5fSchristos					return;
37242542f5fSchristos				}
37342542f5fSchristos			} else {
37442542f5fSchristos				struct rasterize_traps_thread threads[num_threads];
37542542f5fSchristos				int y, dy, n;
37603b705cfSriastradh
37742542f5fSchristos				threads[0].ptr = scratch->devPrivate.ptr;
37842542f5fSchristos				threads[0].stride = scratch->devKind;
37942542f5fSchristos				threads[0].traps = traps;
38042542f5fSchristos				threads[0].ntrap = ntrap;
38142542f5fSchristos				threads[0].bounds = bounds;
38242542f5fSchristos				threads[0].format = format;
38303b705cfSriastradh
38442542f5fSchristos				y = bounds.y1;
38542542f5fSchristos				dy = (height + num_threads - 1) / num_threads;
38642542f5fSchristos				num_threads -= (num_threads-1) * dy >= bounds.y2 - bounds.y1;
38703b705cfSriastradh
38842542f5fSchristos				if (sigtrap_get() == 0) {
38942542f5fSchristos					for (n = 1; n < num_threads; n++) {
39042542f5fSchristos						threads[n] = threads[0];
39142542f5fSchristos						threads[n].ptr += (y - bounds.y1) * threads[n].stride;
39242542f5fSchristos						threads[n].bounds.y1 = y;
39342542f5fSchristos						threads[n].bounds.y2 = y += dy;
39403b705cfSriastradh
39542542f5fSchristos						sna_threads_run(n, rasterize_traps_thread, &threads[n]);
39642542f5fSchristos					}
39703b705cfSriastradh
39842542f5fSchristos					assert(y < threads[0].bounds.y2);
39942542f5fSchristos					threads[0].ptr += (y - bounds.y1) * threads[0].stride;
40042542f5fSchristos					threads[0].bounds.y1 = y;
40142542f5fSchristos					rasterize_traps_thread(&threads[0]);
40203b705cfSriastradh
40342542f5fSchristos					sna_threads_wait();
40442542f5fSchristos					sigtrap_put();
40542542f5fSchristos				} else
40642542f5fSchristos					sna_threads_kill();
40703b705cfSriastradh
40842542f5fSchristos				format = PIXMAN_a8;
40942542f5fSchristos				depth = 8;
41042542f5fSchristos			}
41103b705cfSriastradh		} else {
41242542f5fSchristos			scratch = sna_pixmap_create_unattached(screen,
41342542f5fSchristos							       width, height,
41442542f5fSchristos							       depth);
41542542f5fSchristos			if (!scratch)
41642542f5fSchristos				return;
41703b705cfSriastradh
41842542f5fSchristos			memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
41942542f5fSchristos			image = pixman_image_create_bits(format, width, height,
42042542f5fSchristos							 scratch->devPrivate.ptr,
42142542f5fSchristos							 scratch->devKind);
42242542f5fSchristos			if (image) {
42342542f5fSchristos				for (; ntrap; ntrap--, traps++)
42442542f5fSchristos					if (xTrapezoidValid(traps))
42542542f5fSchristos						pixman_rasterize_trapezoid(image,
42642542f5fSchristos									   (pixman_trapezoid_t *)traps,
42742542f5fSchristos									   -bounds.x1, -bounds.y1);
42842542f5fSchristos				pixman_image_unref(image);
42903b705cfSriastradh			}
43003b705cfSriastradh		}
43103b705cfSriastradh
43242542f5fSchristos		mask = CreatePicture(0, &scratch->drawable,
43342542f5fSchristos				     PictureMatchFormat(screen, depth, format),
43442542f5fSchristos				     0, 0, serverClient, &error);
43542542f5fSchristos		if (mask) {
43642542f5fSchristos			CompositePicture(op, src, mask, dst,
43742542f5fSchristos					 xSrc + bounds.x1 - dst_x,
43842542f5fSchristos					 ySrc + bounds.y1 - dst_y,
43942542f5fSchristos					 0, 0,
44042542f5fSchristos					 bounds.x1, bounds.y1,
44142542f5fSchristos					 width, height);
44242542f5fSchristos			FreePicture(mask, 0);
44303b705cfSriastradh		}
44442542f5fSchristos		sna_pixmap_destroy(scratch);
44503b705cfSriastradh	} else {
44642542f5fSchristos		if (dst->polyEdge == PolyEdgeSharp)
44742542f5fSchristos			maskFormat = PictureMatchFormat(screen, 1, PICT_a1);
44842542f5fSchristos		else
44942542f5fSchristos			maskFormat = PictureMatchFormat(screen, 8, PICT_a8);
45003b705cfSriastradh
45142542f5fSchristos		for (; ntrap; ntrap--, traps++)
45242542f5fSchristos			trapezoids_fallback(sna, op,
45342542f5fSchristos					    src, dst, maskFormat,
45442542f5fSchristos					    xSrc, ySrc, 1, traps);
45503b705cfSriastradh	}
45603b705cfSriastradh}
45703b705cfSriastradh
45803b705cfSriastradhstatic bool
45942542f5fSchristostrapezoid_spans_maybe_inplace(struct sna *sna,
46042542f5fSchristos			      CARD8 op, PicturePtr src, PicturePtr dst,
46142542f5fSchristos			      PictFormatPtr maskFormat)
46203b705cfSriastradh{
46342542f5fSchristos	struct sna_pixmap *priv;
46403b705cfSriastradh
46503b705cfSriastradh	if (NO_SCAN_CONVERTER)
46603b705cfSriastradh		return false;
46703b705cfSriastradh
46842542f5fSchristos	if (dst->alphaMap)
46903b705cfSriastradh		return false;
47042542f5fSchristos	if (is_mono(dst, maskFormat))
47142542f5fSchristos		goto out;
47203b705cfSriastradh
47342542f5fSchristos	switch ((int)dst->format) {
47442542f5fSchristos	case PICT_a8:
47542542f5fSchristos		if (!sna_picture_is_solid(src, NULL))
47642542f5fSchristos			return false;
47703b705cfSriastradh
47842542f5fSchristos		switch (op) {
47942542f5fSchristos		case PictOpIn:
48042542f5fSchristos		case PictOpAdd:
48142542f5fSchristos		case PictOpSrc:
48242542f5fSchristos			break;
48342542f5fSchristos		default:
48442542f5fSchristos			return false;
48542542f5fSchristos		}
48642542f5fSchristos		break;
48703b705cfSriastradh
48842542f5fSchristos	case PICT_x8r8g8b8:
48942542f5fSchristos	case PICT_a8r8g8b8:
49042542f5fSchristos		if (picture_is_gpu(sna, src, 0))
49142542f5fSchristos			return false;
49203b705cfSriastradh
49342542f5fSchristos		switch (op) {
49442542f5fSchristos		case PictOpOver:
49542542f5fSchristos		case PictOpAdd:
49642542f5fSchristos		case PictOpOutReverse:
49742542f5fSchristos			break;
49842542f5fSchristos		case PictOpSrc:
49942542f5fSchristos			if (sna_picture_is_solid(src, NULL))
50042542f5fSchristos				break;
50103b705cfSriastradh
50242542f5fSchristos			if (!sna_drawable_is_clear(dst->pDrawable))
50342542f5fSchristos				return false;
50442542f5fSchristos			break;
50542542f5fSchristos		default:
50642542f5fSchristos			return false;
50742542f5fSchristos		}
50842542f5fSchristos		break;
50942542f5fSchristos	default:
51042542f5fSchristos		return false;
51142542f5fSchristos	}
51203b705cfSriastradh
51342542f5fSchristosout:
51442542f5fSchristos	priv = sna_pixmap_from_drawable(dst->pDrawable);
51542542f5fSchristos	if (priv == NULL) {
51642542f5fSchristos		DBG(("%s? yes -- unattached\n", __FUNCTION__));
51703b705cfSriastradh		return true;
51842542f5fSchristos	}
51903b705cfSriastradh
52042542f5fSchristos	if (priv->cpu_bo && kgem_bo_is_busy(priv->cpu_bo)) {
52142542f5fSchristos		DBG(("%s? no -- CPU bo is busy\n", __FUNCTION__));
52242542f5fSchristos		return false;
52342542f5fSchristos	}
52403b705cfSriastradh
52542542f5fSchristos	if (DAMAGE_IS_ALL(priv->cpu_damage) || priv->gpu_damage == NULL) {
52642542f5fSchristos		DBG(("%s? yes -- damaged on CPU only (all? %d)\n", __FUNCTION__, DAMAGE_IS_ALL(priv->cpu_damage)));
52703b705cfSriastradh		return true;
52803b705cfSriastradh	}
52903b705cfSriastradh
53042542f5fSchristos	if (priv->clear) {
53142542f5fSchristos		DBG(("%s? clear, %s\n", __FUNCTION__,
53242542f5fSchristos		     dst->pDrawable->width <= TOR_INPLACE_SIZE ? "yes" : "no"));
53342542f5fSchristos		return dst->pDrawable->width <= TOR_INPLACE_SIZE;
53403b705cfSriastradh	}
53503b705cfSriastradh
53642542f5fSchristos	if (kgem_bo_is_busy(priv->gpu_bo)) {
53742542f5fSchristos		DBG(("%s? no, GPU bo is busy\n", __FUNCTION__));
53842542f5fSchristos		return false;
53903b705cfSriastradh	}
54042542f5fSchristos
54142542f5fSchristos	if (priv->cpu_damage) {
54242542f5fSchristos		DBG(("%s? yes, idle GPU bo and damage on idle CPU\n", __FUNCTION__));
54342542f5fSchristos		return true;
54403b705cfSriastradh	}
54503b705cfSriastradh
54642542f5fSchristos	DBG(("%s? small enough? %s\n", __FUNCTION__,
54742542f5fSchristos	     dst->pDrawable->width <= TOR_INPLACE_SIZE ? "yes" : "no"));
54842542f5fSchristos	return dst->pDrawable->width <= TOR_INPLACE_SIZE;
54903b705cfSriastradh}
55003b705cfSriastradh
55103b705cfSriastradhvoid
55203b705cfSriastradhsna_composite_trapezoids(CARD8 op,
55303b705cfSriastradh			 PicturePtr src,
55403b705cfSriastradh			 PicturePtr dst,
55503b705cfSriastradh			 PictFormatPtr maskFormat,
55603b705cfSriastradh			 INT16 xSrc, INT16 ySrc,
55703b705cfSriastradh			 int ntrap, xTrapezoid *traps)
55803b705cfSriastradh{
55903b705cfSriastradh	PixmapPtr pixmap = get_drawable_pixmap(dst->pDrawable);
56003b705cfSriastradh	struct sna *sna = to_sna_from_pixmap(pixmap);
56103b705cfSriastradh	struct sna_pixmap *priv;
56242542f5fSchristos	bool force_fallback = false;
56342542f5fSchristos	bool rectilinear, pixel_aligned;
56403b705cfSriastradh	unsigned flags;
56503b705cfSriastradh	int n;
56603b705cfSriastradh
56703b705cfSriastradh	DBG(("%s(op=%d, src=(%d, %d), mask=%08x, ntrap=%d)\n", __FUNCTION__,
56803b705cfSriastradh	     op, xSrc, ySrc,
56903b705cfSriastradh	     maskFormat ? (int)maskFormat->format : 0,
57003b705cfSriastradh	     ntrap));
57103b705cfSriastradh
57203b705cfSriastradh	if (ntrap == 0)
57303b705cfSriastradh		return;
57403b705cfSriastradh
57503b705cfSriastradh	if (NO_ACCEL)
57642542f5fSchristos		goto force_fallback;
57742542f5fSchristos
57842542f5fSchristos	if (FORCE_FALLBACK > 0)
57942542f5fSchristos		goto force_fallback;
58003b705cfSriastradh
58103b705cfSriastradh	if (wedged(sna)) {
58203b705cfSriastradh		DBG(("%s: fallback -- wedged\n", __FUNCTION__));
58342542f5fSchristos		goto force_fallback;
58403b705cfSriastradh	}
58503b705cfSriastradh
58603b705cfSriastradh	if (dst->alphaMap) {
58703b705cfSriastradh		DBG(("%s: fallback -- dst alpha map\n", __FUNCTION__));
58842542f5fSchristos		goto force_fallback;
58903b705cfSriastradh	}
59003b705cfSriastradh
59103b705cfSriastradh	priv = sna_pixmap(pixmap);
59203b705cfSriastradh	if (priv == NULL) {
59303b705cfSriastradh		DBG(("%s: fallback -- dst is unattached\n", __FUNCTION__));
59442542f5fSchristos		goto force_fallback;
59503b705cfSriastradh	}
59603b705cfSriastradh
59742542f5fSchristos	if (FORCE_FALLBACK == 0 &&
59842542f5fSchristos	    !is_gpu_dst(priv) && !picture_is_gpu(sna, src, 0) && untransformed(src)) {
59942542f5fSchristos		DBG(("%s: force fallbacks -- (!gpu dst, %dx%d? %d) && (src-is-cpu? %d && untransformed? %d)\n",
60042542f5fSchristos		     __FUNCTION__, dst->pDrawable->width, dst->pDrawable->height,
60142542f5fSchristos		     !is_gpu_dst(priv), !picture_is_gpu(sna, src, 0), untransformed(src)));
60242542f5fSchristos
60342542f5fSchristosforce_fallback:
60403b705cfSriastradh		force_fallback = true;
60503b705cfSriastradh	}
60603b705cfSriastradh
60703b705cfSriastradh	/* scan through for fast rectangles */
60803b705cfSriastradh	rectilinear = pixel_aligned = true;
60903b705cfSriastradh	if (is_mono(dst, maskFormat)) {
61003b705cfSriastradh		for (n = 0; n < ntrap && rectilinear; n++) {
61103b705cfSriastradh			int lx1 = pixman_fixed_to_int(traps[n].left.p1.x + pixman_fixed_1_minus_e/2);
61203b705cfSriastradh			int lx2 = pixman_fixed_to_int(traps[n].left.p2.x + pixman_fixed_1_minus_e/2);
61303b705cfSriastradh			int rx1 = pixman_fixed_to_int(traps[n].right.p1.x + pixman_fixed_1_minus_e/2);
61403b705cfSriastradh			int rx2 = pixman_fixed_to_int(traps[n].right.p2.x + pixman_fixed_1_minus_e/2);
61503b705cfSriastradh			rectilinear &= lx1 == lx2 && rx1 == rx2;
61603b705cfSriastradh		}
61703b705cfSriastradh	} else if (dst->polyMode != PolyModePrecise) {
61803b705cfSriastradh		for (n = 0; n < ntrap && rectilinear; n++) {
61942542f5fSchristos			int lx1 = pixman_fixed_to_fast(traps[n].left.p1.x);
62042542f5fSchristos			int lx2 = pixman_fixed_to_fast(traps[n].left.p2.x);
62142542f5fSchristos			int rx1 = pixman_fixed_to_fast(traps[n].right.p1.x);
62242542f5fSchristos			int rx2 = pixman_fixed_to_fast(traps[n].right.p2.x);
62342542f5fSchristos			int top = pixman_fixed_to_fast(traps[n].top);
62442542f5fSchristos			int bot = pixman_fixed_to_fast(traps[n].bottom);
62503b705cfSriastradh
62603b705cfSriastradh			rectilinear &= lx1 == lx2 && rx1 == rx2;
62703b705cfSriastradh			pixel_aligned &= ((top | bot | lx1 | lx2 | rx1 | rx2) & FAST_SAMPLES_mask) == 0;
62803b705cfSriastradh		}
62903b705cfSriastradh	} else {
63003b705cfSriastradh		for (n = 0; n < ntrap && rectilinear; n++) {
63103b705cfSriastradh			rectilinear &=
63203b705cfSriastradh				traps[n].left.p1.x == traps[n].left.p2.x &&
63303b705cfSriastradh				traps[n].right.p1.x == traps[n].right.p2.x;
63403b705cfSriastradh			pixel_aligned &=
63503b705cfSriastradh				((traps[n].top | traps[n].bottom |
63603b705cfSriastradh				  traps[n].left.p1.x | traps[n].left.p2.x |
63703b705cfSriastradh				  traps[n].right.p1.x | traps[n].right.p2.x)
63803b705cfSriastradh				 & pixman_fixed_1_minus_e) == 0;
63903b705cfSriastradh		}
64003b705cfSriastradh	}
64103b705cfSriastradh
64242542f5fSchristos	DBG(("%s: rectilinear? %d, pixel-aligned? %d, mono? %d precise? %d\n",
64342542f5fSchristos	     __FUNCTION__, rectilinear, pixel_aligned,
64442542f5fSchristos	     is_mono(dst, maskFormat), is_precise(dst, maskFormat)));
64542542f5fSchristos
64603b705cfSriastradh	flags = 0;
64703b705cfSriastradh	if (rectilinear) {
64803b705cfSriastradh		if (pixel_aligned) {
64903b705cfSriastradh			if (composite_aligned_boxes(sna, op, src, dst,
65003b705cfSriastradh						    maskFormat,
65103b705cfSriastradh						    xSrc, ySrc,
65203b705cfSriastradh						    ntrap, traps,
65303b705cfSriastradh						    force_fallback))
65403b705cfSriastradh			    return;
65503b705cfSriastradh		} else {
65603b705cfSriastradh			if (composite_unaligned_boxes(sna, op, src, dst,
65703b705cfSriastradh						      maskFormat,
65803b705cfSriastradh						      xSrc, ySrc,
65903b705cfSriastradh						      ntrap, traps,
66003b705cfSriastradh						      force_fallback))
66103b705cfSriastradh				return;
66203b705cfSriastradh		}
66303b705cfSriastradh		flags |= COMPOSITE_SPANS_RECTILINEAR;
66403b705cfSriastradh	}
66503b705cfSriastradh
66603b705cfSriastradh	if (force_fallback)
66703b705cfSriastradh		goto fallback;
66803b705cfSriastradh
66903b705cfSriastradh	if (is_mono(dst, maskFormat) &&
67003b705cfSriastradh	    mono_trapezoids_span_converter(sna, op, src, dst,
67103b705cfSriastradh					   xSrc, ySrc,
67203b705cfSriastradh					   ntrap, traps))
67303b705cfSriastradh		return;
67442542f5fSchristos
67542542f5fSchristos	if (trapezoid_spans_maybe_inplace(sna, op, src, dst, maskFormat)) {
67642542f5fSchristos		flags |= COMPOSITE_SPANS_INPLACE_HINT;
67742542f5fSchristos		if (trapezoid_span_inplace(sna, op, src, dst, maskFormat, flags,
67842542f5fSchristos					   xSrc, ySrc, ntrap, traps,
67942542f5fSchristos					   false))
68042542f5fSchristos			return;
68103b705cfSriastradh	}
68203b705cfSriastradh
68342542f5fSchristos	if (trapezoid_span_converter(sna, op, src, dst, maskFormat, flags,
68442542f5fSchristos				     xSrc, ySrc, ntrap, traps))
68542542f5fSchristos		return;
68603b705cfSriastradh
68742542f5fSchristos	if (trapezoid_span_inplace(sna, op, src, dst, maskFormat, flags,
68842542f5fSchristos				   xSrc, ySrc, ntrap, traps,
68942542f5fSchristos				   false))
69042542f5fSchristos		return;
69103b705cfSriastradh
69242542f5fSchristos	if (trapezoid_mask_converter(op, src, dst, maskFormat, flags,
69342542f5fSchristos				     xSrc, ySrc, ntrap, traps))
69442542f5fSchristos		return;
69503b705cfSriastradh
69642542f5fSchristosfallback:
69742542f5fSchristos	if (trapezoid_span_inplace(sna, op, src, dst, maskFormat, flags,
69842542f5fSchristos				   xSrc, ySrc, ntrap, traps,
69942542f5fSchristos				   true))
70042542f5fSchristos		return;
70103b705cfSriastradh
70242542f5fSchristos	if (trapezoid_span_fallback(op, src, dst, maskFormat, flags,
70342542f5fSchristos				    xSrc, ySrc, ntrap, traps))
70442542f5fSchristos		return;
70503b705cfSriastradh
70642542f5fSchristos	if (trapezoids_inplace_fallback(sna, op, src, dst, maskFormat,
70742542f5fSchristos					ntrap, traps))
70842542f5fSchristos		return;
70903b705cfSriastradh
71042542f5fSchristos	DBG(("%s: fallback mask=%08x, ntrap=%d\n", __FUNCTION__,
71142542f5fSchristos	     maskFormat ? (unsigned)maskFormat->format : 0, ntrap));
71242542f5fSchristos	trapezoids_fallback(sna, op, src, dst, maskFormat,
71342542f5fSchristos			    xSrc, ySrc,
71442542f5fSchristos			    ntrap, traps);
71542542f5fSchristos}
71603b705cfSriastradh
71742542f5fSchristosstatic void mark_damaged(PixmapPtr pixmap, struct sna_pixmap *priv,
71842542f5fSchristos			 BoxPtr box, int16_t x, int16_t y)
71942542f5fSchristos{
72042542f5fSchristos	box->x1 += x; box->x2 += x;
72142542f5fSchristos	box->y1 += y; box->y2 += y;
72242542f5fSchristos	if (box->x1 <= 0 && box->y1 <= 0 &&
72342542f5fSchristos	    box->x2 >= pixmap->drawable.width &&
72442542f5fSchristos	    box->y2 >= pixmap->drawable.height) {
72542542f5fSchristos		sna_damage_destroy(&priv->cpu_damage);
72642542f5fSchristos		sna_damage_all(&priv->gpu_damage, pixmap);
72742542f5fSchristos		list_del(&priv->flush_list);
72842542f5fSchristos	} else {
72942542f5fSchristos		sna_damage_add_box(&priv->gpu_damage, box);
73042542f5fSchristos		sna_damage_subtract_box(&priv->cpu_damage, box);
73142542f5fSchristos	}
73203b705cfSriastradh}
73303b705cfSriastradh
73403b705cfSriastradhstatic bool
73503b705cfSriastradhtrap_upload(PicturePtr picture,
73603b705cfSriastradh	    INT16 x, INT16 y,
73703b705cfSriastradh	    int ntrap, xTrap *trap)
73803b705cfSriastradh{
73903b705cfSriastradh	ScreenPtr screen = picture->pDrawable->pScreen;
74003b705cfSriastradh	struct sna *sna = to_sna_from_screen(screen);
74103b705cfSriastradh	PixmapPtr pixmap = get_drawable_pixmap(picture->pDrawable);
74203b705cfSriastradh	PixmapPtr scratch;
74303b705cfSriastradh	struct sna_pixmap *priv;
74403b705cfSriastradh	BoxRec extents;
74503b705cfSriastradh	pixman_image_t *image;
74603b705cfSriastradh	int width, height, depth;
74703b705cfSriastradh	int n;
74803b705cfSriastradh
74903b705cfSriastradh	priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_WRITE);
75003b705cfSriastradh	if (priv == NULL)
75103b705cfSriastradh		return false;
75203b705cfSriastradh
75303b705cfSriastradh	extents = *RegionExtents(picture->pCompositeClip);
75403b705cfSriastradh	for (n = 0; n < ntrap; n++) {
75503b705cfSriastradh		int v;
75603b705cfSriastradh
75703b705cfSriastradh		v = x + pixman_fixed_integer_floor (MIN(trap[n].top.l, trap[n].bot.l));
75803b705cfSriastradh		if (v < extents.x1)
75903b705cfSriastradh			extents.x1 = v;
76003b705cfSriastradh
76103b705cfSriastradh		v = x + pixman_fixed_integer_ceil (MAX(trap[n].top.r, trap[n].bot.r));
76203b705cfSriastradh		if (v > extents.x2)
76303b705cfSriastradh			extents.x2 = v;
76403b705cfSriastradh
76503b705cfSriastradh		v = y + pixman_fixed_integer_floor (trap[n].top.y);
76603b705cfSriastradh		if (v < extents.y1)
76703b705cfSriastradh			extents.y1 = v;
76803b705cfSriastradh
76903b705cfSriastradh		v = y + pixman_fixed_integer_ceil (trap[n].bot.y);
77003b705cfSriastradh		if (v > extents.y2)
77103b705cfSriastradh			extents.y2 = v;
77203b705cfSriastradh	}
77303b705cfSriastradh
77403b705cfSriastradh	DBG(("%s: extents (%d, %d), (%d, %d)\n",
77503b705cfSriastradh	     __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2));
77603b705cfSriastradh
77703b705cfSriastradh	width  = extents.x2 - extents.x1;
77803b705cfSriastradh	height = extents.y2 - extents.y1;
77903b705cfSriastradh	depth = picture->pDrawable->depth;
78003b705cfSriastradh
78103b705cfSriastradh	DBG(("%s: tmp (%dx%d) depth=%d\n",
78203b705cfSriastradh	     __FUNCTION__, width, height, depth));
78303b705cfSriastradh	scratch = sna_pixmap_create_upload(screen,
78403b705cfSriastradh					   width, height, depth,
78503b705cfSriastradh					   KGEM_BUFFER_WRITE);
78603b705cfSriastradh	if (!scratch)
78703b705cfSriastradh		return true;
78803b705cfSriastradh
78903b705cfSriastradh	memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
79042542f5fSchristos	image = pixman_image_create_bits((pixman_format_code_t)picture->format,
79142542f5fSchristos					 width, height,
79203b705cfSriastradh					 scratch->devPrivate.ptr,
79303b705cfSriastradh					 scratch->devKind);
79403b705cfSriastradh	if (image) {
79503b705cfSriastradh		pixman_add_traps (image, -extents.x1, -extents.y1,
79603b705cfSriastradh				  ntrap, (pixman_trap_t *)trap);
79703b705cfSriastradh
79803b705cfSriastradh		pixman_image_unref(image);
79903b705cfSriastradh	}
80003b705cfSriastradh
80103b705cfSriastradh	/* XXX clip boxes */
80203b705cfSriastradh	get_drawable_deltas(picture->pDrawable, pixmap, &x, &y);
80303b705cfSriastradh	sna->render.copy_boxes(sna, GXcopy,
80442542f5fSchristos			       &scratch->drawable, __sna_pixmap_get_bo(scratch), -extents.x1, -extents.x1,
80542542f5fSchristos			       &pixmap->drawable, priv->gpu_bo, x, y,
80603b705cfSriastradh			       &extents, 1, 0);
80703b705cfSriastradh	mark_damaged(pixmap, priv, &extents, x, y);
80803b705cfSriastradh
80903b705cfSriastradh	sna_pixmap_destroy(scratch);
81003b705cfSriastradh	return true;
81103b705cfSriastradh}
81203b705cfSriastradh
81303b705cfSriastradhvoid
81403b705cfSriastradhsna_add_traps(PicturePtr picture, INT16 x, INT16 y, int n, xTrap *t)
81503b705cfSriastradh{
81603b705cfSriastradh	struct sna *sna;
81703b705cfSriastradh
81803b705cfSriastradh	DBG(("%s (%d, %d) x %d\n", __FUNCTION__, x, y, n));
81903b705cfSriastradh
82003b705cfSriastradh	sna = to_sna_from_drawable(picture->pDrawable);
82103b705cfSriastradh	if (is_gpu(sna, picture->pDrawable, PREFER_GPU_SPANS)) {
82203b705cfSriastradh		if (trap_span_converter(sna, picture, x, y, n, t))
82303b705cfSriastradh			return;
82403b705cfSriastradh	}
82503b705cfSriastradh
82603b705cfSriastradh	if (is_gpu(sna, picture->pDrawable, PREFER_GPU_RENDER)) {
82703b705cfSriastradh		if (trap_mask_converter(sna, picture, x, y, n, t))
82803b705cfSriastradh			return;
82903b705cfSriastradh
83003b705cfSriastradh		if (trap_upload(picture, x, y, n, t))
83103b705cfSriastradh			return;
83203b705cfSriastradh	}
83303b705cfSriastradh
83403b705cfSriastradh	DBG(("%s -- fallback\n", __FUNCTION__));
83503b705cfSriastradh	if (sna_drawable_move_to_cpu(picture->pDrawable,
83603b705cfSriastradh				     MOVE_READ | MOVE_WRITE)) {
83703b705cfSriastradh		pixman_image_t *image;
83803b705cfSriastradh		int dx, dy;
83903b705cfSriastradh
84003b705cfSriastradh		if (!(image = image_from_pict(picture, false, &dx, &dy)))
84103b705cfSriastradh			return;
84203b705cfSriastradh
84342542f5fSchristos		if (sigtrap_get() == 0) {
84442542f5fSchristos			pixman_add_traps(image, x + dx, y + dy, n, (pixman_trap_t *)t);
84542542f5fSchristos			sigtrap_put();
84642542f5fSchristos		}
84703b705cfSriastradh
84803b705cfSriastradh		free_pixman_pict(picture, image);
84903b705cfSriastradh	}
85003b705cfSriastradh}
85103b705cfSriastradh
85203b705cfSriastradh#if HAS_PIXMAN_TRIANGLES
85303b705cfSriastradhstatic void
85403b705cfSriastradhtriangles_fallback(CARD8 op,
85503b705cfSriastradh		   PicturePtr src,
85603b705cfSriastradh		   PicturePtr dst,
85703b705cfSriastradh		   PictFormatPtr maskFormat,
85803b705cfSriastradh		   INT16 xSrc, INT16 ySrc,
85903b705cfSriastradh		   int n, xTriangle *tri)
86003b705cfSriastradh{
86103b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
86203b705cfSriastradh
86303b705cfSriastradh	DBG(("%s op=%d, count=%d\n", __FUNCTION__, op, n));
86403b705cfSriastradh
86503b705cfSriastradh	if (maskFormat) {
86603b705cfSriastradh		PixmapPtr scratch;
86703b705cfSriastradh		PicturePtr mask;
86803b705cfSriastradh		INT16 dst_x, dst_y;
86903b705cfSriastradh		BoxRec bounds;
87003b705cfSriastradh		int width, height, depth;
87103b705cfSriastradh		pixman_image_t *image;
87203b705cfSriastradh		pixman_format_code_t format;
87303b705cfSriastradh		int error;
87403b705cfSriastradh
87503b705cfSriastradh		dst_x = pixman_fixed_to_int(tri[0].p1.x);
87603b705cfSriastradh		dst_y = pixman_fixed_to_int(tri[0].p1.y);
87703b705cfSriastradh
87803b705cfSriastradh		miTriangleBounds(n, tri, &bounds);
87903b705cfSriastradh		DBG(("%s: bounds (%d, %d), (%d, %d)\n",
88003b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
88103b705cfSriastradh
88203b705cfSriastradh		if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2)
88303b705cfSriastradh			return;
88403b705cfSriastradh
88503b705cfSriastradh		if (!sna_compute_composite_extents(&bounds,
88603b705cfSriastradh						   src, NULL, dst,
88703b705cfSriastradh						   xSrc, ySrc,
88803b705cfSriastradh						   0, 0,
88903b705cfSriastradh						   bounds.x1, bounds.y1,
89003b705cfSriastradh						   bounds.x2 - bounds.x1,
89103b705cfSriastradh						   bounds.y2 - bounds.y1))
89203b705cfSriastradh			return;
89303b705cfSriastradh
89403b705cfSriastradh		DBG(("%s: extents (%d, %d), (%d, %d)\n",
89503b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
89603b705cfSriastradh
89703b705cfSriastradh		width  = bounds.x2 - bounds.x1;
89803b705cfSriastradh		height = bounds.y2 - bounds.y1;
89903b705cfSriastradh		bounds.x1 -= dst->pDrawable->x;
90003b705cfSriastradh		bounds.y1 -= dst->pDrawable->y;
90103b705cfSriastradh		depth = maskFormat->depth;
90203b705cfSriastradh		format = maskFormat->format | (BitsPerPixel(depth) << 24);
90303b705cfSriastradh
90403b705cfSriastradh		DBG(("%s: mask (%dx%d) depth=%d, format=%08x\n",
90503b705cfSriastradh		     __FUNCTION__, width, height, depth, format));
90603b705cfSriastradh		scratch = sna_pixmap_create_upload(screen,
90703b705cfSriastradh						   width, height, depth,
90803b705cfSriastradh						   KGEM_BUFFER_WRITE);
90903b705cfSriastradh		if (!scratch)
91003b705cfSriastradh			return;
91103b705cfSriastradh
91242542f5fSchristos		memset(scratch->devPrivate.ptr, 0, (size_t)scratch->devKind*height);
91303b705cfSriastradh		image = pixman_image_create_bits(format, width, height,
91403b705cfSriastradh						 scratch->devPrivate.ptr,
91503b705cfSriastradh						 scratch->devKind);
91603b705cfSriastradh		if (image) {
91703b705cfSriastradh			pixman_add_triangles(image,
91803b705cfSriastradh					     -bounds.x1, -bounds.y1,
91903b705cfSriastradh					     n, (pixman_triangle_t *)tri);
92003b705cfSriastradh			pixman_image_unref(image);
92103b705cfSriastradh		}
92203b705cfSriastradh
92303b705cfSriastradh		mask = CreatePicture(0, &scratch->drawable,
92403b705cfSriastradh				     PictureMatchFormat(screen, depth, format),
92503b705cfSriastradh				     0, 0, serverClient, &error);
92603b705cfSriastradh		if (mask) {
92703b705cfSriastradh			CompositePicture(op, src, mask, dst,
92803b705cfSriastradh					 xSrc + bounds.x1 - dst_x,
92903b705cfSriastradh					 ySrc + bounds.y1 - dst_y,
93003b705cfSriastradh					 0, 0,
93103b705cfSriastradh					 bounds.x1, bounds.y1,
93203b705cfSriastradh					 width, height);
93303b705cfSriastradh			FreePicture(mask, 0);
93403b705cfSriastradh		}
93503b705cfSriastradh		sna_pixmap_destroy(scratch);
93603b705cfSriastradh	} else {
93703b705cfSriastradh		if (dst->polyEdge == PolyEdgeSharp)
93803b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 1, PICT_a1);
93903b705cfSriastradh		else
94003b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 8, PICT_a8);
94103b705cfSriastradh
94203b705cfSriastradh		for (; n--; tri++)
94303b705cfSriastradh			triangles_fallback(op,
94403b705cfSriastradh					   src, dst, maskFormat,
94503b705cfSriastradh					   xSrc, ySrc, 1, tri);
94603b705cfSriastradh	}
94703b705cfSriastradh}
94803b705cfSriastradh
94903b705cfSriastradhvoid
95003b705cfSriastradhsna_composite_triangles(CARD8 op,
95103b705cfSriastradh			 PicturePtr src,
95203b705cfSriastradh			 PicturePtr dst,
95303b705cfSriastradh			 PictFormatPtr maskFormat,
95403b705cfSriastradh			 INT16 xSrc, INT16 ySrc,
95503b705cfSriastradh			 int n, xTriangle *tri)
95603b705cfSriastradh{
95703b705cfSriastradh	struct sna *sna = to_sna_from_drawable(dst->pDrawable);
95803b705cfSriastradh
95903b705cfSriastradh	if (triangles_span_converter(sna, op, src, dst, maskFormat,
96003b705cfSriastradh				     xSrc, ySrc,
96103b705cfSriastradh				     n, tri))
96203b705cfSriastradh		return;
96303b705cfSriastradh
96403b705cfSriastradh	if (triangles_mask_converter(op, src, dst, maskFormat,
96503b705cfSriastradh				     xSrc, ySrc,
96603b705cfSriastradh				     n, tri))
96703b705cfSriastradh		return;
96803b705cfSriastradh
96903b705cfSriastradh	triangles_fallback(op, src, dst, maskFormat, xSrc, ySrc, n, tri);
97003b705cfSriastradh}
97103b705cfSriastradh
97203b705cfSriastradhstatic void
97303b705cfSriastradhtristrip_fallback(CARD8 op,
97403b705cfSriastradh		  PicturePtr src,
97503b705cfSriastradh		  PicturePtr dst,
97603b705cfSriastradh		  PictFormatPtr maskFormat,
97703b705cfSriastradh		  INT16 xSrc, INT16 ySrc,
97803b705cfSriastradh		  int n, xPointFixed *points)
97903b705cfSriastradh{
98003b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
98103b705cfSriastradh
98203b705cfSriastradh	if (maskFormat) {
98303b705cfSriastradh		PixmapPtr scratch;
98403b705cfSriastradh		PicturePtr mask;
98503b705cfSriastradh		INT16 dst_x, dst_y;
98603b705cfSriastradh		BoxRec bounds;
98703b705cfSriastradh		int width, height, depth;
98803b705cfSriastradh		pixman_image_t *image;
98903b705cfSriastradh		pixman_format_code_t format;
99003b705cfSriastradh		int error;
99103b705cfSriastradh
99203b705cfSriastradh		dst_x = pixman_fixed_to_int(points->x);
99303b705cfSriastradh		dst_y = pixman_fixed_to_int(points->y);
99403b705cfSriastradh
99503b705cfSriastradh		miPointFixedBounds(n, points, &bounds);
99603b705cfSriastradh		DBG(("%s: bounds (%d, %d), (%d, %d)\n",
99703b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
99803b705cfSriastradh
99903b705cfSriastradh		if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2)
100003b705cfSriastradh			return;
100103b705cfSriastradh
100203b705cfSriastradh		if (!sna_compute_composite_extents(&bounds,
100303b705cfSriastradh						   src, NULL, dst,
100403b705cfSriastradh						   xSrc, ySrc,
100503b705cfSriastradh						   0, 0,
100603b705cfSriastradh						   bounds.x1, bounds.y1,
100703b705cfSriastradh						   bounds.x2 - bounds.x1,
100803b705cfSriastradh						   bounds.y2 - bounds.y1))
100903b705cfSriastradh			return;
101003b705cfSriastradh
101103b705cfSriastradh		DBG(("%s: extents (%d, %d), (%d, %d)\n",
101203b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
101303b705cfSriastradh
101403b705cfSriastradh		width  = bounds.x2 - bounds.x1;
101503b705cfSriastradh		height = bounds.y2 - bounds.y1;
101603b705cfSriastradh		bounds.x1 -= dst->pDrawable->x;
101703b705cfSriastradh		bounds.y1 -= dst->pDrawable->y;
101803b705cfSriastradh		depth = maskFormat->depth;
101903b705cfSriastradh		format = maskFormat->format | (BitsPerPixel(depth) << 24);
102003b705cfSriastradh
102103b705cfSriastradh		DBG(("%s: mask (%dx%d) depth=%d, format=%08x\n",
102203b705cfSriastradh		     __FUNCTION__, width, height, depth, format));
102303b705cfSriastradh		scratch = sna_pixmap_create_upload(screen,
102403b705cfSriastradh						   width, height, depth,
102503b705cfSriastradh						   KGEM_BUFFER_WRITE);
102603b705cfSriastradh		if (!scratch)
102703b705cfSriastradh			return;
102803b705cfSriastradh
102903b705cfSriastradh		memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
103003b705cfSriastradh		image = pixman_image_create_bits(format, width, height,
103103b705cfSriastradh						 scratch->devPrivate.ptr,
103203b705cfSriastradh						 scratch->devKind);
103303b705cfSriastradh		if (image) {
103403b705cfSriastradh			xTriangle tri;
103503b705cfSriastradh			xPointFixed *p[3] = { &tri.p1, &tri.p2, &tri.p3 };
103603b705cfSriastradh			int i;
103703b705cfSriastradh
103803b705cfSriastradh			*p[0] = points[0];
103903b705cfSriastradh			*p[1] = points[1];
104003b705cfSriastradh			*p[2] = points[2];
104103b705cfSriastradh			pixman_add_triangles(image,
104203b705cfSriastradh					     -bounds.x1, -bounds.y1,
104303b705cfSriastradh					     1, (pixman_triangle_t *)&tri);
104403b705cfSriastradh			for (i = 3; i < n; i++) {
104503b705cfSriastradh				*p[i%3] = points[i];
104603b705cfSriastradh				pixman_add_triangles(image,
104703b705cfSriastradh						     -bounds.x1, -bounds.y1,
104803b705cfSriastradh						     1, (pixman_triangle_t *)&tri);
104903b705cfSriastradh			}
105003b705cfSriastradh			pixman_image_unref(image);
105103b705cfSriastradh		}
105203b705cfSriastradh
105303b705cfSriastradh		mask = CreatePicture(0, &scratch->drawable,
105403b705cfSriastradh				     PictureMatchFormat(screen, depth, format),
105503b705cfSriastradh				     0, 0, serverClient, &error);
105603b705cfSriastradh		if (mask) {
105703b705cfSriastradh			CompositePicture(op, src, mask, dst,
105803b705cfSriastradh					 xSrc + bounds.x1 - dst_x,
105903b705cfSriastradh					 ySrc + bounds.y1 - dst_y,
106003b705cfSriastradh					 0, 0,
106103b705cfSriastradh					 bounds.x1, bounds.y1,
106203b705cfSriastradh					 width, height);
106303b705cfSriastradh			FreePicture(mask, 0);
106403b705cfSriastradh		}
106503b705cfSriastradh		sna_pixmap_destroy(scratch);
106603b705cfSriastradh	} else {
106703b705cfSriastradh		xTriangle tri;
106803b705cfSriastradh		xPointFixed *p[3] = { &tri.p1, &tri.p2, &tri.p3 };
106903b705cfSriastradh		int i;
107003b705cfSriastradh
107103b705cfSriastradh		if (dst->polyEdge == PolyEdgeSharp)
107203b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 1, PICT_a1);
107303b705cfSriastradh		else
107403b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 8, PICT_a8);
107503b705cfSriastradh
107603b705cfSriastradh		*p[0] = points[0];
107703b705cfSriastradh		*p[1] = points[1];
107803b705cfSriastradh		*p[2] = points[2];
107903b705cfSriastradh		triangles_fallback(op,
108003b705cfSriastradh				   src, dst, maskFormat,
108103b705cfSriastradh				   xSrc, ySrc, 1, &tri);
108203b705cfSriastradh		for (i = 3; i < n; i++) {
108303b705cfSriastradh			*p[i%3] = points[i];
108403b705cfSriastradh			/* Should xSrc,ySrc be updated? */
108503b705cfSriastradh			triangles_fallback(op,
108603b705cfSriastradh					   src, dst, maskFormat,
108703b705cfSriastradh					   xSrc, ySrc, 1, &tri);
108803b705cfSriastradh		}
108903b705cfSriastradh	}
109003b705cfSriastradh}
109103b705cfSriastradh
109203b705cfSriastradhvoid
109303b705cfSriastradhsna_composite_tristrip(CARD8 op,
109403b705cfSriastradh		       PicturePtr src,
109503b705cfSriastradh		       PicturePtr dst,
109603b705cfSriastradh		       PictFormatPtr maskFormat,
109703b705cfSriastradh		       INT16 xSrc, INT16 ySrc,
109803b705cfSriastradh		       int n, xPointFixed *points)
109903b705cfSriastradh{
110003b705cfSriastradh	struct sna *sna = to_sna_from_drawable(dst->pDrawable);
110103b705cfSriastradh
110203b705cfSriastradh	if (tristrip_span_converter(sna, op, src, dst, maskFormat, xSrc, ySrc, n, points))
110303b705cfSriastradh		return;
110403b705cfSriastradh
110503b705cfSriastradh	tristrip_fallback(op, src, dst, maskFormat, xSrc, ySrc, n, points);
110603b705cfSriastradh}
110703b705cfSriastradh
110803b705cfSriastradhstatic void
110903b705cfSriastradhtrifan_fallback(CARD8 op,
111003b705cfSriastradh		PicturePtr src,
111103b705cfSriastradh		PicturePtr dst,
111203b705cfSriastradh		PictFormatPtr maskFormat,
111303b705cfSriastradh		INT16 xSrc, INT16 ySrc,
111403b705cfSriastradh		int n, xPointFixed *points)
111503b705cfSriastradh{
111603b705cfSriastradh	ScreenPtr screen = dst->pDrawable->pScreen;
111703b705cfSriastradh
111803b705cfSriastradh	if (maskFormat) {
111903b705cfSriastradh		PixmapPtr scratch;
112003b705cfSriastradh		PicturePtr mask;
112103b705cfSriastradh		INT16 dst_x, dst_y;
112203b705cfSriastradh		BoxRec bounds;
112303b705cfSriastradh		int width, height, depth;
112403b705cfSriastradh		pixman_image_t *image;
112503b705cfSriastradh		pixman_format_code_t format;
112603b705cfSriastradh		int error;
112703b705cfSriastradh
112803b705cfSriastradh		dst_x = pixman_fixed_to_int(points->x);
112903b705cfSriastradh		dst_y = pixman_fixed_to_int(points->y);
113003b705cfSriastradh
113103b705cfSriastradh		miPointFixedBounds(n, points, &bounds);
113203b705cfSriastradh		DBG(("%s: bounds (%d, %d), (%d, %d)\n",
113303b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
113403b705cfSriastradh
113503b705cfSriastradh		if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2)
113603b705cfSriastradh			return;
113703b705cfSriastradh
113803b705cfSriastradh		if (!sna_compute_composite_extents(&bounds,
113903b705cfSriastradh						   src, NULL, dst,
114003b705cfSriastradh						   xSrc, ySrc,
114103b705cfSriastradh						   0, 0,
114203b705cfSriastradh						   bounds.x1, bounds.y1,
114303b705cfSriastradh						   bounds.x2 - bounds.x1,
114403b705cfSriastradh						   bounds.y2 - bounds.y1))
114503b705cfSriastradh			return;
114603b705cfSriastradh
114703b705cfSriastradh		DBG(("%s: extents (%d, %d), (%d, %d)\n",
114803b705cfSriastradh		     __FUNCTION__, bounds.x1, bounds.y1, bounds.x2, bounds.y2));
114903b705cfSriastradh
115003b705cfSriastradh		width  = bounds.x2 - bounds.x1;
115103b705cfSriastradh		height = bounds.y2 - bounds.y1;
115203b705cfSriastradh		bounds.x1 -= dst->pDrawable->x;
115303b705cfSriastradh		bounds.y1 -= dst->pDrawable->y;
115403b705cfSriastradh		depth = maskFormat->depth;
115503b705cfSriastradh		format = maskFormat->format | (BitsPerPixel(depth) << 24);
115603b705cfSriastradh
115703b705cfSriastradh		DBG(("%s: mask (%dx%d) depth=%d, format=%08x\n",
115803b705cfSriastradh		     __FUNCTION__, width, height, depth, format));
115903b705cfSriastradh		scratch = sna_pixmap_create_upload(screen,
116003b705cfSriastradh						   width, height, depth,
116103b705cfSriastradh						   KGEM_BUFFER_WRITE);
116203b705cfSriastradh		if (!scratch)
116303b705cfSriastradh			return;
116403b705cfSriastradh
116503b705cfSriastradh		memset(scratch->devPrivate.ptr, 0, scratch->devKind*height);
116603b705cfSriastradh		image = pixman_image_create_bits(format, width, height,
116703b705cfSriastradh						 scratch->devPrivate.ptr,
116803b705cfSriastradh						 scratch->devKind);
116903b705cfSriastradh		if (image) {
117003b705cfSriastradh			xTriangle tri;
117103b705cfSriastradh			xPointFixed *p[3] = { &tri.p1, &tri.p2, &tri.p3 };
117203b705cfSriastradh			int i;
117303b705cfSriastradh
117403b705cfSriastradh			*p[0] = points[0];
117503b705cfSriastradh			*p[1] = points[1];
117603b705cfSriastradh			*p[2] = points[2];
117703b705cfSriastradh			pixman_add_triangles(image,
117803b705cfSriastradh					     -bounds.x1, -bounds.y1,
117903b705cfSriastradh					     1, (pixman_triangle_t *)&tri);
118003b705cfSriastradh			for (i = 3; i < n; i++) {
118103b705cfSriastradh				*p[2 - (i&1)] = points[i];
118203b705cfSriastradh				pixman_add_triangles(image,
118303b705cfSriastradh						     -bounds.x1, -bounds.y1,
118403b705cfSriastradh						     1, (pixman_triangle_t *)&tri);
118503b705cfSriastradh			}
118603b705cfSriastradh			pixman_image_unref(image);
118703b705cfSriastradh		}
118803b705cfSriastradh
118903b705cfSriastradh		mask = CreatePicture(0, &scratch->drawable,
119003b705cfSriastradh				     PictureMatchFormat(screen, depth, format),
119103b705cfSriastradh				     0, 0, serverClient, &error);
119203b705cfSriastradh		if (mask) {
119303b705cfSriastradh			CompositePicture(op, src, mask, dst,
119403b705cfSriastradh					 xSrc + bounds.x1 - dst_x,
119503b705cfSriastradh					 ySrc + bounds.y1 - dst_y,
119603b705cfSriastradh					 0, 0,
119703b705cfSriastradh					 bounds.x1, bounds.y1,
119803b705cfSriastradh					 width, height);
119903b705cfSriastradh			FreePicture(mask, 0);
120003b705cfSriastradh		}
120103b705cfSriastradh		sna_pixmap_destroy(scratch);
120203b705cfSriastradh	} else {
120303b705cfSriastradh		xTriangle tri;
120403b705cfSriastradh		xPointFixed *p[3] = { &tri.p1, &tri.p2, &tri.p3 };
120503b705cfSriastradh		int i;
120603b705cfSriastradh
120703b705cfSriastradh		if (dst->polyEdge == PolyEdgeSharp)
120803b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 1, PICT_a1);
120903b705cfSriastradh		else
121003b705cfSriastradh			maskFormat = PictureMatchFormat(screen, 8, PICT_a8);
121103b705cfSriastradh
121203b705cfSriastradh		*p[0] = points[0];
121303b705cfSriastradh		*p[1] = points[1];
121403b705cfSriastradh		*p[2] = points[2];
121503b705cfSriastradh		triangles_fallback(op,
121603b705cfSriastradh				   src, dst, maskFormat,
121703b705cfSriastradh				   xSrc, ySrc, 1, &tri);
121803b705cfSriastradh		for (i = 3; i < n; i++) {
121903b705cfSriastradh			*p[2 - (i&1)] = points[i];
122003b705cfSriastradh			/* Should xSrc,ySrc be updated? */
122103b705cfSriastradh			triangles_fallback(op,
122203b705cfSriastradh					   src, dst, maskFormat,
122303b705cfSriastradh					   xSrc, ySrc, 1, &tri);
122403b705cfSriastradh		}
122503b705cfSriastradh	}
122603b705cfSriastradh}
122703b705cfSriastradh
122803b705cfSriastradhvoid
122903b705cfSriastradhsna_composite_trifan(CARD8 op,
123003b705cfSriastradh		     PicturePtr src,
123103b705cfSriastradh		     PicturePtr dst,
123203b705cfSriastradh		     PictFormatPtr maskFormat,
123303b705cfSriastradh		     INT16 xSrc, INT16 ySrc,
123403b705cfSriastradh		     int n, xPointFixed *points)
123503b705cfSriastradh{
123603b705cfSriastradh	trifan_fallback(op, src, dst, maskFormat, xSrc, ySrc, n, points);
123703b705cfSriastradh}
123803b705cfSriastradh#endif
1239