                      =================================
                      = random 32bit putpixel example =
                      =================================


Overview
--------

This example draws random pixels to a 32bit ARGB8888 system memory surface
and updates this 32bit surface to the display - converting to the output
format if necessary. It demonstrates working in "virtual 32bit" with fuzzy
32bit mode setting.

NOTE: From now on when i say "32bit" i mean "32bit ARGB8888" the major 32bit
      pixel format supported in PTC



Step 1 - PTC Initialization
---------------------------

>    // try to init ptc from command line (i.e. "random 640 480 ARGB8888")
>    PTC ptc(argc,argv);
>    if (!ptc.ok())
>    {
>        // command line init failed - fall back to 320x200 fuzzy 32bit
>        if (!ptc.Init(320,200))
>        { 
>            ptc.Close();
>            cout << "could not initialize ptc\n";
>            return 1;
>        }
>    }

The code above first tried to initialize a PTC graphics mode from the command
line, then if that fails, falls back onto a "fuzzy" 32bit mode set. Note that
when both mode sets fail (command line and fuzzy32) the ptc object is first
closed to make sure that we restore textmode before outputting an error
message "could not initialize ptc". Note that you do NOT need to manually
close the PTC object before exiting the program, the destructor will always
take care of this for you.


When initializing rand32 from the command line. The format is as below:

    rand32 [xres] [yres] [format]

    e.g. rand32 320 200 RGB565

NOTE: if you go "PTC ptc(320,200,argc,argv)" it will let you get the output
      format alone from the command line, and let you control the xres and
      yres of the mode yourself.

If the command line initialize fails, the PTC::ok() returns 0 and the rand32
program falls back onto "fuzzy 32bit". Note that the second time the PTC
object is initialized (using ptc.Init) it just uses "ptc.Init(320,200)"
FUZZY32 is the default third parameter.


What are fuzzy mode sets? Fuzzy mode sets are what makes ptc cool! When a
fuzzy 32bit mode set is requested PTC tries to set a mode "close to 32bit".
The exact sequence of events occurring in a FUZZY32 mode set are as follows:

try to set xres,yres at:

   1. ARGB8888   (32bit)
   2. RGBA8888   (32bit)
   3. BGRA8888   (32bit)
   4. ABGR8888   (32bit)
   5. RGB888     (24bit)
   6. BGR888     (24bit)
   7. RGB565     (16bit)
   8. BGR565     (16bit)
   9. ARGB1555   (16bit "15bit")
  10. ABGR1555   (16bit "15bit")
  11. FAKEMODE2A (emulated truecolor)


FUZZY32 mode sets are cool because they allow you to work "virtually" in 32bit
while converting when you update to the display. Try the rand32 function with
all the output formats available for a bit of fun (see "globals.h" for a list
of all predefined formats you can try).



Step Two - Surface initialization
---------------------------------

>    // get display resolution
>    int xres=ptc.GetXResolution();
>    int yres=ptc.GetYResolution();
>
>    // create fullscreen 32bit ARGB8888 surface
>    Surface surface(ptc,xres,yres,ARGB8888);

This code here queries the display output x and y resolution, then creates
a system memory surface of format ARGB8888 that is bound to the PTC object
we just initialized (see "surface.h").

A surface is just a chunk of pixel data. Much like a "ramscreen". Surfaces
however, are smarter than just an array of bytes and provide a lot of cool
things for you the programmer. Most importantly, each surface knows what
format that its pixel data is stored as - if one surface is being BitBlt'd
to another surface and they are NOT identical pixel formats. PTC will attempt
a pixel format conversion transparently. You can retrieve the format of a
surface with Surface::GetFormat. See the "format.h" for the declaration of the
FORMAT class - the class that is used by PTC to describe pixel formats.


You may have noticed how the PTC object was passed as the first parameter of
the surface constructor. When a ptc object is passed to a surface constructor,
the surface is "bound" to that interface (the ptc object's "interface" to the
graphics hardware). Bound surfaces are volatile - if you nuke the interface
(the ptc object), then all the bound surfaces are nuked as well.

