Ignore:
File:
1 edited

Legend:

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

    rf2d4a46 r0d62c10  
    11/*
    2  * Copyright (c) 2022 Jiri Svoboda
     2 * Copyright (c) 2020 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3535
    3636#include <errno.h>
    37 #include <gfx/bitmap.h>
    38 #include <gfx/color.h>
    3937#include <gfx/font.h>
    4038#include <gfx/glyph.h>
    41 #include <gfx/render.h>
    4239#include <gfx/text.h>
    43 #include <io/pixelmap.h>
    4440#include <mem.h>
    45 #include <str.h>
    46 #include "../private/font.h"
    47 #include "../private/typeface.h"
    4841
    4942/** Initialize text formatting structure.
     
    7467        errno_t rc;
    7568
    76         if ((font->finfo->props.flags & gff_text_mode) != 0)
    77                 return str_width(str);
    78 
    7969        width = 0;
    8070        cp = str;
     
    9585}
    9686
    97 /** Print string using text characters in text mode.
     87/** Render text.
    9888 *
    99  * @param pos Position of top-left corner of text
    100  * @param fmt Formatting
     89 * @param font Font
     90 * @param pos Anchor position
     91 * @param fmt Text formatting
    10192 * @param str String
    10293 * @return EOK on success or an error code
    10394 */
    104 static errno_t gfx_puttext_textmode(gfx_coord2_t *pos, gfx_text_fmt_t *fmt,
    105     const char *str)
     95errno_t gfx_puttext(gfx_font_t *font, gfx_coord2_t *pos,
     96    gfx_text_fmt_t *fmt, const char *str)
    10697{
    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;
     98        gfx_font_metrics_t fmetrics;
     99        gfx_glyph_metrics_t gmetrics;
     100        size_t stradv;
     101        const char *cp;
     102        gfx_glyph_t *glyph;
     103        gfx_coord2_t cpos;
    111104        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;
    120105        errno_t rc;
    121106
    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  *
    196  * @param pos Anchor position
    197  * @param fmt Text formatting
    198  * @param str String
    199  * @param spos Place to store starting position
    200  */
    201 void 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;
     107        cpos = *pos;
    208108
    209109        /* Adjust position for horizontal alignment */
    210110        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 
     111                width = gfx_text_width(font, str);
    216112                switch (fmt->halign) {
    217113                case gfx_halign_center:
    218                         spos->x -= width / 2;
     114                        cpos.x -= width / 2;
    219115                        break;
    220116                case gfx_halign_right:
    221                         spos->x -= width;
     117                        cpos.x -= width;
    222118                        break;
    223119                default:
     
    227123
    228124        /* Adjust position for vertical alignment */
    229         gfx_font_get_metrics(fmt->font, &fmetrics);
     125        gfx_font_get_metrics(font, &fmetrics);
    230126
    231127        if (fmt->valign != gfx_valign_baseline) {
    232128                switch (fmt->valign) {
    233129                case gfx_valign_top:
    234                         spos->y += fmetrics.ascent;
     130                        cpos.y += fmetrics.ascent;
    235131                        break;
    236132                case gfx_valign_center:
    237                         spos->y += fmetrics.ascent / 2;
     133                        cpos.y += fmetrics.ascent / 2;
    238134                        break;
    239135                case gfx_valign_bottom:
    240                         spos->y -= fmetrics.descent + 1;
     136                        cpos.y -= fmetrics.descent;
    241137                        break;
    242138                default:
     
    244140                }
    245141        }
    246 }
    247142
    248 /** Render text.
    249  *
    250  * @param pos Anchor position
    251  * @param fmt Text formatting
    252  * @param str String
    253  * @return EOK on success or an error code
    254  */
    255 errno_t gfx_puttext(gfx_coord2_t *pos, gfx_text_fmt_t *fmt, const char *str)
    256 {
    257         gfx_glyph_metrics_t gmetrics;
    258         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;
    292143        cp = str;
    293144        while (*cp != '\0') {
    294                 rc = gfx_font_search_glyph(fmt->font, cp, &glyph, &stradv);
     145                rc = gfx_font_search_glyph(font, cp, &glyph, &stradv);
    295146                if (rc != EOK) {
    296147                        ++cp;
     
    299150
    300151                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;
    305152
    306153                rc = gfx_glyph_render(glyph, &cpos);
     
    312159        }
    313160
    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 
    353161        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  */
    369 size_t gfx_text_find_pos(gfx_coord2_t *pos, gfx_text_fmt_t *fmt,
    370     const char *str, gfx_coord2_t *fpos)
    371 {
    372         gfx_glyph_metrics_t gmetrics;
    373         size_t stradv;
    374         const char *cp;
    375         gfx_glyph_t *glyph;
    376         gfx_coord2_t cpos;
    377         size_t off;
    378         size_t strsize;
    379         errno_t rc;
    380 
    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;
    395         }
    396 
    397         cp = str;
    398         off = 0;
    399         while (*cp != '\0') {
    400                 rc = gfx_font_search_glyph(fmt->font, cp, &glyph, &stradv);
    401                 if (rc != EOK) {
    402                         ++cp;
    403                         continue;
    404                 }
    405 
    406                 gfx_glyph_get_metrics(glyph, &gmetrics);
    407 
    408                 if (fpos->x < cpos.x + gmetrics.advance / 2)
    409                         return off;
    410 
    411                 cp += stradv;
    412                 off += stradv;
    413                 cpos.x += gmetrics.advance;
    414         }
    415 
    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  */
    432 void 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  */
    464 void 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;
    479162}
    480163
Note: See TracChangeset for help on using the changeset viewer.