Simple font rendering library

This forums is for OS project announcements including project openings, new releases, update notices, test requests, and job openings (both paying and volunteer).
TLHorse
Posts: 1
Joined: Tue Aug 11, 2020 7:18 am

A question of the transform between .SFN and .O

Post by TLHorse »

Hello everyone,

I'm currently developing a really simple GUI for my operating system. And I've just found this 1-year-ago topic of SSFN from this post: https://wiki.osdev.org/Scalable_Screen_Font

Recently I want to add the ability of rendering text (more precisely, Unicode) on VRam, I've tried to use PSFs format & plain data written in array, but these method are too... clumsy and difficult to maintain, so I changed my plan and started to use SSFN library to render text.

Unfortunately, my OS is still in early-stage. I cannot use any file function such as

Code: Select all

fread()
, I couldn't read data from SFN file directly in my project directory.[/b] The only way to read data from SFN, is to convert .sfn to .o file via objcopy when building, after that I can link datas in my code.

I found that there are a few fonts attached to the SSFN directory, I moved one of them, FreeSans.sfn, into my project directory. I simply created fonts.c and packaged an API:

Code: Select all

#define SSFN_CONSOLEBITMAP_HICOLOR
#include "font.h"
#include "ssfn.h"

extern char _binary_drivers_FreeSans_sfn_start;

/*
 * @brief Plot a character on the VRam.
 * 
 * @param vram Video RAM,
 * @param bpr Bytes per row.
 * @param x X axis of the char.
 * @param y Y axis of the char.
 * @param w Width of the screen.
 * @param h Height of the screen.
 * @param fg Foreground color.
 * @param bg Background color.
 * @param unicode Unicode of the char.
 */
void plot_font(uint8_t *vram, uint16_t bpr, int x, int y, int w, int h,
               uint32_t fg, uint32_t bg, uint32_t unicode) {
    ssfn_src = &_binary_drivers_FreeSans_sfn_start;
    ssfn_dst.ptr = vram;
    ssfn_dst.p = bpr;
    ssfn_dst.fg = fg;
    ssfn_dst.bg = bg;
    ssfn_dst.x = x;
    ssfn_dst.y = y;
    ssfn_dst.w = w;
    ssfn_dst.h = h;

    ssfn_putc(unicode);
}
My Makefile:

Code: Select all

C_SOURCES = ......
HEADERS = ......
OBJ = ${C_SOURCES:.c=.o ...... drivers/FreeSans.o}

CC = /path/to/cc
GDB = /usr/local/bin/gdb
CFLAGS = -g -ffreestanding -Wall -Wextra -fno-exceptions -m32

.............(omitted)

%.o: %.sfn
	i386-elf-objcopy -O elf32-i386 -B i386 -I binary $< $@
My project was successfully built, however it couldn't print the character 'A' when I use it in kernel.c. So I started debug. And I found that, in int ssfn_putc(uint32_t unicode), there's a line of code:

Code: Select all

    if(!ssfn_src || ssfn_src->magic[0] != 'S' || ssfn_src->magic[1] != 'F' || ssfn_src->magic[2] != 'N' ||
        ssfn_src->magic[3] != '2' || !ssfn_dst.ptr || !ssfn_dst.p) return SSFN_ERR_INVINP;
The whole function was returned due to this if statement. I printed ssfn_src->magic via gdb and found that, there is no magic SFN number of my .O file.Hence, my transform from .SFN to .O wasn't correct.

So, my question is, how do I transform .sfn to .o correctly? If there is no such method, are there any alternatives :?:
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Simple font rendering library

Post by bzt »

Hi,

Thanks for checking out!
TLHorse wrote:I cannot use any file function
Don't you worry, this is quite common for kernels. That's why SSFN expects an in memory byte array without any file-related callbacks.

Your "plot_font" function is not okay. You should set up the ssfn_src and ssfn_dst variables in the initialize phase, and then only call ssfn_putc(). This is because ssfn_putc() calculates and adjusts the pen coordinate. Imagine you write an "A", which is 8 pixel wide, then x should be increased by 8. Then imagine that you print a CJK ideogram, which is double wide. In that case x should be increased by 16. If you set ssfn_dst.x every time you plot a character, you'll loose that information.
TLHorse wrote:I printed ssfn_src->magic via gdb and found that, there is no magic SFN number of my .O file.Hence, my transform from .SFN to .O wasn't correct.
The question is, does it contain exactly the same bytes as the file?

