OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Apr 16, 2024 1:48 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: Using the frame-buffer information?
PostPosted: Fri Mar 19, 2021 5:50 am 
Offline
Member
Member

Joined: Sat Feb 20, 2021 3:11 pm
Posts: 93
I came to a stage where I finally implement the output to the screen after a long time debugging with serial. I have read several articles about how to use the frame-buffer, and implemented an easy putPixel function that works correctly. Now I need to output the text to the frame-buffer - I of course found a lot of other people's code, but I don't really wan't to read it as then most of the calculation things in the frame-buffer driver won't not done by me, which is not interesting. So I would want to ask for some resources and information about how to output fonts to the screen, use the framebuffer...

Notes: I am using UEFI GOP with 640x480 resolution and 32 BPP. I don't have the disk driver and my boot-loader exits the UEFI boot services not giving me access to file routines and in general that is not what I want now - so I would like to be able to store fonts like https://github.com/Jacob-C-Smith/GlucOS ... urce/VBE.c and not in something like a PSF.

Also I have read the article https://wiki.osdev.org/Drawing_In_Protected_Mode


Last edited by rpio on Sat Mar 20, 2021 12:18 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Using the frame-buffer information?
PostPosted: Fri Mar 19, 2021 8:38 am 
Offline
Member
Member
User avatar

Joined: Mon Jun 05, 2006 11:00 pm
Posts: 2293
Location: USA (and Australia)
There are three operations that are super userful:
- Rectangle filling
- Line drawing
- Bit Blit

Rectangle filling is super easy. You just loop over every pixel between min_x, max_x, and min_y, and max_y and overwrite the pixel with the color you care about.

Line drawing is more interesting. Drawing straight horizitonal and vertical lines are easy.

For diagonal lines, you'd want to pick a line drawing algorithm

Blending colors together is easy. Colors are usually broken into 3 channels (Red, Green, Blue) and sometimes a 4th (Alpha). Assuming colors are in the range of 0.0 -> 1.0, the formula for alpha blending is:
output[channel] = background[channel] * (1.0 - alpha) + foreground[channel] * alpha

