#include "evas_common_private.h"
#include "region.h"


#ifdef NEWTILER
#define MAXREG 24

EAPI void
evas_common_tilebuf_init(void)
{
}

EAPI Tilebuf *
evas_common_tilebuf_new(int w, int h)
{
   Tilebuf *tb = malloc(sizeof(Tilebuf));
   tb->outbuf_w = w;
   tb->outbuf_h = h;
   tb->region = region_new(tb->outbuf_w, tb->outbuf_h);
   return tb;
}

EAPI void
evas_common_tilebuf_free(Tilebuf *tb)
{
   region_free(tb->region);
   free(tb);
}

EAPI void
evas_common_tilebuf_set_tile_size(Tilebuf *tb EINA_UNUSED, int tw EINA_UNUSED, int th EINA_UNUSED)
{
}

EAPI void
evas_common_tilebuf_get_tile_size(Tilebuf *tb EINA_UNUSED, int *tw, int *th)
{
   if (tw) *tw = 1;
   if (th) *th = 1;
}

EAPI void
evas_common_tilebuf_tile_strict_set(Tilebuf *tb EINA_UNUSED, Eina_Bool strict EINA_UNUSED)
{
}

EAPI int
evas_common_tilebuf_add_redraw(Tilebuf *tb, int x, int y, int w, int h)
{
   region_rect_add(tb->region, x, y, w, h);
   return 1;
}

EAPI int
evas_common_tilebuf_del_redraw(Tilebuf *tb, int x, int y, int w, int h)
{
   region_rect_del(tb->region, x, y, w, h);
   return 1;
}

EAPI int
evas_common_tilebuf_add_motion_vector(Tilebuf *tb EINA_UNUSED, int x EINA_UNUSED, int y EINA_UNUSED, int w EINA_UNUSED, int h EINA_UNUSED, int dx EINA_UNUSED, int dy EINA_UNUSED, int alpha EINA_UNUSED)
{
   return 0;
}

EAPI void
evas_common_tilebuf_clear(Tilebuf *tb)
{
   region_free(tb->region);
   tb->region = region_new(tb->outbuf_w, tb->outbuf_h);
}

static Region *
_region_round(Region *region, int tsize)
{
   Region *region2;
   Box *rects;
   int num, i, w, h;

   region_size_get(region, &w, &h);
   region2 = region_new(w, h);
   rects = region_rects(region);
   num = region_rects_num(region);
   for (i = 0; i < num; i++)
     {
        int x1, y1, x2, y2;

        x1 = (rects[i].x1 / tsize) * tsize;
        y1 = (rects[i].y1 / tsize) * tsize;
        x2 = ((rects[i].x2 + tsize - 1) / tsize) * tsize;
        y2 = ((rects[i].y2 + tsize - 1) / tsize) * tsize;
        region_rect_add(region2, x1, y1, x2 - x1, y2 - y1);
     }
   return region2;
}

EAPI Tilebuf_Rect *
evas_common_tilebuf_get_render_rects(Tilebuf *tb)
{
   Tilebuf_Rect *rects = NULL, *r, *rend, *rbuf;
   Region *region2;
   Box *rects2, *rs;
   int n, num, minx, miny, maxx, maxy;

   region2 = _region_round(tb->region, 16);
   if (!region2) return NULL;

   rects2 = region_rects(region2);
   if (!rects2)
     {
        region_free(region2);
        return NULL;
     }
   n = region_rects_num(region2);
   if (n <= 0)
     {
        region_free(region2);
        return NULL;
     }

   rbuf = malloc(n * sizeof(Tilebuf_Rect));
   if (!rbuf)
     {
        region_free(region2);
        return NULL;
     }

   rend = rbuf + n;
   rs = rects2;
   num = 0;

   minx = rs->x1;
   miny = rs->y1;
   maxx = rs->x2;
   maxy = rs->y2;
   for (r = rbuf; r < rend; r++)
     {
        if (rs->x1 < minx) minx = rs->x1;
        if (rs->y1 < miny) miny = rs->y1;
        if (rs->x2 > maxx) maxx = rs->x2;
        if (rs->y2 > maxy) maxy = rs->y2;
        EINA_INLIST_GET(r)->next = NULL;
        EINA_INLIST_GET(r)->prev = NULL;
        EINA_INLIST_GET(r)->last = NULL;
        r->x = rs->x1;
        r->y = rs->y1;
        r->w = rs->x2 - rs->x1;
        r->h = rs->y2 - rs->y1;
        rs++;
        rects = (Tilebuf_Rect *)
          eina_inlist_append(EINA_INLIST_GET(rects),
                             EINA_INLIST_GET(r));
        num++;
     }
   // if > max, then bounding box
   if (num > MAXREG)
     {
        r = rects;
        EINA_INLIST_GET(r)->next = NULL;
        EINA_INLIST_GET(r)->prev = NULL;
        EINA_INLIST_GET(r)->last = NULL;
        r->x = minx;
        r->y = miny;
        r->w = maxx - minx;
        r->h = maxy - miny;
     }
   region_free(region2);
   return rects;
}

