]> icculus.org git repositories - dana/openbox.git/blob - glft/font.c
improve expose handling for alpha children
[dana/openbox.git] / glft / font.c
1 #include "font.h"
2 #include "glft.h"
3 #include "init.h"
4 #include "debug.h"
5 #include "render.h"
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <GL/glx.h>
9
10 struct GHashTable *glyph_map = NULL;
11
12 #define FLOOR(x)    ((x) & -64)
13 #define CEIL(x)     (((x)+63) & -64)
14 #define TRUNC(x)    ((x) >> 6)
15 #define ROUND(x)    (((x)+32) & -64)
16
17 #define GLFT_SHADOW "shadow"
18 #define GLFT_SHADOW_OFFSET "shadowoffset"
19 #define GLFT_SHADOW_ALPHA "shadowalpha"
20
21 void dest_glyph_map_value(gpointer key, gpointer val, gpointer data)
22 {
23     struct GlftGlyph *g = val;
24     glDeleteTextures(1, &g->tnum);    
25     free(g);
26 }
27
28 static void GlftDefaultSubstitute(Display *d, int s, FcPattern *pat)
29 {
30     FcValue v;
31     double dpi;
32
33     if (FcPatternGet(pat, FC_DPI, 0, &v) == FcResultNoMatch) {
34         dpi = DisplayHeight(d, s) * 25.4 / (double)DisplayHeightMM(d, s);
35         FcPatternAddDouble(pat, FC_DPI, dpi);
36     }
37     if (FcPatternGet(pat, FC_ANTIALIAS, 0, &v) == FcResultNoMatch) {
38         FcPatternAddBool(pat, FC_ANTIALIAS, FcFalse);
39         g_message("SETTING ANTIALIAS TRUE");
40     }
41     if (FcPatternGet(pat, FC_HINTING, 0, &v) == FcResultNoMatch)
42         FcPatternAddBool(pat, FC_HINTING, FcTrue);
43     if (FcPatternGet(pat, FC_AUTOHINT, 0, &v) == FcResultNoMatch)
44         FcPatternAddBool(pat, FC_AUTOHINT, FcFalse);
45     if (FcPatternGet(pat, FC_GLOBAL_ADVANCE, 0, &v) == FcResultNoMatch)
46         FcPatternAddBool(pat, FC_GLOBAL_ADVANCE, FcTrue);
47     if (FcPatternGet(pat, FC_SPACING, 0, &v) == FcResultNoMatch)
48         FcPatternAddInteger(pat, FC_SPACING, FC_PROPORTIONAL);
49     if (FcPatternGet(pat, FC_MINSPACE, 0, &v) == FcResultNoMatch)
50         FcPatternAddBool(pat, FC_MINSPACE, FcTrue);
51     if (FcPatternGet(pat, FC_CHAR_WIDTH, 0, &v) == FcResultNoMatch)
52         FcPatternAddInteger(pat, FC_CHAR_WIDTH, 0);
53     if (FcPatternGet(pat, GLFT_SHADOW, 0, &v) == FcResultNoMatch)
54         FcPatternAddBool(pat, GLFT_SHADOW, FcFalse);
55     if (FcPatternGet(pat, GLFT_SHADOW_OFFSET, 0, &v) == FcResultNoMatch)
56         FcPatternAddInteger(pat, GLFT_SHADOW_OFFSET, 2);
57     if (FcPatternGet(pat, GLFT_SHADOW_ALPHA, 0, &v) == FcResultNoMatch)
58         FcPatternAddDouble(pat, GLFT_SHADOW_ALPHA, 0.5);
59
60     FcDefaultSubstitute(pat);
61 }
62
63 struct GlftFont *GlftFontOpen(Display *d, int screen, const char *name)
64 {
65     struct GlftFont *font;
66     FcPattern *pat, *match;
67     FcResult res;
68     double alpha, psize;
69     FcBool hinting, autohint, advance;
70
71     assert(init_done);
72
73     pat = FcNameParse((const unsigned char*)name);
74     assert(pat);
75
76     /* XXX read our extended attributes here? (if failing below..) */
77
78     FcConfigSubstitute(NULL, pat, FcMatchPattern);
79     GlftDefaultSubstitute(d, screen, pat);
80
81     match = FcFontMatch(NULL, pat, &res);
82     FcPatternDestroy(pat);
83     if (!match) {
84         GlftDebug("failed to find matching font\n");
85         return NULL;
86     }
87     
88     font = malloc(sizeof(struct GlftFont));
89     font->display = d;
90     font->screen = screen;
91     font->pat = match;
92     font->ftflags = FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP;
93
94     if (FcPatternGetString(match, FC_FILE, 0, (FcChar8**) &font->filename) !=
95         FcResultMatch) {
96         GlftDebug("error getting FC_FILE from pattern\n");
97         goto openfail0;
98     }
99
100     switch (FcPatternGetInteger(match, FC_INDEX, 0, &font->index)) {
101     case FcResultNoMatch:
102         font->index = 0;
103         break;
104     case FcResultMatch:
105         break;
106     default:
107         GlftDebug("error getting FC_INDEX from pattern\n");
108         goto openfail0;
109     }
110
111     switch (FcPatternGetBool(match, FC_ANTIALIAS, 0, &font->antialias)) {
112     case FcResultNoMatch:
113         font->antialias = FcTrue;
114         break;
115     case FcResultMatch:
116         break;
117     default:
118         GlftDebug("error getting FC_ANTIALIAS from pattern\n");
119         goto openfail0;
120     }
121
122     switch (FcPatternGetBool(match, FC_HINTING, 0, &hinting)) {
123     case FcResultNoMatch:
124         hinting = FcTrue;
125         break;
126     case FcResultMatch:
127         break;
128     default:
129         GlftDebug("error getting FC_HINTING from pattern\n");
130         goto openfail0;
131     }
132     if (!hinting) font->ftflags |= FT_LOAD_NO_HINTING;
133
134     switch (FcPatternGetBool(match, FC_AUTOHINT, 0, &autohint)) {
135     case FcResultNoMatch:
136         autohint = FcFalse;
137         break;
138     case FcResultMatch:
139         break;
140     default:
141         GlftDebug("error getting FC_AUTOHINT from pattern\n");
142         goto openfail0;
143     }
144     if (autohint) font->ftflags |= FT_LOAD_FORCE_AUTOHINT;
145
146     switch (FcPatternGetBool(match, FC_GLOBAL_ADVANCE, 0, &advance)) {
147     case FcResultNoMatch:
148         advance = FcTrue;
149         break;
150     case FcResultMatch:
151         break;
152     default:
153         GlftDebug("error getting FC_GLOBAL_ADVANCE from pattern\n");
154         goto openfail0;
155     }
156     if (!advance) font->ftflags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
157
158     switch (FcPatternGetInteger(match, FC_SPACING, 0, &font->spacing)) {
159     case FcResultNoMatch:
160         font->spacing = FC_PROPORTIONAL;
161         break;
162     case FcResultMatch:
163         break;
164     default:
165         GlftDebug("error getting FC_SPACING from pattern\n");
166         goto openfail0;
167     }
168
169     switch (FcPatternGetBool(match, FC_MINSPACE, 0, &font->minspace)) {
170     case FcResultNoMatch:
171         font->minspace = FcTrue;
172         break;
173     case FcResultMatch:
174         break;
175     default:
176         GlftDebug("error getting FC_MINSPACE from pattern\n");
177         goto openfail0;
178     }
179
180     switch (FcPatternGetInteger(match, FC_CHAR_WIDTH, 0, &font->char_width)) {
181     case FcResultNoMatch:
182         font->char_width = 0;
183         break;
184     case FcResultMatch:
185         if (font->char_width)
186             font->spacing = FC_MONO;
187         break;
188     default:
189         GlftDebug("error getting FC_CHAR_WIDTH from pattern\n");
190         goto openfail0;
191     }
192
193     if (FcPatternGetDouble(match, FC_PIXEL_SIZE, 0, &psize) != FcResultMatch)
194         goto openfail0;
195     font->ftcharsize = (FT_F26Dot6) psize * 64;
196
197     if (FcPatternGetBool(match, GLFT_SHADOW, 0, &font->shadow) != FcResultMatch)
198         font->shadow = FcFalse;
199
200     if (FcPatternGetInteger(match,GLFT_SHADOW_OFFSET,0,&font->shadow_offset) !=
201         FcResultMatch)
202         font->shadow_offset = 2;
203
204     if (FcPatternGetDouble(match, GLFT_SHADOW_ALPHA,0,&alpha) != FcResultMatch)
205         alpha = 0.5;
206     font->shadow_alpha = (float)alpha;
207
208     /* time to load the font! */
209
210     if (FT_New_Face(ft_lib, font->filename, font->index, &font->face)) {
211         GlftDebug("failed to open FT face\n");
212         goto openfail0;
213     }
214     assert(FT_IS_SCALABLE(font->face));
215     if (!FT_IS_SCALABLE(font->face)) {
216         GlftDebug("got a non-scalable face");
217         goto openfail1;
218     }
219     if (FT_Set_Char_Size(font->face, 0, font->ftcharsize, 0, 0)) {
220         GlftDebug("failed to set char size on FT face\n");
221         goto openfail1;
222     }
223
224     if (!FcPatternGetCharSet(match, FC_CHARSET, 0, &font->chars) !=
225         FcResultMatch)
226         font->chars = FcFreeTypeCharSet(font->face, FcConfigGetBlanks(NULL));
227     if (!font->chars) {
228         GlftDebug("failed to get a valid CharSet\n");
229         goto openfail1;
230     }
231
232     if (font->char_width)
233         font->max_advance_width = font->char_width; 
234     else
235         font->max_advance_width = font->face->size->metrics.max_advance >> 6;
236     font->descent = -(font->face->size->metrics.descender >> 6);
237     font->ascent = font->face->size->metrics.ascender >> 6;
238     if (font->minspace) font->height = font->ascent + font->descent;
239     else                font->height = font->face->size->metrics.height >> 6;
240
241     font->kerning = FT_HAS_KERNING(font->face);
242
243     font->glyph_map = g_hash_table_new(g_int_hash, g_int_equal);
244
245     return font;
246
247 openfail1:
248     FT_Done_Face(font->face);
249 openfail0:
250     FcPatternDestroy(match);
251     free(font);
252     return NULL;
253 }
254
255 void GlftFontClose(struct GlftFont *font)
256 {
257     if (font) {
258         FT_Done_Face(font->face);
259         FcPatternDestroy(font->pat);
260         FcCharSetDestroy(font->chars);
261         g_hash_table_foreach(font->glyph_map, dest_glyph_map_value, NULL);
262         g_hash_table_destroy(font->glyph_map);
263         free(font);
264     }
265 }
266
267 struct GlftGlyph *GlftFontGlyph(struct GlftFont *font, const char *c)
268 {
269     const char *n = g_utf8_next_char(c);
270     int b = n - c;
271     struct GlftGlyph *g;
272     FcChar32 w;
273
274     FcUtf8ToUcs4((const FcChar8*) c, &w, b);
275     
276     g = g_hash_table_lookup(font->glyph_map, &w);
277     if (!g) {
278         if (FT_Load_Glyph(font->face, FcFreeTypeCharIndex(font->face, w),
279                           font->ftflags)) {
280             if (FT_Load_Glyph(font->face, 0, font->ftflags))
281                 return NULL;
282             else {
283                 g = g_hash_table_lookup(font->glyph_map, &w);
284             }
285         }
286     }
287     if (!g) {
288         g = malloc(sizeof(struct GlftGlyph));
289         g->w = w;
290         glGenTextures(1, &g->tnum);
291
292         GlftRenderGlyph(font->face, g);
293
294         if (!(font->spacing == FC_PROPORTIONAL)) {
295             g->width = font->max_advance_width;
296         } else {
297             g->width = TRUNC(ROUND(font->face->glyph->metrics.horiAdvance));
298         }
299         g->x = TRUNC(FLOOR(font->face->glyph->metrics.horiBearingX));
300         g->y = TRUNC(CEIL(font->face->glyph->metrics.horiBearingY));
301         g->height = TRUNC(ROUND(font->face->glyph->metrics.height));
302
303         g_hash_table_insert(font->glyph_map, &g->w, g);
304     }
305
306     return g;
307 }
308
309 int GlftFontAdvance(struct GlftFont *font,
310                     struct GlftGlyph *left,
311                     struct GlftGlyph *right)
312 {
313     FT_Vector v;
314     int k = 0;
315
316     if (left) k+= left->width;
317
318     if (right) {
319         k -= right->x;
320         if (font->kerning) {
321             FT_Get_Kerning(font->face, left->glyph, right->glyph,
322                            FT_KERNING_UNFITTED, &v);
323             k += v.x >> 6;
324         }
325     }
326
327     return k;
328 }
329
330 int GlftFontHeight(struct GlftFont *font)
331 {
332     return font->height;
333 }
334
335 int GlftFontMaxCharWidth(struct GlftFont *font)
336 {
337     return font->max_advance_width;
338 }