1 /*
2 Copyright (c) 2012, Broadcom Europe Ltd
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright
10       notice, this list of conditions and the following disclaimer in the
11       documentation and/or other materials provided with the distribution.
12     * Neither the name of the copyright holder nor the
13       names of its contributors may be used to endorse or promote products
14       derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
28 // Font handling for graphicsx
29 
30 #include <assert.h>
31 #include <stdlib.h>
32 
33 #include "graphics_x_private.h"
34 #include "vgft.h"
35 
36 static FT_Library lib;
37 
vgft_init(void)38 int vgft_init(void)
39 {
40    if (FT_Init_FreeType(&lib) == 0)
41       return 0;
42    else
43    {
44       return -1;
45    }
46 }
47 
vgft_term(void)48 void vgft_term(void)
49 {
50    FT_Done_FreeType(lib);
51 }
52 
53 #define SEGMENTS_COUNT_MAX 256
54 #define COORDS_COUNT_MAX 1024
55 
56 static VGuint segments_count;
57 static VGubyte segments[SEGMENTS_COUNT_MAX];
58 static VGuint coords_count;
59 static VGfloat coords[COORDS_COUNT_MAX];
60 
float_from_26_6(FT_Pos x)61 static VGfloat float_from_26_6(FT_Pos x)
62 {
63    return (VGfloat)x / 64.0f;
64 }
65 
convert_contour(const FT_Vector * points,const char * tags,short points_count)66 static void convert_contour(const FT_Vector *points, const char *tags, short points_count)
67 {
68    int first_coords = coords_count;
69 
70    int first = 1;
71    char last_tag = 0;
72    int c = 0;
73 
74    for (; points_count != 0; ++points, ++tags, --points_count) {
75       ++c;
76 
77       char tag = *tags;
78       if (first) {
79          assert(tag & 0x1);
80          assert(c==1); c=0;
81          segments[segments_count++] = VG_MOVE_TO;
82          first = 0;
83       } else if (tag & 0x1) {
84          /* on curve */
85 
86          if (last_tag & 0x1) {
87             /* last point was also on -- line */
88             assert(c==1); c=0;
89             segments[segments_count++] = VG_LINE_TO;
90          } else {
91             /* last point was off -- quad or cubic */
92             if (last_tag & 0x2) {
93                /* cubic */
94                assert(c==3); c=0;
95                segments[segments_count++] = VG_CUBIC_TO;
96             } else {
97                /* quad */
98                assert(c==2); c=0;
99                segments[segments_count++] = VG_QUAD_TO;
100             }
101          }
102       } else {
103          /* off curve */
104 
105          if (tag & 0x2) {
106             /* cubic */
107 
108             assert((last_tag & 0x1) || (last_tag & 0x2)); /* last either on or off and cubic */
109          } else {
110             /* quad */
111 
112             if (!(last_tag & 0x1)) {
113                /* last was also off curve */
114 
115                assert(!(last_tag & 0x2)); /* must be quad */
116 
117                /* add on point half-way between */
118                assert(c==2); c=1;
119                segments[segments_count++] = VG_QUAD_TO;
120                VGfloat x = (coords[coords_count - 2] + float_from_26_6(points->x)) * 0.5f;
121                VGfloat y = (coords[coords_count - 1] + float_from_26_6(points->y)) * 0.5f;
122                coords[coords_count++] = x;
123                coords[coords_count++] = y;
124             }
125          }
126       }
127       last_tag = tag;
128 
129       coords[coords_count++] = float_from_26_6(points->x);
130       coords[coords_count++] = float_from_26_6(points->y);
131    }
132 
133    if (last_tag & 0x1) {
134       /* last point was also on -- line (implicit with close path) */
135       assert(c==0);
136    } else {
137       ++c;
138 
139       /* last point was off -- quad or cubic */
140       if (last_tag & 0x2) {
141          /* cubic */
142          assert(c==3); c=0;
143          segments[segments_count++] = VG_CUBIC_TO;
144       } else {
145          /* quad */
146          assert(c==2); c=0;
147          segments[segments_count++] = VG_QUAD_TO;
148       }
149 
150       coords[coords_count++] = coords[first_coords + 0];
151       coords[coords_count++] = coords[first_coords + 1];
152    }
153 
154    segments[segments_count++] = VG_CLOSE_PATH;
155 }
156 
convert_outline(const FT_Vector * points,const char * tags,const short * contours,short contours_count,short points_count)157 static void convert_outline(const FT_Vector *points, const char *tags, const short *contours, short contours_count, short points_count)
158 {
159    segments_count = 0;
160    coords_count = 0;
161 
162    short last_contour = 0;
163    for (; contours_count != 0; ++contours, --contours_count) {
164       short contour = *contours + 1;
165       convert_contour(points + last_contour, tags + last_contour, contour - last_contour);
166       last_contour = contour;
167    }
168    assert(last_contour == points_count);
169 
170    assert(segments_count <= SEGMENTS_COUNT_MAX); /* oops... we overwrote some memory */
171    assert(coords_count <= COORDS_COUNT_MAX);
172 }
173 
vgft_font_init(VGFT_FONT_T * font)174 VCOS_STATUS_T vgft_font_init(VGFT_FONT_T *font)
175 {
176    font->ft_face = NULL;
177    font->vg_font = vgCreateFont(0);
178    if (font->vg_font == VG_INVALID_HANDLE)
179    {
180       return VCOS_ENOMEM;
181    }
182    return VCOS_SUCCESS;
183 }
184 
vgft_font_load_mem(VGFT_FONT_T * font,void * mem,size_t len)185 VCOS_STATUS_T vgft_font_load_mem(VGFT_FONT_T *font, void *mem, size_t len)
186 {
187    if (FT_New_Memory_Face(lib, mem, len, 0, &font->ft_face))
188    {
189       return VCOS_EINVAL;
190    }
191    return VCOS_SUCCESS;
192 }
193 
vgft_font_load_file(VGFT_FONT_T * font,const char * file)194 VCOS_STATUS_T vgft_font_load_file(VGFT_FONT_T *font, const char *file)
195 {
196    if (FT_New_Face(lib, file, 0, &font->ft_face)) {
197       return VCOS_EINVAL;
198    }
199    return VCOS_SUCCESS;
200 }
201 
vgft_font_convert_glyphs(VGFT_FONT_T * font,unsigned int char_height,unsigned int dpi_x,unsigned int dpi_y)202 VCOS_STATUS_T vgft_font_convert_glyphs(VGFT_FONT_T *font, unsigned int char_height, unsigned int dpi_x, unsigned int dpi_y)
203 {
204    FT_UInt glyph_index;
205    FT_ULong ch;
206 
207    if (FT_Set_Char_Size(font->ft_face, 0, char_height, dpi_x, dpi_y))
208    {
209       FT_Done_Face(font->ft_face);
210       vgDestroyFont(font->vg_font);
211       return VCOS_EINVAL;
212    }
213 
214    ch = FT_Get_First_Char(font->ft_face, &glyph_index);
215 
216    while (ch != 0)
217    {
218       if (FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT)) {
219          FT_Done_Face(font->ft_face);
220          vgDestroyFont(font->vg_font);
221          return VCOS_ENOMEM;
222       }
223 
224       VGPath vg_path;
225       FT_Outline *outline = &font->ft_face->glyph->outline;
226       if (outline->n_contours != 0) {
227          vg_path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
228          assert(vg_path != VG_INVALID_HANDLE);
229 
230          convert_outline(outline->points, outline->tags, outline->contours, outline->n_contours, outline->n_points);
231          vgAppendPathData(vg_path, segments_count, segments, coords);
232       } else {
233          vg_path = VG_INVALID_HANDLE;
234       }
235 
236       VGfloat origin[] = { 0.0f, 0.0f };
237       VGfloat escapement[] = { float_from_26_6(font->ft_face->glyph->advance.x), float_from_26_6(font->ft_face->glyph->advance.y) };
238       vgSetGlyphToPath(font->vg_font, glyph_index, vg_path, VG_FALSE, origin, escapement);
239 
240       if (vg_path != VG_INVALID_HANDLE) {
241          vgDestroyPath(vg_path);
242       }
243       ch = FT_Get_Next_Char(font->ft_face, ch, &glyph_index);
244    }
245 
246    return VCOS_SUCCESS;
247 }
248 
vgft_font_term(VGFT_FONT_T * font)249 void vgft_font_term(VGFT_FONT_T *font)
250 {
251    if (font->ft_face)
252       FT_Done_Face(font->ft_face);
253    if (font->vg_font)
254       vgDestroyFont(font->vg_font);
255    memset(font, 0, sizeof(*font));
256 }
257 
258 #define CHAR_COUNT_MAX 200
259 static VGuint glyph_indices[CHAR_COUNT_MAX];
260 static VGfloat adjustments_x[CHAR_COUNT_MAX];
261 static VGfloat adjustments_y[CHAR_COUNT_MAX];
262 
263 // Draws the first char_count characters from text, with adjustments, starting
264 // from the current origin.  The peek argument indicates whether to peek ahead
265 // and get a final adjustment based on the next character past char_count, or
266 // else just assume that this is the end of the text and add no final
267 // adjustment.
268 //
269 // Returns silently in some error cases.  Assert fails in some error cases.
270 
draw_chars(VGFT_FONT_T * font,const char * text,int char_count,VGbitfield paint_modes,int peek)271 static void draw_chars(VGFT_FONT_T *font, const char *text, int char_count, VGbitfield paint_modes, int peek) {
272    // Put in first character
273    glyph_indices[0] = FT_Get_Char_Index(font->ft_face, text[0]);
274    int prev_glyph_index = glyph_indices[0];
275 
276    // Calculate glyph_indices and adjustments
277    int i;
278    FT_Vector kern;
279    for (i = 1; i != char_count; ++i) {
280       int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]);
281       if (!glyph_index) { return; }
282       glyph_indices[i] = glyph_index;
283 
284       if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0);
285       adjustments_x[i - 1] = float_from_26_6(kern.x);
286       adjustments_y[i - 1] = float_from_26_6(kern.y);
287 
288       prev_glyph_index = glyph_index;
289    }
290 
291    // Get the last adjustment?
292    if (peek) {
293       int peek_glyph_index = FT_Get_Char_Index(font->ft_face, text[i]);
294       if (!peek_glyph_index) { return; }
295       if (FT_Get_Kerning(font->ft_face, prev_glyph_index, peek_glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0);
296       adjustments_x[char_count - 1] = float_from_26_6(kern.x);
297       adjustments_y[char_count - 1] = float_from_26_6(kern.y);
298    } else {
299       adjustments_x[char_count - 1] = 0.0f;
300       adjustments_y[char_count - 1] = 0.0f;
301    }
302 
303    vgDrawGlyphs(font->vg_font, char_count, glyph_indices, adjustments_x, adjustments_y, paint_modes, VG_FALSE);
304 }
305 
306 // Goes to the x,y position and draws arbitrary number of characters, draws
307 // iteratively if the char_count exceeds the max buffer size given above.
308 
draw_line(VGFT_FONT_T * font,VGfloat x,VGfloat y,const char * text,int char_count,VGbitfield paint_modes)309 static void draw_line(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, int char_count, VGbitfield paint_modes) {
310    if (char_count == 0) return;
311 
312    // Set origin to requested x,y
313    VGfloat glor[] = { x, y };
314    vgSetfv(VG_GLYPH_ORIGIN, 2, glor);
315 
316    // Draw the characters in blocks to reuse buffer memory
317    const char *curr_text = text;
318    int chars_left = char_count;
319    while (chars_left > CHAR_COUNT_MAX) {
320       draw_chars(font, curr_text, CHAR_COUNT_MAX, paint_modes, 1);
321       chars_left -= CHAR_COUNT_MAX;
322       curr_text += CHAR_COUNT_MAX;
323    }
324 
325    // Draw the last block
326    draw_chars(font, curr_text, chars_left, paint_modes, 0);
327 }
328 
329 // Draw multiple lines of text, starting from the given x and y.  The x and y
330 // correspond to the lower left corner of the first line of text, without
331 // descenders.  Unfortunately, for multiline text, this ends up in the middle of
332 // the y-extent of the block.
333 
vgft_font_draw(VGFT_FONT_T * font,VGfloat x,VGfloat y,const char * text,unsigned text_length,VGbitfield paint_modes)334 void vgft_font_draw(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, unsigned text_length, VGbitfield paint_modes)
335 {
336    VGfloat descent = float_from_26_6(font->ft_face->size->metrics.descender);
337    int last_draw = 0;
338    int i = 0;
339    y -= descent;
340    for (;;) {
341       int last = !text[i] || (text_length && i==text_length);
342 
343       if ((text[i] == '\n') || last)
344       {
345          draw_line(font, x, y, text + last_draw, i - last_draw, paint_modes);
346          last_draw = i+1;
347          y -= float_from_26_6(font->ft_face->size->metrics.height);
348       }
349       if (last)
350       {
351          break;
352       }
353       ++i;
354    }
355 }
356 
357 // Get text extents for a single line.  Returns silently in some error cases.
358 // Assert fails in some error cases.
359 
line_extents(VGFT_FONT_T * font,VGfloat * x,VGfloat * y,const char * text,int chars_count)360 static void line_extents(VGFT_FONT_T *font, VGfloat *x, VGfloat *y, const char *text, int chars_count)
361 {
362    int i;
363    int prev_glyph_index = 0;
364    if (chars_count == 0) return;
365 
366    for (i=0; i < chars_count; i++)
367    {
368       int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]);
369       if (!glyph_index) return;
370 
371       if (i != 0)
372       {
373          FT_Vector kern;
374          if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index,
375                             FT_KERNING_DEFAULT, &kern))
376          {
377             assert(0);
378          }
379          *x += float_from_26_6(kern.x);
380          *y += float_from_26_6(kern.y);
381       }
382       FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT);
383       *x += float_from_26_6(font->ft_face->glyph->advance.x);
384 
385       prev_glyph_index = glyph_index;
386    }
387 }
388 
389 // Text extents for some ASCII text.
390 //
391 // Use text_length if non-zero, otherwise look for trailing '\0'.
392 
vgft_get_text_extents(VGFT_FONT_T * font,const char * text,unsigned text_length,VGfloat unused0,VGfloat unused1,VGfloat * w,VGfloat * h)393 void vgft_get_text_extents(VGFT_FONT_T *font,
394                            const char *text,
395                            unsigned text_length,
396                            VGfloat unused0, VGfloat unused1,
397                            VGfloat *w, VGfloat *h) {
398    int last_draw = 0;
399    VGfloat max_x = 0;
400    VGfloat y = 0;
401 
402    int i, last;
403    for (i = 0, last = 0; !last; ++i) {
404       last = !text[i] || (text_length && i==text_length);
405       if ((text[i] == '\n') || last) {
406          VGfloat x = 0;
407          line_extents(font, &x, &y, text + last_draw, i - last_draw);
408          last_draw = i + 1;
409          y -= float_from_26_6(font->ft_face->size->metrics.height);
410          if (x > max_x) max_x = x;
411       }
412    }
413    *w = max_x;
414    *h = -y;
415 }
416 
417 // Get y offset for first line; mitigates issue of start y being middle of block
418 // for multiline renders by vgft_font_draw.  Currently simple, may be worth
419 // adding y kerning?
420 
vgft_first_line_y_offset(VGFT_FONT_T * font)421 VGfloat vgft_first_line_y_offset(VGFT_FONT_T *font) {
422    return float_from_26_6(font->ft_face->size->metrics.height);
423 }
424