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