First of all, if you have downloaded the font directly from the repo, then it is very likely gzip compressed. The normal renderer ssfn_load()/ssfn_render() can transparently uncompress those fonts, but since ssfn_putc() is not allowed to make any function calls (not even malloc) it requires an inflated font.

From the repository's main README:
IMPORTANT NOTE: unlike the normal renderer, this one (ssfn_putc) does not handle gzip compressed fonts. Always pass an inflated font in ssfn_src.
And from the docs/API.md section Render a Glyph with ssfn_putc:
Unlike the functions in the normal renderer, this function does not check its input, can't uncompress gzipped fonts on its own, and it can't handle font collections either. Passing invalid parameters results in an undefined behaviour. This is as simple as it gets.
As well as from docs/sfn_format.md in section Header
If the file starts with the bytes 0x1f and 0x8b, then it is stream compressed. You might want to uncompress it first using a gzip decompression filter (inflate). For command line, use gzip -d. The normal renderer is able to uncompress these fonts transparently, but in lack of memory management the simple renderer can't.
(Just a side note, if you use Midnight Commander F3 View, that also transparently uncompresses gzip, so you'll see the magic there while the file actually starts with the bytes 0x1F 0x8B.)

Try

Code: Select all

mv FreeSans.sfn FreeSans.sfn.gz
gzip -d FreeSans.sfn.gz
(gzip will rename .sfn.gz back to .sfn) After that check if the file starts with the "SFN2" magic. Then repeat the objcopy command and check in gdb that sfn->magic contains "SFN2" as well.

Btw, using objcopy is a good solution. Under POSIX compliant systems (read non-MacOS) I also like to use the ld command, like this:

Code: Select all

i386-elf-ld -r -b binary -o font.o FreeSans.sfn
Second, that FreeSans.sfn contains a vector font. Only the normal renderer can render those, ssfn_putc() is limited to bitmap fonts. Try downloading unifont.sfn.gz or u_vga16.sfn.gz (those are bitmap console fonts).

Alternatively you can rasterize any vector font into a bitmap font using sfnconv -B (height), for example

Code: Select all

./sfnconv -U -B 16 FreeSans.sfn sansconsole.sfn
(where -U tells sfnconv to save uncompressed (non-gzipped) font, and -B 16 means rasterize the vector font at 16 pixels and save as bitmap font.)

Finally, if you really wish to use scalable vector fonts on your console (not recommended), then use the normal renderer, read the API doc on memory management for details.

Cheers,
bzt
rabsym
Posts: 1
Joined: Wed Aug 19, 2020 2:59 am

Re: Simple font rendering library

Post by rabsym »

Hello @bzt

Would like to ask some questions about Scalable Screen Library Font 2.0 regarding using it in another free software proyect and maybe requiring some help to start integration.

Please /msg me, I tried to add you as friend. Thanks in advance.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Simple font rendering library

Post by bzt »

rabsym wrote:Hello @bzt

Would like to ask some questions about Scalable Screen Library Font 2.0 regarding using it in another free software proyect and maybe requiring some help to start integration.

Please /msg me, I tried to add you as friend. Thanks in advance.
PM sent. In general SSFN is a Free and Open Source Software licensed under the terms of MIT license. For getting help, please use the gitlab's issue tracker.

Cheers,
bzt
mrosdev
Member
Member
Posts: 28
Joined: Mon Jun 01, 2020 8:15 am

Re: Simple font rendering library

Post by mrosdev »

Wow nice!
Cdev
Posts: 8
Joined: Wed Mar 10, 2021 5:03 am

Re: Simple font rendering library

Post by Cdev »

I'm having quite the headache with this tool I so love the idea and implementation of .

While I really appreciate you documentation , @bzt , it is insufficient.

Firstly, your demo example should not be use "hicolor" but rather "truecolor" because almost every machine and kernel renders that way, but dummies like me spend 24 hours trying to figure out why their font is rendered corrupted.

Second, I'm using ssfn_putc() , and want to know how can I implement a proper putc-driver , because I see no way of moving to the next line ('\n') ? Doing '\n' has ssfn ignore it, trying to do ssfn_dst.y++; ssfn_dst.x=0; overwrite the current line rather than moving fwd, and ssfn_dst.y--; does not work either. PLEASE HELP , I love SSFN, need details on how to use it.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Simple font rendering library

Post by bzt »

Thanks for your feedback!
Cdev wrote:I'm having quite the headache with this tool I so love the idea and implementation of .

While I really appreciate you documentation , @bzt , it is insufficient.
What do you miss?
Cdev wrote:Firstly, your demo example should not be use "hicolor" but rather "truecolor" because almost every machine and kernel renders that way, but dummies like me spend 24 hours trying to figure out why their font is rendered corrupted.
The only difference between hicolor and truecolor is the define you use. Don't get me wrong, but dummies like you should read the documentation, not just copy'n'paste everything without understanding. (Please don't take this personal, this is a common issue that people are just copy'n'pasting without knowing what the code they copy does.) I've changed the example code in the wiki though.
Cdev wrote:Second, I'm using ssfn_putc() , and want to know how can I implement a proper putc-driver , because I see no way of moving to the next line ('\n') ? Doing '\n' has ssfn ignore it, trying to do ssfn_dst.y++; ssfn_dst.x=0; overwrite the current line rather than moving fwd, and ssfn_dst.y--; does not work either. PLEASE HELP , I love SSFN, need details on how to use it.
This isn't the job for the font rasterizer. Your idea is good, but ssfn_dst.y is in pixels, so you have to add the font's height. A simple solution would be:

