Mercurial > projects > mde
comparison mde/font/font.d @ 100:0ea4a3e651ae
There is now a position marker for text editing.
Changed the way fonts are configured. Actually, not much of the new way exists yet.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sat, 15 Nov 2008 17:39:14 +0000 |
parents | 49e7cfed4b34 |
children | 71f0f1f83620 |
comparison
equal
deleted
inserted
replaced
99:5de5810e3516 | 100:0ea4a3e651ae |
---|---|
19 public import mde.types.Colour; | 19 public import mde.types.Colour; |
20 import mde.lookup.Options; | 20 import mde.lookup.Options; |
21 import mde.font.FontTexture; | 21 import mde.font.FontTexture; |
22 import mde.font.exception; | 22 import mde.font.exception; |
23 | 23 |
24 import mde.file.mergetag.Reader; | |
25 import mde.file.mergetag.DataSet; | |
26 import mde.setup.paths; | |
27 import mde.setup.exception; // InitStage stuff | 24 import mde.setup.exception; // InitStage stuff |
28 | 25 |
29 import derelict.freetype.ft; | 26 import derelict.freetype.ft; |
30 | 27 |
31 import mde.file.deserialize; | |
32 import tango.stdc.stringz; | 28 import tango.stdc.stringz; |
33 import Util = tango.text.Util; | 29 import Util = tango.text.Util; |
34 import tango.util.log.Log : Log, Logger; | 30 import tango.util.log.Log : Log, Logger; |
35 | 31 |
36 // "Publically import" this symbol: | 32 // "Publically import" this symbol: |
44 /** FontStyle class. | 40 /** FontStyle class. |
45 * | 41 * |
46 * Particular to a font and size, and any other effects like bold/italic if ever implemented. | 42 * Particular to a font and size, and any other effects like bold/italic if ever implemented. |
47 * | 43 * |
48 * Note: it is not currently intended to be thread-safe. */ | 44 * Note: it is not currently intended to be thread-safe. */ |
49 class FontStyle : IDataSection | 45 class FontStyle |
50 { | 46 { |
51 //BEGIN Static: manager | 47 //BEGIN Static: manager |
52 static { | 48 static { |
53 debug (drawGlyphCache) void drawTexture() { | 49 debug (drawGlyphCache) void drawTexture() { |
54 if (fontTex !is null) | 50 if (fontTex !is null) |
55 fontTex.drawTexture; | 51 fontTex.drawTexture; |
56 } | 52 } |
57 | 53 |
58 /** Load the freetype library with settings from the file fileName. */ | 54 /** Load the freetype library with settings from options. */ |
59 private const fileName = "fonts"; | |
60 StageState initialize () { | 55 StageState initialize () { |
61 if (FT_Init_FreeType (&library)) | 56 if (FT_Init_FreeType (&library)) |
62 throw new fontException ("error initialising the FreeType library"); | 57 throw new fontException ("error initialising the FreeType library"); |
63 | 58 |
64 // Check version | 59 // Check version |
65 FT_Int maj, min, patch; | 60 FT_Int maj, min, patch; |
66 FT_Library_Version (library, &maj, &min, &patch); | 61 FT_Library_Version (library, &maj, &min, &patch); |
67 if (maj != 2 || min != 3) { | 62 if (maj != 2 || min != 3) { |
68 logger.warn ("Using an untested FreeType version: {}.{}.{}", maj, min, patch); | 63 logger.warn ("Using an untested FreeType version: {}.{}.{}", maj, min, patch); |
69 logger.info ("The only tested version of freetype is 2.3.5"); | 64 logger.info ("The only tested version of freetype is 2.3"); |
70 } | 65 } |
71 | 66 |
72 // Set LCD filtering method if LCD rendering is enabled. | 67 // Set LCD filtering method if LCD rendering is enabled. |
73 const RMF = FT_LOAD_TARGET_LCD | FT_LOAD_TARGET_LCD_V; | 68 const RMF = FT_LOAD_TARGET_LCD | FT_LOAD_TARGET_LCD_V; |
74 if (fontOpts.renderMode() & RMF && | 69 if (fontOpts.renderMode() & RMF && |
84 /* If no support for LCD filtering, then LCD rendering only emulates NORMAL with 3 | 79 /* If no support for LCD filtering, then LCD rendering only emulates NORMAL with 3 |
85 * times wider glyphs. So disable and save the extra work. */ | 80 * times wider glyphs. So disable and save the extra work. */ |
86 fontOpts.renderMode = FT_LOAD_TARGET_NORMAL; | 81 fontOpts.renderMode = FT_LOAD_TARGET_NORMAL; |
87 } | 82 } |
88 | 83 |
89 /* Load font settings | 84 // Load the fallback font; if it throws let exception abort program |
90 * | 85 fallback = new FontStyle (fontOpts.defaultFont(), fontOpts.defaultSize()); |
91 * Each mergetag section corresponds to a font; each is loaded whether used or not | 86 |
92 * (however the actual font files are only loaded on use). A fallback id must be | |
93 * provided in the header which must match a loaded font name; if a non-existant font | |
94 * is requested a warning will be logged and this font returned. */ | |
95 char[] fallbackName; | |
96 try { | |
97 IReader reader; | |
98 reader = confDir.makeMTReader (fileName, PRIORITY.LOW_HIGH, null, true); | |
99 reader.dataSecCreator = delegate IDataSection(ID id) { | |
100 auto f = new FontStyle; | |
101 fonts[id] = f; | |
102 return f; | |
103 }; | |
104 reader.read; | |
105 | |
106 // get fallback name | |
107 char[]* p = "fallback" in reader.dataset.header._charA; | |
108 if (p is null) | |
109 throw new fontException ("No fallback font style specified"); | |
110 fallbackName = *p; | |
111 } catch (NoFileException) { | |
112 throw new fontException ("No font settings file (fonts.[mtt|mtb])"); | |
113 } catch (Exception e) { | |
114 throw new fontException ("Reading font settings failed: "~e.msg); | |
115 } | |
116 | |
117 // Find the fallback | |
118 FontStyle* p = fallbackName in fonts; | |
119 if (p is null) | |
120 throw new fontException ("Fallback font style specified is not found"); | |
121 fallback = *p; | |
122 // Load the fallback now, to ensure it's available. | |
123 // Also note that get() doesn't make sure the fallback is loaded before returning it. | |
124 fallback.load; | |
125 | |
126 return StageState.ACTIVE; | 87 return StageState.ACTIVE; |
127 } | 88 } |
128 | 89 |
129 /** Cleanup: delete all fonts. */ | 90 /** Cleanup: delete all fonts. */ |
130 StageState cleanup () { | 91 StageState cleanup () { |
131 // Clear loaded fonts (each has an FT_Face object needing to be freed): | 92 // Clear loaded fonts (each has an FT_Face object needing to be freed): |
132 foreach (fs; fonts) | 93 /* FIXME |
94 foreach (fs; fonts) | |
133 fs.freeFace; | 95 fs.freeFace; |
96 */ | |
134 | 97 |
135 FT_Done_FreeType (library); // free the library | 98 FT_Done_FreeType (library); // free the library |
136 | 99 |
137 return StageState.INACTIVE; | 100 return StageState.INACTIVE; |
138 } | 101 } |
142 * Also loads the font if it's not already loaded, so the first call may take some time. | 105 * Also loads the font if it's not already loaded, so the first call may take some time. |
143 * | 106 * |
144 * Uses fallback font-style if the desired style isn't known about or fails to load, so | 107 * Uses fallback font-style if the desired style isn't known about or fails to load, so |
145 * this function should never fail or throw, in theory (unless out of memory). The | 108 * this function should never fail or throw, in theory (unless out of memory). The |
146 * fallback should already be loaded. */ | 109 * fallback should already be loaded. */ |
147 FontStyle get(char[] name) { | 110 FontStyle getDefault () { |
148 FontStyle* p = name in fonts; | 111 //FIXME: Ddoc, new purpose; rename |
149 if (p is null) { | 112 return fallback; |
150 logger.warn ("Font style "~name~" requested but not found; reverting to the fallback style."); | |
151 fonts[name] = fallback; // set to prevent another warning getting logged | |
152 return fallback; | |
153 } | |
154 // Got it, but we need to make sure it's loaded: | |
155 try { | |
156 p.load; | |
157 } catch (Exception e) { | |
158 logger.warn ("Font style "~name~" failed to load; reverting to the fallback style."); | |
159 return fallback; | |
160 } | |
161 return *p; | |
162 } | 113 } |
163 | 114 |
164 private: | 115 private: |
165 FT_Library library; | 116 FT_Library library; |
166 FontTexture fontTex; | 117 FontTexture fontTex; |
167 FontStyle[ID] fonts; // all font styles known about; not necessarily loaded | 118 //FontStyle[char[]] fonts; // loaded fonts, by file name FIXME: use hash of struct { char[] path; int size; }? |
168 FontStyle fallback; // used when requested font isn't in fonts | 119 FontStyle fallback; // default & used when requested font can't be loaded |
169 } | 120 } |
170 //END Static | 121 //END Static |
171 | |
172 this() {} | |
173 | |
174 //BEGIN Mergetag code | |
175 //NOTE: would it be better not to use a new mergetag file for this? | |
176 //FIXME: revise when gui can set options | |
177 void addTag (char[] tp, ID id, char[] dt) { | |
178 if (tp == "char[]") { | |
179 if (id == "path") | |
180 path = parseTo!(char[]) (dt); | |
181 } | |
182 else if (tp == "int") { | |
183 if (id == "size") | |
184 size = parseTo!(int) (dt); | |
185 } | |
186 } | |
187 void writeAll (ItemDelg) {} // no writing the config for now | |
188 //END Mergetag code | |
189 | 122 |
190 /** Load the font file. | 123 /** Load the font file. |
191 * | 124 * |
192 * Even if the same font is used at multiple sizes, multiple copies of FT_Face are used. | 125 * Even if the same font is used at multiple sizes, multiple copies of FT_Face are used. |
193 * Sharing an FT_Face would require calling FT_Set_Pixel_Sizes each time a glyph is rendered or | 126 * Sharing an FT_Face would require calling FT_Set_Pixel_Sizes each time a glyph is rendered or |
194 * swapping the size information (face.size)? */ | 127 * swapping the size information (face.size)? */ |
195 void load () | 128 this (char[] path, int size) |
196 in { | 129 in { |
197 assert (library !is null, "font: library is null"); | 130 assert (library !is null, "font: library is null"); |
198 } body { | 131 } body { |
199 if (FT_New_Face (library, toStringz(path), 0, &face)) | 132 if (FT_New_Face (library, toStringz(path), 0, &face)) |
200 throw new fontLoadException ("Unable to read font: "~path); | 133 throw new fontLoadException ("Unable to read font: "~path); |
204 path ~ " is). Please report if you want to see support."); | 137 path ~ " is). Please report if you want to see support."); |
205 /* The following will need to be addressed when adding support for non-scalables: | 138 /* The following will need to be addressed when adding support for non-scalables: |
206 * Use of face.size.metrics.height property. | 139 * Use of face.size.metrics.height property. |
207 */ | 140 */ |
208 | 141 |
209 if (FT_Set_Pixel_Sizes (face, 0,size)) | 142 if (FT_Set_Pixel_Sizes (face, 0, size)) |
210 throw new fontLoadException ("Unable to set pixel size"); | 143 throw new fontLoadException ("Unable to set pixel size"); |
211 | 144 |
212 // Create if necessary: | 145 // Create if necessary: |
213 if (fontTex is null) | 146 if (fontTex is null) |
214 fontTex = new FontTexture; | 147 fontTex = new FontTexture; |
148 | |
149 return this; | |
150 } | |
151 | |
152 this (FontStyle font, int size) { | |
153 //FIXME: copy font's face and set new size? | |
215 } | 154 } |
216 | 155 |
217 /** Update a TextBlock cache, as used by the textBlock function. | 156 /** Update a TextBlock cache, as used by the textBlock function. |
218 * | 157 * |
219 * The only use of this is to get the text block's size ahead of rendering, via TextBlock's w | 158 * The only use of this is to get the text block's size ahead of rendering, via TextBlock's w |
232 } | 171 } |
233 } | 172 } |
234 | 173 |
235 /** Draw a block of text (may inlcude new-lines). | 174 /** Draw a block of text (may inlcude new-lines). |
236 * | 175 * |
176 * Params: | |
177 * x = Top left x-coordinate of text block | |
178 * y = Top left y-coordinate of text block | |
179 * str = Text to render | |
180 * cache = An (optional) TextBlock used for rendering this text, to save some CPU work. See updateBlock | |
181 * col = Colour to render the text; see mde.types.Colour | |
182 * index = Either the index of the character to draw with an edit cursor or size_t.max. Not | |
183 * the index within str, but the number of characters, excluding new-lines. | |
237 * The text block is drawn with top-left corner at x,y. To put the text's baseline at a given | 184 * The text block is drawn with top-left corner at x,y. To put the text's baseline at a given |
238 * y coordinate would require some changes. Line height is fixed based on largest glyph. | 185 * y coordinate would require some changes. Line height is fixed (see getLineSeparation). |
239 * Due to hinter, glyphs are not guaranteed to lie within the "bounding box" defined by cache. | 186 * Due to hinter, glyphs are not guaranteed to lie within the "bounding box" defined by cache. |
240 * Can be changed to test size of each glyph if necessary. | 187 * Can be changed to test size of each glyph if necessary. |
241 * | 188 * |
242 * Specify the text's colour with col. Use textBlockA() instead for transparent text. | 189 * Specify the text's colour with col. Use textBlockA() instead for transparent text. |
243 * | 190 * |
245 * the cache argument. This is the recommended method, although for one-time calls when you | 192 * the cache argument. This is the recommended method, although for one-time calls when you |
246 * don't need to know the size, the other version of textBlock may be used. | 193 * don't need to know the size, the other version of textBlock may be used. |
247 * --------------------------------- | 194 * --------------------------------- |
248 * char[] str; | 195 * char[] str; |
249 * TextBlock strCache; | 196 * TextBlock strCache; |
250 * textBlock (x, y, str, strCache); | 197 * textBlock (x, y, str, strCache, Colour.WHITE); |
251 * --------------------------------- | 198 * --------------------------------- |
252 * The TextBlock cache will be updated as necessary. Besides the initial update, this will only | 199 * The TextBlock cache will be updated as necessary. Besides the initial update, this will only |
253 * be if the font changes, or it is manually invalidated. This can be done by setting the | 200 * be if the font changes, or it is manually invalidated. This can be done by setting the |
254 * TextBlock's cacheVer property to -1, which should be done if str is changed. | 201 * TextBlock's cacheVer property to -1, which should be done if str is changed. |
255 * | 202 * |
256 * The TextBlock's w and h properties are set to the size (in pixels) of the text block; other | 203 * The TextBlock's w and h properties are set to the size (in pixels) of the text block; other |
257 * than this cache only serves as a small optimisation. However, the only way to get the size | 204 * than this cache only serves as a small optimisation. However, the only way to get the size |
258 * of a text block is to use a TextBlock cache and update it, either with this function or with | 205 * of a text block is to use a TextBlock cache and update it, either with this function or with |
259 * the updateBlock function. */ | 206 * the updateBlock function. */ |
260 void textBlock (int x, int y, char[] str, ref TextBlock cache, Colour col) | 207 void textBlock (int x, int y, char[] str, ref TextBlock cache, Colour col, size_t index = size_t.max) |
261 in { | 208 in { |
262 assert (face, "FontStyle: face is null"); | 209 assert (face, "FontStyle: face is null"); |
263 } body { | 210 } body { |
264 try { | 211 try { |
265 fontTex.drawCache (face, str, cache, x, y, col); | 212 fontTex.drawCache (face, str, cache, x, y, col, index); |
266 } catch (Exception e) { | 213 } catch (Exception e) { |
267 logger.warn ("Exception while drawing text: "~e.msg); | 214 logger.warn ("Exception while drawing text: "~e.msg); |
268 } | 215 } |
269 } | 216 } |
270 /** ditto */ | 217 /** ditto */ |
271 void textBlock (int x, int y, char[] str, Colour col) | 218 void textBlock (int x, int y, char[] str, Colour col, size_t index = size_t.max) |
272 in { | 219 in { |
273 assert (face, "FontStyle: face is null"); | 220 assert (face, "FontStyle: face is null"); |
274 } body { | 221 } body { |
275 try { | 222 try { |
276 // Using the cache method for one-time use is slightly less than optimal, but doing so | 223 // Using the cache method for one-time use is slightly less than optimal, but doing so |
277 // isn't really recommended anyway (and maintaining two versions of fontTex.drawText | 224 // isn't really recommended anyway (and maintaining two versions of fontTex.drawText |
278 // would be horrible). | 225 // would be horrible). |
279 TextBlock cache; | 226 TextBlock cache; |
280 fontTex.drawCache (face, str, cache, x, y, col); | 227 fontTex.drawCache (face, str, cache, x, y, col, index); |
281 } catch (Exception e) { | 228 } catch (Exception e) { |
282 logger.warn ("Exception while drawing text: "~e.msg); | 229 logger.warn ("Exception while drawing text: "~e.msg); |
283 } | 230 } |
284 } | 231 } |
285 | 232 |
286 /** A variation of textBlock for transparency. | 233 /** A variation of textBlock for transparency. |
287 * | 234 * |
288 * Set the alpha by calling glColor*() first. See FontTexture.drawCacheA()'s documentation for | 235 * Set the alpha by calling glColor*() first. See FontTexture.drawCacheA()'s documentation for |
289 * details. */ | 236 * details. */ |
290 void textBlockA (int x, int y, char[] str, ref TextBlock cache, Colour col) | 237 void textBlockA (int x, int y, char[] str, ref TextBlock cache, Colour col, size_t index = size_t.max) |
291 in { | 238 in { |
292 assert (face, "FontStyle: face is null"); | 239 assert (face, "FontStyle: face is null"); |
293 } body { | 240 } body { |
294 try { | 241 try { |
295 fontTex.drawCacheA (face, str, cache, x, y, col); | 242 fontTex.drawCacheA (face, str, cache, x, y, col, index); |
296 } catch (Exception e) { | 243 } catch (Exception e) { |
297 logger.warn ("Exception while drawing text: "~e.msg); | 244 logger.warn ("Exception while drawing text: "~e.msg); |
298 } | 245 } |
299 } | 246 } |
300 | 247 |
310 FT_Done_Face (face); | 257 FT_Done_Face (face); |
311 face = null; // functions using face use assertions on face to check its validity. | 258 face = null; // functions using face use assertions on face to check its validity. |
312 } | 259 } |
313 | 260 |
314 private: | 261 private: |
315 char[] path; // path to font file | |
316 int size; // font size | |
317 | |
318 FT_Face face; | 262 FT_Face face; |
319 | |
320 debug(mdeUnitTest) unittest { | |
321 // Don't do a unittest since font relies on loading the freetype library dynamically, | |
322 // normally done by Init. Also font is mostly visual and many problems will be obvious. | |
323 } | |
324 } | 263 } |
325 | |
326 /+class OptionsFont : Options { | |
327 alias store!(+/ |