DirectDraw:
Accessing Surface Memory
By: Jack Hoxley
Written: June 2000
Download:
DD_Ripple.Zip
(49kb) and DD_pixPlot.zip (49kb)
There comes a point when writing
a game (or program) in directdraw and you reach the end of it's usefullness
- you need it to do more, but it cant. Or, you want it to do something that
cant be done using the built in commands. This is when it starts getting fun/complicated....
Directdraw allows you two methods
of accessing surface memory, and both of them can be really annoying to use,
and very complicated. However, in return for your hard work you can create amazing
special effects and features for your program. Go along to Unlimited
Realities homepage for some incredible examples; although not all of them
are in directX, the idea is the same. With some simple (or complicated) maths
algorithms you can create custom effects.
Method 1: Set/GetLockedPixel
The first method is relatively easy, and fairly safe to use, but unfortunately
much slower. You may have noticed that when setting the colour for various things
it requires a long value? These methods allow you to retrieve the long
value of a certain pixel, and set it again.
Sub ProcessMemory
Dim Colour as Long
Dim rArea as RECT
Dim ddsd as DDSURFACEDESC2
rArea.Bottom=480:rArea.Right=640
MainSurf.Lock rArea,ddsd, DDLOCK_WAIT,0
'We now have access to the memory. From now until
the Unlock you are treading
'on thin ice......
'These are our two commands.
Colour=Mainsurf.GetLockedPixel(100,120)
Mainsurf.SetLockedPixel(100,120,Colour+10)
MainSurf.Unlock rArea
End Sub |
|
|
|
Method 2: GetLockedArray
This is the complicated, difficult, dangerous and faster method to use.
I have lost count of the number of times I locked up my computer playing around
with this method. Basically, it passes back an array which points to the surface
memory itself. You can then read/write to this array and the effects will be
mirrored on the surface. Sounds simple. Two points of warning though:
- If you write an invalid value
to the array you will not get an error; and you will be very lucky if you
get back into windows without restarting your computer. Values must be whole
numbers and between 0 and 255.
- If you write to a value outside
the array you will not get an error, and you will probably have to restart
your computer. This is basically a memory leak.
Then there is another problem
- the debugger will not work whilst you are processing memory. Between the Surface.Lock
and Surface.Unlock methods windows ceases execution, this is because directdraw
takes the win16mutex which basically suspends windows. Because windows
is effectively shut down you must do NOTHING complicated or invloved with other
components. ie. No DLL calls, no file access and no running of other applications.
Also, dont try anything clever inside directx, you can't blit from or to a locked
surface, and some other clever operations may well fail. This leads onto another
point - the code must be flawless, if there is an error in the compiled version
you can say goodbye to windows.
You may have had enough of the
problems already, but the final problem is probably the most annoying. The data.
The array will basically show you the raw data from a bitmap, so it's fine if
you understand that, otherwise (like me) it's extremely difficult. Each entry
in the array is a byte which means 0-255 (every colour is 0-255 you may
have noticed). Each entry in the array represents one channel of the colour,
NOT one pixel. using the RGB(x,x,x) statement will be fatal. This is fine if
you aren't interested in the colour of the pixel directly, but it is if you
try setting the colour to be specific. If you only want to deform the current
image, you can do that without paying much attention to the current value -
you just run a formula through it.
Getting the Red/Green/Blue colour
components can be extremely difficult. 16bit is a nightmare; 8, 24 and 32bit
isn't too difficult. Hopefully
you understand how computers store data; there are 8bits to a byte, 1024 bytes
to a kilobyte etc... Therefore; if our array is a collection of bytes, we need
to get the correct bits out of it. For 8bit it is easy - each byte (8 bits to
a byte) represents the colour; because 8 bit mode is palettised this value refers
to the palette entry for that pixel. 16 bit can either be in 555 or 565 mode
- where they are all 5 bits (less than a byte) and there is one bit spare; or
they are all 5 bits and Green is 6 bits, with no bytes to spare. 24 bits are
3 bytes, so each set of three bytes in the array are the channels for the pixel.
It tends to be organised in BGR format rather than RGB... 32 bits are 4 bytes
(getting the hang of this yet?), this is because 32bit colour can have an alpha
channel. The bytes are organised in the format ARGB - so unless you are interested
in the alpha channel you can skip every 4th byte.
Here's the code for accessing
the surface memory using arrays:
Sub ProcessMemory
'Note: This is almost identical to the previous
method...
Dim Colour as Long
Dim rArea as RECT
Dim ddsd as DDSURFACEDESC2
dim Pict() as Byte 'Note, there is NO boundaries
specified
rArea.Bottom=480:rArea.Right=640
MainSurf.Lock rArea,ddsd, DDLOCK_WAIT,0
'We now have access to the memory. From now until
the Unlock you are treading
'on thin ice......
MainSurf.GetLockedArray Pict
'You can now manipulate the array into whatever
form you wish
'But remember the rules/warnings
'The array will still point to the memory until the Unlock command. After
'this point it becomes a normal VB array...
'It all goes in the form of Pict(x,y) = ??
'Even though two dimensions aren't specified, they can be referenced like
that.
'If you're really clever you can access it as one
linear block of memory...
MainSurf.Unlock rArea
End Sub
|
|
|
|
There are two example projects
to go with this tutorial, one is a directX conversion of an unlimited realities
example, the other just a simple example. They can be downloaded from the top
of this page, or from the downloads page.
|