Code: Select all

while(*s) {
  if(*s == '\n') { ssfn_dst.y += font->height; ssfn_dst.x = 0; }
  else ssfn_putc(*s);
  s++;
}
But I can add '\n' and '\r' support to ssfn_putc(). I've made some tweaks with the potrace parameters when converting bitmap fonts to vector fonts (still ain't perfect though), so I'll have to recompile the binaries anyway. I've also added a few sentences on how to handle newlines in the API documentation.

Hint: read the API documentation section "Render a Glyph" with ssfn_render(), it explains how to write to clipped areas (since that uses ssfn_buf_t, clipping works for ssfn_dst too). Otherwise ssfn_putc('\n') will always move the cursor to the left of the screen.

Cheers,
bzt
Cdev
Posts: 8
Joined: Wed Mar 10, 2021 5:03 am

Re: Simple font rendering library

Post by Cdev »

Alright , I respect your project and the effort you put into your documentation , but "you should read the documentation" doesn't apply here.

I read the API docu 3 times , and attempted to understand what your code was doing. I did not "just copy and paste" , I did lack the knowledge of the difference between hicolor and truecolor , because, well I haven't really developed any graphics or an OS before , and (very understandably , but notably) the docu 's comment for [/code]#define SSFN_CONSOLEBITMAP_HICOLOR[/code] was basically something like "use hicolor" . Of course, it is my ignorance to not know the difference , but that has nothing to do with reading the API docs, which obviously I did.

Further, I never was illusioned that your API would handle newline / other escape sequences , and you have every right to leave that to the developer using your putc. I was making my own putc for handling escape sequences anyways. However, the fact that ssfn_dst.y is pixels and not chars , was , at the time of posting my question , mentioned nowhere in the API docs. Had I known this it would have been trivial to firgure out the above. Thanks for clearing that up .If yu care for my opinion, you don't need to add support for escape sequences , just mention or hint at them in the docs and that's enough. The renderer's work is NOT handling input , but rather plotting output, but of course, you know best for your project.

"read the API documentation section "Render a Glyph" with ssfn_render()" - well , since I wasn't using it , I didn't pay much attention to it. Also, I don't quite understand what you mean by clipping. Will ssfn_putc attempt to (the only function I'm really interested in) write beyond ssfn_dst.w ?

NOTE : Since you are recompiling , though I'd let you know : the font editor won't work on macOS BigSur (latest) on my Intel mac
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Simple font rendering library

