Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/gfxfont/src/text.c

    r0d62c10 rf2d4a46  
    11/*
    2  * Copyright (c) 2020 Jiri Svoboda
     2 * Copyright (c) 2022 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3535
    3636#include <errno.h>
     37#include <gfx/bitmap.h>
     38#include <gfx/color.h>
    3739#include <gfx/font.h>
    3840#include <gfx/glyph.h>
     41#include <gfx/render.h>
    3942#include <gfx/text.h>
     43#include <io/pixelmap.h>
    4044#include <mem.h>
     45#include <str.h>
     46#include "../private/font.h"
     47#include "../private/typeface.h"
    4148
    4249/** Initialize text formatting structure.
     
    6673        gfx_coord_t width;
    6774        errno_t rc;
     75
     76        if ((font->finfo->props.flags & gff_text_mode) != 0)
     77                return str_width(str);
    6878
    6979        width = 0;
     
    8595}
    8696
    87 /** Render text.
    88  *
    89  * @param font Font
     97/** Print string using text characters in text mode.
     98 *
     99 * @param pos Position of top-left corner of text
     100 * @param fmt Formatting
     101 * @param str String
     102 * @return EOK on success or an error code
     103 */
     104static errno_t gfx_puttext_textmode(gfx_coord2_t *pos, gfx_text_fmt_t *fmt,
     105    const char *str)
     106{
     107        gfx_context_t *gc = fmt->font->typeface->gc;
     108        gfx_bitmap_params_t params;
     109        gfx_bitmap_t *bitmap;
     110        gfx_bitmap_alloc_t alloc;
     111        gfx_coord_t width;
     112        uint8_t attr;
     113        pixelmap_t pmap;
     114        gfx_coord_t x;
     115        gfx_coord_t rmargin;
     116        pixel_t pixel;
     117        char32_t c;
     118        size_t off;
     119        bool ellipsis;
     120        errno_t rc;
     121
     122        width = str_width(str);
     123        if (fmt->abbreviate && width > fmt->width) {
     124                ellipsis = true;
     125                width = fmt->width;
     126                if (width > 3)
     127                        rmargin = width - 3;
     128                else
     129                        rmargin = width;
     130        } else {
     131                ellipsis = false;
     132                rmargin = width;
     133        }
     134
     135        /*
     136         * NOTE: Creating and destroying bitmap each time is not probably
     137         * the most efficient way.
     138         */
     139
     140        gfx_color_get_ega(fmt->color, &attr);
     141
     142        gfx_bitmap_params_init(&params);
     143        params.rect.p0.x = 0;
     144        params.rect.p0.y = 0;
     145        params.rect.p1.x = width;
     146        params.rect.p1.y = 1;
     147
     148        if (params.rect.p1.x == 0) {
     149                /* Nothing to do. Avoid creating bitmap of zero width. */
     150                return EOK;
     151        }
     152
     153        rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
     154        if (rc != EOK)
     155                return rc;
     156
     157        rc = gfx_bitmap_get_alloc(bitmap, &alloc);
     158        if (rc != EOK) {
     159                gfx_bitmap_destroy(bitmap);
     160                return rc;
     161        }
     162
     163        pmap.width = params.rect.p1.x;
     164        pmap.height = 1;
     165        pmap.data = alloc.pixels;
     166
     167        off = 0;
     168        for (x = 0; x < rmargin; x++) {
     169                c = str_decode(str, &off, STR_NO_LIMIT);
     170                pixel = PIXEL(attr,
     171                    (c >> 16) & 0xff,
     172                    (c >> 8) & 0xff,
     173                    c & 0xff);
     174                pixelmap_put_pixel(&pmap, x, 0, pixel);
     175        }
     176
     177        if (ellipsis) {
     178                for (x = rmargin; x < params.rect.p1.x; x++) {
     179                        c = '.';
     180                        pixel = PIXEL(attr,
     181                            (c >> 16) & 0xff,
     182                            (c >> 8) & 0xff,
     183                            c & 0xff);
     184                        pixelmap_put_pixel(&pmap, x, 0, pixel);
     185                }
     186        }
     187
     188        rc = gfx_bitmap_render(bitmap, NULL, pos);
     189
     190        gfx_bitmap_destroy(bitmap);
     191        return rc;
     192}
     193
     194/** Get text starting position.
     195 *
    90196 * @param pos Anchor position
    91197 * @param fmt Text formatting
    92198 * @param str String
     199 * @param spos Place to store starting position
     200 */
     201void gfx_text_start_pos(gfx_coord2_t *pos, gfx_text_fmt_t *fmt,
     202    const char *str, gfx_coord2_t *spos)
     203{
     204        gfx_font_metrics_t fmetrics;
     205        gfx_coord_t width;
     206
     207        *spos = *pos;
     208
     209        /* Adjust position for horizontal alignment */
     210        if (fmt->halign != gfx_halign_left) {
     211                /* Compute text width */
     212                width = gfx_text_width(fmt->font, str);
     213                if (fmt->abbreviate && width > fmt->width)
     214                        width = fmt->width;
     215
     216                switch (fmt->halign) {
     217                case gfx_halign_center:
     218                        spos->x -= width / 2;
     219                        break;
     220                case gfx_halign_right:
     221                        spos->x -= width;
     222                        break;
     223                default:
     224                        break;
     225                }
     226        }
     227
     228        /* Adjust position for vertical alignment */
     229        gfx_font_get_metrics(fmt->font, &fmetrics);
     230
     231        if (fmt->valign != gfx_valign_baseline) {
     232                switch (fmt->valign) {
     233                case gfx_valign_top:
     234                        spos->y += fmetrics.ascent;
     235                        break;
     236                case gfx_valign_center:
     237                        spos->y += fmetrics.ascent / 2;
     238                        break;
     239                case gfx_valign_bottom:
     240                        spos->y -= fmetrics.descent + 1;
     241                        break;
     242                default:
     243                        break;
     244                }
     245        }
     246}
     247
     248/** Render text.
     249 *
     250 * @param pos Anchor position
     251 * @param fmt Text formatting
     252 * @param str String
    93253 * @return EOK on success or an error code
    94254 */
    95 errno_t gfx_puttext(gfx_font_t *font, gfx_coord2_t *pos,
    96     gfx_text_fmt_t *fmt, const char *str)
    97 {
     255errno_t gfx_puttext(gfx_coord2_t *pos, gfx_text_fmt_t *fmt, const char *str)
     256{
     257        gfx_glyph_metrics_t gmetrics;
    98258        gfx_font_metrics_t fmetrics;
     259        size_t stradv;
     260        const char *cp;
     261        gfx_glyph_t *glyph;
     262        gfx_coord2_t cpos;
     263        gfx_coord2_t spos;
     264        gfx_rect_t rect;
     265        gfx_coord_t width;
     266        gfx_coord_t rmargin;
     267        bool ellipsis;
     268        errno_t rc;
     269
     270        gfx_text_start_pos(pos, fmt, str, &spos);
     271
     272        /* Text mode */
     273        if ((fmt->font->finfo->props.flags & gff_text_mode) != 0)
     274                return gfx_puttext_textmode(&spos, fmt, str);
     275
     276        rc = gfx_set_color(fmt->font->typeface->gc, fmt->color);
     277        if (rc != EOK)
     278                return rc;
     279
     280        width = gfx_text_width(fmt->font, str);
     281
     282        if (fmt->abbreviate && width > fmt->width) {
     283                /* Need to append ellipsis */
     284                ellipsis = true;
     285                rmargin = spos.x + fmt->width - gfx_text_width(fmt->font, "...");
     286        } else {
     287                ellipsis = false;
     288                rmargin = spos.x + width;
     289        }
     290
     291        cpos = spos;
     292        cp = str;
     293        while (*cp != '\0') {
     294                rc = gfx_font_search_glyph(fmt->font, cp, &glyph, &stradv);
     295                if (rc != EOK) {
     296                        ++cp;
     297                        continue;
     298                }
     299
     300                gfx_glyph_get_metrics(glyph, &gmetrics);
     301
     302                /* Stop if we would run over the right margin */
     303                if (fmt->abbreviate && cpos.x + gmetrics.advance > rmargin)
     304                        break;
     305
     306                rc = gfx_glyph_render(glyph, &cpos);
     307                if (rc != EOK)
     308                        return rc;
     309
     310                cp += stradv;
     311                cpos.x += gmetrics.advance;
     312        }
     313
     314        /* Text underlining */
     315        if (fmt->underline) {
     316                gfx_font_get_metrics(fmt->font, &fmetrics);
     317
     318                rect.p0.x = spos.x;
     319                rect.p0.y = spos.y + fmetrics.underline_y0;
     320                rect.p1.x = cpos.x;
     321                rect.p1.y = spos.y + fmetrics.underline_y1;
     322
     323                rc = gfx_fill_rect(fmt->font->typeface->gc, &rect);
     324                if (rc != EOK)
     325                        return rc;
     326        }
     327
     328        /* Render ellipsis, if required */
     329        if (ellipsis) {
     330                rc = gfx_font_search_glyph(fmt->font, ".", &glyph, &stradv);
     331                if (rc != EOK)
     332                        return EOK;
     333
     334                gfx_glyph_get_metrics(glyph, &gmetrics);
     335
     336                rc = gfx_glyph_render(glyph, &cpos);
     337                if (rc != EOK)
     338                        return rc;
     339
     340                cpos.x += gmetrics.advance;
     341
     342                rc = gfx_glyph_render(glyph, &cpos);
     343                if (rc != EOK)
     344                        return rc;
     345
     346                cpos.x += gmetrics.advance;
     347
     348                rc = gfx_glyph_render(glyph, &cpos);
     349                if (rc != EOK)
     350                        return rc;
     351        }
     352
     353        return EOK;
     354}
     355
     356/** Find character position in string by X coordinate.
     357 *
     358 * @param pos Anchor position
     359 * @param fmt Text formatting
     360 * @param str String
     361 * @param fpos Position for which we need to find offset
     362 *
     363 * @return Byte offset in @a str of character corresponding to position
     364 *         @a fpos. Note that the position is rounded, that is,
     365 *         if it is before the center of character A, it will return
     366 *         offset of A, if it is after the center of A, it will return
     367 *         offset of the following character.
     368 */
     369size_t gfx_text_find_pos(gfx_coord2_t *pos, gfx_text_fmt_t *fmt,
     370    const char *str, gfx_coord2_t *fpos)
     371{
    99372        gfx_glyph_metrics_t gmetrics;
    100373        size_t stradv;
     
    102375        gfx_glyph_t *glyph;
    103376        gfx_coord2_t cpos;
    104         gfx_coord_t width;
     377        size_t off;
     378        size_t strsize;
    105379        errno_t rc;
    106380
    107         cpos = *pos;
    108 
    109         /* Adjust position for horizontal alignment */
    110         if (fmt->halign != gfx_halign_left) {
    111                 width = gfx_text_width(font, str);
    112                 switch (fmt->halign) {
    113                 case gfx_halign_center:
    114                         cpos.x -= width / 2;
    115                         break;
    116                 case gfx_halign_right:
    117                         cpos.x -= width;
    118                         break;
    119                 default:
    120                         break;
    121                 }
    122         }
    123 
    124         /* Adjust position for vertical alignment */
    125         gfx_font_get_metrics(font, &fmetrics);
    126 
    127         if (fmt->valign != gfx_valign_baseline) {
    128                 switch (fmt->valign) {
    129                 case gfx_valign_top:
    130                         cpos.y += fmetrics.ascent;
    131                         break;
    132                 case gfx_valign_center:
    133                         cpos.y += fmetrics.ascent / 2;
    134                         break;
    135                 case gfx_valign_bottom:
    136                         cpos.y -= fmetrics.descent;
    137                         break;
    138                 default:
    139                         break;
    140                 }
     381        gfx_text_start_pos(pos, fmt, str, &cpos);
     382
     383        /* Text mode */
     384        if ((fmt->font->finfo->props.flags & gff_text_mode) != 0) {
     385                off = 0;
     386                strsize = str_size(str);
     387                while (off < strsize) {
     388                        if (fpos->x <= cpos.x)
     389                                return off;
     390                        (void) str_decode(str, &off, strsize);
     391                        cpos.x++;
     392                }
     393
     394                return off;
    141395        }
    142396
    143397        cp = str;
     398        off = 0;
    144399        while (*cp != '\0') {
    145                 rc = gfx_font_search_glyph(font, cp, &glyph, &stradv);
     400                rc = gfx_font_search_glyph(fmt->font, cp, &glyph, &stradv);
    146401                if (rc != EOK) {
    147402                        ++cp;
     
    151406                gfx_glyph_get_metrics(glyph, &gmetrics);
    152407
    153                 rc = gfx_glyph_render(glyph, &cpos);
    154                 if (rc != EOK)
    155                         return rc;
     408                if (fpos->x < cpos.x + gmetrics.advance / 2)
     409                        return off;
    156410
    157411                cp += stradv;
     412                off += stradv;
    158413                cpos.x += gmetrics.advance;
    159414        }
    160415
    161         return EOK;
     416        return off;
     417}
     418
     419/** Get text continuation parameters.
     420 *
     421 * Return the anchor position and format needed to continue printing
     422 * text after the specified string. It is allowed for the sources
     423 * (@a pos, @a fmt) and destinations (@a cpos, @a cfmt) to point
     424 * to the same objects, respectively.
     425 *
     426 * @param pos Anchor position
     427 * @param fmt Text formatting
     428 * @param str String
     429 * @param cpos Place to store anchor position for continuation
     430 * @param cfmt Place to store format for continuation
     431 */
     432void gfx_text_cont(gfx_coord2_t *pos, gfx_text_fmt_t *fmt, const char *str,
     433    gfx_coord2_t *cpos, gfx_text_fmt_t *cfmt)
     434{
     435        gfx_coord2_t spos;
     436        gfx_text_fmt_t tfmt;
     437
     438        /* Continuation should start where the current string ends */
     439        gfx_text_start_pos(pos, fmt, str, &spos);
     440        cpos->x = spos.x + gfx_text_width(fmt->font, str);
     441        cpos->y = spos.y;
     442
     443        /*
     444         * Formatting is the same, except the text should be aligned
     445         * so that it starts at the anchor point.
     446         */
     447        tfmt = *fmt;
     448        tfmt.halign = gfx_halign_left;
     449        tfmt.valign = gfx_valign_baseline;
     450
     451        /* Remaining available width */
     452        tfmt.width = fmt->width - (cpos->x - spos.x);
     453
     454        *cfmt = tfmt;
     455}
     456
     457/** Get text bounding rectangle.
     458 *
     459 * @param pos Anchor position
     460 * @param fmt Text formatting
     461 * @param str String
     462 * @param rect Place to store bounding rectangle
     463 */
     464void gfx_text_rect(gfx_coord2_t *pos, gfx_text_fmt_t *fmt, const char *str,
     465    gfx_rect_t *rect)
     466{
     467        gfx_coord2_t spos;
     468        gfx_coord_t width;
     469
     470        gfx_text_start_pos(pos, fmt, str, &spos);
     471        width = gfx_text_width(fmt->font, str);
     472        if (fmt->abbreviate && width > fmt->width)
     473                width = fmt->width;
     474
     475        rect->p0.x = spos.x;
     476        rect->p0.y = spos.y - fmt->font->metrics.ascent;
     477        rect->p1.x = spos.x + width;
     478        rect->p1.y = spos.y +  fmt->font->metrics.descent + 1;
    162479}
    163480
Note: See TracChangeset for help on using the changeset viewer.