EAPI void
evas_common_tilebuf_free_render_rects(Tilebuf_Rect *rects)
{
   free(rects);
}

#else

#define FUZZ 32
#define MAXREG 24
#define MAX_NODES 1024

static inline void rect_list_node_pool_flush(void);
static inline list_node_t *rect_list_node_pool_get(void);
static inline void rect_list_node_pool_put(list_node_t *node);
static inline void rect_init(rect_t *r, int x, int y, int w, int h);
static inline void rect_list_append_node(list_t *rects, list_node_t *node);
static inline void rect_list_append(list_t *rects, const rect_t r);
static inline void rect_list_append_xywh(list_t *rects, int x, int y, int w, int h);
static inline void rect_list_concat(list_t *rects, list_t *other);
static inline list_node_t *rect_list_unlink_next(list_t *rects, list_node_t *parent_node);
static inline void rect_list_del_next(list_t *rects, list_node_t *parent_node);
static inline void rect_list_clear(list_t *rects);
static inline void rect_list_del_split_strict(list_t *rects, const rect_t del_r);
static inline list_node_t *rect_list_add_split_fuzzy(list_t *rects, list_node_t *node, int accepted_error);
static inline void rect_list_merge_rects(list_t *rects, list_t *to_merge, int accepted_error);
static inline void rect_list_add_split_fuzzy_and_merge(list_t *rects, list_node_t *node, int split_accepted_error, int merge_accepted_error);

static const list_node_t list_node_zeroed = { NULL };
static const list_t list_zeroed = { NULL, NULL };

typedef struct list_node_pool
{
   list_node_t *node;
   int len;
   int max;
} list_node_pool_t;

static list_node_pool_t list_node_pool = { NULL, 0, MAX_NODES };

static inline void
rect_list_node_pool_flush(void)
{
   while (list_node_pool.node)
     {
        list_node_t *node = list_node_pool.node;
        list_node_pool.node = node->next;
        list_node_pool.len--;
        free(node);
     }
}

static inline list_node_t *
rect_list_node_pool_get(void)
{
   if (list_node_pool.node)
     {
        list_node_t *node = list_node_pool.node;
        list_node_pool.node = node->next;
        list_node_pool.len--;
        return node;
     }
   else return (list_node_t *)malloc(sizeof(rect_node_t));
}

static inline void
rect_list_node_pool_put(list_node_t *node)
{
   if (list_node_pool.len < list_node_pool.max)
     {
        node->next = list_node_pool.node;
        list_node_pool.node = node;
        list_node_pool.len++;
     }
   else free(node);
}

static inline void
rect_init(rect_t *r, int x, int y, int w, int h)
{
   r->area = w * h;
   r->left = x;
   r->top = y;
   r->right = x + w;
   r->bottom = y + h;
   r->width = w;
   r->height = h;
}

static inline void
rect_list_append_node(list_t *rects, list_node_t *node)
{
   if (rects->tail)
     {
	rects->tail->next = node;
	rects->tail = node;
     }
   else
     {
	rects->head = node;
	rects->tail = node;
     }
}

static inline void
rect_list_append(list_t *rects, const rect_t r)
{
   rect_node_t *rect_node = (rect_node_t *)rect_list_node_pool_get();
   rect_node->rect = r;
   rect_node->_lst = list_node_zeroed;
   rect_list_append_node(rects, (list_node_t *)rect_node);
}

