most of the glft is contained herein. but buggy :)
authorDana Jansens <danakj@orodu.net>
Wed, 28 May 2003 23:52:25 +0000 (23:52 +0000)
committerDana Jansens <danakj@orodu.net>
Wed, 28 May 2003 23:52:25 +0000 (23:52 +0000)
glft/.cvsignore
glft/Makefile.am
glft/font.c
glft/font.h
glft/glft.h
glft/init.c
glft/init.h
glft/render.c [new file with mode: 0644]
glft/render.h [new file with mode: 0644]

index fc1eb28d4bb3232f32aa3cf7576b77c7572b16bb..b9e33618fa579e9f0c7513556f5deb352381ae6e 100644 (file)
@@ -7,3 +7,4 @@ init.lo
 glfttest
 debug.lo
 font.lo
+render.lo
index e106496dc9476e97b91afaeb081760c10a5ec361..3dccc9409f3d3df0c839c00b3ab0a91468c5a98b 100644 (file)
@@ -3,12 +3,14 @@ themedir=$(datadir)/openbox/themes
 theme=operation
 
 CPPFLAGS=$(FC_CFLAGS) $(GLIB_CFLAGS) $(GL_CFLAGS) @CPPFLAGS@ \
+         $(shell freetype-config --cflags) \
          -DG_LOG_DOMAIN=\"GlFt\" \
          -DDEFAULT_THEME=\"$(theme)\" \
          -DTHEMEDIR=\"$(themedir)\"
 
 INCLUDES=-I..
-LIBS=$(FC_LIBS) $(GLIB_LIBS) $(X_LIBS) $(GL_LIBS) @LIBS@
+LIBS=$(FC_LIBS) $(GLIB_LIBS) $(X_LIBS) $(GL_LIBS) \
+     $(shell freetype-config --libs) @LIBS@
 
 noinst_PROGRAMS=glfttest
 glfttest_LDFLAGS=-lglft -L.
@@ -18,13 +20,15 @@ lib_LTLIBRARIES=libglft.la
 libglft_la_SOURCES=\
        init.c \
        font.c \
-       debug.c
+       debug.c \
+       render.c
 
 noinst_HEADERS=\
        glft.h \
        font.h \
        init.h \
-       debug.h
+       debug.h \
+       render.h
 
 MAINTAINERCLEANFILES=Makefile.in
 
index 53c8d7cf49a3854c7d5316dba647fc24e39e2a17..fcb0512c8e943412fd300a51ff0a29725a748ba3 100644 (file)
 #include "glft.h"
 #include "init.h"
 #include "debug.h"
+#include "render.h"
 #include <assert.h>
 #include <stdlib.h>
