Drawing with VESA

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.
Post Reply
srg_13

Drawing with VESA

Post 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
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Drawing with VESA

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
srg_13

Re:Drawing with VESA

Post 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
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Drawing with VESA

Post 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]
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Drawing with VESA

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
srg_13

Re:Drawing with VESA

Post 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 :P

Also, when i try to draw a diagonal line, it looks like this:
+----------------+
|\ |
| \ |
| |
| |
| |
+----------------+

[glow=green,2,300]Stephen[/glow] :)
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Drawing with VESA

Post 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.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Drawing with VESA

Post 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 :P
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Drawing with VESA

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Drawing with VESA

Post 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).
mystran

Re:Drawing with VESA

Post 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.
Post Reply