Page 1 of 1
Drawing with VESA
Posted: Wed Feb 23, 2005 12:51 am
by srg_13
Ok, so i have setup my OS to start up in 800x600 in 8 bit colour using VESA, but when i try to draw anything, it displays it in a small rectangle at the top of the screen. Here is the code im using (it worked with 320x200 VGA):
Code: Select all
unsigned char *VGA = (unsigned char *)0xA0000;
void setpixel(int x, int y, int color)
{
VGA[320*y+x]=color; //(I changed the 320 to 800 but it still wouldn't work.
}
void main()
{
setpixel(20, 30, 15) // just plot a pixel
}
How would I modify this to work in 800x600?
also, is there a way to draw lines and rectangles without doing this:
Code: Select all
void h_line(int x, int y, int length, int color)
{
while(xa!=0)
{
VGA[320*y+x]=color; //This plots a pixel at x,y
x++;
length--;
}
}
Thankyou in advance,
Stephen
P.S. I am in protected mode
Re:Drawing with VESA
Posted: Wed Feb 23, 2005 1:21 am
by Brendan
Hi,
You'd need to change every reference to "[320*y+x]" or anything similar, and all code that uses X and Y. For example, if you've got code to draw a horizontal line at the bottom of the screen ("h_line(0, 199, 320, 123)") it'd need to be changed to "h_line(0, 599, 800, 123)".
Also some video cards have unused space to the right of the displayed space - for e.g. you could have 1024 bytes between each horizontal screen line, where only 800 bytes is displayed. In this case you should really do something like:
Code: Select all
unsigned int horizontal_offset;
void init(void) {
horizontal_offset = get_offset_from_vbe_or_something();
setpixel(20, 30, 15) // just plot a pixel
}
void setpixel(int x, int y, int color) {
VGA[horizontal_offset*y+x]=color;
}
You can optimize the code above by using an array of pointers to replace the multiplication. For horizontal lines I'd use the C function "memset()", and then write the memset function so it uses "rep stosd" where possible (which can be tricky because the start and end isn't always aligned). For rectangles just use "h_line()" or memset in a loop. For e.g.:
Code: Select all
unsigned int horizontal_offset;
unsigned char *y_addresses[600];
void init(void) {
horizontal_offset = get_offset_from_vbe_or_something();
init_y_addresses();
setpixel(20, 30, 15) // just plot a pixel
}
void setpixel(int x, int y, int color) {
*(y_addresses[y] + x) = colour;
}
void h_line(int x, int y, int length, unsigned char color) {
memset(y_addresses[y] + x, color, length);
}
void rectange(int x, int y, int length, int height, unsigned char color) {
for(; height > 0; height--) {
memset(y_addresses[y] + x, color, length);
y++;
}
}
Cheers,
Brendan
Re:Drawing with VESA
Posted: Wed Feb 23, 2005 1:47 am
by srg_13
I tried that, but it still prints at the top of the screen, in a box that is 600 pixels long, but is only about 100 tall, and the whole screen is streched into the box. Is it something to do with the pointer to the video memory? (0xA000)
Thanks,
Stephen
Re:Drawing with VESA
Posted: Wed Feb 23, 2005 2:04 am
by Pype.Clicker
A000 can only offer a 64KB-window on your video memory. Either you need to use the Linear FrameBuffer (located somewhere in high memory, ask VBE to find out), or you have to change the offset of that window within the video memory with the proper VBE calls ...
If that's the cause, trying to draw a diagonal across your screen will result in diagonal hashes in a top-banner of your screen.
[tt]
0 800
+--------------------------+ 0
|\ |
| \ | 65536 / 600 (roughly 100 lines)
| \ | expected
| \ |
| \ |
+-----------*---------------+ 600
+------------------------+ 0
|\ \ \ \ \ \ |
| | 100
| | what you get instead ...
| |
| |
+------------------------+ 600
[/tt]
Re:Drawing with VESA
Posted: Wed Feb 23, 2005 2:28 am
by Brendan
Hi,
Stephen wrote:I tried that, but it still prints at the top of the screen, in a box that is 600 pixels long, but is only about 100 tall, and the whole screen is streched into the box. Is it something to do with the pointer to the video memory? (0xA000)
It's a bit hard to guess (I can't think of any normal bug that'd cause it to be displayed with the correct colours but only 600 pixels wide) - could you run it in Bochs and provide a link to a picture/snapshot of it and/or a link to your code?
Cheers,
Brendan
Re:Drawing with VESA
Posted: Fri Feb 25, 2005 4:18 am
by srg_13
I have some questions:
1. What is VBE?
2. What is the linear FrameBuffer?
3. How many colours are there in 8bpp?
4. How do you find these colours if you have the RGB values?
And when i said 600 pixels wide, I ment 800 pixels
Also, when i try to draw a diagonal line, it looks like this:
+----------------+
|\ |
| \ |
| |
| |
| |
+----------------+
[glow=green,2,300]Stephen[/glow]
Re:Drawing with VESA
Posted: Fri Feb 25, 2005 4:52 am
by Candy
Stephen wrote:
I have some questions:
1. What is VBE?
VBE == VESA Bios Extensions.
It's the stuff in your bios that allows you to use a very high screen resolution in real mode.
2. What is the linear FrameBuffer?
It's telling the VBE code to not map in a small bit at 0xA0000 but instead to map it ALL at some place up high. It allows you to use the entire screen resolution in protected mode after setting it in real mode, without switching back.
3. How many colours are there in 8bpp?
8 bits per pixel, so that gives you 2^8 colors per pixel. Note, they don't all have to be different, you can just assign up to 2^8 colors.
4. How do you find these colours if you have the RGB values?
You can do either way. In 8bpp mode, request the values and transform your RGB to the nearest, or set one of the unused values to your value. In 16+ bits mode you always have a static palette and have to transform your data to that mode stuff, which is usually done by some magic code...
Code: Select all
#ifdef RGB_16_BITS_565
int shift[3] = {0, 5, 11};
int bits[3] = {5, 6, 5};
#else ifdef RGB_16_BITS_555
int shift[3] = {0, 5, 10};
int bits[3] = {5, 5, 5};
#else ifdef RGB_24_BITS || RGB_32_BITS /* identical */
int shift[3] = {0, 8, 16};
int bits[3] = {8, 8, 8};
#else ifdef CANDY_FREAKED_EXAMPLE
int shift[3] = {11, 0, 5};
int bits[3] = {5, 5, 6};
#endif
unsigned int getcolor(int r, int g, int b) {
/* assuming r/g/b each in 8-bits */
int fshift[3] = {8-bits[0], 8-bits[1], 8-bits[2]};
int mask[3] = {(1 << bits[0]) -1, (1 << bits[1]) -1, (1 << bits[1]) -1};
int rbits = ((r >> fshift[0]) & mask[0]) << shift[0];
int gbits = ((g >> fshift[1]) & mask[1]) << shift[1];
int bbits = ((b >> fshift[2]) & mask[2]) << shift[2];
return rbits | gbits | bbits;
}
This code generates a given mix of bits given the RGB values. It works for all but paletted modes.
PD hereby, hope it's of use for you. Non-tested as usual.
Re:Drawing with VESA
Posted: Fri Feb 25, 2005 9:11 am
by Pype.Clicker
Basically, you have a
palette storing 6 bits R, G and B values for each color index.
so
[tt]
01100110
01100110
00000000
30000003
03333330
[/tt]
is a smiley with blue eyes and red smile in the default palette, but it will turn to a yellow smile if you reprogram the palette color #3 with R=63, G=63, B=0 (see FreeVGA for more infos)
There are 2 tricks for displaying truecolor pictures: dithering (used in old display programs) and virtual pixels (used in 1337 demos in the end of nineties)
Virtual pixels consist of building a palette with a shade of red, a shade of blue and a shade of green, then splitting your screen logically to build larger pixels than what pixels actually are.
[tt]
RxBx/G\xRxBx/
xGx/RxB\xGx/
RxBx/G\xRxBx/
xGx/RxB\xGx/
[/tt]
where 'R' will be the 'red' component of a virtual pixel, G the green component and where 'x' are unused pixels. No need to say that this only renders fine on high-res displays and that it's almost useless if you're not at least at 4 meters of the screen
Re:Drawing with VESA
Posted: Fri Feb 25, 2005 11:52 pm
by Brendan
Hi,
I normally just setup the palette so that there's 3 bits for red, 3 bits for green and 2 bits for blue. This way I can convert higher colour depths into 8 bpp with some bit shifting.
This means I can pretend the palette doesn't exist and that every graphics video mode works the same. The disadvantage is that the colours are approximates - it doesn't give the best image quality possible. IMHO if you want the best image quality you should use a higher colour depth to start with
.
Cheers,
Brendan
Re:Drawing with VESA
Posted: Sat Feb 26, 2005 2:09 am
by Candy
Brendan wrote:
I normally just setup the palette so that there's 3 bits for red, 3 bits for green and 2 bits for blue. This way I can convert higher colour depths into 8 bpp with some bit shifting.
This means I can pretend the palette doesn't exist and that every graphics video mode works the same. The disadvantage is that the colours are approximates - it doesn't give the best image quality possible. IMHO if you want the best image quality you should use a higher colour depth to start with
.
In 8bpp it's common to use the "Internet Palette" which has 216 colors predefined. It has 6 scales for each color, not allowing easy determination (the above code won't work) but giving slightly better performance in graphics quality (at least, or so I was told).
Re:Drawing with VESA
Posted: Sat Feb 26, 2005 9:22 am
by mystran
3R/3G/2B actually isn't a bad choice, although adding some basic dithering can do wonders.
The most simple possible way is to have a small, tileable block of random uniform noise (or maybe some predefined pattern, but IMHO uniform noise tends to look better, especially if pattern-tile is something "big" like 128x128) with values between 0 and 1.
Now, if you have a channel (one color component) value of 8-bit range, and you want to convert it to 3-bit range, instead of bitshifting directly, take the relevant pixel from your dither block, scale the dither value so that it's 8-3=5 bits integer value, and add it to the original 8 bit value. Then scale down to 3 bits.
How do you select the value from the dither pattern? Take the (screen, if you dither whole screen) coordinates of a pixel, and apply component-wise modulo to dither-pattern size. For example, screen coordinate (x,y) with dither-pattern of 128x128 pixels would give pattern-coordinates (x mod 128, y mod 128). Obviously, for 2^n pattern sizes it's a simple bitmasking with AND.
What happens is that you distribute your "in middle" pixels between the "round-up" and "round-down" possibilities. Because the dither pattern is uniform noise, the closer we are to the "round-up" value, the less we get the "round-down" pixels, and vice versa. Relatively easy to do, and result IMHO looks MUCH better than simply rounding directly.
Same principle can be applied to downscaling from 16-bits/component to 8-bits/component, or 8-bits/component to 5-bits/component or whatever.
There are all kinds of more fancy dithering methods too, but this one is easy to do. One possible improvement (?) might be using separate dither-patterns for different components.