Rawheds Tutorial#1:
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
                     How to code 16bit color graphics

       by Rawhed(Andrew Griffiths)/Sensory Overload - 26 April 1999

                           andrew@overload.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?


Setting 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.com

Visit the SA demoscene site at : http://www.surf.to/demos
Enter Optimise99!!!

-Rawhed/Sensory Overload
-Mailto:andrew@overload.co.za
-Htpp://www.overload.co.za
-Andrew Griffiths
-South Africa
-05-07-1999