================================= = 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 NOT included with PTC050, it is in the middle of a rewrite and should be back in ptc with version 060, along with linux and xwindows support). 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 start writing to 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. 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 unlock it - expect pain if you do. 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.