source: mainline/uspace/lib/gfxfont/src/text.c@ 4583015

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4583015 was 4583015, checked in by Jiri Svoboda <jiri@…>, 3 years ago

Add font to gfx_text_fmt_t

This is quite logical and saves us one argument that we need to pass to
all text formatting functions.

  • Property mode set to 100644
File size: 9.4 KB
Line 
1/*
2 * Copyright (c) 2022 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libgfxfont
30 * @{
31 */
32/**
33 * @file Text rendering
34 */
35
36#include <errno.h>
37#include <gfx/bitmap.h>
38#include <gfx/color.h>
39#include <gfx/font.h>
40#include <gfx/glyph.h>
41#include <gfx/render.h>
42#include <gfx/text.h>
43#include <io/pixelmap.h>
44#include <mem.h>
45#include <str.h>
46#include "../private/font.h"
47#include "../private/typeface.h"
48
49/** Initialize text formatting structure.
50 *
51 * Text formatting structure must always be initialized using this function
52 * first.
53 *
54 * @param fmt Text formatting structure
55 */
56void gfx_text_fmt_init(gfx_text_fmt_t *fmt)
57{
58 memset(fmt, 0, sizeof(gfx_text_fmt_t));
59}
60
61/** Compute text width.
62 *
63 * @param font Font
64 * @param str String
65 * @return Text width
66 */
67gfx_coord_t gfx_text_width(gfx_font_t *font, const char *str)
68{
69 gfx_glyph_metrics_t gmetrics;
70 size_t stradv;
71 const char *cp;
72 gfx_glyph_t *glyph;
73 gfx_coord_t width;
74 errno_t rc;
75
76 if ((font->finfo->props.flags & gff_text_mode) != 0)
77 return str_width(str);
78
79 width = 0;
80 cp = str;
81 while (*cp != '\0') {
82 rc = gfx_font_search_glyph(font, cp, &glyph, &stradv);
83 if (rc != EOK) {
84 ++cp;
85 continue;
86 }
87
88 gfx_glyph_get_metrics(glyph, &gmetrics);
89
90 cp += stradv;
91 width += gmetrics.advance;
92 }
93
94 return width;
95}
96
97/** Print string using text characters in text mode.
98 *
99 * @param font Font
100 * @param pos Position of top-left corner of text
101 * @param color Text color
102 * @param str String
103 * @return EOK on success or an error code
104 */
105static errno_t gfx_puttext_textmode(gfx_font_t *font, gfx_coord2_t *pos,
106 gfx_color_t *color, const char *str)
107{
108 gfx_context_t *gc = font->typeface->gc;
109 gfx_bitmap_params_t params;
110 gfx_bitmap_t *bitmap;
111 gfx_bitmap_alloc_t alloc;
112 uint8_t attr;
113 pixelmap_t pmap;
114 gfx_coord_t x;
115 pixel_t pixel;
116 char32_t c;
117 size_t off;
118 errno_t rc;
119
120 /*
121 * NOTE: Creating and destroying bitmap each time is not probably
122 * the most efficient way.
123 */
124
125 gfx_color_get_ega(color, &attr);
126
127 gfx_bitmap_params_init(&params);
128 params.rect.p0.x = 0;
129 params.rect.p0.y = 0;
130 params.rect.p1.x = str_width(str);
131 params.rect.p1.y = 1;
132
133 if (params.rect.p1.x == 0) {
134 /* Nothing to do. Avoid creating bitmap of zero width. */
135 return EOK;
136 }
137
138 rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
139 if (rc != EOK)
140 return rc;
141
142 rc = gfx_bitmap_get_alloc(bitmap, &alloc);
143 if (rc != EOK) {
144 gfx_bitmap_destroy(bitmap);
145 return rc;
146 }
147
148 pmap.width = params.rect.p1.x;
149 pmap.height = 1;
150 pmap.data = alloc.pixels;
151
152 off = 0;
153 for (x = 0; x < params.rect.p1.x; x++) {
154 c = str_decode(str, &off, STR_NO_LIMIT);
155 pixel = PIXEL(attr,
156 (c >> 16) & 0xff,
157 (c >> 8) & 0xff,
158 c & 0xff);
159 pixelmap_put_pixel(&pmap, x, 0, pixel);
160 }
161
162 rc = gfx_bitmap_render(bitmap, NULL, pos);
163
164 gfx_bitmap_destroy(bitmap);
165 return rc;
166}
167
168/** Get text starting position.
169 *
170 * @param pos Anchor position
171 * @param fmt Text formatting
172 * @param str String
173 * @param spos Place to store starting position
174 */
175void gfx_text_start_pos(gfx_coord2_t *pos, gfx_text_fmt_t *fmt,
176 const char *str, gfx_coord2_t *spos)
177{
178 gfx_font_metrics_t fmetrics;
179 gfx_coord_t width;
180
181 *spos = *pos;
182
183 /* Adjust position for horizontal alignment */
184 if (fmt->halign != gfx_halign_left) {
185 width = gfx_text_width(fmt->font, str);
186 switch (fmt->halign) {
187 case gfx_halign_center:
188 spos->x -= width / 2;
189 break;
190 case gfx_halign_right:
191 spos->x -= width;
192 break;
193 default:
194 break;
195 }
196 }
197
198 /* Adjust position for vertical alignment */
199 gfx_font_get_metrics(fmt->font, &fmetrics);
200
201 if (fmt->valign != gfx_valign_baseline) {
202 switch (fmt->valign) {
203 case gfx_valign_top:
204 spos->y += fmetrics.ascent;
205 break;
206 case gfx_valign_center:
207 spos->y += fmetrics.ascent / 2;
208 break;
209 case gfx_valign_bottom:
210 spos->y -= fmetrics.descent + 1;
211 break;
212 default:
213 break;
214 }
215 }
216}
217
218/** Render text.
219 *
220 * @param pos Anchor position
221 * @param fmt Text formatting
222 * @param str String
223 * @return EOK on success or an error code
224 */
225errno_t gfx_puttext(gfx_coord2_t *pos, gfx_text_fmt_t *fmt, const char *str)
226{
227 gfx_glyph_metrics_t gmetrics;
228 gfx_font_metrics_t fmetrics;
229 size_t stradv;
230 const char *cp;
231 gfx_glyph_t *glyph;
232 gfx_coord2_t cpos;
233 gfx_coord2_t spos;
234 gfx_rect_t rect;
235 errno_t rc;
236
237 gfx_text_start_pos(pos, fmt, str, &spos);
238
239 /* Text mode */
240 if ((fmt->font->finfo->props.flags & gff_text_mode) != 0)
241 return gfx_puttext_textmode(fmt->font, &spos, fmt->color, str);
242
243 rc = gfx_set_color(fmt->font->typeface->gc, fmt->color);
244 if (rc != EOK)
245 return rc;
246
247 cpos = spos;
248 cp = str;
249 while (*cp != '\0') {
250 rc = gfx_font_search_glyph(fmt->font, cp, &glyph, &stradv);
251 if (rc != EOK) {
252 ++cp;
253 continue;
254 }
255
256 gfx_glyph_get_metrics(glyph, &gmetrics);
257
258 rc = gfx_glyph_render(glyph, &cpos);
259 if (rc != EOK)
260 return rc;
261
262 cp += stradv;
263 cpos.x += gmetrics.advance;
264 }
265
266 /* Text underlining */
267 if (fmt->underline) {
268 gfx_font_get_metrics(fmt->font, &fmetrics);
269
270 rect.p0.x = spos.x;
271 rect.p0.y = spos.y + fmetrics.underline_y0;
272 rect.p1.x = cpos.x;
273 rect.p1.y = spos.y + fmetrics.underline_y1;
274
275 rc = gfx_fill_rect(fmt->font->typeface->gc, &rect);
276 if (rc != EOK)
277 return rc;
278 }
279
280 return EOK;
281}
282
283/** Find character position in string by X coordinate.
284 *
285 * @param pos Anchor position
286 * @param fmt Text formatting
287 * @param str String
288 * @param fpos Position for which we need to find offset
289 *
290 * @return Byte offset in @a str of character corresponding to position
291 * @a fpos. Note that the position is rounded, that is,
292 * if it is before the center of character A, it will return
293 * offset of A, if it is after the center of A, it will return
294 * offset of the following character.
295 */
296size_t gfx_text_find_pos(gfx_coord2_t *pos, gfx_text_fmt_t *fmt,
297 const char *str, gfx_coord2_t *fpos)
298{
299 gfx_glyph_metrics_t gmetrics;
300 size_t stradv;
301 const char *cp;
302 gfx_glyph_t *glyph;
303 gfx_coord2_t cpos;
304 size_t off;
305 size_t strsize;
306 errno_t rc;
307
308 gfx_text_start_pos(pos, fmt, str, &cpos);
309
310 /* Text mode */
311 if ((fmt->font->finfo->props.flags & gff_text_mode) != 0) {
312 off = 0;
313 strsize = str_size(str);
314 while (off < strsize) {
315 if (fpos->x <= cpos.x)
316 return off;
317 (void) str_decode(str, &off, strsize);
318 cpos.x++;
319 }
320
321 return off;
322 }
323
324 cp = str;
325 off = 0;
326 while (*cp != '\0') {
327 rc = gfx_font_search_glyph(fmt->font, cp, &glyph, &stradv);
328 if (rc != EOK) {
329 ++cp;
330 continue;
331 }
332
333 gfx_glyph_get_metrics(glyph, &gmetrics);
334
335 if (fpos->x < cpos.x + gmetrics.advance / 2)
336 return off;
337
338 cp += stradv;
339 off += stradv;
340 cpos.x += gmetrics.advance;
341 }
342
343 return off;
344}
345
346/** Get text continuation parameters.
347 *
348 * Return the anchor position and format needed to continue printing
349 * text after the specified string. It is allowed for the sources
350 * (@a pos, @a fmt) and destinations (@a cpos, @a cfmt) to point
351 * to the same objects, respectively.
352 *
353 * @param pos Anchor position
354 * @param fmt Text formatting
355 * @param str String
356 * @param cpos Place to store anchor position for continuation
357 * @param cfmt Place to store format for continuation
358 */
359void gfx_text_cont(gfx_coord2_t *pos, gfx_text_fmt_t *fmt, const char *str,
360 gfx_coord2_t *cpos, gfx_text_fmt_t *cfmt)
361{
362 gfx_coord2_t spos;
363 gfx_text_fmt_t tfmt;
364
365 /* Continuation should start where the current string ends */
366 gfx_text_start_pos(pos, fmt, str, &spos);
367 cpos->x = spos.x + gfx_text_width(fmt->font, str);
368 cpos->y = spos.y;
369
370 /*
371 * Formatting is the same, except the text should be aligned
372 * so that it starts at the anchor point.
373 */
374 tfmt = *fmt;
375 tfmt.halign = gfx_halign_left;
376 tfmt.valign = gfx_valign_baseline;
377
378 *cfmt = tfmt;
379}
380
381/** Get text bounding rectangle.
382 *
383 * @param pos Anchor position
384 * @param fmt Text formatting
385 * @param str String
386 * @param rect Place to store bounding rectangle
387 */
388void gfx_text_rect(gfx_coord2_t *pos, gfx_text_fmt_t *fmt, const char *str,
389 gfx_rect_t *rect)
390{
391 gfx_coord2_t spos;
392
393 gfx_text_start_pos(pos, fmt, str, &spos);
394
395 rect->p0.x = spos.x;
396 rect->p0.y = spos.y - fmt->font->metrics.ascent;
397 rect->p1.x = spos.x + gfx_text_width(fmt->font, str);
398 rect->p1.y = spos.y + fmt->font->metrics.descent + 1;
399}
400
401/** @}
402 */
Note: See TracBrowser for help on using the repository browser.