Poking Imagebuffers directly, it works! :)

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

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

Very interesting, Are the decls files somewhere to download?

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"


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.

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

There are _two_ 16 bit formats though. RGB 555 and RGB 565.

; 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))


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.

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

So, this is VERY cool. I wonder, also, could something similar work with audio?
M