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