static inline void
rect_list_append_xywh(list_t *rects, int x, int y, int w, int h)
{
   rect_t r;
   rect_init(&r, x, y, w, h);
   rect_list_append(rects, r);
}

static inline void
rect_list_concat(list_t *rects, list_t *other)
{
   if (!other->head) return;
   if (rects->tail)
     {
	rects->tail->next = other->head;
	rects->tail = other->tail;
     }
   else
     {
	rects->head = other->head;
	rects->tail = other->tail;
     }
   *other = list_zeroed;
}

static inline list_node_t *
rect_list_unlink_next(list_t *rects, list_node_t *parent_node)
{
   list_node_t *node;

   if (parent_node)
     {
        node = parent_node->next;
        parent_node->next = node->next;
     }
   else
     {
        node = rects->head;
        rects->head = node->next;
     }
   if (rects->tail == node) rects->tail = parent_node;
   *node = list_node_zeroed;
   return node;
}

static inline void
rect_list_del_next(list_t *rects, list_node_t *parent_node)
{
    list_node_t *node = rect_list_unlink_next(rects, parent_node);
    rect_list_node_pool_put(node);
}

static inline void
rect_list_clear(list_t *rects)
{
   list_node_t *node = rects->head;
   while (node)
     {
        list_node_t *aux;

        aux = node->next;
        rect_list_node_pool_put(node);
        node = aux;
     }
   *rects = list_zeroed;
}

static inline void
_calc_intra_rect_area(const rect_t a, const rect_t b, int *width, int *height)
{
   int max_left, min_right, max_top, min_bottom;

   if (a.left < b.left) max_left = b.left;
   else max_left = a.left;
   if (a.right < b.right) min_right = a.right;
   else min_right = b.right;
   *width = min_right - max_left;

   if (a.top < b.top) max_top = b.top;
   else max_top = a.top;
   if (a.bottom < b.bottom) min_bottom = a.bottom;
   else min_bottom = b.bottom;
   *height = min_bottom - max_top;
}

static inline void
_split_strict(list_t *dirty, const rect_t current, rect_t r)
{
   int h_1, h_2, w_1, w_2;

   h_1 = current.top - r.top;
   h_2 = r.bottom - current.bottom;
   w_1 = current.left - r.left;
   w_2 = r.right - current.right;
   if (h_1 > 0)
     {
	/*    .--.r (b)                .---.r2
         *    |  |                     |   |
         *  .-------.cur (a) .---.r    '---'
         *  | |  |  |     -> |   |   +
         *  | `--'  |        `---'
         *  `-------'
         */
        rect_list_append_xywh(dirty, r.left, r.top, r.width, h_1);
        r.height -= h_1;
        r.top = current.top;
     }
   if (h_2 > 0)
     {
        /*  .-------.cur (a)
         *  | .---. |        .---.r
         *  | |   | |    ->  |   |
         *  `-------'        `---'   +  .---.r2
         *    |   |                     |   |
         *    `---'r (b)                `---'
         */
        rect_list_append_xywh(dirty, r.left, current.bottom, r.width, h_2);
        r.height -= h_2;
     }
   if (w_1 > 0)
     {
        /* (b) r  .----.cur (a)
         *     .--|-.  |      .--.r2   .-.r
         *     |  | |  |  ->  |  |   + | |
         *     `--|-'  |      `--'     `-'
         *        `----'
         */
        rect_list_append_xywh(dirty, r.left, r.top, w_1, r.height);
        /* not necessary to keep these, r (b) will be destroyed */
        /* r.width -= w_1; */
        /* r.left = current.left; */
     }
   if (w_2 > 0)
     {
        /*  .----.cur (a)
         *  |    |
         *  |  .-|--.r (b)  .-.r   .--.r2
         *  |  | |  |    -> | |  + |  |
         *  |  `-|--'       `-'    `--'
         *  `----'
         */
        rect_list_append_xywh(dirty, current.right, r.top, w_2, r.height);
        /* not necessary to keep this, r (b) will be destroyed */
        /* r.width -= w_2; */
     }
}

