-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSDL_VTXT.h
392 lines (313 loc) · 10.5 KB
/
SDL_VTXT.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
/*
SDL_VTXT by Gustavo Aranda (https://github.com/gusarba)
A quick and dirty SDL_Renderer wrapper for the awesome vertext library by Kevin Chin
(https://github.com/kevinmkchin/vertext). It allows the vertetx objects to be
rendered by SDL_Renderer as geometry. It only supports Screen Space coordinates.
Requires both stb_truetype.h (https://github.com/nothings/stb/blob/master/stb_truetype.h)
and vertext.h . Do this:
#define SDL_VTXT_IMPLEMENTATION
before you include this file in *one* C or C++ file to create the implementation.
// i.e. it should look like this:
#include ...
#include ...
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
#define VERTEXT_IMPLEMENTATION
#include "vertext.h"
#define SDL_VTXT_IMPLEMENTATION
#include "SDL_VTXT.h"
See the accompanying file "example.c" to see a practical example.
USAGE:
Create a SDL_VTXT object like so:
SDL_VTXT* vf = SDL_VTXT_Init(renderer, 24, "../data/sweet16mono.ttf");
You can set the cursor, add some text, new lines, glyphs, etc...:
SDL_VTXT_SetCursor(vf, 100, 200);
SDL_VTXT_AppendLine(vf, "The quick brown fox");
SDL_VTXT_NewLine(vf, 100);
SDL_VTXT_AppendLine(vf, "jumps over the lazy dog");
SDL_VTXT_AppendGlyph(vf, '.');
Those operations will be stored in an internal command buffer that will
execute them in the render phase.
Somewhere before calling SDL_RenderPresent() , call:
SDL_VTXT_Render(vf, renderer);
To render the SDL_VTXT object.
*/
#ifndef __SDL_VTXT__
#define __SDL_VTXT__
#if !SDL_VERSION_ATLEAST(2,0,17)
#error This library requires SDL 2.0.17+ because of SDL_RenderGeometry() function
#endif
#ifdef SDL_VTXT_STATIC
#define SDL_VTXT_DEF static
#else
#define SDL_VTXT_DEF extern
#endif
/** VTXTOperation is an enum representing the operations that can be done
* with a vertext object: add line, add new line and add glyph.
*/
typedef enum VTXTOperation {
kVTXTOperation_AppendLine,
kVTXTOperation_NewLine,
kVTXTOperation_AppendGlyph
} VTXTOperation;
/** VTXTCommand is a command containing an operation and the possible arguments
* for that operation.
*/
typedef struct VTXTCommand {
VTXTOperation op;
const char* line;
int cursor_x;
char glyph;
struct VTXTCommand* next;
} VTXTCommand;
/** SDL_VTXT is the main data type of the library, representing a vertext
* object that can be renderer with a SDL_Renderer.
* It contains graphic objects like a SDL_Texture and a SDL_Color buffer
* that is resized on the fly to match the vertex buffer.
*/
typedef struct SDL_VTXT {
int text_size;
int cursor_x;
int cursor_y;
SDL_Color text_color;
vtxt_font font_handle;
Uint32 pixel_format;
SDL_Texture* font_tex;
SDL_Color* color_buffer;
int color_array_count;
struct VTXTCommand* commands;
} SDL_VTXT;
/** Creates and initializes a SDL_VTXT object.
* @return NULL if failure.
*/
SDL_VTXT_DEF SDL_VTXT* SDL_VTXT_Init(SDL_Renderer* renderer, int text_size, const char* font_filename);
/** Sets the text color of an SDL_VTXT object.
*
* The color tinting is done by changing the color values of the vertices.
*/
SDL_VTXT_DEF int SDL_VTXT_SetColor(SDL_VTXT* vfont, SDL_Color color);
/** Sets the drawing cursor of an SDL_VTXT object.
*/
SDL_VTXT_DEF int SDL_VTXT_SetCursor(SDL_VTXT* vfont, int x, int y);
/** Adds an AppendLine command to a SDL_VTXT object.
*/
SDL_VTXT_DEF int SDL_VTXT_AppendLine(SDL_VTXT* vfont, const char* line);
/** Adds an NewLine command to a SDL_VTXT object.
*/
SDL_VTXT_DEF int SDL_VTXT_NewLine(SDL_VTXT* vfont, int new_x);
/** Adds an AppendGlyph command to a SDL_VTXT object.
*/
SDL_VTXT_DEF int SDL_VTXT_AppendGlyph(SDL_VTXT* vfont, char glyph);
/** Clears all commands from a SDL_VTXT object, effectively rendering nothing.
*/
SDL_VTXT_DEF int SDL_VTXT_Clear(SDL_VTXT* vfont);
/** Renders a SDL_VTXT object as geometry with a SDL_Renderer.
*/
SDL_VTXT_DEF int SDL_VTXT_Render(SDL_VTXT* vfont, SDL_Renderer* renderer);
/** Destroys and frees a SDL_VTXT object. Do not use the object after this call.
*/
SDL_VTXT_DEF int SDL_VTXT_Release(SDL_VTXT* vfont);
#endif // __SDL_VTXT__
///////////////////// IMPLEMENTATION //////////////////////////
#ifdef SDL_VTXT_IMPLEMENTATION
SDL_VTXT_DEF int SDL_VTXT_ResizeColorBuffer(SDL_VTXT* vfont, int size) {
if (vfont->color_array_count != size) {
// Color buffer is dirty. Reallocate
vfont->color_buffer = (SDL_Color*) SDL_realloc(vfont->color_buffer, sizeof(SDL_Color) * size);
if (vfont->color_buffer) {
vfont->color_array_count = size;
} else {
// TODO: LOG
return -1;
}
} else {
return 0;
}
return 0;
}
SDL_VTXT_DEF int SDL_VTXT_ResetTexture(SDL_VTXT* vfont, SDL_Renderer* renderer, int use_all_channels) {
vfont->pixel_format = SDL_MasksToPixelFormatEnum(32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
vfont->font_tex = SDL_CreateTexture(renderer,
vfont->pixel_format,
SDL_TEXTUREACCESS_STREAMING,
vfont->font_handle.font_atlas.width,
vfont->font_handle.font_atlas.height);
void *pixels = NULL;
int pitch = 0;
SDL_LockTexture(vfont->font_tex, NULL, &pixels, &pitch);
int alpha_idx = 0;
Uint8* rgba = (Uint8*) pixels;
for (int row = 0; row < vfont->font_handle.font_atlas.height; ++row) {
for (int col_byte = 0; col_byte < pitch; col_byte += 4) {
rgba[0] = vfont->font_handle.font_atlas.pixels[alpha_idx];
if (use_all_channels == 0) {
rgba[1] = 0xFF;
rgba[2] = 0xFF;
rgba[3] = 0xFF;
} else {
rgba[1] = vfont->font_handle.font_atlas.pixels[alpha_idx];
rgba[2] = vfont->font_handle.font_atlas.pixels[alpha_idx];
rgba[3] = vfont->font_handle.font_atlas.pixels[alpha_idx];
}
++alpha_idx;
rgba += 4;
}
}
SDL_UnlockTexture(vfont->font_tex);
SDL_SetTextureBlendMode(vfont->font_tex, SDL_BLENDMODE_BLEND);
return 0;
}
SDL_VTXT_DEF int SDL_VTXT_FillColorBuffer(SDL_VTXT* vfont) {
for (int ii = 0; ii < vfont->color_array_count; ++ii) {
vfont->color_buffer[ii].r = vfont->text_color.r;
vfont->color_buffer[ii].g = vfont->text_color.g;
vfont->color_buffer[ii].b = vfont->text_color.b;
vfont->color_buffer[ii].a = vfont->text_color.a;
}
return 0;
}
SDL_VTXT_DEF SDL_VTXT* SDL_VTXT_Init(SDL_Renderer* renderer, int text_size, const char* font_filename) {
// Read ttf file into memory
unsigned char* font_file = NULL;
SDL_RWops *rw = SDL_RWFromFile(font_filename, "rb");
if (rw == NULL) {
SDL_Log("Could not open font file");
return NULL;
} else {
Sint64 res_size = SDL_RWsize(rw);
unsigned char* res = (unsigned char*)SDL_malloc(res_size + 1);
Sint64 nb_read_total = 0, nb_read = 1;
unsigned char* buf = res;
while (nb_read_total < res_size && nb_read != 0) {
nb_read = SDL_RWread(rw, buf, 1, (res_size - nb_read_total));
nb_read_total += nb_read;
buf += nb_read;
}
SDL_RWclose(rw);
if (nb_read_total != res_size) {
SDL_free(res);
return NULL;
}
res[nb_read_total] = '\0';
font_file = res;
}
SDL_VTXT* ret = (SDL_VTXT*) SDL_malloc(sizeof(SDL_VTXT));
ret->text_size = text_size;
// Create vertext font
vtxt_init_font(&ret->font_handle, font_file, text_size);
SDL_free(font_file); // Does this break anything?
ret->text_color.r = 0xFF;
ret->text_color.g = 0xFF;
ret->text_color.b = 0xFF;
ret->text_color.a = 0xFF;
ret->pixel_format = 0;
ret->font_tex = NULL;
ret->commands = NULL;
ret->color_buffer = NULL;
ret->color_array_count = 0;
SDL_VTXT_ResetTexture(ret, renderer, 0);
return ret;
}
SDL_VTXT_DEF int SDL_VTXT_SetColor(SDL_VTXT* vfont, SDL_Color color) {
vfont->text_color = color;
return 0;
}
SDL_VTXT_DEF int SDL_VTXT_SetCursor(SDL_VTXT* vfont, int x, int y) {
vfont->cursor_x = x;
vfont->cursor_y = y;
return 0;
}
SDL_VTXT_DEF int SDL_VTXT_AddCommand(SDL_VTXT* vfont, VTXTCommand* cmd) {
cmd->next = NULL;
VTXTCommand** n = &vfont->commands;
while (*n != NULL) {
n = &((*n)->next);
}
*n = cmd;
return 0;
}
SDL_VTXT_DEF int SDL_VTXT_AppendLine(SDL_VTXT* vfont, const char* line) {
VTXTCommand* cmd = (VTXTCommand*) SDL_malloc(sizeof(VTXTCommand));
cmd->op = kVTXTOperation_AppendLine;
cmd->line = line;
cmd->cursor_x = 0;
cmd->glyph = '\0';
return SDL_VTXT_AddCommand(vfont, cmd);
}
SDL_VTXT_DEF int SDL_VTXT_NewLine(SDL_VTXT* vfont, int new_x) {
VTXTCommand* cmd = (VTXTCommand*) SDL_malloc(sizeof(VTXTCommand));
cmd->op = kVTXTOperation_NewLine;
cmd->line = NULL;
cmd->cursor_x = new_x;
cmd->glyph = '\0';
return SDL_VTXT_AddCommand(vfont, cmd);
}
SDL_VTXT_DEF int SDL_VTXT_AppendGlyph(SDL_VTXT* vfont, char glyph) {
VTXTCommand* cmd = (VTXTCommand*) SDL_malloc(sizeof(VTXTCommand));
cmd->op = kVTXTOperation_AppendGlyph;
cmd->line = NULL;
cmd->cursor_x = 0;
cmd->glyph = glyph;
return SDL_VTXT_AddCommand(vfont, cmd);
}
SDL_VTXT_DEF int SDL_VTXT_Clear(SDL_VTXT* vfont) {
// Release commands
VTXTCommand* cmd = vfont->commands;
while (cmd != NULL) {
VTXTCommand* n = cmd->next;
SDL_free(cmd);
cmd = n;
}
vfont->commands = NULL;
return 0;
}
SDL_VTXT_DEF int SDL_VTXT_Render(SDL_VTXT* vfont, SDL_Renderer* renderer) {
// Move cursor
vtxt_move_cursor(vfont->cursor_x, vfont->cursor_y);
// Execute commands
VTXTCommand* cmd = vfont->commands;
while (cmd != NULL) {
switch(cmd->op) {
case kVTXTOperation_AppendLine: {
vtxt_append_line(cmd->line, &vfont->font_handle, vfont->text_size);
break;
}
case kVTXTOperation_NewLine: {
vtxt_new_line(cmd->cursor_x, &vfont->font_handle);
break;
}
case kVTXTOperation_AppendGlyph: {
vtxt_append_glyph(cmd->glyph, &vfont->font_handle, vfont->text_size);
break;
}
}
cmd = cmd->next;
}
vtxt_vertex_buffer vb = vtxt_grab_buffer();
SDL_VTXT_ResizeColorBuffer(vfont, vb.vertex_count);
SDL_VTXT_FillColorBuffer(vfont);
SDL_RenderGeometryRaw(renderer, vfont->font_tex,
vb.vertex_buffer,
4*4,
(const int*)vfont->color_buffer,
0,
vb.vertex_buffer+2,
4*4,
vb.vertex_count,
vb.index_buffer,
vb.indices_array_count,
4);
vtxt_clear_buffer();
return 0;
}
SDL_VTXT_DEF int SDL_VTXT_Release(SDL_VTXT* vfont) {
// Destroy texture
if (vfont->font_tex) {
SDL_DestroyTexture(vfont->font_tex);
}
SDL_VTXT_Clear(vfont);
SDL_free(vfont);
return 0;
}
#undef SDL_VTXT_IMPLEMENTATION
#endif // SDL_VTXT_IMPLEMENTATION