Post by bzt »

Cdev wrote:Alright , I respect your project and the effort you put into your documentation , but "you should read the documentation" doesn't apply here.

I read the API docu 3 times , and attempted to understand what your code was doing. I did not "just copy and paste" , I did lack the knowledge of the difference between hicolor and truecolor
Well, that's unfortunate, but I don't think it should be discussed in a font renderer's manual.
Cdev wrote:because, well I haven't really developed any graphics or an OS before , and (very understandably , but notably) the docu 's comment for [/code]#define SSFN_CONSOLEBITMAP_HICOLOR[/code] was basically something like "use hicolor" .
Yes, absolutely. I expect the readers to know the difference between packed pixel models. For example, if you read a book about file systems, there will be no explanation on how a sector is read or write, because that's supposed to be known.
Cdev wrote:Of course, it is my ignorance to not know the difference , but that has nothing to do with reading the API docs, which obviously I did.
Okay, sorry, I thought you are one of the many who just copy'n'pastes blindly and expects everything to work.
Cdev wrote:Further, I never was illusioned that your API would handle newline / other escape sequences , and you have every right to leave that to the developer using your putc. I was making my own putc for handling escape sequences anyways.
Yes, exactly. The "putc" in the name does not mean it's a putc implementation, rather it means it should be called from your putc. Console driver has to handle a lot of character sequences, and when it decides that the character is to be shown, that's when SSFN comes into play.
Cdev wrote:However, the fact that ssfn_dst.y is pixels and not chars , was , at the time of posting my question , mentioned nowhere in the API docs.
You are right, I should add that.
Cdev wrote:Had I known this it would have been trivial to firgure out the above. Thanks for clearing that up .
You're welcome.
Cdev wrote:If yu care for my opinion, you don't need to add support for escape sequences , just mention or hint at them in the docs and that's enough. The renderer's work is NOT handling input , but rather plotting output, but of course, you know best for your project.
Exactly. I've added newline for convenience, but I've mentioned it in the API.md doc that you should parse the sequences yourself.
Cdev wrote:"read the API documentation section "Render a Glyph" with ssfn_render()" - well , since I wasn't using it , I didn't pay much attention to it. Also, I don't quite understand what you mean by clipping. Will ssfn_putc attempt to (the only function I'm really interested in) write beyond ssfn_dst.w ?
If ssfn_dst.w is set, then no, it won't. Clipping means that you specify a rectangular area on screen where the render is allowed to draw. For a simple console, that area is the entire screen. But for a windowed GUI terminal, it could be anywhere on screen and could have any size (because you can resize and move the window around).
Cdev wrote:NOTE : Since you are recompiling , though I'd let you know : the font editor won't work on macOS BigSur (latest) on my Intel mac
What's the error message? I don't have BigSur, so there's absolutely no chance that I could compile or test it on that. If you are willing to provide detailed error messages in a gitlab issue, then I'll do my best to guide you how to fix.

Cheers,
bzt
Cdev
Posts: 8
Joined: Wed Mar 10, 2021 5:03 am

Re: Simple font rendering library

Post by Cdev »

I expect the readers to know the difference between packed pixel models.
Absolutely, my bad.

Thanks for adding my suggestions to the docs, hope it helps others. Thanks again for all the work in making ssfn, I'm quite glad to be able use it in my kernel, makes it easier to focus on more kernel-level things.
What's the error message?
Well it was something vauge and I don't remember quite so well, and I can't currently post a gitlab issue considering I'm out travelling without my mac. Apple does a lot of BS with their walled garden enforcement, breaking binaries left and right, so it's likely due to that. I'll try out the new release and get back to you if it doesn't work, which it likely won't , if you haven't changed anything in relation to macos-specific toolchain (if you have any). Out of curiosity , how do you build/test your OSX releases ?

Next, I have a small doubt...

The API docu says :
To clear the glyph's background, set ssfn_dst.bg to some value where the most
significant byte (alpha channel for true-color mode) is 255, like 0xFF000000.
Suppose I wish to render a back bg, how would one do so , since this seems like hex for red ?

Thanks and regards.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Simple font rendering library

