summaryrefslogtreecommitdiff
path: root/src/sna/sna_damage.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sna/sna_damage.c')
-rw-r--r--src/sna/sna_damage.c944
1 files changed, 944 insertions, 0 deletions
diff --git a/src/sna/sna_damage.c b/src/sna/sna_damage.c
new file mode 100644
index 00000000..21af2d0a
--- /dev/null
+++ b/src/sna/sna_damage.c
@@ -0,0 +1,944 @@
+/**************************************************************************
+
+Copyright (c) 2011 Intel Corporation
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sna.h"
+#include "sna_damage.h"
+
+#if DEBUG_DAMAGE
+#undef DBG
+#define DBG(x) ErrorF x
+
+static const char *_debug_describe_region(char *buf, int max,
+ RegionPtr region)
+{
+ BoxPtr extents;
+ BoxPtr box;
+ int n;
+ int len;
+
+ if (region == NULL)
+ return "nil";
+
+ n = REGION_NUM_RECTS(region);
+ if (n == 0)
+ return "[0]";
+
+ extents = REGION_EXTENTS(NULL, region);
+ if (n == 1) {
+ sprintf(buf,
+ "[(%d, %d), (%d, %d)]",
+ extents->x1, extents->y1,
+ extents->x2, extents->y2);
+ return buf;
+ }
+
+ len = sprintf(buf,
+ "[(%d, %d), (%d, %d) x %d: ",
+ extents->x1, extents->y1,
+ extents->x2, extents->y2,
+ n) + 3;
+ max -= 2;
+ box = REGION_RECTS(region);
+ while (n--) {
+ char tmp[80];
+ int this;
+
+ this = snprintf(tmp, sizeof(tmp),
+ "((%d, %d), (%d, %d))%s",
+ box->x1, box->y1,
+ box->x2, box->y2,
+ n ? ", ..." : "");
+ box++;
+
+ if (this > max - len)
+ break;
+
+ len -= 3;
+ memcpy(buf + len, tmp, this);
+ len += this;
+ }
+ buf[len++] = ']';
+ buf[len] = '\0';
+ return buf;
+}
+
+static const char *_debug_describe_damage(char *buf, int max,
+ struct sna_damage *damage)
+{
+ char damage_str[500], region_str[500];
+ int str_max;
+
+ if (damage == NULL)
+ return "None";
+
+ str_max = max/2 - 6;
+ if (str_max > sizeof(damage_str))
+ str_max = sizeof(damage_str);
+
+ sprintf(damage_str, "[%d : ...]", damage->n);
+ snprintf(buf, max, "[[(%d, %d), (%d, %d)]: %s + %s]",
+ damage->extents.x1, damage->extents.y1,
+ damage->extents.x2, damage->extents.y2,
+ _debug_describe_region(region_str, str_max,
+ &damage->region),
+ damage_str);
+
+ return buf;
+}
+
+#endif
+
+struct sna_damage_box {
+ struct list list;
+ uint16_t size, remain;
+};
+
+struct sna_damage_elt {
+ enum mode {
+ ADD,
+ SUBTRACT,
+ } mode;
+ BoxPtr box;
+ uint16_t n;
+};
+
+static struct sna_damage *_sna_damage_create(void)
+{
+ struct sna_damage *damage;
+
+ damage = malloc(sizeof(*damage));
+ damage->n = 0;
+ damage->size = 16;
+ damage->elts = malloc(sizeof(*damage->elts) * damage->size);
+ list_init(&damage->boxes);
+ damage->last_box = NULL;
+ damage->mode = ADD;
+ pixman_region_init(&damage->region);
+ damage->extents.x1 = damage->extents.y1 = MAXSHORT;
+ damage->extents.x2 = damage->extents.y2 = MINSHORT;
+
+ return damage;
+}
+
+static BoxPtr _sna_damage_create_boxes(struct sna_damage *damage,
+ int count)
+{
+ struct sna_damage_box *box;
+ int n;
+
+ if (damage->last_box && damage->last_box->remain >= count) {
+ box = damage->last_box;
+ n = box->size - box->remain;
+ DBG((" %s(%d): reuse last box, used=%d, remain=%d\n",
+ __FUNCTION__, count, n, box->remain));
+ box->remain -= count;
+ if (box->remain == 0)
+ damage->last_box = NULL;
+ return (BoxPtr)(box+1) + n;
+ }
+
+ n = ALIGN(count, 64);
+
+ DBG((" %s(%d->%d): new\n", __FUNCTION__, count, n));
+
+ box = malloc(sizeof(*box) + sizeof(BoxRec)*n);
+ box->size = n;
+ box->remain = n - count;
+ list_add(&box->list, &damage->boxes);
+
+ damage->last_box = box;
+ return (BoxPtr)(box+1);
+}
+
+static void
+_sna_damage_create_elt(struct sna_damage *damage,
+ enum mode mode,
+ const BoxRec *boxes, int count)
+{
+ struct sna_damage_elt *elt;
+
+ DBG((" %s(%s): n=%d, prev=(%s, remain %d)\n", __FUNCTION__,
+ mode == ADD ? "add" : "subtract",
+ damage->n,
+ damage->n ? damage->elts[damage->n-1].mode == ADD ? "add" : "subtract" : "none",
+ damage->last_box ? damage->last_box->remain : 0));
+
+ if (damage->last_box && damage->elts[damage->n-1].mode == mode) {
+ int n;
+
+ n = count;
+ if (n > damage->last_box->remain)
+ n = damage->last_box->remain;
+
+ elt = damage->elts + damage->n-1;
+ memcpy(elt->box + elt->n, boxes, n * sizeof(BoxRec));
+ elt->n += n;
+ damage->last_box->remain -= n;
+ if (damage->last_box->remain == 0)
+ damage->last_box = NULL;
+
+ count -=n;
+ boxes += n;
+ if (count == 0)
+ return;
+ }
+
+ if (damage->n == damage->size) {
+ int newsize = damage->size * 2;
+ struct sna_damage_elt *newelts = realloc(damage->elts,
+ newsize*sizeof(*elt));
+ if (newelts == NULL)
+ return;
+
+ damage->elts = newelts;
+ damage->size = newsize;
+ }
+
+ DBG((" %s(): new elt\n", __FUNCTION__));
+
+ elt = damage->elts + damage->n++;
+ elt->mode = mode;
+ elt->n = count;
+ elt->box = memcpy(_sna_damage_create_boxes(damage, count),
+ boxes, count * sizeof(BoxRec));
+}
+
+static void free_list(struct list *head)
+{
+ while (!list_is_empty(head)) {
+ struct list *l = head->next;
+ list_del(l);
+ free(l);
+ }
+}
+
+static void __sna_damage_reduce(struct sna_damage *damage)
+{
+ int n, m, j;
+ int nboxes;
+ BoxPtr boxes;
+ pixman_region16_t tmp, *region = &damage->region;
+
+ DBG((" reduce: before damage.n=%d region.n=%d\n",
+ damage->n, REGION_NUM_RECTS(region)));
+
+ m = 0;
+ nboxes = damage->elts[0].n;
+ boxes = damage->elts[0].box;
+ for (n = 1; n < damage->n; n++) {
+ if (damage->elts[n].mode != damage->elts[m].mode) {
+ if (!boxes) {
+ boxes = malloc(sizeof(BoxRec)*nboxes);
+ nboxes = 0;
+ for (j = m; j < n; j++) {
+ memcpy(boxes+nboxes,
+ damage->elts[j].box,
+ damage->elts[j].n*sizeof(BoxRec));
+ nboxes += damage->elts[j].n;
+ }
+ }
+
+ pixman_region_init_rects(&tmp, boxes, nboxes);
+ if (damage->elts[m].mode == ADD)
+ pixman_region_union(region, region, &tmp);
+ else
+ pixman_region_subtract(region, region, &tmp);
+ pixman_region_fini(&tmp);
+
+ if (boxes != damage->elts[m].box)
+ free(boxes);
+
+ m = n;
+ boxes = damage->elts[n].box;
+ nboxes = damage->elts[n].n;
+ } else {
+ boxes = NULL;
+ nboxes += damage->elts[n].n;
+ }
+ }
+
+ if (!boxes) {
+ boxes = malloc(sizeof(BoxRec)*nboxes);
+ nboxes = 0;
+ for (j = m; j < n; j++) {
+ memcpy(boxes+nboxes,
+ damage->elts[j].box,
+ damage->elts[j].n*sizeof(BoxRec));
+ nboxes += damage->elts[j].n;
+ }
+ }
+
+ pixman_region_init_rects(&tmp, boxes, nboxes);
+ if (damage->elts[m].mode == ADD)
+ pixman_region_union(region, region, &tmp);
+ else
+ pixman_region_subtract(region, region, &tmp);
+ pixman_region_fini(&tmp);
+
+ damage->extents = region->extents;
+
+ if (boxes != damage->elts[m].box)
+ free(boxes);
+
+ damage->n = 0;
+ free_list(&damage->boxes);
+ damage->last_box = NULL;
+ damage->mode = ADD;
+
+ DBG((" reduce: after region.n=%d\n", REGION_NUM_RECTS(region)));
+}
+
+inline static struct sna_damage *__sna_damage_add(struct sna_damage *damage,
+ RegionPtr region)
+{
+ if (!RegionNotEmpty(region))
+ return damage;
+
+ if (!damage)
+ damage = _sna_damage_create();
+
+ if (damage->mode == SUBTRACT)
+ __sna_damage_reduce(damage);
+ damage->mode = ADD;
+
+ if (REGION_NUM_RECTS(&damage->region) <= 1) {
+ pixman_region_union(&damage->region, &damage->region, region);
+ damage->extents = damage->region.extents;
+ return damage;
+ }
+
+ if (pixman_region_contains_rectangle(&damage->region,
+ &region->extents) == PIXMAN_REGION_IN)
+ return damage;
+
+ _sna_damage_create_elt(damage, ADD,
+ REGION_RECTS(region),
+ REGION_NUM_RECTS(region));
+
+ if (damage->extents.x1 > region->extents.x1)
+ damage->extents.x1 = region->extents.x1;
+ if (damage->extents.x2 < region->extents.x2)
+ damage->extents.x2 = region->extents.x2;
+
+ if (damage->extents.y1 > region->extents.y1)
+ damage->extents.y1 = region->extents.y1;
+ if (damage->extents.y2 < region->extents.y2)
+ damage->extents.y2 = region->extents.y2;
+
+ return damage;
+}
+
+#if DEBUG_DAMAGE
+fastcall struct sna_damage *_sna_damage_add(struct sna_damage *damage,
+ RegionPtr region)
+{
+ char region_buf[120];
+ char damage_buf[1000];
+
+ DBG(("%s(%s + %s)\n", __FUNCTION__,
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
+ _debug_describe_region(region_buf, sizeof(region_buf), region)));
+
+ damage = __sna_damage_add(damage, region);
+
+ ErrorF(" = %s\n",
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage));
+
+ return damage;
+}
+#else
+fastcall struct sna_damage *_sna_damage_add(struct sna_damage *damage,
+ RegionPtr region)
+{
+ return __sna_damage_add(damage, region);
+}
+#endif
+
+inline static struct sna_damage *__sna_damage_add_box(struct sna_damage *damage,
+ const BoxRec *box)
+{
+ if (box->y2 <= box->y1 || box->x2 <= box->x1)
+ return damage;
+
+ if (!damage)
+ damage = _sna_damage_create();
+
+ if (damage->mode == SUBTRACT)
+ __sna_damage_reduce(damage);
+ damage->mode = ADD;
+
+ if (REGION_NUM_RECTS(&damage->region) == 0) {
+ pixman_region_init_rects(&damage->region, box, 1);
+ damage->extents = *box;
+ return damage;
+ }
+
+ if (pixman_region_contains_rectangle(&damage->region,
+ (BoxPtr)box) == PIXMAN_REGION_IN)
+ return damage;
+
+ _sna_damage_create_elt(damage, ADD, box, 1);
+
+ if (damage->extents.x1 > box->x1)
+ damage->extents.x1 = box->x1;
+ if (damage->extents.x2 < box->x2)
+ damage->extents.x2 = box->x2;
+
+ if (damage->extents.y1 > box->y1)
+ damage->extents.y1 = box->y1;
+ if (damage->extents.y2 < box->y2)
+ damage->extents.y2 = box->y2;
+
+ return damage;
+}
+
+#if DEBUG_DAMAGE
+fastcall struct sna_damage *_sna_damage_add_box(struct sna_damage *damage,
+ const BoxRec *box)
+{
+ char damage_buf[1000];
+
+ DBG(("%s(%s + [(%d, %d), (%d, %d)])\n", __FUNCTION__,
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
+ box->x1, box->y1, box->x2, box->y2));
+
+ damage = __sna_damage_add_box(damage, box);
+
+ ErrorF(" = %s\n",
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage));
+
+ return damage;
+}
+#else
+fastcall struct sna_damage *_sna_damage_add_box(struct sna_damage *damage,
+ const BoxRec *box)
+{
+ return __sna_damage_add_box(damage, box);
+}
+#endif
+
+struct sna_damage *_sna_damage_all(struct sna_damage *damage,
+ int width, int height)
+{
+ DBG(("%s(%d, %d)\n", __FUNCTION__, width, height));
+
+ if (damage) {
+ free_list(&damage->boxes);
+ pixman_region_fini(&damage->region);
+ damage->n = 0;
+ damage->last_box = NULL;
+ } else
+ damage = _sna_damage_create();
+
+ pixman_region_init_rect(&damage->region, 0, 0, width, height);
+ damage->extents = damage->region.extents;
+ damage->mode = ADD;
+
+ return damage;
+}
+
+static inline Bool sna_damage_maybe_contains_box(struct sna_damage *damage,
+ const BoxRec *box)
+{
+ if (box->x2 <= damage->extents.x1 ||
+ box->x1 >= damage->extents.x2)
+ return FALSE;
+
+ if (box->y2 <= damage->extents.y1 ||
+ box->y1 >= damage->extents.y2)
+ return FALSE;
+
+ return TRUE;
+}
+
+static struct sna_damage *__sna_damage_subtract(struct sna_damage *damage,
+ RegionPtr region)
+{
+ if (damage == NULL)
+ return NULL;
+
+ if (!RegionNotEmpty(&damage->region)) {
+ __sna_damage_destroy(damage);
+ return NULL;
+ }
+
+ if (!RegionNotEmpty(region))
+ return damage;
+
+ if (!sna_damage_maybe_contains_box(damage, &region->extents))
+ return damage;
+
+ if (damage->n == 0) {
+ if (pixman_region_equal(region, &damage->region)) {
+ __sna_damage_destroy(damage);
+ return NULL;
+ }
+
+ if (!pixman_region_not_empty(&damage->region)) {
+ __sna_damage_destroy(damage);
+ return NULL;
+ }
+
+ if (REGION_NUM_RECTS(&damage->region) == 1 &&
+ REGION_NUM_RECTS(region) == 1) {
+ pixman_region_subtract(&damage->region,
+ &damage->region,
+ region);
+ damage->extents = damage->region.extents;
+ return damage;
+ }
+ }
+
+ damage->mode = SUBTRACT;
+ _sna_damage_create_elt(damage, SUBTRACT,
+ REGION_RECTS(region),
+ REGION_NUM_RECTS(region));
+
+ return damage;
+}
+
+#if DEBUG_DAMAGE
+fastcall struct sna_damage *_sna_damage_subtract(struct sna_damage *damage,
+ RegionPtr region)
+{
+ char damage_buf[1000];
+ char region_buf[120];
+
+ ErrorF("%s(%s - %s)...\n", __FUNCTION__,
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
+ _debug_describe_region(region_buf, sizeof(region_buf), region));
+
+ damage = __sna_damage_subtract(damage, region);
+
+ ErrorF(" = %s\n",
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage));
+
+ return damage;
+}
+#else
+fastcall struct sna_damage *_sna_damage_subtract(struct sna_damage *damage,
+ RegionPtr region)
+{
+ return __sna_damage_subtract(damage, region);
+}
+#endif
+
+inline static struct sna_damage *__sna_damage_subtract_box(struct sna_damage *damage,
+ const BoxRec *box)
+{
+ if (damage == NULL)
+ return NULL;
+
+ if (!RegionNotEmpty(&damage->region)) {
+ __sna_damage_destroy(damage);
+ return NULL;
+ }
+
+ if (!sna_damage_maybe_contains_box(damage, box))
+ return damage;
+
+ if (damage->n == 0) {
+ if (!pixman_region_not_empty(&damage->region)) {
+ __sna_damage_destroy(damage);
+ return NULL;
+ }
+
+ if (REGION_NUM_RECTS(&damage->region) == 1) {
+ pixman_region16_t region;
+
+ pixman_region_init_rects(&region, box, 1);
+ pixman_region_subtract(&damage->region,
+ &damage->region,
+ &region);
+ damage->extents = damage->region.extents;
+ return damage;
+ }
+ }
+
+ damage->mode = SUBTRACT;
+ _sna_damage_create_elt(damage, SUBTRACT, box, 1);
+
+ return damage;
+}
+
+#if DEBUG_DAMAGE
+fastcall struct sna_damage *_sna_damage_subtract_box(struct sna_damage *damage,
+ const BoxRec *box)
+{
+ char damage_buf[1000];
+
+ ErrorF("%s(%s - (%d, %d), (%d, %d))...\n", __FUNCTION__,
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
+ box->x1, box->y1, box->x2, box->y2);
+
+ damage = __sna_damage_subtract_box(damage, box);
+
+ ErrorF(" = %s\n",
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage));
+
+ return damage;
+}
+#else
+fastcall struct sna_damage *_sna_damage_subtract_box(struct sna_damage *damage,
+ const BoxRec *box)
+{
+ return __sna_damage_subtract_box(damage, box);
+}
+#endif
+
+static int _sna_damage_contains_box(struct sna_damage *damage,
+ const BoxPtr box)
+{
+ if (!damage)
+ return PIXMAN_REGION_OUT;;
+
+ if (!sna_damage_maybe_contains_box(damage, box))
+ return PIXMAN_REGION_OUT;
+
+ if (damage->n)
+ __sna_damage_reduce(damage);
+
+ return pixman_region_contains_rectangle(&damage->region, box);
+}
+
+#if DEBUG_DAMAGE
+int sna_damage_contains_box(struct sna_damage *damage,
+ const BoxPtr box)
+{
+ char damage_buf[1000];
+ int ret;
+
+ DBG(("%s(%s, [(%d, %d), (%d, %d)])\n", __FUNCTION__,
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
+ box->x1, box->y1, box->x2, box->y2));
+
+ ret = _sna_damage_contains_box(damage, box);
+ ErrorF(" = %d\n", ret);
+
+ return ret;
+}
+#else
+int sna_damage_contains_box(struct sna_damage *damage,
+ const BoxPtr box)
+{
+ return _sna_damage_contains_box(damage, box);
+}
+#endif
+
+static Bool _sna_damage_intersect(struct sna_damage *damage,
+ RegionPtr region, RegionPtr result)
+{
+ if (!damage)
+ return FALSE;
+
+ if (region->extents.x2 <= damage->extents.x1 ||
+ region->extents.x1 >= damage->extents.x2)
+ return FALSE;
+
+ if (region->extents.y2 <= damage->extents.y1 ||
+ region->extents.y1 >= damage->extents.y2)
+ return FALSE;
+
+ if (damage->n)
+ __sna_damage_reduce(damage);
+
+ if (!pixman_region_not_empty(&damage->region))
+ return FALSE;
+
+ RegionNull(result);
+ RegionIntersect(result, &damage->region, region);
+
+ return RegionNotEmpty(result);
+}
+
+#if DEBUG_DAMAGE
+Bool sna_damage_intersect(struct sna_damage *damage,
+ RegionPtr region, RegionPtr result)
+{
+ char damage_buf[1000];
+ char region_buf[120];
+ Bool ret;
+
+ ErrorF("%s(%s, %s)...\n", __FUNCTION__,
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage),
+ _debug_describe_region(region_buf, sizeof(region_buf), region));
+
+ ret = _sna_damage_intersect(damage, region, result);
+ ErrorF(" = %d %s\n",
+ ret,
+ _debug_describe_region(region_buf, sizeof(region_buf), result));
+
+ return ret;
+}
+#else
+Bool sna_damage_intersect(struct sna_damage *damage,
+ RegionPtr region, RegionPtr result)
+{
+ return _sna_damage_intersect(damage, region, result);
+}
+#endif
+
+static int _sna_damage_get_boxes(struct sna_damage *damage, BoxPtr *boxes)
+{
+ if (!damage)
+ return 0;
+
+ if (damage->n)
+ __sna_damage_reduce(damage);
+
+ *boxes = REGION_RECTS(&damage->region);
+ return REGION_NUM_RECTS(&damage->region);
+}
+
+struct sna_damage *_sna_damage_reduce(struct sna_damage *damage)
+{
+ DBG(("%s()\n", __FUNCTION__));
+
+ if (damage->n)
+ __sna_damage_reduce(damage);
+
+ if (!pixman_region_not_empty(&damage->region)) {
+ __sna_damage_destroy(damage);
+ damage = NULL;
+ }
+
+ return damage;
+}
+
+#if DEBUG_DAMAGE
+int sna_damage_get_boxes(struct sna_damage *damage, BoxPtr *boxes)
+{
+ char damage_buf[1000];
+ int count;
+
+ ErrorF("%s(%s)...\n", __FUNCTION__,
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage));
+
+ count = _sna_damage_get_boxes(damage, boxes);
+ ErrorF(" = %d\n", count);
+
+ return count;
+}
+#else
+int sna_damage_get_boxes(struct sna_damage *damage, BoxPtr *boxes)
+{
+ return _sna_damage_get_boxes(damage, boxes);
+}
+#endif
+
+void __sna_damage_destroy(struct sna_damage *damage)
+{
+ free(damage->elts);
+
+ free_list(&damage->boxes);
+
+ pixman_region_fini(&damage->region);
+ free(damage);
+}
+
+#if DEBUG_DAMAGE && TEST_DAMAGE
+struct sna_damage_selftest{
+ int width, height;
+};
+
+static void st_damage_init_random_box(struct sna_damage_selftest *test,
+ BoxPtr box)
+{
+ int x, y, w, h;
+
+ if (test->width == 1) {
+ x = 0, w = 1;
+ } else {
+ x = rand() % (test->width - 1);
+ w = 1 + rand() % (test->width - x - 1);
+ }
+
+ if (test->height == 1) {
+ y = 0, h = 1;
+ } else {
+ y = rand() % (test->height - 1);
+ h = 1 + rand() % (test->height - y - 1);
+ }
+
+ box->x1 = x;
+ box->x2 = x+w;
+
+ box->y1 = y;
+ box->y2 = y+h;
+}
+
+static void st_damage_init_random_region1(struct sna_damage_selftest *test,
+ pixman_region16_t *region)
+{
+ int x, y, w, h;
+
+ if (test->width == 1) {
+ x = 0, w = 1;
+ } else {
+ x = rand() % (test->width - 1);
+ w = 1 + rand() % (test->width - x - 1);
+ }
+
+ if (test->height == 1) {
+ y = 0, h = 1;
+ } else {
+ y = rand() % (test->height - 1);
+ h = 1 + rand() % (test->height - y - 1);
+ }
+
+ pixman_region_init_rect(region, x, y, w, h);
+}
+
+static void st_damage_add(struct sna_damage_selftest *test,
+ struct sna_damage **damage,
+ pixman_region16_t *region)
+{
+ pixman_region16_t tmp;
+
+ st_damage_init_random_region1(test, &tmp);
+
+ sna_damage_add(damage, &tmp);
+ pixman_region_union(region, region, &tmp);
+}
+
+static void st_damage_add_box(struct sna_damage_selftest *test,
+ struct sna_damage **damage,
+ pixman_region16_t *region)
+{
+ BoxRec box;
+
+ st_damage_init_random_box(test, &box);
+
+ sna_damage_add_box(damage, &box);
+ pixman_region_union_rectangle(region, region,
+ box.x1, box.y2,
+ box.x2 - box.x1,
+ box.y2 - box.y1);
+}
+
+static void st_damage_subtract(struct sna_damage_selftest *test,
+ struct sna_damage **damage,
+ pixman_region16_t *region)
+{
+ pixman_region16_t tmp;
+
+ st_damage_init_random_region1(test, &tmp);
+
+ sna_damage_subtract(damage, &tmp);
+ pixman_region_subtract(region, region, &tmp);
+}
+
+static void st_damage_all(struct sna_damage_selftest *test,
+ struct sna_damage **damage,
+ pixman_region16_t *region)
+{
+ pixman_region16_t tmp;
+
+ pixman_region_init_rect(&tmp, 0, 0, test->width, test->height);
+
+ sna_damage_all(damage, test->width, test->height);
+ pixman_region_union(region, region, &tmp);
+}
+
+static bool st_check_equal(struct sna_damage_selftest *test,
+ struct sna_damage **damage,
+ pixman_region16_t *region)
+{
+ int d_num, r_num;
+ BoxPtr d_boxes, r_boxes;
+
+ d_num = sna_damage_get_boxes(*damage, &d_boxes);
+ r_boxes = pixman_region_rectangles(region, &r_num);
+
+ if (d_num != r_num) {
+ ErrorF("%s: damage and ref contain different number of rectangles\n",
+ __FUNCTION__);
+ return FALSE;
+ }
+
+ if (memcmp(d_boxes, r_boxes, d_num*sizeof(BoxRec))) {
+ ErrorF("%s: damage and ref contain different rectangles\n",
+ __FUNCTION__);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void sna_damage_selftest(void)
+{
+ void (*const op[])(struct sna_damage_selftest *test,
+ struct sna_damage **damage,
+ pixman_region16_t *region) = {
+ st_damage_add,
+ st_damage_add_box,
+ st_damage_subtract,
+ st_damage_all
+ };
+ bool (*const check[])(struct sna_damage_selftest *test,
+ struct sna_damage **damage,
+ pixman_region16_t *region) = {
+ st_check_equal,
+ //st_check_contains,
+ };
+ char region_buf[120];
+ char damage_buf[1000];
+ int pass;
+
+ for (pass = 0; pass < 1024; pass++) {
+ struct sna_damage_selftest test;
+ struct sna_damage *damage;
+ pixman_region16_t ref;
+ int iter, i;
+
+ iter = rand() % 1024;
+
+ test.width = 1 + rand() % 2048;
+ test.height = 1 + rand() % 2048;
+
+ damage = _sna_damage_create();
+ pixman_region_init(&ref);
+
+ for (i = 0; i < iter; i++) {
+ op[rand() % ARRAY_SIZE(op)](&test, &damage, &ref);
+ }
+
+ if (!check[rand() % ARRAY_SIZE(check)](&test, &damage, &ref)) {
+ ErrorF("%s: failed - region = %s, damage = %s\n", __FUNCTION__,
+ _debug_describe_region(region_buf, sizeof(region_buf), &ref),
+ _debug_describe_damage(damage_buf, sizeof(damage_buf), damage));
+ assert(0);
+ }
+
+ pixman_region_fini(&ref);
+ sna_damage_destroy(&damage);
+ }
+}
+#endif