1 #include "evas_gl_private.h"
2 
3 // FIXME: this is a verbatim copy of the software poly renderer. it just
4 // use gl to draw 1 pixel high spans like software does. this is to make
5 // sure rendering correctness matches the software engine but also to save
6 // time in coming up with a good triangulation algorithm. if you want to
7 // feel free to turn this into a real triangulation system and use gl to its
8 // fullest, but as such polygons are used so little, it's not worth it.
9 
10 typedef struct _RGBA_Span RGBA_Span;
11 typedef struct _RGBA_Edge RGBA_Edge;
12 typedef struct _RGBA_Vertex RGBA_Vertex;
13 
14 struct _RGBA_Span
15 {
16    EINA_INLIST;
17    int x, y, w;
18 };
19 
20 struct _RGBA_Edge
21 {
22    double x, dx;
23    int i;
24 };
25 
26 struct _RGBA_Vertex
27 {
28    double x, y;
29    int i;
30 };
31 
32 #define POLY_EDGE_DEL(_i)                                               \
33    {                                                                       \
34       int _j;                                                              \
35       \
36       for (_j = 0; (_j < num_active_edges) && (edges[_j].i != _i); _j++);  \
37       if (_j < num_active_edges)                                           \
38         {                                                                  \
39            num_active_edges--;                                             \
40            memmove(&(edges[_j]), &(edges[_j + 1]),                         \
41                       (num_active_edges - _j) * sizeof(RGBA_Edge));           \
42         }                                                                  \
43    }
44 
45 #define POLY_EDGE_ADD(_i, _y)                                           \
46    {                                                                       \
47       int _j;                                                              \
48       float _dx;                                                           \
49       RGBA_Vertex *_p, *_q;                                                \
50       if (_i < (n - 1)) _j = _i + 1;                                       \
51       else _j = 0;                                                         \
52       if (point[_i].y < point[_j].y)                                       \
53         {                                                                  \
54            _p = &(point[_i]);                                              \
55            _q = &(point[_j]);                                              \
56         }                                                                  \
57       else                                                                 \
58         {                                                                  \
59            _p = &(point[_j]);                                              \
60            _q = &(point[_i]);                                              \
61         }                                                                  \
62       edges[num_active_edges].dx = _dx = (_q->x - _p->x) / (_q->y - _p->y); \
63       edges[num_active_edges].x = (_dx * ((float)_y + 0.5 - _p->y)) + _p->x; \
64       edges[num_active_edges].i = _i;                                      \
65       num_active_edges++;                                                  \
66    }
67 
68 Evas_GL_Polygon *
evas_gl_common_poly_point_add(Evas_GL_Polygon * poly,int x,int y)69 evas_gl_common_poly_point_add(Evas_GL_Polygon *poly, int x, int y)
70 {
71    Evas_GL_Polygon_Point *pt;
72 
73    pt = calloc(1, sizeof(Evas_GL_Polygon_Point));
74    if (!pt) return NULL;
75 
76    if (!poly) poly = calloc(1, sizeof(Evas_GL_Polygon));
77    if (!poly)
78      {
79         free(pt);
80         return NULL;
81      }
82 
83    pt->x = x;
84    pt->y = y;
85    poly->points = eina_list_append(poly->points, pt);
86    poly->changed = 1;
87    return poly;
88 }
89 
90 Evas_GL_Polygon *
evas_gl_common_poly_points_clear(Evas_GL_Polygon * poly)91 evas_gl_common_poly_points_clear(Evas_GL_Polygon *poly)
92 {
93    if (!poly) return NULL;
94    while (poly->points)
95      {
96 	Evas_GL_Polygon_Point *pt;
97 
98 	pt = poly->points->data;
99 	poly->points = eina_list_remove(poly->points, pt);
100 	free(pt);
101      }
102    free(poly);
103    return NULL;
104 }
105 
106 static int
polygon_point_sorter(const void * a,const void * b)107 polygon_point_sorter(const void *a, const void *b)
108 {
109    RGBA_Vertex *p, *q;
110 
111    p = (RGBA_Vertex *)a;
112    q = (RGBA_Vertex *)b;
113    if (p->y <= q->y) return -1;
114    return 1;
115 }
116 
117 static int
polygon_edge_sorter(const void * a,const void * b)118 polygon_edge_sorter(const void *a, const void *b)
119 {
120    RGBA_Edge *p, *q;
121 
122    p = (RGBA_Edge *)a;
123    q = (RGBA_Edge *)b;
124    if (p->x <= q->x) return -1;
125    return 1;
126 }
127 
128 void
evas_gl_common_poly_draw(Evas_Engine_GL_Context * gc,Evas_GL_Polygon * poly,int dx,int dy)129 evas_gl_common_poly_draw(Evas_Engine_GL_Context *gc, Evas_GL_Polygon *poly, int dx, int dy)
130 {
131    Cutout_Rect  *r;
132    int c, cx, cy, cw, ch, cr, cg, cb, ca, i;
133    int x = 0, y = 0, w = 0, h = 0;
134    Evas_GL_Texture *mtex = NULL;
135    Eina_Bool mask_smooth = EINA_FALSE;
136    Eina_Bool mask_color = EINA_FALSE;
137    int mx = 0, my = 0, mw = 0, mh = 0;
138    Evas_GL_Image *mask;
139 
140    Eina_List *l;
141    int n, k, num_active_edges, yy0, yy1, *sorted_index, j;
142    RGBA_Edge *edges;
143    RGBA_Vertex *point;
144    Evas_GL_Polygon_Point *pt;
145    Eina_Inlist *spans;
146 
147    /* save out clip info */
148    c = gc->dc->clip.use; cx = gc->dc->clip.x; cy = gc->dc->clip.y; cw = gc->dc->clip.w; ch = gc->dc->clip.h;
149 
150    ca = (gc->dc->col.col >> 24) & 0xff;
151    if (ca <= 0) return;
152    cr = (gc->dc->col.col >> 16) & 0xff;
153    cg = (gc->dc->col.col >> 8 ) & 0xff;
154    cb = (gc->dc->col.col      ) & 0xff;
155 
156    mask = gc->dc->clip.mask;
157    if (mask)
158      {
159         evas_gl_common_image_update(gc, mask);
160         mtex = mask->tex;
161         if (mtex && mtex->pt && mtex->pt->w && mtex->pt->h)
162           {
163              // canvas coords
164              mx = gc->dc->clip.mask_x;
165              my = gc->dc->clip.mask_y;
166              mw = mask->w;
167              mh = mask->h;
168              mask_smooth = mask->scaled.smooth;
169              mask_color = gc->dc->clip.mask_color;
170           }
171         else mtex = NULL;
172      }
173 
174    n = eina_list_count(poly->points);
175    if (n < 3) return;
176    edges = malloc(sizeof(RGBA_Edge) * n);
177    if (!edges) return;
178    point = malloc(sizeof(RGBA_Vertex) * n);
179    if (!point)
180      {
181         free(edges);
182         return;
183      }
184    sorted_index = malloc(sizeof(int) * n);
185    if (!sorted_index)
186      {
187         free(edges);
188         free(point);
189         return;
190      }
191 
192    k = 0;
193    EINA_LIST_FOREACH(poly->points, l, pt)
194      {
195         point[k].x = pt->x + dx;
196         point[k].y = pt->y + dy;
197         point[k].i = k;
198         k++;
199      }
200    qsort(point, n, sizeof(RGBA_Vertex), polygon_point_sorter);
201    for (k = 0; k < n; k++) sorted_index[k] = point[k].i;
202    k = 0;
203 
204    EINA_LIST_FOREACH(poly->points, l, pt)
205      {
206         point[k].x = pt->x + dx;
207         point[k].y = pt->y + dy;
208         point[k].i = k;
209         k++;
210      }
211 
212    yy0 = MAX(cy, ceil(point[sorted_index[0]].y - 0.5));
213    yy1 = MIN(cy + ch - 1, floor(point[sorted_index[n - 1]].y - 0.5));
214 
215    k = 0;
216    num_active_edges = 0;
217    spans = NULL;
218 
219    for (y = yy0; y <= yy1; y++)
220      {
221         for (; (k < n) && (point[sorted_index[k]].y <= ((double)y + 0.5)); k++)
222           {
223              i = sorted_index[k];
224 
225              if (i > 0) j = i - 1;
226              else j = n - 1;
227              if (point[j].y <= ((double)y - 0.5))
228                {
229                   POLY_EDGE_DEL(j)
230                }
231              else if (point[j].y > ((double)y + 0.5))
232                {
233                   POLY_EDGE_ADD(j, y)
234                }
235              if (i < (n - 1)) j = i + 1;
236              else j = 0;
237              if (point[j].y <= ((double)y - 0.5))
238                {
239                   POLY_EDGE_DEL(i)
240                }
241              else if (point[j].y > ((double)y + 0.5))
242                {
243                   POLY_EDGE_ADD(i, y)
244                }
245           }
246 
247         qsort(edges, num_active_edges, sizeof(RGBA_Edge), polygon_edge_sorter);
248 
249         for (j = 0; j < num_active_edges; j += 2)
250           {
251              int x0, x1;
252 
253              x0 = ceil(edges[j].x - 0.5);
254              if (j < (num_active_edges - 1))
255                x1 = floor(edges[j + 1].x - 0.5);
256              else
257                x1 = x0;
258              if ((x1 >= cx) && (x0 < (cx + cw)) && (x0 <= x1))
259                {
260                   RGBA_Span *span;
261 
262                   if (x0 < cx) x0 = cx;
263                   if (x1 >= (cx + cw)) x1 = cx + cw - 1;
264                   span = malloc(sizeof(RGBA_Span));
265                   spans = eina_inlist_append(spans, EINA_INLIST_GET(span));
266                   span->y = y;
267                   span->x = x0;
268                   span->w = (x1 - x0) + 1;
269                }
270              edges[j].x += edges[j].dx;
271              edges[j + 1].x += edges[j + 1].dx;
272           }
273      }
274 
275    free(edges);
276    free(point);
277    free(sorted_index);
278 
279    evas_common_draw_context_clip_clip(gc->dc, 0, 0, gc->shared->w, gc->shared->h);
280 
281    if (spans)
282      {
283         RGBA_Span *span;
284 
285         /* no cutouts - cut right to the chase */
286         if (!gc->dc->cutout.rects)
287           {
288              EINA_INLIST_FOREACH(spans, span)
289                {
290                   x = span->x;
291                   y = span->y;
292                   w = span->w;
293                   h = 1;
294                   evas_gl_common_context_rectangle_push(gc, x, y, w, h,
295                                                         cr, cg, cb, ca,
296                                                         mtex, mx, my, mw, mh,
297                                                         mask_smooth, mask_color);
298                }
299           }
300         else
301           {
302              /* our clip is 0 size.. abort */
303              if ((gc->dc->clip.w > 0) && (gc->dc->clip.h > 0))
304                {
305                   _evas_gl_common_cutout_rects = evas_common_draw_context_apply_cutouts(gc->dc, _evas_gl_common_cutout_rects);
306                   for (i = 0; i < _evas_gl_common_cutout_rects->active; ++i)
307                     {
308                        r = _evas_gl_common_cutout_rects->rects + i;
309                        if ((r->w > 0) && (r->h > 0))
310                          {
311                             EINA_INLIST_FOREACH(spans, span)
312                               {
313                                  x = span->x;
314                                  y = span->y;
315                                  w = span->w;
316                                  h = 1;
317                                  RECTS_CLIP_TO_RECT(x, y, w, h, r->x, r->y, r->w, r->h);
318                                  if ((w > 0) && (h > 0))
319                                    evas_gl_common_context_rectangle_push(gc, x, y, w, h,
320                                                                          cr, cg, cb, ca,
321                                                                          mtex, mx, my, mw, mh,
322                                                                          mask_smooth, mask_color);
323                               }
324                          }
325                     }
326                   evas_common_draw_context_cutouts_free(_evas_gl_common_cutout_rects);
327                }
328           }
329         while (spans)
330           {
331              span = (RGBA_Span *)spans;
332              spans = eina_inlist_remove(spans, spans);
333              free(span);
334           }
335      }
336 
337    /* restore clip info */
338    gc->dc->clip.use = c; gc->dc->clip.x = cx; gc->dc->clip.y = cy; gc->dc->clip.w = cw; gc->dc->clip.h = ch;
339 
340 }
341