Poking Imagebuffers directly, it works! :)

Blitz3D Forums/Blitz3D Programming/Poking Imagebuffers directly, it works! :)

Tom(Posted 2004) [#1]
Hi,

In the last 2 days I've been trying to grab an image from a window into a BB buffer. Various ways I tried didn't work, and read/writepixel is dog slow.

This evening I was looking for a way to access Imagebuffers better, and I've figured it out!

Note that, this is not good programming practice, it's experimental, and future BB builds may break it.

The pointer to an imagebuffer is Imagebuffer + 72. By redirecting a Bank to this address, you can simply Poke directly to the image buffer! The functionality is like LockedPixels() in Blitz+

Additionaly you should be able to pass the Bank pointer to an external DLL and write direct to the buffer.

There are some quirks, lock/unlockbuffer isn't needed for regular image buffers, but they ARE needed to access the frontbuffer() & backbuffer() [thanks again Andreas!], and I don't yet know if B3D uses 32bit for all images internaly, adjustments would have to be made for that.

One other thing, whilst I was looking for 'access', some people who've tested it for me are telling me it's much faster than writepixelfast, so that's a bonus I guess :)

Here goes!

Graphics 800,600,32,2
SetBuffer BackBuffer()

; How to access an ImageBuffer directly
;
; NOTE!!!! This may not be safe to do, and future builds may break it!
;
; You'll need to add these definitions to kernel32.decls in your
; Userlibs folder. If you already have definitions from other code
; snippets, you'll have to rename these, and change references to
; them in the code.
;
;api_RtlMoveMemory%(Destination%,Source*,Length%) : "RtlMoveMemory"
;api_RtlMoveMemory2%(Destination*,Source%,Length%) : "RtlMoveMemory" 
;api_RtlMoveMemory4%(Destination%,Source%,Length%) : "RtlMoveMemory"

Const IMAGEBUFFER_OFFSET=72

image=CreateImage(256,256)
iBuff=ImageBuffer(image)

; NEEDED! Activates the image buffer ?!?!?
LockBuffer iBuff
UnlockBuffer iBuff

; This Bank will be redirected & resized to point to the Imagebuffer
Bank=CreateBank(4)

; This is a small check for later to show the Bank
; Pointer gets restored properly
PokeInt(Bank,0,199)

; Store the old banks pointer so we can clean up properly
OldBankInfo=CreateBank(8)
StoreBankInfo(Bank,OldBankInfo)

PointBankToImagebuffer(Bank,image)

.redo
t=MilliSecs()
For y=0 To 255
	For x=0 To 255
		; Bank, x, y, RGBA, ImageHeight
		PokeImageBuffer(Bank,x,y,GetRGB(x,0,y),256)
	Next
Next
time=MilliSecs() - t


While Not MouseHit(1)
	Cls
	DrawImage image,MouseX(),MouseY()
	Text 0,0,"LMB to Exit, RMB to redraw square using Poke!"
	Text 0,12,"'Bank' size is: "+ BankSize(Bank)
	Text 0,24,"Redraw: "+ time
	Flip
	If MouseHit(2) Goto redo
Wend


; Important! We must restore the old Bank so we can free it!
RestoreBankInfo(Bank,OldBankInfo)

; Prove the old pointer is restored
Text 0,24,"Check 'Bank' is restored (199?): "+ PeekInt(Bank,0)
Text 0,36,"Bank Size is (4?): "+ BankSize(Bank)
Flip



WaitKey
End


FreeBank Bank
FreeBank OldBankInfo

Function GetRGB(Red,Green,Blue)
	Return Blue Or (Green Shl 8) Or (Red Shl 16)
End Function

Function PokeImageBuffer(buffer,x,y,rgba,imageH)
	PokeInt( buffer, (y*imageH*4) + (4*x), rgba )
End Function

Function StoreBankInfo(b,info)
	Local temp=CreateBank(4)
	api_RtlMoveMemory2(temp,b+4,4)
	PokeInt(info,0,PeekInt(temp,0)) ; store old pointer
	api_RtlMoveMemory2(temp,b+8,4)
	PokeInt(info,4,PeekInt(temp,0)) ; store old size
	FreeBank temp
End Function

; The clever bit, we change 'Bank' to point to the ImageBuffer!
Function PointBankToImagebuffer(b,image)
	api_RtlMoveMemory4(b+4,ImageBuffer(image)+IMAGEBUFFER_OFFSET,4)
	size=ImageWidth(image) * ImageHeight(image) * 4
	Local temp=CreateBank(4)
	PokeInt(temp,0,size)
	api_RtlMoveMemory(b+8,temp,4)
	FreeBank temp
End Function

Function RestoreBankInfo(b,info)
	Local temp=CreateBank(4)
	PokeInt(temp,0,PeekInt(info,0))
	api_RtlMoveMemory(b+4,temp,4)		; restore old pointer
	PokeInt(temp,0,PeekInt(info,4))
	api_RtlMoveMemory(b+8,temp,4)		; restore old size
	FreeBank temp
End Function
	


It's very important that you restore the bank back else you won't be able to free it properly :)