Post by bzt »

Cdev wrote:Thanks for adding my suggestions to the docs, hope it helps others.
You're welcome, and thanks for your feedback!
Cdev wrote:Well it was something vauge and I don't remember quite so well, and I can't currently post a gitlab issue considering I'm out travelling without my mac. Apple does a lot of BS with their walled garden enforcement, breaking binaries left and right, so it's likely due to that. I'll try out the new release and get back to you if it doesn't work, which it likely won't , if you haven't changed anything in relation to macos-specific toolchain (if you have any).
Nope, I'm using the toolchain (which is called gcc but actually is a masquaraded CLang) installed by "xcode-select --install". Only verbatim, unmodified Apple software without any patches, no third-party tool used.

Say, does the command line sfnconv tool work? It doesn't have any dependencies, except for the libc of course. The GUI application sfnedit relies on SDL2, so maybe you could try to replace that with the latest version (it's under /Applications/SFNEdit.app/Contents/Frameworks, if you delete that directory, it will then fallback to the up-to-date one from /Library/Frameworks).
Cdev wrote:Out of curiosity , how do you build/test your OSX releases ?
Not on the latest Mac hardware with the latest MacOS, if that's what you're asking. I can't afford a Mac, so I must do hacks to get a running Darwin environment, but all the software and the Mach kernel are original.
Cdev wrote:The API docu says :
To clear the glyph's background, set ssfn_dst.bg to some value where the most
significant byte (alpha channel for true-color mode) is 255, like 0xFF000000.
Suppose I wish to render a back bg, how would one do so , since this seems like hex for red ?
Nope, as the documentation says that's the alpha-channel. Background is taken from the lower bits: 8 bit for paletted modes (the palette index), 16 bit for hicolor, and all the rest 24 bits for truecolor (8 bits per channel). While HTML colors are in byte order, 32 bit values on little endian machines (such as the x86) are reversed, so 4 bytes in memory 0x01, 0x02, 0x03, 0x04 in order becomes 0x04030201 when you access it as a single integer (with uint32_t). BTW the format is either 0xAARRGGBB or 0xAABBGGRR, and you can use a negative width in the destination ssfn_buf_t struct to make SSFN switch the red and blue channels (could be needed for colored glyphs). For ssfn_putc the channel order doesn't matter, only the pixel size as it only support bitmaps fonts and not colored outlines nor pixel fonts, so it just simply copies the color from the struct to the buffer unchanged.

Cheers,
bzt
Cdev
Posts: 8
Joined: Wed Mar 10, 2021 5:03 am

Re: Simple font rendering library

Post by Cdev »

Say, does the command line sfnconv tool work? It doesn't have any dependencies, except for the libc of course. The GUI application sfnedit relies on SDL2, so maybe you could try to replace that with the latest version (it's under /Applications/SFNEdit.app/Contents/Frameworks, if you delete that directory, it will then fallback to the up-to-date one from /Library/Frameworks).
I'm not sure if it does, but it should. I had only tried the GUI app. I'll test this once I'm back home with access to my mac and let you know. Would you prefer to continue that discussion in the forum or elsewhere ?
Nope, as the documentation says that's the alpha-channel.
Yeah I was guessing so too, those are the reserved 32-24=8 bits. Got it :)

I had a small doubt, when using this to print memory mapping entries i received from my bootloader in qemu-kvm + virt-man , there were 12 entries but I was only able to print 7, with the 8th not being printed fully and nothing thereafter appearing. I suspect this is due to reaching the end of the framebuffer's y-resolution ? How does ssfn_putc() handle reaching the end of framebuffer's height (does it scroll/stop printing/ start from the top again?) ; and if it doesn't handle it I'll handle it in my putc, but do let me know. Or is this got to do with that or is this something to do with the emulator itself ? I would verify by booting it on real hardware, but that's kind of impossible in my current situation.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Simple font rendering library

Post by bzt »