static inline void
rect_list_del_split_strict(list_t *rects, const rect_t del_r)
{
   list_t modified = list_zeroed;
   list_node_t *cur_node, *prev_node;
   int intra_width, intra_height;
   rect_t current;

   prev_node = NULL;
   cur_node = rects->head;
   while (cur_node)
     {
        current = ((rect_node_t*)cur_node)->rect;
        _calc_intra_rect_area(del_r, current, &intra_width, &intra_height);
        if ((intra_width <= 0) || (intra_height <= 0))
          {
             /*  .---.current      .---.del_r
              *  |   |             |   |
              *  `---+---.del_r    `---+---.current
              *      |   |             |   |
              *      `---'             `---'
              * no interception, nothing to do
              */
              prev_node = cur_node;
              cur_node = cur_node->next;
          }
        else if ((intra_width == current.width) &&
                 (intra_height == current.height))
          {
             /*  .-------.del_r
              *  | .---. |
              *  | |   | |
              *  | `---'current
              *  `-------'
              * current is contained, remove from rects
              */
              cur_node = cur_node->next;
              rect_list_del_next(rects, prev_node);
          }
        else
          {
              _split_strict(&modified, del_r, current);
              cur_node = cur_node->next;
              rect_list_del_next(rects, prev_node);
          }
     }

   rect_list_concat(rects, &modified);
}

static inline void
_calc_intra_outer_rect_area(const rect_t a, const rect_t b,
                            rect_t *intra, rect_t *outer)
{
   int min_left, max_left, min_right, max_right;
   int min_top, max_top, min_bottom, max_bottom;

   if (a.left < b.left)
     {
        max_left = b.left;
        min_left = a.left;
     }
   else
     {
        max_left = a.left;
        min_left = b.left;
     }
   if (a.right < b.right)
     {
        min_right = a.right;
        max_right = b.right;
     }
   else
     {
        min_right = b.right;
        max_right = a.right;
     }
   intra->left = max_left;
   intra->right = min_right;
   intra->width = min_right - max_left;
   outer->left = min_left;
   outer->right = max_right;
   outer->width = max_right - min_left;
   if (a.top < b.top)
     {
	max_top = b.top;
        min_top = a.top;
     }
   else
     {
        max_top = a.top;
        min_top = b.top;
     }
   if (a.bottom < b.bottom)
     {
        min_bottom = a.bottom;
        max_bottom = b.bottom;
     }
   else
     {
        min_bottom = b.bottom;
        max_bottom = a.bottom;
     }
   intra->top = max_top;
   intra->bottom = min_bottom;
   intra->height = min_bottom - max_top;
   if ((intra->width > 0) && (intra->height > 0))
     intra->area = intra->width * intra->height;
   else
     intra->area = 0;
   outer->top = min_top;
   outer->bottom = max_bottom;
   outer->height = max_bottom - min_top;
   outer->area = outer->width * outer->height;
}

enum
{
   SPLIT_FUZZY_ACTION_NONE,
   SPLIT_FUZZY_ACTION_SPLIT,
   SPLIT_FUZZY_ACTION_MERGE
};

static inline int
_split_fuzzy(list_t *dirty, const rect_t a, rect_t *b)
{
   int h_1, h_2, w_1, w_2, action;

   h_1 = a.top - b->top;
   h_2 = b->bottom - a.bottom;
   w_1 = a.left - b->left;
   w_2 = b->right - a.right;

   action = SPLIT_FUZZY_ACTION_NONE;
   if (h_1 > 0)
     {
        /*    .--.r (b)                .---.r2
         *    |  |                     |   |
         *  .-------.cur (a) .---.r    '---'
         *  | |  |  |     -> |   |   +
         *  | `--'  |        `---'
         *  `-------'
         */
        rect_list_append_xywh(dirty, b->left, b->top, b->width, h_1);
        b->height -= h_1;
        b->top = a.top;
        action = SPLIT_FUZZY_ACTION_SPLIT;
     }
   if (h_2 > 0)
     {
        /*  .-------.cur (a)
         *  | .---. |        .---.r
         *  | |   | |    ->  |   |
         *  `-------'        `---'   +  .---.r2
         *    |   |                     |   |
         *    `---'r (b)                `---'
         */
        rect_list_append_xywh(dirty, b->left, a.bottom, b->width, h_2);
        b->height -= h_2;
        action = SPLIT_FUZZY_ACTION_SPLIT;
     }
   if (((w_1 > 0) || (w_2 > 0)) && (a.height == b->height))
     return SPLIT_FUZZY_ACTION_MERGE;
   if (w_1 > 0)
     {
        /* (b)  r  .----.cur (a)
         *      .--|-.  |      .--.r2   .-.r
         *      |  | |  |  ->  |  |   + | |
         *      `--|-'  |      `--'     `-'
         *         `----'
         */
        rect_list_append_xywh(dirty, b->left, b->top, w_1, b->height);
        /* not necessary to keep these, r (b) will be destroyed */
        /* b->width -= w_1; */
        /* b->left = a.left; */
        action = SPLIT_FUZZY_ACTION_SPLIT;
     }
   if (w_2 > 0)
     {
        /* .----.cur (a)
         * |    |
         * |  .-|--.r (b)  .-.r   .--.r2
         * |  | |  |    -> | |  + |  |
         * |  `-|--'       `-'    `--'
         * `----'
         */
        rect_list_append_xywh(dirty, a.right, b->top, w_2, b->height);
        /* not necessary to keep these, r (b) will be destroyed */
        /* b->width -= w_2; */
        action = SPLIT_FUZZY_ACTION_SPLIT;
     }
   return action;
}

