by Rawhed(Andrew Griffiths)/Sensory Overload - 26 April 1999
sfeist@netactive.co.za
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Contents
-=\Foreword
-=\Why 16bpp color?
-=\Background
-=\Setting The Mode
-=\Double Buffers
-=\Putpixel
-=\Getpixel
-=\Copying To Video Memory
-=\How to deal with RGB(Transparency)
-=\Additive
-=\Subtractive
-=\Other Ideas
-=\Closing words
-=\Greets
-=\Contact Me
Foreword
Ok, I had a request to make this, so I will. I also think that a lot of
the .za dos coders out there are still using mode13h, and I'd like to
show them how cool 16bpp color mode is. This is not to show how to
optimise 16bpp coding, it just shows the simplest ways of doing things.
Optimising is something you can workout later.
Why 16bpp color? (BTW bpp = bits per pixel).
Well its simply very cool. Firstly you can have 65536 different colors on
the screen at once, compared to 256 colors. So your pictures will look a lot
cooler. Secondly its easy, so why not? ;) Thirdly you can do VERY cool
effects very easily. Effects like transparency(multiply layers), fog,
RGB lighting, shadows etc. - all all much easier in this mode. Fourthly
all your effects(smoothing, dithering, scaling etc) are much easier and you
don't have to worry if a color is in the palette, you can just create the
color. I've been a 8bpp coders for a long time, but since I've tried 16bpp
I've never gone back...(well, once :)
Background
16bit color mode is exactally that. Each pixel is 16bits(2 bytes), as
opposed to 1bytes/pixel in 256color modes. So a 320x200 mode will
require 128000bytes of space in 16bit color mode, compared to the normal
64000, because we have to write twice as many bytes - yet there are still
64000 pixels. I'm going to be dealing with 320x200x16bit, as I just
want to introduce this concept, then you can easily extent it to other
resolutions. I suggest you read a good doc on the VESA spec, as it
has a lot of very cool info, like hardware scrolling, all the videomodes,
how to get videocard info etc. I'll just give you the skeleton to play
with to get you started in 16bit vmodes.
Another thing is that there is no pallete! Nada. We are now working with
RED, GREEN & BLUE. The 16bit number which is each pixel is structured like
this(in binary):
FEDCBA9876543210 - bit position rrrrrggggggbbbbb - pixel
The 1st 5 bits are for how much BLUE the pixel has, the next 6 bits are
for how much GREEN the pixel has, and the next 5 bits are for how much
RED the pixel has. So remember that 5bits can be from 0-31, and 6bits from
0-63. So green has more of a range than red and blue. Which is cool, because
of all the colors, green is the one the human eye is most sensitive to.
So for example:
FEDCBA9876543210 - bit position 0000000000000000 - pixel = black FEDCBA9876543210 - bit position 1111111111111111 - pixel = white FEDCBA9876543210 - bit position 0000000000011111 - pixel = red FEDCBA9876543210 - bit position 0000011111100000 - pixel = green FEDCBA9876543210 - bit position 1111100000000000 - pixel = blue FEDCBA9876543210 - bit position 1111100000011111 - pixel = purple
Get it?
Getting The Mode
To set the mode you need to do a BIOS interrupt. Yes its slow, but we're
only doing it once, so its fine. You can read up on how to do more advanced
things like setting up a linear frame buffer by yourself.
mov ax,4f02 ;the "setmode" function(hex) mov bx,10e ;the video mode to set(hex) int 10h
4f02h is the setmode function. Consult a cool VESA doc for all the other
functions. 10e is the video mode number. Just like 13h is the mode number
for 320x200x8bit, 10e is for 320x200x16bit. Ok so now you are in
320x200x16bit. Now lets have some fun.
Double Buffers
Its basically essential to use a double buffer. Firstly for speed(writing
to video memory is slooow), and also so that the viewer only sees the
completed picture. Another very good reason is that if you aren't using
LFB(linear frame buffer), you will have to deal with page(64k) boundaries.
Its better to use a 128k buffer, and then every frame flip the 128k buffer
to video memory, doing 64k at a time for 2 banks(changing the bank inbetween).
Although I would suggest that you figure out LFB, it makes things much
easier :)
Putpixel
Just like a normal mode13h putpixel, except that you must remember 2 things
when finding the correct pointer address from the X/Y coordinates.
1)Each pixel is 16bits, not 8bits, so you need to add X*2.(or add X twice)
2)Although the width of the screen is 320 pixels, you need to multiply by
640 because of the number of bytes.
mov ebx,[X] shl ebx,1 ;X*2 mov edi,[Y] mov edx,edi shl edi,9 shl edx,7 ;Y*640 add edi,edx add edi,ebx ;edi=(x*2)+(y*640) add edi,[address] ;edi=edi+pointer to vbuffer mov ax,[color] ;ax=16bit color value mov [edi],ax ;write it!
Getpixel
Getpixel is VERY similar:
mov ebx,[X] shl ebx,1 ;X*2 mov edi,[Y] mov edx,edi shl edi,9 shl edx,7 ;Y*640 add edi,edx add edi,ebx ;edi=(x*2)+(y*640) add edi,[address] ;edi=edi+pointer to vbuffer mov ax,[edi],ax ;get it! mov [color],ax ;ax=16bit color value
Copying To Video Memory
You see....if you aren't using a LFB it gets complicated(well not too complex).
But since I won't get into LFB's, i'll show you a simple way quickly.
Since video memory is by default(on most vcards) divided into chunks of
64k, you can only write 64k. So you can only write half the screen in
320x200x16bit mode. What we have to do is this:
1)Switch to bank0
2)Copy 1st 64k
3)Switch to bank1
4)Copy 2nd 64k
Thats it. Except that 128k doesn't equal 320x200x2. So 1st we write
64k(65536 bytes) and then in bank 1 we write 128000-65536=62464, which you
can see:
;1st 64k mov ax,4f05h ;VESA bank set function mov bx,0 mov dx,0 ;bank number(0) int 10h mov edi,0a0000h ;we want to write to video memory mov esi,[where] ;from this address mov ecx,16384 ;this ammount(65536/4) rep movsd ;write loop, 4bytes/write ;2nd 64k mov ax, 4f05h ;VESA bank set function mov bx,0 mov dx,1 ;bank number(1) int 10h mov edi,0a0000h ;we want to write to video memory mov esi,[where] ;from this address add esi,65536 ;plus this ammount mov ecx,15616 ;this ammount(128000-65536)/4 rep movsd ;write loop, 4bytes/write
That will copy your 128buffer to the screen so you can see it :)
How To Deal With RGB(Transparency)
One of the most important things to be able to do(initially), is to be able
to extract the RGB values from the 16bit color value. But one must remember
that not ALL videocards handle 16bit modes the same. Some structure the
pixels:
RGB16 rrrrrggggggbbbbb (most common) BGR16 bbbbbggggggrrrrr (backwards) RGB15 rrrrr0gggggbbbbb (everything is 5bits)
After reading your VESA spec, you will see how to get info from VESA calls
about which type your card is using. I'll just work with RGB16 for now.
You have 5bits:RED 6bits:GREEN and 5bits:BLUE, you must extract the RGB
values using masks and shifting.
Some of us had to figure all of this out by ourselves, but here it is:
mov ax,[edi] ;pixel value mov bx,ax ;backup pixel value ;Now extract BLUE and bx,0000000000011111b ;mask unwanted bits mov [blue],bx ;nab it ;Now extract GREEN mov bx,ax ;backup pixel value and bx,0000011111100000b ;mask unwanted bits shr bx,5 ;shift right mov [green],bx ;nab it ;Now extract RED mov bx,ax ;backup pixel value and bx,1111100000000000b ;mask unwanted bits shr bx,11 ;shift right mov [red],bx ;nab it
Now we have red, green & blue in separate variables.
Now imagine if for transparency we got the RGB values of 2 pixels, and did
this:
r=(red1+red2)/2; g=(green1+green2)/2; b=(blue1+blue1)/2;
And then wrote the pixel(after recompiling the RGB into the color value):
mov ax,[r] ;get red shl ax,11 ;shift it mov bx,[g] ;get green shl bx,5 ;shift it add ax,bx ;add it to pixel mov bx,[b] ;get blue add ax,bx ;add it to pixel mov [edi],ax ;write the pixel
So I recompiled the separate RGB values into one 16bit value again, and then
wrote it. So you see how you could to transparency now?
Additive
Very similiarly, what if we did:
r=(red1+red2); g=(green1+green2); b=(blue1+blue2); if (r>31) r=31; if (g>63) g=63; if (b>31) b=31; writepixel(r,g,b);
Then we would be doing very cool additive drawing, without overflowing.
So you could layer sprites on top of each other, and then would combine
their color values, until they add up to white. This can be used very
effectively in explosion/particle effects :)
Subtractive
Very similiarly, what if we did:
r=(red1-red2); g=(green1-green2); b=(blue1-blue2); if (r<0) r=0; if (g<0) g=0; if (b<0) b=0; writepixel(r,g,b);
Other Ideas
I can't go through all the possible uses ;) But I will just put a few thoughts
into your mind. How easy would crossfading be using 16bpp? How about
a bluring effect? How about a function that makes the image negative colors?
How about a function which changes the percentage of RGB in an images.
How about cool transparent texturemapped 3d objects? How about lighting
images using additive mapping of a lightmap? How about shadows? If you
are scaling/mapping an image to a differnt size/shape - won't you be
able to interpolate the colors, and add new colors to add extra detail
and eliminate blockyness when zooming in close? Won't anti-aliasing be easy?
Closing Words
Hehe, I hope you understood this tutorial, and that it will help you with
your coding. I might write other tutorials if people request them. I have
much to learn about coding, but I am always happy to share what I do know
with others. Happy coding!
Greets
The SA demoscene!
The demoscene.
Anybody who has every released source.
Anybody who has every released a tutorial.
Saurax, Rawnerve, and all the old SO members(contact me!)
Viper, Maverick, Caz, Neuron, Riot, Deadpoet, Jahya.
Celestial & workers-with
People who are hosting the sademoscene site! thanks!
All my friends far away!
Contact Me
Things might change, so these are my email addies in order of stability. If
you try one and nothing happens, try another :)
andrew@overload.co.za sfeist@netactive.co.za rawhed@hotmail.comVisit the SA demoscene site at : http://www.surf.to/demos