vgft.c (13046B)
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 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 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 61 static VGfloat float_from_26_6(FT_Pos x) 62 { 63 return (VGfloat)x / 64.0f; 64 } 65 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 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 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 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 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 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 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 259 #define CHAR_COUNT_MAX 200 260 static VGuint glyph_indices[CHAR_COUNT_MAX]; 261 static VGfloat adjustments_x[CHAR_COUNT_MAX]; 262 static VGfloat adjustments_y[CHAR_COUNT_MAX]; 263 264 // Draws the first char_count characters from text, with adjustments, starting 265 // from the current origin. The peek argument indicates whether to peek ahead 266 // and get a final adjustment based on the next character past char_count, or 267 // else just assume that this is the end of the text and add no final 268 // adjustment. 269 270 static void draw_chars(VGFT_FONT_T *font, const char *text, int char_count, VGbitfield paint_modes, int peek) { 271 // Put in first character 272 glyph_indices[0] = FT_Get_Char_Index(font->ft_face, text[0]); 273 int prev_glyph_index = glyph_indices[0]; 274 275 // Calculate glyph_indices and adjustments 276 int i; 277 FT_Vector kern; 278 for (i = 1; i != char_count; ++i) { 279 int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); 280 if (!glyph_index) { return; } 281 glyph_indices[i] = glyph_index; 282 283 if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0); 284 adjustments_x[i - 1] = float_from_26_6(kern.x); 285 adjustments_y[i - 1] = float_from_26_6(kern.y); 286 287 prev_glyph_index = glyph_index; 288 } 289 290 // Get the last adjustment? 291 if (peek) { 292 int peek_glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); 293 if (!peek_glyph_index) { return; } 294 if (FT_Get_Kerning(font->ft_face, prev_glyph_index, peek_glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0); 295 adjustments_x[char_count - 1] = float_from_26_6(kern.x); 296 adjustments_y[char_count - 1] = float_from_26_6(kern.y); 297 } else { 298 adjustments_x[char_count - 1] = 0.0f; 299 adjustments_y[char_count - 1] = 0.0f; 300 } 301 302 vgDrawGlyphs(font->vg_font, char_count, glyph_indices, adjustments_x, adjustments_y, paint_modes, VG_FALSE); 303 } 304 305 // Goes to the x,y position and draws arbitrary number of characters, draws 306 // iteratively if the char_count exceeds the max buffer size given above. 307 308 static void draw_line(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, int char_count, VGbitfield paint_modes) { 309 if (char_count == 0) return; 310 311 // Set origin to requested x,y 312 VGfloat glor[] = { x, y }; 313 vgSetfv(VG_GLYPH_ORIGIN, 2, glor); 314 315 // Draw the characters in blocks to reuse buffer memory 316 const char *curr_text = text; 317 int chars_left = char_count; 318 while (chars_left > CHAR_COUNT_MAX) { 319 draw_chars(font, curr_text, CHAR_COUNT_MAX, paint_modes, 1); 320 chars_left -= CHAR_COUNT_MAX; 321 curr_text += CHAR_COUNT_MAX; 322 } 323 324 // Draw the last block 325 draw_chars(font, curr_text, chars_left, paint_modes, 0); 326 } 327 328 void vgft_font_draw(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, unsigned text_length, VGbitfield paint_modes) 329 { 330 VGfloat descent = float_from_26_6(font->ft_face->size->metrics.descender); 331 int last_draw = 0; 332 int i = 0; 333 y -= descent; 334 for (;;) { 335 int last = !text[i] || (text_length && i==text_length); 336 337 if ((text[i] == '\n') || last) 338 { 339 draw_line(font, x, y, text + last_draw, i - last_draw, paint_modes); 340 last_draw = i+1; 341 y -= float_from_26_6(font->ft_face->size->metrics.height); 342 } 343 if (last) 344 { 345 break; 346 } 347 ++i; 348 } 349 } 350 351 // Get text extents for a single line 352 353 static void line_extents(VGFT_FONT_T *font, VGfloat *x, VGfloat *y, const char *text, int chars_count) 354 { 355 int i; 356 int prev_glyph_index = 0; 357 if (chars_count == 0) return; 358 359 for (i=0; i < chars_count; i++) 360 { 361 int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); 362 if (!glyph_index) return; 363 364 if (i != 0) 365 { 366 FT_Vector kern; 367 if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, 368 FT_KERNING_DEFAULT, &kern)) 369 { 370 assert(0); 371 } 372 *x += float_from_26_6(kern.x); 373 *y += float_from_26_6(kern.y); 374 } 375 FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT); 376 *x += float_from_26_6(font->ft_face->glyph->advance.x); 377 378 prev_glyph_index = glyph_index; 379 } 380 } 381 382 // Text extents for some ASCII text. 383 // 384 // Use text_length if non-zero, otherwise look for trailing '\0'. 385 386 void vgft_get_text_extents(VGFT_FONT_T *font, 387 const char *text, 388 unsigned text_length, 389 VGfloat unused0, VGfloat unused1, 390 VGfloat *w, VGfloat *h) { 391 int last_draw = 0; 392 VGfloat max_x = 0; 393 VGfloat y = 0; 394 395 int i, last; 396 for (i = 0, last = 0; !last; ++i) { 397 last = !text[i] || (text_length && i==text_length); 398 if ((text[i] == '\n') || last) { 399 VGfloat x = 0; 400 line_extents(font, &x, &y, text + last_draw, i - last_draw); 401 last_draw = i + 1; 402 y -= float_from_26_6(font->ft_face->size->metrics.height); 403 if (x > max_x) max_x = x; 404 } 405 } 406 *w = max_x; 407 *h = -y; 408 } 409 410 // Get y offset for first line; mitigates issue of start y being middle of block 411 // for multiline renders by vgft_font_draw. Currently simple, may be worth 412 // adding y kerning? 413 414 VGfloat vgft_first_line_y_offset(VGFT_FONT_T *font) { 415 return float_from_26_6(font->ft_face->size->metrics.height); 416 }