1 module gfm.sdl2.sdlttf;
2 
3 import std..string;
4 
5 import bindbc.sdl,
6        bindbc.sdl.ttf;
7 
8 import std.experimental.logger;
9 
10 import gfm.sdl2.sdl,
11        gfm.sdl2.surface;
12 
13 /// SDL_ttf library wrapper.
14 final class SDLTTF
15 {
16     public
17     {
18         /// Loads the SDL_ttf library.
19         /// Throws: $(D SDL2Exception) on error.
20         this(SDL2 sdl2)
21         {
22             _sdl2 = sdl2; // force loading of SDL first
23             _logger = sdl2._logger;
24             _SDLTTFInitialized = false;
25 
26             const ret = loadSDLTTF();
27             if(ret < sdlTTFSupport)
28             {
29                 if(ret == SDLTTFSupport.noLibrary)
30                     throwSDL2TTFException("SDL TTF shared library failed to load");
31                 else if(SDLTTFSupport.badLibrary)
32                     // One or more symbols failed to load. The likely cause is that the
33                     // shared library is for a lower version than bindbc-sdl was configured
34                     // to load (via SDL_201, SDL_202, etc.)
35                     throwSDL2TTFException("One or more symbols of SDL TTF shared library failed to load");
36                 throwSDL2TTFException("The version of the SDL TTF library on your system is too low. Please upgrade.");
37             }
38 
39             int res = TTF_Init();
40             if (res != 0)
41                 throwSDL2TTFException("TTF_Init");
42 
43             _SDLTTFInitialized = true;
44         }
45 
46         /// Releases the SDL_ttf library.
47         ~this()
48         {
49             if (_SDLTTFInitialized)
50             {
51                 debug ensureNotInGC("SDLTTF");
52                 _SDLTTFInitialized = false;
53                 TTF_Quit();
54             }
55         }
56     }
57 
58     private
59     {
60         Logger _logger;
61         SDL2 _sdl2;
62         bool _SDLTTFInitialized;
63 
64         void throwSDL2TTFException(string callThatFailed)
65         {
66             string message = format("%s failed: %s", callThatFailed, getErrorString());
67             throw new SDL2Exception(message);
68         }
69 
70         const(char)[] getErrorString()
71         {
72             return fromStringz(TTF_GetError());
73         }
74     }
75 }
76 
77 /// SDL_ttf loaded font wrapper.
78 final class SDLFont
79 {
80     public
81     {
82         /// Loads a font from a file.
83         /// Params:
84         ///     sdlttf = library object.
85         ///     filename = path of the font file.
86         ///     ptSize = font size in 72 dpi ("This basically translates to pixel height" says the doc).
87         /// Throws: $(D SDL2Exception) on error.
88         this(SDLTTF sdlttf, string filename, int ptSize)
89         {
90             _sdlttf = sdlttf;
91             _font = TTF_OpenFont(toStringz(filename), ptSize);
92             if (_font is null)
93                 _sdlttf.throwSDL2TTFException("TTF_OpenFont");
94         }
95 
96         /// Releases the SDL resource.
97         ~this()
98         {
99             if (_font !is null)
100             {
101                 debug ensureNotInGC("SDLFont");
102                 TTF_CloseFont(_font);
103                 _font = null;
104             }
105         }
106 
107         /// Returns: Font style.
108         int style()
109         {
110             return TTF_GetFontStyle(_font);
111         }
112 
113         /// Set font style.
114         int setStyle(int newStyle)
115         {
116             if (newStyle != TTF_GetFontStyle(_font))
117                 TTF_SetFontStyle(_font, newStyle);
118             return newStyle;
119         }
120 
121         /// Returns: Font hinting.
122         int hinting()
123         {
124             return TTF_GetFontHinting(_font);
125         }
126 
127         /// Set font hinting.
128         int setHinting(int newHinting)
129         {
130             if (newHinting != TTF_GetFontHinting(_font))
131                 TTF_SetFontHinting(_font, newHinting);
132             return newHinting;
133         }
134 
135         /// Returns: Font outline.
136         int outline()
137         {
138             return TTF_GetFontOutline(_font);
139         }
140 
141         /// Set font outline.
142         int setOutline(int newOutline)
143         {
144             if (newOutline != TTF_GetFontOutline(_font))
145                 TTF_SetFontOutline(_font, newOutline);
146             return newOutline;
147         }
148 
149         /// Returns: true if kerning is enabled.
150         bool getKerning()
151         {
152             return TTF_GetFontKerning(_font) != 0;
153         }
154 
155         /// Enables/Disables font kerning.
156         bool setKerning(bool enabled)
157         {
158             TTF_SetFontKerning(_font, enabled ? 1 : 0);
159             return enabled;
160         }
161 
162         /// Returns: Maximum height of a glyph in pixels.
163         int height()
164         {
165             return TTF_FontAscent(_font);
166         }
167 
168         /// Returns: Height above baseline in pixels.
169         int ascent()
170         {
171             return TTF_FontAscent(_font);
172         }
173 
174         /// Returns: Height below baseline.
175         int descent()
176         {
177             return TTF_FontDescent(_font);
178         }
179 
180         /// Returns: Line skip, the recommended pixel interval between two lines.
181         int lineSkip()
182         {
183             return TTF_FontLineSkip(_font);
184         }
185 
186         /// Returns: Size of text in pixels if rendered with this font.
187         SDL_Point measureText(string text)
188         {
189             int w, h;
190             TTF_SizeUTF8(_font, toStringz(text), &w, &h);
191             return SDL_Point(w, h);
192         }
193 
194         /// Create a 32-bit ARGB surface and render the given character at high quality,
195         /// using alpha blending to dither the font with the given color.
196         /// Throws: $(D SDL2Exception) on error.
197         SDL2Surface renderGlyphBlended(dchar ch, SDL_Color color)
198         {
199             return checkedSurface(TTF_RenderGlyph_Blended(_font, cast(ushort)ch, color));
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         /// Create a 32-bit ARGB surface and render the given text at high quality,
227         /// using alpha blending to dither the font with the given color.
228         /// Uses multi-line text wrapping.
229         /// Throws: $(D SDL2Exception) on error.
230         SDL2Surface renderTextBlendedWrapped(string text, SDL_Color color, uint wrapLength)
231         {
232             return checkedSurface(TTF_RenderUTF8_Blended_Wrapped(_font, toStringz(text), color, wrapLength));
233         }
234     }
235 
236     private
237     {
238         SDLTTF _sdlttf;
239         TTF_Font *_font;
240 
241         SDL2Surface checkedSurface(SDL_Surface* s)
242         {
243             if (s is null)
244                 _sdlttf.throwSDL2TTFException("TTF_Render");
245             return new SDL2Surface(_sdlttf._sdl2, s, SDL2Surface.Owned.YES);
246         }
247     }
248 }