diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | res/LICENSE_OFL.txt | 92 | ||||
-rw-r--r-- | res/NotoEmoji-Regular.ttf | bin | 0 -> 418804 bytes | |||
-rw-r--r-- | src/ui/text.c | 264 | ||||
-rw-r--r-- | src/ui/text.h | 11 |
5 files changed, 168 insertions, 200 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 0aaa2bb5..a7e38f5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -19,6 +19,7 @@ set (EMBED_RESOURCES | |||
19 | res/FiraSans-Bold.ttf | 19 | res/FiraSans-Bold.ttf |
20 | res/FiraSans-LightItalic.ttf | 20 | res/FiraSans-LightItalic.ttf |
21 | res/FiraMono-Regular.ttf | 21 | res/FiraMono-Regular.ttf |
22 | res/NotoEmoji-Regular.ttf | ||
22 | res/SourceSansPro-Regular.ttf | 23 | res/SourceSansPro-Regular.ttf |
23 | ) | 24 | ) |
24 | # if (UNIX AND NOT APPLE) | 25 | # if (UNIX AND NOT APPLE) |
diff --git a/res/LICENSE_OFL.txt b/res/LICENSE_OFL.txt new file mode 100644 index 00000000..d952d62c --- /dev/null +++ b/res/LICENSE_OFL.txt | |||
@@ -0,0 +1,92 @@ | |||
1 | This Font Software is licensed under the SIL Open Font License, | ||
2 | Version 1.1. | ||
3 | |||
4 | This license is copied below, and is also available with a FAQ at: | ||
5 | http://scripts.sil.org/OFL | ||
6 | |||
7 | ----------------------------------------------------------- | ||
8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 | ||
9 | ----------------------------------------------------------- | ||
10 | |||
11 | PREAMBLE | ||
12 | The goals of the Open Font License (OFL) are to stimulate worldwide | ||
13 | development of collaborative font projects, to support the font | ||
14 | creation efforts of academic and linguistic communities, and to | ||
15 | provide a free and open framework in which fonts may be shared and | ||
16 | improved in partnership with others. | ||
17 | |||
18 | The OFL allows the licensed fonts to be used, studied, modified and | ||
19 | redistributed freely as long as they are not sold by themselves. The | ||
20 | fonts, including any derivative works, can be bundled, embedded, | ||
21 | redistributed and/or sold with any software provided that any reserved | ||
22 | names are not used by derivative works. The fonts and derivatives, | ||
23 | however, cannot be released under any other type of license. The | ||
24 | requirement for fonts to remain under this license does not apply to | ||
25 | any document created using the fonts or their derivatives. | ||
26 | |||
27 | DEFINITIONS | ||
28 | "Font Software" refers to the set of files released by the Copyright | ||
29 | Holder(s) under this license and clearly marked as such. This may | ||
30 | include source files, build scripts and documentation. | ||
31 | |||
32 | "Reserved Font Name" refers to any names specified as such after the | ||
33 | copyright statement(s). | ||
34 | |||
35 | "Original Version" refers to the collection of Font Software | ||
36 | components as distributed by the Copyright Holder(s). | ||
37 | |||
38 | "Modified Version" refers to any derivative made by adding to, | ||
39 | deleting, or substituting -- in part or in whole -- any of the | ||
40 | components of the Original Version, by changing formats or by porting | ||
41 | the Font Software to a new environment. | ||
42 | |||
43 | "Author" refers to any designer, engineer, programmer, technical | ||
44 | writer or other person who contributed to the Font Software. | ||
45 | |||
46 | PERMISSION & CONDITIONS | ||
47 | Permission is hereby granted, free of charge, to any person obtaining | ||
48 | a copy of the Font Software, to use, study, copy, merge, embed, | ||
49 | modify, redistribute, and sell modified and unmodified copies of the | ||
50 | Font Software, subject to the following conditions: | ||
51 | |||
52 | 1) Neither the Font Software nor any of its individual components, in | ||
53 | Original or Modified Versions, may be sold by itself. | ||
54 | |||
55 | 2) Original or Modified Versions of the Font Software may be bundled, | ||
56 | redistributed and/or sold with any software, provided that each copy | ||
57 | contains the above copyright notice and this license. These can be | ||
58 | included either as stand-alone text files, human-readable headers or | ||
59 | in the appropriate machine-readable metadata fields within text or | ||
60 | binary files as long as those fields can be easily viewed by the user. | ||
61 | |||
62 | 3) No Modified Version of the Font Software may use the Reserved Font | ||
63 | Name(s) unless explicit written permission is granted by the | ||
64 | corresponding Copyright Holder. This restriction only applies to the | ||
65 | primary font name as presented to the users. | ||
66 | |||
67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font | ||
68 | Software shall not be used to promote, endorse or advertise any | ||
69 | Modified Version, except to acknowledge the contribution(s) of the | ||
70 | Copyright Holder(s) and the Author(s) or with their explicit written | ||
71 | permission. | ||
72 | |||
73 | 5) The Font Software, modified or unmodified, in part or in whole, | ||
74 | must be distributed entirely under this license, and must not be | ||
75 | distributed under any other license. The requirement for fonts to | ||
76 | remain under this license does not apply to any document created using | ||
77 | the Font Software. | ||
78 | |||
79 | TERMINATION | ||
80 | This license becomes null and void if any of the above conditions are | ||
81 | not met. | ||
82 | |||
83 | DISCLAIMER | ||
84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF | ||
86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT | ||
87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE | ||
88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL | ||
90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM | ||
92 | OTHER DEALINGS IN THE FONT SOFTWARE. | ||
diff --git a/res/NotoEmoji-Regular.ttf b/res/NotoEmoji-Regular.ttf new file mode 100644 index 00000000..19b7badf --- /dev/null +++ b/res/NotoEmoji-Regular.ttf | |||
Binary files differ | |||
diff --git a/src/ui/text.c b/src/ui/text.c index 26bb9599..3ea046cb 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -25,15 +25,15 @@ iDeclareTypeConstructionArgs(Glyph, iChar ch) | |||
25 | struct Impl_Glyph { | 25 | struct Impl_Glyph { |
26 | iHashNode node; | 26 | iHashNode node; |
27 | iRect rect[2]; /* zero and half pixel offset */ | 27 | iRect rect[2]; /* zero and half pixel offset */ |
28 | int advance; | ||
29 | iInt2 d[2]; | 28 | iInt2 d[2]; |
29 | float advance; /* scaled */ | ||
30 | }; | 30 | }; |
31 | 31 | ||
32 | void init_Glyph(iGlyph *d, iChar ch) { | 32 | void init_Glyph(iGlyph *d, iChar ch) { |
33 | d->node.key = ch; | 33 | d->node.key = ch; |
34 | d->rect[0] = zero_Rect(); | 34 | d->rect[0] = zero_Rect(); |
35 | d->rect[1] = zero_Rect(); | 35 | d->rect[1] = zero_Rect(); |
36 | d->advance = 0; | 36 | d->advance = 0.0f; |
37 | } | 37 | } |
38 | 38 | ||
39 | void deinit_Glyph(iGlyph *d) { | 39 | void deinit_Glyph(iGlyph *d) { |
@@ -48,7 +48,7 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) | |||
48 | 48 | ||
49 | /*-----------------------------------------------------------------------------------------------*/ | 49 | /*-----------------------------------------------------------------------------------------------*/ |
50 | 50 | ||
51 | iDeclareType(Font) | 51 | iDeclareType(Font) |
52 | 52 | ||
53 | struct Impl_Font { | 53 | struct Impl_Font { |
54 | iBlock * data; | 54 | iBlock * data; |
@@ -58,9 +58,12 @@ struct Impl_Font { | |||
58 | int baseline; | 58 | int baseline; |
59 | iHash glyphs; | 59 | iHash glyphs; |
60 | int baselineOffset; | 60 | int baselineOffset; |
61 | enum iFontId emojiFont; /* font to use for emojis */ | ||
61 | }; | 62 | }; |
62 | 63 | ||
63 | static void init_Font(iFont *d, const iBlock *data, int height, int bloff) { | 64 | static iFont *font_Text_(enum iFontId id); |
65 | |||
66 | static void init_Font(iFont *d, const iBlock *data, int height, int bloff, enum iFontId emojiFont) { | ||
64 | init_Hash(&d->glyphs); | 67 | init_Hash(&d->glyphs); |
65 | d->baselineOffset = bloff; | 68 | d->baselineOffset = bloff; |
66 | d->data = NULL; | 69 | d->data = NULL; |
@@ -69,8 +72,9 @@ static void init_Font(iFont *d, const iBlock *data, int height, int bloff) { | |||
69 | stbtt_InitFont(&d->font, constData_Block(data), 0); | 72 | stbtt_InitFont(&d->font, constData_Block(data), 0); |
70 | d->scale = stbtt_ScaleForPixelHeight(&d->font, height); | 73 | d->scale = stbtt_ScaleForPixelHeight(&d->font, height); |
71 | int ascent; | 74 | int ascent; |
72 | stbtt_GetFontVMetrics(&d->font, &ascent, 0, 0); | 75 | stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL); |
73 | d->baseline = (int) ascent * d->scale; | 76 | d->baseline = (int) ascent * d->scale; |
77 | d->emojiFont = emojiFont; | ||
74 | } | 78 | } |
75 | 79 | ||
76 | static void deinit_Font(iFont *d) { | 80 | static void deinit_Font(iFont *d) { |
@@ -120,21 +124,33 @@ void init_Text(SDL_Renderer *render) { | |||
120 | d->cacheRowHeight = 0; | 124 | d->cacheRowHeight = 0; |
121 | } | 125 | } |
122 | /* Load the fonts. */ { | 126 | /* Load the fonts. */ { |
123 | const struct { const iBlock *ttf; int size; } fontData[max_FontId] = { | 127 | const struct { |
124 | { &fontSourceSansProRegular_Embedded, fontSize_UI }, | 128 | const iBlock *ttf; |
125 | { &fontFiraSansRegular_Embedded, fontSize_UI }, | 129 | int size; |
126 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f }, | 130 | int emojiFont; |
127 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.666f }, | 131 | } fontData[max_FontId] = { |
128 | { &fontFiraSansRegular_Embedded, fontSize_UI * 1.333f }, | 132 | { &fontSourceSansProRegular_Embedded, fontSize_UI, emoji_FontId }, |
129 | { &fontFiraSansLightItalic_Embedded, fontSize_UI }, | 133 | { &fontFiraSansRegular_Embedded, fontSize_UI, emoji_FontId }, |
130 | { &fontFiraSansBold_Embedded, fontSize_UI }, | 134 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, smallEmoji_FontId }, |
131 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.333f }, | 135 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.666f, smallEmoji_FontId }, |
132 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.666f }, | 136 | { &fontFiraSansRegular_Embedded, fontSize_UI * 1.333f, mediumEmoji_FontId }, |
133 | { &fontFiraSansBold_Embedded, fontSize_UI * 2.0f }, | 137 | { &fontFiraSansLightItalic_Embedded, fontSize_UI, emoji_FontId }, |
138 | { &fontFiraSansBold_Embedded, fontSize_UI, emoji_FontId }, | ||
139 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.333f, mediumEmoji_FontId }, | ||
140 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.666f, largeEmoji_FontId }, | ||
141 | { &fontFiraSansBold_Embedded, fontSize_UI * 2.000f, hugeEmoji_FontId }, | ||
142 | { &fontNotoEmojiRegular_Embedded, fontSize_UI, emoji_FontId }, | ||
143 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.333f, mediumEmoji_FontId }, | ||
144 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.666f, largeEmoji_FontId }, | ||
145 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 2.000f, hugeEmoji_FontId }, | ||
146 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 0.866f, smallEmoji_FontId }, | ||
134 | }; | 147 | }; |
135 | iForIndices(i, fontData) { | 148 | iForIndices(i, fontData) { |
136 | init_Font(&d->fonts[i], fontData[i].ttf, fontData[i].size, | 149 | init_Font(&d->fonts[i], |
137 | i == 0 ? fontSize_UI / 20 : 0); | 150 | fontData[i].ttf, |
151 | fontData[i].size, | ||
152 | i == 0 ? fontSize_UI / 18 : 0, | ||
153 | fontData[i].emojiFont); | ||
138 | } | 154 | } |
139 | } | 155 | } |
140 | } | 156 | } |
@@ -150,6 +166,10 @@ void deinit_Text(void) { | |||
150 | iRelease(d->ansiEscape); | 166 | iRelease(d->ansiEscape); |
151 | } | 167 | } |
152 | 168 | ||
169 | iFont *font_Text_(enum iFontId id) { | ||
170 | return &text_.fonts[id]; | ||
171 | } | ||
172 | |||
153 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch, float xShift) { | 173 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch, float xShift) { |
154 | int w, h; | 174 | int w, h; |
155 | uint8_t *bmp = stbtt_GetCodepointBitmapSubpixel( | 175 | uint8_t *bmp = stbtt_GetCodepointBitmapSubpixel( |
@@ -161,59 +181,10 @@ static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch, float xShift) | |||
161 | return surface; | 181 | return surface; |
162 | } | 182 | } |
163 | 183 | ||
164 | static iBool isSpecialChar_(iChar ch) { | ||
165 | return ch >= specialSymbol_Text && ch < 0x20; | ||
166 | } | ||
167 | |||
168 | static float symbolEmWidth_(int symbol) { | ||
169 | return 1.5f; | ||
170 | } | ||
171 | |||
172 | static float symbolAdvance_(int symbol) { | ||
173 | return 1.5f; | ||
174 | } | ||
175 | |||
176 | static int specialChar_(iChar ch) { | ||
177 | return ch - specialSymbol_Text; | ||
178 | } | ||
179 | |||
180 | iLocalDef SDL_Rect sdlRect_(const iRect rect) { | 184 | iLocalDef SDL_Rect sdlRect_(const iRect rect) { |
181 | return (SDL_Rect){ rect.pos.x, rect.pos.y, rect.size.x, rect.size.y }; | 185 | return (SDL_Rect){ rect.pos.x, rect.pos.y, rect.size.x, rect.size.y }; |
182 | } | 186 | } |
183 | 187 | ||
184 | #if 0 | ||
185 | static void fillTriangle_(SDL_Surface *surface, const SDL_Rect *rect, int dir) { | ||
186 | const uint32_t color = 0xffffffff; | ||
187 | SDL_LockSurface(surface); | ||
188 | uint32_t *row = surface->pixels; | ||
189 | row += rect->x + rect->y * surface->pitch / 4; | ||
190 | for (int y = 0; y < rect->h; y++, row += surface->pitch / 4) { | ||
191 | float norm = (float) y / (float) (rect->h - 1) * 2.0f; | ||
192 | if (norm > 1.0f) norm = 2.0f - norm; | ||
193 | const int len = norm * rect->w; | ||
194 | const float fract = norm * rect->w - len; | ||
195 | const uint32_t fractColor = 0xffffff00 | (int) (fract * 0xff); | ||
196 | if (dir > 0) { | ||
197 | for (int x = 0; x < len; x++) { | ||
198 | row[x] = color; | ||
199 | } | ||
200 | if (len < rect->w) { | ||
201 | row[len] = fractColor; | ||
202 | } | ||
203 | } | ||
204 | else { | ||
205 | for (int x = 0; x < len; x++) { | ||
206 | row[rect->w - len + x] = color; | ||
207 | } | ||
208 | if (len < rect->w) { | ||
209 | row[rect->w - len - 1] = fractColor; | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | SDL_UnlockSurface(surface); | ||
214 | } | ||
215 | #endif | ||
216 | |||
217 | static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | 188 | static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { |
218 | iText *txt = &text_; | 189 | iText *txt = &text_; |
219 | SDL_Renderer *render = txt->render; | 190 | SDL_Renderer *render = txt->render; |
@@ -222,12 +193,12 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
222 | const iChar ch = char_Glyph(glyph); | 193 | const iChar ch = char_Glyph(glyph); |
223 | iBool fromStb = iFalse; | 194 | iBool fromStb = iFalse; |
224 | iRect *glRect = &glyph->rect[hoff]; | 195 | iRect *glRect = &glyph->rect[hoff]; |
225 | if (!isSpecialChar_(ch)) { | 196 | /* Rasterize the glyph using stbtt. */ { |
226 | /* Rasterize the glyph using stbtt. */ | ||
227 | surface = rasterizeGlyph_Font_(d, ch, hoff * 0.5f); | 197 | surface = rasterizeGlyph_Font_(d, ch, hoff * 0.5f); |
228 | if (hoff == 0) { | 198 | if (hoff == 0) { |
229 | int lsb; | 199 | int adv, lsb; |
230 | stbtt_GetCodepointHMetrics(&d->font, ch, &glyph->advance, &lsb); | 200 | stbtt_GetCodepointHMetrics(&d->font, ch, &adv, &lsb); |
201 | glyph->advance = d->scale * adv; | ||
231 | } | 202 | } |
232 | stbtt_GetCodepointBitmapBoxSubpixel(&d->font, | 203 | stbtt_GetCodepointBitmapBoxSubpixel(&d->font, |
233 | ch, | 204 | ch, |
@@ -244,47 +215,6 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
244 | tex = SDL_CreateTextureFromSurface(render, surface); | 215 | tex = SDL_CreateTextureFromSurface(render, surface); |
245 | glRect->size = init_I2(surface->w, surface->h); | 216 | glRect->size = init_I2(surface->w, surface->h); |
246 | } | 217 | } |
247 | else { | ||
248 | /* Metrics for special symbols. */ | ||
249 | int em, lsb; | ||
250 | const int symbol = specialChar_(ch); | ||
251 | stbtt_GetCodepointHMetrics(&d->font, 'M', &em, &lsb); | ||
252 | glyph->d[hoff].x = d->baseline / 10; | ||
253 | glyph->d[hoff].y = -d->baseline; | ||
254 | glyph->advance = em * symbolAdvance_(symbol); | ||
255 | glyph->rect[hoff].size = init_I2(symbolEmWidth_(symbol) * em * d->scale, d->height); | ||
256 | #if 0 | ||
257 | if (isRasterizedSymbol_(ch)) { | ||
258 | /* Rasterize manually. */ | ||
259 | surface = SDL_CreateRGBSurfaceWithFormat( | ||
260 | 0, width_Rect(glyph->rect), height_Rect(glyph->rect), 32, SDL_PIXELFORMAT_RGBA8888); | ||
261 | SDL_FillRect(surface, NULL, 0); | ||
262 | const uint32_t white = 0xffffffff; | ||
263 | switch (specialChar_(ch)) { | ||
264 | case play_SpecialSymbol: | ||
265 | fillTriangle_(surface, &(SDL_Rect){ 0, 0, surface->w, d->baseline }, 1); | ||
266 | break; | ||
267 | case pause_SpecialSymbol: { | ||
268 | const int w = surface->w * 4 / 11; | ||
269 | SDL_FillRect(surface, &(SDL_Rect){ 0, 0, w, d->baseline }, white); | ||
270 | SDL_FillRect(surface, &(SDL_Rect){ surface->w - w, 0, w, d->baseline }, white); | ||
271 | break; | ||
272 | } | ||
273 | case rewind_SpecialSymbol: { | ||
274 | const int w1 = surface->w / 7; | ||
275 | const int w2 = surface->w * 3 / 7; | ||
276 | const int h = d->baseline * 4 / 5; | ||
277 | const int off = (d->baseline - h) / 2; | ||
278 | SDL_FillRect(surface, &(SDL_Rect){ 0, off, w1, h}, white); | ||
279 | fillTriangle_(surface, &(SDL_Rect){ w1, off, w2, h }, -1); | ||
280 | fillTriangle_(surface, &(SDL_Rect){ surface->w * 4 / 7, off, w2, h }, -1); | ||
281 | break; | ||
282 | } | ||
283 | } | ||
284 | tex = SDL_CreateTextureFromSurface(render, surface); | ||
285 | } | ||
286 | #endif | ||
287 | } | ||
288 | /* Determine placement in the glyph cache texture, advancing in rows. */ | 218 | /* Determine placement in the glyph cache texture, advancing in rows. */ |
289 | if (txt->cachePos.x + glRect->size.x > txt->cacheSize.x) { | 219 | if (txt->cachePos.x + glRect->size.x > txt->cacheSize.x) { |
290 | txt->cachePos.x = 0; | 220 | txt->cachePos.x = 0; |
@@ -294,87 +224,31 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
294 | glRect->pos = txt->cachePos; | 224 | glRect->pos = txt->cachePos; |
295 | SDL_SetRenderTarget(render, txt->cache); | 225 | SDL_SetRenderTarget(render, txt->cache); |
296 | const SDL_Rect dstRect = sdlRect_(*glRect); | 226 | const SDL_Rect dstRect = sdlRect_(*glRect); |
297 | if (surface) { | 227 | SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect); |
298 | SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect); | ||
299 | } | ||
300 | else { | ||
301 | #if 0 | ||
302 | /* Draw a special symbol. */ | ||
303 | SDL_SetRenderDrawColor(render, 255, 255, 255, 255); | ||
304 | const iInt2 tl = init_I2(dstRect.x, dstRect.y); | ||
305 | const iInt2 br = init_I2(dstRect.x + dstRect.w - 1, dstRect.y + dstRect.h - 1); | ||
306 | const int midX = tl.x + dstRect.w / 2; | ||
307 | const int midY = tl.y + dstRect.h / 2; | ||
308 | const int symH = dstRect.h * 2 / 6; | ||
309 | /* Frame. */ | ||
310 | if (isFramedSymbol_(ch)) { | ||
311 | SDL_RenderDrawLines( | ||
312 | render, | ||
313 | (SDL_Point[]){ | ||
314 | { tl.x, tl.y }, { br.x, tl.y }, { br.x, br.y }, { tl.x, br.y }, { tl.x, tl.y } }, | ||
315 | 5); | ||
316 | } | ||
317 | iArray points; | ||
318 | init_Array(&points, sizeof(SDL_Point)); | ||
319 | switch (specialChar_(ch)) { | ||
320 | case 0: /* silence */ | ||
321 | break; | ||
322 | case 1: /* sine */ | ||
323 | for (int i = 0; i < dstRect.w; ++i) { | ||
324 | float rad = 2.0f * iMathPif * (float) i / dstRect.w; | ||
325 | SDL_Point pt = { tl.x + i, midY + sin(rad) * symH}; | ||
326 | pushBack_Array(&points, &pt); | ||
327 | } | ||
328 | SDL_RenderDrawLines(render, constData_Array(&points), size_Array(&points)); | ||
329 | break; | ||
330 | case 2: /* square */ | ||
331 | SDL_RenderDrawLines(render, | ||
332 | (SDL_Point[]){ { tl.x, midY - symH }, | ||
333 | { midX, midY - symH }, | ||
334 | { midX, midY + symH }, | ||
335 | { br.x, midY + symH } }, | ||
336 | 4); | ||
337 | break; | ||
338 | case 3: /* saw */ | ||
339 | SDL_RenderDrawLines(render, | ||
340 | (SDL_Point[]){ { tl.x, midY }, | ||
341 | { midX, midY - symH }, | ||
342 | { midX, midY + symH }, | ||
343 | { br.x, midY } }, | ||
344 | 4); | ||
345 | break; | ||
346 | case 4: /* triangle */ | ||
347 | SDL_RenderDrawLines(render, | ||
348 | (SDL_Point[]){ { tl.x, midY }, | ||
349 | { tl.x + dstRect.w / 4, midY - symH }, | ||
350 | { br.x - dstRect.w / 4, midY + symH }, | ||
351 | { br.x, midY } }, | ||
352 | 4); | ||
353 | break; | ||
354 | case 5: /* noise */ | ||
355 | for (int i = 0; i < dstRect.w; ++i) { | ||
356 | for (int p = 0; p < 2; ++p) { | ||
357 | const float val = iRandomf() * 2.0f - 1.0f; | ||
358 | pushBack_Array(&points, &(SDL_Point){ tl.x + i, midY - val * symH }); | ||
359 | } | ||
360 | } | ||
361 | SDL_RenderDrawPoints(render, constData_Array(&points), size_Array(&points)); | ||
362 | break; | ||
363 | } | ||
364 | deinit_Array(&points); | ||
365 | #endif | ||
366 | } | ||
367 | SDL_SetRenderTarget(render, NULL); | 228 | SDL_SetRenderTarget(render, NULL); |
368 | if (tex) { | 229 | if (tex) { |
369 | SDL_DestroyTexture(tex); | 230 | SDL_DestroyTexture(tex); |
370 | iAssert(surface); | 231 | iAssert(surface); |
371 | if (fromStb) stbtt_FreeBitmap(surface->pixels, NULL); | 232 | stbtt_FreeBitmap(surface->pixels, NULL); |
372 | SDL_FreeSurface(surface); | 233 | SDL_FreeSurface(surface); |
373 | } | 234 | } |
374 | /* Update cache cursor. */ | 235 | /* Update cache cursor. */ |
375 | txt->cachePos.x += glRect->size.x; | 236 | txt->cachePos.x += glRect->size.x; |
376 | txt->cacheRowHeight = iMax(txt->cacheRowHeight, glRect->size.y); | 237 | txt->cacheRowHeight = iMax(txt->cacheRowHeight, glRect->size.y); |
377 | iAssert(txt->cachePos.y + txt->cacheRowHeight <= txt->cacheSize.y); | 238 | iAssert(txt->cachePos.y + txt->cacheRowHeight <= txt->cacheSize.y); |
239 | /* TODO: Automatically enlarge the cache if running out of space? | ||
240 | Maybe make it paged. */ | ||
241 | } | ||
242 | |||
243 | iLocalDef iBool isEmoji_(iChar ch) { | ||
244 | return ch >= 0x1f000 && ch < 0x20000; | ||
245 | } | ||
246 | |||
247 | iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch) { | ||
248 | if (isEmoji_(ch) && font_Text_(d->emojiFont) != d) { | ||
249 | return font_Text_(d->emojiFont); | ||
250 | } | ||
251 | return d; | ||
378 | } | 252 | } |
379 | 253 | ||
380 | static const iGlyph *glyph_Font_(iFont *d, iChar ch) { | 254 | static const iGlyph *glyph_Font_(iFont *d, iChar ch) { |
@@ -411,7 +285,6 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
411 | int xposLimit, const char **continueFrom_out, int *runAdvance_out) { | 285 | int xposLimit, const char **continueFrom_out, int *runAdvance_out) { |
412 | iInt2 size = zero_I2(); | 286 | iInt2 size = zero_I2(); |
413 | const iInt2 orig = pos; | 287 | const iInt2 orig = pos; |
414 | const stbtt_fontinfo *info = &d->font; | ||
415 | float xpos = pos.x; | 288 | float xpos = pos.x; |
416 | float xposMax = xpos; | 289 | float xposMax = xpos; |
417 | iAssert(xposLimit == 0 || mode == measure_RunMode); | 290 | iAssert(xposLimit == 0 || mode == measure_RunMode); |
@@ -454,7 +327,8 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
454 | continue; | 327 | continue; |
455 | } | 328 | } |
456 | } | 329 | } |
457 | const iGlyph *glyph = glyph_Font_(d, ch); | 330 | iFont *font = characterFont_Font_(d, ch); /* may switch to an Emoji font */ |
331 | const iGlyph *glyph = glyph_Font_(font, ch); | ||
458 | int x1 = xpos; | 332 | int x1 = xpos; |
459 | const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; | 333 | const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; |
460 | int x2 = x1 + glyph->rect[hoff].size.x; | 334 | int x2 = x1 + glyph->rect[hoff].size.x; |
@@ -464,25 +338,26 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
464 | break; | 338 | break; |
465 | } | 339 | } |
466 | size.x = iMax(size.x, x2 - orig.x); | 340 | size.x = iMax(size.x, x2 - orig.x); |
467 | size.y = iMax(size.y, pos.y + d->height - orig.y); | 341 | size.y = iMax(size.y, pos.y + font->height - orig.y); |
468 | if (mode != measure_RunMode) { | 342 | if (mode != measure_RunMode) { |
469 | SDL_Rect dst = { x1 + glyph->d[hoff].x, | 343 | SDL_Rect dst = { x1 + glyph->d[hoff].x, |
470 | pos.y + d->baseline + glyph->d[hoff].y, | 344 | pos.y + font->baseline + glyph->d[hoff].y, |
471 | glyph->rect[hoff].size.x, | 345 | glyph->rect[hoff].size.x, |
472 | glyph->rect[hoff].size.y }; | 346 | glyph->rect[hoff].size.y }; |
473 | SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect[hoff], &dst); | 347 | SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect[hoff], &dst); |
474 | } | 348 | } |
475 | xpos += d->scale * glyph->advance; | 349 | xpos += glyph->advance; |
476 | xposMax = iMax(xposMax, xpos); | 350 | xposMax = iMax(xposMax, xpos); |
477 | if (!isSpace_Char(prevCh) && isSpace_Char(ch)) { | 351 | if (!isSpace_Char(prevCh) && isSpace_Char(ch)) { |
478 | lastWordEnd = chPos; | 352 | lastWordEnd = chPos; |
479 | } | 353 | } |
480 | /* Check the next character. */ { | 354 | /* Check the next character. */ |
355 | if (font == d) { | ||
481 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ | 356 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ |
482 | const char *peek = chPos; | 357 | const char *peek = chPos; |
483 | const iChar next = nextChar_(&peek, text.end); | 358 | const iChar next = nextChar_(&peek, text.end); |
484 | if (next) { | 359 | if (next) { |
485 | xpos += d->scale * stbtt_GetCodepointKernAdvance(info, ch, next); | 360 | xpos += font->scale * stbtt_GetCodepointKernAdvance(&d->font, ch, next); |
486 | } | 361 | } |
487 | } | 362 | } |
488 | prevCh = ch; | 363 | prevCh = ch; |
@@ -608,15 +483,16 @@ void drawString_Text(int fontId, iInt2 pos, int color, const iString *text) { | |||
608 | draw_Text_(fontId, pos, color, range_String(text)); | 483 | draw_Text_(fontId, pos, color, range_String(text)); |
609 | } | 484 | } |
610 | 485 | ||
611 | void drawCentered_Text(int fontId, iRect rect, int color, const char *text, ...) { | 486 | void drawCentered_Text(int fontId, iRect rect, int color, const char *format, ...) { |
612 | iBlock chars; | 487 | iBlock chars; |
613 | init_Block(&chars, 0); { | 488 | init_Block(&chars, 0); { |
614 | va_list args; | 489 | va_list args; |
615 | va_start(args, text); | 490 | va_start(args, format); |
616 | vprintf_Block(&chars, text, args); | 491 | vprintf_Block(&chars, format, args); |
617 | va_end(args); | 492 | va_end(args); |
618 | } | 493 | } |
619 | const iInt2 textSize = advance_Text(fontId, cstr_Block(&chars)); | 494 | const char *text = cstr_Block(&chars); |
495 | iInt2 textSize = advance_Text(fontId, text); | ||
620 | draw_Text_(fontId, sub_I2(mid_Rect(rect), divi_I2(textSize, 2)), color, | 496 | draw_Text_(fontId, sub_I2(mid_Rect(rect), divi_I2(textSize, 2)), color, |
621 | (iRangecc){ constBegin_Block(&chars), constEnd_Block(&chars) }); | 497 | (iRangecc){ constBegin_Block(&chars), constEnd_Block(&chars) }); |
622 | deinit_Block(&chars); | 498 | deinit_Block(&chars); |
diff --git a/src/ui/text.h b/src/ui/text.h index 982218eb..18718d37 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -16,6 +16,11 @@ enum iFontId { | |||
16 | mediumBold_FontId, | 16 | mediumBold_FontId, |
17 | largeBold_FontId, | 17 | largeBold_FontId, |
18 | hugeBold_FontId, | 18 | hugeBold_FontId, |
19 | emoji_FontId, | ||
20 | mediumEmoji_FontId, | ||
21 | largeEmoji_FontId, | ||
22 | hugeEmoji_FontId, | ||
23 | smallEmoji_FontId, | ||
19 | max_FontId, | 24 | max_FontId, |
20 | /* UI fonts: */ | 25 | /* UI fonts: */ |
21 | uiLabel_FontId = default_FontId, | 26 | uiLabel_FontId = default_FontId, |
@@ -32,12 +37,6 @@ enum iFontId { | |||
32 | header3_FontId = medium_FontId, | 37 | header3_FontId = medium_FontId, |
33 | }; | 38 | }; |
34 | 39 | ||
35 | #define specialSymbol_Text 0x10 | ||
36 | |||
37 | enum iSpecialSymbol { | ||
38 | silence_SpecialSymbol, | ||
39 | }; | ||
40 | |||
41 | void init_Text (SDL_Renderer *); | 40 | void init_Text (SDL_Renderer *); |
42 | void deinit_Text (void); | 41 | void deinit_Text (void); |
43 | 42 | ||