Cdev wrote:I'm not sure if it does, but it should. I had only tried the GUI app. I'll test this once I'm back home with access to my mac and let you know. Would you prefer to continue that discussion in the forum or elsewhere ?
Thanks! I think a gitlab issue would be the perfect place for this.
Cdev wrote:How does ssfn_putc() handle reaching the end of framebuffer's height (does it scroll/stop printing/ start from the top again?) ; and if it doesn't handle it I'll handle it in my putc, but do let me know.
That depends how you set it up. If ssfn_dst.w and ssfn_dst.h isn't set then it doesn't care, it expects that you call it with valid values only. If they are set, then printing will be clipped to that area, and no pixel will be modified outside (so let's say it stops printing in a sense). In either case, ssfn_putc() doesn't scroll the screen because that's the job for the console driver and not the font raster's. You should check ssfn_dst.y when you handle newlines, scroll the screen and subtract ssfn_src->heihgt from ssfn_dst.y. And if you want the text to be wrapped, then you should also check ssfn_dst.x to framebuffer width and move the pen to the next line. In general, cursor position is your responsibility, ssfn_putc just adjust pen position when it prints a character so that you don't have to look up character widths and advances.
Cdev wrote:Or is this got to do with that or is this something to do with the emulator itself ? I would verify by booting it on real hardware, but that's kind of impossible in my current situation.
I don't think so. It's more like up to the boot loader or the firmware to set up paging properly.

Cheers,
bzt
Samsonium
Posts: 2
Joined: Thu Sep 19, 2024 7:11 am

Re: Simple font rendering library

Post by Samsonium »

I had some problems using SSFN (simple renderer). The first problem was a “packed” font file (FreeSans) with GZip. This problem I was able to solve with the help of this thread. But a new problem appeared - `ssfn_putc` returns 0, but no characters appear on the screen.
At the moment I am using Limine bootloader, from which a framebuffer is provided. Here is the code I am trying to initialize the SSFN with:

Code: Select all

bool term_init()
{
    for (size_t i = 0; i < screen->height * screen->pitch / 4; i++)
        ((uint32_t *)screen->address)[i] = 0x1F1F1F;

    cursor_x = 0;
    cursor_y = 0;

    ssfn_src = (ssfn_font_t *)&_binary__opt_assets_FreeSans_sfn_start;
    ssfn_dst.ptr = screen->address;
    ssfn_dst.w = screen->width;
    ssfn_dst.h = screen->height;
    ssfn_dst.x = cursor_x;
    ssfn_dst.y = cursor_y;
    ssfn_dst.fg = 0xFFFFFFFF;
    ssfn_dst.p = screen->pitch;

    if(ssfn_putc(' ') != 0)
        return false;

    return true;
}
Where screen is just struct limine_framebuffer *screen. Here is the debug info from GDB:

Code: Select all

(gdb) print ssfn_src->magic
$2 = "SFN2"
(gdb) print ssfn_dst->ptr
$4 = (uint8_t *) 0xffff8000fd000000 "---"
(gdb) print ssfn_dst->width
There is no member named width.
(gdb) print ssfn_dst->w
$5 = 1280
(gdb) print ssfn_dst->h
$6 = 720
(gdb) print ssfn_dst->p
$7 = 5120
(gdb) print cursor_x
$8 = 0
(gdb) print cursor_y
$9 = 0
(gdb) print screen->address
$10 = (void *) 0xffff8000fd000000
I've been trying to figure it out for two days now, but I guess I don't understand something. I apologize for possibly asking stupid questions
thewrongchristian
Member
Member
Posts: 424
Joined: Tue Apr 03, 2018 2:44 am

Re: Simple font rendering library

Post by thewrongchristian »

Samsonium wrote: Thu Sep 19, 2024 7:21 am I had some problems using SSFN (simple renderer). The first problem was a “packed” font file (FreeSans) with GZip. This problem I was able to solve with the help of this thread. But a new problem appeared - `ssfn_putc` returns 0, but no characters appear on the screen.
At the moment I am using Limine bootloader, from which a framebuffer is provided. Here is the code I am trying to initialize the SSFN with:
...

I've been trying to figure it out for two days now, but I guess I don't understand something. I apologize for possibly asking stupid questions
@bzt hasn't been active on this forum for some time, more than 3 years in fact.

The gitlab repository still looks active though. So if you get no responses, it might be worth contacting bzt directory via gitlab.
Post Reply