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

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

Entry text selection (using keyboard)

Text can be selected with movement keys while holding down Shift.
Selection can be deleted by pressing Backspace, Delete or typing
in replacement text.

  • Property mode set to 100644
File size: 9.2 KB
RevLine 
[8fa65af0]1/*
[d63623f]2 * Copyright (c) 2021 Jiri Svoboda
[8fa65af0]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>
[6a87f28]37#include <gfx/bitmap.h>
[b433f68]38#include <gfx/color.h>
[8fa65af0]39#include <gfx/font.h>
40#include <gfx/glyph.h>
[b433f68]41#include <gfx/render.h>
[8fa65af0]42#include <gfx/text.h>
[6a87f28]43#include <io/pixelmap.h>
[8fa65af0]44#include <mem.h>
[6a87f28]45#include <str.h>
46#include "../private/font.h"
47#include "../private/typeface.h"
[8fa65af0]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
[6a87f28]76 if ((font->finfo->props.flags & gff_text_mode) != 0)
77 return str_width(str);
78
[8fa65af0]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
[6a87f28]97/** Print string using text characters in text mode.
98 *
99 * @param font Font
100 * @param pos Position of top-left corner of text
[b433f68]101 * @param color Text color
[6a87f28]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,
[b433f68]106 gfx_color_t *color, const char *str)
[6a87f28]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;
[b433f68]112 uint16_t r, g, b;
[6a87f28]113 pixelmap_t pmap;
114 gfx_coord_t x;
115 pixel_t pixel;
116 errno_t rc;
117
118 /*
119 * NOTE: Creating and destroying bitmap each time is not probably
120 * the most efficient way.
121 */
122
[b433f68]123 gfx_color_get_rgb_i16(color, &r, &g, &b);
124
125 /*
126 * We are setting the *background* color, the foreground color
127 * will be set to its complement.
128 */
129 r = 0xff ^ (r >> 8);
130 g = 0xff ^ (g >> 8);
131 b = 0xff ^ (b >> 8);
132
[6a87f28]133 gfx_bitmap_params_init(&params);
134 params.rect.p0.x = 0;
135 params.rect.p0.y = 0;
136 params.rect.p1.x = str_width(str);
137 params.rect.p1.y = 1;
138
[17fac946]139 if (params.rect.p1.x == 0) {
140 /* Nothing to do. Avoid creating bitmap of zero width. */
141 return EOK;
142 }
143
[6a87f28]144 rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
145 if (rc != EOK)
146 return rc;
147
148 rc = gfx_bitmap_get_alloc(bitmap, &alloc);
149 if (rc != EOK) {
150 gfx_bitmap_destroy(bitmap);
151 return rc;
152 }
153
154 pmap.width = params.rect.p1.x;
155 pmap.height = 1;
156 pmap.data = alloc.pixels;
157
158 for (x = 0; x < params.rect.p1.x; x++) {
[b433f68]159 pixel = PIXEL(str[x], r, g, b);
[6a87f28]160 pixelmap_put_pixel(&pmap, x, 0, pixel);
161 }
162
163 rc = gfx_bitmap_render(bitmap, NULL, pos);
164
165 gfx_bitmap_destroy(bitmap);
166 return rc;
167}
168
[d63623f]169/** Get text starting position.
[8fa65af0]170 *
171 * @param font Font
172 * @param pos Anchor position
173 * @param fmt Text formatting
174 * @param str String
[d63623f]175 * @param spos Place to store starting position
[8fa65af0]176 */
[d63623f]177void gfx_text_start_pos(gfx_font_t *font, gfx_coord2_t *pos,
178 gfx_text_fmt_t *fmt, const char *str, gfx_coord2_t *spos)
[8fa65af0]179{
180 gfx_font_metrics_t fmetrics;
181 gfx_coord2_t cpos;
182 gfx_coord_t width;
183
[d63623f]184 *spos = *pos;
[8fa65af0]185
186 /* Adjust position for horizontal alignment */
187 if (fmt->halign != gfx_halign_left) {
188 width = gfx_text_width(font, str);
189 switch (fmt->halign) {
190 case gfx_halign_center:
[d63623f]191 spos->x -= width / 2;
[8fa65af0]192 break;
193 case gfx_halign_right:
[d63623f]194 spos->x -= width - 1;
[8fa65af0]195 break;
196 default:
197 break;
198 }
199 }
200
201 /* Adjust position for vertical alignment */
[8bf9058]202 gfx_font_get_metrics(font, &fmetrics);
[8fa65af0]203
[8bf9058]204 if (fmt->valign != gfx_valign_baseline) {
[8fa65af0]205 switch (fmt->valign) {
206 case gfx_valign_top:
[d63623f]207 spos->y += fmetrics.ascent;
[8fa65af0]208 break;
209 case gfx_valign_center:
[d63623f]210 spos->y += fmetrics.ascent / 2;
[8fa65af0]211 break;
[8bf9058]212 case gfx_valign_bottom:
213 cpos.y -= fmetrics.descent;
[0d62c10]214 break;
[8fa65af0]215 default:
216 break;
217 }
218 }
[d63623f]219}
220
221/** Render text.
222 *
223 * @param font Font
224 * @param pos Anchor position
225 * @param fmt Text formatting
226 * @param str String
227 * @return EOK on success or an error code
228 */
229errno_t gfx_puttext(gfx_font_t *font, gfx_coord2_t *pos,
230 gfx_text_fmt_t *fmt, const char *str)
231{
232 gfx_glyph_metrics_t gmetrics;
233 size_t stradv;
234 const char *cp;
235 gfx_glyph_t *glyph;
236 gfx_coord2_t cpos;
237 errno_t rc;
238
239 gfx_text_start_pos(font, pos, fmt, str, &cpos);
[8fa65af0]240
[6a87f28]241 /* Text mode */
242 if ((font->finfo->props.flags & gff_text_mode) != 0)
[b433f68]243 return gfx_puttext_textmode(font, &cpos, fmt->color, str);
244
245 rc = gfx_set_color(font->typeface->gc, fmt->color);
246 if (rc != EOK)
247 return rc;
[6a87f28]248
[8fa65af0]249 cp = str;
250 while (*cp != '\0') {
251 rc = gfx_font_search_glyph(font, cp, &glyph, &stradv);
252 if (rc != EOK) {
253 ++cp;
254 continue;
255 }
256
257 gfx_glyph_get_metrics(glyph, &gmetrics);
258
259 rc = gfx_glyph_render(glyph, &cpos);
260 if (rc != EOK)
261 return rc;
262
263 cp += stradv;
264 cpos.x += gmetrics.advance;
265 }
266
267 return EOK;
268}
269
[d63623f]270/** Find character position in string by X coordinate.
271 *
272 * @param font Font
273 * @param pos Anchor position
274 * @param fmt Text formatting
275 * @param str String
276 * @param fpos Position for which we need to find offset
277 *
278 * @return Byte offset in @a str of character corresponding to position
279 * @a fpos. Note that the position is rounded, that is,
280 * if it is before the center of character A, it will return
281 * offset of A, if it is after the center of A, it will return
282 * offset of the following character.
283 */
284size_t gfx_text_find_pos(gfx_font_t *font, gfx_coord2_t *pos,
285 gfx_text_fmt_t *fmt, const char *str, gfx_coord2_t *fpos)
286{
287 gfx_glyph_metrics_t gmetrics;
288 size_t stradv;
289 const char *cp;
290 gfx_glyph_t *glyph;
291 gfx_coord2_t cpos;
292 size_t off;
293 size_t strsize;
294 errno_t rc;
295
296 gfx_text_start_pos(font, pos, fmt, str, &cpos);
297
298 /* Text mode */
299 if ((font->finfo->props.flags & gff_text_mode) != 0) {
300 off = 0;
301 strsize = str_size(str);
302 while (off < strsize) {
303 if (fpos->x <= cpos.x)
304 return off;
305 (void) str_decode(str, &off, strsize);
306 cpos.x++;
307 }
308
309 return off;
310 }
311
312 cp = str;
313 off = 0;
314 while (*cp != '\0') {
315 rc = gfx_font_search_glyph(font, cp, &glyph, &stradv);
316 if (rc != EOK) {
317 ++cp;
318 continue;
319 }
320
321 gfx_glyph_get_metrics(glyph, &gmetrics);
322
323 if (fpos->x < cpos.x + gmetrics.advance / 2)
324 return off;
325
326 cp += stradv;
327 off += stradv;
328 cpos.x += gmetrics.advance;
329 }
330
331 return off;
332}
333
[9eb8d12]334/** Get text continuation parameters.
335 *
336 * Return the anchor position and format needed to continue printing
337 * text after the specified string. It is allowed for the sources
338 * (@a pos, @a fmt) and destinations (@a cpos, @a cfmt) to point
339 * to the same objects, respectively.
340 *
341 * @param font Font
342 * @param pos Anchor position
343 * @param fmt Text formatting
344 * @param str String
345 * @param cpos Place to store anchor position for continuation
346 * @param cfmt Place to store format for continuation
347 */
348void gfx_text_cont(gfx_font_t *font, gfx_coord2_t *pos,
349 gfx_text_fmt_t *fmt, const char *str, gfx_coord2_t *cpos,
350 gfx_text_fmt_t *cfmt)
351{
352 gfx_coord2_t spos;
353 gfx_text_fmt_t tfmt;
354
355 /* Continuation should start where the current string ends */
356 gfx_text_start_pos(font, pos, fmt, str, &spos);
357 cpos->x = spos.x + gfx_text_width(font, str);
358 cpos->y = spos.y;
359
360 /*
361 * Formatting is the same, except the text should be aligned
362 * so that it starts at the anchor point.
363 */
364 tfmt = *fmt;
365 tfmt.halign = gfx_halign_left;
366 tfmt.valign = gfx_valign_baseline;
367
368 *cfmt = tfmt;
369}
370
371/** Get text bounding rectangle.
372 *
373 * @param font Font
374 * @param pos Anchor position
375 * @param fmt Text formatting
376 * @param str String
377 * @param rect Place to store bounding rectangle
378 */
379void gfx_text_rect(gfx_font_t *font, gfx_coord2_t *pos,
380 gfx_text_fmt_t *fmt, const char *str, gfx_rect_t *rect)
381{
382 gfx_coord2_t spos;
383
384 gfx_text_start_pos(font, pos, fmt, str, &spos);
385
386 rect->p0.x = spos.x;
387 rect->p0.y = spos.y - font->metrics.ascent;
388 rect->p1.x = spos.x + gfx_text_width(font, str);
389 rect->p1.y = spos.y + font->metrics.descent + 1;
390}
391
[8fa65af0]392/** @}
393 */
Note: See TracBrowser for help on using the repository browser.