static inline list_node_t *
rect_list_add_split_fuzzy(list_t *rects, list_node_t *node, int accepted_error)
{
   list_t dirty = list_zeroed;
   list_node_t *old_last = rects->tail;

   if (!rects->head)
     {
        rect_list_append_node(rects, node);
        return old_last;
     }
   rect_list_append_node(&dirty, node);
   while (dirty.head)
     {
	list_node_t *d_node, *cur_node, *prev_cur_node;
        int keep_dirty;
        rect_t r;

        d_node = rect_list_unlink_next(&dirty, NULL);
        r = ((rect_node_t *)d_node)->rect;
        prev_cur_node = NULL;
        cur_node = rects->head;
        keep_dirty = 1;
        while (cur_node)
	  {
	     int area, action;
	     rect_t current, intra, outer;

	     current = ((rect_node_t *)cur_node)->rect;
	     _calc_intra_outer_rect_area(r, current, &intra, &outer);
	     area = current.area + r.area - intra.area;
	     if ((intra.width == r.width) && (intra.height == r.height))
	       {
		  /*  .-------.cur
		   *  | .---.r|
		   *  | |   | |
		   *  | `---' |
		   *  `-------'
		   */
		  keep_dirty = 0;
		  break;
	       }
	     else if ((intra.width == current.width) &&
		      (intra.height == current.height))
	       {
		  /* .-------.r
		   * | .---.cur
		   * | |   | |
		   * | `---' |
		   * `-------'
		   */
		  if (old_last == cur_node)
                    old_last = prev_cur_node;
		  cur_node = cur_node->next;
		  rect_list_del_next(rects, prev_cur_node);
	       }
	     else if ((outer.area - area) <= accepted_error)
	       {
		  /* .-----------. bounding box (outer)
		   * |.---. .---.|
		   * ||cur| |r  ||
		   * ||   | |   ||
		   * |`---' `---'|
		   * `-----------'
		   * merge them, remove both and add merged
		   */
		  rect_node_t *n;

		  if (old_last == cur_node)
                    old_last = prev_cur_node;

		  n = (rect_node_t *)rect_list_unlink_next(rects, prev_cur_node);
		  n->rect = outer;
		  rect_list_append_node(&dirty, (list_node_t *)n);

		  keep_dirty = 0;
		  break;
	       }
	     else if ((intra.area - area) <= accepted_error)
	       {
		  /*  .---.cur     .---.r
		   *  |   |        |   |
		   *  `---+---.r   `---+---.cur
		   *      |   |        |   |
		   *      `---'        `---'
		   *  no split, no merge
		   */
		  prev_cur_node = cur_node;
		  cur_node = cur_node->next;
	       }
	     else
	       {
		  /* split is required */
		  action = _split_fuzzy(&dirty, current, &r);
		  if (action == SPLIT_FUZZY_ACTION_MERGE)
		    {
		       /* horizontal merge is possible: remove both, add merged */
		       rect_node_t *n;

		       if (old_last == cur_node)
			 old_last = prev_cur_node;

		       n = (rect_node_t *)
			 rect_list_unlink_next(rects, prev_cur_node);

		       n->rect.left = outer.left;
		       n->rect.width = outer.width;
		       n->rect.right = outer.right;
		       n->rect.area = outer.width * r.height;
		       rect_list_append_node(&dirty, (list_node_t *)n);
		    }
		  else if (action == SPLIT_FUZZY_ACTION_NONE)
		    {
		       /* this rect check was totally useless,
			* should never happen */
		       /* prev_cur_node = cur_node; */
		       /* cur_node = cur_node->next; */
		       WRN("Should not get here!");
		       abort();
		    }
		  keep_dirty = 0;
		  break;
	       }
	  }
        if (UNLIKELY(keep_dirty)) rect_list_append_node(rects, d_node);
        else rect_list_node_pool_put(d_node);
     }
    return old_last;
}

