[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH RESEND v3 1/2] font: Add font scaling feature to grub_font_dr
From: |
Daniel Kiper |
Subject: |
Re: [PATCH RESEND v3 1/2] font: Add font scaling feature to grub_font_draw_glyph() |
Date: |
Thu, 7 Jul 2022 16:01:34 +0200 |
User-agent: |
NeoMutt/20170113 (1.7.2) |
On Mon, Jun 27, 2022 at 05:35:24PM +0800, Zhang Boyang wrote:
> This patch adds an argument 'scale' to grub_font_draw_glyph(). If
> scale > 1, then the function will create a new scaled bitmap of the
> drawing glyph, and draws the scaled glyph. The scaled bitmap is cached
> in the glyph itself, so it can be reused if same glyph is used many
> times.
>
> To avoid interger overflow during scaling, this patch intruduces
> dimension limits to font files, described by GRUB_FONT_MAX_DIMENSION.
> The scaled glyph should also obey this limit. In addition, scale factor
> is also limited by GRUB_FONT_MAX_SCALE.
If you use overflow aware math operators defined as macros, e.g.
grub_mul(), defined in include/grub/safemath.h probably you
can drop these artificial limits.
> Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
> ---
> grub-core/commands/videotest.c | 4 +-
> grub-core/font/font.c | 120 ++++++++++++++++++++++++++++++---
> grub-core/gfxmenu/font.c | 2 +-
> grub-core/term/gfxterm.c | 2 +-
> include/grub/font.h | 16 ++++-
> 5 files changed, 131 insertions(+), 13 deletions(-)
>
> diff --git a/grub-core/commands/videotest.c b/grub-core/commands/videotest.c
> index ac145afc2..d95ee411d 100644
> --- a/grub-core/commands/videotest.c
> +++ b/grub-core/commands/videotest.c
> @@ -87,7 +87,7 @@ grub_cmd_videotest (grub_command_t cmd __attribute__
> ((unused)),
> return grub_error (GRUB_ERR_BAD_FONT, "no font loaded");
>
> glyph = grub_font_get_glyph (fixed, '*');
> - grub_font_draw_glyph (glyph, color, 200 ,0);
> + grub_font_draw_glyph (glyph, color, 200, 0, 1);
>
> color = grub_video_map_rgb (255, 255, 255);
>
> @@ -148,7 +148,7 @@ grub_cmd_videotest (grub_command_t cmd __attribute__
> ((unused)),
> {
> color = grub_video_map_color (i);
> palette[i] = color;
> - grub_font_draw_glyph (glyph, color, 16 + i * 16, 220);
> + grub_font_draw_glyph (glyph, color, 16 + i * 16, 220, 1);
> }
> }
>
> diff --git a/grub-core/font/font.c b/grub-core/font/font.c
> index 42189c325..46ffec84c 100644
> --- a/grub-core/font/font.c
> +++ b/grub-core/font/font.c
> @@ -137,6 +137,8 @@ ascii_glyph_lookup (grub_uint32_t code)
> ascii_font_glyph[current]->offset_x = 0;
> ascii_font_glyph[current]->offset_y = -2;
> ascii_font_glyph[current]->device_width = 8;
> + ascii_font_glyph[current]->scaled_bitmap = NULL;
> + ascii_font_glyph[current]->scale = 0;
> ascii_font_glyph[current]->font = NULL;
>
> grub_memcpy (ascii_font_glyph[current]->bitmap,
> @@ -172,6 +174,8 @@ grub_font_loader_init (void)
> unknown_glyph->offset_x = 0;
> unknown_glyph->offset_y = -3;
> unknown_glyph->device_width = 8;
> + unknown_glyph->scaled_bitmap = NULL;
> + unknown_glyph->scale = 0;
> grub_memcpy (unknown_glyph->bitmap,
> unknown_glyph_bitmap, sizeof (unknown_glyph_bitmap));
>
> @@ -646,6 +650,20 @@ grub_font_load (const char *filename)
> goto fail;
> }
>
> + if (font->max_char_width <= 0
> + || font->max_char_width > GRUB_FONT_MAX_DIMENSION
> + || font->max_char_height <= 0
> + || font->max_char_height > GRUB_FONT_MAX_DIMENSION
> + || font->ascent <= 0
> + || font->ascent > GRUB_FONT_MAX_DIMENSION
> + || font->descent <= 0
> + || font->descent > GRUB_FONT_MAX_DIMENSION)
> + {
> + grub_error (GRUB_ERR_BAD_FONT,
> + "invalid font file: bad font metrics");
grub_error (GRUB_ERR_BAD_FONT, N_("invalid font file: bad font metrics"));
Every grub_error() message has to be processed by N_() macro. And yes,
there is no space after N_ and before "(".
Additionally, I am OK with lines a bit longer than 80 chars. So, you can
put grub_error() call in one line here and below.
> + goto fail;
> + }
> +
> /* Add the font to the global font registry. */
> if (register_font (font) != 0)
> goto fail;
> @@ -738,7 +756,7 @@ grub_font_get_glyph_internal (grub_font_t font,
> grub_uint32_t code)
> grub_uint16_t height;
> grub_int16_t xoff;
> grub_int16_t yoff;
> - grub_int16_t dwidth;
> + grub_uint16_t dwidth;
> int len;
>
> if (index_entry->glyph)
> @@ -760,7 +778,18 @@ grub_font_get_glyph_internal (grub_font_t font,
> grub_uint32_t code)
> || read_be_uint16 (font->file, &height) != 0
> || read_be_int16 (font->file, &xoff) != 0
> || read_be_int16 (font->file, &yoff) != 0
> - || read_be_int16 (font->file, &dwidth) != 0)
> + || read_be_uint16 (font->file, &dwidth) != 0)
> + {
> + remove_font (font);
> + return 0;
> + }
> +
> + /* Reject illegal glyphs. */
> + if (width > font->max_char_width
> + || height > font->max_char_height
> + || xoff < -GRUB_FONT_MAX_DIMENSION || xoff > GRUB_FONT_MAX_DIMENSION
> + || yoff < -GRUB_FONT_MAX_DIMENSION || yoff > GRUB_FONT_MAX_DIMENSION
> + || dwidth > GRUB_FONT_MAX_DIMENSION)
> {
> remove_font (font);
> return 0;
> @@ -780,6 +809,8 @@ grub_font_get_glyph_internal (grub_font_t font,
> grub_uint32_t code)
> glyph->offset_x = xoff;
> glyph->offset_y = yoff;
> glyph->device_width = dwidth;
> + glyph->scaled_bitmap = NULL;
> + glyph->scale = 0;
>
> /* Don't try to read empty bitmaps (e.g., space characters). */
> if (len != 0)
> @@ -787,6 +818,7 @@ grub_font_get_glyph_internal (grub_font_t font,
> grub_uint32_t code)
> if (grub_file_read (font->file, glyph->bitmap, len) != len)
> {
> remove_font (font);
> + grub_free (glyph->scaled_bitmap);
> grub_free (glyph);
> return 0;
> }
> @@ -1054,6 +1086,8 @@ grub_font_dup_glyph (struct grub_font_glyph *glyph)
> return NULL;
> grub_memcpy (ret, glyph, sizeof (*ret)
> + (glyph->width * glyph->height + 7) / 8);
> + ret->scaled_bitmap = NULL;
> + ret->scale = 0;
> return ret;
> }
> #endif
> @@ -1524,11 +1558,22 @@ grub_font_construct_glyph (grub_font_t hinted_font,
>
> if (max_glyph_size < sizeof (*glyph) + (bounds.width * bounds.height +
> GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT)
> {
> - grub_free (glyph);
> + if (glyph)
> + {
> + grub_free (glyph->scaled_bitmap);
> + grub_free (glyph);
> + }
> max_glyph_size = (sizeof (*glyph) + (bounds.width * bounds.height +
> GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT) * 2;
> if (max_glyph_size < 8)
> max_glyph_size = 8;
> glyph = grub_malloc (max_glyph_size);
> + if (glyph)
> + {
> + glyph->scaled_bitmap = NULL;
> + glyph->scale = 0;
> + }
> + else
> + max_glyph_size = 0;
> }
> if (!glyph)
> {
> @@ -1536,6 +1581,7 @@ grub_font_construct_glyph (grub_font_t hinted_font,
> return main_glyph;
> }
>
> + grub_free (glyph->scaled_bitmap);
> grub_memset (glyph, 0, sizeof (*glyph)
> + (bounds.width * bounds.height
> + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT);
> @@ -1545,6 +1591,8 @@ grub_font_construct_glyph (grub_font_t hinted_font,
> glyph->height = bounds.height;
> glyph->offset_x = bounds.x;
> glyph->offset_y = bounds.y;
> + glyph->scaled_bitmap = NULL;
> + glyph->scale = 0;
>
> if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)
> grub_font_blit_glyph_mirror (glyph, main_glyph,
> @@ -1563,12 +1611,50 @@ grub_font_construct_glyph (grub_font_t hinted_font,
> return glyph;
> }
>
> +/* Try to scale the glyph bitmap. If failed, glyph will not be touched.
> + If succeeded, scaled bitmap will be stored at glyph->scaled_bitmap , and
> + the effective scale factor will be stored at glyph->scale . */
Incorrectly formatted comment. Please fix this. Here you can find more
details about comments formatting [1].
> +static void
> +try_scale_glyph (struct grub_font_glyph * glyph, int scale)
> +{
> + int i, j;
> + grub_uint8_t *bitmap;
> +
> + if (glyph->scale == scale)
> + return;
> +
> + if (scale > GRUB_FONT_MAX_SCALE
> + || glyph->height * scale > GRUB_FONT_MAX_DIMENSION
> + || glyph->width * scale > GRUB_FONT_MAX_DIMENSION)
> + return;
> +
> + bitmap = grub_zalloc (((glyph->width * scale) *
> + (glyph->height * scale) + 7) / 8);
grub_calloc()? And I think you should use grub_mul() and grub_add()
macros here and below...
> + if (!bitmap)
if (bitmap = NULL)
Please use NULL explicitly in such comparisons.
> + return;
> +
> + /* FIXME: suboptimal. */
> + for (i = 0; i < glyph->height * scale; i++)
> + for (j = 0; j < glyph->width * scale; j++)
> + {
> + int n = (i / scale) * glyph->width + (j / scale);
> + int m = i * (glyph->width * scale) + j;
> + int pixel = (glyph->bitmap[n / 8] >> (7 - n % 8)) & 1;
> + bitmap[m / 8] |= pixel << (7 - m % 8);
> + }
> +
> + grub_free (glyph->scaled_bitmap);
> + glyph->scaled_bitmap = bitmap;
> + glyph->scale = scale;
> +}
> +
> /* Draw the specified glyph at (x, y). The y coordinate designates the
> baseline of the character, while the x coordinate designates the left
> side location of the character. */
> grub_err_t
> grub_font_draw_glyph (struct grub_font_glyph * glyph,
> - grub_video_color_t color, int left_x, int baseline_y)
> + grub_video_color_t color, int left_x, int baseline_y,
> + int scale)
> {
> struct grub_video_bitmap glyph_bitmap;
>
> @@ -1601,11 +1687,29 @@ grub_font_draw_glyph (struct grub_font_glyph * glyph,
> &glyph_bitmap.mode_info.fg_alpha);
> glyph_bitmap.data = glyph->bitmap;
>
> - int bitmap_left = left_x + glyph->offset_x;
> - int bitmap_bottom = baseline_y - glyph->offset_y;
> - int bitmap_top = bitmap_bottom - glyph->height;
> + if (scale > 1)
> + {
> + try_scale_glyph (glyph, scale);
> + if (glyph->scale == scale)
> + {
> + glyph_bitmap.mode_info.width = glyph->width * scale;
> + glyph_bitmap.mode_info.height = glyph->height * scale;
> + glyph_bitmap.mode_info.pitch = glyph->width * scale;
> + glyph_bitmap.data = glyph->scaled_bitmap;
> + }
> + else
> + {
> + /* Scaled bitmap not suitable, fallback to no-scale. */
> + scale = 1;
> + }
> + }
> +
> + int bitmap_left = left_x + glyph->offset_x * scale;
> + int bitmap_bottom = baseline_y - glyph->offset_y * scale;
> + int bitmap_top = bitmap_bottom - glyph->height * scale;
You do a lot of scaling math here and there. It would be probably better
if you use safe math macros wrapped in some scaling functions/macros.
> return grub_video_blit_bitmap (&glyph_bitmap, GRUB_VIDEO_BLIT_BLEND,
> bitmap_left, bitmap_top,
> - 0, 0, glyph->width, glyph->height);
> + 0, 0,
> + glyph->width * scale, glyph->height * scale);
> }
> diff --git a/grub-core/gfxmenu/font.c b/grub-core/gfxmenu/font.c
> index 756c24f20..ed59ca954 100644
> --- a/grub-core/gfxmenu/font.c
> +++ b/grub-core/gfxmenu/font.c
> @@ -67,7 +67,7 @@ grub_font_draw_string (const char *str, grub_font_t font,
> err = grub_errno;
> goto out;
> }
> - err = grub_font_draw_glyph (glyph, color, x, baseline_y);
> + err = grub_font_draw_glyph (glyph, color, x, baseline_y, 1);
> if (err)
> goto out;
> x += glyph->device_width;
> diff --git a/grub-core/term/gfxterm.c b/grub-core/term/gfxterm.c
> index 3c468f459..4512dee6f 100644
> --- a/grub-core/term/gfxterm.c
> +++ b/grub-core/term/gfxterm.c
> @@ -656,7 +656,7 @@ paint_char (unsigned cx, unsigned cy)
> /* Render glyph to text layer. */
> grub_video_set_active_render_target (text_layer);
> grub_video_fill_rect (bgcolor, x, y, width, height);
> - grub_font_draw_glyph (glyph, color, x, y + ascent);
> + grub_font_draw_glyph (glyph, color, x, y + ascent, 1);
> grub_video_set_active_render_target (render_target);
>
> /* Mark character to be drawn. */
> diff --git a/include/grub/font.h b/include/grub/font.h
> index 708fa42ac..8fc646713 100644
> --- a/include/grub/font.h
> +++ b/include/grub/font.h
> @@ -24,6 +24,13 @@
> #include <grub/file.h>
> #include <grub/unicode.h>
>
> +/* Limit font dimensions to avoid interger overflow. Because some font
> metrics
> + may be derived from this limit, we must reserve some space for
> arithmetics,
> + so this value can't be too high. When scaling fonts, this limit should
> also
> + applies to the font after scaling. */
Please fix this comment formatting.
> +#define GRUB_FONT_MAX_DIMENSION 500
> +#define GRUB_FONT_MAX_SCALE 10
> +
> /* Forward declaration of opaque structure grub_font.
> Users only pass struct grub_font pointers to the font module functions,
> and do not have knowledge of the structure contents. */
Ditto and below please...
> @@ -79,6 +86,12 @@ struct grub_font_glyph
> /* Number of pixels to advance to start the next character. */
> grub_uint16_t device_width;
>
> + /* Pointer to cached scaled bitmap. Allocated during a scaling drawing.
> */
> + grub_uint8_t *scaled_bitmap;
> +
> + /* The scale factor for cached scaled bitmap. */
> + grub_uint16_t scale;
> +
> /* Row-major order, packed bits (no padding; rows can break within a byte).
> The length of the array is (width * height + 7) / 8. Within a
> byte, the most significant bit is the first (leftmost/uppermost) pixel.
> @@ -141,7 +154,8 @@ struct grub_font_glyph *EXPORT_FUNC
> (grub_font_get_glyph_with_fallback) (grub_fo
>
> grub_err_t EXPORT_FUNC (grub_font_draw_glyph) (struct grub_font_glyph *glyph,
> grub_video_color_t color,
> - int left_x, int baseline_y);
> + int left_x, int baseline_y,
> + int scale);
>
> int
> EXPORT_FUNC (grub_font_get_constructed_device_width) (grub_font_t
> hinted_font,
Daniel
[1] https://www.gnu.org/software/grub/manual/grub-dev/grub-dev.html#Comments
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Re: [PATCH RESEND v3 1/2] font: Add font scaling feature to grub_font_draw_glyph(),
Daniel Kiper <=