1 /** 2 Allows the user to load and create bitmap fonts 3 4 To actually draw text on the screen use the Window draw functions 5 */ 6 module dgt.font; 7 import derelict.sdl2.sdl, derelict.sdl2.ttf; 8 import dgt.array, dgt.geom, dgt.io, dgt.texture, dgt.window; 9 import dgt.util : nullTerminate; 10 11 /** 12 An enum that determines character styling 13 */ 14 enum FontStyle : int 15 { 16 normal = TTF_STYLE_NORMAL, 17 bold = TTF_STYLE_BOLD, 18 italic = TTF_STYLE_ITALIC, 19 underline = TTF_STYLE_UNDERLINE, 20 strikethrough = TTF_STYLE_STRIKETHROUGH 21 } 22 23 /** 24 A structure that stores rendered font glyps for drawing on the screen 25 */ 26 struct Font 27 { 28 static immutable FONT_MAX_CHARS = 223; 29 static immutable FONT_CHAR_OFFSET = 32; 30 private Texture source; 31 private Array!Texture characterTextures; 32 private int height; 33 34 @disable this(); 35 @disable this(this); 36 37 @nogc nothrow public: 38 /** 39 Load a font from a TTF file with a given size and style 40 */ 41 this(in string filename, in int size, in FontStyle style) 42 { 43 //Pass the C function a null-terminated path to avoid string literal issues 44 auto pathNullTerminated = nullTerminate(filename); 45 TTF_Font* font = TTF_OpenFont(pathNullTerminated.ptr, size); 46 pathNullTerminated.destroy(); 47 TTF_SetFontStyle(font, style); 48 if (font == null) 49 println("Font with filename ", filename, " not found"); 50 SDL_Color color = SDL_Color(255, 255, 255, 255); 51 //Create a null-terminated buffer to send glyphs to the TTF library 52 char[2] buffer = ['\0', '\0']; 53 SDL_Surface*[FONT_MAX_CHARS] characters; 54 int total_width = 0; 55 height = 0; 56 //Render each ASCII character to a surface 57 for (int i = 0; i < FONT_MAX_CHARS; i++) 58 { 59 buffer[0] = getCharFromIndex(i); 60 //Render the character and note how much space it takes up 61 characters[i] = TTF_RenderText_Solid(font, buffer.ptr, color); 62 total_width += characters[i].w; 63 if (characters[i].h > height) 64 height = characters[i].h; 65 } 66 TTF_CloseFont(font); 67 //Blit all of the characters to a large surface 68 SDL_Surface* full = SDL_CreateRGBSurface(0, total_width, height, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); 69 float position = 0; 70 for (int i = 0; i < FONT_MAX_CHARS; i++) 71 { 72 SDL_Rect dest = SDL_Rect(cast(int)position, 0, 0, 0); 73 SDL_BlitSurface(characters[i], null, full, &dest); 74 position += characters[i].w; 75 } 76 //Load the surface into a texture 77 source = Texture(full); 78 SDL_FreeSurface(full); 79 //Add reference to the texture for each character 80 position = 0; 81 characterTextures = Array!Texture(FONT_MAX_CHARS); 82 for (int i = 0; i < FONT_MAX_CHARS; i++) 83 { 84 characterTextures.add(source.getSlice(Rectangle(position, 0, characters[i].w, characters[i].h))); 85 position += characterTextures[i].size.width; 86 SDL_FreeSurface(characters[i]); 87 } 88 } 89 90 ~this() 91 { 92 source.destroy(); 93 characterTextures.destroy(); 94 } 95 96 pure: 97 /// Get the glyph for a given character 98 Texture render(in char c) const 99 { 100 return characterTextures[getIndexFromChar(c)]; 101 } 102 103 ///Find how much space a string would take when rendered 104 Rectangle getSizeOfString(in string str, float lineHeight = 1) const 105 { 106 float position = 0; 107 float width = 0, height = cast(int)(this.height * lineHeight); 108 for(size_t i = 0; i < str.length; i++) 109 { 110 char c = str[i]; 111 if (position > width) 112 width = position; 113 //a tab is equivalent to 4 space characters 114 if (c == '\t') 115 position += 4 * render(' ').size.width; 116 //Move down a line 117 else if (c == '\n') 118 { 119 height += cast(int)(characterHeight * lineHeight); 120 position = 0; 121 } 122 else if (c != '\r') 123 position += render(c).size.width; 124 } 125 return Rectangle(0, 0, width, height); 126 } 127 128 //Get the pixel height of the characters in the font 129 @property int characterHeight() const { return height; } 130 131 private int getIndexFromChar(in char c) const 132 { 133 return c - FONT_CHAR_OFFSET; 134 } 135 136 private char getCharFromIndex(in int index) const 137 { 138 return cast(char)(index + FONT_CHAR_OFFSET); 139 } 140 }