static inline void
_calc_outer_rect_area(const rect_t a, const rect_t b, rect_t *outer)
{
   int min_left, max_right;
   int min_top, max_bottom;

   if (a.left < b.left) min_left = a.left;
   else min_left = b.left;
   if (a.right < b.right) max_right = b.right;
   else max_right = a.right;
   outer->left = min_left;
   outer->right = max_right;
   outer->width = max_right - min_left;
   if (a.top < b.top) min_top = a.top;
   else min_top = b.top;
   if (a.bottom < b.bottom) max_bottom = b.bottom;
   else max_bottom = a.bottom;
   outer->top = min_top;
   outer->bottom = max_bottom;
   outer->height = max_bottom - min_top;
   outer->area = outer->width * outer->height;
}

static inline void
rect_list_merge_rects(list_t *rects, list_t *to_merge, int accepted_error)
{
   while (to_merge->head)
     {
        list_node_t *node, *parent_node;
        rect_t r1;
        int merged;

        r1 = ((rect_node_t *)to_merge->head)->rect;
        merged = 0;
        parent_node = NULL;
        node = rects->head;
        while (node)
	  {
	     rect_t r2, outer;
	     int area;

	     r2 = ((rect_node_t *)node)->rect;
	     _calc_outer_rect_area(r1, r2, &outer);
	     area = r1.area + r2.area; /* intra area is taken as 0 */
	     if (outer.area - area <= accepted_error)
	       {
		  /* remove both r1 and r2, create r3
		   * actually r3 uses r2 instance, saves memory */
		  rect_node_t *n;

		  n = (rect_node_t *)rect_list_unlink_next(rects, parent_node);
		  n->rect = outer;
		  rect_list_append_node(to_merge, (list_node_t *)n);
		  merged = 1;
		  break;
	       }
	     parent_node = node;
	     node = node->next;
	  }
        if (!merged)
	  {
	     list_node_t *n;
	     n = rect_list_unlink_next(to_merge, NULL);
	     rect_list_append_node(rects, n);
	  }
	else
	  rect_list_del_next(to_merge, NULL);
    }
}

static inline void
rect_list_add_split_fuzzy_and_merge(list_t *rects,
                                    list_node_t *node,
                                    int split_accepted_error,
                                    int merge_accepted_error)
{
   list_node_t *n;

   n = rect_list_add_split_fuzzy(rects, node, split_accepted_error);
   if (n && n->next)
     {
        list_t to_merge;
        /* split list into 2 segments, already merged and to merge */
        to_merge.head = n->next;
        to_merge.tail = rects->tail;
        rects->tail = n;
        n->next = NULL;
        rect_list_merge_rects(rects, &to_merge, merge_accepted_error);
     }
}

static inline int
_add_redraw(list_t *rects, int x, int y, int w, int h, int fuzz)
{
   rect_node_t *rn;
   rn = (rect_node_t *)rect_list_node_pool_get();
   rn->_lst = list_node_zeroed;
   rect_init(&rn->rect, x, y, w, h);
   rect_list_add_split_fuzzy_and_merge(rects, (list_node_t *)rn, fuzz, fuzz);
   return 1;
}

/////////////////////////////////////////////////////////////////

EAPI void
evas_common_tilebuf_init(void)
{
}

EAPI Tilebuf *
evas_common_tilebuf_new(int w, int h)
{
   Tilebuf *tb;

   tb = calloc(1, sizeof(Tilebuf));
   if (!tb) return NULL;
   tb->tile_size.w = 8;
   tb->tile_size.h = 8;
   tb->outbuf_w = w;
   tb->outbuf_h = h;
   return tb;
}

