1428d7b3dSmrg/**************************************************************************
2428d7b3dSmrg
3428d7b3dSmrgCopyright (c) 2011 Intel Corporation
4428d7b3dSmrg
5428d7b3dSmrgPermission is hereby granted, free of charge, to any person obtaining a
6428d7b3dSmrgcopy of this software and associated documentation files (the
7428d7b3dSmrg"Software"), to deal in the Software without restriction, including
8428d7b3dSmrgwithout limitation the rights to use, copy, modify, merge, publish,
9428d7b3dSmrgdistribute, sub license, and/or sell copies of the Software, and to
10428d7b3dSmrgpermit persons to whom the Software is furnished to do so, subject to
11428d7b3dSmrgthe following conditions:
12428d7b3dSmrg
13428d7b3dSmrgThe above copyright notice and this permission notice (including the
14428d7b3dSmrgnext paragraph) shall be included in all copies or substantial portions
15428d7b3dSmrgof the Software.
16428d7b3dSmrg
17428d7b3dSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18428d7b3dSmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19428d7b3dSmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20428d7b3dSmrgIN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
21428d7b3dSmrgANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22428d7b3dSmrgTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23428d7b3dSmrgSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24428d7b3dSmrg
25428d7b3dSmrg **************************************************************************/
26428d7b3dSmrg
27428d7b3dSmrg#ifdef HAVE_CONFIG_H
28428d7b3dSmrg#include "config.h"
29428d7b3dSmrg#endif
30428d7b3dSmrg
31428d7b3dSmrg#include "sna.h"
32428d7b3dSmrg#include "sna_damage.h"
33428d7b3dSmrg
34428d7b3dSmrg/*
35428d7b3dSmrg * sna_damage is a batching layer on top of the regular pixman_region_t.
36428d7b3dSmrg * It is required as the ever-growing accumulation of invidual small
37428d7b3dSmrg * damage regions is an O(n^2) operation. Instead the accumulation of a
38428d7b3dSmrg * batch can be done in closer to O(n.lgn), and so prevents abysmal
39428d7b3dSmrg * performance in x11perf -copywinwin10.
40428d7b3dSmrg *
41428d7b3dSmrg * As with the core of SNA, damage is handled modally. That is, it
42428d7b3dSmrg * accumulates whilst rendering and then subtracts during migration of the
43428d7b3dSmrg * pixmap from GPU to CPU or vice versa. As such we can track the current
44428d7b3dSmrg * mode globally and when that mode switches perform the update of the region
45428d7b3dSmrg * in a single operation.
46428d7b3dSmrg *
47428d7b3dSmrg * Furthermore, we can track whether the whole pixmap is damaged and so
48428d7b3dSmrg * cheapy discard no-ops.
49428d7b3dSmrg */
50428d7b3dSmrg
51428d7b3dSmrgstruct sna_damage_box {
52428d7b3dSmrg	struct list list;
53428d7b3dSmrg	int size;
54428d7b3dSmrg} __attribute__((packed));
55428d7b3dSmrg
56428d7b3dSmrgstatic struct sna_damage *__freed_damage;
57428d7b3dSmrg
58428d7b3dSmrgstatic inline bool region_is_singular(const RegionRec *r)
59428d7b3dSmrg{
60428d7b3dSmrg	return r->data == NULL;
61428d7b3dSmrg}
62428d7b3dSmrg
63428d7b3dSmrgstatic inline bool region_is_singular_or_empty(const RegionRec *r)
64428d7b3dSmrg{
65428d7b3dSmrg	return r->data == NULL || r->data->numRects == 0;
66428d7b3dSmrg}
67428d7b3dSmrg
68428d7b3dSmrg#if HAS_DEBUG_FULL
69428d7b3dSmrgstatic const char *_debug_describe_region(char *buf, int max,
70428d7b3dSmrg					  const RegionRec *region)
71428d7b3dSmrg{
72428d7b3dSmrg	const BoxRec *box;
73428d7b3dSmrg	int n, len;
74428d7b3dSmrg
75428d7b3dSmrg	if (region == NULL)
76428d7b3dSmrg		return "nil";
77428d7b3dSmrg
78428d7b3dSmrg	n = region_num_rects(region);
79428d7b3dSmrg	if (n == 0)
80428d7b3dSmrg		return "[0]";
81428d7b3dSmrg
82428d7b3dSmrg	if (n == 1) {
83428d7b3dSmrg		sprintf(buf,
84428d7b3dSmrg			"[(%d, %d), (%d, %d)]",
85428d7b3dSmrg			region->extents.x1, region->extents.y1,
86428d7b3dSmrg			region->extents.x2, region->extents.y2);
87428d7b3dSmrg		return buf;
88428d7b3dSmrg	}
89428d7b3dSmrg
90428d7b3dSmrg	len = sprintf(buf,
91428d7b3dSmrg		      "[(%d, %d), (%d, %d) x %d: ",
92428d7b3dSmrg		      region->extents.x1, region->extents.y1,
93428d7b3dSmrg		      region->extents.x2, region->extents.y2,
94428d7b3dSmrg		      n) + 3;
95428d7b3dSmrg	max -= 2;
96428d7b3dSmrg	box = region_rects(region);
97428d7b3dSmrg	while (n--) {
98428d7b3dSmrg		char tmp[80];
99428d7b3dSmrg		int this;
100428d7b3dSmrg
101428d7b3dSmrg		this = snprintf(tmp, sizeof(tmp),
102428d7b3dSmrg				"((%d, %d), (%d, %d))%s",
103428d7b3dSmrg				box->x1, box->y1,
104428d7b3dSmrg				box->x2, box->y2,
105428d7b3dSmrg				n ? ", ..." : "");
106428d7b3dSmrg		box++;
107428d7b3dSmrg
108428d7b3dSmrg		if (this > max - len)
109428d7b3dSmrg			break;
110428d7b3dSmrg
111428d7b3dSmrg		len -= 3;
112428d7b3dSmrg		memcpy(buf + len, tmp, this);
113428d7b3dSmrg		len += this;
114428d7b3dSmrg	}
115428d7b3dSmrg	buf[len++] = ']';
116428d7b3dSmrg	buf[len] = '\0';
117428d7b3dSmrg	return buf;
118428d7b3dSmrg}
119428d7b3dSmrg
120428d7b3dSmrgstatic const char *_debug_describe_damage(char *buf, int max,
121428d7b3dSmrg					  const struct sna_damage *damage)
122428d7b3dSmrg{
123428d7b3dSmrg	char damage_str[500], region_str[500];
124428d7b3dSmrg	int str_max;
125428d7b3dSmrg
126428d7b3dSmrg	if (damage == NULL)
127428d7b3dSmrg		return "None";
128428d7b3dSmrg
129428d7b3dSmrg	str_max = max/2 - 6;
130428d7b3dSmrg	if (str_max > sizeof(damage_str))
131428d7b3dSmrg		str_max = sizeof(damage_str);
132428d7b3dSmrg
133428d7b3dSmrg	if (damage->mode == DAMAGE_ALL) {
134428d7b3dSmrg		snprintf(buf, max, "[[(%d, %d), (%d, %d)]: all]",
135428d7b3dSmrg			 damage->extents.x1, damage->extents.y1,
136428d7b3dSmrg			 damage->extents.x2, damage->extents.y2);
137428d7b3dSmrg	} else {
138428d7b3dSmrg		if (damage->dirty) {
139428d7b3dSmrg			sprintf(damage_str, "%c[ ...]",
140428d7b3dSmrg				damage->mode == DAMAGE_SUBTRACT ? '-' : '+');
141428d7b3dSmrg		} else
142428d7b3dSmrg			damage_str[0] = '\0';
143428d7b3dSmrg		snprintf(buf, max, "[[(%d, %d), (%d, %d)]: %s %s]%c",
144428d7b3dSmrg			 damage->extents.x1, damage->extents.y1,
145428d7b3dSmrg			 damage->extents.x2, damage->extents.y2,
146428d7b3dSmrg			 _debug_describe_region(region_str, str_max,
147428d7b3dSmrg						&damage->region),
148428d7b3dSmrg			 damage_str, damage->dirty ? '*' : ' ');
149428d7b3dSmrg	}
150428d7b3dSmrg
151428d7b3dSmrg	return buf;
152428d7b3dSmrg}
153428d7b3dSmrg#endif
154428d7b3dSmrg
155428d7b3dSmrgstatic struct sna_damage_box *
156428d7b3dSmrglast_box(struct sna_damage *damage)
157428d7b3dSmrg{
158428d7b3dSmrg	return list_entry(damage->embedded_box.list.prev,
159428d7b3dSmrg			  struct sna_damage_box,
160428d7b3dSmrg			  list);
161428d7b3dSmrg}
162428d7b3dSmrg
163428d7b3dSmrgstatic void
164428d7b3dSmrgreset_embedded_box(struct sna_damage *damage)
165428d7b3dSmrg{
166428d7b3dSmrg	damage->dirty = false;
167428d7b3dSmrg	damage->box = damage->embedded_box.box;
168428d7b3dSmrg	damage->embedded_box.size =
169428d7b3dSmrg		damage->remain = ARRAY_SIZE(damage->embedded_box.box);
170428d7b3dSmrg	list_init(&damage->embedded_box.list);
171428d7b3dSmrg}
172428d7b3dSmrg
173428d7b3dSmrgstatic void reset_extents(struct sna_damage *damage)
174428d7b3dSmrg{
175428d7b3dSmrg	damage->extents.x1 = damage->extents.y1 = MAXSHORT;
176428d7b3dSmrg	damage->extents.x2 = damage->extents.y2 = MINSHORT;
177428d7b3dSmrg}
178428d7b3dSmrg
179428d7b3dSmrgstatic struct sna_damage *_sna_damage_create(void)
180428d7b3dSmrg{
181428d7b3dSmrg	struct sna_damage *damage;
182428d7b3dSmrg
183428d7b3dSmrg	if (__freed_damage) {
184428d7b3dSmrg		damage = __freed_damage;
185428d7b3dSmrg		__freed_damage = *(void **)__freed_damage;
186428d7b3dSmrg	} else {
187428d7b3dSmrg		damage = malloc(sizeof(*damage));
188428d7b3dSmrg		if (damage == NULL)
189428d7b3dSmrg			return NULL;
190428d7b3dSmrg	}
191428d7b3dSmrg	reset_embedded_box(damage);
192428d7b3dSmrg	damage->mode = DAMAGE_ADD;
193428d7b3dSmrg	pixman_region_init(&damage->region);
194428d7b3dSmrg	reset_extents(damage);
195428d7b3dSmrg
196428d7b3dSmrg	return damage;
197428d7b3dSmrg}
198428d7b3dSmrg
199428d7b3dSmrgstruct sna_damage *sna_damage_create(void)
200428d7b3dSmrg{
201428d7b3dSmrg	return _sna_damage_create();
202428d7b3dSmrg}
203428d7b3dSmrg
204428d7b3dSmrgstatic void free_list(struct list *head)
205428d7b3dSmrg{
206428d7b3dSmrg	while (!list_is_empty(head)) {
207428d7b3dSmrg		struct list *l = head->next;
208428d7b3dSmrg		list_del(l);
209428d7b3dSmrg		free(l);
210428d7b3dSmrg	}
211428d7b3dSmrg}
212428d7b3dSmrg
213428d7b3dSmrgstatic void __sna_damage_reduce(struct sna_damage *damage)
214428d7b3dSmrg{
215428d7b3dSmrg	int n, nboxes;
216428d7b3dSmrg	BoxPtr boxes, free_boxes = NULL;
217428d7b3dSmrg	pixman_region16_t *region = &damage->region;
218428d7b3dSmrg	struct sna_damage_box *iter;
219428d7b3dSmrg
220428d7b3dSmrg	assert(damage->mode != DAMAGE_ALL);
221428d7b3dSmrg	assert(damage->dirty);
222428d7b3dSmrg
223428d7b3dSmrg	DBG(("    reduce: before region.n=%d\n", region_num_rects(region)));
224428d7b3dSmrg
225428d7b3dSmrg	nboxes = damage->embedded_box.size;
226428d7b3dSmrg	list_for_each_entry(iter, &damage->embedded_box.list, list)
227428d7b3dSmrg		nboxes += iter->size;
228428d7b3dSmrg	DBG(("   nboxes=%d, residual=%d\n", nboxes, damage->remain));
229428d7b3dSmrg	nboxes -= damage->remain;
230428d7b3dSmrg	if (nboxes == 0)
231428d7b3dSmrg		goto done;
232428d7b3dSmrg	if (nboxes == 1) {
233428d7b3dSmrg		pixman_region16_t tmp;
234428d7b3dSmrg
235428d7b3dSmrg		tmp.extents = damage->embedded_box.box[0];
236428d7b3dSmrg		tmp.data = NULL;
237428d7b3dSmrg
238428d7b3dSmrg		if (damage->mode == DAMAGE_ADD)
239428d7b3dSmrg			pixman_region_union(region, region, &tmp);
240428d7b3dSmrg		else
241428d7b3dSmrg			pixman_region_subtract(region, region, &tmp);
242428d7b3dSmrg		damage->extents = region->extents;
243428d7b3dSmrg
244428d7b3dSmrg		goto done;
245428d7b3dSmrg	}
246428d7b3dSmrg
247428d7b3dSmrg	if (damage->mode == DAMAGE_ADD)
248428d7b3dSmrg		nboxes += region_num_rects(region);
249428d7b3dSmrg
250428d7b3dSmrg	iter = last_box(damage);
251428d7b3dSmrg	n = iter->size - damage->remain;
252428d7b3dSmrg	boxes = (BoxRec *)(iter+1);
253428d7b3dSmrg	DBG(("   last box count=%d/%d, need=%d\n", n, iter->size, nboxes));
254428d7b3dSmrg	if (nboxes > iter->size) {
255428d7b3dSmrg		boxes = malloc(sizeof(BoxRec)*nboxes);
256428d7b3dSmrg		if (boxes == NULL)
257428d7b3dSmrg			goto done;
258428d7b3dSmrg
259428d7b3dSmrg		free_boxes = boxes;
260428d7b3dSmrg	}
261428d7b3dSmrg
262428d7b3dSmrg	if (boxes != damage->embedded_box.box) {
263428d7b3dSmrg		if (list_is_empty(&damage->embedded_box.list)) {
264428d7b3dSmrg			DBG(("   copying embedded boxes\n"));
265428d7b3dSmrg			memcpy(boxes,
266428d7b3dSmrg			       damage->embedded_box.box,
267428d7b3dSmrg			       n*sizeof(BoxRec));
268428d7b3dSmrg		} else {
269428d7b3dSmrg			if (boxes != (BoxPtr)(iter+1)) {
270428d7b3dSmrg				DBG(("   copying %d boxes from last\n", n));
271428d7b3dSmrg				memcpy(boxes, iter+1, n*sizeof(BoxRec));
272428d7b3dSmrg			}
273428d7b3dSmrg
274428d7b3dSmrg			iter = list_entry(iter->list.prev,
275428d7b3dSmrg					  struct sna_damage_box,
276428d7b3dSmrg					  list);
277428d7b3dSmrg			while (&iter->list != &damage->embedded_box.list) {
278428d7b3dSmrg				DBG(("   copy %d boxes from %d\n",
279428d7b3dSmrg				     iter->size, n));
280428d7b3dSmrg				memcpy(boxes + n, iter+1,
281428d7b3dSmrg				       iter->size * sizeof(BoxRec));
282428d7b3dSmrg				n += iter->size;
283428d7b3dSmrg
284428d7b3dSmrg				iter = list_entry(iter->list.prev,
285428d7b3dSmrg						  struct sna_damage_box,
286428d7b3dSmrg						  list);
287428d7b3dSmrg			}
288428d7b3dSmrg
289428d7b3dSmrg			DBG(("   copying embedded boxes to %d\n", n));
290428d7b3dSmrg			memcpy(boxes + n,
291428d7b3dSmrg			       damage->embedded_box.box,
292428d7b3dSmrg			       sizeof(damage->embedded_box.box));
293428d7b3dSmrg			n += damage->embedded_box.size;
294428d7b3dSmrg		}
295428d7b3dSmrg	}
296428d7b3dSmrg
297428d7b3dSmrg	if (damage->mode == DAMAGE_ADD) {
298428d7b3dSmrg		memcpy(boxes + n,
299428d7b3dSmrg		       region_rects(region),
300428d7b3dSmrg		       region_num_rects(region)*sizeof(BoxRec));
301428d7b3dSmrg		assert(n + region_num_rects(region) == nboxes);
302428d7b3dSmrg		pixman_region_fini(region);
303428d7b3dSmrg		pixman_region_init_rects(region, boxes, nboxes);
304428d7b3dSmrg
305428d7b3dSmrg		assert(pixman_region_not_empty(region));
306428d7b3dSmrg		assert(damage->extents.x1 == region->extents.x1 &&
307428d7b3dSmrg		       damage->extents.y1 == region->extents.y1 &&
308428d7b3dSmrg		       damage->extents.x2 == region->extents.x2 &&
309428d7b3dSmrg		       damage->extents.y2 == region->extents.y2);
310428d7b3dSmrg	} else {
311428d7b3dSmrg		pixman_region16_t tmp;
312428d7b3dSmrg
313428d7b3dSmrg		assert(n == nboxes);
314428d7b3dSmrg		pixman_region_init_rects(&tmp, boxes, nboxes);
315428d7b3dSmrg		pixman_region_subtract(region, region, &tmp);
316428d7b3dSmrg		pixman_region_fini(&tmp);
317428d7b3dSmrg
318428d7b3dSmrg		assert(damage->extents.x1 <= region->extents.x1 &&
319428d7b3dSmrg		       damage->extents.y1 <= region->extents.y1 &&
320428d7b3dSmrg		       damage->extents.x2 >= region->extents.x2 &&
321428d7b3dSmrg		       damage->extents.y2 >= region->extents.y2);
322428d7b3dSmrg		if (pixman_region_not_empty(region))
323428d7b3dSmrg			damage->extents = region->extents;
324428d7b3dSmrg		else
325428d7b3dSmrg			reset_extents(damage);
326428d7b3dSmrg	}
327428d7b3dSmrg
328428d7b3dSmrg	free(free_boxes);
329428d7b3dSmrg
330428d7b3dSmrgdone:
331428d7b3dSmrg	damage->mode = DAMAGE_ADD;
332428d7b3dSmrg	free_list(&damage->embedded_box.list);
333428d7b3dSmrg	reset_embedded_box(damage);
334428d7b3dSmrg
335428d7b3dSmrg	DBG(("    reduce: after region.n=%d\n", region_num_rects(region)));
336428d7b3dSmrg}
337428d7b3dSmrg
338428d7b3dSmrgstatic bool _sna_damage_create_boxes(struct sna_damage *damage,
339428d7b3dSmrg				     int count)
340428d7b3dSmrg{
341428d7b3dSmrg	struct sna_damage_box *box;
342428d7b3dSmrg	int n;
343428d7b3dSmrg
344428d7b3dSmrg	box = last_box(damage);
345428d7b3dSmrg	n = 4*box->size;
346428d7b3dSmrg	if (n < count)
347428d7b3dSmrg		n = ALIGN(count, 64);
348428d7b3dSmrg
349428d7b3dSmrg	DBG(("    %s(%d->%d): new\n", __FUNCTION__, count, n));
350428d7b3dSmrg
351428d7b3dSmrg	if (n >= (INT_MAX - sizeof(*box)) / sizeof(BoxRec))
352428d7b3dSmrg		return false;
353428d7b3dSmrg
354428d7b3dSmrg	box = malloc(sizeof(*box) + sizeof(BoxRec)*n);
355428d7b3dSmrg	if (box == NULL)
356428d7b3dSmrg		return false;
357428d7b3dSmrg
358428d7b3dSmrg	list_add_tail(&box->list, &damage->embedded_box.list);
359428d7b3dSmrg
360428d7b3dSmrg	box->size = damage->remain = n;
361428d7b3dSmrg	damage->box = (BoxRec *)(box + 1);
362428d7b3dSmrg	return true;
363428d7b3dSmrg}
364428d7b3dSmrg
365428d7b3dSmrgstatic struct sna_damage *
366428d7b3dSmrg_sna_damage_create_elt(struct sna_damage *damage,
367428d7b3dSmrg		       const BoxRec *boxes, int count)
368428d7b3dSmrg{
369428d7b3dSmrg	int n;
370428d7b3dSmrg
371428d7b3dSmrg	DBG(("    %s: prev=(remain %d), count=%d\n",
372428d7b3dSmrg	     __FUNCTION__, damage->remain, count));
373428d7b3dSmrg	assert(count);
374428d7b3dSmrg
375428d7b3dSmrgrestart:
376428d7b3dSmrg	n = count;
377428d7b3dSmrg	if (n > damage->remain)
378428d7b3dSmrg		n = damage->remain;
379428d7b3dSmrg	if (n) {
380428d7b3dSmrg		memcpy(damage->box, boxes, n * sizeof(BoxRec));
381428d7b3dSmrg		damage->box += n;
382428d7b3dSmrg		damage->remain -= n;
383428d7b3dSmrg		damage->dirty = true;
384428d7b3dSmrg
385428d7b3dSmrg		count -= n;
386428d7b3dSmrg		boxes += n;
387428d7b3dSmrg		if (count == 0)
388428d7b3dSmrg			return damage;
389428d7b3dSmrg	}
390428d7b3dSmrg
391428d7b3dSmrg	DBG(("    %s(): new elt\n", __FUNCTION__));
392428d7b3dSmrg	assert(damage->remain == 0);
393428d7b3dSmrg	assert(damage->box - (BoxRec *)(last_box(damage)+1) == last_box(damage)->size);
394428d7b3dSmrg
395428d7b3dSmrg	if (!_sna_damage_create_boxes(damage, count)) {
396428d7b3dSmrg		unsigned mode;
397428d7b3dSmrg
398428d7b3dSmrg		if (!damage->dirty)
399428d7b3dSmrg			return damage;
400428d7b3dSmrg
401428d7b3dSmrg		mode = damage->mode;
402428d7b3dSmrg		__sna_damage_reduce(damage);
403428d7b3dSmrg		damage->mode = mode;
404428d7b3dSmrg
405428d7b3dSmrg		goto restart;
406428d7b3dSmrg	}
407428d7b3dSmrg
408428d7b3dSmrg	memcpy(damage->box, boxes, count * sizeof(BoxRec));
409428d7b3dSmrg	damage->box += count;
410428d7b3dSmrg	damage->remain -= count;
411428d7b3dSmrg	damage->dirty = true;
412428d7b3dSmrg	assert(damage->remain >= 0);
413428d7b3dSmrg
414428d7b3dSmrg	return damage;
415428d7b3dSmrg}
416428d7b3dSmrg
417428d7b3dSmrgstatic struct sna_damage *
418428d7b3dSmrg_sna_damage_create_elt_from_boxes(struct sna_damage *damage,
419428d7b3dSmrg				  const BoxRec *boxes, int count,
420428d7b3dSmrg				  int16_t dx, int16_t dy)
421428d7b3dSmrg{
422428d7b3dSmrg	int i, n;
423428d7b3dSmrg
424428d7b3dSmrg	DBG(("    %s: prev=(remain %d)\n", __FUNCTION__, damage->remain));
425428d7b3dSmrg	assert(count);
426428d7b3dSmrg
427428d7b3dSmrgrestart:
428428d7b3dSmrg	n = count;
429428d7b3dSmrg	if (n > damage->remain)
430428d7b3dSmrg		n = damage->remain;
431428d7b3dSmrg	if (n) {
432428d7b3dSmrg		for (i = 0; i < n; i++) {
433428d7b3dSmrg			damage->box[i].x1 = boxes[i].x1 + dx;
434428d7b3dSmrg			damage->box[i].x2 = boxes[i].x2 + dx;
435428d7b3dSmrg			damage->box[i].y1 = boxes[i].y1 + dy;
436428d7b3dSmrg			damage->box[i].y2 = boxes[i].y2 + dy;
437428d7b3dSmrg		}
438428d7b3dSmrg		damage->box += n;
439428d7b3dSmrg		damage->remain -= n;
440428d7b3dSmrg		damage->dirty = true;
441428d7b3dSmrg
442428d7b3dSmrg		count -= n;
443428d7b3dSmrg		boxes += n;
444428d7b3dSmrg		if (count == 0)
445428d7b3dSmrg			return damage;
446428d7b3dSmrg	}
447428d7b3dSmrg
448428d7b3dSmrg	DBG(("    %s(): new elt\n", __FUNCTION__));
449428d7b3dSmrg	assert(damage->remain == 0);
450428d7b3dSmrg	assert(damage->box - (BoxRec *)(last_box(damage)+1) == last_box(damage)->size);
451428d7b3dSmrg
452428d7b3dSmrg	if (!_sna_damage_create_boxes(damage, count)) {
453428d7b3dSmrg		unsigned mode;
454428d7b3dSmrg
455428d7b3dSmrg		if (!damage->dirty)
456428d7b3dSmrg			return damage;
457428d7b3dSmrg
458428d7b3dSmrg		mode = damage->mode;
459428d7b3dSmrg		__sna_damage_reduce(damage);
460428d7b3dSmrg		damage->mode = mode;
461428d7b3dSmrg
462428d7b3dSmrg		goto restart;
463428d7b3dSmrg	}
464428d7b3dSmrg
465428d7b3dSmrg	for (i = 0; i < count; i++) {
466428d7b3dSmrg		damage->box[i].x1 = boxes[i].x1 + dx;
467428d7b3dSmrg		damage->box[i].x2 = boxes[i].x2 + dx;
468428d7b3dSmrg		damage->box[i].y1 = boxes[i].y1 + dy;
469428d7b3dSmrg		damage->box[i].y2 = boxes[i].y2 + dy;
470428d7b3dSmrg	}
471428d7b3dSmrg	damage->box += count;
472428d7b3dSmrg	damage->remain -= count;
473428d7b3dSmrg	damage->dirty = true;
474428d7b3dSmrg	assert(damage->remain >= 0);
475428d7b3dSmrg
476428d7b3dSmrg	return damage;
477428d7b3dSmrg}
478428d7b3dSmrg
479428d7b3dSmrgstatic struct sna_damage *
480428d7b3dSmrg_sna_damage_create_elt_from_rectangles(struct sna_damage *damage,
481428d7b3dSmrg				       const xRectangle *r, int count,
482428d7b3dSmrg				       int16_t dx, int16_t dy)
483428d7b3dSmrg{
484428d7b3dSmrg	int i, n;
485428d7b3dSmrg
486428d7b3dSmrg	DBG(("    %s: prev=(remain %d), count=%d\n",
487428d7b3dSmrg	     __FUNCTION__, damage->remain, count));
488428d7b3dSmrg	assert(count);
489428d7b3dSmrg
490428d7b3dSmrgrestart:
491428d7b3dSmrg	n = count;
492428d7b3dSmrg	if (n > damage->remain)
493428d7b3dSmrg		n = damage->remain;
494428d7b3dSmrg	if (n) {
495428d7b3dSmrg		for (i = 0; i < n; i++) {
496428d7b3dSmrg			damage->box[i].x1 = r[i].x + dx;
497428d7b3dSmrg			damage->box[i].x2 = damage->box[i].x1 + r[i].width;
498428d7b3dSmrg			damage->box[i].y1 = r[i].y + dy;
499428d7b3dSmrg			damage->box[i].y2 = damage->box[i].y1 + r[i].height;
500428d7b3dSmrg		}
501428d7b3dSmrg		damage->box += n;
502428d7b3dSmrg		damage->remain -= n;
503428d7b3dSmrg		damage->dirty = true;
504428d7b3dSmrg
505428d7b3dSmrg		count -= n;
506428d7b3dSmrg		r += n;
507428d7b3dSmrg		if (count == 0)
508428d7b3dSmrg			return damage;
509428d7b3dSmrg	}
510428d7b3dSmrg
511428d7b3dSmrg	DBG(("    %s(): new elt\n", __FUNCTION__));
512428d7b3dSmrg	assert(damage->remain == 0);
513428d7b3dSmrg	assert(damage->box - (BoxRec *)(last_box(damage)+1) == last_box(damage)->size);
514428d7b3dSmrg
515428d7b3dSmrg	if (!_sna_damage_create_boxes(damage, count)) {
516428d7b3dSmrg		unsigned mode;
517428d7b3dSmrg
518428d7b3dSmrg		if (!damage->dirty)
519428d7b3dSmrg			return damage;
520428d7b3dSmrg
521428d7b3dSmrg		mode = damage->mode;
522428d7b3dSmrg		__sna_damage_reduce(damage);
523428d7b3dSmrg		damage->mode = mode;
524428d7b3dSmrg
525428d7b3dSmrg		goto restart;
526428d7b3dSmrg	}
527428d7b3dSmrg
528428d7b3dSmrg	for (i = 0; i < count; i++) {
529428d7b3dSmrg		damage->box[i].x1 = r[i].x + dx;
530428d7b3dSmrg		damage->box[i].x2 = damage->box[i].x1 + r[i].width;
531428d7b3dSmrg		damage->box[i].y1 = r[i].y + dy;
532428d7b3dSmrg		damage->box[i].y2 = damage->box[i].y1 + r[i].height;
533428d7b3dSmrg	}
534428d7b3dSmrg	damage->box += count;
535428d7b3dSmrg	damage->remain -= count;
536428d7b3dSmrg	damage->dirty = true;
537428d7b3dSmrg	assert(damage->remain >= 0);
538428d7b3dSmrg
539428d7b3dSmrg	return damage;
540428d7b3dSmrg}
541428d7b3dSmrg
542428d7b3dSmrgstatic struct sna_damage *
543428d7b3dSmrg_sna_damage_create_elt_from_points(struct sna_damage *damage,
544428d7b3dSmrg				   const DDXPointRec *p, int count,
545428d7b3dSmrg				   int16_t dx, int16_t dy)
546428d7b3dSmrg{
547428d7b3dSmrg	int i, n;
548428d7b3dSmrg
549428d7b3dSmrg	DBG(("    %s: prev=(remain %d), count=%d\n",
550428d7b3dSmrg	     __FUNCTION__, damage->remain, count));
551428d7b3dSmrg	assert(count);
552428d7b3dSmrg
553428d7b3dSmrgrestart:
554428d7b3dSmrg	n = count;
555428d7b3dSmrg	if (n > damage->remain)
556428d7b3dSmrg		n = damage->remain;
557428d7b3dSmrg	if (n) {
558428d7b3dSmrg		for (i = 0; i < n; i++) {
559428d7b3dSmrg			damage->box[i].x1 = p[i].x + dx;
560428d7b3dSmrg			damage->box[i].x2 = damage->box[i].x1 + 1;
561428d7b3dSmrg			damage->box[i].y1 = p[i].y + dy;
562428d7b3dSmrg			damage->box[i].y2 = damage->box[i].y1 + 1;
563428d7b3dSmrg		}
564428d7b3dSmrg		damage->box += n;
565428d7b3dSmrg		damage->remain -= n;
566428d7b3dSmrg		damage->dirty = true;
567428d7b3dSmrg
568428d7b3dSmrg		count -= n;
569428d7b3dSmrg		p += n;
570428d7b3dSmrg		if (count == 0)
571428d7b3dSmrg			return damage;
572428d7b3dSmrg	}
573428d7b3dSmrg
574428d7b3dSmrg	DBG(("    %s(): new elt\n", __FUNCTION__));
575428d7b3dSmrg	assert(damage->remain == 0);
576428d7b3dSmrg	assert(damage->box - (BoxRec *)(last_box(damage)+1) == last_box(damage)->size);
577428d7b3dSmrg
578428d7b3dSmrg	if (!_sna_damage_create_boxes(damage, count)) {
579428d7b3dSmrg		unsigned mode;
580428d7b3dSmrg
581428d7b3dSmrg		if (!damage->dirty)
582428d7b3dSmrg			return damage;
583428d7b3dSmrg
584428d7b3dSmrg		mode = damage->mode;
585428d7b3dSmrg		__sna_damage_reduce(damage);
586428d7b3dSmrg		damage->mode = mode;
587428d7b3dSmrg
588428d7b3dSmrg		goto restart;
589428d7b3dSmrg	}
590428d7b3dSmrg
591428d7b3dSmrg	for (i = 0; i < count; i++) {
592428d7b3dSmrg		damage->box[i].x1 = p[i].x + dx;
593428d7b3dSmrg		damage->box[i].x2 = damage->box[i].x1 + 1;
594428d7b3dSmrg		damage->box[i].y1 = p[i].y + dy;
595428d7b3dSmrg		damage->box[i].y2 = damage->box[i].y1 + 1;
596428d7b3dSmrg	}
597428d7b3dSmrg	damage->box += count;
598428d7b3dSmrg	damage->remain -= count;
599428d7b3dSmrg	damage->dirty = true;
600428d7b3dSmrg	assert(damage->remain >= 0);
601428d7b3dSmrg
602428d7b3dSmrg	return damage;
603428d7b3dSmrg}
604428d7b3dSmrg
605428d7b3dSmrgstatic void damage_union(struct sna_damage *damage, const BoxRec *box)
606428d7b3dSmrg{
607428d7b3dSmrg	DBG(("%s: extending damage (%d, %d), (%d, %d) by (%d, %d), (%d, %d)\n",
608428d7b3dSmrg	     __FUNCTION__,
609428d7b3dSmrg	     damage->extents.x1, damage->extents.y1,
610428d7b3dSmrg	     damage->extents.x2, damage->extents.y2,
611428d7b3dSmrg	     box->x1, box->y1, box->x2, box->y2));
612428d7b3dSmrg	assert(box->x2 > box->x1 && box->y2 > box->y1);
613428d7b3dSmrg	if (damage->extents.x2 < damage->extents.x1) {
614428d7b3dSmrg		damage->extents = *box;
615428d7b3dSmrg	} else {
616428d7b3dSmrg		if (damage->extents.x1 > box->x1)
617428d7b3dSmrg			damage->extents.x1 = box->x1;
618428d7b3dSmrg		if (damage->extents.x2 < box->x2)
619428d7b3dSmrg			damage->extents.x2 = box->x2;
620428d7b3dSmrg
621428d7b3dSmrg		if (damage->extents.y1 > box->y1)
622428d7b3dSmrg			damage->extents.y1 = box->y1;
623428d7b3dSmrg		if (damage->extents.y2 < box->y2)
624428d7b3dSmrg			damage->extents.y2 = box->y2;
625428d7b3dSmrg	}
626428d7b3dSmrg	assert(damage->extents.x2 > damage->extents.x1);
627428d7b3dSmrg	assert(damage->extents.y2 > damage->extents.y1);
628428d7b3dSmrg}
629428d7b3dSmrg
630428d7b3dSmrgstatic void _pixman_region_union_box(RegionRec *region, const BoxRec *box)
631428d7b3dSmrg{
632428d7b3dSmrg	RegionRec u = { *box, NULL };
633428d7b3dSmrg	pixman_region_union(region, region, &u);
634428d7b3dSmrg}
635428d7b3dSmrg
636428d7b3dSmrgstatic bool box_contains_region(const BoxRec *b, const RegionRec *r)
637428d7b3dSmrg{
638428d7b3dSmrg	return (b->x1 <= r->extents.x1 && b->x2 >= r->extents.x2 &&
639428d7b3dSmrg		b->y1 <= r->extents.y1 && b->y2 >= r->extents.y2);
640428d7b3dSmrg}
641428d7b3dSmrg
642428d7b3dSmrgstatic struct sna_damage *__sna_damage_add_box(struct sna_damage *damage,
643428d7b3dSmrg					       const BoxRec *box)
644428d7b3dSmrg{
645428d7b3dSmrg	if (box->y2 <= box->y1 || box->x2 <= box->x1)
646428d7b3dSmrg		return damage;
647428d7b3dSmrg
648428d7b3dSmrg	if (!damage) {
649428d7b3dSmrg		damage = _sna_damage_create();
650428d7b3dSmrg		if (damage == NULL)
651428d7b3dSmrg			return NULL;
652428d7b3dSmrg	} else switch (damage->mode) {
653428d7b3dSmrg	case DAMAGE_ALL:
654428d7b3dSmrg		return damage;
655428d7b3dSmrg	case DAMAGE_SUBTRACT:
656428d7b3dSmrg		__sna_damage_reduce(damage);
657428d7b3dSmrg	case DAMAGE_ADD:
658428d7b3dSmrg		break;
659428d7b3dSmrg	}
660428d7b3dSmrg
661428d7b3dSmrg	if (region_is_singular_or_empty(&damage->region) ||
662428d7b3dSmrg	    box_contains_region(box, &damage->region)) {
663428d7b3dSmrg		_pixman_region_union_box(&damage->region, box);
664428d7b3dSmrg		assert(damage->region.extents.x2 > damage->region.extents.x1);
665428d7b3dSmrg		assert(damage->region.extents.y2 > damage->region.extents.y1);
666428d7b3dSmrg		damage_union(damage, box);
667428d7b3dSmrg		return damage;
668428d7b3dSmrg	}
669428d7b3dSmrg
670428d7b3dSmrg	if (pixman_region_contains_rectangle(&damage->region,
671428d7b3dSmrg					     (BoxPtr)box) == PIXMAN_REGION_IN)
672428d7b3dSmrg		return damage;
673428d7b3dSmrg
674428d7b3dSmrg	damage_union(damage, box);
675428d7b3dSmrg	return _sna_damage_create_elt(damage, box, 1);
676428d7b3dSmrg}
677428d7b3dSmrg
678428d7b3dSmrginline static struct sna_damage *__sna_damage_add(struct sna_damage *damage,
679428d7b3dSmrg						  RegionPtr region)
680428d7b3dSmrg{
681428d7b3dSmrg	assert(RegionNotEmpty(region));
682428d7b3dSmrg
683428d7b3dSmrg	if (!damage) {
684428d7b3dSmrg		damage = _sna_damage_create();
685428d7b3dSmrg		if (damage == NULL)
686428d7b3dSmrg			return NULL;
687428d7b3dSmrg	} else switch (damage->mode) {
688428d7b3dSmrg	case DAMAGE_ALL:
689428d7b3dSmrg		return damage;
690428d7b3dSmrg	case DAMAGE_SUBTRACT:
691428d7b3dSmrg		__sna_damage_reduce(damage);
692428d7b3dSmrg	case DAMAGE_ADD:
693428d7b3dSmrg		break;
694428d7b3dSmrg	}
695428d7b3dSmrg
696428d7b3dSmrg	if (region_is_singular(region))
697428d7b3dSmrg		return __sna_damage_add_box(damage, &region->extents);
698428d7b3dSmrg
699428d7b3dSmrg	if (region_is_singular_or_empty(&damage->region)) {
700428d7b3dSmrg		pixman_region_union(&damage->region, &damage->region, region);
701428d7b3dSmrg		assert(damage->region.extents.x2 > damage->region.extents.x1);
702428d7b3dSmrg		assert(damage->region.extents.y2 > damage->region.extents.y1);
703428d7b3dSmrg		damage_union(damage, &region->extents);
704428d7b3dSmrg		return damage;
705428d7b3dSmrg	}
706428d7b3dSmrg
707428d7b3dSmrg	if (pixman_region_contains_rectangle(&damage->region,
708428d7b3dSmrg					     &region->extents) == PIXMAN_REGION_IN)
709428d7b3dSmrg		return damage;
710428d7b3dSmrg
711428d7b3dSmrg	damage_union(damage, &region->extents);
712428d7b3dSmrg	return _sna_damage_create_elt(damage,
713428d7b3dSmrg				      region_rects(region),
714428d7b3dSmrg				      region_num_rects(region));
715428d7b3dSmrg}
716428d7b3dSmrg
717428d7b3dSmrg#if HAS_DEBUG_FULL
718428d7b3dSmrgfastcall struct sna_damage *_sna_damage_add(struct sna_damage *damage,
719428d7b3dSmrg					    RegionPtr region)
720428d7b3dSmrg{
721428d7b3dSmrg	char region_buf[120];
722428d7b3dSmrg	char damage_buf[1000];
723428d7b3dSmrg
724428d7b3dSmrg	DBG(("%s(%s + %s)\n", __FUNCTION__,
725428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
726428d7b3dSmrg	     _debug_describe_region(region_buf, sizeof(region_buf), region)));
727428d7b3dSmrg
728428d7b3dSmrg	damage = __sna_damage_add(damage, region);
729428d7b3dSmrg
730428d7b3dSmrg	DBG(("  = %s\n",
731428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
732428d7b3dSmrg	assert(region_num_rects(&damage->region));
733428d7b3dSmrg	assert(damage->region.extents.x2 > damage->region.extents.x1);
734428d7b3dSmrg	assert(damage->region.extents.y2 > damage->region.extents.y1);
735428d7b3dSmrg
736428d7b3dSmrg	return damage;
737428d7b3dSmrg}
738428d7b3dSmrg#else
739428d7b3dSmrgfastcall struct sna_damage *_sna_damage_add(struct sna_damage *damage,
740428d7b3dSmrg					    RegionPtr region)
741428d7b3dSmrg{
742428d7b3dSmrg	return __sna_damage_add(damage, region);
743428d7b3dSmrg}
744428d7b3dSmrg#endif
745428d7b3dSmrg
746428d7b3dSmrginline static struct sna_damage *
747428d7b3dSmrg__sna_damage_add_boxes(struct sna_damage *damage,
748428d7b3dSmrg		       const BoxRec *box, int n,
749428d7b3dSmrg		       int16_t dx, int16_t dy)
750428d7b3dSmrg{
751428d7b3dSmrg	BoxRec extents;
752428d7b3dSmrg	int i;
753428d7b3dSmrg
754428d7b3dSmrg	assert(n);
755428d7b3dSmrg
756428d7b3dSmrg	if (!damage) {
757428d7b3dSmrg		damage = _sna_damage_create();
758428d7b3dSmrg		if (damage == NULL)
759428d7b3dSmrg			return NULL;
760428d7b3dSmrg	} else switch (damage->mode) {
761428d7b3dSmrg	case DAMAGE_ALL:
762428d7b3dSmrg		return damage;
763428d7b3dSmrg	case DAMAGE_SUBTRACT:
764428d7b3dSmrg		__sna_damage_reduce(damage);
765428d7b3dSmrg	case DAMAGE_ADD:
766428d7b3dSmrg		break;
767428d7b3dSmrg	}
768428d7b3dSmrg
769428d7b3dSmrg	assert(box[0].x2 > box[0].x1 && box[0].y2 > box[0].y1);
770428d7b3dSmrg	extents = box[0];
771428d7b3dSmrg	for (i = 1; i < n; i++) {
772428d7b3dSmrg		assert(box[i].x2 > box[i].x1 && box[i].y2 > box[i].y1);
773428d7b3dSmrg		if (extents.x1 > box[i].x1)
774428d7b3dSmrg			extents.x1 = box[i].x1;
775428d7b3dSmrg		if (extents.x2 < box[i].x2)
776428d7b3dSmrg			extents.x2 = box[i].x2;
777428d7b3dSmrg		if (extents.y1 > box[i].y1)
778428d7b3dSmrg			extents.y1 = box[i].y1;
779428d7b3dSmrg		if (extents.y2 < box[i].y2)
780428d7b3dSmrg			extents.y2 = box[i].y2;
781428d7b3dSmrg	}
782428d7b3dSmrg
783428d7b3dSmrg	assert(extents.y2 > extents.y1 && extents.x2 > extents.x1);
784428d7b3dSmrg
785428d7b3dSmrg	extents.x1 += dx;
786428d7b3dSmrg	extents.x2 += dx;
787428d7b3dSmrg	extents.y1 += dy;
788428d7b3dSmrg	extents.y2 += dy;
789428d7b3dSmrg
790428d7b3dSmrg	if (n == 1)
791428d7b3dSmrg		return __sna_damage_add_box(damage, &extents);
792428d7b3dSmrg
793428d7b3dSmrg	if (pixman_region_contains_rectangle(&damage->region,
794428d7b3dSmrg					     &extents) == PIXMAN_REGION_IN)
795428d7b3dSmrg		return damage;
796428d7b3dSmrg
797428d7b3dSmrg	damage_union(damage, &extents);
798428d7b3dSmrg	return _sna_damage_create_elt_from_boxes(damage, box, n, dx, dy);
799428d7b3dSmrg}
800428d7b3dSmrg
801428d7b3dSmrg#if HAS_DEBUG_FULL
802428d7b3dSmrgstruct sna_damage *_sna_damage_add_boxes(struct sna_damage *damage,
803428d7b3dSmrg					 const BoxRec *b, int n,
804428d7b3dSmrg					 int16_t dx, int16_t dy)
805428d7b3dSmrg{
806428d7b3dSmrg	char damage_buf[1000];
807428d7b3dSmrg
808428d7b3dSmrg	DBG(("%s(%s + [(%d, %d), (%d, %d) ... x %d])\n", __FUNCTION__,
809428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
810428d7b3dSmrg	     b->x1, b->y1, b->x2, b->y2, n));
811428d7b3dSmrg
812428d7b3dSmrg	damage = __sna_damage_add_boxes(damage, b, n, dx, dy);
813428d7b3dSmrg
814428d7b3dSmrg	DBG(("  = %s\n",
815428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
816428d7b3dSmrg	if (region_num_rects(&damage->region)) {
817428d7b3dSmrg		assert(damage->region.extents.x2 > damage->region.extents.x1);
818428d7b3dSmrg		assert(damage->region.extents.y2 > damage->region.extents.y1);
819428d7b3dSmrg	}
820428d7b3dSmrg
821428d7b3dSmrg	return damage;
822428d7b3dSmrg}
823428d7b3dSmrg#else
824428d7b3dSmrgstruct sna_damage *_sna_damage_add_boxes(struct sna_damage *damage,
825428d7b3dSmrg					 const BoxRec *b, int n,
826428d7b3dSmrg					 int16_t dx, int16_t dy)
827428d7b3dSmrg{
828428d7b3dSmrg	return __sna_damage_add_boxes(damage, b, n, dx, dy);
829428d7b3dSmrg}
830428d7b3dSmrg#endif
831428d7b3dSmrg
832428d7b3dSmrginline static struct sna_damage *
833428d7b3dSmrg__sna_damage_add_rectangles(struct sna_damage *damage,
834428d7b3dSmrg			    const xRectangle *r, int n,
835428d7b3dSmrg			    int16_t dx, int16_t dy)
836428d7b3dSmrg{
837428d7b3dSmrg	BoxRec extents;
838428d7b3dSmrg	int i;
839428d7b3dSmrg
840428d7b3dSmrg	assert(n);
841428d7b3dSmrg
842428d7b3dSmrg	assert(r[0].width && r[0].height);
843428d7b3dSmrg	extents.x1 = r[0].x;
844428d7b3dSmrg	extents.x2 = r[0].x + r[0].width;
845428d7b3dSmrg	extents.y1 = r[0].y;
846428d7b3dSmrg	extents.y2 = r[0].y + r[0].height;
847428d7b3dSmrg	for (i = 1; i < n; i++) {
848428d7b3dSmrg		assert(r[i].width && r[i].height);
849428d7b3dSmrg		if (extents.x1 > r[i].x)
850428d7b3dSmrg			extents.x1 = r[i].x;
851428d7b3dSmrg		if (extents.x2 < r[i].x + r[i].width)
852428d7b3dSmrg			extents.x2 = r[i].x + r[i].width;
853428d7b3dSmrg		if (extents.y1 > r[i].y)
854428d7b3dSmrg			extents.y1 = r[i].y;
855428d7b3dSmrg		if (extents.y2 < r[i].y + r[i].height)
856428d7b3dSmrg			extents.y2 = r[i].y + r[i].height;
857428d7b3dSmrg	}
858428d7b3dSmrg
859428d7b3dSmrg	assert(extents.y2 > extents.y1 && extents.x2 > extents.x1);
860428d7b3dSmrg
861428d7b3dSmrg	extents.x1 += dx;
862428d7b3dSmrg	extents.x2 += dx;
863428d7b3dSmrg	extents.y1 += dy;
864428d7b3dSmrg	extents.y2 += dy;
865428d7b3dSmrg
866428d7b3dSmrg	if (n == 1)
867428d7b3dSmrg		return __sna_damage_add_box(damage, &extents);
868428d7b3dSmrg
869428d7b3dSmrg	if (!damage) {
870428d7b3dSmrg		damage = _sna_damage_create();
871428d7b3dSmrg		if (damage == NULL)
872428d7b3dSmrg			return NULL;
873428d7b3dSmrg	} else switch (damage->mode) {
874428d7b3dSmrg	case DAMAGE_ALL:
875428d7b3dSmrg		return damage;
876428d7b3dSmrg	case DAMAGE_SUBTRACT:
877428d7b3dSmrg		__sna_damage_reduce(damage);
878428d7b3dSmrg	case DAMAGE_ADD:
879428d7b3dSmrg		break;
880428d7b3dSmrg	}
881428d7b3dSmrg
882428d7b3dSmrg	if (pixman_region_contains_rectangle(&damage->region,
883428d7b3dSmrg					     &extents) == PIXMAN_REGION_IN)
884428d7b3dSmrg		return damage;
885428d7b3dSmrg
886428d7b3dSmrg	damage_union(damage, &extents);
887428d7b3dSmrg	return _sna_damage_create_elt_from_rectangles(damage, r, n, dx, dy);
888428d7b3dSmrg}
889428d7b3dSmrg
890428d7b3dSmrg#if HAS_DEBUG_FULL
891428d7b3dSmrgstruct sna_damage *_sna_damage_add_rectangles(struct sna_damage *damage,
892428d7b3dSmrg					      const xRectangle *r, int n,
893428d7b3dSmrg					      int16_t dx, int16_t dy)
894428d7b3dSmrg{
895428d7b3dSmrg	char damage_buf[1000];
896428d7b3dSmrg
897428d7b3dSmrg	DBG(("%s(%s + [(%d, %d)x(%d, %d) ... x %d])\n", __FUNCTION__,
898428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
899428d7b3dSmrg	     r->x, r->y, r->width, r->height, n));
900428d7b3dSmrg
901428d7b3dSmrg	damage = __sna_damage_add_rectangles(damage, r, n, dx, dy);
902428d7b3dSmrg
903428d7b3dSmrg	DBG(("  = %s\n",
904428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
905428d7b3dSmrg	if (region_num_rects(&damage->region)) {
906428d7b3dSmrg		assert(damage->region.extents.x2 > damage->region.extents.x1);
907428d7b3dSmrg		assert(damage->region.extents.y2 > damage->region.extents.y1);
908428d7b3dSmrg	}
909428d7b3dSmrg
910428d7b3dSmrg	return damage;
911428d7b3dSmrg}
912428d7b3dSmrg#else
913428d7b3dSmrgstruct sna_damage *_sna_damage_add_rectangles(struct sna_damage *damage,
914428d7b3dSmrg					      const xRectangle *r, int n,
915428d7b3dSmrg					      int16_t dx, int16_t dy)
916428d7b3dSmrg{
917428d7b3dSmrg	return __sna_damage_add_rectangles(damage, r, n, dx, dy);
918428d7b3dSmrg}
919428d7b3dSmrg#endif
920428d7b3dSmrg
921428d7b3dSmrg/* XXX pass in extents? */
922428d7b3dSmrginline static struct sna_damage *
923428d7b3dSmrg__sna_damage_add_points(struct sna_damage *damage,
924428d7b3dSmrg			const DDXPointRec *p, int n,
925428d7b3dSmrg			int16_t dx, int16_t dy)
926428d7b3dSmrg{
927428d7b3dSmrg	BoxRec extents;
928428d7b3dSmrg	int i;
929428d7b3dSmrg
930428d7b3dSmrg	assert(n);
931428d7b3dSmrg
932428d7b3dSmrg	extents.x2 = extents.x1 = p[0].x;
933428d7b3dSmrg	extents.y2 = extents.y1 = p[0].y;
934428d7b3dSmrg	for (i = 1; i < n; i++) {
935428d7b3dSmrg		if (extents.x1 > p[i].x)
936428d7b3dSmrg			extents.x1 = p[i].x;
937428d7b3dSmrg		else if (extents.x2 < p[i].x)
938428d7b3dSmrg			extents.x2 = p[i].x;
939428d7b3dSmrg		if (extents.y1 > p[i].y)
940428d7b3dSmrg			extents.y1 = p[i].y;
941428d7b3dSmrg		else if (extents.y2 < p[i].y)
942428d7b3dSmrg			extents.y2 = p[i].y;
943428d7b3dSmrg	}
944428d7b3dSmrg
945428d7b3dSmrg	extents.x1 += dx;
946428d7b3dSmrg	extents.x2 += dx + 1;
947428d7b3dSmrg	extents.y1 += dy;
948428d7b3dSmrg	extents.y2 += dy + 1;
949428d7b3dSmrg
950428d7b3dSmrg	if (n == 1)
951428d7b3dSmrg		return __sna_damage_add_box(damage, &extents);
952428d7b3dSmrg
953428d7b3dSmrg	if (!damage) {
954428d7b3dSmrg		damage = _sna_damage_create();
955428d7b3dSmrg		if (damage == NULL)
956428d7b3dSmrg			return NULL;
957428d7b3dSmrg	} else switch (damage->mode) {
958428d7b3dSmrg	case DAMAGE_ALL:
959428d7b3dSmrg		return damage;
960428d7b3dSmrg	case DAMAGE_SUBTRACT:
961428d7b3dSmrg		__sna_damage_reduce(damage);
962428d7b3dSmrg	case DAMAGE_ADD:
963428d7b3dSmrg		break;
964428d7b3dSmrg	}
965428d7b3dSmrg
966428d7b3dSmrg	if (pixman_region_contains_rectangle(&damage->region,
967428d7b3dSmrg					     &extents) == PIXMAN_REGION_IN)
968428d7b3dSmrg		return damage;
969428d7b3dSmrg
970428d7b3dSmrg	damage_union(damage, &extents);
971428d7b3dSmrg	return _sna_damage_create_elt_from_points(damage, p, n, dx, dy);
972428d7b3dSmrg}
973428d7b3dSmrg
974428d7b3dSmrg#if HAS_DEBUG_FULL
975428d7b3dSmrgstruct sna_damage *_sna_damage_add_points(struct sna_damage *damage,
976428d7b3dSmrg					  const DDXPointRec *p, int n,
977428d7b3dSmrg					  int16_t dx, int16_t dy)
978428d7b3dSmrg{
979428d7b3dSmrg	char damage_buf[1000];
980428d7b3dSmrg
981428d7b3dSmrg	DBG(("%s(%s + [(%d, %d) ... x %d])\n", __FUNCTION__,
982428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
983428d7b3dSmrg	     p->x, p->y, n));
984428d7b3dSmrg
985428d7b3dSmrg	damage = __sna_damage_add_points(damage, p, n, dx, dy);
986428d7b3dSmrg
987428d7b3dSmrg	DBG(("  = %s\n",
988428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
989428d7b3dSmrg	if (region_num_rects(&damage->region)) {
990428d7b3dSmrg		assert(damage->region.extents.x2 > damage->region.extents.x1);
991428d7b3dSmrg		assert(damage->region.extents.y2 > damage->region.extents.y1);
992428d7b3dSmrg	}
993428d7b3dSmrg
994428d7b3dSmrg	return damage;
995428d7b3dSmrg}
996428d7b3dSmrg#else
997428d7b3dSmrgstruct sna_damage *_sna_damage_add_points(struct sna_damage *damage,
998428d7b3dSmrg					  const DDXPointRec *p, int n,
999428d7b3dSmrg					  int16_t dx, int16_t dy)
1000428d7b3dSmrg{
1001428d7b3dSmrg	return __sna_damage_add_points(damage, p, n, dx, dy);
1002428d7b3dSmrg}
1003428d7b3dSmrg#endif
1004428d7b3dSmrg
1005428d7b3dSmrg#if HAS_DEBUG_FULL
1006428d7b3dSmrgfastcall struct sna_damage *_sna_damage_add_box(struct sna_damage *damage,
1007428d7b3dSmrg						const BoxRec *box)
1008428d7b3dSmrg{
1009428d7b3dSmrg	char damage_buf[1000];
1010428d7b3dSmrg
1011428d7b3dSmrg	DBG(("%s(%s + [(%d, %d), (%d, %d)])\n", __FUNCTION__,
1012428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1013428d7b3dSmrg	     box->x1, box->y1, box->x2, box->y2));
1014428d7b3dSmrg
1015428d7b3dSmrg	damage = __sna_damage_add_box(damage, box);
1016428d7b3dSmrg
1017428d7b3dSmrg	DBG(("  = %s\n",
1018428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1019428d7b3dSmrg	assert(region_num_rects(&damage->region));
1020428d7b3dSmrg	assert(damage->region.extents.x2 > damage->region.extents.x1);
1021428d7b3dSmrg	assert(damage->region.extents.y2 > damage->region.extents.y1);
1022428d7b3dSmrg
1023428d7b3dSmrg	return damage;
1024428d7b3dSmrg}
1025428d7b3dSmrg#else
1026428d7b3dSmrgfastcall struct sna_damage *_sna_damage_add_box(struct sna_damage *damage,
1027428d7b3dSmrg						const BoxRec *box)
1028428d7b3dSmrg{
1029428d7b3dSmrg	return __sna_damage_add_box(damage, box);
1030428d7b3dSmrg}
1031428d7b3dSmrg#endif
1032428d7b3dSmrg
1033428d7b3dSmrgstruct sna_damage *__sna_damage_all(struct sna_damage *damage,
1034428d7b3dSmrg				    int width, int height)
1035428d7b3dSmrg{
1036428d7b3dSmrg	DBG(("%s(%d, %d)\n", __FUNCTION__, width, height));
1037428d7b3dSmrg
1038428d7b3dSmrg	if (damage) {
1039428d7b3dSmrg		pixman_region_fini(&damage->region);
1040428d7b3dSmrg		free_list(&damage->embedded_box.list);
1041428d7b3dSmrg		reset_embedded_box(damage);
1042428d7b3dSmrg	} else {
1043428d7b3dSmrg		damage = _sna_damage_create();
1044428d7b3dSmrg		if (damage == NULL)
1045428d7b3dSmrg			return NULL;
1046428d7b3dSmrg	}
1047428d7b3dSmrg
1048428d7b3dSmrg	pixman_region_init_rect(&damage->region, 0, 0, width, height);
1049428d7b3dSmrg	damage->extents = damage->region.extents;
1050428d7b3dSmrg	damage->mode = DAMAGE_ALL;
1051428d7b3dSmrg
1052428d7b3dSmrg	return damage;
1053428d7b3dSmrg}
1054428d7b3dSmrg
1055428d7b3dSmrgstruct sna_damage *_sna_damage_is_all(struct sna_damage *damage,
1056428d7b3dSmrg				      int width, int height)
1057428d7b3dSmrg{
1058428d7b3dSmrg	DBG(("%s(%d, %d)%s?\n", __FUNCTION__, width, height,
1059428d7b3dSmrg	     damage->dirty ? "*" : ""));
1060428d7b3dSmrg	DBG(("%s: (%d, %d), (%d, %d)\n", __FUNCTION__,
1061428d7b3dSmrg	     damage->extents.x1, damage->extents.y1,
1062428d7b3dSmrg	     damage->extents.x2, damage->extents.y2));
1063428d7b3dSmrg
1064428d7b3dSmrg	assert(damage->mode == DAMAGE_ADD);
1065428d7b3dSmrg	assert(damage->extents.x1 == 0 &&
1066428d7b3dSmrg	       damage->extents.y1 == 0 &&
1067428d7b3dSmrg	       damage->extents.x2 == width &&
1068428d7b3dSmrg	       damage->extents.y2 == height);
1069428d7b3dSmrg
1070428d7b3dSmrg	if (damage->dirty) {
1071428d7b3dSmrg		__sna_damage_reduce(damage);
1072428d7b3dSmrg		assert(RegionNotEmpty(&damage->region));
1073428d7b3dSmrg	}
1074428d7b3dSmrg
1075428d7b3dSmrg	if (damage->region.data) {
1076428d7b3dSmrg		DBG(("%s: no, not singular\n", __FUNCTION__));
1077428d7b3dSmrg		return damage;
1078428d7b3dSmrg	}
1079428d7b3dSmrg
1080428d7b3dSmrg	assert(damage->extents.x1 == 0 &&
1081428d7b3dSmrg	       damage->extents.y1 == 0 &&
1082428d7b3dSmrg	       damage->extents.x2 == width &&
1083428d7b3dSmrg	       damage->extents.y2 == height);
1084428d7b3dSmrg
1085428d7b3dSmrg	return __sna_damage_all(damage, width, height);
1086428d7b3dSmrg}
1087428d7b3dSmrg
1088428d7b3dSmrgstatic bool box_contains(const BoxRec *a, const BoxRec *b)
1089428d7b3dSmrg{
1090428d7b3dSmrg	if (b->x1 < a->x1 || b->x2 > a->x2)
1091428d7b3dSmrg		return false;
1092428d7b3dSmrg
1093428d7b3dSmrg	if (b->y1 < a->y1 || b->y2 > a->y2)
1094428d7b3dSmrg		return false;
1095428d7b3dSmrg
1096428d7b3dSmrg	return true;
1097428d7b3dSmrg}
1098428d7b3dSmrg
1099428d7b3dSmrgstatic struct sna_damage *__sna_damage_subtract(struct sna_damage *damage,
1100428d7b3dSmrg						RegionPtr region)
1101428d7b3dSmrg{
1102428d7b3dSmrg	if (damage == NULL)
1103428d7b3dSmrg		return NULL;
1104428d7b3dSmrg
1105428d7b3dSmrg	if (RegionNil(&damage->region)) {
1106428d7b3dSmrgno_damage:
1107428d7b3dSmrg		__sna_damage_destroy(damage);
1108428d7b3dSmrg		return NULL;
1109428d7b3dSmrg	}
1110428d7b3dSmrg
1111428d7b3dSmrg	assert(RegionNotEmpty(region));
1112428d7b3dSmrg
1113428d7b3dSmrg	if (!sna_damage_overlaps_box(damage, &region->extents))
1114428d7b3dSmrg		return damage;
1115428d7b3dSmrg
1116428d7b3dSmrg	if (region_is_singular(region) &&
1117428d7b3dSmrg	    box_contains(&region->extents, &damage->extents))
1118428d7b3dSmrg		goto no_damage;
1119428d7b3dSmrg
1120428d7b3dSmrg	if (damage->mode == DAMAGE_ALL) {
1121428d7b3dSmrg		pixman_region_subtract(&damage->region,
1122428d7b3dSmrg				       &damage->region,
1123428d7b3dSmrg				       region);
1124428d7b3dSmrg		if (damage->region.extents.x2 <= damage->region.extents.x1 ||
1125428d7b3dSmrg		    damage->region.extents.y2 <= damage->region.extents.y1)
1126428d7b3dSmrg			goto no_damage;
1127428d7b3dSmrg
1128428d7b3dSmrg		damage->extents = damage->region.extents;
1129428d7b3dSmrg		damage->mode = DAMAGE_ADD;
1130428d7b3dSmrg		return damage;
1131428d7b3dSmrg	}
1132428d7b3dSmrg
1133428d7b3dSmrg	if (damage->mode != DAMAGE_SUBTRACT) {
1134428d7b3dSmrg		if (damage->dirty) {
1135428d7b3dSmrg			__sna_damage_reduce(damage);
1136428d7b3dSmrg			assert(RegionNotEmpty(&damage->region));
1137428d7b3dSmrg		}
1138428d7b3dSmrg
1139428d7b3dSmrg		if (pixman_region_equal(region, &damage->region))
1140428d7b3dSmrg			goto no_damage;
1141428d7b3dSmrg
1142428d7b3dSmrg		if (region_is_singular(&damage->region) &&
1143428d7b3dSmrg		    region_is_singular(region)) {
1144428d7b3dSmrg			pixman_region_subtract(&damage->region,
1145428d7b3dSmrg					       &damage->region,
1146428d7b3dSmrg					       region);
1147428d7b3dSmrg			if (damage->region.extents.x2 <= damage->region.extents.x1 ||
1148428d7b3dSmrg			    damage->region.extents.y2 <= damage->region.extents.y1)
1149428d7b3dSmrg				goto no_damage;
1150428d7b3dSmrg
1151428d7b3dSmrg			damage->extents = damage->region.extents;
1152428d7b3dSmrg			assert(pixman_region_not_empty(&damage->region));
1153428d7b3dSmrg			return damage;
1154428d7b3dSmrg		}
1155428d7b3dSmrg
1156428d7b3dSmrg		damage->mode = DAMAGE_SUBTRACT;
1157428d7b3dSmrg	}
1158428d7b3dSmrg
1159428d7b3dSmrg	return _sna_damage_create_elt(damage,
1160428d7b3dSmrg				      region_rects(region),
1161428d7b3dSmrg				      region_num_rects(region));
1162428d7b3dSmrg}
1163428d7b3dSmrg
1164428d7b3dSmrg#if HAS_DEBUG_FULL
1165428d7b3dSmrgfastcall struct sna_damage *_sna_damage_subtract(struct sna_damage *damage,
1166428d7b3dSmrg						 RegionPtr region)
1167428d7b3dSmrg{
1168428d7b3dSmrg	char damage_buf[1000];
1169428d7b3dSmrg	char region_buf[120];
1170428d7b3dSmrg
1171428d7b3dSmrg	DBG(("%s(%s - %s)...\n", __FUNCTION__,
1172428d7b3dSmrg	       _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1173428d7b3dSmrg	       _debug_describe_region(region_buf, sizeof(region_buf), region)));
1174428d7b3dSmrg
1175428d7b3dSmrg	damage = __sna_damage_subtract(damage, region);
1176428d7b3dSmrg
1177428d7b3dSmrg	DBG(("  = %s\n",
1178428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1179428d7b3dSmrg
1180428d7b3dSmrg	return damage;
1181428d7b3dSmrg}
1182428d7b3dSmrg#else
1183428d7b3dSmrgfastcall struct sna_damage *_sna_damage_subtract(struct sna_damage *damage,
1184428d7b3dSmrg						 RegionPtr region)
1185428d7b3dSmrg{
1186428d7b3dSmrg	return __sna_damage_subtract(damage, region);
1187428d7b3dSmrg}
1188428d7b3dSmrg#endif
1189428d7b3dSmrg
1190428d7b3dSmrginline static struct sna_damage *__sna_damage_subtract_box(struct sna_damage *damage,
1191428d7b3dSmrg							   const BoxRec *box)
1192428d7b3dSmrg{
1193428d7b3dSmrg	assert(box->x2 > box->x1 && box->y2 > box->y1);
1194428d7b3dSmrg
1195428d7b3dSmrg	if (damage == NULL)
1196428d7b3dSmrg		return NULL;
1197428d7b3dSmrg
1198428d7b3dSmrg	if (RegionNil(&damage->region)) {
1199428d7b3dSmrg		__sna_damage_destroy(damage);
1200428d7b3dSmrg		return NULL;
1201428d7b3dSmrg	}
1202428d7b3dSmrg
1203428d7b3dSmrg	if (!sna_damage_overlaps_box(damage, box))
1204428d7b3dSmrg		return damage;
1205428d7b3dSmrg
1206428d7b3dSmrg	if (box_contains(box, &damage->extents)) {
1207428d7b3dSmrg		__sna_damage_destroy(damage);
1208428d7b3dSmrg		return NULL;
1209428d7b3dSmrg	}
1210428d7b3dSmrg
1211428d7b3dSmrg	if (damage->mode != DAMAGE_SUBTRACT) {
1212428d7b3dSmrg		if (damage->dirty) {
1213428d7b3dSmrg			__sna_damage_reduce(damage);
1214428d7b3dSmrg			assert(RegionNotEmpty(&damage->region));
1215428d7b3dSmrg		}
1216428d7b3dSmrg
1217428d7b3dSmrg		if (region_is_singular(&damage->region)) {
1218428d7b3dSmrg			pixman_region16_t region;
1219428d7b3dSmrg
1220428d7b3dSmrg			pixman_region_init_rects(&region, box, 1);
1221428d7b3dSmrg			pixman_region_subtract(&damage->region,
1222428d7b3dSmrg					       &damage->region,
1223428d7b3dSmrg					       &region);
1224428d7b3dSmrg			damage->extents = damage->region.extents;
1225428d7b3dSmrg			damage->mode = DAMAGE_ADD;
1226428d7b3dSmrg			return damage;
1227428d7b3dSmrg		}
1228428d7b3dSmrg
1229428d7b3dSmrg		damage->mode = DAMAGE_SUBTRACT;
1230428d7b3dSmrg	}
1231428d7b3dSmrg
1232428d7b3dSmrg	return _sna_damage_create_elt(damage, box, 1);
1233428d7b3dSmrg}
1234428d7b3dSmrg
1235428d7b3dSmrg#if HAS_DEBUG_FULL
1236428d7b3dSmrgfastcall struct sna_damage *_sna_damage_subtract_box(struct sna_damage *damage,
1237428d7b3dSmrg						     const BoxRec *box)
1238428d7b3dSmrg{
1239428d7b3dSmrg	char damage_buf[1000];
1240428d7b3dSmrg
1241428d7b3dSmrg	DBG(("%s(%s - (%d, %d), (%d, %d))...\n", __FUNCTION__,
1242428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1243428d7b3dSmrg	     box->x1, box->y1, box->x2, box->y2));
1244428d7b3dSmrg
1245428d7b3dSmrg	damage = __sna_damage_subtract_box(damage, box);
1246428d7b3dSmrg
1247428d7b3dSmrg	DBG(("  = %s\n",
1248428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1249428d7b3dSmrg
1250428d7b3dSmrg	return damage;
1251428d7b3dSmrg}
1252428d7b3dSmrg#else
1253428d7b3dSmrgfastcall struct sna_damage *_sna_damage_subtract_box(struct sna_damage *damage,
1254428d7b3dSmrg						     const BoxRec *box)
1255428d7b3dSmrg{
1256428d7b3dSmrg	return __sna_damage_subtract_box(damage, box);
1257428d7b3dSmrg}
1258428d7b3dSmrg#endif
1259428d7b3dSmrg
1260428d7b3dSmrgstatic struct sna_damage *__sna_damage_subtract_boxes(struct sna_damage *damage,
1261428d7b3dSmrg						      const BoxRec *box, int n,
1262428d7b3dSmrg						      int dx, int dy)
1263428d7b3dSmrg{
1264428d7b3dSmrg	BoxRec extents;
1265428d7b3dSmrg	int i;
1266428d7b3dSmrg
1267428d7b3dSmrg	if (damage == NULL)
1268428d7b3dSmrg		return NULL;
1269428d7b3dSmrg
1270428d7b3dSmrg	if (RegionNil(&damage->region)) {
1271428d7b3dSmrg		__sna_damage_destroy(damage);
1272428d7b3dSmrg		return NULL;
1273428d7b3dSmrg	}
1274428d7b3dSmrg
1275428d7b3dSmrg	assert(n);
1276428d7b3dSmrg
1277428d7b3dSmrg	assert(box[0].x2 > box[0].x1 && box[0].y2 > box[0].y1);
1278428d7b3dSmrg	extents = box[0];
1279428d7b3dSmrg	for (i = 1; i < n; i++) {
1280428d7b3dSmrg		assert(box[i].x2 > box[i].x1 && box[i].y2 > box[i].y1);
1281428d7b3dSmrg		if (extents.x1 > box[i].x1)
1282428d7b3dSmrg			extents.x1 = box[i].x1;
1283428d7b3dSmrg		if (extents.x2 < box[i].x2)
1284428d7b3dSmrg			extents.x2 = box[i].x2;
1285428d7b3dSmrg		if (extents.y1 > box[i].y1)
1286428d7b3dSmrg			extents.y1 = box[i].y1;
1287428d7b3dSmrg		if (extents.y2 < box[i].y2)
1288428d7b3dSmrg			extents.y2 = box[i].y2;
1289428d7b3dSmrg	}
1290428d7b3dSmrg
1291428d7b3dSmrg	assert(extents.y2 > extents.y1 && extents.x2 > extents.x1);
1292428d7b3dSmrg
1293428d7b3dSmrg	extents.x1 += dx;
1294428d7b3dSmrg	extents.x2 += dx;
1295428d7b3dSmrg	extents.y1 += dy;
1296428d7b3dSmrg	extents.y2 += dy;
1297428d7b3dSmrg
1298428d7b3dSmrg	if (!sna_damage_overlaps_box(damage, &extents))
1299428d7b3dSmrg		return damage;
1300428d7b3dSmrg
1301428d7b3dSmrg	if (n == 1)
1302428d7b3dSmrg		return __sna_damage_subtract_box(damage, &extents);
1303428d7b3dSmrg
1304428d7b3dSmrg	if (damage->mode != DAMAGE_SUBTRACT) {
1305428d7b3dSmrg		if (damage->dirty) {
1306428d7b3dSmrg			__sna_damage_reduce(damage);
1307428d7b3dSmrg			assert(RegionNotEmpty(&damage->region));
1308428d7b3dSmrg		}
1309428d7b3dSmrg
1310428d7b3dSmrg		damage->mode = DAMAGE_SUBTRACT;
1311428d7b3dSmrg	}
1312428d7b3dSmrg
1313428d7b3dSmrg	return _sna_damage_create_elt_from_boxes(damage, box, n, dx, dy);
1314428d7b3dSmrg}
1315428d7b3dSmrg
1316428d7b3dSmrg#if HAS_DEBUG_FULL
1317428d7b3dSmrgfastcall struct sna_damage *_sna_damage_subtract_boxes(struct sna_damage *damage,
1318428d7b3dSmrg						       const BoxRec *box, int n,
1319428d7b3dSmrg						       int dx, int dy)
1320428d7b3dSmrg{
1321428d7b3dSmrg	char damage_buf[1000];
1322428d7b3dSmrg
1323428d7b3dSmrg	DBG(("%s(%s - [(%d,%d), (%d,%d)...x%d])...\n", __FUNCTION__,
1324428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1325428d7b3dSmrg	     box->x1 + dx, box->y1 + dy,
1326428d7b3dSmrg	     box->x2 + dx, box->y2 + dy,
1327428d7b3dSmrg	     n));
1328428d7b3dSmrg
1329428d7b3dSmrg	damage = __sna_damage_subtract_boxes(damage, box, n, dx, dy);
1330428d7b3dSmrg
1331428d7b3dSmrg	DBG(("  = %s\n",
1332428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1333428d7b3dSmrg
1334428d7b3dSmrg	return damage;
1335428d7b3dSmrg}
1336428d7b3dSmrg#else
1337428d7b3dSmrgfastcall struct sna_damage *_sna_damage_subtract_boxes(struct sna_damage *damage,
1338428d7b3dSmrg						       const BoxRec *box, int n,
1339428d7b3dSmrg						       int dx, int dy)
1340428d7b3dSmrg{
1341428d7b3dSmrg	return __sna_damage_subtract_boxes(damage, box, n, dx, dy);
1342428d7b3dSmrg}
1343428d7b3dSmrg#endif
1344428d7b3dSmrg
1345428d7b3dSmrgstatic int __sna_damage_contains_box(struct sna_damage **_damage,
1346428d7b3dSmrg				     const BoxRec *box)
1347428d7b3dSmrg{
1348428d7b3dSmrg	struct sna_damage *damage = *_damage;
1349428d7b3dSmrg	const BoxRec *b;
1350428d7b3dSmrg	int n, count, ret;
1351428d7b3dSmrg
1352428d7b3dSmrg	if (damage->mode == DAMAGE_ALL)
1353428d7b3dSmrg		return PIXMAN_REGION_IN;
1354428d7b3dSmrg
1355428d7b3dSmrg	if (!sna_damage_overlaps_box(damage, box))
1356428d7b3dSmrg		return PIXMAN_REGION_OUT;
1357428d7b3dSmrg
1358428d7b3dSmrg	ret = pixman_region_contains_rectangle(&damage->region, (BoxPtr)box);
1359428d7b3dSmrg	if (!damage->dirty)
1360428d7b3dSmrg		return ret;
1361428d7b3dSmrg
1362428d7b3dSmrg	if (damage->mode == DAMAGE_ADD) {
1363428d7b3dSmrg		if (ret == PIXMAN_REGION_IN)
1364428d7b3dSmrg			return ret;
1365428d7b3dSmrg
1366428d7b3dSmrg		count = damage->embedded_box.size;
1367428d7b3dSmrg		if (list_is_empty(&damage->embedded_box.list))
1368428d7b3dSmrg			count -= damage->remain;
1369428d7b3dSmrg
1370428d7b3dSmrg		b = damage->embedded_box.box;
1371428d7b3dSmrg		for (n = 0; n < count; n++) {
1372428d7b3dSmrg			if (box_contains(&b[n], box))
1373428d7b3dSmrg				return PIXMAN_REGION_IN;
1374428d7b3dSmrg		}
1375428d7b3dSmrg	} else {
1376428d7b3dSmrg		if (ret == PIXMAN_REGION_OUT)
1377428d7b3dSmrg			return ret;
1378428d7b3dSmrg
1379428d7b3dSmrg		count = damage->embedded_box.size;
1380428d7b3dSmrg		if (list_is_empty(&damage->embedded_box.list))
1381428d7b3dSmrg			count -= damage->remain;
1382428d7b3dSmrg
1383428d7b3dSmrg		b = damage->embedded_box.box;
1384428d7b3dSmrg		for (n = 0; n < count; n++) {
1385428d7b3dSmrg			if (box_contains(&b[n], box))
1386428d7b3dSmrg				return PIXMAN_REGION_OUT;
1387428d7b3dSmrg		}
1388428d7b3dSmrg	}
1389428d7b3dSmrg
1390428d7b3dSmrg	__sna_damage_reduce(damage);
1391428d7b3dSmrg	if (!pixman_region_not_empty(&damage->region)) {
1392428d7b3dSmrg		__sna_damage_destroy(damage);
1393428d7b3dSmrg		*_damage = NULL;
1394428d7b3dSmrg		return PIXMAN_REGION_OUT;
1395428d7b3dSmrg	}
1396428d7b3dSmrg
1397428d7b3dSmrg	return pixman_region_contains_rectangle(&damage->region, (BoxPtr)box);
1398428d7b3dSmrg}
1399428d7b3dSmrg
1400428d7b3dSmrg#if HAS_DEBUG_FULL
1401428d7b3dSmrgint _sna_damage_contains_box(struct sna_damage **damage,
1402428d7b3dSmrg			     const BoxRec *box)
1403428d7b3dSmrg{
1404428d7b3dSmrg	char damage_buf[1000];
1405428d7b3dSmrg	int ret;
1406428d7b3dSmrg
1407428d7b3dSmrg	DBG(("%s(%s, [(%d, %d), (%d, %d)])\n", __FUNCTION__,
1408428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), *damage),
1409428d7b3dSmrg	     box->x1, box->y1, box->x2, box->y2));
1410428d7b3dSmrg
1411428d7b3dSmrg	ret = __sna_damage_contains_box(damage, box);
1412428d7b3dSmrg	DBG(("  = %d", ret));
1413428d7b3dSmrg	if (ret)
1414428d7b3dSmrg		DBG((" [(%d, %d), (%d, %d)...]",
1415428d7b3dSmrg		     box->x1, box->y1, box->x2, box->y2));
1416428d7b3dSmrg	DBG(("\n"));
1417428d7b3dSmrg
1418428d7b3dSmrg	return ret;
1419428d7b3dSmrg}
1420428d7b3dSmrg#else
1421428d7b3dSmrgint _sna_damage_contains_box(struct sna_damage **damage,
1422428d7b3dSmrg			     const BoxRec *box)
1423428d7b3dSmrg{
1424428d7b3dSmrg	return __sna_damage_contains_box(damage, box);
1425428d7b3dSmrg}
1426428d7b3dSmrg#endif
1427428d7b3dSmrg
1428428d7b3dSmrgstatic bool box_overlaps(const BoxRec *a, const BoxRec *b)
1429428d7b3dSmrg{
1430428d7b3dSmrg	return (a->x1 < b->x2 && a->x2 > b->x1 &&
1431428d7b3dSmrg		a->y1 < b->y2 && a->y2 > b->y1);
1432428d7b3dSmrg}
1433428d7b3dSmrg
1434428d7b3dSmrgbool _sna_damage_contains_box__no_reduce(const struct sna_damage *damage,
1435428d7b3dSmrg					 const BoxRec *box)
1436428d7b3dSmrg{
1437428d7b3dSmrg	int n, count;
1438428d7b3dSmrg	const BoxRec *b;
1439428d7b3dSmrg
1440428d7b3dSmrg	assert(damage && damage->mode != DAMAGE_ALL);
1441428d7b3dSmrg	if (!box_contains(&damage->extents, box))
1442428d7b3dSmrg		return false;
1443428d7b3dSmrg
1444428d7b3dSmrg	n = pixman_region_contains_rectangle((pixman_region16_t *)&damage->region, (BoxPtr)box);
1445428d7b3dSmrg	if (!damage->dirty)
1446428d7b3dSmrg		return n == PIXMAN_REGION_IN;
1447428d7b3dSmrg
1448428d7b3dSmrg	if (damage->mode == DAMAGE_ADD) {
1449428d7b3dSmrg		if (n == PIXMAN_REGION_IN)
1450428d7b3dSmrg			return true;
1451428d7b3dSmrg
1452428d7b3dSmrg		count = damage->embedded_box.size;
1453428d7b3dSmrg		if (list_is_empty(&damage->embedded_box.list))
1454428d7b3dSmrg			count -= damage->remain;
1455428d7b3dSmrg
1456428d7b3dSmrg		b = damage->embedded_box.box;
1457428d7b3dSmrg		for (n = 0; n < count; n++) {
1458428d7b3dSmrg			if (box_contains(&b[n], box))
1459428d7b3dSmrg				return true;
1460428d7b3dSmrg		}
1461428d7b3dSmrg
1462428d7b3dSmrg		return false;
1463428d7b3dSmrg	} else {
1464428d7b3dSmrg		if (n != PIXMAN_REGION_IN)
1465428d7b3dSmrg			return false;
1466428d7b3dSmrg
1467428d7b3dSmrg		if (!list_is_empty(&damage->embedded_box.list))
1468428d7b3dSmrg			return false;
1469428d7b3dSmrg
1470428d7b3dSmrg		count = damage->embedded_box.size - damage->remain;
1471428d7b3dSmrg		b = damage->embedded_box.box;
1472428d7b3dSmrg		for (n = 0; n < count; n++) {
1473428d7b3dSmrg			if (box_overlaps(&b[n], box))
1474428d7b3dSmrg				return false;
1475428d7b3dSmrg		}
1476428d7b3dSmrg
1477428d7b3dSmrg		return true;
1478428d7b3dSmrg	}
1479428d7b3dSmrg}
1480428d7b3dSmrg
1481428d7b3dSmrgstatic bool __sna_damage_intersect(struct sna_damage *damage,
1482428d7b3dSmrg				   RegionPtr region, RegionPtr result)
1483428d7b3dSmrg{
1484428d7b3dSmrg	assert(damage && damage->mode != DAMAGE_ALL);
1485428d7b3dSmrg	assert(RegionNotEmpty(region));
1486428d7b3dSmrg
1487428d7b3dSmrg	if (region->extents.x2 <= damage->extents.x1 ||
1488428d7b3dSmrg	    region->extents.x1 >= damage->extents.x2)
1489428d7b3dSmrg		return false;
1490428d7b3dSmrg
1491428d7b3dSmrg	if (region->extents.y2 <= damage->extents.y1 ||
1492428d7b3dSmrg	    region->extents.y1 >= damage->extents.y2)
1493428d7b3dSmrg		return false;
1494428d7b3dSmrg
1495428d7b3dSmrg	if (damage->dirty)
1496428d7b3dSmrg		__sna_damage_reduce(damage);
1497428d7b3dSmrg
1498428d7b3dSmrg	if (!pixman_region_not_empty(&damage->region))
1499428d7b3dSmrg		return false;
1500428d7b3dSmrg
1501428d7b3dSmrg	RegionNull(result);
1502428d7b3dSmrg	RegionIntersect(result, &damage->region, region);
1503428d7b3dSmrg
1504428d7b3dSmrg	return RegionNotEmpty(result);
1505428d7b3dSmrg}
1506428d7b3dSmrg
1507428d7b3dSmrg#if HAS_DEBUG_FULL
1508428d7b3dSmrgbool _sna_damage_intersect(struct sna_damage *damage,
1509428d7b3dSmrg			   RegionPtr region, RegionPtr result)
1510428d7b3dSmrg{
1511428d7b3dSmrg	char damage_buf[1000];
1512428d7b3dSmrg	char region_buf[120];
1513428d7b3dSmrg	bool ret;
1514428d7b3dSmrg
1515428d7b3dSmrg	DBG(("%s(%s, %s)...\n", __FUNCTION__,
1516428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
1517428d7b3dSmrg	     _debug_describe_region(region_buf, sizeof(region_buf), region)));
1518428d7b3dSmrg
1519428d7b3dSmrg	ret = __sna_damage_intersect(damage, region, result);
1520428d7b3dSmrg	if (ret)
1521428d7b3dSmrg		DBG(("  = %s\n",
1522428d7b3dSmrg		     _debug_describe_region(region_buf, sizeof(region_buf), result)));
1523428d7b3dSmrg	else
1524428d7b3dSmrg		DBG(("  = none\n"));
1525428d7b3dSmrg
1526428d7b3dSmrg	return ret;
1527428d7b3dSmrg}
1528428d7b3dSmrg#else
1529428d7b3dSmrgbool _sna_damage_intersect(struct sna_damage *damage,
1530428d7b3dSmrg			  RegionPtr region, RegionPtr result)
1531428d7b3dSmrg{
1532428d7b3dSmrg	return __sna_damage_intersect(damage, region, result);
1533428d7b3dSmrg}
1534428d7b3dSmrg#endif
1535428d7b3dSmrg
1536428d7b3dSmrgstatic int __sna_damage_get_boxes(struct sna_damage *damage, const BoxRec **boxes)
1537428d7b3dSmrg{
1538428d7b3dSmrg	assert(damage && damage->mode != DAMAGE_ALL);
1539428d7b3dSmrg
1540428d7b3dSmrg	if (damage->dirty)
1541428d7b3dSmrg		__sna_damage_reduce(damage);
1542428d7b3dSmrg
1543428d7b3dSmrg	assert(!damage->dirty);
1544428d7b3dSmrg	assert(damage->mode == DAMAGE_ADD);
1545428d7b3dSmrg
1546428d7b3dSmrg	*boxes = region_rects(&damage->region);
1547428d7b3dSmrg	return region_num_rects(&damage->region);
1548428d7b3dSmrg}
1549428d7b3dSmrg
1550428d7b3dSmrgstruct sna_damage *_sna_damage_reduce(struct sna_damage *damage)
1551428d7b3dSmrg{
1552428d7b3dSmrg	DBG(("%s\n", __FUNCTION__));
1553428d7b3dSmrg
1554428d7b3dSmrg	__sna_damage_reduce(damage);
1555428d7b3dSmrg
1556428d7b3dSmrg	assert(!damage->dirty);
1557428d7b3dSmrg	assert(damage->mode == DAMAGE_ADD);
1558428d7b3dSmrg
1559428d7b3dSmrg	if (!pixman_region_not_empty(&damage->region)) {
1560428d7b3dSmrg		__sna_damage_destroy(damage);
1561428d7b3dSmrg		damage = NULL;
1562428d7b3dSmrg	}
1563428d7b3dSmrg
1564428d7b3dSmrg	return damage;
1565428d7b3dSmrg}
1566428d7b3dSmrg
1567428d7b3dSmrg#if HAS_DEBUG_FULL
1568428d7b3dSmrgint _sna_damage_get_boxes(struct sna_damage *damage, const BoxRec **boxes)
1569428d7b3dSmrg{
1570428d7b3dSmrg	char damage_buf[1000];
1571428d7b3dSmrg	int count;
1572428d7b3dSmrg
1573428d7b3dSmrg	DBG(("%s(%s)...\n", __FUNCTION__,
1574428d7b3dSmrg	     _debug_describe_damage(damage_buf, sizeof(damage_buf), damage)));
1575428d7b3dSmrg
1576428d7b3dSmrg	count = __sna_damage_get_boxes(damage, boxes);
1577428d7b3dSmrg	DBG(("  = %d\n", count));
1578428d7b3dSmrg
1579428d7b3dSmrg	return count;
1580428d7b3dSmrg}
1581428d7b3dSmrg#else
1582428d7b3dSmrgint _sna_damage_get_boxes(struct sna_damage *damage, const BoxRec **boxes)
1583428d7b3dSmrg{
1584428d7b3dSmrg	return __sna_damage_get_boxes(damage, boxes);
1585428d7b3dSmrg}
1586428d7b3dSmrg#endif
1587428d7b3dSmrg
1588428d7b3dSmrgstruct sna_damage *_sna_damage_combine(struct sna_damage *l,
1589428d7b3dSmrg				       struct sna_damage *r,
1590428d7b3dSmrg				       int dx, int dy)
1591428d7b3dSmrg{
1592428d7b3dSmrg	if (r->dirty)
1593428d7b3dSmrg		__sna_damage_reduce(r);
1594428d7b3dSmrg
1595428d7b3dSmrg	if (pixman_region_not_empty(&r->region)) {
1596428d7b3dSmrg		pixman_region_translate(&r->region, dx, dy);
1597428d7b3dSmrg		l = __sna_damage_add(l, &r->region);
1598428d7b3dSmrg	}
1599428d7b3dSmrg
1600428d7b3dSmrg	return l;
1601428d7b3dSmrg}
1602428d7b3dSmrg
1603428d7b3dSmrgvoid __sna_damage_destroy(struct sna_damage *damage)
1604428d7b3dSmrg{
1605428d7b3dSmrg	free_list(&damage->embedded_box.list);
1606428d7b3dSmrg
1607428d7b3dSmrg	pixman_region_fini(&damage->region);
1608428d7b3dSmrg	*(void **)damage = __freed_damage;
1609428d7b3dSmrg	__freed_damage = damage;
1610428d7b3dSmrg}
1611428d7b3dSmrg
1612428d7b3dSmrg#if TEST_DAMAGE && HAS_DEBUG_FULL
1613428d7b3dSmrgstruct sna_damage_selftest{
1614428d7b3dSmrg	int width, height;
1615428d7b3dSmrg};
1616428d7b3dSmrg
1617428d7b3dSmrgstatic void st_damage_init_random_box(struct sna_damage_selftest *test,
1618428d7b3dSmrg				      BoxPtr box)
1619428d7b3dSmrg{
1620428d7b3dSmrg	int x, y, w, h;
1621428d7b3dSmrg
1622428d7b3dSmrg	if (test->width == 1) {
1623428d7b3dSmrg		x = 0, w = 1;
1624428d7b3dSmrg	} else {
1625428d7b3dSmrg		x = rand() % (test->width - 1);
1626428d7b3dSmrg		w = 1 + rand() % (test->width - x - 1);
1627428d7b3dSmrg	}
1628428d7b3dSmrg
1629428d7b3dSmrg	if (test->height == 1) {
1630428d7b3dSmrg		y = 0, h = 1;
1631428d7b3dSmrg	} else {
1632428d7b3dSmrg		y = rand() % (test->height - 1);
1633428d7b3dSmrg		h = 1 + rand() % (test->height - y - 1);
1634428d7b3dSmrg	}
1635428d7b3dSmrg
1636428d7b3dSmrg	box->x1 = x;
1637428d7b3dSmrg	box->x2 = x+w;
1638428d7b3dSmrg
1639428d7b3dSmrg	box->y1 = y;
1640428d7b3dSmrg	box->y2 = y+h;
1641428d7b3dSmrg}
1642428d7b3dSmrg
1643428d7b3dSmrgstatic void st_damage_init_random_region1(struct sna_damage_selftest *test,
1644428d7b3dSmrg					  pixman_region16_t *region)
1645428d7b3dSmrg{
1646428d7b3dSmrg	int x, y, w, h;
1647428d7b3dSmrg
1648428d7b3dSmrg	if (test->width == 1) {
1649428d7b3dSmrg		x = 0, w = 1;
1650428d7b3dSmrg	} else {
1651428d7b3dSmrg		x = rand() % (test->width - 1);
1652428d7b3dSmrg		w = 1 + rand() % (test->width - x - 1);
1653428d7b3dSmrg	}
1654428d7b3dSmrg
1655428d7b3dSmrg	if (test->height == 1) {
1656428d7b3dSmrg		y = 0, h = 1;
1657428d7b3dSmrg	} else {
1658428d7b3dSmrg		y = rand() % (test->height - 1);
1659428d7b3dSmrg		h = 1 + rand() % (test->height - y - 1);
1660428d7b3dSmrg	}
1661428d7b3dSmrg
1662428d7b3dSmrg	pixman_region_init_rect(region, x, y, w, h);
1663428d7b3dSmrg}
1664428d7b3dSmrg
1665428d7b3dSmrgstatic void st_damage_add(struct sna_damage_selftest *test,
1666428d7b3dSmrg			  struct sna_damage **damage,
1667428d7b3dSmrg			  pixman_region16_t *region)
1668428d7b3dSmrg{
1669428d7b3dSmrg	pixman_region16_t tmp;
1670428d7b3dSmrg
1671428d7b3dSmrg	st_damage_init_random_region1(test, &tmp);
1672428d7b3dSmrg
1673428d7b3dSmrg	if (!DAMAGE_IS_ALL(*damage))
1674428d7b3dSmrg		sna_damage_add(damage, &tmp);
1675428d7b3dSmrg	pixman_region_union(region, region, &tmp);
1676428d7b3dSmrg}
1677428d7b3dSmrg
1678428d7b3dSmrgstatic void st_damage_add_box(struct sna_damage_selftest *test,
1679428d7b3dSmrg			      struct sna_damage **damage,
1680428d7b3dSmrg			      pixman_region16_t *region)
1681428d7b3dSmrg{
1682428d7b3dSmrg	RegionRec r;
1683428d7b3dSmrg
1684428d7b3dSmrg	st_damage_init_random_box(test, &r.extents);
1685428d7b3dSmrg	r.data = NULL;
1686428d7b3dSmrg
1687428d7b3dSmrg	if (!DAMAGE_IS_ALL(*damage))
1688428d7b3dSmrg		sna_damage_add_box(damage, &r.extents);
1689428d7b3dSmrg	pixman_region_union(region, region, &r);
1690428d7b3dSmrg}
1691428d7b3dSmrg
1692428d7b3dSmrgstatic void st_damage_subtract(struct sna_damage_selftest *test,
1693428d7b3dSmrg			       struct sna_damage **damage,
1694428d7b3dSmrg			       pixman_region16_t *region)
1695428d7b3dSmrg{
1696428d7b3dSmrg	pixman_region16_t tmp;
1697428d7b3dSmrg
1698428d7b3dSmrg	st_damage_init_random_region1(test, &tmp);
1699428d7b3dSmrg
1700428d7b3dSmrg	sna_damage_subtract(damage, &tmp);
1701428d7b3dSmrg	pixman_region_subtract(region, region, &tmp);
1702428d7b3dSmrg}
1703428d7b3dSmrg
1704428d7b3dSmrgstatic void st_damage_subtract_box(struct sna_damage_selftest *test,
1705428d7b3dSmrg				   struct sna_damage **damage,
1706428d7b3dSmrg				   pixman_region16_t *region)
1707428d7b3dSmrg{
1708428d7b3dSmrg	RegionRec r;
1709428d7b3dSmrg
1710428d7b3dSmrg	st_damage_init_random_box(test, &r.extents);
1711428d7b3dSmrg	r.data = NULL;
1712428d7b3dSmrg
1713428d7b3dSmrg	sna_damage_subtract_box(damage, &r.extents);
1714428d7b3dSmrg	pixman_region_subtract(region, region, &r);
1715428d7b3dSmrg}
1716428d7b3dSmrg
1717428d7b3dSmrgstatic void st_damage_all(struct sna_damage_selftest *test,
1718428d7b3dSmrg			  struct sna_damage **damage,
1719428d7b3dSmrg			  pixman_region16_t *region)
1720428d7b3dSmrg{
1721428d7b3dSmrg	pixman_region16_t tmp;
1722428d7b3dSmrg
1723428d7b3dSmrg	pixman_region_init_rect(&tmp, 0, 0, test->width, test->height);
1724428d7b3dSmrg
1725428d7b3dSmrg	if (!DAMAGE_IS_ALL(*damage))
1726428d7b3dSmrg		sna_damage_all(damage, test->width, test->height);
1727428d7b3dSmrg	pixman_region_union(region, region, &tmp);
1728428d7b3dSmrg}
1729428d7b3dSmrg
1730428d7b3dSmrgstatic bool st_check_equal(struct sna_damage_selftest *test,
1731428d7b3dSmrg			   struct sna_damage **damage,
1732428d7b3dSmrg			   pixman_region16_t *region)
1733428d7b3dSmrg{
1734428d7b3dSmrg	int d_num, r_num;
1735428d7b3dSmrg	BoxPtr d_boxes, r_boxes;
1736428d7b3dSmrg
1737428d7b3dSmrg	d_num = *damage ? sna_damage_get_boxes(*damage, &d_boxes) : 0;
1738428d7b3dSmrg	r_boxes = pixman_region_rectangles(region, &r_num);
1739428d7b3dSmrg
1740428d7b3dSmrg	if (d_num != r_num) {
1741428d7b3dSmrg		ERR(("%s: damage and ref contain different number of rectangles\n",
1742428d7b3dSmrg		     __FUNCTION__));
1743428d7b3dSmrg		return false;
1744428d7b3dSmrg	}
1745428d7b3dSmrg
1746428d7b3dSmrg	if (memcmp(d_boxes, r_boxes, d_num*sizeof(BoxRec))) {
1747428d7b3dSmrg		ERR(("%s: damage and ref contain different rectangles\n",
1748428d7b3dSmrg		     __FUNCTION__));
1749428d7b3dSmrg		return false;
1750428d7b3dSmrg	}
1751428d7b3dSmrg
1752428d7b3dSmrg	return true;
1753428d7b3dSmrg}
1754428d7b3dSmrg
1755428d7b3dSmrgvoid sna_damage_selftest(void)
1756428d7b3dSmrg{
1757428d7b3dSmrg	void (*const op[])(struct sna_damage_selftest *test,
1758428d7b3dSmrg			   struct sna_damage **damage,
1759428d7b3dSmrg			   pixman_region16_t *region) = {
1760428d7b3dSmrg		st_damage_add,
1761428d7b3dSmrg		st_damage_add_box,
1762428d7b3dSmrg		st_damage_subtract,
1763428d7b3dSmrg		st_damage_subtract_box,
1764428d7b3dSmrg		st_damage_all
1765428d7b3dSmrg	};
1766428d7b3dSmrg	bool (*const check[])(struct sna_damage_selftest *test,
1767428d7b3dSmrg			      struct sna_damage **damage,
1768428d7b3dSmrg			      pixman_region16_t *region) = {
1769428d7b3dSmrg		st_check_equal,
1770428d7b3dSmrg		//st_check_contains,
1771428d7b3dSmrg	};
1772428d7b3dSmrg	char region_buf[120];
1773428d7b3dSmrg	char damage_buf[1000];
1774428d7b3dSmrg	int pass;
1775428d7b3dSmrg
1776428d7b3dSmrg	for (pass = 0; pass < 16384; pass++) {
1777428d7b3dSmrg		struct sna_damage_selftest test;
1778428d7b3dSmrg		struct sna_damage *damage;
1779428d7b3dSmrg		pixman_region16_t ref;
1780428d7b3dSmrg		int iter, i;
1781428d7b3dSmrg
1782428d7b3dSmrg		iter = 1 + rand() % (1 + (pass / 64));
1783428d7b3dSmrg		DBG(("%s: pass %d, iters=%d\n", __FUNCTION__, pass, iter));
1784428d7b3dSmrg
1785428d7b3dSmrg		test.width = 1 + rand() % 2048;
1786428d7b3dSmrg		test.height = 1 + rand() % 2048;
1787428d7b3dSmrg
1788428d7b3dSmrg		damage = _sna_damage_create();
1789428d7b3dSmrg		pixman_region_init(&ref);
1790428d7b3dSmrg
1791428d7b3dSmrg		for (i = 0; i < iter; i++) {
1792428d7b3dSmrg			op[rand() % ARRAY_SIZE(op)](&test, &damage, &ref);
1793428d7b3dSmrg		}
1794428d7b3dSmrg
1795428d7b3dSmrg		if (!check[rand() % ARRAY_SIZE(check)](&test, &damage, &ref)) {
1796428d7b3dSmrg			FatalError("%s: failed - region = %s, damage = %s\n", __FUNCTION__,
1797428d7b3dSmrg				   _debug_describe_region(region_buf, sizeof(region_buf), &ref),
1798428d7b3dSmrg				   _debug_describe_damage(damage_buf, sizeof(damage_buf), damage));
1799428d7b3dSmrg		}
1800428d7b3dSmrg
1801428d7b3dSmrg		pixman_region_fini(&ref);
1802428d7b3dSmrg		sna_damage_destroy(&damage);
1803428d7b3dSmrg	}
1804428d7b3dSmrg}
1805428d7b3dSmrg#endif
1806428d7b3dSmrg
1807428d7b3dSmrgvoid _sna_damage_debug_get_region(struct sna_damage *damage, RegionRec *r)
1808428d7b3dSmrg{
1809428d7b3dSmrg	int n, nboxes;
1810428d7b3dSmrg	BoxPtr boxes;
1811428d7b3dSmrg	struct sna_damage_box *iter;
1812428d7b3dSmrg
1813428d7b3dSmrg	RegionCopy(r, &damage->region);
1814428d7b3dSmrg	if (!damage->dirty)
1815428d7b3dSmrg		return;
1816428d7b3dSmrg
1817428d7b3dSmrg	nboxes = damage->embedded_box.size;
1818428d7b3dSmrg	list_for_each_entry(iter, &damage->embedded_box.list, list)
1819428d7b3dSmrg		nboxes += iter->size;
1820428d7b3dSmrg	nboxes -= damage->remain;
1821428d7b3dSmrg	if (nboxes == 0)
1822428d7b3dSmrg		return;
1823428d7b3dSmrg
1824428d7b3dSmrg	if (nboxes == 1) {
1825428d7b3dSmrg		pixman_region16_t tmp;
1826428d7b3dSmrg
1827428d7b3dSmrg		tmp.extents = damage->embedded_box.box[0];
1828428d7b3dSmrg		tmp.data = NULL;
1829428d7b3dSmrg
1830428d7b3dSmrg		if (damage->mode == DAMAGE_ADD)
1831428d7b3dSmrg			pixman_region_union(r, r, &tmp);
1832428d7b3dSmrg		else
1833428d7b3dSmrg			pixman_region_subtract(r, r, &tmp);
1834428d7b3dSmrg
1835428d7b3dSmrg		return;
1836428d7b3dSmrg	}
1837428d7b3dSmrg
1838428d7b3dSmrg	if (damage->mode == DAMAGE_ADD)
1839428d7b3dSmrg		nboxes += region_num_rects(r);
1840428d7b3dSmrg
1841428d7b3dSmrg	iter = last_box(damage);
1842428d7b3dSmrg	n = iter->size - damage->remain;
1843428d7b3dSmrg	boxes = malloc(sizeof(BoxRec)*nboxes);
1844428d7b3dSmrg	if (boxes == NULL)
1845428d7b3dSmrg		return;
1846428d7b3dSmrg
1847428d7b3dSmrg	if (list_is_empty(&damage->embedded_box.list)) {
1848428d7b3dSmrg		memcpy(boxes,
1849428d7b3dSmrg		       damage->embedded_box.box,
1850428d7b3dSmrg		       n*sizeof(BoxRec));
1851428d7b3dSmrg	} else {
1852428d7b3dSmrg		if (boxes != (BoxPtr)(iter+1))
1853428d7b3dSmrg			memcpy(boxes, iter+1, n*sizeof(BoxRec));
1854428d7b3dSmrg
1855428d7b3dSmrg		iter = list_entry(iter->list.prev,
1856428d7b3dSmrg				  struct sna_damage_box,
1857428d7b3dSmrg				  list);
1858428d7b3dSmrg		while (&iter->list != &damage->embedded_box.list) {
1859428d7b3dSmrg			memcpy(boxes + n, iter+1,
1860428d7b3dSmrg			       iter->size * sizeof(BoxRec));
1861428d7b3dSmrg			n += iter->size;
1862428d7b3dSmrg
1863428d7b3dSmrg			iter = list_entry(iter->list.prev,
1864428d7b3dSmrg					  struct sna_damage_box,
1865428d7b3dSmrg					  list);
1866428d7b3dSmrg		}
1867428d7b3dSmrg
1868428d7b3dSmrg		memcpy(boxes + n,
1869428d7b3dSmrg		       damage->embedded_box.box,
1870428d7b3dSmrg		       sizeof(damage->embedded_box.box));
1871428d7b3dSmrg		n += damage->embedded_box.size;
1872428d7b3dSmrg	}
1873428d7b3dSmrg
1874428d7b3dSmrg	if (damage->mode == DAMAGE_ADD) {
1875428d7b3dSmrg		memcpy(boxes + n,
1876428d7b3dSmrg		       region_rects(r),
1877428d7b3dSmrg		       region_num_rects(r)*sizeof(BoxRec));
1878428d7b3dSmrg		assert(n + region_num_rects(r) == nboxes);
1879428d7b3dSmrg		pixman_region_fini(r);
1880428d7b3dSmrg		pixman_region_init_rects(r, boxes, nboxes);
1881428d7b3dSmrg
1882428d7b3dSmrg		assert(pixman_region_not_empty(r));
1883428d7b3dSmrg		assert(damage->extents.x1 == r->extents.x1 &&
1884428d7b3dSmrg		       damage->extents.y1 == r->extents.y1 &&
1885428d7b3dSmrg		       damage->extents.x2 == r->extents.x2 &&
1886428d7b3dSmrg		       damage->extents.y2 == r->extents.y2);
1887428d7b3dSmrg	} else {
1888428d7b3dSmrg		pixman_region16_t tmp;
1889428d7b3dSmrg
1890428d7b3dSmrg		pixman_region_init_rects(&tmp, boxes, nboxes);
1891428d7b3dSmrg		pixman_region_subtract(r, r, &tmp);
1892428d7b3dSmrg		pixman_region_fini(&tmp);
1893428d7b3dSmrg
1894428d7b3dSmrg		assert(damage->extents.x1 <= r->extents.x1 &&
1895428d7b3dSmrg		       damage->extents.y1 <= r->extents.y1 &&
1896428d7b3dSmrg		       damage->extents.x2 >= r->extents.x2 &&
1897428d7b3dSmrg		       damage->extents.y2 >= r->extents.y2);
1898428d7b3dSmrg	}
1899428d7b3dSmrg
1900428d7b3dSmrg	free(boxes);
1901428d7b3dSmrg}
1902