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