EAPI void
evas_common_tilebuf_free(Tilebuf *tb)
{
   rect_list_clear(&tb->rects);
   rect_list_node_pool_flush();
   free(tb);
}

EAPI void
evas_common_tilebuf_set_tile_size(Tilebuf *tb, int tw, int th)
{
   tb->tile_size.w = tw;
   tb->tile_size.h = th;
}

EAPI void
evas_common_tilebuf_get_tile_size(Tilebuf *tb, int *tw, int *th)
{
   if (tw) *tw = tb->tile_size.w;
   if (th) *th = tb->tile_size.h;
}

EAPI void
evas_common_tilebuf_tile_strict_set(Tilebuf *tb, Eina_Bool strict)
{
   tb->strict_tiles = strict;
}

EAPI int
evas_common_tilebuf_add_redraw(Tilebuf *tb, int x, int y, int w, int h)
{
   if ((w <= 0) || (h <= 0)) return 0;
   RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, tb->outbuf_w, tb->outbuf_h);
   if ((w <= 0) || (h <= 0)) return 0;
   // optimize a common case -> adding the exact same rect 2x in a row
   if ((tb->prev_add.x == x) && (tb->prev_add.y == y) &&
       (tb->prev_add.w == w) && (tb->prev_add.h == h)) return 1;
   tb->prev_add.x = x; tb->prev_add.y = y;
   tb->prev_add.w = w; tb->prev_add.h = h;
   tb->prev_del.w = 0; tb->prev_del.h = 0;
   return _add_redraw(&tb->rects, x, y, w, h, FUZZ * FUZZ);
}

EAPI int
evas_common_tilebuf_del_redraw(Tilebuf *tb, int x, int y, int w, int h)
{
   rect_t r;

   if (!tb->rects.head) return 0;
   if ((w <= 0) || (h <= 0)) return 0;
   RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, tb->outbuf_w, tb->outbuf_h);
   if ((w <= 0) || (h <= 0)) return 0;
   // optimize a common case -> deleting the exact same rect 2x in a row
   if ((tb->prev_del.x == x) && (tb->prev_del.y == y) &&
       (tb->prev_del.w == w) && (tb->prev_del.h == h)) return 1;
   tb->prev_del.x = x; tb->prev_del.y = y;
   tb->prev_del.w = w; tb->prev_del.h = h;
   tb->prev_add.w = 0; tb->prev_add.h = 0;
   rect_init(&r, x, y, w, h);
   rect_list_del_split_strict(&tb->rects, r);
   tb->need_merge = 1;
   return 0;
}

EAPI int
evas_common_tilebuf_add_motion_vector(Tilebuf *tb EINA_UNUSED, int x EINA_UNUSED, int y EINA_UNUSED, int w EINA_UNUSED, int h EINA_UNUSED, int dx EINA_UNUSED, int dy EINA_UNUSED, int alpha EINA_UNUSED)
{
   return 0;
}

EAPI void
evas_common_tilebuf_clear(Tilebuf *tb)
{
   tb->prev_add.x = tb->prev_add.y = tb->prev_add.w = tb->prev_add.h = 0;
   tb->prev_del.x = tb->prev_del.y = tb->prev_del.w = tb->prev_del.h = 0;
   rect_list_clear(&tb->rects);
   tb->need_merge = 0;
}