If you want to do some speed tests, note that in this demo I'm calling GetRGB often, and multiplying same values in Pokebuffer, those could be tweaked [thanks Andreas for that!]

Have fun!
Tom


jfk EO-11110(Posted 2004) [#2]
Very interesting, Are the decls files somewhere to download?


BlitzSupport(Posted 2004) [#3]
Nice, Tom!

JFK: If you have a file in Blitz 3D -> userlibs called kernel32.decls you just add the RtlMoveMemory lines to it:

api_RtlMoveMemory%(Destination%,Source*,Length%) : "RtlMoveMemory"
api_RtlMoveMemory2%(Destination*,Source%,Length%) : "RtlMoveMemory" 
api_RtlMoveMemory4%(Destination%,Source%,Length%) : "RtlMoveMemory"


If you don't have it, create a plain text file with that name, paste the above lines in, and stick this at the very top of the file:

.lib "kernel32"



sswift(Posted 2004) [#4]
But don't you also need to know lockedformat and lockedpitch?

BlitzPlus supports poking directly to buffers, but the lockedformat and lockedpitch commands are there to tell you what format the color is in, (RGB 565, 555, or 888) and how wide each line of the buffer is. Because even though an image is 256x256, it might be stored in a larger buffer which means you have to skip 256 pixels at the end of each line to get back to the start of the next line.

I haven't played with this so I don't know if that's the case here, but it seems like it would have to be. You can get away with just using 24bit color so you always know the color format, but you still need to know the pitch, unless maybe you blit only into individual images and not into screen buffers. Maybe.


Tom(Posted 2004) [#5]
I fixed a typo, I forgot to change the name of the bank on one of the the freebank lines.

Andreas pointed out that if you do Graphics3D 800,600,16,1 it will use 16bit for images/textures, so I'm looking for a routine to convert down from 32bit to 16bit.

Texture Bit Depth can be checked like so:

; NEEDED! Activates the image buffer ?!?!?
LockBuffer iBuff
UnlockBuffer iBuff

; After lock/unlock
infoB = CreateBank(12) 
api_RtlMoveMemory2(infoB,ImageBuffer(image)+92,12) 
; Width=PeekInt(Bank,0) 
; Height=PeekInt(Bank,4) 
If PeekInt(infoB,8)=16
	BPP=2 ; write 2 Bytes for 16bit
Else
	BPP=4 ; 4 bytes (Int) for 32bit
End If
FreeBank infoB


Then adjust code accordingly

It's interesting that you can point a Bank to anything...

Tom


sswift(Posted 2004) [#6]
There are _two_ 16 bit formats though. RGB 555 and RGB 565.


sswift(Posted 2004) [#7]
; Convert from RGB24 to individual color components.
R = ((RGB24 Shr 16) And 255) 
G = ((RGB24 Shr 8)  And 255) 
B = (RGB24          And 255) 

; Convert from individual color components to 24 bit RGB.
RGB24 = ($ff000000) Or (G Shl 8) Or (R Shl 16) Or B 

; Convert from individual color components to 16 bit 565 RGB.
RGB565 = ((R shr 3) shl 11) Or ((G shr 2) shl 5) Or (B shr 3))

; Convert from individual color components to 16 bit 555 RGB.
RGB555 = ((R shr 3) shl 10) Or ((G shr 3) shl 5) Or (B shr 3))



AbbaRue(Posted 2004) [#8]
This little article is awsome.
I only wish the next version of Blitz3D would have new commands that allow handles to be interchanged.
It wouldn't be hard to allow handle exchanges between image buffers, Textures, Banks, and sprites.
They are all very simular data.
Should also be able to transfer any of these over to an array.
All this should be possible without having transfer data over from one to the other.
Just change an image buffer handle into a texture handle for example.
That type of functionality would make Blitz3D a lot more powerful and usefull.


Tom(Posted 2004) [#9]
Hi,

From the information we gathered, Andreas has provided us with some functions that effectively work just as they do in BlitzPlus!

http://www.blitzcoder.com/cgi-bin/ubb-cgi/postdisplay.cgi?forum=Forum6&topic=001266

Additionaly, I figured out how to Blit from any window into a buffer, so a good day all round!

Snippet
[code]
; Assuming 32bit..
; 'temp' is the Bank pointing to our imagebuffer()
; the color bits are copied to it :)
hdcNew=api_CreateCompatibleDC(Bhdc) ; New Device Context to hold the Bitmap
hBmp=api_CreateBitmap(512,512,1,32,0) ; The Bitmap we'll Blit to
api_SelectObject(hdcNew,hBmp) ; Select the new Bitmap
api_BitBlt(hdcNew,0,0,512,512,SOURCE_HDC,DestX,DestY,SRCCOPY) ; Blit to the new bitmap
api_GetBitmapBits(hBmp,512*4*512,temp) ; Copy the Bitmap Bits (color!) from Bitmap to temp (our image buffer!)
...
free up HDCs e.t.c..........
[/CODE]

Cya!
Tom


mearrin69(Posted 2004) [#10]
So, this is VERY cool. I wonder, also, could something similar work with audio?
M


chi(Posted 2010) [#11]
VERY COOL!

But it´s not working anymore...
PokeInt( buffer, (y*imageH*4) + (4*x), rgba )

... gives me a MAV

any ideas would be appreciated!!! ;)


thx, chi


Zethrax(Posted 2010) [#12]
This thread is six years old, so updates to Blitz3D have probably broken this method.


chi(Posted 2010) [#13]
Probably... but worth a try!


Yasha(Posted 2010) [#14]
buffer = TextureBuffer or ImageBuffer

LockBuffer buffer
BufferPtr = MemoryFastPeekInt(buffer + 72)
BufferPitch = MemoryFastPeekInt(buffer + 56)

MemoryFastPokeInt(BufferPtr + ( ix + iy * BufferPitch ) * 4, PixColour)


...works, but is not significantly faster than WritePixelFast. The buffer still needs to be locked for the BufferPtr to be a valid pointer, which suggests that the real slowdown nowadays is in uploading the new information to the graphics card.


chi(Posted 2010) [#15]
...works, but is not significantly faster than WritePixelFast.

doh, I´m looking for something (much) faster than WritePixelFast! At least fast enough to transfer 60 fps without any problems ;)


Serpent(Posted 2010) [#16]
I wish I found this thread before when I was working on a screenshooting function... Either way I managed to get mine to work. It does a complete 1680x1050 screenshot on my PC in around 35ms! It's in the code archives but here it is again:




and if you can be bothered to read my descriptions on how to use them:



Basically, my functions make use of BitBlt for the screen capturing bit - rather than specifying the handle to a window, I use '0' to get the screen. I think '0' is considered the handle for the 'Desktop' window - the highest in the window hierachy. Either way it works.

To adapt this for capturing a window, you could simply replace all of the zeros with a variable that specifies the window handle and it should still work (as far as I know).

Also my functions don't quite achieve 30 FPS for a 1680 by 1050 screenshot, they are as fast I need, and I can't see a way of improving them.


Note: The reason that there is a '2D' and a '3D' one is that 2D buffers seem to fail if the buffer width isn't a multiple of 16 and 3D buffers fail if the width isn't a power of 2. The functions account for these problems.



Edit: Decls file (Note: may be a few unnecessary functions in here - sry)



chi(Posted 2010) [#17]
Hey thx! I´ll definitely check it out the next days! (afk)