1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5#include <ft2build.h> 6#include FT_FREETYPE_H 7 8#include <VG/openvg.h> 9 10#include "eglut.h" 11 12#define MAX_GLYPHS 256 13static struct glyph { 14 VGboolean is_path; 15 VGHandle handle; 16 VGfloat origin[2]; 17 VGfloat escapement[2]; 18} glyph_string[MAX_GLYPHS]; 19 20static int glyph_string_len; 21static VGHandle glyph_string_font = VG_INVALID_HANDLE; 22 23static VGint width, height; 24 25static VGint 26glyph_string_add_path(VGPath path, const VGfloat origin[2], VGfloat escapement[2]) 27{ 28 struct glyph *g; 29 30 if (glyph_string_len >= MAX_GLYPHS) 31 return -1; 32 33#ifdef OPENVG_VERSION_1_1 34 if (glyph_string_font != VG_INVALID_HANDLE) { 35 vgSetGlyphToPath(glyph_string_font, glyph_string_len, 36 path, VG_TRUE, origin, escapement); 37 return glyph_string_len++; 38 } 39#endif 40 41 g = &glyph_string[glyph_string_len]; 42 g->is_path = VG_TRUE; 43 g->handle = (VGHandle) path; 44 g->origin[0] = origin[0]; 45 g->origin[1] = origin[1]; 46 g->escapement[0] = escapement[0]; 47 g->escapement[1] = escapement[1]; 48 49 return glyph_string_len++; 50} 51 52static VGint 53glyph_string_add_image(VGImage image, const VGfloat origin[2], VGfloat escapement[2]) 54{ 55 struct glyph *g; 56 57 if (glyph_string_len >= MAX_GLYPHS) 58 return -1; 59 60#ifdef OPENVG_VERSION_1_1 61 if (glyph_string_font != VG_INVALID_HANDLE) { 62 vgSetGlyphToImage(glyph_string_font, glyph_string_len, 63 image, origin, escapement); 64 return glyph_string_len++; 65 } 66#endif 67 68 g = &glyph_string[glyph_string_len]; 69 g->is_path = VG_FALSE; 70 g->handle = (VGHandle) image; 71 g->origin[0] = origin[0]; 72 g->origin[1] = origin[1]; 73 g->escapement[0] = escapement[0]; 74 g->escapement[1] = escapement[1]; 75 76 return glyph_string_len++; 77} 78 79static void 80glyph_string_draw(VGfloat x, VGfloat y) 81{ 82 VGfloat pen[2]; 83 int i; 84 85#ifdef OPENVG_VERSION_1_1 86 if (glyph_string_font != VG_INVALID_HANDLE) { 87 VGuint indices[MAX_GLYPHS]; 88 89 for (i = 0; i < glyph_string_len; i++) 90 indices[i] = i; 91 92 pen[0] = x; 93 pen[1] = y; 94 vgSetfv(VG_GLYPH_ORIGIN, 2, pen); 95 vgDrawGlyphs(glyph_string_font, glyph_string_len, indices, 96 NULL, NULL, VG_FILL_PATH, VG_TRUE); 97 98 return; 99 } 100#endif 101 102 pen[0] = (VGint) (x + 0.5f); 103 pen[1] = (VGint) (y + 0.5f); 104 105 for (i = 0; i < glyph_string_len; i++) { 106 const struct glyph *g = &glyph_string[i]; 107 108 if (g->handle == VG_INVALID_HANDLE) 109 continue; 110 111 vgSeti(VG_MATRIX_MODE, (glyph_string[i].is_path) ? 112 VG_MATRIX_PATH_USER_TO_SURFACE : VG_MATRIX_IMAGE_USER_TO_SURFACE); 113 vgLoadIdentity(); 114 vgTranslate(pen[0] - (VGint) g->origin[0], pen[1] - (VGint) g->origin[1]); 115 116 if (glyph_string[i].is_path) 117 vgDrawPath((VGPath) glyph_string[i].handle, VG_FILL_PATH); 118 else 119 vgDrawImage((VGImage) glyph_string[i].handle); 120 121 pen[0] += (VGint) (g->escapement[0] + 0.5f); 122 pen[1] += (VGint) (g->escapement[1] + 0.5f); 123 } 124} 125 126static int 127path_append(VGPath path, VGubyte segment, const FT_Vector **vectors) 128{ 129 VGfloat coords[6]; 130 int i, num_vectors; 131 132 switch (segment) { 133 case VG_MOVE_TO: 134 case VG_LINE_TO: 135 num_vectors = 1; 136 break; 137 case VG_QUAD_TO: 138 num_vectors = 2; 139 break; 140 case VG_CUBIC_TO: 141 num_vectors = 3; 142 break; 143 default: 144 return -1; 145 break; 146 } 147 148 for (i = 0; i < num_vectors; i++) { 149 coords[2 * i + 0] = (float) vectors[i]->x / 64.0f; 150 coords[2 * i + 1] = (float) vectors[i]->y / 64.0f; 151 } 152 153 vgAppendPathData(path, 1, &segment, (const void *) coords); 154 155 return 0; 156} 157 158static int 159decompose_move_to(const FT_Vector *to, void *user) 160{ 161 VGPath path = (VGPath) (long) user; 162 163 return path_append(path, VG_MOVE_TO, &to); 164} 165 166static int 167decompose_line_to(const FT_Vector *to, void *user) 168{ 169 VGPath path = (VGPath) (long) user; 170 171 return path_append(path, VG_LINE_TO, &to); 172} 173 174static int 175decompose_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) 176{ 177 VGPath path = (VGPath) (long) user; 178 const FT_Vector *vectors[2] = { control, to }; 179 180 return path_append(path, VG_QUAD_TO, vectors); 181} 182 183static int 184decompose_cubic_to(const FT_Vector *control1, const FT_Vector *control2, 185 const FT_Vector *to, void *user) 186{ 187 VGPath path = (VGPath) (long) user; 188 const FT_Vector *vectors[3] = { control1, control2, to }; 189 190 return path_append(path, VG_CUBIC_TO, vectors); 191} 192 193static VGHandle 194convert_outline_glyph(FT_GlyphSlot glyph) 195{ 196 FT_Outline_Funcs funcs = { 197 decompose_move_to, 198 decompose_line_to, 199 decompose_conic_to, 200 decompose_cubic_to, 201 0, 0 202 }; 203 VGPath path; 204 205 path = vgCreatePath(VG_PATH_FORMAT_STANDARD, 206 VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, glyph->outline.n_points, 207 VG_PATH_CAPABILITY_ALL); 208 209 if (FT_Outline_Decompose(&glyph->outline, &funcs, (void *) (long) path)) { 210 vgDestroyPath(path); 211 path = VG_INVALID_HANDLE; 212 } 213 214 return (VGHandle) path; 215} 216 217static VGHandle 218convert_bitmap_glyph(FT_GlyphSlot glyph) 219{ 220 VGImage image; 221 VGint width, height, stride; 222 unsigned char *data; 223 int i, j; 224 225 switch (glyph->bitmap.pixel_mode) { 226 case FT_PIXEL_MODE_MONO: 227 case FT_PIXEL_MODE_GRAY: 228 break; 229 default: 230 return VG_INVALID_HANDLE; 231 break; 232 } 233 234 data = glyph->bitmap.buffer; 235 width = glyph->bitmap.width; 236 height = glyph->bitmap.rows; 237 stride = glyph->bitmap.pitch; 238 239 /* mono to gray, and flip if needed */ 240 if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { 241 data = malloc(width * height); 242 if (!data) 243 return VG_INVALID_HANDLE; 244 245 for (i = 0; i < height; i++) { 246 char *dst = &data[width * i]; 247 const unsigned char *src; 248 249 if (stride > 0) 250 src = glyph->bitmap.buffer + stride * (height - i - 1); 251 else 252 src = glyph->bitmap.buffer - stride * i; 253 254 for (j = 0; j < width; j++) { 255 if (src[j / 8] & (1 << (7 - (j % 8)))) 256 dst[j] = 0xff; 257 else 258 dst[j] = 0x0; 259 } 260 } 261 stride = -width; 262 } 263 264 image = vgCreateImage(VG_A_8, width, height, 265 VG_IMAGE_QUALITY_NONANTIALIASED); 266 267 if (stride < 0) { 268 stride = -stride; 269 vgImageSubData(image, data, stride, VG_A_8, 270 0, 0, width, height); 271 } 272 else { 273 /* flip vertically */ 274 for (i = 0; i < height; i++) { 275 const char *row = data + stride * i; 276 277 vgImageSubData(image, row, stride, VG_A_8, 278 0, height - i - 1, width, 1); 279 } 280 } 281 282 if (data != glyph->bitmap.buffer) 283 free(data); 284 285 return (VGHandle) image; 286} 287 288static void 289glyph_string_init(const char *font, int size, const char *str) 290{ 291 FT_Library lib = NULL; 292 FT_Face face = NULL; 293 int i; 294 295 if (FT_Init_FreeType(&lib)) { 296 printf("failed to initialize freetype\n"); 297 goto fail; 298 } 299 300 if (FT_New_Face(lib, font, 0, &face)) { 301 printf("failed to load %s\n", glyph_string); 302 goto fail; 303 } 304 305 if (FT_Select_Charmap(face, FT_ENCODING_UNICODE)) { 306 printf("failed to select an unicode charmap\n"); 307 goto fail; 308 } 309 310 if (FT_Set_Pixel_Sizes(face, size, size)) 311 printf("failed to set pixel sizes\n"); 312 313 for (i = 0; str[i]; i++) { 314 VGfloat origin[2], escapement[2]; 315 VGHandle handle; 316 317 /* 318 * if a character appears more than once, it will be loaded and converted 319 * again... 320 */ 321 if (FT_Load_Char(face, str[i], FT_LOAD_DEFAULT)) { 322 printf("failed to load glyph '%c'\n", str[i]); 323 goto fail; 324 } 325 326 escapement[0] = (VGfloat) face->glyph->advance.x / 64.0f; 327 escapement[1] = (VGfloat) face->glyph->advance.y / 64.0f; 328 329 switch (face->glyph->format) { 330 case FT_GLYPH_FORMAT_OUTLINE: 331 handle = convert_outline_glyph(face->glyph); 332 origin[0] = 0.0f; 333 origin[1] = 0.0f; 334 glyph_string_add_path((VGPath) handle, origin, escapement); 335 break; 336 case FT_GLYPH_FORMAT_BITMAP: 337 handle = convert_bitmap_glyph(face->glyph); 338 origin[0] = (VGfloat) (-face->glyph->bitmap_left); 339 origin[1] = (VGfloat) 340 (face->glyph->bitmap.rows - face->glyph->bitmap_top); 341 glyph_string_add_image((VGImage) handle, origin, escapement); 342 break; 343 default: 344 printf("unsupported format for glyph '%c'\n", str[i]); 345 break; 346 } 347 if (handle == VG_INVALID_HANDLE) 348 printf("failed to add glyph '%c'\n", str[i]); 349 } 350 351fail: 352 if (face) 353 FT_Done_Face(face); 354 if (lib) 355 FT_Done_FreeType(lib); 356} 357 358static void 359display(void) 360{ 361 vgClear(0, 0, width, height); 362 glyph_string_draw(10.0, 10.0); 363} 364 365 366/* new window size or exposure */ 367static void 368reshape(int w, int h) 369{ 370 width = w; 371 height = h; 372} 373 374 375static void 376init(void) 377{ 378 VGPaint paint; 379 float clear_color[4] = { 0.9, 0.9, 0.9, 1.0 }; 380 381 vgSetfv(VG_CLEAR_COLOR, 4, clear_color); 382 vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_STENCIL); 383 384 paint = vgCreatePaint(); 385 vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); 386 vgSetColor(paint, 0x660000ff); 387 vgSetPaint(paint, VG_FILL_PATH); 388 389#ifdef OPENVG_VERSION_1_1 390 { 391 const char *ver = (const char *) vgGetString(VG_VERSION); 392 393 if (!strcmp(ver, "1.1")) 394 glyph_string_font = vgCreateFont(0); 395 } 396#endif 397 398 if (glyph_string_font != VG_INVALID_HANDLE) 399 printf("using OpenVG text support\n"); 400} 401 402int 403main(int argc, char *argv[]) 404{ 405 const char *font, *str; 406 407 if (argc < 2) { 408 printf("Usage: %s <path-to-font> [<string>]\n", argv[0]); 409 return 1; 410 } 411 412 font = argv[1]; 413 str = (argc > 2) ? argv[2] : "Hello World"; 414 415 eglutInitWindowSize(480, 80); 416 eglutInitAPIMask(EGLUT_OPENVG_BIT); 417 eglutInit(argc, argv); 418 419 eglutCreateWindow("Text Example"); 420 421 eglutReshapeFunc(reshape); 422 eglutDisplayFunc(display); 423 424 init(); 425 glyph_string_init(font, 64, str); 426 427 eglutMainLoop(); 428 429 return 0; 430} 431