ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Font.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for SDL3 and C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include <assert.h>
25#include <math.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <Objectively/Hash.h>
30#include <Objectively/Array.h>
31#include <Objectively/String.h>
32
33#include "Font.h"
34#include "Log.h"
35#include "View.h"
36#include "Window.h"
37
38#include "../Assets/coda.ttf.h"
39
40const EnumName FontStyleNames[] = MakeEnumNames(
41 MakeEnumAlias(FontStyleRegular, regular),
42 MakeEnumAlias(FontStyleBold, bold),
43 MakeEnumAlias(FontStyleItalic, italic),
44 MakeEnumAlias(FontStyleUnderline, underline),
45 MakeEnumAlias(FontStyleStrikeThrough, strikethrough)
46);
47
48#define _Class _Font
49
50#pragma mark - Object
51
55static void dealloc(Object *self) {
56
57 Font *this = (Font *) self;
58
59 free(this->family);
60
61 release(this->data);
62
63 TTF_CloseFont(this->font);
64
65 super(Object, self, dealloc);
66}
67
71static int hash(const Object *self) {
72
73 Font *this = (Font *) self;
74
75 int hash = HASH_SEED;
76 hash = HashForCString(hash, this->family);
77 hash = HashForInteger(hash, this->size);
78 hash = HashForInteger(hash, this->style);
79 hash = HashForInteger(hash, this->renderSize);
80
81 return hash;
82}
83
87static bool isEqual(const Object *self, const Object *other) {
88
89 if (super(Object, self, isEqual, other)) {
90 return true;
91 }
92
93 if (other && $(other, isKindOfClass, _Font())) {
94
95 const Font *this = (Font *) self;
96 const Font *that = (Font *) other;
97
98 if (!strcmp(this->family, that->family) &&
99 this->size == that->size &&
100 this->style == that->style &&
101 this->renderSize == that->renderSize) {
102 return true;
103 }
104 }
105
106 return false;
107}
108
109#pragma mark - Font
110
111static Dictionary *_cache;
112static Array *_fonts;
113
118static void cacheFont(Data *data, const char *family) {
119 $(_cache, setObjectForKeyPath, data, family);
120}
121
126static Font *cachedFont(const char *family, int size, int style) {
127
128 if (family == NULL) {
129 family = DEFAULT_FONT_FAMILY;
130 }
131 if (size < 1) {
133 }
134 if (style < FontStyleRegular || style > FontStyleStrikeThrough) {
135 style = DEFAULT_FONT_STYLE;
136 }
137
138 const Array *fonts = (Array *) _fonts;
139 for (size_t i = 0; i < fonts->count; i++) {
140
141 Font *font = $(fonts, objectAtIndex, i);
142
143 if (!strcmp(font->family, family) &&
144 font->size == size &&
145 font->style == style) {
146 return font;
147 }
148 }
149
150 Data *data = $((Dictionary *) _cache, objectForKeyPath, family);
151 if (data) {
152 Font *font = $(alloc(Font), initWithData, data, family, size, style);
153 assert(font);
154
155 $(_fonts, addObject, font);
156 release(font);
157
158 return font;
159 }
160
161 MVC_LogWarn("%s-%d-%d not found\n", family, size, style);
162 return $$(Font, defaultFont);
163}
164
169static void clearCache(void) {
170 $(_cache, removeAllObjects);
171}
172
177static Font *defaultFont(void) {
178 static Once once;
179
180 do_once(&once, {
181 Data *data = $(alloc(Data), initWithConstMemory, coda_ttf, coda_ttf_len - 1);
182 assert(data);
183
185
186 release(data);
187 });
188
190}
191
196static Font *initWithData(Font *self, Data *data, const char *family, int size, int style) {
197
198 self = (Font *) super(Object, self, init);
199 if (self) {
200
201 self->data = retain(data);
202 assert(self->data);
203
204 self->family = strdup(family);
205 assert(self->family);
206
207 self->size = size;
208 assert(self->size);
209
210 self->style = style;
211 self->scale = 1.0f;
212
213 $(self, renderDeviceDidReset);
214 }
215
216 return self;
217}
218
223static SDL_Surface *renderCharacters(const Font *self, const char *chars, SDL_Color color, int wrapWidth) {
224
225 SDL_Surface *surface;
226 if (wrapWidth) {
227 surface = TTF_RenderText_Blended_Wrapped(self->font, chars, 0, color, wrapWidth * self->scale);
228 } else {
229 surface = TTF_RenderText_Blended(self->font, chars, 0, color);
230 }
231
232 SDL_Surface *converted = NULL;
233 if (surface) {
234 converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32);
235 SDL_DestroySurface(surface);
236 } else {
237 MVC_LogError("%s\n", SDL_GetError());
238 }
239
240 return converted;
241}
242
247static void renderDeviceDidReset(Font *self) {
248
249 const int renderSize = self->size * self->scale;
250 if (renderSize != self->renderSize) {
251
252 self->renderSize = renderSize;
253
254 if (self->font) {
255 TTF_CloseFont(self->font);
256 }
257
258 SDL_IOStream *buffer = SDL_IOFromConstMem(self->data->bytes, (int) self->data->length);
259 assert(buffer);
260
261 self->font = TTF_OpenFontIO(buffer, 1, self->renderSize);
262 assert(self->font);
263
264 TTF_SetFontStyle(self->font, self->style);
265 TTF_SetFontHinting(self->font, TTF_HINTING_LIGHT_SUBPIXEL);
266 }
267}
268
273static void sizeCharacters(const Font *self, const char *chars, int *w, int *h) {
274
275 if (w) {
276 *w = 0;
277 }
278 if (h) {
279 *h = 0;
280 }
281
282 if (chars) {
283 char *lines = strdup(chars);
284
285 const int font_h = TTF_GetFontHeight(self->font);
286
287 for (char *line = strtok(lines, "\n\r"); line; line = strtok(NULL, "\n\r")) {
288
289 if (w) {
290 int line_w;
291 TTF_GetStringSize(self->font, line, 0, &line_w, NULL);
292 *w = max(*w, line_w);
293 }
294 if (h) {
295 *h += font_h;
296 }
297 }
298 free(lines);
299
300 if (w) {
301 *w = ceilf(*w / self->scale);
302 }
303 if (h) {
304 *h = ceilf(*h / self->scale);
305 }
306 }
307}
308
309#pragma mark - Class lifecycle
310
314static void initialize(Class *clazz) {
315
316 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
317 ((ObjectInterface *) clazz->interface)->hash = hash;
318 ((ObjectInterface *) clazz->interface)->isEqual = isEqual;
319
320 ((FontInterface *) clazz->interface)->cachedFont = cachedFont;
321 ((FontInterface *) clazz->interface)->cacheFont = cacheFont;
322 ((FontInterface *) clazz->interface)->clearCache = clearCache;
323 ((FontInterface *) clazz->interface)->defaultFont = defaultFont;
324 ((FontInterface *) clazz->interface)->initWithData = initWithData;
325 ((FontInterface *) clazz->interface)->renderCharacters = renderCharacters;
326 ((FontInterface *) clazz->interface)->renderDeviceDidReset = renderDeviceDidReset;
327 ((FontInterface *) clazz->interface)->sizeCharacters = sizeCharacters;
328
329 const bool init = TTF_Init();
330 assert(init);
331 (void) init;
332
333 _cache = $$(Dictionary, dictionary);
334 assert(_cache);
335
336 _fonts = $$(Array, array);
337 assert(_fonts);
338}
339
343static void destroy(Class *clazz) {
344
345 release(_cache);
346 release(_fonts);
347
348 TTF_Quit();
349}
350
355Class *_Font(void) {
356 static Class *clazz;
357 static Once once;
358
359 do_once(&once, {
360 clazz = _initialize(&(const ClassDef) {
361 .name = "Font",
362 .superclass = _Object(),
363 .instanceSize = sizeof(Font),
364 .interfaceOffset = offsetof(Font, interface),
365 .interfaceSize = sizeof(FontInterface),
367 .destroy = destroy,
368 });
369 });
370
371 return clazz;
372}
373
374#undef _Class
static View * init(View *self)
Definition Box.c:67
static void destroy(Class *clazz)
Definition Font.c:343
static void sizeCharacters(const Font *self, const char *chars, int *w, int *h)
Definition Font.c:273
static SDL_Surface * renderCharacters(const Font *self, const char *chars, SDL_Color color, int wrapWidth)
Definition Font.c:223
static void clearCache(void)
Definition Font.c:169
static Font * cachedFont(const char *family, int size, int style)
Definition Font.c:126
static bool isEqual(const Object *self, const Object *other)
Definition Font.c:87
Class * _Font(void)
Definition Font.c:355
static void renderDeviceDidReset(Font *self)
Definition Font.c:247
static Font * initWithData(Font *self, Data *data, const char *family, int size, int style)
Definition Font.c:196
static void dealloc(Object *self)
Definition Font.c:55
static Dictionary * _cache
Definition Font.c:111
const EnumName FontStyleNames[]
Definition Font.c:40
static void initialize(Class *clazz)
Definition Font.c:314
static void cacheFont(Data *data, const char *family)
Definition Font.c:118
static Array * _fonts
Definition Font.c:112
static int hash(const Object *self)
Definition Font.c:71
static Font * defaultFont(void)
Definition Font.c:177
TrueType fonts.
#define DEFAULT_FONT_STYLE
Definition Font.h:36
@ FontStyleUnderline
Definition Font.h:50
@ FontStyleBold
Definition Font.h:48
@ FontStyleStrikeThrough
Definition Font.h:51
@ FontStyleRegular
Definition Font.h:47
@ FontStyleItalic
Definition Font.h:49
#define DEFAULT_FONT_FAMILY
Definition Font.h:34
#define DEFAULT_FONT_SIZE
Definition Font.h:35
static SDL_Size size(const Image *self)
Definition Image.c:181
View logging facilities via SDL_Log.
#define MVC_LogWarn(fmt,...)
Definition Log.h:54
#define MVC_LogError(fmt,...)
Definition Log.h:57
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
TrueType fonts.
Definition Font.h:63
void(* cacheFont)(Data *data, const char *family)
Caches the specified font Data.
Definition Font.h:142
float scale
The display pixel density scale (e.g. 2.0 on Retina). Updated by callers via the scale field before i...
Definition Font.h:100
int renderSize
The render size, adjusted for display density.
Definition Font.h:94
char * family
The family name.
Definition Font.h:84
void renderCharacters(const Font *self, const char *chars, SDL_Color color, int wrapWidth)
Renders the given characters in this Font.
Definition Font.c:223
Font * initWithData(Font *self, Data *data, int size, int index)
int size
The point size.
Definition Font.h:105
TTF_Font * font
The backing font.
Definition Font.h:89
Data * data
The raw font data.
Definition Font.h:79
int style
The style.
Definition Font.h:110