Demos (master): egl/openvg: Add text demo.

Chia-I Wu olv at kemper.freedesktop.org
Mon Nov 29 10:41:23 UTC 2010


Module: Demos
Branch: master
Commit: 97d048fa913eebf5d5493fd1d7c5fb3d207cc5e2
URL:    http://cgit.freedesktop.org/mesa/demos/commit/?id=97d048fa913eebf5d5493fd1d7c5fb3d207cc5e2

Author: Chia-I Wu <olvaffe at gmail.com>
Date:   Mon Nov 29 18:36:28 2010 -0600

egl/openvg: Add text demo.

This demo loads font files using freetype.  OpenVG text support will be
used if available, but it is not required.

---

 configure.ac               |    3 +
 src/egl/openvg/Makefile.am |    9 +
 src/egl/openvg/text.c      |  431 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 443 insertions(+), 0 deletions(-)

diff --git a/configure.ac b/configure.ac
index 282b365..d6753a9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -155,6 +155,8 @@ fi
 DEMO_CFLAGS="$DEMO_CFLAGS $CWARNFLAGS"
 
 PKG_CHECK_MODULES(X11, [x11 xext], [x11_enabled=yes], [enable_x11=no])
+PKG_CHECK_MODULES(FREETYPE2, [freetype2],
+		  [freetype2_enabled=yes], [freetype2_enabled=no])
 
 mesa_source_enabled=no
 AC_ARG_WITH([mesa-source],
@@ -206,6 +208,7 @@ AM_CONDITIONAL(HAVE_GLESV2, test "x$glesv2_enabled" = "xyes")
 AM_CONDITIONAL(HAVE_VG, test "x$vg_enabled" = "xyes")
 AM_CONDITIONAL(HAVE_GLUT, test "x$glut_enabled" = "xyes")
 AM_CONDITIONAL(HAVE_X11, test "x$x11_enabled" = "xyes")
+AM_CONDITIONAL(HAVE_FREETYPE2, test "x$freetype2_enabled" = "xyes")
 AM_CONDITIONAL(HAVE_OSMESA, test "x$osmesa_enabled" = "xyes")
 AM_CONDITIONAL(BUILD_GLTRACE, false)
 AM_CONDITIONAL(HAVE_MESA_SOURCE, test "x$mesa_source_enabled" = "xyes")
diff --git a/src/egl/openvg/Makefile.am b/src/egl/openvg/Makefile.am
index a13192c..7318a43 100644
--- a/src/egl/openvg/Makefile.am
+++ b/src/egl/openvg/Makefile.am
@@ -40,6 +40,11 @@ if HAVE_X11
 EGL_X11_DEMOS = \
 	lion_x11 \
 	sp_x11
+
+if HAVE_FREETYPE2
+EGL_X11_DEMOS += \
+	text
+endif
 endif
 
 if HAVE_EGL
@@ -63,5 +68,9 @@ sp_screen_LDADD = ../eglut/libeglut_screen.la
 lion_x11_LDADD = ../eglut/libeglut_x11.la
 sp_x11_LDADD = ../eglut/libeglut_x11.la
 
+text_SOURCES = text.c
+text_CFLAGS = $(AM_CFLAGS) @FREETYPE2_CFLAGS@
+text_LDADD = @FREETYPE2_LIBS@ ../eglut/libeglut_x11.la
+
 SUBDIRS = \
 	trivial
diff --git a/src/egl/openvg/text.c b/src/egl/openvg/text.c
new file mode 100644
index 0000000..492581c
--- /dev/null
+++ b/src/egl/openvg/text.c
@@ -0,0 +1,431 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <VG/openvg.h>
+
+#include "eglut.h"
+
+#define MAX_GLYPHS 256
+static struct glyph {
+   VGboolean is_path;
+   VGHandle handle;
+   VGfloat origin[2];
+   VGfloat escapement[2];
+} glyph_string[MAX_GLYPHS];
+
+static int glyph_string_len;
+static VGHandle glyph_string_font = VG_INVALID_HANDLE;
+
+static VGint width, height;
+
+static VGint
+glyph_string_add_path(VGPath path, const VGfloat origin[2], VGfloat escapement[2])
+{
+   struct glyph *g;
+
+   if (glyph_string_len >= MAX_GLYPHS)
+      return -1;
+
+#ifdef OPENVG_VERSION_1_1
+   if (glyph_string_font != VG_INVALID_HANDLE) {
+      vgSetGlyphToPath(glyph_string_font, glyph_string_len,
+            path, VG_TRUE, origin, escapement);
+      return glyph_string_len++;
+   }
+#endif
+
+   g = &glyph_string[glyph_string_len];
+   g->is_path = VG_TRUE;
+   g->handle = (VGHandle) path;
+   g->origin[0] = origin[0];
+   g->origin[1] = origin[1];
+   g->escapement[0] = escapement[0];
+   g->escapement[1] = escapement[1];
+
+   return glyph_string_len++;
+}
+
+static VGint
+glyph_string_add_image(VGImage image, const VGfloat origin[2], VGfloat escapement[2])
+{
+   struct glyph *g;
+
+   if (glyph_string_len >= MAX_GLYPHS)
+      return -1;
+
+#ifdef OPENVG_VERSION_1_1
+   if (glyph_string_font != VG_INVALID_HANDLE) {
+      vgSetGlyphToImage(glyph_string_font, glyph_string_len,
+            image, origin, escapement);
+      return glyph_string_len++;
+   }
+#endif
+
+   g = &glyph_string[glyph_string_len];
+   g->is_path = VG_FALSE;
+   g->handle = (VGHandle) image;
+   g->origin[0] = origin[0];
+   g->origin[1] = origin[1];
+   g->escapement[0] = escapement[0];
+   g->escapement[1] = escapement[1];
+
+   return glyph_string_len++;
+}
+
+static void
+glyph_string_draw(VGfloat x, VGfloat y)
+{
+   VGfloat pen[2];
+   int i;
+
+#ifdef OPENVG_VERSION_1_1
+   if (glyph_string_font != VG_INVALID_HANDLE) {
+      VGuint indices[MAX_GLYPHS];
+
+      for (i = 0; i < glyph_string_len; i++)
+         indices[i] = i;
+
+      pen[0] = x;
+      pen[1] = y;
+      vgSetfv(VG_GLYPH_ORIGIN, 2, pen);
+      vgDrawGlyphs(glyph_string_font, glyph_string_len, indices,
+            NULL, NULL, VG_FILL_PATH, VG_TRUE);
+
+      return;
+   }
+#endif
+
+   pen[0] = (VGint) (x + 0.5f);
+   pen[1] = (VGint) (y + 0.5f);
+
+   for (i = 0; i < glyph_string_len; i++) {
+      const struct glyph *g = &glyph_string[i];
+
+      if (g->handle == VG_INVALID_HANDLE)
+         continue;
+
+      vgSeti(VG_MATRIX_MODE, (glyph_string[i].is_path) ?
+            VG_MATRIX_PATH_USER_TO_SURFACE : VG_MATRIX_IMAGE_USER_TO_SURFACE);
+      vgLoadIdentity();
+      vgTranslate(pen[0] - (VGint) g->origin[0], pen[1] - (VGint) g->origin[1]);
+
+      if (glyph_string[i].is_path)
+         vgDrawPath((VGPath) glyph_string[i].handle, VG_FILL_PATH);
+      else
+         vgDrawImage((VGImage) glyph_string[i].handle);
+
+      pen[0] += (VGint) (g->escapement[0] + 0.5f);
+      pen[1] += (VGint) (g->escapement[1] + 0.5f);
+   }
+}
+
+static int
+path_append(VGPath path, VGubyte segment, const FT_Vector **vectors)
+{
+   VGfloat coords[6];
+   int i, num_vectors;
+
+   switch (segment) {
+   case VG_MOVE_TO:
+   case VG_LINE_TO:
+      num_vectors = 1;
+      break;
+   case VG_QUAD_TO:
+      num_vectors = 2;
+      break;
+   case VG_CUBIC_TO:
+      num_vectors = 3;
+      break;
+   default:
+      return -1;
+      break;
+   }
+
+   for (i = 0; i < num_vectors; i++) {
+      coords[2 * i + 0] = (float) vectors[i]->x / 64.0f;
+      coords[2 * i + 1] = (float) vectors[i]->y / 64.0f;
+   }
+
+   vgAppendPathData(path, 1, &segment, (const void *) coords);
+
+   return 0;
+}
+
+static int
+decompose_move_to(const FT_Vector *to, void *user)
+{
+   VGPath path = (VGPath) (long) user;
+
+   return path_append(path, VG_MOVE_TO, &to);
+}
+
+static int
+decompose_line_to(const FT_Vector *to, void *user)
+{
+   VGPath path = (VGPath) (long) user;
+
+   return path_append(path, VG_LINE_TO, &to);
+}
+
+static int
+decompose_conic_to(const FT_Vector *control, const FT_Vector *to, void *user)
+{
+   VGPath path = (VGPath) (long) user;
+   const FT_Vector *vectors[2] = { control, to };
+
+   return path_append(path, VG_QUAD_TO, vectors);
+}
+
+static int
+decompose_cubic_to(const FT_Vector *control1, const FT_Vector *control2,
+                   const FT_Vector *to, void *user)
+{
+   VGPath path = (VGPath) (long) user;
+   const FT_Vector *vectors[3] = { control1, control2, to };
+
+   return path_append(path, VG_CUBIC_TO, vectors);
+}
+
+static VGHandle
+convert_outline_glyph(FT_GlyphSlot glyph)
+{
+   FT_Outline_Funcs funcs = {
+      decompose_move_to,
+      decompose_line_to,
+      decompose_conic_to,
+      decompose_cubic_to,
+      0, 0
+   };
+   VGPath path;
+
+   path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+         VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, glyph->outline.n_points,
+         VG_PATH_CAPABILITY_ALL);
+
+   if (FT_Outline_Decompose(&glyph->outline, &funcs, (void *) (long) path)) {
+      vgDestroyPath(path);
+      path = VG_INVALID_HANDLE;
+   }
+
+   return (VGHandle) path;
+}
+
+static VGHandle
+convert_bitmap_glyph(FT_GlyphSlot glyph)
+{
+   VGImage image;
+   VGint width, height, stride;
+   unsigned char *data;
+   int i, j;
+
+   switch (glyph->bitmap.pixel_mode) {
+   case FT_PIXEL_MODE_MONO:
+   case FT_PIXEL_MODE_GRAY:
+      break;
+   default:
+      return VG_INVALID_HANDLE;
+      break;
+   }
+
+   data = glyph->bitmap.buffer;
+   width = glyph->bitmap.width;
+   height = glyph->bitmap.rows;
+   stride = glyph->bitmap.pitch;
+
+   /* mono to gray, and flip if needed */
+   if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
+      data = malloc(width * height);
+      if (!data)
+         return VG_INVALID_HANDLE;
+
+      for (i = 0; i < height; i++) {
+         char *dst = &data[width * i];
+         const unsigned char *src;
+
+         if (stride > 0)
+            src = glyph->bitmap.buffer + stride * (height - i - 1);
+         else
+            src = glyph->bitmap.buffer - stride * i;
+
+         for (j = 0; j < width; j++) {
+            if (src[j / 8] & (1 << (7 - (j % 8))))
+               dst[j] = 0xff;
+            else
+               dst[j] = 0x0;
+         }
+      }
+      stride = -width;
+   }
+
+   image = vgCreateImage(VG_A_8, width, height,
+         VG_IMAGE_QUALITY_NONANTIALIASED);
+
+   if (stride < 0) {
+      stride = -stride;
+      vgImageSubData(image, data, stride, VG_A_8,
+            0, 0, width, height);
+   }
+   else {
+      /* flip vertically */
+      for (i = 0; i < height; i++) {
+         const char *row = data + stride * i;
+
+         vgImageSubData(image, row, stride, VG_A_8,
+               0, height - i - 1, width, 1);
+      }
+   }
+
+   if (data != glyph->bitmap.buffer)
+      free(data);
+
+   return (VGHandle) image;
+}
+
+static void
+glyph_string_init(const char *font, int size, const char *str)
+{
+   FT_Library lib = NULL;
+   FT_Face face = NULL;
+   int i;
+
+   if (FT_Init_FreeType(&lib)) {
+      printf("failed to initialize freetype\n");
+      goto fail;
+   }
+
+   if (FT_New_Face(lib, font, 0, &face)) {
+      printf("failed to load %s\n", glyph_string);
+      goto fail;
+   }
+
+   if (FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
+      printf("failed to select an unicode charmap\n");
+      goto fail;
+   }
+
+   if (FT_Set_Pixel_Sizes(face, size, size))
+      printf("failed to set pixel sizes\n");
+
+   for (i = 0; str[i]; i++) {
+      VGfloat origin[2], escapement[2];
+      VGHandle handle;
+
+      /*
+       * if a character appears more than once, it will be loaded and converted
+       * again...
+       */
+      if (FT_Load_Char(face, str[i], FT_LOAD_DEFAULT)) {
+         printf("failed to load glyph '%c'\n", str[i]);
+         goto fail;
+      }
+
+      escapement[0] = (VGfloat) face->glyph->advance.x / 64.0f;
+      escapement[1] = (VGfloat) face->glyph->advance.y / 64.0f;
+
+      switch (face->glyph->format) {
+      case FT_GLYPH_FORMAT_OUTLINE:
+         handle = convert_outline_glyph(face->glyph);
+         origin[0] = 0.0f;
+         origin[1] = 0.0f;
+         glyph_string_add_path((VGPath) handle, origin, escapement);
+         break;
+      case FT_GLYPH_FORMAT_BITMAP:
+         handle = convert_bitmap_glyph(face->glyph);
+         origin[0] = (VGfloat) (-face->glyph->bitmap_left);
+         origin[1] = (VGfloat)
+            (face->glyph->bitmap.rows - face->glyph->bitmap_top);
+         glyph_string_add_image((VGImage) handle, origin, escapement);
+         break;
+      default:
+         printf("unsupported format for glyph '%c'\n", str[i]);
+         break;
+      }
+      if (handle == VG_INVALID_HANDLE)
+         printf("failed to add glyph '%c'\n", str[i]);
+   }
+
+fail:
+   if (face)
+      FT_Done_Face(face);
+   if (lib)
+      FT_Done_FreeType(lib);
+}
+
+static void
+display(void)
+{
+   vgClear(0, 0, width, height);
+   glyph_string_draw(10.0, 10.0);
+   eglutPostRedisplay();
+}
+
+
+/* new window size or exposure */
+static void
+reshape(int w, int h)
+{
+   width  = w;
+   height = h;
+}
+
+
+static void
+init(void)
+{
+   VGPaint paint;
+   float clear_color[4] = { 0.9, 0.9, 0.9, 1.0 };
+
+   vgSetfv(VG_CLEAR_COLOR, 4, clear_color);
+   vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_STENCIL);
+
+   paint = vgCreatePaint();
+   vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+   vgSetColor(paint, 0x660000ff);
+   vgSetPaint(paint, VG_FILL_PATH);
+
+#ifdef OPENVG_VERSION_1_1
+   {
+      const char *ver = (const char *) vgGetString(VG_VERSION);
+
+      if (!strcmp(ver, "1.1"))
+         glyph_string_font = vgCreateFont(0);
+   }
+#endif
+
+   if (glyph_string_font != VG_INVALID_HANDLE)
+      printf("using OpenVG text support\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+   const char *font, *str;
+
+   if (argc < 2) {
+      printf("Usage: %s <path-to-font> [<string>]\n", argv[0]);
+      return 1;
+   }
+
+   font = argv[1];
+   str = (argc > 2) ? argv[2] : "Hello World";
+
+   eglutInitWindowSize(480, 80);
+   eglutInitAPIMask(EGLUT_OPENVG_BIT);
+   eglutInit(argc, argv);
+
+   eglutCreateWindow("Text Example");
+
+   eglutReshapeFunc(reshape);
+   eglutDisplayFunc(display);
+
+   init();
+   glyph_string_init(font, 64, str);
+
+   eglutMainLoop();
+
+   return 0;
+}




More information about the mesa-commit mailing list