How do I load a .sfn font into my kernel?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
mrjbom
Member
Member
Posts: 317
Joined: Sun Jul 21, 2019 7:34 am

How do I load a .sfn font into my kernel?

Post by mrjbom »

Hi.
I want to use Scalable Screen Font in my kernel, but I don't understand how I can load a .sfn file.
As far as I understand, I should convert it to a .o file using objcopy and find a character there that points to the address of the beginning of characters and pass it to a Scalable Screen Font?
Tell me more.
Thanks.
Last edited by mrjbom on Mon Apr 27, 2020 1:57 pm, edited 2 times in total.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: How do I load a. sfn font into my kernel?

Post by bzt »

mrjbom wrote:Hi.
I want to use Scalable Screen Font in my kernel, but I don't understand how I can load a .sfn file.
The same way as you would load any other file. For example you could use your VFS implementation and locate the font in your initrd. Or you can load it from your boot partition using a simple FAT driver etc. You can also embed a font in your kernel. This is up to you.
mrjbom wrote:As far as I understand, I should convert it to a .o file using objcopy and find a character there that points to the address of the beginning of characters and pass it to a Scalable Screen Font?
Tell me more.
Thanks.
Yes, converting into .o and linking with your kernel is definitely one way to do it. Embedding is the simplest as absolutely no run-time support needed and therefore suits an early kernel console's needs (downside you have to recompile the kernel to replace the font).

Let's say you have a "console.sfn" file. Then you can use the linker for example like this:

Code: Select all

ld -r -b binary -o console.o console.sfn
This object file will contain a label, _binary_console_sfn_start. You can check this with

Code: Select all

readelf -s console.o
Now all you need to do is passing the address of this label to the SSFN renderer:

Code: Select all

extern unsigned char _binary_console_sfn_start;

/* for the simple renderer */
ssfn_font = &_binary_console_sfn_start;