+#include <GL/glx.h>
+
+struct GHashTable *glyph_map = NULL;
+
+#define FLOOR(x)    ((x) & -64)
+#define CEIL(x)     (((x)+63) & -64)
+#define TRUNC(x)    ((x) >> 6)
+#define ROUND(x)    (((x)+32) & -64)
+
+void dest_glyph_map_value(gpointer data)
+{
+    struct GlftGlyph *g = data;
+    glDeleteLists(g->dlist, 1);    
+    free(data);
+}
 
 struct GlftFont *GlftFontOpen(const char *name)
 {
     struct GlftFont *font;
-    FcPattern *pat;
-    FcFontSet *set;
-    double alpha;
+    FcPattern *pat, *match;
+    FcResult res;
+    double alpha, psize;
+    FcBool hinting, autohint, advance;
 
     assert(init_done);
 
     pat = FcNameParse((const unsigned char*)name);
     assert(pat);
-    set = FcFontSetCreate();
-    if (!FcFontSetAdd(set, pat)) {
-        FcPatternDestroy(pat);
-        FcFontSetDestroy(set);
-        GlftDebug("failed to load font\n");
+
+    /* XXX read our extended attributes here? (if failing below..) */
+
+    FcConfigSubstitute(NULL, pat, FcMatchPattern);
+    FcDefaultSubstitute(pat);
+
+    match = FcFontMatch(NULL, pat, &res);
+    printf("Pattern ");
+    FcPatternPrint(pat);
+    printf("Match ");
+    FcPatternPrint(match);
+    FcPatternDestroy(pat);
+    if (!match) {
+        GlftDebug("failed to find matching font\n");
         return NULL;
     }
     
     font = malloc(sizeof(struct GlftFont));
-    font->set = set;
-    if (FcResultMatch != FcPatternGetBool(pat, "shadow", 0, &font->shadow))
+    font->pat = match;
+    font->ftflags = FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP;
+
+    if (FcPatternGetString(match, FC_FILE, 0, (FcChar8**) &font->filename) !=
+        FcResultMatch) {
+        GlftDebug("error getting FC_FILE from pattern\n");
+        goto openfail0;
+    }
+
+    switch (FcPatternGetInteger(match, FC_INDEX, 0, &font->index)) {
+    case FcResultNoMatch:
+        font->index = 0;
+        break;
+    case FcResultMatch:
+        break;
+    default:
+        GlftDebug("error getting FC_INDEX from pattern\n");
+        goto openfail0;
+    }
+
+    switch (FcPatternGetBool(match, FC_ANTIALIAS, 0, &font->antialias)) {
+    case FcResultNoMatch:
+        font->antialias = FcTrue;
+        break;
+    case FcResultMatch:
+        break;
+    default:
+        GlftDebug("error getting FC_ANTIALIAS from pattern\n");
+        goto openfail0;
+    }
+
+    switch (FcPatternGetBool(match, FC_HINTING, 0, &hinting)) {
+    case FcResultNoMatch:
+        hinting = FcTrue;
+        break;
+    case FcResultMatch:
+        break;
+    default:
+        GlftDebug("error getting FC_HINTING from pattern\n");
+        goto openfail0;
+    }
+    if (!hinting) font->ftflags |= FT_LOAD_NO_HINTING;
+
+    switch (FcPatternGetBool(match, FC_AUTOHINT, 0, &autohint)) {
+    case FcResultNoMatch:
+        autohint = FcFalse;
+        break;
+    case FcResultMatch:
+        break;
+    default:
+        GlftDebug("error getting FC_AUTOHINT from pattern\n");
+        goto openfail0;
+    }
+    if (autohint) font->ftflags |= FT_LOAD_FORCE_AUTOHINT;
+
+    switch (FcPatternGetBool(match, FC_GLOBAL_ADVANCE, 0, &advance)) {
+    case FcResultNoMatch:
+        advance = FcTrue;
+        break;
+    case FcResultMatch:
+        break;
+    default:
+        GlftDebug("error getting FC_GLOBAL_ADVANCE from pattern\n");
+        goto openfail0;
+    }
+    if (!advance) font->ftflags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+
+    switch (FcPatternGetInteger(match, FC_SPACING, 0, &font->spacing)) {
+    case FcResultNoMatch:
+        font->spacing = FC_PROPORTIONAL;
+        break;
+    case FcResultMatch:
+        break;
+    default:
+        GlftDebug("error getting FC_SPACING from pattern\n");
+        goto openfail0;
+    }
+
+    switch (FcPatternGetBool(match, FC_MINSPACE, 0, &font->minspace)) {
+    case FcResultNoMatch:
+        font->minspace = FcFalse;
+        break;
+    case FcResultMatch:
+        break;
+    default:
+        GlftDebug("error getting FC_MINSPACE from pattern\n");
+        goto openfail0;
+    }
+
+    switch (FcPatternGetInteger(match, FC_CHAR_WIDTH, 0, &font->char_width)) {
+    case FcResultNoMatch:
+        font->char_width = 0;
+        break;
+    case FcResultMatch:
+        if (font->char_width)
+            font->spacing = FC_MONO;
+        break;
+    default:
+        GlftDebug("error getting FC_CHAR_WIDTH from pattern\n");
+        goto openfail0;
+    }
+
+    if (FcPatternGetDouble(match, FC_PIXEL_SIZE, 0, &psize) != FcResultMatch)
+        goto openfail0;
+    font->ftcharsize = (FT_F26Dot6) psize * 64;
+
+    if (FcPatternGetBool(match, "shadow", 0, &font->shadow) != FcResultMatch)
         font->shadow = FcFalse;
-    if (FcResultMatch !=
-        FcPatternGetInteger(pat, "shadowoffset", 0, &font->shadow_offset))
+
+    if (FcPatternGetInteger(match, "shadowoffset", 0, &font->shadow_offset) !=
+        FcResultMatch)
         font->shadow_offset = 2;
-    if (FcResultMatch != FcPatternGetDouble(pat, "shadowalpha", 0, &alpha))
+
+    if (FcPatternGetDouble(match, "shadowalpha", 0, &alpha) != FcResultMatch)
         alpha = 0.5;
     font->shadow_alpha = (float)alpha;
+
+    /* time to load the font! */
+
+    if (FT_New_Face(ft_lib, font->filename, font->index, &font->face)) {
+        GlftDebug("failed to open FT face\n");
+        goto openfail0;
+    }
+    if (FT_Set_Char_Size(font->face, 0, font->ftcharsize, 0, 0)) {
+        GlftDebug("failed to set char size on FT face\n");
+        goto openfail0;
+    }
+
+    if (!FcPatternGetCharSet(match, FC_CHARSET, 0, &font->chars) !=
+        FcResultMatch)
+        font->chars = FcFreeTypeCharSet(font->face, FcConfigGetBlanks(NULL));
+    if (!font->chars) {
+        GlftDebug("failed to get a valid CharSet\n");
+        goto openfail0;
+    }
+
+    font->glyph_map = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
+                                            dest_glyph_map_value);
+
+    if (font->char_width)
+        font->max_advance_width = font->char_width; 
+    else
+        font->max_advance_width = font->face->size->metrics.max_advance >> 6;
+    font->descent = -(font->face->size->metrics.descender >> 6);
+    font->ascent = font->face->size->metrics.ascender >> 6;
+    if (font->minspace) font->height = font->ascent + font->descent;
+    else                font->height = font->face->size->metrics.height >> 6;
+
     return font;
+
+openfail0:
+    FcPatternDestroy(match);
+    free(font);
+    return NULL;
 }
 
 void GlftFontClose(struct GlftFont *font)
 {
     if (font) {
-        FcFontSetDestroy(font->set);
+        FT_Done_Face(font->face);
+        FcPatternDestroy(font->pat);
+        FcCharSetDestroy(font->chars);
+        g_hash_table_destroy(font->glyph_map);
         free(font);
     }
 }
