Screenshooting Problems

Blitz3D Forums/Blitz3D Programming/Screenshooting Problems

Serpent(Posted 2010) [#1]
First off, before anyone gets the wrong impressions, I am NOT talking about screenshooting in-game (i.e. capturing the front buffer). I am talking about a screenshot of the entire screen. Windows and all.

If any of you have ever heard of or used 'Deskspace' or similar programs, this is somewhat like what I'm trying to do. The user presses several hotkeys, and it looks like you zoom out from the current screen. You can then navigate between different screens.

From a technical point of view, this is what happens:
- The program takes a screenshot of the entire screen
- The program shows itself on top of other windows, and hides other windows
- The program displays a nice 3D 'wheel' of different screens.
- The screenshot is assigned a texture to the appropriate 3D rectangle representing the screen the user was just on.
- All of the screens that were just hidden are also assigned to that screen.
- The user can switch between screens.
- When the user selects a screen, the program shows the windows that correspond to that screen and hides itself.


Sorry about all of that, but I think its good that you actually know what I'm doing before I start asking questions. The only part that I am having trouble with is taking a screenshot. I have trawled over the web for ages. The best I've been able to do is find some PowerBASIC code. I have created a PowerBASIC DLL with an inbuilt function that saves the screen to a .bmp file which I then load as a texture in Blitz3D.

There are a few problems with this technique.
First of all, in order to get around some memory and loading "Optimisation" thing that DirectX does (see this topic I created before), each time a screenshot is taken I must tell the PowerBASIC function to save the file under a different name. For this I created a small name 'incrementing' function, but it's annoying and should be unnecessary anyway.
Secondly, several times I have had problems saving the file where the program takes several seconds and about 50 calls of the screenshot function to get it working (which the user of the program would not be happy about).
Finally, as far as I can tell, user created .decls files must either refer to a DLL in the system32 directory or have a full path to the DLL. This makes my program useless if it is not in the exact directory specified.

Obviously, there should be some way to bypass the whole part where the file is written to the hard drive and then loaded - i.e. transfer the screenshot directly from memory into a Blitz3D texture.
The best results that I have got so far involve getting the DLL to return a string containing all the data for the image and then, pixel by pixel, drawing it to an image with WritePixelFast. However, this is at least 30 times slower that the file saving/loading method.



Basically, I'm wondering if anyone can:

1. Transfer the PowerBASIC code into a function that can be compiled with Blitz (so that the program does not require an external DLL). I am trying to do this myself, but am having considerable trouble with it.
2. Find a way to bypass the whole saving to a .bmp file and then loading and deleting it. i.e. transfer the screenshot directly to an image or texture.
3. Explain how the PowerBASIC code BitBlts the screen into its own BITMAP structure.




Here is the PowerBASIC function I use for screenshooting:




Thankyou anyone who can be bothered to read all of this.


Oiduts Studios(Posted 2010) [#2]
-


Midimaster(Posted 2010) [#3]
I found this on the german blitzforum. It works perfect in BMax:




Serpent(Posted 2010) [#4]
Thanks Midimaster I'll see what I can do with this. Can't believe I couldn't find this after literally days of googling!


Serpent(Posted 2010) [#5]
I can see a few possible problems in converting this to Blitz3D code.
For a start, short integers and bytes are used in the BITMAPINFO type.
Blitz3D doesn't support any of the Pixmap stuff.

The best way I could hope to convert this BMax code is to somehow replace all of the pixmap stuff (hopefully this can be done using banks or something...) and somehow get around all of the BMax types which don't actually exist in B3D. I'll see if this can be done...


Charrua(Posted 2010) [#6]
hi, do a bank with the sizeof(the entire type) and then peek/poke selectively using bytes in pairs (to get words or 16 bits short integers) and int to get 32 bits integers.

btw sizeof(info) = 44 bytes.

Juan


Serpent(Posted 2010) [#7]
Oh so are custom types just stored as banks anyway? I thought there was more information stored than just the values of each variable. I'll try this - thanks Juan


Serpent(Posted 2010) [#8]
I think I'll try and adapt the PowerBasic code to work in Blitz3D - it seems slightly simpler than the BlitzMax code and I'm beginning to actually understand it. Also, the PowerBasic code eventually gets a pointer to the BGR values of each pixel within the bitmap, whereas the BlitzMax code deals with 'Pixmaps'. As far as I can tell this is actually a 'structure', rather than just a set of bytes containing the colour values for each pixel.


Serpent(Posted 2010) [#9]
This post looks pretty much dead, but anyway I've actually got a working screenshot function in Blitz3D going now.

The only problem is that the function returns a pointer to a bank with BGR values for the pixels. Then the fastest way I have of converting this to an image is through 'WritePixelFast'-ing to a screen-sized buffer. Although this is done in under a second, the idea is that the screen capture should be done practically instantly - if you're a user of this program, you'd want it to be quick and responsive. It would be great if anyone knows of a faster way to do this.


Here's the .decls file:


And here is the code:



Charrua(Posted 2010) [#10]
hi, the GetSystemMetrics function isn't included.
any way, i think that should be a fast way to do it, rtlMoveMemory of user32.decls perhaps?

Juan


Charrua(Posted 2010) [#11]
i set Widht and Heigth and run your program
i read Millisecs() to know the time, and the ScreenShot function is about 70ms in my laptop (toshiba L305, dualcore 2.16, intel mobile video)
and, 70..80 ms the draw to the backbuffer.

Juan


Charrua(Posted 2010) [#12]
that's the modifyed code i used:



Juan


Serpent(Posted 2010) [#13]
Oh sorry I missed the declaration for GetSystemMetrics:
.lib "user32.dll"
GetSystemMetrics%(nIndex%)

Used to find the Height and Width of screen.

With my computer (Inter Core2 Quad, nVidia 9600GS gfx card, display res. 1680x1050) it takes around 60ms to transfer the screen to the bank, but then takes around 750-800ms to write to the BackBuffer or Texture or Image Buffers.

And yeah, I was thinking that there should be a way to transfer the data directly from Mem. loc of the bank to the Mem. loc of a blitz image/texture/buffer, but I haven't looked into Win32 API commands for this. I'll try this next. The main problem I think will be determining the memory locations of blitz images/buffers but I'm hoping the handles blitz uses are just pointers.


Serpent(Posted 2010) [#14]
Judging from all of my failing RtlMoveMemory experiments, Blitz handles aren't just pointers to the data :(
It seems that the handles are pointers to structures in memory but I have no clue as to their format or how to find the actual bits containing the data in memory from them.


Charrua(Posted 2010) [#15]
by the way, there are a FastPointer lib for free.

but you have to know the structs on memory see:

http://fastlibs.com/libraries.php


Some time ago, in a post i see some blitz structs posted in his site

Blitz3D+ uses memory offsets inside blitz entities structs to get some values like scale that isn't reported by any BlitzFunction.

http://blitzbasic.com/Community/posts.php?topic=75711#846042


Juan


_PJ_(Posted 2010) [#16]
I'm a bit late in seeing this, but if it's still of any usde, there was an old code-archive post which I have used to capture the desktop image.

This relies on hiding the Blitz window with the windows API, and storws the desktop pixels in a bank. The bank data is then translated into a bitmap image:

It can be a bit slow, though...

; Blitz Basic 3D Example to get the desktop in blitz image and window :-)

; You can make a screensaver with it ;-)

; MR 19.03.2003

;--------------------------------------------------------------------------- automatik Blitz window on start 

;your Desktop width x height
Const dwidth# =MAKESURETOFILLTHISIN
Const dheight#=MAKESURETOFILLTHISIN


;your Blitz Window
Const swidth# =MAKESURETOFILLTHISIN
Const sheight#=MAKESURETOFILLTHISIN


;to find the Window
AppTitle "BlitzSaver"

;--------------------------------------------------------------------------- find Blitz Start Window Handle and HIDE it !

Global bhWnd=FindBlitzWindow("BlitzSaver")

;ShowWindow commands
Const SW_HIDE = 0
Const SW_SHOW = 5

api_ShowWindow(bhwnd,SW_HIDE)

;--------------------------------------------------------------------------- copy desktop to bank
Global ddc=DesktopDC()
Global dbank=CreateBank(swidth*sheight*4)
Local x,y,c
Local fx#,fy#
fx=dwidth /swidth
fy=dheight/sheight
For x=0 To swidth-1
 For y=0 To sheight-1
  c=GetPixel(ddc,x*fx,y*fx)
  PokeInt dbank,(x*4)+(swidth*y*4),c
 Next
Next
api_ReleaseDC(0,ddc)
;--------------------------------------------------------------------------- Show Start Blitz Window !
api_ShowWindow(bhwnd,SW_SHOW)
;--------------------------------------------------------------------------- Make the real Blitz Window :-)
Graphics3D swidth,sheight,32,2 ;<- note it is 3D but you can also use simple 2D 
SetBuffer BackBuffer()

Global cam=CreateCamera()
CameraClsMode cam,False,True 
MoveEntity cam,0,0,-25

Global cube=CreateCube()
EntityColor cube,0,255,0
ScaleMesh cube,0.5,0.5,0.5
ScaleMesh cube,10,10,10
EntityAlpha cube,0.5

AmbientLight 16,16,16 

Global light=CreateLight()
LightRange light,50
MoveEntity light,-25,25,-50
PointEntity light,cube

;--------------------------------------------------------------------------- Copy bank to image

Global imgDesktop=CreateImage(swidth,sheight)

LockBuffer ImageBuffer(imgDesktop) 
For x=0 To ImageWidth(imgDesktop)-1
 For y=0 To ImageHeight(imgDesktop)-1
  c=PeekInt(dbank,(x*4)+(swidth*y*4)) 
  Color 0,0,c  
  WritePixelFast x,y,ARGB(ColorBlue(),ColorGreen(),ColorRed()),ImageBuffer(imgDesktop) 
 Next
Next
UnlockBuffer ImageBuffer(imgDesktop) 

;######################################################################

FlushKeys
FlushMouse

Local ti#

While Not KeyHit(1)

 ti=MilliSecs()

 DrawBlock imgDesktop,0,0

 TurnEntity cube,-1,1,0
 RenderWorld
 
 ;50 FPS = each frame need 20 ms = (1/50)*1000 = 0.02 sec
 While Abs(MilliSecs()-ti)<20.0  
 Wend

 Flip
Wend
FlushKeys
FlushMouse

End

;######################################################################

Function FindBlitzWindow(title$) 
 ;MR 18.03.2003
 ;API Call
 ;not testet for Blitz2D+ !
 Local hWnd=api_FindWindow("Blitz Runtime Class",title$) 
 Return hWnd
End Function 

;######################################################################

Function DesktopDC()
 ;MR 18.03.2003
 ;API Call
 Local dc=api_GetDC(0) 
 Return dc
End Function

;######################################################################

Function ARGB(r,g,b)
 ;Return ((128 * $1000000) Or (r * $10000) Or (g * $100) Or b)
 Return ((r * $10000) Or (g * $100) Or b)
End Function

;###################################################################### USERLIB

;put this once in ..\userlibs\user32.decls
;.lib "user32.dll"
;FindWindow%( class$,Text$ ):"FindWindowA"
;GetDC%(hWnd% ):"GetDC"
;ReleaseDC (hWnd%,hDC%):"ReleaseDC"
;ShowWindow%(hWnd%,nCmdShow%):"ShowWindow"

;put this once in ..\userlibs\Gdi32.decls
;.lib "Gdi32.dll"
;GetPixel%(hdc%,X%,Y%):"GetPixel"

;add on user libs restart blitz ide !

;######################################################################



Serpent(Posted 2010) [#17]
Malice:
Thanks for this. This is sort of what I do except instead of GetPixel I used BitBlt. One thing - you don't need api calls to find the Blitz window. Just type:
hWnd = GetSystemProperty("AppHWND")

to get the window handle.
The problem with your code - like mine - is that it takes a while to run. Just like mine, you use WritePixelFast to 'copy' the information from the buffer to the screen/bitmaps/etc. I need a super-fast way to do this for my program. I'm surprised I didn't find this post when scrawling through many many searches!

Juan:
Useful links - thankyou! I'll see what I can do with them.


Serpent(Posted 2010) [#18]
Yes! The actual structures in memory! Thanks Juan.


Serpent(Posted 2010) [#19]
FastPointer looks very useful and interesting, but I won't be able to use it for this program. The idea is that it is a standalone executable - no external files, including DLLs.