View Full Version : Can DotNet screen cap part of the screen?
Jon-Huhn 11-02-2009, 03:08 PM Is it possible with DotNet to "screencap" a portion of the screen? I have a dialog that flashes and flickers alot as it redraws itself, and I'd like to take a screencap of the dialog before the redrawing, and use that bitmap to cover up the dialog while it redraws, and then remove the bitmap once the redrawing is done, so it appears to be an instantanous update.
Is it possible with DotNet to capture a portion of the screen?
Thanks!
|
|
MarcoBrunetta
11-02-2009, 03:16 PM
Well I'm guessing worst case scenario, you could just capture the whole screen and crop the part you want, but it's not a very clean solution. Is the dialog/control that flashes also .NET? Most of those have some methods to disable redraws while you update them.
Jon-Huhn
11-02-2009, 03:42 PM
I'm totally fine with first grabbing the entire screen, and then cropping down if necessary (although I highly suspect there would be a way to feed the dotnet function a rect so it can target just the area I need)
The dialog is not a DotNet form, it's a maxscript rollout opened as a dialog, containing a mixture of native maxscript controls and DotNet controls.
Thanks.
MarcoBrunetta
11-02-2009, 03:46 PM
Yhea probably, I was just suggestion a possible course of action if you couldn't make that work.
However I still think you should check all other possibilities first. What about setting the visible property to false on all controls?
Jon-Huhn
11-02-2009, 03:51 PM
I haven't tried that yet, but I'm pretty sure I'll still see some flashing... all the controls will disapear for a frame, and then reappear in their new configurations. It's that flashing that I'm trying to eliminate.
denisT
11-02-2009, 05:08 PM
I haven't tried that yet, but I'm pretty sure I'll still see some flashing... all the controls will disapear for a frame, and then reappear in their new configurations. It's that flashing that I'm trying to eliminate.
the best way to reduce flickering is to disable redraw of whole window before updating of the controls and set to enable after. it's easy to do. (and much faster then make a screenshot)
Jon-Huhn
11-02-2009, 05:31 PM
Denis,
I'm only aware of disableSceneRedraw() for disabling the redrawing of scenes, and that only seems to affect the 3D world itslef, and not the various UI's. Is there another command that disables the UI's from being redrawn as well?
Thanks.
What exactly are you drawing into? Dotnet objects or Max rollouts and UI items? What you are suggesting would be very slow.
Jon-Huhn
11-02-2009, 05:50 PM
Paul,
Well, right now, when the user hits a certain button causing the dialog to reconfigure itself, I have a big button appear over the entire dialog area to cover up the controls moving to new positions. Once they're all in place, I move the big button off to -1000,-1000 to reveal the dialog controls again. This of course causes the entire dialog interface to appear blanked out for one frame, but at least keeps the user from seeing individual controls scaling/moving to new locations.
So my thought was, that if I could apply a bitmap to the large button that blocks everything, and that bitmap is just a screenshot of what the dialog looked like before the reconfiguration, it would completely removing the perception of any flashing or anything.
Interesting idea. Not sure how to go about that. Does this take a long time?
Kameleon
11-02-2009, 06:25 PM
If you place the controls inside a Panel control, you can use the DrawToBitmap method. You could also have a look at the invalidate and update methods.
Edit: Better yet, try SuspendLayout and ResumeLayout
Jon-Huhn
11-02-2009, 06:34 PM
Paul,
As far as just the blank button itself being moved on/off screen, it takes almost no time that I can tell (haven't officially timed it, though).
But the real test will be how quickly an image can be grabbed of the dialog, and then applied to the button. And also if it can be done at all.
Artur,
If I had done the entire dialog in DotNet, that would be an option, but I've come too far at this point using a mix of DotNet and maxscript controls, that it won't be worth my time simply to eliminate some flickering.
Kameleon
11-02-2009, 06:49 PM
Can you use an external .dll?
Jon-Huhn
11-02-2009, 06:59 PM
I'm not familiar with using external dll's, but I'm willing to learn. As long as it was something that was either garanteed to be on every end user's machine, or I was able to package it with the tool.
That being said, it really seems like DotNot itself would allow some way to grab a screen shot of any arbitray rect on the screen.... I just have to find it.
MarcoBrunetta
11-02-2009, 07:19 PM
(DotNetClass "system.drawing.Graphics").CopyFromScreen seems to be the key
There's a simple code sample in C# here:
http://weblogs.asp.net/jalpeshpvadgama/archive/2008/01/28/how-to-take-screenshot-in-c.aspx
Jon-Huhn
11-02-2009, 07:32 PM
Thanks, Marco! That looks very promising. It'll take me a bit to figure out how to translate into Maxscript, but I'll let you know how it turns out when I do.
Thanks so much!
MarcoBrunetta
11-02-2009, 07:38 PM
Sure, I'll give it a try myself when I get some free time, shouldn't be too hard.
Jon-Huhn
11-02-2009, 08:56 PM
Yikes, can't wrap my head around all these DotNet objects and classes.
So apparently, first you have to create an image object, and from that you can create a graphics object, which is what has the CopyFromScreen() method? When I try the following code, it just returns "undefined":
img=(dotNetClass "System.Drawing.Image").fromFile "C:\\someDummyGraphic.tif"
gfx=(DotNetClass "system.drawing.Graphics").fromImage img
screenCap=gfx.CopyFromScreen 0 0 0 0 (dotNetObject "System.Drawing.Size" 100 100)
I haven't figured out how to make image objects out of thin air, so here I'm building one using the fromFile() method, to pull in a blank .tif file that I made just for testing purposes. The img and gfx variables are being filled with valid values, but the screenCap varaible is just coming back undefined.
Anyone know what I'm doing wrong (and also know how to make image objects without needing an exising external file saved prior)?
Jon-Huhn
11-02-2009, 09:11 PM
Oops, I'm understanding now. The pixels are pasted into the image object stored in img.
MarcoBrunetta
11-02-2009, 09:15 PM
img=(dotNetObject "System.Drawing.Bitmap" 100 100)
gfx=(DotNetClass "system.drawing.Graphics").fromImage img
screenCap=gfx.CopyFromScreen 0 0 0 0 (dotNetObject "System.Drawing.Size" 100 100)
gfx.CopyFromScreen 0 0 0 0 (dotNetObject "System.Drawing.Size" 100 100)
img.save "C:\\test.jpg"
Jon-Huhn
11-03-2009, 01:09 AM
Thanks, Marco
So my idea didn't work. I'm able to now screen cap the dialog's rect and paste that into a button that's temporarily scaled to the size of the dialog in order to hide the controls flickering underneath, but in the end it still looks like one big flicker, and the pasted image doesn't have time to display on the screen. I suspect if I used a timer to spread out the activites into seperate CPU cycles this would work the way I envisioned, but then it will take even longer to redraw the dialog because of the delays added. I might experiment with that at some point, but for now I'll just live with the flicker.
Thanks all for your input.
Are you using a subRollout? If not that might help, move the rollout off screen, make the change and move it back. That way each item isn't being placed one at a time.
Might be just as much of a flicker how ever.
Jon-Huhn
11-03-2009, 04:10 PM
In this particular case I'm not using a subRollout, although for some other stuff I will be, so I'll investigate your idea then to see if it helps at least with that part.
Thanks!
denisT
11-03-2009, 06:05 PM
Denis,
I'm only aware of disableSceneRedraw() for disabling the redrawing of scenes, and that only seems to affect the 3D world itslef, and not the various UI's. Is there another command that disables the UI's from being redrawn as well?
Thanks.
it's how to lock/unlock window redraw:
fn getWinRedrawClass =
(
source = ""
source += "using System;\n"
source += "using System.Runtime.InteropServices;\n"
source += "class WinRedraw \n"
source += "{\n"
source += "\t[DllImport(\"user32.dll\")]\n"
source += "\tstatic extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);\n"
source += "\tpublic bool RedrawAllWindow(IntPtr hWnd, uint flags) { return RedrawWindow(hWnd, IntPtr.Zero, IntPtr.Zero, flags); }\n"
source += "\tpublic bool RedrawAllWindow(Int32 hWnd, uint flags) { return RedrawWindow((IntPtr)hWnd, IntPtr.Zero, IntPtr.Zero, flags); }\n"
source += "}\n"
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.GenerateInMemory = on
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
input = compilerResults.CompiledAssembly.CreateInstance "WinRedraw"
)
global WinRedrawClass = if WinRedrawClass == undefined then getWinRedrawClass() else WinRedrawClass
try(destroydialog RedrawTestRol) catch()
rollout RedrawTestRol "Redraw Test" width:200 height:240
(
fn setWindowRedraw hwnd act = if hwnd != undefined do
(
local WM_SETREDRAW = 0x000B
windows.sendmessage hwnd WM_SETREDRAW act 1
)
dotNetControl tv "TreeView" backcolor:(dotNetClass "System.Drawing.Color").Gray width:200 height:200 pos:[0,0]
button b1 "" width:22 pos:[-100,0]
button b2 "" width:22 pos:[-100,0]
button b3 "" width:22 pos:[-100,0]
button b4 "" width:22 pos:[-100,0]
button b5 "" width:22 pos:[-100,0]
button lockRedraw "Lock Redraw" width:90 pos:[8, 210]
button withRedraw "With Redraw" width:90 pos:[100, 210]
local buttons = #()
local hwnd
fn loadTV =
(
i = dotnetobject "TreeNode" ""
items = for k=1 to 400 collect
(
item = i.clone()
item.text = "Item " + k as string
item
)
tv.BeginUpdate()
tv.nodes.clear()
tv.nodes.addrange items
tv.EndUpdate()
tv.Refresh()
)
on lockRedraw pressed do
(
setWindowRedraw hwnd 0 -- set redraw to off
for b in buttons do b.pos = random [0,0] [158,178]
loadTV()
setWindowRedraw hwnd 1 -- set redraw to on
flags = 0x81 -- RDW_INVALIDATE | RDW_ALLCHILDREN
-- or
flags = 0x01 -- RDW_INVALIDATE
WinRedrawClass.redrawallwindow hwnd flags -- force redraw
)
on withRedraw pressed do
(
for b in buttons do b.pos = random [0,0] [158,178]
loadTV()
)
on RedrawTestRol open do
(
loadTV()
hwnd = (windows.getChildHWND 0 "Redraw Test")[1]
buttons = for b in RedrawTestRol.controls where iskindof b ButtonControl and b.text == "" collect
(
b.pos = random [0,0] [158,178]
b.text = b.name
b
)
)
)
createdialog RedrawTestRol
if anyone knows how to force window redraw without c#, please let me know
CGTalk Moderation
11-03-2009, 06:05 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
vBulletin v3.0.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.