1 #include "evas_common_private.h"
2 #include "evas_private.h"
3 #include "evas_blend_private.h"
4
5 #include "language/evas_bidi_utils.h" /*defines BIDI_SUPPORT if possible */
6 #include "evas_font_private.h" /* for Frame-Queuing support */
7
8 #include "evas_font_ot.h"
9 #include "draw.h"
10
11 struct _Evas_Glyph
12 {
13 RGBA_Font_Glyph *fg;
14 int x, y;
15 FT_UInt idx;
16 };
17
18 EAPI void
evas_common_font_draw_init(void)19 evas_common_font_draw_init(void)
20 {
21 }
22
23 static void *
_evas_font_image_new(RGBA_Font_Glyph * fg,int alpha,Evas_Colorspace cspace)24 _evas_font_image_new(RGBA_Font_Glyph *fg, int alpha, Evas_Colorspace cspace)
25 {
26 DATA32 *image_data;
27 int src_w, src_h;
28
29 if (!fg) return NULL;
30
31 image_data = (DATA32 *)fg->glyph_out->bitmap.buffer;
32 src_w = fg->glyph_out->bitmap.width;
33 src_h = fg->glyph_out->bitmap.rows;
34
35 return evas_cache_image_data(evas_common_image_cache_get(), src_w, src_h, image_data, alpha, cspace);
36 }
37
38 static void
_evas_font_image_draw(void * context,void * surface,void * image,RGBA_Font_Glyph * fg,int x,int y,int w,int h,int smooth)39 _evas_font_image_draw(void *context, void *surface, void *image, RGBA_Font_Glyph *fg, int x, int y, int w, int h, int smooth)
40 {
41 RGBA_Image *im;
42 int src_w, src_h;
43
44 if (!image || !fg) return;
45 im = image;
46 src_w = fg->glyph_out->bitmap.width;
47 src_h = fg->glyph_out->bitmap.rows;
48
49 #ifdef BUILD_PIPE_RENDER
50 if ((eina_cpu_count() > 1))
51 {
52 evas_common_rgba_image_scalecache_prepare((Image_Entry *)(im),
53 surface, context, smooth,
54 0, 0, src_w, src_h,
55 x, y, w, h);
56
57 evas_common_pipe_image_draw(im, surface, context, smooth,
58 0, 0, src_w, src_h,
59 x, y, w, h);
60 }
61 else
62 #endif
63 {
64 evas_common_rgba_image_scalecache_prepare
65 (&im->cache_entry, surface, context, smooth,
66 0, 0, src_w, src_h,
67 x, y, w, h);
68 evas_common_rgba_image_scalecache_do
69 (&im->cache_entry, surface, context, smooth,
70 0, 0, src_w, src_h,
71 x, y, w, h);
72
73 evas_common_cpu_end_opt();
74 }
75 }
76
77 /*
78 * BiDi handling: We receive the shaped string + other props from text_props,
79 * we need to reorder it so we'll have the visual string (the way we draw)
80 * and then for kerning we have to switch the order of the kerning query (as the prev
81 * is on the right, and not on the left).
82 */
83 EAPI Eina_Bool
evas_common_font_rgba_draw(RGBA_Image * dst,RGBA_Draw_Context * dc,int x,int y,Evas_Glyph_Array * glyphs,RGBA_Gfx_Func func EINA_UNUSED,int ext_x,int ext_y,int ext_w,int ext_h,int im_w,int im_h EINA_UNUSED)84 evas_common_font_rgba_draw(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y,
85 Evas_Glyph_Array *glyphs, RGBA_Gfx_Func func EINA_UNUSED, int ext_x, int ext_y, int ext_w,
86 int ext_h, int im_w, int im_h EINA_UNUSED)
87 {
88 Evas_Glyph *glyph;
89
90 if (!glyphs) return EINA_FALSE;
91 if (!glyphs->array) return EINA_FALSE;
92
93 EINA_INARRAY_FOREACH(glyphs->array, glyph)
94 {
95 RGBA_Font_Glyph *fg;
96 int chr_x, chr_y, w, h;
97
98 fg = glyph->fg;
99
100 w = fg->glyph_out->bitmap.width;
101 h = fg->glyph_out->bitmap.rows;
102
103 if (FT_HAS_FIXED_SIZES(fg->fi->src->ft.face))
104 {
105 if ((fg->fi->bitmap_scalable & EFL_TEXT_FONT_BITMAP_SCALABLE_COLOR) &&
106 FT_HAS_COLOR(fg->fi->src->ft.face))
107 {
108 if ( !fg->fi->is_resized )
109 {
110 w *= fg->fi->scale_factor;
111 h *= fg->fi->scale_factor;
112 }
113
114 }
115 }
116
117 if ((!fg->ext_dat) && (dc->font_ext.func.gl_new))
118 {
119 /* extension calls */
120 fg->ext_dat = dc->font_ext.func.gl_new(dc->font_ext.data, fg);
121 fg->ext_dat_free = dc->font_ext.func.gl_free;
122 }
123
124 if (dc->font_ext.func.gl_image_new)
125 {
126 if ((!fg->ext_dat) && FT_HAS_COLOR(fg->fi->src->ft.face))
127 {
128 /* extension calls */
129 fg->ext_dat = dc->font_ext.func.gl_image_new
130 (dc->font_ext.data, fg, EINA_TRUE, EVAS_COLORSPACE_ARGB8888);
131 fg->ext_dat_free = dc->font_ext.func.gl_image_free;
132 }
133 }
134 else
135 {
136 if ((!fg->col_dat) && FT_HAS_COLOR(fg->fi->src->ft.face))
137 {
138 fg->col_dat = _evas_font_image_new(fg, EINA_TRUE, EVAS_COLORSPACE_ARGB8888);
139 }
140 }
141
142 chr_x = x + glyph->x;
143 chr_y = y + glyph->y;
144 if (chr_x < (ext_x + ext_w))
145 {
146 if ((w > 0) && ((chr_x + w) > ext_x))
147 {
148 if (fg->glyph_out->rle)
149 {
150 if ((fg->ext_dat) && (dc->font_ext.func.gl_draw))
151 dc->font_ext.func.gl_draw(dc->font_ext.data, dst,
152 dc, fg,
153 chr_x, y - (chr_y - y), w, h);
154 else
155 // TODO: scale with evas_font_compress_draw.c...
156 evas_common_font_glyph_draw(fg, dc, dst, im_w,
157 chr_x, y - (chr_y - y), w, h,
158 ext_x, ext_y,
159 ext_w, ext_h);
160 }
161 else if (FT_HAS_COLOR(fg->fi->src->ft.face))
162 {
163 if ((fg->ext_dat) && (dc->font_ext.func.gl_image_draw))
164 dc->font_ext.func.gl_image_draw
165 (dc->font_ext.data, fg->ext_dat,
166 chr_x, y - (chr_y - y), w, h, EINA_TRUE);
167 else if (fg->col_dat)
168 _evas_font_image_draw
169 (dc, dst, fg->col_dat, fg,
170 chr_x, y - (chr_y - y), w, h, EINA_TRUE);
171 }
172 }
173 }
174 else
175 break;
176 }
177
178 return EINA_TRUE;
179 }
180
181 void
evas_common_font_glyphs_ref(Evas_Glyph_Array * array)182 evas_common_font_glyphs_ref(Evas_Glyph_Array *array)
183 {
184 array->refcount++;
185 }
186
187 void
evas_common_font_glyphs_unref(Evas_Glyph_Array * array)188 evas_common_font_glyphs_unref(Evas_Glyph_Array *array)
189 {
190 if (--array->refcount) return;
191
192 eina_inarray_free(array->array);
193 evas_common_font_int_unref(array->fi);
194 free(array);
195 }
196
197 void
evas_common_font_fonts_ref(Evas_Font_Array * array)198 evas_common_font_fonts_ref(Evas_Font_Array *array)
199 {
200 array->refcount++;
201 }
202
203 void
evas_common_font_fonts_unref(Evas_Font_Array * array)204 evas_common_font_fonts_unref(Evas_Font_Array *array)
205 {
206 if (--array->refcount) return;
207
208 eina_inarray_free(array->array);
209 free(array);
210 }
211
212 EAPI void
evas_common_font_draw_prepare(Evas_Text_Props * text_props)213 evas_common_font_draw_prepare(Evas_Text_Props *text_props)
214 {
215 RGBA_Font_Int *fi;
216 RGBA_Font_Glyph *fg = NULL;
217 Eina_Inarray *glyphs;
218 size_t unit = 32;
219 Eina_Bool reused_glyphs;
220 EVAS_FONT_WALK_TEXT_INIT();
221
222 fi = text_props->font_instance;
223 if (!fi) return;
224
225 if ((!text_props->changed) &&
226 (text_props->generation == fi->generation) &&
227 text_props->glyphs)
228 return;
229
230 if (text_props->len < unit) unit = text_props->len;
231 if (text_props->glyphs && text_props->glyphs->refcount == 1)
232 {
233 glyphs = text_props->glyphs->array;
234 glyphs->len = 0;
235 reused_glyphs = EINA_TRUE;
236 }
237 else
238 {
239 glyphs = eina_inarray_new(sizeof(Evas_Glyph), unit);
240 reused_glyphs = EINA_FALSE;
241 }
242 evas_common_font_int_reload(fi);
243
244 if (fi->src->current_size != fi->size)
245 {
246 evas_common_font_source_reload(fi->src);
247 FTLOCK();
248 FT_Activate_Size(fi->ft.size);
249 FTUNLOCK();
250 fi->src->current_size = fi->size;
251 }
252
253 EVAS_FONT_WALK_TEXT_START()
254 {
255 Evas_Glyph *glyph;
256 FT_UInt idx;
257
258 if (!EVAS_FONT_WALK_IS_VISIBLE) continue;
259 idx = EVAS_FONT_WALK_INDEX;
260
261 fg = evas_common_font_int_cache_glyph_get(fi, idx);
262 if (!fg) continue;
263 if (!evas_common_font_int_cache_glyph_render(fg))
264 {
265 fg = NULL;
266 goto error;
267 }
268
269 glyph = eina_inarray_grow(glyphs, 1);
270 if (!glyph) goto error;
271
272 glyph->fg = fg;
273 glyph->idx = idx;
274 glyph->x = EVAS_FONT_WALK_PEN_X + EVAS_FONT_WALK_X_OFF + EVAS_FONT_WALK_X_BEAR;
275 glyph->y = EVAS_FONT_WALK_PEN_Y + EVAS_FONT_WALK_Y_OFF + EVAS_FONT_WALK_Y_BEAR;
276 }
277 EVAS_FONT_WALK_TEXT_END();
278
279 if (!reused_glyphs)
280 {
281 if (text_props->glyphs) evas_common_font_glyphs_unref(text_props->glyphs);
282
283 text_props->glyphs = malloc(sizeof(*text_props->glyphs));
284 if (!text_props->glyphs) goto error;
285 text_props->glyphs->refcount = 1;
286 text_props->glyphs->array = glyphs;
287 text_props->glyphs->fi = fi;
288 fi->references++;
289 }
290
291 /* check if there's a request queue in fi, if so ask cserve2 to render
292 * those glyphs
293 */
294
295 text_props->generation = fi->generation;
296 text_props->changed = EINA_FALSE;
297
298 return;
299
300 error:
301 eina_inarray_free(glyphs);
302 }
303
304 EAPI Eina_Bool
evas_common_font_draw_cb(RGBA_Image * dst,RGBA_Draw_Context * dc,int x,int y,Evas_Glyph_Array * glyphs,Evas_Common_Font_Draw_Cb cb)305 evas_common_font_draw_cb(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs, Evas_Common_Font_Draw_Cb cb)
306 {
307 int ext_x, ext_y, ext_w, ext_h;
308 int im_w, im_h;
309 RGBA_Gfx_Func func;
310 Cutout_Rect *r;
311 int c, cx, cy, cw, ch;
312 int i;
313
314 if (!glyphs) return EINA_FALSE;
315
316 im_w = dst->cache_entry.w;
317 im_h = dst->cache_entry.h;
318
319 // evas_common_font_size_use(fn);
320 func = evas_common_gfx_func_composite_mask_color_span_get(dc->col.col, dst->cache_entry.flags.alpha, 1, dc->render_op);
321
322 if (!dc->cutout.rects)
323 {
324 ext_x = 0; ext_y = 0; ext_w = im_w; ext_h = im_h;
325 if (dc->clip.use)
326 {
327 ext_x = dc->clip.x;
328 ext_y = dc->clip.y;
329 ext_w = dc->clip.w;
330 ext_h = dc->clip.h;
331 if (ext_x < 0)
332 {
333 ext_w += ext_x;
334 ext_x = 0;
335 }
336 if (ext_y < 0)
337 {
338 ext_h += ext_y;
339 ext_y = 0;
340 }
341 if ((ext_x + ext_w) > im_w)
342 ext_w = im_w - ext_x;
343 if ((ext_y + ext_h) > im_h)
344 ext_h = im_h - ext_y;
345 }
346 if (ext_w <= 0) return EINA_FALSE;
347 if (ext_h <= 0) return EINA_FALSE;
348
349 return cb(dst, dc, x, y, glyphs,
350 func, ext_x, ext_y, ext_w, ext_h,
351 im_w, im_h);
352 }
353 else
354 {
355 Eina_Bool ret = EINA_FALSE;
356 c = dc->clip.use; cx = dc->clip.x; cy = dc->clip.y; cw = dc->clip.w; ch = dc->clip.h;
357 evas_common_draw_context_clip_clip(dc, 0, 0, dst->cache_entry.w, dst->cache_entry.h);
358 /* our clip is 0 size.. abort */
359 if ((dc->clip.w > 0) && (dc->clip.h > 0))
360 {
361 dc->cache.rects = evas_common_draw_context_apply_cutouts(dc, dc->cache.rects);
362 for (i = 0; i < dc->cache.rects->active; ++i)
363 {
364 r = dc->cache.rects->rects + i;
365 evas_common_draw_context_set_clip(dc, r->x, r->y, r->w, r->h);
366 ret |= cb(dst, dc, x, y, glyphs,
367 func, r->x, r->y, r->w, r->h,
368 im_w, im_h);
369 }
370 evas_common_draw_context_cache_update(dc);
371 }
372 dc->clip.use = c; dc->clip.x = cx; dc->clip.y = cy; dc->clip.w = cw; dc->clip.h = ch;
373
374 return ret;
375 }
376 }
377
378 EAPI void
evas_common_font_draw(RGBA_Image * dst,RGBA_Draw_Context * dc,int x,int y,Evas_Glyph_Array * glyphs)379 evas_common_font_draw(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs)
380 {
381 evas_common_font_draw_cb(dst, dc, x, y, glyphs,
382 evas_common_font_rgba_draw);
383 }
384
385 EAPI void
evas_common_font_draw_do(const Cutout_Rects * reuse,const Eina_Rectangle * clip,RGBA_Gfx_Func func,RGBA_Image * dst,RGBA_Draw_Context * dc,int x,int y,const Evas_Text_Props * text_props)386 evas_common_font_draw_do(const Cutout_Rects *reuse, const Eina_Rectangle *clip, RGBA_Gfx_Func func,
387 RGBA_Image *dst, RGBA_Draw_Context *dc,
388 int x, int y, const Evas_Text_Props *text_props)
389 {
390 Eina_Rectangle area;
391 Cutout_Rect *r;
392 int i;
393 int im_w, im_h;
394
395 im_w = dst->cache_entry.w;
396 im_h = dst->cache_entry.h;
397
398 if (!reuse)
399 {
400 evas_common_draw_context_clip_clip(dc,
401 clip->x, clip->y,
402 clip->w, clip->h);
403 evas_common_font_rgba_draw(dst, dc, x, y, text_props->glyphs,
404 func,
405 dc->clip.x, dc->clip.y,
406 dc->clip.w, dc->clip.h,
407 im_w, im_h);
408 return;
409 }
410
411 for (i = 0; i < reuse->active; ++i)
412 {
413 r = reuse->rects + i;
414
415 EINA_RECTANGLE_SET(&area, r->x, r->y, r->w - 1, r->h - 1);
416 if (!eina_rectangle_intersection(&area, clip)) continue ;
417 evas_common_draw_context_set_clip(dc, area.x, area.y, area.w, area.h);
418 evas_common_font_rgba_draw(dst, dc, x, y, text_props->glyphs,
419 func, area.x, area.y, area.w, area.h,
420 im_w, im_h);
421 }
422 }
423
424 EAPI Eina_Bool
evas_common_font_draw_prepare_cutout(Cutout_Rects ** reuse,RGBA_Image * dst,RGBA_Draw_Context * dc,RGBA_Gfx_Func * func)425 evas_common_font_draw_prepare_cutout(Cutout_Rects **reuse, RGBA_Image *dst, RGBA_Draw_Context *dc, RGBA_Gfx_Func *func)
426 {
427 int im_w, im_h;
428
429 im_w = dst->cache_entry.w;
430 im_h = dst->cache_entry.h;
431
432 *func = evas_common_gfx_func_composite_mask_color_span_get(dc->col.col, dst->cache_entry.flags.alpha, 1, dc->render_op);
433
434 evas_common_draw_context_clip_clip(dc, 0, 0, im_w, im_h);
435 if (dc->clip.w <= 0) return EINA_FALSE;
436 if (dc->clip.h <= 0) return EINA_FALSE;
437
438 if (dc->cutout.rects)
439 {
440 *reuse = evas_common_draw_context_apply_cutouts(dc, *reuse);
441 }
442
443 return EINA_TRUE;
444 }
445
446 // this draws a compressed font glyph and decompresses on the fly as it
447 // draws, saving memory bandwidth and providing speedups
448 EAPI void
evas_common_font_glyph_draw(RGBA_Font_Glyph * fg,RGBA_Draw_Context * dc,RGBA_Image * dst_image,int dst_pitch,int dx,int dy,int dw,int dh,int cx,int cy,int cw,int ch)449 evas_common_font_glyph_draw(RGBA_Font_Glyph *fg,
450 RGBA_Draw_Context *dc,
451 RGBA_Image *dst_image, int dst_pitch,
452 int dx, int dy, int dw, int dh, int cx, int cy, int cw, int ch)
453 {
454 RGBA_Font_Glyph_Out *fgo = fg->glyph_out;
455 int x, y, w, h, x1, x2, y1, y2, i, *iptr;
456 DATA32 *dst = dst_image->image.data;
457 DATA32 coltab[16], col;
458 DATA16 mtab[16], v;
459
460 if (!dst) return;
461
462 // FIXME: Use dw, dh for scaling glyphs...
463 (void) dw;
464 (void) dh;
465 x = dx;
466 y = dy;
467 w = fgo->bitmap.width; h = fgo->bitmap.rows;
468 // skip if totally clipped out
469 if ((y >= (cy + ch)) || ((y + h) <= cy) ||
470 (x >= (cx + cw)) || ((x + w) <= cx)) return;
471 // figure y1/y2 limit range
472 y1 = 0; y2 = h;
473 if ((y + y1) < cy) y1 = cy - y;
474 if ((y + y2) > (cy + ch)) y2 = cy + ch - y;
475 // figure x1/x2 limit range
476 x1 = 0; x2 = w;
477 if ((x + x1) < cx) x1 = cx - x;
478 if ((x + x2) > (cx + cw)) x2 = cx + cw - x;
479 col = dc->col.col;
480 if (dst_image->cache_entry.space == EVAS_COLORSPACE_GRY8)
481 {
482 // FIXME: Font draw not optimized for Alpha targets! SLOW!
483 // This is not pretty :)
484
485 DATA8 *src8, *dst8;
486 Draw_Func_Alpha func;
487 int row;
488
489 if (EINA_UNLIKELY(x < 0))
490 {
491 x1 += (-x);
492 x = 0;
493 if ((x2 - x1) <= 0) return;
494 }
495 if (EINA_UNLIKELY(y < 0))
496 {
497 y1 += (-y);
498 y = 0;
499 if ((y2 - y1) <= 0) return;
500 }
501
502 dst8 = dst_image->image.data8 + x + (y * dst_pitch);
503 func = efl_draw_alpha_func_get(dc->render_op, EINA_FALSE);
504 src8 = evas_common_font_glyph_uncompress(fg, NULL, NULL);
505 if (!src8) return;
506
507 for (row = y1; row < y2; row++)
508 {
509 DATA8 *d = dst8 + ((row - y1) * dst_pitch);
510 DATA8 *s = src8 + (row * w) + x1;
511 func(d, s, x2 - x1);
512 }
513 free(src8);
514 }
515 else if (dc->clip.mask)
516 {
517 RGBA_Gfx_Func func;
518 DATA8 *src8, *mask;
519 DATA32 *buf, *ptr, *buf_ptr;
520 RGBA_Image *im = dc->clip.mask;
521 int row;
522
523 buf = alloca(sizeof(DATA32) * w * h);
524
525 // Adjust clipping info
526 if (EINA_UNLIKELY((x + x1) < dc->clip.mask_x))
527 x1 = dc->clip.mask_x - x;
528 if (EINA_UNLIKELY((y + y1) < dc->clip.mask_y))
529 y1 = dc->clip.mask_y - y;
530 if (EINA_UNLIKELY((x + x2) > (int)(x + x1 + im->cache_entry.w)))
531 x2 = x1 + im->cache_entry.w;
532 if (EINA_UNLIKELY((y + y2) > (int)(y + y1 + im->cache_entry.h)))
533 y2 = y1 + im->cache_entry.h;
534
535 // Step 1: alpha glyph drawing
536 src8 = evas_common_font_glyph_uncompress(fg, NULL, NULL);
537 if (!src8) return;
538
539 // Step 2: color blending to buffer
540 func = evas_common_gfx_func_composite_mask_color_span_get(col, dst_image->cache_entry.flags.alpha, 1, EVAS_RENDER_COPY);
541 for (row = y1; row < y2; row++)
542 {
543 buf_ptr = buf + (row * w) + x1;
544 DATA8 *s = src8 + (row * w) + x1;
545 func(NULL, s, col, buf_ptr, x2 - x1);
546 }
547 free(src8);
548
549 // Step 3: masking to destination
550 func = evas_common_gfx_func_composite_pixel_mask_span_get(im->cache_entry.flags.alpha, im->cache_entry.flags.alpha_sparse, dst_image->cache_entry.flags.alpha, dst_pitch, dc->render_op);
551 for (row = y1; row < y2; row++)
552 {
553 mask = im->image.data8
554 + (y + row - dc->clip.mask_y) * im->cache_entry.w
555 + (x + x1 - dc->clip.mask_x);
556
557 ptr = dst + (x + x1) + ((y + row) * dst_pitch);
558 buf_ptr = buf + (row * w) + x1;
559 func(buf_ptr, mask, 0, ptr, x2 - x1);
560 }
561 }
562 else
563 {
564 // build fast multiply + mask color tables to avoid compute. this works
565 // because of our very limited 4bit range of alpha values
566 for (i = 0; i <= 0xf; i++)
567 {
568 v = (i << 4) | i;
569 coltab[i] = MUL_SYM(v, col);
570 mtab[i] = 256 - (coltab[i] >> 24);
571 }
572 #ifdef BUILD_MMX
573 if (evas_common_cpu_has_feature(CPU_FEATURE_MMX))
574 {
575 #define MMX 1
576 #include "evas_font_compress_draw.c"
577 #undef MMX
578 }
579 else
580 #endif
581
582 #ifdef BUILD_NEON
583 if (evas_common_cpu_has_feature(CPU_FEATURE_NEON))
584 {
585 #define NEON 1
586 #include "evas_font_compress_draw.c"
587 #undef NEON
588 }
589 else
590 #endif
591
592 // Plain C
593 {
594 #include "evas_font_compress_draw.c"
595 }
596 }
597 }
598
599