/* for the userspace renderer */
ssfn_load(&ctx, &_binary_console_sfn_start);
Further examples can be found here. Some linkers (like MinGW's) add two underscores, and prefixes the filename with "__binary_".

Please note that the simple renderer (which is intended for kernel consoles) can only render unscaled bitmap fonts (converted from X11 Bitmap Fonts or PC Screen Fonts etc.). Its goal was to be simple and small (less than 1k and totally dependency-free). Passing an invalid font in ssfn_font results in UD. The normal userspace renderer requires libc, and in return supports all fonts (bitmap, pixmap, vector), scaling, anti-aliasing etc.

Some linkers do not support the "-b binary" option (like CLang on MacOSX), for those you can use objconv, or you can convert the font into a C header file, like "unsigned char _binary_console_font_start[] = { 0xXX, 0xXX, 0xXX, ... };". A simple awk or perl script can read the file and print out hex bytes to do this (or any other script language that I know, except shell script). In PHP or Python you can use the "unpack" function.

For example Linux uses this C array approach for basic fonts during early boot time, then it switches to console font loaded from the file system.

Cheers,
bzt
User avatar
mrjbom
Member
Member
Posts: 317
Joined: Sun Jul 21, 2019 7:34 am

Re: How do I load a. sfn font into my kernel?

Post by mrjbom »

bzt wrote:

Code: Select all

ld -r -b binary -o console.o console.sfn
This object file will contain a label, _binary_console_sfn_start. You can check this with
I have a problem with the layout of the .o file with font.
I use a cross-compiler for linking .o files.
I do it with this command:

Code: Select all

./i386-elf-4.9.1-Linux-x86_64/bin/i386-elf-gcc -T link.ld -o kernel-0 -ffreestanding ./o/bootloaderasm.o ./o/irqhandlersasm.o ./FreeSans.o ${buildObjectRoutes[@]} -nostdlib -lgcc
I get this error message:

Code: Select all

./FreeSans.o: file not recognized
gcc linker just doesn't want to work with the font object.
What's wrong?
Last edited by mrjbom on Sat Apr 25, 2020 6:56 am, edited 1 time in total.
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: How do I load a. sfn font into my kernel?

Post by iansjack »

You forgot to post the error message.
User avatar
mrjbom
Member
Member
Posts: 317
Joined: Sun Jul 21, 2019 7:34 am

Re: How do I load a. sfn font into my kernel?

Post by mrjbom »

iansjack wrote:You forgot to post the error message.
The problem was that I used ld that comes as part of the distribution, but I need to use ld that comes as part of the cross-compiler.
User avatar
mrjbom
Member
Member
Posts: 317
Joined: Sun Jul 21, 2019 7:34 am

Re: How do I load a. sfn font into my kernel?

Post by mrjbom »

bzt wrote: Now all you need to do is passing the address of this label to the SSFN renderer:

Code: Select all

extern unsigned char _binary_console_sfn_start;

/* for the simple renderer */
ssfn_font = &_binary_console_sfn_start;

/* for the userspace renderer */
ssfn_load(&ctx, &_binary_console_sfn_start);
I have a problem.
The symbol is not drawn, instead I see some garbage on the screen(these bars).
Image

Using ld from the cross compiler, I created .o file as you suggested.

Declared variables in .h file

Code: Select all

unsigned char _binary_FSfont_sfn_start;
unsigned char _binary_FSfont_sfn_end;
unsigned char _binary_FSfont_sfn_size;
Declared them in the .c file

Code: Select all

unsigned char _binary_FSfont_sfn_start;
unsigned char _binary_FSfont_sfn_end;
unsigned char _binary_FSfont_sfn_size;
Then I wrote this code:

Code: Select all

// set up context by global variables
ssfn_font = (ssfn_font_t*)&_binary_FSfont_sfn_start;     // the bitmap font to use
ssfn_dst_ptr = (uint8_t*)lfb_framebuffer_addr;        // framebuffer address and bytes per line
ssfn_dst_pitch = 4096;
ssfn_fg = 0xFFFF;                           // colors, white on black
ssfn_bg = 0;
ssfn_x = 100;                               // coordinates to draw to
ssfn_y = 200;
 
// render one glyph directly to the screen and then adjust ssfn_x and ssfn_y
dprintf("code: %i\n", ssfn_putc(0x41));
dprintf("_binary_FSfont_sfn_start = 0x%X\n", &_binary_FSfont_sfn_start);
dprintf("_binary_FSfont_sfn_end = 0x%X\n", &_binary_FSfont_sfn_end);
dprintf("_binary_FSfont_sfn_size = 0x%X\n", &_binary_FSfont_sfn_size);
The addresses are correct, and the error code is zero.

Code: Select all

code: 0
_binary_FSfont_sfn_start = 0x106000
_binary_FSfont_sfn_end = 0x1610c0
_binary_FSfont_sfn_size = 0x5b0c0
I believe that I incorrectly configured a certain variable, such as ssfn_dst_pitch, how do I configure it correctly?
How do I set this variable?
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: How do I load a. sfn font into my kernel?

Post by bzt »

mrjbom wrote: Declared them in the .c file

Code: Select all

unsigned char _binary_FSfont_sfn_start;
unsigned char _binary_FSfont_sfn_end;
unsigned char _binary_FSfont_sfn_size;
You should not declare them. They are provided by the .o file, so in your header you should define them as "extern". Btw, it is only the "_start" that you'll need.
mrjbom wrote:

Code: Select all

dprintf("code: %i\n", ssfn_putc(0x41));
I'm not sure what is your intend with this. The "ssfn_putc()" renders directly into the framebuffer, its return value is irrelevant.

Also I've warned you that "ssfn_putc()" does not check its input, and accepts only bitmap fonts. Is "_binary_FSfont_sfn_start" a bitmap font? I believe not, I think it's a vector font, converted from FreeSerif.ttf. Those can be displayed only by the normal user space renderer (as vector fonts needs rasterization, which depends on malloc to allocate raster lines).

To check if a font contains bitmap glyphs or vector glyphs, you can dump the font with "sfn2asc -d". If the "Fragments" section contains only SSFN_FRAG_BITMAP then you're good. Or, if you prefer GUIs, you can open the font with "sfnedit", and click on one character. If you see curves in the Edit window, then it's a vector glyph. If you see a bitmap image, then it's a bitmap glyph. SSFN files can store mixed glyphs (like all characters has vector contours except emojis are defined by colorful pixelmaps.)
mrjbom wrote:I believe that I incorrectly configured a certain variable, such as ssfn_dst_pitch, how do I configure it correctly?
How do I set this variable?
You get it from VESA info / GRUB MultiBoot struct. VESA calls it "scanline", but that's essentially the same thing as the pitch. In short, that's the number of bytes per line (which is not necessarily the same as width times byte per pixel). You must also set the proper define for your selected video mode. SSFN_CONSOLEBITMAP_HICOLOR is for 15 and 16 bit modes.

If you're using 32 bits, then include the console renderer with truecolor, and set the color with 32 bit:

Code: Select all

#define SSFN_CONSOLEBITMAP_TRUECOLOR
ssfn_fg = 0x00FFFFFF;
You should read the SSFN API documentation, it explains this all.
The define selects the destination buffer's pixel format. SSFN_CONSOLEBITMAP_PALETTE selects 1 byte
(indexed), SSFN_CONSOLEBITMAP_HICOLOR selects 2 bytes (5-5-5 or 5-6-5 RGB) and SSFN_CONSOLEBITMAP_TRUECOLOR
selects 4 bytes (8-8-8-8 xRGB). For performance reasons, 3 bytes (24 bit true color) mode is not supported.
The SSFN_CONSOLEBITMAP_CLEARBG can be added to any of the above defines. That will turn transparency off, and
will fill the glyph's background with ssfn_bg.
Cheers,
bzt
User avatar
mrjbom
Member
Member
Posts: 317
Joined: Sun Jul 21, 2019 7:34 am

Re: How do I load a. sfn font into my kernel?

Post by mrjbom »

bzt wrote:
mrjbom wrote: Declared them in the .c file

Code: Select all

unsigned char _binary_FSfont_sfn_start;
unsigned char _binary_FSfont_sfn_end;
unsigned char _binary_FSfont_sfn_size;
You should not declare them. They are provided by the .o file, so in your header you should define them as "extern". Btw, it is only the "_start" that you'll need.
mrjbom wrote:

Code: Select all

dprintf("code: %i\n", ssfn_putc(0x41));
I'm not sure what is your intend with this. The "ssfn_putc()" renders directly into the framebuffer, its return value is irrelevant.

Also I've warned you that "ssfn_putc()" does not check its input, and accepts only bitmap fonts. Is "_binary_FSfont_sfn_start" a bitmap font? I believe not, I think it's a vector font, converted from FreeSerif.ttf. Those can be displayed only by the normal user space renderer (as vector fonts needs rasterization, which depends on malloc to allocate raster lines).

To check if a font contains bitmap glyphs or vector glyphs, you can dump the font with "sfn2asc -d". If the "Fragments" section contains only SSFN_FRAG_BITMAP then you're good. Or, if you prefer GUIs, you can open the font with "sfnedit", and click on one character. If you see curves in the Edit window, then it's a vector glyph. If you see a bitmap image, then it's a bitmap glyph. SSFN files can store mixed glyphs (like all characters has vector contours except emojis are defined by colorful pixelmaps.)
mrjbom wrote:I believe that I incorrectly configured a certain variable, such as ssfn_dst_pitch, how do I configure it correctly?
How do I set this variable?
You get it from VESA info / GRUB MultiBoot struct. VESA calls it "scanline", but that's essentially the same thing as the pitch. In short, that's the number of bytes per line (which is not necessarily the same as width times byte per pixel). You must also set the proper define for your selected video mode. SSFN_CONSOLEBITMAP_HICOLOR is for 15 and 16 bit modes.

If you're using 32 bits, then include the console renderer with truecolor, and set the color with 32 bit:

Code: Select all

#define SSFN_CONSOLEBITMAP_TRUECOLOR
ssfn_fg = 0x00FFFFFF;
You should read the SSFN API documentation, it explains this all.
The define selects the destination buffer's pixel format. SSFN_CONSOLEBITMAP_PALETTE selects 1 byte
(indexed), SSFN_CONSOLEBITMAP_HICOLOR selects 2 bytes (5-5-5 or 5-6-5 RGB) and SSFN_CONSOLEBITMAP_TRUECOLOR
selects 4 bytes (8-8-8-8 xRGB). For performance reasons, 3 bytes (24 bit true color) mode is not supported.
The SSFN_CONSOLEBITMAP_CLEARBG can be added to any of the above defines. That will turn transparency off, and
will fill the glyph's background with ssfn_bg.
Cheers,
bzt
I understand, but I still have some difficulties in the visualization of the glyph. I am trying to draw a white letter A on a dark background, it is drawn correctly, but in addition to it, extra pixels were drawn at the bottom.
Also, the position of the letter does not match the position specified during rendering
Image
This is what the glyph creation code looks like:

Code: Select all

void test_func()
{
    ssfn_t ctx;                                          /* the renderer context */
    ssfn_glyph_t *glyph;                           /* the returned rasterized bitmap */

    memset(&ctx, 0, sizeof(ssfn_t));

    //FreeSans.sfn
    ssfn_load(&ctx, (ssfn_font_t*)&_binary_FSfont_sfn_start);

    ssfn_select(&ctx,
        SSFN_FAMILY_SANS, NULL,                             /* family */
        SSFN_STYLE_REGULAR /*| SSFN_STYLE_UNDERLINE*/, 64,  /* style and size */
        SSFN_MODE_BITMAP                                    /* rendering mode */
    );

    glyph = ssfn_render(&ctx, 0x41);

    //render gryph
    draw_glyph_lfb_mem(glyph, 400, 300, 0xFFFFFF);

    /* free resources */
    kfree(glyph);                                        /* no special treatment for freeing glyphs */
    ssfn_free(&ctx);                                    /* free the renderer context's internal buffers */
}
This is what the glyph rendering code looks like:

Code: Select all

void draw_glyph_lfb_mem(ssfn_glyph_t *glyph, int pen_x, int pen_y, uint32_t fgcolor)
{
    int x, y, i, m;

    /* align glyph properly, we may have received a vertical letter */
    if(glyph->adv_y)
        pen_x -= glyph->baseline;
    else
        pen_y -= glyph->baseline;

    switch(glyph->mode) {
        case SSFN_MODE_OUTLINE:
            if(glyph->pitch>1) {
                x = glyph->data[0]; y = glyph->data[1];
                for(i = 0; i < glyph->pitch; i += 2) {
                    /* end of a contour? */
                    if(glyph->data[i] == 255 && glyph->data[i+1] == 255) i += 2;
                    /* no, connect this point to the previous one. You should provide your own line() */
                    else draw_line_lfb_mem(pen_x + x, pen_y + y, pen_x + glyph->data[i], pen_y + glyph->data[i+1], fgcolor);
                    x = glyph->data[i]; y = glyph->data[i+1];
                }
            }
        break;

        case SSFN_MODE_BITMAP:
            for(y = 0; y < glyph->h; y++)
                for(x = 0, i = 0, m = 1; x < glyph->w; x++, m <<= 1) {
                    if(m > 0x80) { m = 1; i++; }
                    //SDL_PIXEL = (glyph->data[y * glyph->pitch + i] & m) ? 0xFF000000 | fgcolor : 0;
                    lfb_framebuffer_addr[y * MBI->framebuffer_pitch / 4 + x] = ((glyph->data[y * glyph->pitch + i] & m) ? 0xFF000000 | fgcolor : 0);
                }
        break;
/*
        case SSFN_MODE_ALPHA:
            for(y = 0; y < glyph->h; y++)
                for(x = 0; x < glyph->w; x++)
                    SDL_PIXEL = (uint32_t)((glyph->data[y * glyph->pitch + x] << 24) | fgcolor);
        break;

        case SSFN_MODE_CMAP:
            for(y = 0; y < glyph->h; y++)
                for(x = 0; x < glyph->w; x++)
                    SDL_PIXEL = SSFN_CMAP_TO_ARGB(glyph->data[y * glyph->pitch + x], glyph->cmap, fgcolor);
        break;
*/
    }
}
What am I doing wrong?
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: How do I load a. sfn font into my kernel?

Post by bzt »

Good to see you managed to load the font! I see you decided to go on with the user space renderer.
mrjbom wrote:Also, the position of the letter does not match the position specified during rendering
That's perfectly normal. Characters are drawn at their baseline, you have to take that into account. For example, pen_y is going to be the bottom most pixels for "A", but it is in the middle for "g".

Also you should pass a variable to "draw_glyph_lfb_mem", and then adjust that variable by glyph->adv_x to get the coordinate of the next character.

Read through the SSFN API, it starts with explaining the font metrics. If something is not straigtforward in the doc, please let me know and I'll add more explanations. Glyphs are scaled to their natural sizes. To scale the full glyph's size, use SSFN_STYLE_ABS_SIZE to style parameter (stands for absolute size). Note that with absolute size you'll get odd sized glyphs for "A" and "g" relative to each other, as the overall bounding box will be scaled.
mrjbom wrote:What am I doing wrong?
Not entirely sure, I need more information to help you. I see you don't use the ALPHA rendering, which is the prefered way (you fill up a box with a desired color, then you simply copy the alpha channel from the glyph, and voilá, you have an anti-aliased pixel map which you can blit as any other image). Just a suggestion, using bitmap is fine too.

From the picture it looks like your font has incorrect height set (or just as well could be right for 64 pixels, as many fonts has "tails" going beneath the baseline, however looks a bit too high). I'd suggest to print out the glyph's width, height and baseline values to see if they're correct. Not sure what font you're using. Dumping the font is very helpful. It will also do some very through verification, and it will warn you if some metrics are incorrectly set by any chance (like baseline is bigger than height for example). You can tweak those properties using command line options to the converters. Also if you see any problem, then you can fix the font easily: use sfn2asc to convert it into a text file. Then you can edit the font with a plain text editor, and check if you have an extremely high glyph that makes the entire font that high. Then use bit2sfn that will convert the text version back into binary (or use the correct options when converting).

Take a look at the aforementioned sfntest directory, there are many examples there both for simple renderer and user space renderer, and also for bitmap and vector fonts too. The fonts directory has some example fonts. First try those, and only use your own font once your code is working perfectly with the fonts in the repo. This way you can rule out if the problem is with your code or with the font itself.

For example, unifont.sfn.gz and u_vga16.sfn are a bitmap fonts, usable by ssfn_putc and ssfn_render both. FreeSans.sfn.gz is a vector font, converted from FreeSans.otf, known to have correct metrics. You can render this into a bitmap of a certain size using ssfn_render (but ssfn_putc can't use it as its not a bitmap font).

You can use sfntest1.c to see how to use ssfn_putc. sfntest2.c uses the user space renderer. Try to pass your FSfont.sfn font to these on the command line. If there's something wrong with your font metrics, you'll see.

Cheers,
bzt
User avatar
mrjbom
Member
Member
Posts: 317
Joined: Sun Jul 21, 2019 7:34 am

Re: How do I load a. sfn font into my kernel?

Post by mrjbom »

bzt wrote:Good to see you managed to load the font! I see you decided to go on with the user space renderer.
mrjbom wrote:Also, the position of the letter does not match the position specified during rendering
That's perfectly normal. Characters are drawn at their baseline, you have to take that into account. For example, pen_y is going to be the bottom most pixels for "A", but it is in the middle for "g".

Also you should pass a variable to "draw_glyph_lfb_mem", and then adjust that variable by glyph->adv_x to get the coordinate of the next character.

Read through the SSFN API, it starts with explaining the font metrics. If something is not straigtforward in the doc, please let me know and I'll add more explanations. Glyphs are scaled to their natural sizes. To scale the full glyph's size, use SSFN_STYLE_ABS_SIZE to style parameter (stands for absolute size). Note that with absolute size you'll get odd sized glyphs for "A" and "g" relative to each other, as the overall bounding box will be scaled.
mrjbom wrote:What am I doing wrong?
Not entirely sure, I need more information to help you. I see you don't use the ALPHA rendering, which is the prefered way (you fill up a box with a desired color, then you simply copy the alpha channel from the glyph, and voilá, you have an anti-aliased pixel map which you can blit as any other image). Just a suggestion, using bitmap is fine too.

From the picture it looks like your font has incorrect height set (or just as well could be right for 64 pixels, as many fonts has "tails" going beneath the baseline, however looks a bit too high). I'd suggest to print out the glyph's width, height and baseline values to see if they're correct. Not sure what font you're using. Dumping the font is very helpful. It will also do some very through verification, and it will warn you if some metrics are incorrectly set by any chance (like baseline is bigger than height for example). You can tweak those properties using command line options to the converters. Also if you see any problem, then you can fix the font easily: use sfn2asc to convert it into a text file. Then you can edit the font with a plain text editor, and check if you have an extremely high glyph that makes the entire font that high. Then use bit2sfn that will convert the text version back into binary (or use the correct options when converting).

Take a look at the aforementioned sfntest directory, there are many examples there both for simple renderer and user space renderer, and also for bitmap and vector fonts too. The fonts directory has some example fonts. First try those, and only use your own font once your code is working perfectly with the fonts in the repo. This way you can rule out if the problem is with your code or with the font itself.

For example, unifont.sfn.gz and u_vga16.sfn are a bitmap fonts, usable by ssfn_putc and ssfn_render both. FreeSans.sfn.gz is a vector font, converted from FreeSans.otf, known to have correct metrics. You can render this into a bitmap of a certain size using ssfn_render (but ssfn_putc can't use it as its not a bitmap font).

You can use sfntest1.c to see how to use ssfn_putc. sfntest2.c uses the user space renderer. Try to pass your FSfont.sfn font to these on the command line. If there's something wrong with your font metrics, you'll see.

Cheers,
bzt

I solved the problem with the fact that my letter was drawn in the wrong position on the axis that I indicated.
But I can't figure out how to align it vertically.
I want the upper-left corner of the letter to be in that pink dot.
Image

This is how I draw the glyph

Code: Select all

void draw_glyph_lfb_mem(ssfn_glyph_t *glyph, int pen_x, int pen_y, uint32_t fgcolor)
{
    int x, y, i, m;

    /* align glyph properly, we may have received a vertical letter */
    if(glyph->adv_y)
        pen_x -= glyph->baseline;
    else
        pen_y -= glyph->baseline;

    switch(glyph->mode) {
        case SSFN_MODE_OUTLINE:
            if(glyph->pitch>1) {
                x = glyph->data[0]; y = glyph->data[1];
                for(i = 0; i < glyph->pitch; i += 2) {
                    /* end of a contour? */
                    if(glyph->data[i] == 255 && glyph->data[i+1] == 255) i += 2;
                    /* no, connect this point to the previous one. You should provide your own line() */
                    else draw_line_lfb_mem(pen_x + x, pen_y + y, pen_x + glyph->data[i], pen_y + glyph->data[i+1], fgcolor);
                    x = glyph->data[i]; y = glyph->data[i+1];
                }
            }
        break;

        case SSFN_MODE_BITMAP:
            for(y = 0; y < glyph->h; y++)
                for(x = 0, i = 0, m = 1; x < glyph->w; x++, m <<= 1) {
                    if(m > 0x80) { m = 1; i++; }
                    //SDL_PIXEL = (glyph->data[y * glyph->pitch + i] & m) ? 0xFF000000 | fgcolor : 0;
                    lfb_framebuffer_addr[(pen_y + y - glyph->baseline) * MBI->framebuffer_pitch / 4 + (pen_x + x)] = ((glyph->data[y * glyph->pitch + i] & m) ? 0xFF000000 | fgcolor : 0);
                }
        break;

        case SSFN_MODE_ALPHA:
            for(y = 0; y < glyph->h; y++)
                for(x = 0; x < glyph->w; x++)
                    lfb_framebuffer_addr[(pen_y + y - glyph->baseline) * MBI->framebuffer_pitch / 4 + (pen_x + x)] = (uint32_t)((glyph->data[y * glyph->pitch + x] << 24) | fgcolor);
        break;

        case SSFN_MODE_CMAP:
            for(y = 0; y < glyph->h; y++)
                for(x = 0; x < glyph->w; x++)
                    lfb_framebuffer_addr[(pen_y + y - glyph->baseline) * MBI->framebuffer_pitch / 4 + (pen_x + x)] = SSFN_CMAP_TO_ARGB(glyph->data[y * glyph->pitch + x], glyph->cmap, fgcolor);
        break;
    }
}
How do I get rid of the extra dark area and align the letter vertically?
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: How do I load a .sfn font into my kernel?

Post by bzt »

Hi,

I can only repeat myself: Try your font with sfntest2, and try your code with one of the shipped fonts. Dump the font (sfn2asc -d) or convert it into a text version and open it in a text editor.

Maybe attach the font here so that I can take a look at it.

Just a sidenote, you don't align Latin letters vertically, you align them on their baseline. Otherwise you can align the generated glyph's bounding box. The ssfn_render function returns a (in your case) bitmap, you're free to place that anywhere on screen. Only ssfn_putc renders to the screen directly.

Cheers,
bzt
User avatar
mrjbom
Member
Member
Posts: 317
Joined: Sun Jul 21, 2019 7:34 am

Re: How do I load a .sfn font into my kernel?

Post by mrjbom »

bzt wrote:Hi,

I can only repeat myself: Try your font with sfntest2, and try your code with one of the shipped fonts. Dump the font (sfn2asc -d) or convert it into a text version and open it in a text editor.

Maybe attach the font here so that I can take a look at it.
This is a font that I took from a folder in your repository.
bzt wrote:Just a sidenote, you don't align Latin letters vertically, you align them on their baseline. Otherwise you can align the generated glyph's bounding box. The ssfn_render function returns a (in your case) bitmap, you're free to place that anywhere on screen. Only ssfn_putc renders to the screen directly.
Tell me if it is normal that the height of the glyph is disproportionately large compared to its width.
For example, in my case(and early reports show the code) I get the dimensions of the glyph.

Code: Select all

glyph height: 187
glyph width: 39
gryph baseline: 44
The API Font Metrics specifies that the height of the glyph should not be so large and should not be more than the size of the letter itself and not capture anything extra, but in my case it is too large. Due to the fact that the height of the glyph is so big, I get these problems.
I tried using FreeSerif from your repository, it doesn't solve the problem with glyph height, it's always too big.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: How do I load a .sfn font into my kernel?

Post by bzt »

mrjbom wrote:This is a font that I took from a folder in your repository.
So your FSfont.sfn file is actually the FreeSans font (converted from TTF). Good to know!
mrjbom wrote:Tell me if it is normal that the height of the glyph is disproportionately large compared to its width.
Yes, it could be. That depends on the glyphs defined. There are many letters which have acutes above and also below, not to mention Asian scripts like Devanagari. However showing "A" at the very top is definitely wrong, because FreeSans also has Latin scripts with accents.
mrjbom wrote:For example, in my case(and early reports show the code) I get the dimensions of the glyph.

Code: Select all

glyph height: 187
glyph width: 39
gryph baseline: 44
The API Font Metrics specifies that the height of the glyph should not be so large and should not be more than the size of the letter itself and not capture anything extra
No, you misunderstood. The height can be, and usually is larger than the size. Without absolute size style, the difference of the bearing top and the baseline is the size. So for example if you render a letter "A" in 64 pixels, then the difference between the top and bottom lines which have pixels set in "A" will be 64 pixels. The returned pixel map must be taller, because there must be place for letters like "Á" (more above) and "q" (more below). Use SSFN_STYLE_ABS_SIZE if you want the render to return a pixel map with height of exactly 64 pixels (but then the actual size of "A" will depend on the bounding box, aka the other glyphs defined in the font).

Anyway, I'll take a look at it, because at first 187 seems too big for 64 pixels size, and the baseline of 44 can't be right.

Cheers,
bzt
User avatar
mrjbom
Member
Member
Posts: 317
Joined: Sun Jul 21, 2019 7:34 am

Re: How do I load a .sfn font into my kernel?

Post by mrjbom »

bzt wrote:Use SSFN_STYLE_ABS_SIZE if you want the render to return a pixel map with height of exactly 64 pixels (but then the actual size of "A" will depend on the bounding box, aka the other glyphs defined in the font).
When this style is added, the height of the glyph actually becomes 64, but the width becomes approximately 10. The letter just gets smaller, but a huge dark space under it remains.

I also encountered this problem: some letters come down, their baseline is clearly lower than the rest of the letters.
Image

Code: Select all

    //FreeSans.sfn
    ssfn_load(&ctx, (ssfn_font_t*)&_binary_FSfont_sfn_start);

    ssfn_select(&ctx,
        SSFN_FAMILY_SANS, NULL,                             /* family */
        SSFN_STYLE_REGULAR /*| SSFN_STYLE_UNDERLINE*/, 64,  /* style and size */
        SSFN_MODE_BITMAP                                    /* rendering mode */
    );

    //render gryph
    int x = 400, y = 300;
    glyph = ssfn_render(&ctx, 'A');
    draw_glyph_lfb_mem(glyph, x, y, 0xFFFFFF);
    x += glyph->adv_x;
    y += glyph->adv_y;
    glyph = ssfn_render(&ctx, 'a');
    draw_glyph_lfb_mem(glyph, x, y, 0xFFFFFF);
    x += glyph->adv_x;
    y += glyph->adv_y;
    glyph = ssfn_render(&ctx, 'B');
    draw_glyph_lfb_mem(glyph, x, y, 0xFFFFFF);
    x += glyph->adv_x;
    y += glyph->adv_y;
    glyph = ssfn_render(&ctx, 'b');
    draw_glyph_lfb_mem(glyph, x, y, 0xFFFFFF);
I don't know why this happens because I don't change the font or anything.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: How do I load a .sfn font into my kernel?

Post by bzt »

Okay, we have two things here.

First, about the baseline, the problem is definitely in your code. I've quickly modified sfntest8: I've added 64 pixel size as the 7th line, and "AaBb" to the string. As you can see in the attachment it is rendered correctly in all sizes (28, 120 and finally 64).

Second, I can confirm that the rendered bitmap is taller than it should be. I did the math on paper, and with FreeSans using quality 6 (which has bounding box top 284, bottom 737 and baseline at 634), rendering in 64 pixels size should result in 82 pixels, not 187. This means there's an issue when calculating the height, which I'll find and fix.

Thanks for the feedback, I couldn't catch this bug without you! Until the fix arrives, I'd recommend printing characters with transparent background. You can clear the area first, then put the text (not the best solution, I know, but this is just a temporary workaround for the time being).

Cheers,
bzt
Attachments
sfn_sans.png
Post Reply