#include "evas_common_private.h" #include "evas_convert_main.h" #include "evas_private.h" EAPI Cutout_Rects * evas_common_draw_context_cutouts_new(void) { Cutout_Rects *rects; rects = calloc(1, sizeof(Cutout_Rects)); return rects; } static void evas_common_draw_context_cutouts_dup(Cutout_Rects *rects2, const Cutout_Rects *rects) { if (!rects) return; rects2->active = rects->active; rects2->max = rects->active; rects2->last_add = rects->last_add; if (rects2->max > 0) { const size_t sz = sizeof(Cutout_Rect) * rects2->max; rects2->rects = malloc(sz); memcpy(rects2->rects, rects->rects, sz); return; } else rects2->rects = NULL; } EAPI void evas_common_draw_context_cutouts_free(Cutout_Rects* rects) { if (!rects) return; rects->active = 0; rects->last_add.w = 0; } EAPI void evas_common_draw_context_cutouts_real_free(Cutout_Rects* rects) { if (!rects) return; free(rects->rects); free(rects); } EAPI void evas_common_draw_context_cutouts_del(Cutout_Rects* rects, int idx) { if ((idx >= 0) && (idx < rects->active)) { Cutout_Rect *rect; rect = rects->rects + idx; memmove(rect, rect + 1, sizeof(Cutout_Rect) * (rects->active - idx - 1)); rects->active--; rects->last_add.w = 0; } } static int _init_count = 0; static Eina_Trash *_ctxt_spares = NULL; static int _ctxt_spares_count = 0; static SLK(_ctx_spares_lock); static void _evas_common_draw_context_real_free(RGBA_Draw_Context *dc) { #ifdef HAVE_PIXMAN # if defined(PIXMAN_FONT) || defined(PIXMAN_RECT) || defined(PIXMAN_LINE) || defined(PIXMAN_POLY) if (dc->col.pixman_color_image) pixman_image_unref(dc->col.pixman_color_image); # endif #endif evas_common_draw_context_apply_clean_cutouts(&dc->cutout); evas_common_draw_context_cutouts_real_free(dc->cache.rects); free(dc); } static void _evas_common_draw_context_stash(RGBA_Draw_Context *dc) { if (_ctxt_spares_count >= 8) { _evas_common_draw_context_real_free(dc); return ; } #ifdef HAVE_PIXMAN # if defined(PIXMAN_FONT) || defined(PIXMAN_RECT) || defined(PIXMAN_LINE) || defined(PIXMAN_POLY) if (dc->col.pixman_color_image) { pixman_image_unref(dc->col.pixman_color_image); dc->col.pixman_color_image = NULL; } # endif #endif evas_common_draw_context_apply_clean_cutouts(&dc->cutout); evas_common_draw_context_cutouts_real_free(dc->cache.rects); SLKL(_ctx_spares_lock); eina_trash_push(&_ctxt_spares, dc); _ctxt_spares_count++; SLKU(_ctx_spares_lock); } static RGBA_Draw_Context * _evas_common_draw_context_find(void) { RGBA_Draw_Context *dc; if (!_ctxt_spares) { dc = malloc(sizeof(RGBA_Draw_Context)); } else { SLKL(_ctx_spares_lock); dc = eina_trash_pop(&_ctxt_spares); _ctxt_spares_count--; SLKU(_ctx_spares_lock); } return dc; } EAPI void evas_common_init(void) { if (_init_count++) return; SLKI(_ctx_spares_lock); evas_common_cpu_init(); evas_common_blend_init(); evas_common_image_init(); evas_common_convert_init(); evas_common_scale_init(); evas_common_scale_sample_init(); evas_common_rectangle_init(); evas_common_polygon_init(); evas_common_line_init(); evas_common_font_init(); evas_common_draw_init(); evas_common_tilebuf_init(); } EAPI void evas_common_shutdown(void) { if (--_init_count) return; evas_font_dir_cache_free(); evas_common_font_shutdown(); evas_common_image_shutdown(); evas_common_image_cache_free(); evas_common_scale_sample_shutdown(); // just in case any thread is still doing things... don't del this here // RGBA_Draw_Context *dc; // SLKL(_ctx_spares_lock); // EINA_LIST_FREE(_ctxt_spares, dc) _evas_common_draw_context_real_free(dc); // _ctxt_spares_count = 0; // SLKU(_ctx_spares_lock); // SLKD(_ctx_spares_lock); } EAPI void evas_common_draw_init(void) { } EAPI RGBA_Draw_Context * evas_common_draw_context_new(void) { RGBA_Draw_Context *dc; dc = _evas_common_draw_context_find(); if (!dc) return NULL; memset(dc, 0, sizeof(RGBA_Draw_Context)); return dc; } EAPI RGBA_Draw_Context * evas_common_draw_context_dup(RGBA_Draw_Context *dc) { RGBA_Draw_Context *dc2 = _evas_common_draw_context_find(); if (!dc) return dc2; memcpy(dc2, dc, sizeof(RGBA_Draw_Context)); evas_common_draw_context_cutouts_dup(&dc2->cutout, &dc->cutout); #ifdef HAVE_PIXMAN # if defined(PIXMAN_FONT) || defined(PIXMAN_RECT) || defined(PIXMAN_LINE) || defined(PIXMAN_POLY) dc2->col.pixman_color_image = NULL; # endif #endif dc2->cache.rects = NULL; dc2->cache.used = 0; return dc2; } EAPI void evas_common_draw_context_free(RGBA_Draw_Context *dc) { if (!dc) return; _evas_common_draw_context_stash(dc); } EAPI void evas_common_draw_context_clear_cutouts(RGBA_Draw_Context *dc) { evas_common_draw_context_cutouts_free(&dc->cutout); } EAPI void evas_common_draw_context_font_ext_set(RGBA_Draw_Context *dc, void *data, void *(*gl_new) (void *data, RGBA_Font_Glyph *fg), void (*gl_free) (void *ext_dat), void (*gl_draw) (void *data, void *dest, void *context, RGBA_Font_Glyph *fg, int x, int y, int w, int h), void *(*gl_image_new) (void *gc, RGBA_Font_Glyph *fg, int alpha, Evas_Colorspace cspace), void (*gl_image_free) (void *image), void (*gl_image_draw) (void *gc, void *im, int dx, int dy, int dw, int dh, int smooth)) { dc->font_ext.data = data; dc->font_ext.func.gl_new = gl_new; dc->font_ext.func.gl_free = gl_free; dc->font_ext.func.gl_draw = gl_draw; dc->font_ext.func.gl_image_new = gl_image_new; dc->font_ext.func.gl_image_free = gl_image_free; dc->font_ext.func.gl_image_draw = gl_image_draw; } EAPI void evas_common_draw_context_clip_clip(RGBA_Draw_Context *dc, int x, int y, int w, int h) { if (dc->clip.use) { RECTS_CLIP_TO_RECT(dc->clip.x, dc->clip.y, dc->clip.w, dc->clip.h, x, y, w, h); } else evas_common_draw_context_set_clip(dc, x, y, w, h); } EAPI void evas_common_draw_context_set_clip(RGBA_Draw_Context *dc, int x, int y, int w, int h) { dc->clip.use = 1; dc->clip.x = x; dc->clip.y = y; dc->clip.w = w; dc->clip.h = h; } EAPI void evas_common_draw_context_unset_clip(RGBA_Draw_Context *dc) { dc->clip.use = 0; } EAPI void evas_common_draw_context_set_color(RGBA_Draw_Context *dc, int r, int g, int b, int a) { R_VAL(&(dc->col.col)) = (DATA8)r; G_VAL(&(dc->col.col)) = (DATA8)g; B_VAL(&(dc->col.col)) = (DATA8)b; A_VAL(&(dc->col.col)) = (DATA8)a; #ifdef HAVE_PIXMAN #if defined(PIXMAN_FONT) || defined(PIXMAN_RECT) || defined(PIXMAN_LINE) || defined(PIXMAN_POLY) if (dc->col.pixman_color_image) pixman_image_unref(dc->col.pixman_color_image); pixman_color_t pixman_color; pixman_color.alpha = (dc->col.col & 0xff000000) >> 16; pixman_color.red = (dc->col.col & 0x00ff0000) >> 8; pixman_color.green = (dc->col.col & 0x0000ff00); pixman_color.blue = (dc->col.col & 0x000000ff) << 8; dc->col.pixman_color_image = pixman_image_create_solid_fill(&pixman_color); #endif #endif } EAPI void evas_common_draw_context_set_multiplier(RGBA_Draw_Context *dc, int r, int g, int b, int a) { dc->mul.use = 1; R_VAL(&(dc->mul.col)) = (DATA8)r; G_VAL(&(dc->mul.col)) = (DATA8)g; B_VAL(&(dc->mul.col)) = (DATA8)b; A_VAL(&(dc->mul.col)) = (DATA8)a; } EAPI void evas_common_draw_context_unset_multiplier(RGBA_Draw_Context *dc) { dc->mul.use = 0; } EAPI void evas_common_draw_context_add_cutout(RGBA_Draw_Context *dc, int x, int y, int w, int h) { // if (dc->cutout.rects > 512) return; if (dc->clip.use) { #if 1 // this is a bit faster int x1, x2, y1, y2; int cx1, cx2, cy1, cy2; x2 = x + w; cx1 = dc->clip.x; if (x2 <= cx1) return; x1 = x; cx2 = cx1 + dc->clip.w; if (x1 >= cx2) return; if (x1 < cx1) x1 = cx1; if (x2 > cx2) x2 = cx2; y2 = y + h; cy1 = dc->clip.y; if (y2 <= cy1) return; y1 = y; cy2 = cy1 + dc->clip.h; if (y1 >= cy2) return; if (y1 < cy1) y1 = cy1; if (y2 > cy2) y2 = cy2; x = x1; y = y1; w = x2 - x1; h = y2 - y1; #else RECTS_CLIP_TO_RECT(x, y, w, h, dc->clip.x, dc->clip.y, dc->clip.w, dc->clip.h); #endif } if ((w * h) <= (8 * 8)) return; if (dc->cutout.last_add.w > 0) { if ((dc->cutout.last_add.x == x) && (dc->cutout.last_add.y == y) && (dc->cutout.last_add.w == w) && (dc->cutout.last_add.h == h)) return; } dc->cutout.last_add.x = x; dc->cutout.last_add.y = y; dc->cutout.last_add.w = w; dc->cutout.last_add.h = h; evas_common_draw_context_cutouts_add(&dc->cutout, x, y, w, h); } static int evas_common_draw_context_cutout_split(Cutout_Rects *res, int idx, Cutout_Rect *split) { /* 1 input rect, multiple out */ Cutout_Rect in = res->rects[idx]; /* this is to save me a LOT of typing */ #define INX1 (in.x) #define INX2 (in.x + in.w) #define SPX1 (split->x) #define SPX2 (split->x + split->w) #define INY1 (in.y) #define INY2 (in.y + in.h) #define SPY1 (split->y) #define SPY2 (split->y + split->h) #define X1_IN (in.x < split->x) #define X2_IN ((in.x + in.w) > (split->x + split->w)) #define Y1_IN (in.y < split->y) #define Y2_IN ((in.y + in.h) > (split->y + split->h)) #define R_NEW(_r, _x, _y, _w, _h) { evas_common_draw_context_cutouts_add(_r, _x, _y, _w, _h); } if (!RECTS_INTERSECT(in.x, in.y, in.w, in.h, split->x, split->y, split->w, split->h)) { /* No colision => no clipping, don't touch it. */ return 1; } /* S = split (ie cut out rect) */ /* +--+ = in (rect to be cut) */ /* * +---+ * | | * | S | * | | * +---+ * */ if (X1_IN && X2_IN && Y1_IN && Y2_IN) { R_NEW(res, in.x, in.y, in.w, SPY1 - in.y); R_NEW(res, in.x, SPY1, SPX1 - in.x, SPY2 - SPY1); R_NEW(res, SPX2, SPY1, INX2 - SPX2, SPY2 - SPY1); /* out => (in.x, SPY2, in.w, INY2 - SPY2) */ res->rects[idx].h = INY2 - SPY2; res->rects[idx].y = SPY2; return 1; } /* SSSSSSS * S+---+S * S|SSS|S * S|SSS|S * S|SSS|S * S+---+S * SSSSSSS */ if (!X1_IN && !X2_IN && !Y1_IN && !Y2_IN) { evas_common_draw_context_cutouts_del(res, idx); return 0; } /* SSS * S+---+ * S|S | * S|S | * S|S | * S+---+ * SSS */ if (!X1_IN && X2_IN && !Y1_IN && !Y2_IN) { /* in => (SPX2, in.y, INX2 - SPX2, in.h) */ res->rects[idx].w = INX2 - SPX2; res->rects[idx].x = SPX2; return 1; } /* S * +---+ * | S | * | S | * | S | * +---+ * S */ if (X1_IN && X2_IN && !Y1_IN && !Y2_IN) { R_NEW(res, in.x, in.y, SPX1 - in.x, in.h); /* in => (SPX2, in.y, INX2 - SPX2, in.h) */ res->rects[idx].w = INX2 - SPX2; res->rects[idx].x = SPX2; return 1; } /* SSS * +---+S * | S|S * | S|S * | S|S * +---+S * SSS */ if (X1_IN && !X2_IN && !Y1_IN && !Y2_IN) { /* in => (in.x, in.y, SPX1 - in.x, in.h) */ res->rects[idx].w = SPX1 - in.x; return 1; } /* SSSSSSS * S+---+S * S|SSS|S * | | * | | * +---+ * */ if (!X1_IN && !X2_IN && !Y1_IN && Y2_IN) { /* in => (in.x, SPY2, in.w, INY2 - SPY2) */ res->rects[idx].h = INY2 - SPY2; res->rects[idx].y = SPY2; return 1; } /* * +---+ * | | * S|SSS|S * | | * +---+ * */ if (!X1_IN && !X2_IN && Y1_IN && Y2_IN) { R_NEW(res, in.x, SPY2, in.w, INY2 - SPY2); /* in => (in.x, in.y, in.w, SPY1 - in.y) */ res->rects[idx].h = SPY1 - in.y; return 1; } /* * +---+ * | | * | | * S|SSS|S * S+---+S * SSSSSSS */ if (!X1_IN && !X2_IN && Y1_IN && !Y2_IN) { /* in => (in.x, in.y, in.w, SPY1 - in.y) */ res->rects[idx].h = SPY1 - in.y; return 1; } /* SSS * S+---+ * S|S | * | | * | | * +---+ * */ if (!X1_IN && X2_IN && !Y1_IN && Y2_IN) { R_NEW(res, SPX2, in.y, INX2 - SPX2, SPY2 - in.y); /* in => (in.x, SPY2, in.w, INY2 - SPY2) */ res->rects[idx].h = INY2 - SPY2; res->rects[idx].y = SPY2; return 1; } /* S * +---+ * | S | * | | * | | * +---+ * */ if (X1_IN && X2_IN && !Y1_IN && Y2_IN) { R_NEW(res, in.x, in.y, SPX1 - in.x, SPY2 - in.y); R_NEW(res, SPX2, in.y, INX2 - SPX2, SPY2 - in.y); /* in => (in.x, SPY2, in.w, INY2 - SPY2) */ res->rects[idx].h = INY2 - SPY2; res->rects[idx].y = SPY2; return 1; } /* SSS * +---+S * | S|S * | | * | | * +---+ * */ if (X1_IN && !X2_IN && !Y1_IN && Y2_IN) { R_NEW(res, in.x, in.y, SPX1 - in.x, SPY2 - in.y); /* in => (in.x, SPY2, in.w, INY2 - SPY2) */ res->rects[idx].h = INY2 - SPY2; res->rects[idx].y = SPY2; return 1; } /* * +---+ * | | * S|S | * | | * +---+ * */ if (!X1_IN && X2_IN && Y1_IN && Y2_IN) { R_NEW(res, in.x, SPY2, in.w, INY2 - SPY2); R_NEW(res, SPX2, SPY1, INX2 - SPX2, SPY2 - SPY1); /* in => (in.x, SPY2, in.w, INY2 - SPY2) */ res->rects[idx].h = SPY1 - in.y; return 1; } /* * +---+ * | | * | S|S * | | * +---+ * */ if (X1_IN && !X2_IN && Y1_IN && Y2_IN) { R_NEW(res, in.x, SPY2, in.w, INY2 - SPY2); R_NEW(res, in.x, SPY1, SPX1 - in.x, SPY2 - SPY1); /* in => (in.x, in.y, in.w, SPY1 - in.y) */ res->rects[idx].h = SPY1 - in.y; return 1; } /* * +---+ * | | * | | * S|S | * S+---+ * SSS */ if (!X1_IN && X2_IN && Y1_IN && !Y2_IN) { R_NEW(res, SPX2, SPY1, INX2 - SPX2, INY2 - SPY1); /* in => (in.x, in.y, in.w, SPY1 - in.y) */ res->rects[idx].h = SPY1 - in.y; return 1; } /* * +---+ * | | * | | * | S | * +---+ * S */ if (X1_IN && X2_IN && Y1_IN && !Y2_IN) { R_NEW(res, in.x, SPY1, SPX1 - in.x, INY2 - SPY1); R_NEW(res, SPX2, SPY1, INX2 - SPX2, INY2 - SPY1); /* in => (in.x, in.y, in.w, SPY1 - in.y) */ res->rects[idx].h = SPY1 - in.y; return 1; } /* * +---+ * | | * | | * | S|S * +---+S * SSS */ if (X1_IN && !X2_IN && Y1_IN && !Y2_IN) { R_NEW(res, in.x, SPY1, SPX1 - in.x, INY2 - SPY1); /* in => (in.x, in.y, in.w, SPY1 - in.y) */ res->rects[idx].h = SPY1 - in.y; return 1; } evas_common_draw_context_cutouts_del(res, idx); return 0; #undef INX1 #undef INX2 #undef SPX1 #undef SPX2 #undef INY1 #undef INY2 #undef SPY1 #undef SPY2 #undef X1_IN #undef X2_IN #undef Y1_IN #undef Y2_IN #undef R_NEW } EAPI void evas_common_draw_context_target_set(RGBA_Draw_Context *dc, int x, int y, int w, int h) { dc->cutout_target.x = x; dc->cutout_target.y = y; dc->cutout_target.w = w; dc->cutout_target.h = h; } static int _srt_y(const void *d1, const void *d2) { const Cutout_Rect *r1 = d1, *r2 = d2; if (r1->y == r2->y) return r1->x - r2->x; return r1->y - r2->y; } static int _srt_x(const void *d1, const void *d2) { const Cutout_Rect *r1 = d1, *r2 = d2; if (r1->x == r2->x) return r1->y - r2->y; return r1->x - r2->x; } EAPI Cutout_Rects * evas_common_draw_context_apply_cutouts(RGBA_Draw_Context *dc, Cutout_Rects *reuse) { Cutout_Rects *res = NULL; int i, j, active, found = 0; if (!dc->clip.use) return NULL; if ((dc->clip.w <= 0) || (dc->clip.h <= 0)) return NULL; if (!reuse) res = evas_common_draw_context_cutouts_new(); else { evas_common_draw_context_cutouts_free(reuse); res = reuse; } // this avoids a nasty case of O(n^2)/2 below with lots of rectangles // to merge so only do this merging if the number of rects is small enough // not to blow out into insanity evas_common_draw_context_cutouts_add(res, dc->clip.x, dc->clip.y, dc->clip.w, dc->clip.h); for (i = 0; i < dc->cutout.active; i++) { if ((dc->cutout_target.w != 0) && (!RECTS_INTERSECT(dc->cutout.rects[i].x, dc->cutout.rects[i].y, dc->cutout.rects[i].w, dc->cutout.rects[i].h, dc->cutout_target.x, dc->cutout_target.y, dc->cutout_target.w, dc->cutout_target.h))) continue; // Don't loop on the element just added to the list as they are // already correctly clipped. active = res->active; for (j = 0; j < active; ) { if (evas_common_draw_context_cutout_split (res, j, dc->cutout.rects + i)) j++; else active--; } } /* merge rects */ #define RI res->rects[i] #define RJ res->rects[j] if (res->active > 1) { if (res->active > 5) { // fast path for larger numbers of rects to merge by using // qsort to sort by y and x to limit the number of rects // we have to walk as rects that have a different y cannot // be merged anyway (or x). qsort(res->rects, res->active, sizeof(res->rects[0]), _srt_y); for (i = 0; i < res->active; i++) { if (RI.w == 0) continue; // skip empty rect for (j = i + 1; j < res->active; j++) { if (RJ.y != RI.y) break; // new line, sorted thus skip if (RJ.w == 0) continue; // skip empty rect // if J is the same height (could be merged) if (RJ.h == RI.h) { // if J is immediately to the right of I if (RJ.x == (RI.x + RI.w)) { RI.w = (RJ.x + RJ.w) - RI.x; // expand RI RJ.w = 0; // invalidate found++; } // since we sort y and THEN x, if height matches // but it's not immediately adjacent, no more // rects exists that can be merged else break; } } } qsort(res->rects, res->active, sizeof(res->rects[0]), _srt_x); for (i = 0; i < res->active; i++) { if (RI.w == 0) continue; // skip empty rect for (j = i + 1; j < res->active; j++) { if (RJ.x != RI.x) break; // new line, sorted thus skip if (RJ.w == 0) continue; // skip empty rect // if J is the same height (could be merged) if (RJ.w == RI.w) { // if J is immediately to the right of I if (RJ.y == (RI.y + RI.h)) { RI.h = (RJ.y + RJ.h) - RI.y; // expand RI RJ.w = 0; // invalidate found++; } // since we sort y and THEN x, if height matches // but it's not immediately adjacent, no more // rects exists that can be merged else break; } } } } else { // for a small number of rects, keep things simple as the count // is small and big-o complexity isnt a problem yet found = 1; while (found) { found = 0; for (i = 0; i < res->active; i++) { for (j = i + 1; j < res->active; j++) { // skip empty rects we are removing if (RJ.w == 0) continue; // check if its same width, immediately above or below if ((RJ.w == RI.w) && (RJ.x == RI.x)) { if ((RJ.y + RJ.h) == RI.y) // above { RI.y = RJ.y; RI.h += RJ.h; RJ.w = 0; found++; } else if ((RI.y + RI.h) == RJ.y) // below { RI.h += RJ.h; RJ.w = 0; found++; } } // check if its same height, immediately left or right else if ((RJ.h == RI.h) && (RJ.y == RI.y)) { if ((RJ.x + RJ.w) == RI.x) // left { RI.x = RJ.x; RI.w += RJ.w; RJ.w = 0; found++; } else if ((RI.x + RI.w) == RJ.x) // right { RI.w += RJ.w; RJ.w = 0; found++; } } } } } } // Repack the cutout j = 0; for (i = 0; i < res->active; i++) { if (RI.w == 0) continue; if (i != j) RJ = RI; j++; } res->active = j; } return res; } EAPI void evas_common_draw_context_apply_clear_cutouts(Cutout_Rects *rects) { evas_common_draw_context_apply_clean_cutouts(rects); free(rects); } EAPI void evas_common_draw_context_apply_clean_cutouts(Cutout_Rects *rects) { free(rects->rects); rects->rects = NULL; rects->active = 0; rects->max = 0; rects->last_add.w = 0; } EAPI void evas_common_draw_context_set_anti_alias(RGBA_Draw_Context *dc , unsigned char aa) { dc->anti_alias = !!aa; } EAPI void evas_common_draw_context_set_color_interpolation(RGBA_Draw_Context *dc, int color_space) { dc->interpolation.color_space = color_space; } EAPI void evas_common_draw_context_set_render_op(RGBA_Draw_Context *dc , int op) { dc->render_op = op; }