1 module gfm.sdl2.sdlttf;
2 
3 import std.string;
4 
5 import derelict.sdl2.sdl,
6        derelict.sdl2.ttf,
7        derelict.util.exception;
8 
9 import std.logger;
10 
11 import gfm.core.text,
12        gfm.math.vector,
13        gfm.sdl2.sdl,
14        gfm.sdl2.surface;
15 
16 /// SDL_ttf library wrapper.
17 final class SDLTTF
18 {
19     public
20     {
21         /// Loads the SDL_ttf library.
22         /// Throws: $(D SDL2Exception) on error.
23         this(SDL2 sdl2)
24         {
25             _sdl2 = sdl2; // force loading of SDL first
26             _logger = sdl2._logger;
27             _SDLTTFInitialized = false;
28 
29             try
30             {
31                 DerelictSDL2ttf.load();
32             }
33             catch(DerelictException e)
34             {
35                 throw new SDL2Exception(e.msg);
36             }
37 
38             int res = TTF_Init();
39             if (res != 0)
40                 throwSDL2TTFException("TTF_Init");
41 
42              _logger.infof("SDL_ttf: initialized.");
43             _SDLTTFInitialized = true;
44         }
45 
46         /// Releases the SDL_ttf library.
47         void close()
48         {
49             if (_SDLTTFInitialized)
50             {
51                 _SDLTTFInitialized = false;
52                 TTF_Quit();
53             }
54 
55             DerelictSDL2ttf.unload();
56         }
57 
58         ~this()
59         {
60             close();
61         }   
62     }
63 
64     private
65     {
66         Logger _logger;
67         SDL2 _sdl2;
68         bool _SDLTTFInitialized;
69 
70         void throwSDL2TTFException(string callThatFailed)
71         {
72             string message = format("%s failed: %s", callThatFailed, getErrorString());
73             throw new SDL2Exception(message);
74         }
75 
76         string getErrorString()
77         {
78             return sanitizeUTF8(TTF_GetError(), _logger, "SDL_TTF error string");
79         }
80     }
81 }
82 
83 /// SDL_ttf loaded font wrapper.
84 final class SDLFont
85 {
86     public
87     {
88         /// Loads a font from a file.
89         /// Params:
90         ///     ptSize = font size in 72 dpi ("This basically translates to pixel height" says the doc).
91         /// Throws: $(D SDL2Exception) on error.
92         this(SDLTTF sdlttf, string filename, int ptSize)
93         {
94             _sdlttf = sdlttf;
95             _font = TTF_OpenFont(toStringz(filename), ptSize);
96             if (_font is null)
97                 _sdlttf.throwSDL2TTFException("TTF_OpenFont");
98         }
99 
100         ~this()
101         {
102             close();
103         }
104 
105         /// Releases the SDL resource.
106         void close()
107         {
108             if (_font !is null)
109             {
110                 TTF_CloseFont(_font);
111                 _font = null;
112             }
113         }
114 
115         /// Returns: Font style.
116         int style()
117         {
118             return TTF_GetFontStyle(_font);
119         }
120 
121         /// Set font style.
122         int setStyle(int newStyle)
123         {
124             if (newStyle != TTF_GetFontStyle(_font))
125                 TTF_SetFontStyle(_font, newStyle);
126             return newStyle;
127         }
128 
129         /// Returns: Font hinting.
130         int hinting()
131         {
132             return TTF_GetFontHinting(_font);
133         }
134 
135         /// Set font hinting.
136         int setHinting(int newHinting)
137         {
138             if (newHinting != TTF_GetFontHinting(_font))
139                 TTF_SetFontHinting(_font, newHinting);
140             return newHinting;
141         }
142 
143         /// Returns: Font outline.
144         int outline()
145         {
146             return TTF_GetFontOutline(_font);
147         }
148 
149         /// Set font outline.
150         int setOutline(int newOutline)
151         {
152             if (newOutline != TTF_GetFontOutline(_font))
153                 TTF_SetFontOutline(_font, newOutline);
154             return newOutline;
155         }
156 
157         /// Returns: true if kerning is enabled.
158         bool getKerning()
159         {
160             return TTF_GetFontKerning(_font) != 0;
161         }
162 
163         /// Enables/Disables font kerning.
164         bool setKerning(bool enabled)
165         {
166             TTF_SetFontKerning(_font, enabled ? 1 : 0);
167             return enabled;
168         }
169 
170         /// Returns: Maximum height of a glyph in pixels.
171         int height()
172         {
173             return TTF_FontAscent(_font);
174         }
175 
176         /// Returns: Height above baseline in pixels.
177         int ascent()
178         {
179             return TTF_FontAscent(_font);
180         }
181 
182         /// Returns: Height below baseline.
183         int descent()
184         {
185             return TTF_FontDescent(_font);
186         }
187 
188         /// Returns: Line skip, the recommended pixel interval between two lines.
189         int lineSkip()
190         {
191             return TTF_FontLineSkip(_font);
192         }
193 
194         /// Returns: Size of text in pixels if rendered with this font.
195         vec2i measureText(string text)
196         {
197             int w, h;
198             TTF_SizeUTF8(_font, toStringz(text), &w, &h);
199             return vec2i(w, h);
200         }
201 
202         /// Create a 32-bit ARGB surface and render the given text at high quality, 
203         /// using alpha blending to dither the font with the given color.
204         /// Throws: $(D SDL2Exception) on error.
205         SDL2Surface renderTextBlended(string text, SDL_Color color)
206         {
207             return checkedSurface(TTF_RenderUTF8_Blended(_font, toStringz(text), color));
208         }
209 
210         /// Create an 8-bit palettized surface and render the given text at fast 
211         /// quality with the given font and color.
212         /// Throws: $(D SDL2Exception) on error.
213         SDL2Surface renderTextSolid(string text, SDL_Color color)
214         {
215             return checkedSurface(TTF_RenderUTF8_Solid(_font, toStringz(text), color));
216         }
217 
218         /// Create an 8-bit palettized surface and render the given text at high 
219         /// quality with the given font and colors.
220         /// Throws: $(D SDL2Exception) on error.
221         SDL2Surface renderTextShaded(string text, SDL_Color fg, SDL_Color bg)
222         {
223             return checkedSurface(TTF_RenderUTF8_Shaded(_font, toStringz(text), fg, bg));
224         }
225     }
226 
227     private
228     {
229         SDLTTF _sdlttf;
230         TTF_Font *_font;
231 
232         SDL2Surface checkedSurface(SDL_Surface* s)
233         {
234             if (s is null)
235                 _sdlttf.throwSDL2TTFException("TTF_Render");
236             return new SDL2Surface(_sdlttf._sdl2, s, SDL2Surface.Owned.YES);
237         }
238     }
239 }