(That's the theory, but instead of 0.0 -> 1.0, you're probably using bytes from 0 to 255. The theory doesn't change, you just need to adjust the math.)

Bit blitting is basically copying an image to the screen, but you skip the transparent pixels. An easy example is to think of the mouse cursor. You define it as a grid of pixels, but a mouse isn't square so the pixels around the outside of the mouse cursor shape are transparent, and your bit blit algorithm skips those pixels.

You'll also hear double buffering. If the image you want to draw is composed of multiple operations (e.g. clear a color, draw some lines over it, draw a font over it), then if you draw straight into the framebuffer, the user's display could show the pixel at any time, so the image can flicker because the user will briefly see pixels that are supposed to be overritten by other pixels (e.g. it'll flicker between the background color and foreground text). So double buffering means you allocate an array (also known as a backbuffer, or texture), and you do your drawing operations into this array, and only once you have the final state of the pixels you copy it across to the frame buffer.

---

Now let's talk about fonts:
Quick and dirty answer:

Until I get to the stage where I can load fonts dynamically off disk, I found it super easy to get started with inlining the rasterized font into a header: e.g https://github.com/AndrewAPrice/Perception/blob/master/third_party/DejaVuSans.inl

But.. I can't find the stb_font_inl_generator.c that I used that generated that header. I did find a large collection of fonts here, generated using the same method:
https://github.com/stetre/moonfonts/tree/master/src/fonts

What the generator did was rasterize (convert vector graphics into pixel graphics) the font at a certain size, then embed those pixels into a C header.

Using the font is super simple, here's my loading drawing code: https://github.com/AndrewAPrice/Perception/blob/master/Libraries/perception/source/perception/font.cc It loads two things - a grid of alpha values (a giant texture with all of the characters on it), and details about each character.

The details of each character include where in the grid of alpha values the texture lives so you can copy it to the screen, and how wide each character is. We care about how wide our characters are because that lets us measure strings by looping over each character and summing their widths. We care about measuring strings because then we can center align text, or make sure text isn't too big for the container we're rendering it in.

The texture is a a grid of pixels by their alpha value, and you use that to determine how to blend the font color with the background.

---

Long answer:

Most fonts are vector fonts - fonts that are made up of lines instead of pixels:
Image
These lines are often Bézier curves and they are stored in a file format such as TrueType. Because they are vector format (described as lines instead of pixels), the fonts can be rasterized (coverted to pixels) at any resolution. A common rasterizing technique is called scanline rendering where you calculate a bounding box, and for each line, you walk the pixels left-to-right, and everytime you pass a line you flip between filled and empty:
Image

The author of stb_truetype (which looks super easy to embed into an OS) wrote about his technique here: http://nothings.org/gamedev/rasterize/

Here's another good source: https://docs.microsoft.com/en-us/typogr ... pec/ttch01

You probaby don't want to rasterize each character every time you want to draw it on the screen because that could be super slow. Instead, you'd want to rasterize the characters you're going to use once, then copy them into the framebuffer similar to how I described above with my in-lined fonts.

Unicode 13.0 has 143,859 characters. Most fonts come nowhere near providing a glyph for each of the Unicode characters. But, you might not want to rasterize every character in the font at loading time, only those you want to show. Also, if you want to render the same character at 16 pixels tall, then later at 19 pixels tall, you'll need to rasterize them again so they stay super sharp at both resolutions. One of the interesting problems to think about is when to rasterize characters, which characters to rasterize, and when to release the memory of rasterized characters you no longer need.

_________________
My OS is Perception.


Top
 Profile  
 
 Post subject: Re: Using the frame-buffer information?
PostPosted: Fri Mar 19, 2021 9:48 am 
Offline
Member
Member

Joined: Tue Aug 11, 2020 12:14 pm
Posts: 151
AndrewAPrice wrote:
You'll also hear double buffering. If the image you want to draw is composed of multiple operations (e.g. clear a color, draw some lines over it, draw a font over it), then if you draw straight into the framebuffer, the user's display could show the pixel at any time, so the image can flicker because the user will briefly see pixels that are supposed to be overritten by other pixels (e.g. it'll flicker between the background color and foreground text). So double buffering means you allocate an array (also known as a backbuffer, or texture), and you do your drawing operations into this array, and only once you have the final state of the pixels you copy it across to the frame buffer.

I'm not sure if it's a particularly valid technique any more, but eons ago when I did some graphics work, I would do a modified version of the v-sync technique described in the wiki page, except that I would also disable screen redraws (VGA control register index 01h) while updating video memory. This is in case you don't finish writing before the next v-sync.

It's probably not going to work well with today's high speed graphics, but it may be sufficient for console output if you're more concerned with clean, no-flicker updates than performance.


Top
 Profile  
 
 Post subject: Re: Using the frame-buffer information?
PostPosted: Fri Mar 19, 2021 11:06 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5134
sj95126 wrote:
I'm not sure if it's a particularly valid technique any more, but eons ago when I did some graphics work, I would do a modified version of the v-sync technique described in the wiki page, except that I would also disable screen redraws (VGA control register index 01h) while updating video memory. This is in case you don't finish writing before the next v-sync.

Wouldn't disabling redraw cause the screen to be totally blank if you ran out of time? Or are you talking about double-buffering and disabling the buffer swap?

sj95126 wrote:
It's probably not going to work well with today's high speed graphics, but it may be sufficient for console output if you're more concerned with clean, no-flicker updates than performance.

Synchronizing with the display refresh rate works fine with modern graphics, but VGA registers don't. You need at least a little bit of a driver for the display adapter if you want to do that nowadays.


Top
 Profile  
 
 Post subject: Re: Using the frame-buffer information?
PostPosted: Sat Mar 20, 2021 11:36 am 
Offline
Member
Member

Joined: Sat Feb 20, 2021 3:11 pm
Posts: 93
AndrewAPrice wrote:
There are three operations that are super userful:
- Rectangle filling
- Line drawing
- Bit Blit

Rectangle filling is super easy. You just loop over every pixel between min_x, max_x, and min_y, and max_y and overwrite the pixel with the color you care about.

Line drawing is more interesting. Drawing straight horizitonal and vertical lines are easy.

For diagonal lines, you'd want to pick a line drawing algorithm

Blending colors together is easy. Colors are usually broken into 3 channels (Red, Green, Blue) and sometimes a 4th (Alpha). Assuming colors are in the range of 0.0 -> 1.0, the formula for alpha blending is:
output[channel] = background[channel] * (1.0 - alpha) + foreground[channel] * alpha

(That's the theory, but instead of 0.0 -> 1.0, you're probably using bytes from 0 to 255. The theory doesn't change, you just need to adjust the math.)

Bit blitting is basically copying an image to the screen, but you skip the transparent pixels. An easy example is to think of the mouse cursor. You define it as a grid of pixels, but a mouse isn't square so the pixels around the outside of the mouse cursor shape are transparent, and your bit blit algorithm skips those pixels.

You'll also hear double buffering. If the image you want to draw is composed of multiple operations (e.g. clear a color, draw some lines over it, draw a font over it), then if you draw straight into the framebuffer, the user's display could show the pixel at any time, so the image can flicker because the user will briefly see pixels that are supposed to be overritten by other pixels (e.g. it'll flicker between the background color and foreground text). So double buffering means you allocate an array (also known as a backbuffer, or texture), and you do your drawing operations into this array, and only once you have the final state of the pixels you copy it across to the frame buffer.

---

Now let's talk about fonts:
Quick and dirty answer:

Until I get to the stage where I can load fonts dynamically off disk, I found it super easy to get started with inlining the rasterized font into a header: e.g https://github.com/AndrewAPrice/Perception/blob/master/third_party/DejaVuSans.inl

But.. I can't find the stb_font_inl_generator.c that I used that generated that header. I did find a large collection of fonts here, generated using the same method:
https://github.com/stetre/moonfonts/tree/master/src/fonts

What the generator did was rasterize (convert vector graphics into pixel graphics) the font at a certain size, then embed those pixels into a C header.

Using the font is super simple, here's my loading drawing code: https://github.com/AndrewAPrice/Perception/blob/master/Libraries/perception/source/perception/font.cc It loads two things - a grid of alpha values (a giant texture with all of the characters on it), and details about each character.

The details of each character include where in the grid of alpha values the texture lives so you can copy it to the screen, and how wide each character is. We care about how wide our characters are because that lets us measure strings by looping over each character and summing their widths. We care about measuring strings because then we can center align text, or make sure text isn't too big for the container we're rendering it in.

The texture is a a grid of pixels by their alpha value, and you use that to determine how to blend the font color with the background.

---

Long answer:

Most fonts are vector fonts - fonts that are made up of lines instead of pixels:
Image
These lines are often Bézier curves and they are stored in a file format such as TrueType. Because they are vector format (described as lines instead of pixels), the fonts can be rasterized (coverted to pixels) at any resolution. A common rasterizing technique is called scanline rendering where you calculate a bounding box, and for each line, you walk the pixels left-to-right, and everytime you pass a line you flip between filled and empty:
Image

The author of stb_truetype (which looks super easy to embed into an OS) wrote about his technique here: http://nothings.org/gamedev/rasterize/

Here's another good source: https://docs.microsoft.com/en-us/typogr ... pec/ttch01

You probaby don't want to rasterize each character every time you want to draw it on the screen because that could be super slow. Instead, you'd want to rasterize the characters you're going to use once, then copy them into the framebuffer similar to how I described above with my in-lined fonts.

Unicode 13.0 has 143,859 characters. Most fonts come nowhere near providing a glyph for each of the Unicode characters. But, you might not want to rasterize every character in the font at loading time, only those you want to show. Also, if you want to render the same character at 16 pixels tall, then later at 19 pixels tall, you'll need to rasterize them again so they stay super sharp at both resolutions. One of the interesting problems to think about is when to rasterize characters, which characters to rasterize, and when to release the memory of rasterized characters you no longer need.



Thanks for your answer, I think maybe much much later I will implement vector fonts but that is certainly not until I come to user-space, but for now the bitmap fonts are fine to. Except that I have a problem - I tried to output fonts that I by hand copied from PSF to a C array, but only some strange things get output to the screen(while the glyph is Pi number), and I really can't understand the problem

The PSF fonts are 8 x 16


Last edited by rpio on Sun Jan 23, 2022 4:48 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Using the frame-buffer information?
PostPosted: Sat Mar 20, 2021 11:52 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5134
ngx wrote:

You probably meant something like "if (glyph[i] & 1 << bit_shift)". That might draw it backwards, though - doesn't PSF pack the leftmost pixel in the MSB?


Top
 Profile  
 
 Post subject: Re: Using the frame-buffer information?
PostPosted: Sat Mar 20, 2021 12:05 pm 
Offline
Member
Member

Joined: Sat Feb 20, 2021 3:11 pm
Posts: 93
Octocontrabass wrote:
ngx wrote:

You probably meant something like "if (glyph[i] & 1 << bit_shift)". That might draw it backwards, though - doesn't PSF pack the leftmost pixel in the MSB?


I figured it out, the problem was that I was increasing the bit shift var by one every time, completely forgetting that the number 0x7 does not represent bit seven or number 0x5 does not represent 5. I just changed it to mask having 1 on bit zero, then move 1 to the bit 1, 2...


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot] and 61 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group