This of course sucks - so there is an alternative. If you DONT pass a ptc
object to the constructor, then the surface is "unbound". The surface is
not dependant on an interface object (ptc object), and will exist until YOU
delete it. Unbound surfaces do have their limitations, as they are unable to
latch onto native features of the interface, such as hardware acceleration.
For example, when using PTC with DirectX, bound surfaces bind to the IDirectX
interface (managed inside the PTC object) and are implemented internally
as native DirectDraw surfaces and doing so they gain hardware acceleration).
Unbound surfaces under DirectX however, don't know about hardware acceleration
because they are just implemented as dumb blocks of memory.



Step 3 - The main loop
----------------------

>    // main loop
>    while (!ptc.kbhit())
>    {
>        // lock surface
>        uint* buffer=(uint*)surface.Lock();
>        if (!buffer)
>        {
>            ptc.Close();
>            cout << "null surface lock!\n";
>            return 1;
>        }
>        
>        // plot 100 random pixels
>        for (int i=0; i<100; i++)
>        {
>            int x=random(xres);
>            int y=random(yres);
>            buffer[xres*y+x]=RGB32(random(255),random(255),random(255));
>        }
>
>        // unlock surface
>        surface.Unlock();
>
>        // update to display
>        surface.Update();
>    }
>    return 0;

The first thing you may notice, is that the main loop uses "ptc.kbhit" not
kbhit to loop until a key is pressed. This is a portability issue, and is
designed like this so that win32 programs can still "think" they are console
applications and not need to worry about windows keyboard messages etc. This
greatly speeds porting between dos <-> windows as you do NOT need to manage
your own window (note. win32 support is included from PTC052 onwards, see
the "windows" example for more information about using PTC with windows).


Secondly, a strange function "surface.Lock" is being called. What this
function does is return a pointer to the surfaces pixel data. It is this
pointer you use to draw directly to the surface.

Surface::Lock may fail. If it does so, then it will return NULL. Please,
*PLEASE* check that surface lock returns a non-null pointer before you use it!

When does a lock fail? When the surface is invalid. When is a surface invalid?
Typically this occurs when a) a bound surface was nuked because its interface
was nuked b) the surface was a video surface and it was nuked because you
switched modes c) DirectDraw nuked your surface because it felt like it. A
surface can be checked for validity with the Surface::ok function. I
guarantee to you that no surface will ever fail to lock while it is valid,
however, this doesn't mean that you should remove your check for NULL because
DirectDraw has a nasty habit of nuking surfaces whenever it feels like,
especially VIDEO surfaces, and makes no guarantees about SYSTEM surfaces
either :P ... If you want portable and stable code please check for NULL!


After the surface is locked, the variable "buffer" points to the pixel data.
We use this pointer to then write to it. The function RGB32 packs three color
chars r,g,b into an ARGB8888 color integer (which is the format of the surface
we are working with).

Finally, the surface is unlocked and the program loops. Please Lock/Unlock
for each frame. Don't just lock the surface once and use that pointer for the
entire program. If you do this under DirectX with PTC, then you will run into
BIG problems. Finally, remember that all successful (non-NULL) locks need to
be matched with an unlock. If you mess up your Lock/Unlock pairs expect pain.

NOTE: You can lock the same surface multiple times. Each successive lock
increases the lock count, each unlock decrements the count. This is a more
relaxed policy than what DirectDraw does with its surface architecture. I like
to think of a lock as being more like a reference count. While a surface is
locked the memory is *guaranteed* to remain at the address the user has been
given. When the lock count reaches zero, PTC just may decide to shuffle the
surfaces around a bit, so don't rely on your pointer after you have unlocked
it - consider it invalid.

However, even though PTC permits multiple surface locks for convenience. I
recommend against it. It is nice and certainly makes things a bit nicer - but
when it comes to hardware acceleration, the hardware doesn't really like to
share the surface with you while it is working. So basically, if you are
going call any functions that offer hardware acceleration (such as
Surface::Update, Surface::BitBlt and Surface::Clear) i would advise you to
unlock the surface completely before you do so to ensure that the hardware
can have exclusive access to the surface memory. The issue of Lock contention
between the software and the hardware will be dealt with further in an example
program when DirectX support returns in PTC060.



Next: See rand16.cpp for an example of "virtual 16bit" with FUZZY16 mode sets.