+
+struct GlftGlyph *GlftFontGlyph(struct GlftFont *font, const char *c)
+{
+    const char *n = g_utf8_next_char(c);
+    int b = n - c;
+    struct GlftGlyph *g;
+    FcChar32 w;
+
+    FcUtf8ToUcs4((const FcChar8*) c, &w, b);
+    
+    g = g_hash_table_lookup(font->glyph_map, &w);
+    if (!g) {
+        if (FT_Load_Glyph(font->face, FcFreeTypeCharIndex(font->face, w),
+                          font->ftflags)) {
+            if (FT_Load_Glyph(font->face, 0, font->ftflags))
+                return NULL;
+            else {
+                g = g_hash_table_lookup(font->glyph_map, &w);
+            }
+        }
+    }
+    if (!g) {
+        assert(font->face->face_flags & FT_FACE_FLAG_SCALABLE);
+
+        g = malloc(sizeof(struct GlftGlyph));
+        g->w = w;
+        g->dlist = glGenLists(1);
+
+        GlftRenderGlyph(font->face, g->dlist);
+
+        if (!(font->spacing == FC_PROPORTIONAL)) {
+            g->width = font->max_advance_width;
+        } else {
+            /*g->width = TRUNC(ROUND(font->face->glyph->advance.x));*/
+            g->width = font->face->glyph->metrics.width >> 6;
+        }
+        g->height = -(font->face->glyph->metrics.height >> 6);
+
+        g_hash_table_insert(font->glyph_map, &g->w, &g);
+    }
+
+    return g;
+}
+
+void GlftMeasureString(struct GlftFont *font,
+                       const char *str,
+                       int bytes,
+                       int *w,
+                       int *h)
+{
+    const char *c;
+    struct GlftGlyph *g;
+
+    if (!g_utf8_validate(str, bytes, NULL)) {
+        GlftDebug("Invalid UTF-8 in string\n");
+        return;
+    }
+
+    *w = 0;
+    *h = 0;
+
+    c = str;
+    while (c) {
+        g = GlftFontGlyph(font, c);
+        if (g) {
+            *w += g->width;
+            *h = MAX(g->height, *h);
+        } else {
+            *w += font->max_advance_width;
+        }
+        c = g_utf8_next_char(c);
+        if (c - str > bytes) break;
+    }
+}
+
index 2eab714f39e86560ea9ec1348397d25c945490ab..f428a9e8ec5302a13daab003c8e292f65cc0127f 100644 (file)
@@ -3,13 +3,53 @@
 
 #include <fontconfig/fontconfig.h>
 
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <fontconfig/fcfreetype.h>
+
+#include <glib.h>
+
 struct GlftFont {
-    FcFontSet *set;
+    FcPattern *pat;
+    FcCharSet *chars;
+
+    char *filename;
+    int index;
 
+    FT_Face face;
+    FT_Int ftflags;
+    FT_F26Dot6 ftcharsize;
+
+    FcBool antialias;
+    int spacing;
+    FcBool minspace;
+    int char_width;
     /* extended font attributes */
     FcBool shadow;
     int shadow_offset;
     float shadow_alpha;
+
+    GHashTable *glyph_map;
+
+    /* public shit */
+    int ascent;
+    int descent;
+    int height;
+    int max_advance_width;
 };
 
