Changeset 48:a98ffb64f066
- Timestamp:
- 05/31/08 07:40:26 (7 months ago)
- Files:
-
- codeDoc/gui/content.txt (added)
- codeDoc/jobs.txt (modified) (2 diffs)
- data/conf/fonts.mtt (added)
- data/conf/options.mtt (modified) (1 diff)
- mde/gl/basic.d (modified) (3 diffs)
- mde/gl/draw.d (modified) (2 diffs)
- mde/gui/widget/Ifaces.d (modified) (1 diff)
- mde/gui/widget/miscWidgets.d (modified) (1 diff)
- mde/resource/FontTexture.d (added)
- mde/resource/exception.d (modified) (2 diffs)
- mde/resource/font.d (modified) (4 diffs)
- mde/scheduler/init2.d (modified) (1 diff)
- mde/sdl.d (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
codeDoc/jobs.txt
r47 r48 4 4 5 5 In progress: 6 Implementing font rendering7 Reading ft tutorial8 Use cartesian coordinates? Or not?9 6 10 7 … … 55 52 56 53 Done (for git log message): 54 Implemented font rendering (grayscale only; i.e. non-LCD). 55 FontTexture creates a texture and caches glyphs. 56 Font supports multiple styles/faces, loaded from config file (should probably be loaded via Options instead). 57 TextBlock cache for glyph placement within a string. data/conf/options.mtt
r43 r48 10 10 <bool|noFrame=false> 11 11 <bool|resizable=true> 12 <bool|hardware= true>12 <bool|hardware=false> 13 13 <bool|fullscreen=false> 14 14 <int|screenW=1280> mde/gl/basic.d
r45 r48 26 26 void glSetup () { 27 27 glDisable(GL_DEPTH_TEST); 28 glEnable (GL_TEXTURE_2D); 28 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 29 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 30 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 31 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 32 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); 33 glEnable(GL_TEXTURE_2D); 34 glShadeModel(GL_FLAT); 29 35 30 36 glClearColor (0.0f, 0.0f, 0.0f, 0.0f); … … 32 38 glMatrixMode(GL_MODELVIEW); 33 39 glLoadIdentity(); 40 41 // Used for font rendering: 42 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 43 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 44 //NOTE: wrap mode may have an effect, but shouldn't be noticed... 34 45 } 35 46 … … 56 67 } 57 68 void drawBox (int x, int y, int w, int h) { 69 glDisable(GL_TEXTURE_2D); 58 70 glRecti(x, y+h, x+w, y); 59 71 } mde/gl/draw.d
r45 r48 27 27 import tango.util.log.Log : Log, Logger; 28 28 29 import mde.resource.font; 29 30 private Logger logger; 30 31 static this () { … … 38 39 39 40 gui.draw (); 41 FontStyle.drawTexture; 40 42 41 43 GLenum err = glGetError(); mde/gui/widget/Ifaces.d
r46 r48 45 45 46 46 /** Returns the window's gui. */ 47 //NOTE: was going to remove this, but it's used more than I thought48 47 IGui gui (); 49 48 mde/gui/widget/miscWidgets.d
r45 r48 115 115 void draw () { 116 116 super.draw(); 117 if (font is null) font = Font.get("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"); 118 font.drawStr (x,y, "Text Widget"); 117 if (font is null) font = FontStyle.get("default"); 118 font.textBlock (x,y, "|â|â| .,.,.", textCache); // test new-lines and unicode characters 119 //old string: "Text Widget\nαβγ â ÎÎÎ" 119 120 } 120 121 121 122 protected: 122 static Font font; 123 TextBlock textCache; 124 static FontStyle font; 123 125 } mde/resource/exception.d
r44 r48 18 18 import mde.exception; 19 19 20 /// Thrown when initialis ing freetype fails.20 /// Thrown when initialisation fails 21 21 class fontException : mdeException { 22 22 char[] getSymbol () { … … 31 31 /// Thrown when loading a freetype font fails. 32 32 class fontLoadException : fontException { 33 this (char[] msg) { 34 super(msg); 35 } 36 } 37 38 /// Thrown when problems occur with glyphs (rendering, etc.) 39 class fontGlyphException : fontException { 33 40 char[] getSymbol () { 34 return super.getSymbol ~ ". resource.font";41 return super.getSymbol ~ ".glyph"; 35 42 } 36 43 mde/resource/font.d
r45 r48 17 17 module mde.resource.font; 18 18 19 import mde.resource.FontTexture; 19 20 import mde.resource.exception; 21 22 import mde.mergetag.Reader; 23 import mde.mergetag.DataSet; 24 import mde.mergetag.exception; 25 import mde.resource.paths; 20 26 21 27 import derelict.freetype.ft; 22 28 import derelict.opengl.gl; 23 29 30 import tango.scrapple.text.convert.parseTo : parseTo; 24 31 import tango.stdc.stringz; 32 import Util = tango.text.Util; 25 33 import tango.util.log.Log : Log, Logger; 34 35 // "Publically import" this symbol: 36 alias mde.resource.FontTexture.TextBlock TextBlock; 26 37 27 38 private Logger logger; … … 30 41 } 31 42 32 /** Font class.43 /** FontStyle class. 33 44 * 34 * Particular to a font and size . (Maybe not size?)45 * Particular to a font and size, and any other effects like bold/italic if ever implemented. 35 46 * 36 47 * Note: it is not currently intended to be thread-safe. */ 37 class Font 48 class FontStyle : IDataSection 38 49 { 39 50 //BEGIN Static: manager 40 51 static { 52 debug void drawTexture() { 53 if (fontTex !is null) 54 fontTex.drawTexture; 55 } 56 41 57 /** Load the freetype library. */ 42 58 void initialize () { 59 if (!confDir.exists (fileName)) 60 throw new fontException ("No font settings file (fonts.[mtt|mtb])"); 61 43 62 if (FT_Init_FreeType (&library)) 44 63 throw new fontException ("error initialising the FreeType library"); 45 } 46 47 //FIXME: don't use GC for Font resources 64 65 // Check version 66 FT_Int maj, min, patch; 67 FT_Library_Version (library, &maj, &min, &patch); 68 if (maj != 2 || min != 3) { 69 char[128] tmp; 70 logger.warn (logger.format (tmp, "Using an untested FreeType version: {}.{}.{}", maj, min, patch)); 71 } 72 73 /* Load font settings 74 * 75 * Each mergetag section corresponds to a font; each is loaded whether used or not 76 * (however the actual font files are only loaded on use). A fallback id must be 77 * provided in the header which must match a loaded font name; if a non-existant font 78 * is requested a warning will be logged and this font returned. */ 79 char[] fallbackName; 80 try { 81 IReader reader; 82 reader = confDir.makeMTReader (fileName, PRIORITY.LOW_HIGH, null, true); 83 reader.dataSecCreator = delegate IDataSection(ID id) { 84 auto f = new FontStyle; 85 fonts[id] = f; 86 return f; 87 }; 88 reader.read; 89 90 // get fallback name 91 char[]* p = "fallback" in reader.dataset.header.Arg!(char[]).Arg; 92 if (p is null) 93 throw new fontException ("No fallback font style specified"); 94 fallbackName = *p; 95 } 96 catch (MTException e) { 97 throw new fontException ("Mergetag exception: "~e.msg); 98 } 99 100 // Find the fallback 101 FontStyle* p = fallbackName in fonts; 102 if (p is null) 103 throw new fontException ("Fallback font style specified is not found"); 104 fallback = *p; 105 // Load the fallback now, to ensure it's available. 106 // Also note that get() doesn't make sure the fallback is loaded before returning it. 107 fallback.load; 108 } 109 private const fileName = "fonts"; 110 111 //FIXME: don't use GC for FontStyle resources 48 112 /** Cleanup: delete all fonts. */ 49 113 void cleanup () { 50 if (font)51 delete font;52 53 114 FT_Done_FreeType (library); 54 115 } 55 116 56 /** Get a font. 57 * 58 * Later specify font/size. 59 * 60 * Throws: 61 * fontLoadException when unable to load the font. */ 62 Font get(char[] path) { 63 if (font is null) font = new Font(path); 64 return font; 117 /** Get a FontStyle instance, for a section in the fonts.mtt file. 118 * 119 * Also loads the font if it's not already loaded, so the first call may take some time. 120 * 121 * Uses fallback font-style if the desired style isn't known about or fails to load, so 122 * this function should never fail or throw, in theory (unless out of memory). The 123 * fallback should already be loaded. */ 124 FontStyle get(char[] name) { 125 FontStyle* p = name in fonts; 126 if (p is null) { 127 logger.warn ("Font style "~name~" requested but not found; reverting to the fallback style."); 128 fonts[name] = fallback; // set to prevent another warning getting logged 129 return fallback; 130 } 131 // Got it, but we need to make sure it's loaded: 132 try { 133 p.load; 134 } catch (Exception e) { 135 logger.warn ("Font style "~name~" failed to load; reverting to the fallback style."); 136 return fallback; 137 } 138 return *p; 65 139 } 66 140 67 141 private: 68 142 FT_Library library; 69 Font font; 143 FontTexture fontTex; 144 FontStyle[ID] fonts; // all font styles known about; not necessarily loaded 145 FontStyle fallback; // used when requested font isn't in fonts 70 146 } 71 147 //END Static 72 148 73 74 /** Load & cache a new font. */ 75 this (char[] path) 149 this() {} 150 151 //BEGIN Mergetag code 152 //NOTE: would it be better not to use a new mergetag file for this? 153 //FIXME: revise when gui can set options 154 void addTag (char[] tp, ID id, char[] dt) { 155 if (tp == "char[]") { 156 if (id == "path") 157 path = parseTo!(char[]) (dt); 158 } 159 else if (tp == "int") { 160 if (id == "size") 161 size = parseTo!(int) (dt); 162 } 163 } 164 void writeAll (ItemDelg) {} // no writing the config for now 165 //END Mergetag code 166 167 /** Load the font file. 168 * 169 * Even if the same font is used at multiple sizes, multiple copies of FT_Face are used. 170 * Sharing an FT_Face would require calling FT_Set_Pixel_Sizes each time a glyph is rendered or 171 * swapping the size information (face.size)? */ 172 void load () 76 173 in { 77 174 assert (library !is null, "font: library is null"); … … 80 177 throw new fontLoadException ("Unable to read font: "~path); 81 178 82 if (FT_Set_Pixel_Sizes (face, 0,16)) 179 if (!FT_IS_SCALABLE (face)) 180 throw new fontLoadException ("Currently no support for non-scalable fonts (which " ~ 181 path ~ " is). Please report if you want to see support."); 182 /* The following will need to be addressed when adding support for non-scalables: 183 * Use of face.size.metrics.height property. 184 */ 185 186 if (FT_Set_Pixel_Sizes (face, 0,size)) 83 187 throw new fontLoadException ("Unable to set pixel size"); 84 } 85 86 void drawStr (int x, int y, char[] str) { 87 FT_Vector pen = { x*64, y*64 }; 88 auto g = face.glyph; 89 90 FT_Matrix m; 91 m.xx = 0x10000; 92 m.xy = m.yx = 0; 93 m.yy = -0x10000; 94 95 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 96 97 FT_Pos y_adj = 0; // y adjustment (for height) 98 99 FT_Bool useKerning = FT_HAS_KERNING (face); 100 FT_UInt previous = 0; 101 102 foreach (chr; str) { 103 auto gi = FT_Get_Char_Index (face, chr); 104 105 if (useKerning && previous && gi) 106 { 107 FT_Vector delta; 108 109 110 FT_Get_Kerning (face, previous, gi, FT_Kerning_Mode.FT_KERNING_DEFAULT, &delta); 111 112 pen.x += delta.x; 113 } 114 115 FT_Set_Transform(face, &m, &pen); 116 if (FT_Load_Glyph(face, gi, FT_LOAD_RENDER)) 117 return; // give up 118 119 if (y_adj < g.metrics.height) y_adj = g.metrics.height; 120 121 auto b = g.bitmap; 122 if (b.pixel_mode != FT_Pixel_Mode.FT_PIXEL_MODE_GRAY || b.num_grays != 256) { 123 char[128] tmp; 124 logger.warn (logger.format (tmp,"Unsupported freetype bitmap format: {}, {}", b.pixel_mode, b.num_grays)); 125 return; 126 } 127 if (b.pitch != b.width) 128 logger.info ("b.pitch != b.width"); 129 130 //NOTE: y direction! 131 glRasterPos2i (g.bitmap_left,g.bitmap_top /+ (y_adj >> 6)+/); 132 glDrawPixels (b.width, b.rows, GL_LUMINANCE, GL_UNSIGNED_BYTE, cast(void*) b.buffer); 133 134 pen.x += g.advance.x; 135 pen.y += g.advance.y; 136 previous = gi; 137 } 188 189 // Create if necessary: 190 if (fontTex is null) 191 fontTex = new FontTexture; 192 } 193 194 /** Update a TextBlock cache, as used by the textBlock function. 195 * 196 * The only use of this is to get the text block's size ahead of rendering, via TextBlock's w 197 * and h properties. 198 * 199 * This function will only actually update the cache if it is invalid, caused either by the 200 * font being changed or if cache.cacheVer < 0. */ 201 void updateBlock (char[] str, ref TextBlock cache) { 202 fontTex.updateCache (face, str, cache); 203 } 204 205 /** Draw a block of text (may inlcude new-lines). 206 * 207 * As a CPU-side code optimisation, store a TextBlock (unique to str) and pass a reference as 208 * the cache argument. This is the recommended method, although for one-time calls when you 209 * don't need to know the size, the other version of textBlock may be used. 210 * --------------------------------- 211 * char[] str; 212 * TextBlock strCache; 213 * textBlock (x, y, str, strCache); 214 * --------------------------------- 215 * The TextBlock cache will be updated as necessary. Besides the initial update, this will only 216 * be if the font changes, or it is manually invalidated. This can be done by setting the 217 * TextBlock's cacheVer property to -1, which should be done if str is changed. 218 * 219 * The TextBlock's w and h properties are set to the size (in pixels) of the text block; other 220 * than this cache only serves as a small optimisation. However, the only way to get the size 221 * of a text block is to use a TextBlock cache and update it, either with this function or with 222 * the updateBlock function. */ 223 void textBlock (int x, int y, char[] str, ref TextBlock cache) { 224 try { 225 fontTex.drawTextCache (face, str, cache, x, y); 226 } catch (Exception e) { 227 logger.warn ("Exception while drawing text: "~e.msg); 228 } 229 } 230 /** ditto */ 231 void textBlock (int x, int y, char[] str) { 232 try { 233 // Using the cache method for one-time use is slightly less than optimal, but doing so 234 // isn't really recommended anyway (and maintaining two versions of fontTex.drawText 235 // would be horrible). 236 TextBlock cache; 237 fontTex.drawTextCache (face, str, cache, x, y); 238 } catch (Exception e) { 239 logger.warn ("Exception while drawing text: "~e.msg); 240 } 241 } 242 243 244 /** The font-specified vertical distance between the baseline of consecutive lines. */ 245 int getLineSeparation () { 246 return face.size.metrics.height >> 6; 138 247 } 139 248 … … 143 252 144 253 private: 254 char[] path; // path to font file 255 int size; // font size 256 145 257 FT_Face face; 146 258 } mde/scheduler/init2.d
r44 r48 87 87 void initFreeType () { // init func 88 88 try { 89 font.Font .initialize;89 font.FontStyle.initialize; 90 90 } catch (Exception e) { 91 91 logger.fatal ("initFreeType failed: " ~ e.msg); mde/sdl.d
r45 r48 28 28 29 29 import derelict.sdl.sdl; 30 import derelict.opengl.gl; // for loading a later gl version 31 import derelict.util.exception; 30 32 31 33 private Logger logger; … … 77 79 78 80 // OpenGL attributes 79 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);80 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);81 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);82 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);81 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); 82 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); 83 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); 84 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); 83 85 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1); 84 86 … … 90 92 logger.fatal (msg ? fromStringz(msg) : "no reason available"); 91 93 94 // Print a load of info: 95 logger.info ("Available video modes:"); 96 char[128] tmp; 97 SDL_Rect** modes = SDL_ListModes (null, SDL_FULLSCREEN); 98 if (modes is null) logger.info ("None!"); 99 else if (modes is cast(SDL_Rect**) -1) logger.info ("All modes are available"); 100 else { 101 for (uint i = 0; modes[i] !is null; ++i) { 102 logger.info (logger.format (tmp, "\t{}x{}", modes[i].w, modes[i].h)); 103 } 104 } 105 106 SDL_VideoInfo* vi = SDL_GetVideoInfo (); 107 if (vi !is null) { 108 logger.info ("Video info:"); 109 logger.info ("Hardware surface support: "~ (vi.flags & SDL_HWSURFACE ? "yes" : "no")); 110 logger.info (logger.format (tmp, "Video memory: {}", vi.video_mem)); 111 112 if (vi.vfmt !is null) { 113 logger.info ("Best video mode:"); 114 logger.info (logger.format (tmp, "Bits per pixel: {}", vi.vfmt.BitsPerPixel)); 115 } 116 } 117 92 118 setInitFailure (); 93 119 return; 94 120 } 95 121 96 // Now (must be done after GL context is created) we can try to load later version. 97 // The initial loading provides opengl 1.1 features. 98 /+ No later GL features are currently used. 122 /* Now (must be done after GL context is created) we can try to load later version. 123 * The initial loading provides opengl 1.1 features. 124 * 125 * Currently the latest version used is 1.3; adjust this as necessary. However, before using 126 * features from any OpenGL version > 1.1 a check must be made on what was loaded by calling 127 * DerelictGL.availableVersion(). Note that availableVersion() could be used instead to load 128 * the highest supported version but this way we know what we're getting. 129 */ 99 130 try { 100 DerelictGL.loadVersions(GLVersion.Version21); 101 } catch (DerelictException de) { 102 logger.fatal ("Loading OpenGL version > 1.1 failed:"); 103 logger.fatal (de.msg); 104 131 DerelictGL.loadVersions(GLVersion.Version13); 132 } catch (SharedLibProcLoadException e) { 133 logger.warn ("Loading OpenGL version 1.3 failed:"); 134 logger.warn (e.msg); 135 136 //NOTE: might be worth guaranteeing a minimal version to save later checks? 137 /+ Do this if you want the program to abort: 105 138 setInitFailure (); 106 139 return; 107 }108 +/140 +/ 141 } 109 142 110 143 // OpenGL stuff: … … 143 176 } 144 177 145 /+ Load of info-printing stuff (currently doesn't have a use)146 // Print a load of info:147 logger.info ("Available video modes:");148 char[128] tmp;149 SDL_Rect** modes = SDL_ListModes (null, SDL_FULLSCREEN);150 if (modes is null) logger.info ("None!");151 else if (modes is cast(SDL_Rect**) -1) logger.info ("All modes are available");152 else {153 for (uint i = 0; modes[i] !is null; ++i) {154 logger.info (logger.format (tmp, "\t{}x{}", modes[i].w, modes[i].h));155 }156 }157 158 SDL_VideoInfo* vi = SDL_GetVideoInfo ();159 if (vi !is null) {160 logger.info ("Video info:");161 logger.info ("Hardware surface support: "~ (vi.flags & SDL_HWSURFACE ? "yes" : "no"));162 logger.info (logger.format (tmp, "Video memory: {}", vi.video_mem));163 164 if (vi.vfmt !is null) {165 logger.info ("Best video mode:");166 logger.info (logger.format (tmp, "Bits per pixel: {}", vi.vfmt.BitsPerPixel));167 }168 }169 +/170 171 178 172 179 /** All video options. */
