Using the frame-buffer information?
Using the frame-buffer information?
...
Last edited by rpio on Tue Aug 13, 2024 12:15 pm, edited 2 times in total.
- AndrewAPrice
- Member
- Posts: 2300
- Joined: Mon Jun 05, 2006 11:00 pm
- Location: USA (and Australia)
Re: Using the frame-buffer information?
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/Percept ... VuSans.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/tre ... /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/Percept ... on/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:
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:
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.
- 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/Percept ... VuSans.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/tre ... /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/Percept ... on/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:
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:
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.
Re: Using the frame-buffer information?
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.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.
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.
-
- Member
- Posts: 5568
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Using the frame-buffer information?
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: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.
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.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.
Re: Using the frame-buffer information?
...
Last edited by rpio on Tue Aug 13, 2024 12:14 pm, edited 2 times in total.
-
- Member
- Posts: 5568
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Using the frame-buffer information?
You probably meant something like "if (glyph & 1 << bit_shift)". That might draw it backwards, though - doesn't PSF pack the leftmost pixel in the MSB?ngx wrote:Code - https://pastebin.com/Ka7VyBuG