+struct GlftGlyph {
+    /* The character in UCS-4 encoding */
+    FcChar32 w;
+    /* OpenGL display list for the character */
+    unsigned int dlist;
+
+    int width;
+    int height;
+};
+
+/*! Takes a character in UTF-8 encoding and returns an OpenGL display list
+ for it */
+struct GlftGlyph *GlftFontGlyph(struct GlftFont *font, const char *c);
+
 #endif
index b8e4bccf92c18523df974de1a9c175e25bd9b58f..9adfe8e6a36601b3bf4e0c857b00972f6023466a 100644 (file)
@@ -17,4 +17,20 @@ void GlftFontClose(struct GlftFont *font);
 
 /* rendering */
 
+/*! Renders a string in UTF-8 encoding */
+void GlftRenderString(struct GlftFont *font,
+                      const char *str,
+                      int bytes,
+                      int x,
+                      int y);
+
+/* metrics */
+
+/*! Measures a string in UTF-8 encoding */
+void GlftMeasureString(struct GlftFont *font,
+                       const char *str,
+                       int bytes,
+                       int *w,
+                       int *h);
+
 #endif
index bedd16c546d48e5058c6bb95a81f186ec34933e4..2c4410efac33616fa85b276e2f0e764528d373ae 100644 (file)
@@ -1,8 +1,14 @@
 #include "glft.h"
+#include "font.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <fontconfig/fcfreetype.h>
 
 FcBool init_done = 0;
+FT_Library ft_lib;
 
 FcBool GlftInit()
 {
-    return init_done = FcInit();
+    return init_done = (FcInit() && !FT_Init_FreeType(&ft_lib));
 }
index 43fcb226a9ed82520efa4736dafbf31e88bc545f..e5f3166d95e0f4dc1f0079212fcb664f75d041a0 100644 (file)
@@ -2,5 +2,6 @@
 #define __glft_init_h
 
 extern FcBool init_done;
+extern FT_Library ft_lib;
 
 #endif
diff --git a/glft/render.c b/glft/render.c
new file mode 100644 (file)
index 0000000..5eb545e
--- /dev/null
@@ -0,0 +1,38 @@
+#include "render.h"
+#include "font.h"
+#include "debug.h"
+#include <glib.h>
+#include <GL/glx.h>
+
+void GlftRenderGlyph(FT_Face face, unsigned int dlist)
+{
+    FT_GlyphSlot slot = face->glyph;
+}
+
+void GlftRenderString(struct GlftFont *font, const char *str, int bytes,
+                      int x, int y)
+{
+    const char *c;
+    struct GlftGlyph *g;
+
+    if (!g_utf8_validate(str, bytes, NULL)) {
+        GlftDebug("Invalid UTF-8 in string\n");
+        return;
+    }
+
+    glPushMatrix();
+
+    c = str;
+    while (c) {
+        g = GlftFontGlyph(font, c);
+        if (g) {
+            glCallList(g->dlist);
+            glTranslatef(g->width, 0.0, 0.0);
+        } else
+            glTranslatef(font->max_advance_width, 0.0, 0.0);
+        c = g_utf8_next_char(c);
+        if (c - str >= bytes) break;
+    }
+
+    glPopMatrix();
+}
diff --git a/glft/render.h b/glft/render.h
new file mode 100644 (file)
index 0000000..9a3c151
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __glft_render_h
+#define __glft_render_h
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+void GlftRenderGlyph(FT_Face face, unsigned int dlist);
+
+#endif