ObjectivelyMVC 0.1.0
Object oriented MVC framework for OpenGL, SDL2 and GNU C
Font.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for OpenGL, SDL2 and GNU 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 <string.h>
26
27#include <Objectively/Hash.h>
28#include <Objectively/MutableArray.h>
29#include <Objectively/String.h>
30
31#include "Font.h"
32#include "Log.h"
33#include "View.h"
34#include "Window.h"
35
36#include "coda.ttf.h"
37
38const EnumName FontStyleNames[] = MakeEnumNames(
39 MakeEnumAlias(FontStyleRegular, regular),
40 MakeEnumAlias(FontStyleBold, bold),
41 MakeEnumAlias(FontStyleItalic, italic),
42 MakeEnumAlias(FontStyleUnderline, underline),
43 MakeEnumAlias(FontStyleStrikeThrough, strikethrough)
44);
45
46#define _Class _Font
47
48#pragma mark - Object
49
53static void dealloc(Object *self) {
54
55 Font *this = (Font *) self;
56
57 free(this->family);
58
59 release(this->data);
60
61 TTF_CloseFont(this->font);
62
63 super(Object, self, dealloc);
64}
65
69static int hash(const Object *self) {
70
71 Font *this = (Font *) self;
72
73 int hash = HASH_SEED;
74 hash = HashForCString(hash, this->family);
75 hash = HashForInteger(hash, this->size);
76 hash = HashForInteger(hash, this->style);
77 hash = HashForInteger(hash, this->renderSize);
78
79 return hash;
80}
81
85static _Bool isEqual(const Object *self, const Object *other) {
86
87 if (super(Object, self, isEqual, other)) {
88 return true;
89 }
90
91 if (other && $(other, isKindOfClass, _Font())) {
92
93 const Font *this = (Font *) self;
94 const Font *that = (Font *) other;
95
96 if (!strcmp(this->family, that->family) &&
97 this->size == that->size &&
98 this->style == that->style &&
99 this->renderSize == that->renderSize) {
100 return true;
101 }
102 }
103
104 return false;
105}
106
107#pragma mark - Font
108
109static MutableDictionary *_cache;
110static MutableArray *_fonts;
111
116static void cacheFont(Data *data, const char *family) {
117 $(_cache, setObjectForKeyPath, data, family);
118}
119
124static Font *cachedFont(const char *family, int size, int style) {
125
126 if (family == NULL) {
127 family = DEFAULT_FONT_FAMILY;
128 }
129 if (size < 1) {
131 }
132 if (style < FontStyleRegular || style > FontStyleStrikeThrough) {
133 style = DEFAULT_FONT_STYLE;
134 }
135
136 const int renderSize = size * MVC_WindowScale(NULL, NULL, NULL);
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 font->renderSize == renderSize) {
147 return font;
148 }
149 }
150
151 Data *data = $((Dictionary *) _cache, objectForKeyPath, family);
152 if (data) {
153 Font *font = $(alloc(Font), initWithData, data, family, size, style);
154 assert(font);
155
156 $(_fonts, addObject, font);
157 release(font);
158
159 return font;
160 }
161
162 MVC_LogWarn("%s-%d-%d not found\n", family, size, style);
163 return $$(Font, defaultFont);
164}
165
170static void clearCache(void) {
171 $(_cache, removeAllObjects);
172}
173
178static Font *defaultFont(void) {
179 static Once once;
180
181 do_once(&once, {
182 Data *data = $(alloc(Data), initWithConstMemory, coda_ttf, coda_ttf_len);
183 assert(data);
184
186
187 release(data);
188 });
189
191}
192
197static Font *initWithData(Font *self, Data *data, const char *family, int size, int style) {
198
199 self = (Font *) super(Object, self, init);
200 if (self) {
201
202 self->data = retain(data);
203 assert(self->data);
204
205 self->family = strdup(family);
206 assert(self->family);
207
208 self->size = size;
209 assert(self->size);
210
211 self->style = style;
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_RenderUTF8_Blended_Wrapped(self->font, chars, color, wrapWidth * MVC_WindowScale(NULL, NULL, NULL));
228 } else {
229 surface = TTF_RenderUTF8_Blended(self->font, chars, color);
230 }
231
232 SDL_Surface *converted = NULL;
233 if (surface) {
234 converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32, 0);
235 SDL_FreeSurface(surface);
236 } else {
237 MVC_LogError("%s\n", TTF_GetError());
238 }
239
240 return converted;
241}
242
247static void renderDeviceDidReset(Font *self) {
248
249 const int renderSize = self->size * MVC_WindowScale(NULL, NULL, NULL);
250 if (renderSize != self->renderSize) {
251
252 self->renderSize = renderSize;
253
254 if (self->font) {
255 TTF_CloseFont(self->font);
256 }
257
258 SDL_RWops *buffer = SDL_RWFromConstMem(self->data->bytes, (int) self->data->length);
259 assert(buffer);
260
261 self->font = TTF_OpenFontRW(buffer, 1, self->renderSize);
262 assert(self->font);
263
264 TTF_SetFontStyle(self->font, self->style);
265 }
266}
267
272static void sizeCharacters(const Font *self, const char *chars, int *w, int *h) {
273
274 if (w) {
275 *w = 0;
276 }
277 if (h) {
278 *h = 0;
279 }
280
281 if (chars) {
282 char *lines = strdup(chars);
283
284 for (char *line = strtok(lines, "\n\r"); line; line = strtok(NULL, "\n\r")) {
285
286 int line_w, line_h;
287 TTF_SizeUTF8(self->font, line, &line_w, &line_h);
288
289 if (w) {
290 *w = max(*w, line_w);
291 }
292 if (h) {
293 *h += line_h;
294 }
295 }
296 free(lines);
297
298 const float scale = MVC_WindowScale(NULL, NULL, NULL);
299 if (w) {
300 *w = ceil(*w / scale);
301 }
302 if (h) {
303 *h = ceil(*h / scale);
304 }
305 }
306}
307
308#pragma mark - Class lifecycle
309
313static void initialize(Class *clazz) {
314
315 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
316 ((ObjectInterface *) clazz->interface)->hash = hash;
317 ((ObjectInterface *) clazz->interface)->isEqual = isEqual;
318
319 ((FontInterface *) clazz->interface)->cachedFont = cachedFont;
320 ((FontInterface *) clazz->interface)->cacheFont = cacheFont;
321 ((FontInterface *) clazz->interface)->clearCache = clearCache;
322 ((FontInterface *) clazz->interface)->defaultFont = defaultFont;
323 ((FontInterface *) clazz->interface)->initWithData = initWithData;
324 ((FontInterface *) clazz->interface)->renderCharacters = renderCharacters;
325 ((FontInterface *) clazz->interface)->renderDeviceDidReset = renderDeviceDidReset;
326 ((FontInterface *) clazz->interface)->sizeCharacters = sizeCharacters;
327
328 const int err = TTF_Init();
329 assert(err == 0);
330
331 _cache = $$(MutableDictionary, dictionary);
332 assert(_cache);
333
334 _fonts = $$(MutableArray, array);
335 assert(_fonts);
336}
337
341static void destroy(Class *clazz) {
342
343 release(_cache);
344 release(_fonts);
345
346 TTF_Quit();
347}
348
353Class *_Font(void) {
354 static Class *clazz;
355 static Once once;
356
357 do_once(&once, {
358 clazz = _initialize(&(const ClassDef) {
359 .name = "Font",
360 .superclass = _Object(),
361 .instanceSize = sizeof(Font),
362 .interfaceOffset = offsetof(Font, interface),
363 .interfaceSize = sizeof(FontInterface),
365 .destroy = destroy,
366 });
367 });
368
369 return clazz;
370}
371
372#undef _Class
static void destroy(Class *clazz)
Definition: Font.c:341
static _Bool isEqual(const Object *self, const Object *other)
Definition: Font.c:85
static void dealloc(Object *self)
Definition: Font.c:53
const EnumName FontStyleNames[]
Definition: Font.c:38
static void initialize(Class *clazz)
Definition: Font.c:313
static MutableDictionary * _cache
Definition: Font.c:109
static MutableArray * _fonts
Definition: Font.c:110
static int hash(const Object *self)
Definition: Font.c:69
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
View logging facilities via SDL_Log.
#define MVC_LogWarn(fmt,...)
Definition: Log.h:52
#define MVC_LogError(fmt,...)
Definition: Log.h:55
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
double MVC_WindowScale(SDL_Window *window, int *height, int *drawableHeight)
Resolves the scale factor of the specified window for High-DPI support.
Definition: Window.c:47
CollectionView * init(CollectionView *self, const SDL_Rect *frame)
Initializes this CollectionView with the specified frame and style.
TrueType fonts.
Definition: Font.h:63
void(* cacheFont)(Data *data, const char *family)
Caches the specified font Data.
Definition: Font.h:136
ident font
The backing font.
Definition: Font.h:89
int renderSize
The render size, adjusted for display density.
Definition: Font.h:94
Font * cachedFont(const char *family, int size, int style)
Resolves the cached Font with the given attributes.
Definition: Font.c:124
void renderCharacters(const Font *self, const char *chars, SDL_Color color, int wrapWidth)
Renders the given characters in this Font.
Definition: Font.c:223
void renderDeviceDidReset(Font *self)
This method should be invoked when the render context is invalidated.
Definition: Font.c:247
char * family
The family name.
Definition: Font.h:84
void clearCache(void)
Clears the Font cache.
Definition: Font.c:170
Font * defaultFont(void)
Definition: Font.c:178
Font * initWithData(Font *self, Data *data, int size, int index)
Class * _Font(void)
The Font archetype.
Definition: Font.c:353
int size
The point size.
Definition: Font.h:99
void sizeCharacters(const Font *self, const char *chars, int *w, int *h)
Definition: Font.c:272
Data * data
The raw font data.
Definition: Font.h:79
int style
The style.
Definition: Font.h:104
SDL_Size size(const Image *self)
Definition: Image.c:181