1428d7b3dSmrg/*
2428d7b3dSmrg * Copyright (c) 2011 Intel Corporation
3428d7b3dSmrg *
4428d7b3dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
5428d7b3dSmrg * copy of this software and associated documentation files (the "Software"),
6428d7b3dSmrg * to deal in the Software without restriction, including without limitation
7428d7b3dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8428d7b3dSmrg * and/or sell copies of the Software, and to permit persons to whom the
9428d7b3dSmrg * Software is furnished to do so, subject to the following conditions:
10428d7b3dSmrg *
11428d7b3dSmrg * The above copyright notice and this permission notice (including the next
12428d7b3dSmrg * paragraph) shall be included in all copies or substantial portions of the
13428d7b3dSmrg * Software.
14428d7b3dSmrg *
15428d7b3dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16428d7b3dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17428d7b3dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18428d7b3dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19428d7b3dSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20428d7b3dSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21428d7b3dSmrg * SOFTWARE.
22428d7b3dSmrg *
23428d7b3dSmrg * Authors:
24428d7b3dSmrg *    Chris Wilson <chris@chris-wilson.co.uk>
25428d7b3dSmrg *
26428d7b3dSmrg */
27428d7b3dSmrg
28428d7b3dSmrg#ifdef HAVE_CONFIG_H
29428d7b3dSmrg#include "config.h"
30428d7b3dSmrg#endif
31428d7b3dSmrg
32428d7b3dSmrg#include "sna.h"
33428d7b3dSmrg#include "sna_render.h"
34428d7b3dSmrg#include "sna_render_inline.h"
35428d7b3dSmrg#include "fb/fbpict.h"
36428d7b3dSmrg
37428d7b3dSmrg#include <mipict.h>
38428d7b3dSmrg
39428d7b3dSmrg#define NO_COMPOSITE 0
40428d7b3dSmrg#define NO_COMPOSITE_RECTANGLES 0
41428d7b3dSmrg
42428d7b3dSmrg#define BOUND(v)	(INT16) ((v) < MINSHORT ? MINSHORT : (v) > MAXSHORT ? MAXSHORT : (v))
43428d7b3dSmrg
44428d7b3dSmrgbool sna_composite_create(struct sna *sna)
45428d7b3dSmrg{
46428d7b3dSmrg	xRenderColor color = { 0 };
47428d7b3dSmrg	int error;
48428d7b3dSmrg
49428d7b3dSmrg	sna->clear = CreateSolidPicture(0, &color, &error);
50428d7b3dSmrg	return sna->clear != NULL;
51428d7b3dSmrg}
52428d7b3dSmrg
53428d7b3dSmrgvoid sna_composite_close(struct sna *sna)
54428d7b3dSmrg{
55428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
56428d7b3dSmrg
57428d7b3dSmrg	if (sna->clear) {
58428d7b3dSmrg		FreePicture(sna->clear, 0);
59428d7b3dSmrg		sna->clear = NULL;
60428d7b3dSmrg	}
61428d7b3dSmrg}
62428d7b3dSmrg
63428d7b3dSmrgstatic inline bool
64428d7b3dSmrgregion_is_singular(pixman_region16_t *region)
65428d7b3dSmrg{
66428d7b3dSmrg	return region->data == NULL;
67428d7b3dSmrg}
68428d7b3dSmrg
69428d7b3dSmrgstatic inline bool
70428d7b3dSmrgregion_is_empty(pixman_region16_t *region)
71428d7b3dSmrg{
72428d7b3dSmrg	return region->data && region->data->numRects == 0;
73428d7b3dSmrg}
74428d7b3dSmrg
75428d7b3dSmrgstatic inline pixman_bool_t
76428d7b3dSmrgclip_to_dst(pixman_region16_t *region,
77428d7b3dSmrg	    pixman_region16_t *clip,
78428d7b3dSmrg	    int		dx,
79428d7b3dSmrg	    int		dy)
80428d7b3dSmrg{
81428d7b3dSmrg	DBG(("%s: region: %dx[(%d, %d), (%d, %d)], clip: %dx[(%d, %d), (%d, %d)]\n",
82428d7b3dSmrg	     __FUNCTION__,
83428d7b3dSmrg	     pixman_region_n_rects(region),
84428d7b3dSmrg	     region->extents.x1, region->extents.y1,
85428d7b3dSmrg	     region->extents.x2, region->extents.y2,
86428d7b3dSmrg	     pixman_region_n_rects(clip),
87428d7b3dSmrg	     clip->extents.x1, clip->extents.y1,
88428d7b3dSmrg	     clip->extents.x2, clip->extents.y2));
89428d7b3dSmrg
90428d7b3dSmrg	if (region_is_singular(region) && region_is_singular(clip)) {
91428d7b3dSmrg		pixman_box16_t *r = &region->extents;
92428d7b3dSmrg		pixman_box16_t *c = &clip->extents;
93428d7b3dSmrg		int v;
94428d7b3dSmrg
95428d7b3dSmrg		if (r->x1 < (v = c->x1 + dx))
96428d7b3dSmrg			r->x1 = BOUND(v);
97428d7b3dSmrg		if (r->x2 > (v = c->x2 + dx))
98428d7b3dSmrg			r->x2 = BOUND(v);
99428d7b3dSmrg		if (r->y1 < (v = c->y1 + dy))
100428d7b3dSmrg			r->y1 = BOUND(v);
101428d7b3dSmrg		if (r->y2 > (v = c->y2 + dy))
102428d7b3dSmrg			r->y2 = BOUND(v);
103428d7b3dSmrg
104428d7b3dSmrg		if (r->x1 >= r->x2 || r->y1 >= r->y2) {
105428d7b3dSmrg			pixman_region_init(region);
106428d7b3dSmrg			return FALSE;
107428d7b3dSmrg		}
108428d7b3dSmrg
109428d7b3dSmrg		return true;
110428d7b3dSmrg	} else if (region_is_empty(clip)) {
111428d7b3dSmrg		return FALSE;
112428d7b3dSmrg	} else {
113428d7b3dSmrg		if (dx | dy)
114428d7b3dSmrg			pixman_region_translate(region, -dx, -dy);
115428d7b3dSmrg		if (!pixman_region_intersect(region, region, clip))
116428d7b3dSmrg			return FALSE;
117428d7b3dSmrg		if (dx | dy)
118428d7b3dSmrg			pixman_region_translate(region, dx, dy);
119428d7b3dSmrg
120428d7b3dSmrg		return !region_is_empty(region);
121428d7b3dSmrg	}
122428d7b3dSmrg}
123428d7b3dSmrg
124428d7b3dSmrgstatic inline bool
125428d7b3dSmrgpicture_has_clip(PicturePtr p)
126428d7b3dSmrg{
127428d7b3dSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,16,99,1,0)
128428d7b3dSmrg	return p->clientClip;
129428d7b3dSmrg#else
130428d7b3dSmrg	return p->clientClipType != CT_NONE;
131428d7b3dSmrg#endif
132428d7b3dSmrg}
133428d7b3dSmrg
134428d7b3dSmrgstatic inline bool
135428d7b3dSmrgclip_to_src(RegionPtr region, PicturePtr p, int dx, int dy)
136428d7b3dSmrg{
137428d7b3dSmrg	bool result;
138428d7b3dSmrg
139428d7b3dSmrg	if (!picture_has_clip(p))
140428d7b3dSmrg		return true;
141428d7b3dSmrg
142428d7b3dSmrg	pixman_region_translate(p->clientClip,
143428d7b3dSmrg				p->clipOrigin.x + dx,
144428d7b3dSmrg				p->clipOrigin.y + dy);
145428d7b3dSmrg
146428d7b3dSmrg	result = RegionIntersect(region, region, p->clientClip);
147428d7b3dSmrg
148428d7b3dSmrg	pixman_region_translate(p->clientClip,
149428d7b3dSmrg				-(p->clipOrigin.x + dx),
150428d7b3dSmrg				-(p->clipOrigin.y + dy));
151428d7b3dSmrg
152428d7b3dSmrg	return result && !region_is_empty(region);
153428d7b3dSmrg}
154428d7b3dSmrg
155428d7b3dSmrgbool
156428d7b3dSmrgsna_compute_composite_region(RegionPtr region,
157428d7b3dSmrg			     PicturePtr src, PicturePtr mask, PicturePtr dst,
158428d7b3dSmrg			     INT16 src_x,  INT16 src_y,
159428d7b3dSmrg			     INT16 mask_x, INT16 mask_y,
160428d7b3dSmrg			     INT16 dst_x,  INT16 dst_y,
161428d7b3dSmrg			     CARD16 width, CARD16 height)
162428d7b3dSmrg{
163428d7b3dSmrg	int v;
164428d7b3dSmrg
165428d7b3dSmrg	DBG(("%s: dst=(%d, %d)x(%d, %d)\n",
166428d7b3dSmrg	     __FUNCTION__,
167428d7b3dSmrg	     dst_x, dst_y,
168428d7b3dSmrg	     width, height));
169428d7b3dSmrg
170428d7b3dSmrg	region->extents.x1 = dst_x < 0 ? 0 : dst_x;
171428d7b3dSmrg	v = dst_x + width;
172428d7b3dSmrg	if (v > dst->pDrawable->width)
173428d7b3dSmrg		v = dst->pDrawable->width;
174428d7b3dSmrg	region->extents.x2 = v;
175428d7b3dSmrg
176428d7b3dSmrg	region->extents.y1 = dst_y < 0 ? 0 : dst_y;
177428d7b3dSmrg	v = dst_y + height;
178428d7b3dSmrg	if (v > dst->pDrawable->height)
179428d7b3dSmrg		v = dst->pDrawable->height;
180428d7b3dSmrg	region->extents.y2 = v;
181428d7b3dSmrg
182428d7b3dSmrg	region->data = 0;
183428d7b3dSmrg
184428d7b3dSmrg	DBG(("%s: initial clip against dst->pDrawable: (%d, %d), (%d, %d)\n",
185428d7b3dSmrg	     __FUNCTION__,
186428d7b3dSmrg	     region->extents.x1, region->extents.y1,
187428d7b3dSmrg	     region->extents.x2, region->extents.y2));
188428d7b3dSmrg
189428d7b3dSmrg	if (region->extents.x1 >= region->extents.x2 ||
190428d7b3dSmrg	    region->extents.y1 >= region->extents.y2)
191428d7b3dSmrg		return false;
192428d7b3dSmrg
193428d7b3dSmrg	region->extents.x1 += dst->pDrawable->x;
194428d7b3dSmrg	region->extents.x2 += dst->pDrawable->x;
195428d7b3dSmrg	region->extents.y1 += dst->pDrawable->y;
196428d7b3dSmrg	region->extents.y2 += dst->pDrawable->y;
197428d7b3dSmrg
198428d7b3dSmrg	dst_x += dst->pDrawable->x;
199428d7b3dSmrg	dst_y += dst->pDrawable->y;
200428d7b3dSmrg
201428d7b3dSmrg	/* clip against dst */
202428d7b3dSmrg	if (!clip_to_dst(region, dst->pCompositeClip, 0, 0))
203428d7b3dSmrg		return false;
204428d7b3dSmrg
205428d7b3dSmrg	DBG(("%s: clip against dst->pCompositeClip: (%d, %d), (%d, %d)\n",
206428d7b3dSmrg	     __FUNCTION__,
207428d7b3dSmrg	     region->extents.x1, region->extents.y1,
208428d7b3dSmrg	     region->extents.x2, region->extents.y2));
209428d7b3dSmrg
210428d7b3dSmrg	if (dst->alphaMap) {
211428d7b3dSmrg		if (!clip_to_dst(region, dst->alphaMap->pCompositeClip,
212428d7b3dSmrg				 -dst->alphaOrigin.x,
213428d7b3dSmrg				 -dst->alphaOrigin.y)) {
214428d7b3dSmrg			pixman_region_fini (region);
215428d7b3dSmrg			return false;
216428d7b3dSmrg		}
217428d7b3dSmrg	}
218428d7b3dSmrg
219428d7b3dSmrg	/* clip against src */
220428d7b3dSmrg	if (src) {
221428d7b3dSmrg		if (src->pDrawable) {
222428d7b3dSmrg			src_x += src->pDrawable->x;
223428d7b3dSmrg			src_y += src->pDrawable->y;
224428d7b3dSmrg		}
225428d7b3dSmrg		if (!clip_to_src(region, src, dst_x - src_x, dst_y - src_y)) {
226428d7b3dSmrg			pixman_region_fini (region);
227428d7b3dSmrg			return false;
228428d7b3dSmrg		}
229428d7b3dSmrg		DBG(("%s: clip against src (%dx%d clip=%d): (%d, %d), (%d, %d)\n",
230428d7b3dSmrg		       __FUNCTION__,
231428d7b3dSmrg		       src->pDrawable ? src->pDrawable->width : 0,
232428d7b3dSmrg		       src->pDrawable ? src->pDrawable->height : 0,
233428d7b3dSmrg		       picture_has_clip(src),
234428d7b3dSmrg		       region->extents.x1, region->extents.y1,
235428d7b3dSmrg		       region->extents.x2, region->extents.y2));
236428d7b3dSmrg
237428d7b3dSmrg		if (src->alphaMap) {
238428d7b3dSmrg			if (!clip_to_src(region, src->alphaMap,
239428d7b3dSmrg					 dst_x - (src_x - src->alphaOrigin.x),
240428d7b3dSmrg					 dst_y - (src_y - src->alphaOrigin.y))) {
241428d7b3dSmrg				pixman_region_fini(region);
242428d7b3dSmrg				return false;
243428d7b3dSmrg			}
244428d7b3dSmrg		}
245428d7b3dSmrg	}
246428d7b3dSmrg
247428d7b3dSmrg	/* clip against mask */
248428d7b3dSmrg	if (mask) {
249428d7b3dSmrg		if (mask->pDrawable) {
250428d7b3dSmrg			mask_x += mask->pDrawable->x;
251428d7b3dSmrg			mask_y += mask->pDrawable->y;
252428d7b3dSmrg		}
253428d7b3dSmrg		if (!clip_to_src(region, mask, dst_x - mask_x, dst_y - mask_y)) {
254428d7b3dSmrg			pixman_region_fini(region);
255428d7b3dSmrg			return false;
256428d7b3dSmrg		}
257428d7b3dSmrg		if (mask->alphaMap) {
258428d7b3dSmrg			if (!clip_to_src(region, mask->alphaMap,
259428d7b3dSmrg					 dst_x - (mask_x - mask->alphaOrigin.x),
260428d7b3dSmrg					 dst_y - (mask_y - mask->alphaOrigin.y))) {
261428d7b3dSmrg				pixman_region_fini(region);
262428d7b3dSmrg				return false;
263428d7b3dSmrg			}
264428d7b3dSmrg		}
265428d7b3dSmrg
266428d7b3dSmrg		DBG(("%s: clip against mask: (%d, %d), (%d, %d)\n",
267428d7b3dSmrg		     __FUNCTION__,
268428d7b3dSmrg		     region->extents.x1, region->extents.y1,
269428d7b3dSmrg		     region->extents.x2, region->extents.y2));
270428d7b3dSmrg	}
271428d7b3dSmrg
272428d7b3dSmrg	return !region_is_empty(region);
273428d7b3dSmrg}
274428d7b3dSmrg
275428d7b3dSmrgstatic void
276428d7b3dSmrgtrim_extents(BoxPtr extents, const PicturePtr p, int dx, int dy)
277428d7b3dSmrg{
278428d7b3dSmrg	const BoxPtr box = REGION_EXTENTS(NULL, p->pCompositeClip);
279428d7b3dSmrg
280428d7b3dSmrg	DBG(("%s: trim((%d, %d), (%d, %d)) against ((%d, %d), (%d, %d)) + (%d, %d)\n",
281428d7b3dSmrg	     __FUNCTION__,
282428d7b3dSmrg	     extents->x1, extents->y1, extents->x2, extents->y2,
283428d7b3dSmrg	     box->x1, box->y1, box->x2, box->y2,
284428d7b3dSmrg	     dx, dy));
285428d7b3dSmrg
286428d7b3dSmrg	if (extents->x1 < box->x1 + dx)
287428d7b3dSmrg		extents->x1 = box->x1 + dx;
288428d7b3dSmrg	if (extents->x2 > box->x2 + dx)
289428d7b3dSmrg		extents->x2 = box->x2 + dx;
290428d7b3dSmrg
291428d7b3dSmrg	if (extents->y1 < box->y1 + dy)
292428d7b3dSmrg		extents->y1 = box->y1 + dy;
293428d7b3dSmrg	if (extents->y2 > box->y2 + dy)
294428d7b3dSmrg		extents->y2 = box->y2 + dy;
295428d7b3dSmrg}
296428d7b3dSmrg
297428d7b3dSmrgstatic void
298428d7b3dSmrg_trim_source_extents(BoxPtr extents, const PicturePtr p, int dx, int dy)
299428d7b3dSmrg{
300428d7b3dSmrg	if (picture_has_clip(p))
301428d7b3dSmrg		trim_extents(extents, p, dx, dy);
302428d7b3dSmrg}
303428d7b3dSmrg
304428d7b3dSmrgstatic void
305428d7b3dSmrgtrim_source_extents(BoxPtr extents, const PicturePtr p, int dx, int dy)
306428d7b3dSmrg{
307428d7b3dSmrg	if (p->pDrawable) {
308428d7b3dSmrg		dx += p->pDrawable->x;
309428d7b3dSmrg		dy += p->pDrawable->y;
310428d7b3dSmrg	}
311428d7b3dSmrg	_trim_source_extents(extents, p, dx, dy);
312428d7b3dSmrg	if (p->alphaMap)
313428d7b3dSmrg		_trim_source_extents(extents, p->alphaMap,
314428d7b3dSmrg				     dx - p->alphaOrigin.x,
315428d7b3dSmrg				     dy - p->alphaOrigin.y);
316428d7b3dSmrg
317428d7b3dSmrg	DBG(("%s: -> (%d, %d), (%d, %d)\n",
318428d7b3dSmrg	     __FUNCTION__,
319428d7b3dSmrg	     extents->x1, extents->y1,
320428d7b3dSmrg	     extents->x2, extents->y2));
321428d7b3dSmrg}
322428d7b3dSmrg
323428d7b3dSmrgbool
324428d7b3dSmrgsna_compute_composite_extents(BoxPtr extents,
325428d7b3dSmrg			      PicturePtr src, PicturePtr mask, PicturePtr dst,
326428d7b3dSmrg			      INT16 src_x,  INT16 src_y,
327428d7b3dSmrg			      INT16 mask_x, INT16 mask_y,
328428d7b3dSmrg			      INT16 dst_x,  INT16 dst_y,
329428d7b3dSmrg			      CARD16 width, CARD16 height)
330428d7b3dSmrg{
331428d7b3dSmrg	int v;
332428d7b3dSmrg
333428d7b3dSmrg	DBG(("%s: dst=(%d, %d)x(%d, %d)\n",
334428d7b3dSmrg	     __FUNCTION__,
335428d7b3dSmrg	     dst_x, dst_y,
336428d7b3dSmrg	     width, height));
337428d7b3dSmrg
338428d7b3dSmrg	extents->x1 = dst_x < 0 ? 0 : dst_x;
339428d7b3dSmrg	v = dst_x + width;
340428d7b3dSmrg	if (v > dst->pDrawable->width)
341428d7b3dSmrg		v = dst->pDrawable->width;
342428d7b3dSmrg	extents->x2 = v;
343428d7b3dSmrg
344428d7b3dSmrg	extents->y1 = dst_y < 0 ? 0 : dst_y;
345428d7b3dSmrg	v = dst_y + height;
346428d7b3dSmrg	if (v > dst->pDrawable->height)
347428d7b3dSmrg		v = dst->pDrawable->height;
348428d7b3dSmrg	extents->y2 = v;
349428d7b3dSmrg
350428d7b3dSmrg	DBG(("%s: initial clip against dst->pDrawable: (%d, %d), (%d, %d)\n",
351428d7b3dSmrg	     __FUNCTION__,
352428d7b3dSmrg	     extents->x1, extents->y1,
353428d7b3dSmrg	     extents->x2, extents->y2));
354428d7b3dSmrg
355428d7b3dSmrg	if (extents->x1 >= extents->x2 || extents->y1 >= extents->y2)
356428d7b3dSmrg		return false;
357428d7b3dSmrg
358428d7b3dSmrg	extents->x1 += dst->pDrawable->x;
359428d7b3dSmrg	extents->x2 += dst->pDrawable->x;
360428d7b3dSmrg	extents->y1 += dst->pDrawable->y;
361428d7b3dSmrg	extents->y2 += dst->pDrawable->y;
362428d7b3dSmrg
363428d7b3dSmrg	if (extents->x1 < dst->pCompositeClip->extents.x1)
364428d7b3dSmrg		extents->x1 = dst->pCompositeClip->extents.x1;
365428d7b3dSmrg	if (extents->x2 > dst->pCompositeClip->extents.x2)
366428d7b3dSmrg		extents->x2 = dst->pCompositeClip->extents.x2;
367428d7b3dSmrg
368428d7b3dSmrg	if (extents->y1 < dst->pCompositeClip->extents.y1)
369428d7b3dSmrg		extents->y1 = dst->pCompositeClip->extents.y1;
370428d7b3dSmrg	if (extents->y2 > dst->pCompositeClip->extents.y2)
371428d7b3dSmrg		extents->y2 = dst->pCompositeClip->extents.y2;
372428d7b3dSmrg
373428d7b3dSmrg	DBG(("%s: initial clip against dst->pCompositeClip: (%d, %d), (%d, %d)\n",
374428d7b3dSmrg	     __FUNCTION__,
375428d7b3dSmrg	     extents->x1, extents->y1,
376428d7b3dSmrg	     extents->x2, extents->y2));
377428d7b3dSmrg
378428d7b3dSmrg	if (extents->x1 >= extents->x2 || extents->y1 >= extents->y2)
379428d7b3dSmrg		return false;
380428d7b3dSmrg
381428d7b3dSmrg	dst_x += dst->pDrawable->x;
382428d7b3dSmrg	dst_y += dst->pDrawable->y;
383428d7b3dSmrg
384428d7b3dSmrg	/* clip against dst */
385428d7b3dSmrg	trim_extents(extents, dst, 0, 0);
386428d7b3dSmrg	if (dst->alphaMap)
387428d7b3dSmrg		trim_extents(extents, dst->alphaMap,
388428d7b3dSmrg			     -dst->alphaOrigin.x,
389428d7b3dSmrg			     -dst->alphaOrigin.y);
390428d7b3dSmrg
391428d7b3dSmrg	DBG(("%s: clip against dst: (%d, %d), (%d, %d)\n",
392428d7b3dSmrg	     __FUNCTION__,
393428d7b3dSmrg	     extents->x1, extents->y1,
394428d7b3dSmrg	     extents->x2, extents->y2));
395428d7b3dSmrg
396428d7b3dSmrg	if (src)
397428d7b3dSmrg		trim_source_extents(extents, src, dst_x - src_x, dst_y - src_y);
398428d7b3dSmrg	if (mask)
399428d7b3dSmrg		trim_source_extents(extents, mask,
400428d7b3dSmrg				    dst_x - mask_x, dst_y - mask_y);
401428d7b3dSmrg
402428d7b3dSmrg	if (extents->x1 >= extents->x2 || extents->y1 >= extents->y2)
403428d7b3dSmrg		return false;
404428d7b3dSmrg
405428d7b3dSmrg	if (region_is_singular(dst->pCompositeClip))
406428d7b3dSmrg		return true;
407428d7b3dSmrg
408428d7b3dSmrg	return pixman_region_contains_rectangle(dst->pCompositeClip,
409428d7b3dSmrg						extents) != PIXMAN_REGION_OUT;
410428d7b3dSmrg}
411428d7b3dSmrg
412428d7b3dSmrg#if HAS_DEBUG_FULL
413428d7b3dSmrgstatic void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function)
414428d7b3dSmrg{
415428d7b3dSmrg	if (box->x1 < 0 || box->y1 < 0 ||
416428d7b3dSmrg	    box->x2 > pixmap->drawable.width ||
417428d7b3dSmrg	    box->y2 > pixmap->drawable.height)
418428d7b3dSmrg	{
419428d7b3dSmrg		FatalError("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n",
420428d7b3dSmrg			   function,
421428d7b3dSmrg			   box->x1, box->y1, box->x2, box->y2,
422428d7b3dSmrg			   pixmap->drawable.width,
423428d7b3dSmrg			   pixmap->drawable.height);
424428d7b3dSmrg	}
425428d7b3dSmrg}
426428d7b3dSmrg#define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__)
427428d7b3dSmrg#else
428428d7b3dSmrg#define assert_pixmap_contains_box(p, b)
429428d7b3dSmrg#endif
430428d7b3dSmrg
431428d7b3dSmrgstatic void apply_damage(struct sna_composite_op *op, RegionPtr region)
432428d7b3dSmrg{
433428d7b3dSmrg	DBG(("%s: damage=%p, region=%d [(%d, %d), (%d, %d) + (%d, %d)]\n",
434428d7b3dSmrg	     __FUNCTION__, op->damage, region_num_rects(region),
435428d7b3dSmrg	     region->extents.x1, region->extents.y1,
436428d7b3dSmrg	     region->extents.x2, region->extents.y2,
437428d7b3dSmrg	     op->dst.x, op->dst.y));
438428d7b3dSmrg
439428d7b3dSmrg	if (op->damage == NULL)
440428d7b3dSmrg		return;
441428d7b3dSmrg
442428d7b3dSmrg	if (op->dst.x | op->dst.y)
443428d7b3dSmrg		RegionTranslate(region, op->dst.x, op->dst.y);
444428d7b3dSmrg
445428d7b3dSmrg	assert_pixmap_contains_box(op->dst.pixmap, RegionExtents(region));
446428d7b3dSmrg	if (region->data == NULL &&
447428d7b3dSmrg	    region->extents.x2 - region->extents.x1 == op->dst.width &&
448428d7b3dSmrg	    region->extents.y2 - region->extents.y1 == op->dst.height) {
449428d7b3dSmrg		*op->damage = _sna_damage_all(*op->damage,
450428d7b3dSmrg					      op->dst.width,
451428d7b3dSmrg					      op->dst.height);
452428d7b3dSmrg		op->damage = NULL;
453428d7b3dSmrg	} else
454428d7b3dSmrg		sna_damage_add(op->damage, region);
455428d7b3dSmrg}
456428d7b3dSmrg
457428d7b3dSmrgstatic inline bool use_cpu(PixmapPtr pixmap, struct sna_pixmap *priv,
458428d7b3dSmrg			   CARD8 op, INT16 width, INT16 height)
459428d7b3dSmrg{
460428d7b3dSmrg	if (priv->cpu_bo && kgem_bo_is_busy(priv->cpu_bo))
461428d7b3dSmrg		return false;
462428d7b3dSmrg
463428d7b3dSmrg	if (DAMAGE_IS_ALL(priv->cpu_damage) &&
464428d7b3dSmrg	    (op > PictOpSrc ||
465428d7b3dSmrg	     width  < pixmap->drawable.width ||
466428d7b3dSmrg	     height < pixmap->drawable.height))
467428d7b3dSmrg		return true;
468428d7b3dSmrg
469428d7b3dSmrg	if (priv->gpu_bo)
470428d7b3dSmrg		return false;
471428d7b3dSmrg
472428d7b3dSmrg	return (priv->create & KGEM_CAN_CREATE_GPU) == 0;
473428d7b3dSmrg}
474428d7b3dSmrg
475428d7b3dSmrgstatic void validate_source(PicturePtr picture)
476428d7b3dSmrg{
477428d7b3dSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,10,99,901,0)
478428d7b3dSmrg	miCompositeSourceValidate(picture);
479428d7b3dSmrg#else
480428d7b3dSmrg	miCompositeSourceValidate(picture,
481428d7b3dSmrg				  0, 0,
482428d7b3dSmrg				  picture->pDrawable ? picture->pDrawable->width : 0,
483428d7b3dSmrg				  picture->pDrawable ? picture->pDrawable->height : 0);
484428d7b3dSmrg#endif
485428d7b3dSmrg}
486428d7b3dSmrg
487428d7b3dSmrgvoid
488428d7b3dSmrgsna_composite_fb(CARD8 op,
489428d7b3dSmrg		 PicturePtr src,
490428d7b3dSmrg		 PicturePtr mask,
491428d7b3dSmrg		 PicturePtr dst,
492428d7b3dSmrg		 RegionPtr region,
493428d7b3dSmrg		 INT16 src_x, INT16 src_y,
494428d7b3dSmrg		 INT16 msk_x, INT16 msk_y,
495428d7b3dSmrg		 INT16 dst_x, INT16 dst_y,
496428d7b3dSmrg		 CARD16 width, CARD16 height)
497428d7b3dSmrg{
498428d7b3dSmrg	pixman_image_t *src_image, *mask_image, *dest_image;
499428d7b3dSmrg	int src_xoff, src_yoff;
500428d7b3dSmrg	int msk_xoff, msk_yoff;
501428d7b3dSmrg	int dst_xoff, dst_yoff;
502428d7b3dSmrg	int16_t tx, ty;
503428d7b3dSmrg	unsigned flags;
504428d7b3dSmrg
505428d7b3dSmrg	DBG(("%s -- op=%d, fallback dst=(%d, %d)+(%d, %d), size=(%d, %d): region=((%d,%d), (%d, %d))\n",
506428d7b3dSmrg	     __FUNCTION__, op,
507428d7b3dSmrg	     dst_x, dst_y,
508428d7b3dSmrg	     dst->pDrawable->x, dst->pDrawable->y,
509428d7b3dSmrg	     width, height,
510428d7b3dSmrg	     region->extents.x1, region->extents.y1,
511428d7b3dSmrg	     region->extents.x2, region->extents.y2));
512428d7b3dSmrg
513428d7b3dSmrg	if (src->pDrawable) {
514428d7b3dSmrg		DBG(("%s: fallback -- move src to cpu\n", __FUNCTION__));
515428d7b3dSmrg		if (!sna_drawable_move_to_cpu(src->pDrawable,
516428d7b3dSmrg					      MOVE_READ))
517428d7b3dSmrg			return;
518428d7b3dSmrg
519428d7b3dSmrg		if (src->alphaMap &&
520428d7b3dSmrg		    !sna_drawable_move_to_cpu(src->alphaMap->pDrawable,
521428d7b3dSmrg					      MOVE_READ))
522428d7b3dSmrg			return;
523428d7b3dSmrg	}
524428d7b3dSmrg
525428d7b3dSmrg	validate_source(src);
526428d7b3dSmrg
527428d7b3dSmrg	if (mask) {
528428d7b3dSmrg		if (mask->pDrawable) {
529428d7b3dSmrg			DBG(("%s: fallback -- move mask to cpu\n", __FUNCTION__));
530428d7b3dSmrg			if (!sna_drawable_move_to_cpu(mask->pDrawable,
531428d7b3dSmrg						      MOVE_READ))
532428d7b3dSmrg				return;
533428d7b3dSmrg
534428d7b3dSmrg			if (mask->alphaMap &&
535428d7b3dSmrg			    !sna_drawable_move_to_cpu(mask->alphaMap->pDrawable,
536428d7b3dSmrg						      MOVE_READ))
537428d7b3dSmrg				return;
538428d7b3dSmrg		}
539428d7b3dSmrg
540428d7b3dSmrg		validate_source(mask);
541428d7b3dSmrg	}
542428d7b3dSmrg
543428d7b3dSmrg	DBG(("%s: fallback -- move dst to cpu\n", __FUNCTION__));
544428d7b3dSmrg	if (op <= PictOpSrc && !dst->alphaMap)
545428d7b3dSmrg		flags = MOVE_WRITE | MOVE_INPLACE_HINT;
546428d7b3dSmrg	else
547428d7b3dSmrg		flags = MOVE_WRITE | MOVE_READ;
548428d7b3dSmrg	if (!sna_drawable_move_region_to_cpu(dst->pDrawable, region, flags))
549428d7b3dSmrg		return;
550428d7b3dSmrg	if (dst->alphaMap &&
551428d7b3dSmrg	    !sna_drawable_move_to_cpu(dst->alphaMap->pDrawable, flags))
552428d7b3dSmrg		return;
553428d7b3dSmrg
554428d7b3dSmrg	if (mask == NULL &&
555428d7b3dSmrg	    src->pDrawable &&
556428d7b3dSmrg	    dst->pDrawable->bitsPerPixel >= 8 &&
557428d7b3dSmrg	    src->filter != PictFilterConvolution &&
558428d7b3dSmrg	    (op == PictOpSrc || (op == PictOpOver && !PICT_FORMAT_A(src->format))) &&
559428d7b3dSmrg	    (dst->format == src->format || dst->format == alphaless(src->format)) &&
560428d7b3dSmrg	    sna_transform_is_imprecise_integer_translation(src->transform, src->filter,
561428d7b3dSmrg							   dst->polyMode == PolyModePrecise,
562428d7b3dSmrg							   &tx, &ty)) {
563428d7b3dSmrg		PixmapPtr dst_pixmap = get_drawable_pixmap(dst->pDrawable);
564428d7b3dSmrg		PixmapPtr src_pixmap = get_drawable_pixmap(src->pDrawable);
565428d7b3dSmrg		int16_t sx = src_x + tx - (dst->pDrawable->x + dst_x);
566428d7b3dSmrg		int16_t sy = src_y + ty - (dst->pDrawable->y + dst_y);
567428d7b3dSmrg
568428d7b3dSmrg		assert(src->pDrawable->bitsPerPixel == dst->pDrawable->bitsPerPixel);
569428d7b3dSmrg		assert(src_pixmap->drawable.bitsPerPixel == dst_pixmap->drawable.bitsPerPixel);
570428d7b3dSmrg
571428d7b3dSmrg		if (region->extents.x1 + sx >= 0 &&
572428d7b3dSmrg		    region->extents.y1 + sy >= 0 &&
573428d7b3dSmrg		    region->extents.x2 + sx <= src->pDrawable->width &&
574428d7b3dSmrg		    region->extents.y2 + sy <= src->pDrawable->height) {
575428d7b3dSmrg			if (sigtrap_get() == 0) {
576428d7b3dSmrg				const BoxRec *box = region_rects(region);
577428d7b3dSmrg				int nbox = region_num_rects(region);
578428d7b3dSmrg
579428d7b3dSmrg				sx += src->pDrawable->x;
580428d7b3dSmrg				sy += src->pDrawable->y;
581428d7b3dSmrg				if (get_drawable_deltas(src->pDrawable, src_pixmap, &tx, &ty))
582428d7b3dSmrg					sx += tx, sy += ty;
583428d7b3dSmrg
584428d7b3dSmrg				assert(region->extents.x1 + sx >= 0);
585428d7b3dSmrg				assert(region->extents.x2 + sx <= src_pixmap->drawable.width);
586428d7b3dSmrg				assert(region->extents.y1 + sy >= 0);
587428d7b3dSmrg				assert(region->extents.y2 + sy <= src_pixmap->drawable.height);
588428d7b3dSmrg
589428d7b3dSmrg				get_drawable_deltas(dst->pDrawable, dst_pixmap, &tx, &ty);
590428d7b3dSmrg
591428d7b3dSmrg				assert(nbox);
592428d7b3dSmrg				do {
593428d7b3dSmrg					assert(box->x1 + sx >= 0);
594428d7b3dSmrg					assert(box->x2 + sx <= src_pixmap->drawable.width);
595428d7b3dSmrg					assert(box->y1 + sy >= 0);
596428d7b3dSmrg					assert(box->y2 + sy <= src_pixmap->drawable.height);
597428d7b3dSmrg
598428d7b3dSmrg					assert(box->x1 + tx >= 0);
599428d7b3dSmrg					assert(box->x2 + tx <= dst_pixmap->drawable.width);
600428d7b3dSmrg					assert(box->y1 + ty >= 0);
601428d7b3dSmrg					assert(box->y2 + ty <= dst_pixmap->drawable.height);
602428d7b3dSmrg
603428d7b3dSmrg					assert(box->x2 > box->x1 && box->y2 > box->y1);
604428d7b3dSmrg
605428d7b3dSmrg					sigtrap_assert_active();
606428d7b3dSmrg					memcpy_blt(src_pixmap->devPrivate.ptr,
607428d7b3dSmrg						   dst_pixmap->devPrivate.ptr,
608428d7b3dSmrg						   dst_pixmap->drawable.bitsPerPixel,
609428d7b3dSmrg						   src_pixmap->devKind,
610428d7b3dSmrg						   dst_pixmap->devKind,
611428d7b3dSmrg						   box->x1 + sx, box->y1 + sy,
612428d7b3dSmrg						   box->x1 + tx, box->y1 + ty,
613428d7b3dSmrg						   box->x2 - box->x1, box->y2 - box->y1);
614428d7b3dSmrg					box++;
615428d7b3dSmrg				} while (--nbox);
616428d7b3dSmrg				sigtrap_put();
617428d7b3dSmrg			}
618428d7b3dSmrg
619428d7b3dSmrg			return;
620428d7b3dSmrg		}
621428d7b3dSmrg	}
622428d7b3dSmrg
623428d7b3dSmrg	src_image = image_from_pict(src, FALSE, &src_xoff, &src_yoff);
624428d7b3dSmrg	mask_image = image_from_pict(mask, FALSE, &msk_xoff, &msk_yoff);
625428d7b3dSmrg	dest_image = image_from_pict(dst, TRUE, &dst_xoff, &dst_yoff);
626428d7b3dSmrg
627428d7b3dSmrg	if (src_image && dest_image && !(mask && !mask_image))
628428d7b3dSmrg		sna_image_composite(op, src_image, mask_image, dest_image,
629428d7b3dSmrg				    src_x + src_xoff, src_y + src_yoff,
630428d7b3dSmrg				    msk_x + msk_xoff, msk_y + msk_yoff,
631428d7b3dSmrg				    dst_x + dst_xoff, dst_y + dst_yoff,
632428d7b3dSmrg				    width, height);
633428d7b3dSmrg
634428d7b3dSmrg	free_pixman_pict(src, src_image);
635428d7b3dSmrg	free_pixman_pict(mask, mask_image);
636428d7b3dSmrg	free_pixman_pict(dst, dest_image);
637428d7b3dSmrg}
638428d7b3dSmrg
639428d7b3dSmrgvoid
640428d7b3dSmrgsna_composite(CARD8 op,
641428d7b3dSmrg	      PicturePtr src,
642428d7b3dSmrg	      PicturePtr mask,
643428d7b3dSmrg	      PicturePtr dst,
644428d7b3dSmrg	      INT16 src_x,  INT16 src_y,
645428d7b3dSmrg	      INT16 mask_x, INT16 mask_y,
646428d7b3dSmrg	      INT16 dst_x,  INT16 dst_y,
647428d7b3dSmrg	      CARD16 width, CARD16 height)
648428d7b3dSmrg{
649428d7b3dSmrg	PixmapPtr pixmap = get_drawable_pixmap(dst->pDrawable);
650428d7b3dSmrg	struct sna *sna = to_sna_from_pixmap(pixmap);
651428d7b3dSmrg	struct sna_pixmap *priv;
652428d7b3dSmrg	struct sna_composite_op tmp;
653428d7b3dSmrg	RegionRec region;
654428d7b3dSmrg	int dx, dy;
655428d7b3dSmrg
656428d7b3dSmrg	DBG(("%s(%d src=%ld+(%d, %d), mask=%ld+(%d, %d), dst=%ld+(%d, %d)+(%d, %d), size=(%d, %d)\n",
657428d7b3dSmrg	     __FUNCTION__, op,
658428d7b3dSmrg	     get_picture_id(src), src_x, src_y,
659428d7b3dSmrg	     get_picture_id(mask), mask_x, mask_y,
660428d7b3dSmrg	     get_picture_id(dst), dst_x, dst_y,
661428d7b3dSmrg	     dst->pDrawable->x, dst->pDrawable->y,
662428d7b3dSmrg	     width, height));
663428d7b3dSmrg
664428d7b3dSmrg	if (region_is_empty(dst->pCompositeClip)) {
665428d7b3dSmrg		DBG(("%s: empty clip, skipping\n", __FUNCTION__));
666428d7b3dSmrg		return;
667428d7b3dSmrg	}
668428d7b3dSmrg
669428d7b3dSmrg	if (op == PictOpClear) {
670428d7b3dSmrg		DBG(("%s: discarding source and mask for clear\n", __FUNCTION__));
671428d7b3dSmrg		mask = NULL;
672428d7b3dSmrg		if (sna->clear)
673428d7b3dSmrg			src = sna->clear;
674428d7b3dSmrg	}
675428d7b3dSmrg
676428d7b3dSmrg	if (mask && sna_composite_mask_is_opaque(mask)) {
677428d7b3dSmrg		DBG(("%s: removing opaque %smask\n",
678428d7b3dSmrg		     __FUNCTION__,
679428d7b3dSmrg		     mask->componentAlpha && PICT_FORMAT_RGB(mask->format) ? "CA " : ""));
680428d7b3dSmrg		mask = NULL;
681428d7b3dSmrg	}
682428d7b3dSmrg
683428d7b3dSmrg	if (!sna_compute_composite_region(&region,
684428d7b3dSmrg					  src, mask, dst,
685428d7b3dSmrg					  src_x,  src_y,
686428d7b3dSmrg					  mask_x, mask_y,
687428d7b3dSmrg					  dst_x,  dst_y,
688428d7b3dSmrg					  width,  height))
689428d7b3dSmrg		return;
690428d7b3dSmrg
691428d7b3dSmrg	if (NO_COMPOSITE)
692428d7b3dSmrg		goto fallback;
693428d7b3dSmrg
694428d7b3dSmrg	if (wedged(sna)) {
695428d7b3dSmrg		DBG(("%s: fallback -- wedged\n", __FUNCTION__));
696428d7b3dSmrg		goto fallback;
697428d7b3dSmrg	}
698428d7b3dSmrg
699428d7b3dSmrg	if (!can_render_to_picture(dst)) {
700428d7b3dSmrg		DBG(("%s: fallback due to unhandled picture\n", __FUNCTION__));
701428d7b3dSmrg		goto fallback;
702428d7b3dSmrg	}
703428d7b3dSmrg
704428d7b3dSmrg	priv = sna_pixmap(pixmap);
705428d7b3dSmrg	if (priv == NULL) {
706428d7b3dSmrg		DBG(("%s: fallback as destination pixmap=%ld is unattached\n",
707428d7b3dSmrg		     __FUNCTION__, pixmap->drawable.serialNumber));
708428d7b3dSmrg		goto fallback;
709428d7b3dSmrg	}
710428d7b3dSmrg
711428d7b3dSmrg	if (use_cpu(pixmap, priv, op, width, height) &&
712428d7b3dSmrg	    !picture_is_gpu(sna, src, PREFER_GPU_RENDER) &&
713428d7b3dSmrg	    !picture_is_gpu(sna, mask, PREFER_GPU_RENDER)) {
714428d7b3dSmrg		DBG(("%s: fallback, dst pixmap=%ld is too small (or completely damaged)\n",
715428d7b3dSmrg		     __FUNCTION__, pixmap->drawable.serialNumber));
716428d7b3dSmrg		goto fallback;
717428d7b3dSmrg	}
718428d7b3dSmrg
719428d7b3dSmrg	dx = region.extents.x1 - (dst_x + dst->pDrawable->x);
720428d7b3dSmrg	dy = region.extents.y1 - (dst_y + dst->pDrawable->y);
721428d7b3dSmrg
722428d7b3dSmrg	DBG(("%s: composite region extents:+(%d, %d) -> (%d, %d), (%d, %d) + (%d, %d)\n",
723428d7b3dSmrg	     __FUNCTION__,
724428d7b3dSmrg	     dx, dy,
725428d7b3dSmrg	     region.extents.x1, region.extents.y1,
726428d7b3dSmrg	     region.extents.x2, region.extents.y2,
727428d7b3dSmrg	     get_drawable_dx(dst->pDrawable),
728428d7b3dSmrg	     get_drawable_dy(dst->pDrawable)));
729428d7b3dSmrg
730428d7b3dSmrg	if (op <= PictOpSrc && priv->cpu_damage) {
731428d7b3dSmrg		int16_t x, y;
732428d7b3dSmrg
733428d7b3dSmrg		if (get_drawable_deltas(dst->pDrawable, pixmap, &x, &y))
734428d7b3dSmrg			pixman_region_translate(&region, x, y);
735428d7b3dSmrg
736428d7b3dSmrg		sna_damage_subtract(&priv->cpu_damage, &region);
737428d7b3dSmrg		if (priv->cpu_damage == NULL) {
738428d7b3dSmrg			list_del(&priv->flush_list);
739428d7b3dSmrg			priv->cpu = false;
740428d7b3dSmrg		}
741428d7b3dSmrg
742428d7b3dSmrg		if (x|y)
743428d7b3dSmrg			pixman_region_translate(&region, -x, -y);
744428d7b3dSmrg	}
745428d7b3dSmrg
746428d7b3dSmrg	if (!sna->render.composite(sna,
747428d7b3dSmrg				   op, src, mask, dst,
748428d7b3dSmrg				   src_x + dx,  src_y + dy,
749428d7b3dSmrg				   mask_x + dx, mask_y + dy,
750428d7b3dSmrg				   region.extents.x1,
751428d7b3dSmrg				   region.extents.y1,
752428d7b3dSmrg				   region.extents.x2 - region.extents.x1,
753428d7b3dSmrg				   region.extents.y2 - region.extents.y1,
754428d7b3dSmrg				   region.data ? COMPOSITE_PARTIAL : 0,
755428d7b3dSmrg				   memset(&tmp, 0, sizeof(tmp)))) {
756428d7b3dSmrg		DBG(("%s: fallback due unhandled composite op\n", __FUNCTION__));
757428d7b3dSmrg		goto fallback;
758428d7b3dSmrg	}
759428d7b3dSmrg
760428d7b3dSmrg	if (region.data == NULL)
761428d7b3dSmrg		tmp.box(sna, &tmp, &region.extents);
762428d7b3dSmrg	else
763428d7b3dSmrg		tmp.boxes(sna, &tmp,
764428d7b3dSmrg			  RegionBoxptr(&region),
765428d7b3dSmrg			  region_num_rects(&region));
766428d7b3dSmrg	apply_damage(&tmp, &region);
767428d7b3dSmrg	tmp.done(sna, &tmp);
768428d7b3dSmrg
769428d7b3dSmrg	goto out;
770428d7b3dSmrg
771428d7b3dSmrgfallback:
772428d7b3dSmrg	DBG(("%s: fallback -- fbComposite\n", __FUNCTION__));
773428d7b3dSmrg	sna_composite_fb(op, src, mask, dst, &region,
774428d7b3dSmrg			 src_x,  src_y,
775428d7b3dSmrg			 mask_x, mask_y,
776428d7b3dSmrg			 dst_x,  dst_y,
777428d7b3dSmrg			 width,  height);
778428d7b3dSmrgout:
779428d7b3dSmrg	REGION_UNINIT(NULL, &region);
780428d7b3dSmrg}
781428d7b3dSmrg
782428d7b3dSmrgvoid
783428d7b3dSmrgsna_composite_rectangles(CARD8		 op,
784428d7b3dSmrg			 PicturePtr	 dst,
785428d7b3dSmrg			 xRenderColor	*color,
786428d7b3dSmrg			 int		 num_rects,
787428d7b3dSmrg			 xRectangle	*rects)
788428d7b3dSmrg{
789428d7b3dSmrg	struct sna *sna = to_sna_from_drawable(dst->pDrawable);
790428d7b3dSmrg	PixmapPtr pixmap;
791428d7b3dSmrg	struct sna_pixmap *priv;
792428d7b3dSmrg	struct kgem_bo *bo;
793428d7b3dSmrg	struct sna_damage **damage;
794428d7b3dSmrg	pixman_region16_t region;
795428d7b3dSmrg	pixman_box16_t stack_boxes[64], *boxes = stack_boxes, *b;
796428d7b3dSmrg	int16_t dst_x, dst_y;
797428d7b3dSmrg	int i, num_boxes;
798428d7b3dSmrg	unsigned hint;
799428d7b3dSmrg
800428d7b3dSmrg	DBG(("%s(op=%d, %08x x %d [(%d, %d)x(%d, %d) ...])\n",
801428d7b3dSmrg	     __FUNCTION__, op,
802428d7b3dSmrg	     (color->alpha >> 8 << 24) |
803428d7b3dSmrg	     (color->red   >> 8 << 16) |
804428d7b3dSmrg	     (color->green >> 8 << 8) |
805428d7b3dSmrg	     (color->blue  >> 8 << 0),
806428d7b3dSmrg	     num_rects,
807428d7b3dSmrg	     rects[0].x, rects[0].y, rects[0].width, rects[0].height));
808428d7b3dSmrg
809428d7b3dSmrg	if (!num_rects)
810428d7b3dSmrg		return;
811428d7b3dSmrg
812428d7b3dSmrg	if (region_is_empty(dst->pCompositeClip)) {
813428d7b3dSmrg		DBG(("%s: empty clip, skipping\n", __FUNCTION__));
814428d7b3dSmrg		return;
815428d7b3dSmrg	}
816428d7b3dSmrg
817428d7b3dSmrg	if ((color->red|color->green|color->blue|color->alpha) <= 0x00ff) {
818428d7b3dSmrg		switch (op) {
819428d7b3dSmrg		case PictOpOver:
820428d7b3dSmrg		case PictOpOutReverse:
821428d7b3dSmrg		case PictOpAdd:
822428d7b3dSmrg			return;
823428d7b3dSmrg		case  PictOpInReverse:
824428d7b3dSmrg		case  PictOpSrc:
825428d7b3dSmrg			op = PictOpClear;
826428d7b3dSmrg			break;
827428d7b3dSmrg		case  PictOpAtopReverse:
828428d7b3dSmrg			op = PictOpOut;
829428d7b3dSmrg			break;
830428d7b3dSmrg		case  PictOpXor:
831428d7b3dSmrg			op = PictOpOverReverse;
832428d7b3dSmrg			break;
833428d7b3dSmrg		}
834428d7b3dSmrg	}
835428d7b3dSmrg	if (color->alpha <= 0x00ff) {
836428d7b3dSmrg		switch (op) {
837428d7b3dSmrg		case PictOpOver:
838428d7b3dSmrg		case PictOpOutReverse:
839428d7b3dSmrg			return;
840428d7b3dSmrg		case  PictOpInReverse:
841428d7b3dSmrg			op = PictOpClear;
842428d7b3dSmrg			break;
843428d7b3dSmrg		case  PictOpAtopReverse:
844428d7b3dSmrg			op = PictOpOut;
845428d7b3dSmrg			break;
846428d7b3dSmrg		case  PictOpXor:
847428d7b3dSmrg			op = PictOpOverReverse;
848428d7b3dSmrg			break;
849428d7b3dSmrg		}
850428d7b3dSmrg	} else if (color->alpha >= 0xff00) {
851428d7b3dSmrg		switch (op) {
852428d7b3dSmrg		case PictOpOver:
853428d7b3dSmrg			op = PictOpSrc;
854428d7b3dSmrg			break;
855428d7b3dSmrg		case PictOpInReverse:
856428d7b3dSmrg			return;
857428d7b3dSmrg		case PictOpOutReverse:
858428d7b3dSmrg			op = PictOpClear;
859428d7b3dSmrg			break;
860428d7b3dSmrg		case  PictOpAtopReverse:
861428d7b3dSmrg			op = PictOpOverReverse;
862428d7b3dSmrg			break;
863428d7b3dSmrg		case  PictOpXor:
864428d7b3dSmrg			op = PictOpOut;
865428d7b3dSmrg			break;
866428d7b3dSmrg		}
867428d7b3dSmrg	}
868428d7b3dSmrg
869428d7b3dSmrg	/* Avoid reducing overlapping translucent rectangles */
870428d7b3dSmrg	if (op == PictOpOver &&
871428d7b3dSmrg	    num_rects == 1 &&
872428d7b3dSmrg	    sna_drawable_is_clear(dst->pDrawable))
873428d7b3dSmrg		op = PictOpSrc;
874428d7b3dSmrg
875428d7b3dSmrg	DBG(("%s: converted to op %d\n", __FUNCTION__, op));
876428d7b3dSmrg
877428d7b3dSmrg	if (num_rects > ARRAY_SIZE(stack_boxes)) {
878428d7b3dSmrg		boxes = malloc(sizeof(pixman_box16_t) * num_rects);
879428d7b3dSmrg		if (boxes == NULL)
880428d7b3dSmrg			return;
881428d7b3dSmrg	}
882428d7b3dSmrg
883428d7b3dSmrg	for (i = num_boxes = 0; i < num_rects; i++) {
884428d7b3dSmrg		boxes[num_boxes].x1 = rects[i].x + dst->pDrawable->x;
885428d7b3dSmrg		if (boxes[num_boxes].x1 < dst->pCompositeClip->extents.x1)
886428d7b3dSmrg			boxes[num_boxes].x1 = dst->pCompositeClip->extents.x1;
887428d7b3dSmrg
888428d7b3dSmrg		boxes[num_boxes].y1 = rects[i].y + dst->pDrawable->y;
889428d7b3dSmrg		if (boxes[num_boxes].y1 < dst->pCompositeClip->extents.y1)
890428d7b3dSmrg			boxes[num_boxes].y1 = dst->pCompositeClip->extents.y1;
891428d7b3dSmrg
892428d7b3dSmrg		boxes[num_boxes].x2 = bound(rects[i].x + dst->pDrawable->x, rects[i].width);
893428d7b3dSmrg		if (boxes[num_boxes].x2 > dst->pCompositeClip->extents.x2)
894428d7b3dSmrg			boxes[num_boxes].x2 = dst->pCompositeClip->extents.x2;
895428d7b3dSmrg
896428d7b3dSmrg		boxes[num_boxes].y2 = bound(rects[i].y + dst->pDrawable->y, rects[i].height);
897428d7b3dSmrg		if (boxes[num_boxes].y2 > dst->pCompositeClip->extents.y2)
898428d7b3dSmrg			boxes[num_boxes].y2 = dst->pCompositeClip->extents.y2;
899428d7b3dSmrg
900428d7b3dSmrg		DBG(("%s[%d] (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n",
901428d7b3dSmrg		     __FUNCTION__, i,
902428d7b3dSmrg		     rects[i].x, rects[i].y, rects[i].width, rects[i].height,
903428d7b3dSmrg		     boxes[num_boxes].x1, boxes[num_boxes].y1, boxes[num_boxes].x2, boxes[num_boxes].y2));
904428d7b3dSmrg
905428d7b3dSmrg		if (boxes[num_boxes].x2 > boxes[num_boxes].x1 &&
906428d7b3dSmrg		    boxes[num_boxes].y2 > boxes[num_boxes].y1)
907428d7b3dSmrg			num_boxes++;
908428d7b3dSmrg	}
909428d7b3dSmrg
910428d7b3dSmrg	if (num_boxes == 0)
911428d7b3dSmrg		goto cleanup_boxes;
912428d7b3dSmrg
913428d7b3dSmrg	if (!pixman_region_init_rects(&region, boxes, num_boxes))
914428d7b3dSmrg		goto cleanup_boxes;
915428d7b3dSmrg
916428d7b3dSmrg	DBG(("%s: nrects=%d, region=(%d, %d), (%d, %d) x %d\n",
917428d7b3dSmrg	     __FUNCTION__, num_rects,
918428d7b3dSmrg	     region.extents.x1, region.extents.y1,
919428d7b3dSmrg	     region.extents.x2, region.extents.y2,
920428d7b3dSmrg	     num_boxes));
921428d7b3dSmrg
922428d7b3dSmrg	if (dst->pCompositeClip->data &&
923428d7b3dSmrg	    (!pixman_region_intersect(&region, &region, dst->pCompositeClip) ||
924428d7b3dSmrg	     region_is_empty(&region))) {
925428d7b3dSmrg		DBG(("%s: zero-intersection between rectangles and clip\n",
926428d7b3dSmrg		     __FUNCTION__));
927428d7b3dSmrg		goto cleanup_region;
928428d7b3dSmrg	}
929428d7b3dSmrg
930428d7b3dSmrg	DBG(("%s: clipped extents (%d, %d),(%d, %d) x %d\n",
931428d7b3dSmrg	     __FUNCTION__,
932428d7b3dSmrg	     RegionExtents(&region)->x1, RegionExtents(&region)->y1,
933428d7b3dSmrg	     RegionExtents(&region)->x2, RegionExtents(&region)->y2,
934428d7b3dSmrg	     region_num_rects(&region)));
935428d7b3dSmrg
936428d7b3dSmrg	/* XXX xserver-1.8: CompositeRects is not tracked by Damage, so we must
937428d7b3dSmrg	 * manually append the damaged regions ourselves.
938428d7b3dSmrg	 *
939428d7b3dSmrg	 * Note that DamageRegionAppend() will apply the drawable-deltas itself.
940428d7b3dSmrg	 */
941428d7b3dSmrg	DamageRegionAppend(dst->pDrawable, &region);
942428d7b3dSmrg
943428d7b3dSmrg	pixmap = get_drawable_pixmap(dst->pDrawable);
944428d7b3dSmrg	if (get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y))
945428d7b3dSmrg		pixman_region_translate(&region, dst_x, dst_y);
946428d7b3dSmrg
947428d7b3dSmrg	DBG(("%s: pixmap +(%d, %d) extents (%d, %d),(%d, %d)\n",
948428d7b3dSmrg	     __FUNCTION__, dst_x, dst_y,
949428d7b3dSmrg	     RegionExtents(&region)->x1, RegionExtents(&region)->y1,
950428d7b3dSmrg	     RegionExtents(&region)->x2, RegionExtents(&region)->y2));
951428d7b3dSmrg	assert_pixmap_contains_box(pixmap, RegionExtents(&region));
952428d7b3dSmrg
953428d7b3dSmrg	if (NO_COMPOSITE_RECTANGLES)
954428d7b3dSmrg		goto fallback;
955428d7b3dSmrg
956428d7b3dSmrg	if (wedged(sna))
957428d7b3dSmrg		goto fallback;
958428d7b3dSmrg
959428d7b3dSmrg	if (!can_render_to_picture(dst)) {
960428d7b3dSmrg		DBG(("%s: fallback, dst has an incompatible picture\n", __FUNCTION__));
961428d7b3dSmrg		goto fallback;
962428d7b3dSmrg	}
963428d7b3dSmrg
964428d7b3dSmrg	priv = sna_pixmap(pixmap);
965428d7b3dSmrg	if (priv == NULL || too_small(priv)) {
966428d7b3dSmrg		DBG(("%s: fallback, dst pixmap=%ld too small or not attached\n",
967428d7b3dSmrg		     __FUNCTION__, pixmap->drawable.serialNumber));
968428d7b3dSmrg		goto fallback;
969428d7b3dSmrg	}
970428d7b3dSmrg
971428d7b3dSmrg	/* If we going to be overwriting any CPU damage with a subsequent
972428d7b3dSmrg	 * operation, then we may as well delete it without moving it
973428d7b3dSmrg	 * first to the GPU.
974428d7b3dSmrg	 */
975428d7b3dSmrg	hint = can_render(sna) ? PREFER_GPU : 0;
976428d7b3dSmrg	if (op <= PictOpSrc) {
977428d7b3dSmrg		if (priv->clear) {
978428d7b3dSmrg			uint32_t pixel;
979428d7b3dSmrg			bool ok;
980428d7b3dSmrg
981428d7b3dSmrg			if (op == PictOpClear) {
982428d7b3dSmrg				ok = sna_get_pixel_from_rgba(&pixel,
983428d7b3dSmrg							     0, 0, 0, 0,
984428d7b3dSmrg							     dst->format);
985428d7b3dSmrg			} else {
986428d7b3dSmrg				ok = sna_get_pixel_from_rgba(&pixel,
987428d7b3dSmrg							     color->red,
988428d7b3dSmrg							     color->green,
989428d7b3dSmrg							     color->blue,
990428d7b3dSmrg							     color->alpha,
991428d7b3dSmrg							     dst->format);
992428d7b3dSmrg			}
993428d7b3dSmrg			if (ok && priv->clear_color == pixel)
994428d7b3dSmrg				goto done;
995428d7b3dSmrg		}
996428d7b3dSmrg
997428d7b3dSmrg		if (region.data == NULL) {
998428d7b3dSmrg			hint |= IGNORE_DAMAGE;
999428d7b3dSmrg			if (region_subsumes_drawable(&region, &pixmap->drawable))
1000428d7b3dSmrg				hint |= REPLACES;
1001428d7b3dSmrg			if (priv->cpu_damage &&
1002428d7b3dSmrg			    (hint & REPLACES ||
1003428d7b3dSmrg			     region_subsumes_damage(&region, priv->cpu_damage))) {
1004428d7b3dSmrg				DBG(("%s: discarding existing CPU damage\n", __FUNCTION__));
1005428d7b3dSmrg				if (priv->gpu_bo && priv->gpu_bo->proxy) {
1006428d7b3dSmrg					assert(priv->gpu_damage == NULL);
1007428d7b3dSmrg					kgem_bo_destroy(&sna->kgem, priv->gpu_bo);
1008428d7b3dSmrg					priv->gpu_bo = NULL;
1009428d7b3dSmrg				}
1010428d7b3dSmrg				sna_damage_destroy(&priv->cpu_damage);
1011428d7b3dSmrg				list_del(&priv->flush_list);
1012428d7b3dSmrg			}
1013428d7b3dSmrg			if (hint & REPLACES ||
1014428d7b3dSmrg			    box_inplace(pixmap, &region.extents)) {
1015428d7b3dSmrg				if (priv->gpu_bo && priv->cpu_damage == NULL) {
1016428d7b3dSmrg					DBG(("%s: promoting to full GPU\n", __FUNCTION__));
1017428d7b3dSmrg					assert(priv->gpu_bo->proxy == NULL);
1018428d7b3dSmrg					sna_damage_all(&priv->gpu_damage, pixmap);
1019428d7b3dSmrg				}
1020428d7b3dSmrg			}
1021428d7b3dSmrg		}
1022428d7b3dSmrg		if (priv->cpu_damage == NULL) {
1023428d7b3dSmrg			DBG(("%s: dropping last-cpu hint\n", __FUNCTION__));
1024428d7b3dSmrg			priv->cpu = false;
1025428d7b3dSmrg		}
1026428d7b3dSmrg	}
1027428d7b3dSmrg
1028428d7b3dSmrg	bo = sna_drawable_use_bo(&pixmap->drawable, hint,
1029428d7b3dSmrg				 &region.extents, &damage);
1030428d7b3dSmrg	if (bo == NULL) {
1031428d7b3dSmrg		DBG(("%s: fallback due to no GPU bo\n", __FUNCTION__));
1032428d7b3dSmrg		goto fallback;
1033428d7b3dSmrg	}
1034428d7b3dSmrg	if (hint & REPLACES)
1035428d7b3dSmrg		kgem_bo_pair_undo(&sna->kgem, priv->gpu_bo, priv->cpu_bo);
1036428d7b3dSmrg
1037428d7b3dSmrg	if (op <= PictOpSrc) {
1038428d7b3dSmrg		b = pixman_region_rectangles(&region, &num_boxes);
1039428d7b3dSmrg		if (!sna->render.fill_boxes(sna, op, dst->format, color,
1040428d7b3dSmrg					    &pixmap->drawable, bo, b, num_boxes)) {
1041428d7b3dSmrg			DBG(("%s: fallback - acceleration failed\n", __FUNCTION__));
1042428d7b3dSmrg			goto fallback;
1043428d7b3dSmrg		}
1044428d7b3dSmrg	} else if (dst->pCompositeClip->data == NULL) {
1045428d7b3dSmrg		for (i = 0; i < num_boxes; i++) {
1046428d7b3dSmrg			boxes[i].x1 += dst_x;
1047428d7b3dSmrg			boxes[i].x2 += dst_x;
1048428d7b3dSmrg			boxes[i].y1 += dst_y;
1049428d7b3dSmrg			boxes[i].y2 += dst_y;
1050428d7b3dSmrg		}
1051428d7b3dSmrg		if (!sna->render.fill_boxes(sna, op, dst->format, color,
1052428d7b3dSmrg					    &pixmap->drawable, bo, boxes, num_boxes)) {
1053428d7b3dSmrg			DBG(("%s: fallback - acceleration failed\n", __FUNCTION__));
1054428d7b3dSmrg			goto fallback;
1055428d7b3dSmrg		}
1056428d7b3dSmrg	} else {
1057428d7b3dSmrg		for (i = 0; i < num_boxes; i++) {
1058428d7b3dSmrg			RegionRec tmp = { boxes[i] };
1059428d7b3dSmrg			if (pixman_region_intersect(&tmp, &tmp, dst->pCompositeClip)) {
1060428d7b3dSmrg				int n = 0;
1061428d7b3dSmrg
1062428d7b3dSmrg				b = pixman_region_rectangles(&tmp, &n);
1063428d7b3dSmrg				if (n) {
1064428d7b3dSmrg					if (dst_x | dst_y)
1065428d7b3dSmrg						pixman_region_translate(&tmp, dst_x, dst_y);
1066428d7b3dSmrg
1067428d7b3dSmrg					n = !sna->render.fill_boxes(sna, op, dst->format, color,
1068428d7b3dSmrg								    &pixmap->drawable, bo, b, n);
1069428d7b3dSmrg				}
1070428d7b3dSmrg
1071428d7b3dSmrg				pixman_region_fini(&tmp);
1072428d7b3dSmrg
1073428d7b3dSmrg				if (n) {
1074428d7b3dSmrg					DBG(("%s: fallback - acceleration failed\n", __FUNCTION__));
1075428d7b3dSmrg					goto fallback;
1076428d7b3dSmrg				}
1077428d7b3dSmrg			}
1078428d7b3dSmrg		}
1079428d7b3dSmrg	}
1080428d7b3dSmrg
1081428d7b3dSmrg	if (damage)
1082428d7b3dSmrg		sna_damage_add(damage, &region);
1083428d7b3dSmrg
1084428d7b3dSmrg	/* Clearing a pixmap after creation is a common operation, so take
1085428d7b3dSmrg	 * advantage and reduce further damage operations.
1086428d7b3dSmrg	 */
1087428d7b3dSmrg	if (region_subsumes_drawable(&region, &pixmap->drawable)) {
1088428d7b3dSmrg		if (damage) {
1089428d7b3dSmrg			sna_damage_all(damage, pixmap);
1090428d7b3dSmrg			sna_damage_destroy(damage == &priv->gpu_damage ?
1091428d7b3dSmrg					   &priv->cpu_damage : &priv->gpu_damage);
1092428d7b3dSmrg		}
1093428d7b3dSmrg
1094428d7b3dSmrg		if (op <= PictOpSrc && bo == priv->gpu_bo) {
1095428d7b3dSmrg			bool ok;
1096428d7b3dSmrg
1097428d7b3dSmrg			assert(DAMAGE_IS_ALL(priv->gpu_damage));
1098428d7b3dSmrg
1099428d7b3dSmrg			priv->clear_color = 0;
1100428d7b3dSmrg			ok = true;
1101428d7b3dSmrg			if (op == PictOpSrc)
1102428d7b3dSmrg				ok = sna_get_pixel_from_rgba(&priv->clear_color,
1103428d7b3dSmrg							     color->red,
1104428d7b3dSmrg							     color->green,
1105428d7b3dSmrg							     color->blue,
1106428d7b3dSmrg							     color->alpha,
1107428d7b3dSmrg							     dst->format);
1108428d7b3dSmrg			priv->clear = ok;
1109428d7b3dSmrg			DBG(("%s: pixmap=%ld marking clear [%08x]? %d\n",
1110428d7b3dSmrg			     __FUNCTION__, pixmap->drawable.serialNumber,
1111428d7b3dSmrg			     priv->clear_color, ok));
1112428d7b3dSmrg		}
1113428d7b3dSmrg	}
1114428d7b3dSmrg	goto done;
1115428d7b3dSmrg
1116428d7b3dSmrgfallback:
1117428d7b3dSmrg	DBG(("%s: fallback\n", __FUNCTION__));
1118428d7b3dSmrg	if (op <= PictOpSrc)
1119428d7b3dSmrg		hint = MOVE_WRITE;
1120428d7b3dSmrg	else
1121428d7b3dSmrg		hint = MOVE_WRITE | MOVE_READ;
1122428d7b3dSmrg	if (!sna_drawable_move_region_to_cpu(&pixmap->drawable, &region, hint))
1123428d7b3dSmrg		goto done;
1124428d7b3dSmrg
1125428d7b3dSmrg	if (dst->alphaMap &&
1126428d7b3dSmrg	    !sna_drawable_move_to_cpu(dst->alphaMap->pDrawable, hint))
1127428d7b3dSmrg		goto done;
1128428d7b3dSmrg
1129428d7b3dSmrg	assert(pixmap->devPrivate.ptr);
1130428d7b3dSmrg
1131428d7b3dSmrg	if (sigtrap_get() == 0) {
1132428d7b3dSmrg		if (op <= PictOpSrc) {
1133428d7b3dSmrg			int nbox = region_num_rects(&region);
1134428d7b3dSmrg			const BoxRec *box = region_rects(&region);
1135428d7b3dSmrg			uint32_t pixel;
1136428d7b3dSmrg
1137428d7b3dSmrg			if (op == PictOpClear)
1138428d7b3dSmrg				pixel = 0;
1139428d7b3dSmrg			else if (!sna_get_pixel_from_rgba(&pixel,
1140428d7b3dSmrg							  color->red,
1141428d7b3dSmrg							  color->green,
1142428d7b3dSmrg							  color->blue,
1143428d7b3dSmrg							  color->alpha,
1144428d7b3dSmrg							  dst->format))
1145428d7b3dSmrg				goto fallback_composite;
1146428d7b3dSmrg
1147428d7b3dSmrg			sigtrap_assert_active();
1148428d7b3dSmrg			if (pixel == 0 &&
1149428d7b3dSmrg			    box->x2 - box->x1 == pixmap->drawable.width &&
1150428d7b3dSmrg			    box->y2 - box->y1 == pixmap->drawable.height) {
1151428d7b3dSmrg				memset(pixmap->devPrivate.ptr, 0,
1152428d7b3dSmrg				       pixmap->devKind*pixmap->drawable.height);
1153428d7b3dSmrg			} else do {
1154428d7b3dSmrg				DBG(("%s: fallback fill: (%d, %d)x(%d, %d) %08x\n",
1155428d7b3dSmrg				     __FUNCTION__,
1156428d7b3dSmrg				     box->x1, box->y1,
1157428d7b3dSmrg				     box->x2 - box->x1,
1158428d7b3dSmrg				     box->y2 - box->y1,
1159428d7b3dSmrg				     pixel));
1160428d7b3dSmrg
1161428d7b3dSmrg				pixman_fill(pixmap->devPrivate.ptr,
1162428d7b3dSmrg					    pixmap->devKind/sizeof(uint32_t),
1163428d7b3dSmrg					    pixmap->drawable.bitsPerPixel,
1164428d7b3dSmrg					    box->x1, box->y1,
1165428d7b3dSmrg					    box->x2 - box->x1,
1166428d7b3dSmrg					    box->y2 - box->y1,
1167428d7b3dSmrg					    pixel);
1168428d7b3dSmrg				box++;
1169428d7b3dSmrg			} while (--nbox);
1170428d7b3dSmrg		} else {
1171428d7b3dSmrg			PicturePtr src;
1172428d7b3dSmrg			int error;
1173428d7b3dSmrg
1174428d7b3dSmrgfallback_composite:
1175428d7b3dSmrg			DBG(("%s: fallback -- fbComposite()\n", __FUNCTION__));
1176428d7b3dSmrg			src = CreateSolidPicture(0, color, &error);
1177428d7b3dSmrg			if (src) {
1178428d7b3dSmrg				do {
1179428d7b3dSmrg					fbComposite(op, src, NULL, dst,
1180428d7b3dSmrg						    0, 0,
1181428d7b3dSmrg						    0, 0,
1182428d7b3dSmrg						    rects->x, rects->y,
1183428d7b3dSmrg						    rects->width, rects->height);
1184428d7b3dSmrg					rects++;
1185428d7b3dSmrg				} while (--num_rects);
1186428d7b3dSmrg				FreePicture(src, 0);
1187428d7b3dSmrg			}
1188428d7b3dSmrg		}
1189428d7b3dSmrg		sigtrap_put();
1190428d7b3dSmrg	}
1191428d7b3dSmrg
1192428d7b3dSmrgdone:
1193428d7b3dSmrg	DamageRegionProcessPending(dst->pDrawable);
1194428d7b3dSmrg
1195428d7b3dSmrgcleanup_region:
1196428d7b3dSmrg	pixman_region_fini(&region);
1197428d7b3dSmrgcleanup_boxes:
1198428d7b3dSmrg	if (boxes != stack_boxes)
1199428d7b3dSmrg		free(boxes);
1200428d7b3dSmrg}
1201