EAPI Tilebuf_Rect *
evas_common_tilebuf_get_render_rects(Tilebuf *tb)
{
   list_node_t *n;
   list_t to_merge;
   Tilebuf_Rect *rects = NULL, *rbuf, *r;
   int bx1 = 0, bx2 = 0, by1 = 0, by2 = 0, num = 0, x1, x2, y1, y2, i;

/* don't need this since the below is now always on
   if (tb->need_merge)
     {
        to_merge = tb->rects;
        tb->rects = list_zeroed;
        rect_list_merge_rects(&tb->rects, &to_merge, 0);
        tb->need_merge = 0;
     }
 */
   if (1)
// always fuzz merge for optimal perf
//   if (!tb->strict_tiles)
     {
        // round up rects to tb->tile_size.w and tb->tile_size.h
        to_merge = list_zeroed;
        for (n = tb->rects.head; n; n = n->next)
          {
             x1 = ((rect_node_t *)n)->rect.left;
             x2 = x1 + ((rect_node_t *)n)->rect.width;
             y1 = ((rect_node_t *)n)->rect.top;
             y2 = y1 + ((rect_node_t *)n)->rect.height;
             x1 = tb->tile_size.w * (x1 / tb->tile_size.w);
             y1 = tb->tile_size.h * (y1 / tb->tile_size.h);
             x2 = tb->tile_size.w * ((x2 + tb->tile_size.w - 1) / tb->tile_size.w);
             y2 = tb->tile_size.h * ((y2 + tb->tile_size.h - 1) / tb->tile_size.h);
             _add_redraw(&to_merge, x1, y1, x2 - x1, y2 - y1, 0);
          }
        rect_list_clear(&tb->rects);
        rect_list_merge_rects(&tb->rects, &to_merge, 0);
     }
   n = tb->rects.head;
   if (n)
     {
        RECTS_CLIP_TO_RECT(((rect_node_t *)n)->rect.left,
                           ((rect_node_t *)n)->rect.top,
                           ((rect_node_t *)n)->rect.width,
                           ((rect_node_t *)n)->rect.height,
                           0, 0, tb->outbuf_w, tb->outbuf_h);
        num = 1;
        bx1 = ((rect_node_t *)n)->rect.left;
        bx2 = bx1 + ((rect_node_t *)n)->rect.width;
        by1 = ((rect_node_t *)n)->rect.top;
        by2 = by1 + ((rect_node_t *)n)->rect.height;
        n = n->next;
        for (; n; n = n->next)
          {
             RECTS_CLIP_TO_RECT(((rect_node_t *)n)->rect.left,
                                ((rect_node_t *)n)->rect.top,
                                ((rect_node_t *)n)->rect.width,
                                ((rect_node_t *)n)->rect.height,
                                0, 0, tb->outbuf_w, tb->outbuf_h);
             x1 = ((rect_node_t *)n)->rect.left;
             if (x1 < bx1) bx1 = x1;
             x2 = x1 + ((rect_node_t *)n)->rect.width;
             if (x2 > bx2) bx2 = x2;

             y1 = ((rect_node_t *)n)->rect.top;
             if (y1 < by1) by1 = y1;
             y2 = y1 + ((rect_node_t *)n)->rect.height;
             if (y2 > by2) by2 = y2;
             num++;
          }
     }
   else
     return NULL;

   /* magic number - if we have > MAXREG regions to update, take bounding */
   if (num > MAXREG)
     {
        r = malloc(sizeof(Tilebuf_Rect));
        if (r)
          {
             EINA_INLIST_GET(r)->next = NULL;
             EINA_INLIST_GET(r)->prev = NULL;
             EINA_INLIST_GET(r)->last = NULL;
             r->x = bx1;
             r->y = by1;
             r->w = bx2 - bx1;
             r->h = by2 - by1;
             rects = (Tilebuf_Rect *)
               eina_inlist_append(EINA_INLIST_GET(rects),
                                  EINA_INLIST_GET(r));
          }
        return rects;
     }

   rbuf = malloc(sizeof(Tilebuf_Rect) * num);
   if (!rbuf) return NULL;

   for (i = 0, n = tb->rects.head; n; n = n->next)
     {
        rect_t cur;

        cur = ((rect_node_t *)n)->rect;
        if ((cur.width > 0) && (cur.height > 0))
          {
             r = &(rbuf[i]);
             EINA_INLIST_GET(r)->next = NULL;
             EINA_INLIST_GET(r)->prev = NULL;
             EINA_INLIST_GET(r)->last = NULL;
             r->x = cur.left;
             r->y = cur.top;
             r->w = cur.width;
             r->h = cur.height;
             rects = (Tilebuf_Rect *)
               eina_inlist_append(EINA_INLIST_GET(rects),
                                  EINA_INLIST_GET(r));
             i++;
          }
     }

   // It is possible that due to the clipping we do not return any rectangle here.
   if (!rects) free(rbuf);

   return rects;
}

EAPI void
evas_common_tilebuf_free_render_rects(Tilebuf_Rect *rects)
{
   free(rects);
}
#endif
