I need to take print-screens of a Windows application very very fast to make a video out of it... I have been using C# all the way, but I'm open to any language in which this process may be faster.
I have used many techniques:
.net functions: Bitmap.CopyFromScreen()
GDI
Direct3d/DirectX
The fastest I've got was using GDI and still I get less than 10 fotograms per second. I would need a bit more than that, at least 20 or 30...
It seems very strange to me that such a simple operation is so demanding. And it looks as if using a faster cpu doesn't change the situation.
What can I do? Is it possible to directly capture the drawing of an application using gdi or something? Or maybe even lower-level functions to catch the info being thrown to the graphics card?
Any light on this issue would be pretty much appreciated.
Thanks a lot
A lot of programs use a driver and allows your application to hook into the lower level display routines. I'm not exactly sure how this is done, but it is possible.
Here is a starting point on writing Windows driver. http://msdn.microsoft.com/en-us/library/ms809956.aspx
Here is something I just found via Google:
http://www.hmelyoff.com/index.php?section=17
You probably want to use something like Camtasia. Depends on why you're making the video.
I use a rewritten version of Jeff's User-Friendly Exception Handling, and he uses BitBlt from GDI to capture screenshots. Seems fast enough to me, but I haven't benchmarked it, and we just use it for one-at-a-time shots when there's an unhandled exception thrown.
#region Win32 API screenshot calls
// Win32 API calls necessary to support screen capture
[DllImport("gdi32", EntryPoint = "BitBlt", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int BitBlt(int hDestDC, int x, int y, int nWidth, int nHeight, int hSrcDC, int xSrc,
int ySrc, int dwRop);
[DllImport("user32", EntryPoint = "GetDC", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int GetDC(int hwnd);
[DllImport("user32", EntryPoint = "ReleaseDC", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int ReleaseDC(int hwnd, int hdc);
#endregion
private static ImageFormat screenshotImageFormat = ImageFormat.Png;
/// <summary>
/// Takes a screenshot of the desktop and saves to filename and format specified
/// </summary>
/// <param name="fileName"></param>
private static void TakeScreenshotPrivate(string fileName)
{
Rectangle r = Screen.PrimaryScreen.Bounds;
using (Bitmap bitmap = new Bitmap(r.Right, r.Bottom))
{
const int SRCCOPY = 13369376;
using (Graphics g = Graphics.FromImage(bitmap))
{
// Get a device context to the windows desktop and our destination bitmaps
int hdcSrc = GetDC(0);
IntPtr hdcDest = g.GetHdc();
// Copy what is on the desktop to the bitmap
BitBlt(hdcDest.ToInt32(), 0, 0, r.Right, r.Bottom, hdcSrc, 0, 0, SRCCOPY);
// Release device contexts
g.ReleaseHdc(hdcDest);
ReleaseDC(0, hdcSrc);
string formatExtension = screenshotImageFormat.ToString().ToLower();
string expectedExtension = string.Format(".{0}", formatExtension);
if (Path.GetExtension(fileName) != expectedExtension)
{
fileName += expectedExtension;
}
switch (formatExtension)
{
case "jpeg":
BitmapToJPEG(bitmap, fileName, 80);
break;
default:
bitmap.Save(fileName, screenshotImageFormat);
break;
}
// Save the complete path/filename of the screenshot for possible later use
ScreenshotFullPath = fileName;
}
}
}
/// <summary>
/// Save bitmap object to JPEG of specified quality level
/// </summary>
/// <param name="bitmap"></param>
/// <param name="fileName"></param>
/// <param name="compression"></param>
private static void BitmapToJPEG(Image bitmap, string fileName, long compression)
{
EncoderParameters encoderParameters = new EncoderParameters(1);
ImageCodecInfo codecInfo = GetEncoderInfo("image/jpeg");
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, compression);
bitmap.Save(fileName, codecInfo, encoderParameters);
}
Related
I've been writing Java SE 8 desktop application. I use Eclipse IDE, Oracle's JDK, and run it on MS Windows 10 OS.
My app draws diagrams, in short. I draw a diagram on JPanel which becomes part of JTabbedPane. It displays it well on GUI, and it is very responsive. The problem shows up when I pass the diagram on printing service. But instead of printing it on printer, I choose, "Microsoft print to PDF" service. What happens next, is that if the diagram is large, when you scroll it down, you will observe that its quality drops down. That is, grids start disappearing, new lines appear, etc.
The pic out here.
As you can see, eventually vertical grids vanish, diagonal line creeps in, and later gets even worse. And that is unwelcome.
Relevant code in here:
public final class GanttChartDiagram extends TopDiagram{
#Override
public void paintComponent(Graphics graph){
super.paintComponent(graph);
Graphics2D g2D = (Graphics2D)graph;
g2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
...........
#Override
public Dimension getPreferredSize(){
return new Dimension(this.diagramWidth + 20, this.diagramHeight + 20);
}
}
}
The getPreferredSize() method identifies the size of diagram, so that the app knows how to adjust the scroll-bars accordingly to fit the diagram in. Otherwise by default it return 0, if not overridden.
That is the class where I draw the diagram.
The super-class out here:
public abstract class TopDiagram extends JPanel implements Printable {
private static final long serialVersionUID = 1469816888488484L;
#Override
public void paintComponent(Graphics graph){
super.paintComponent(graph);
};
/**
* Prints selected diagram
*/
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
Graphics2D dimension = (Graphics2D)graphics;
dimension.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
if(pageIndex < PrintingImageBuffer.getImageBuffer().size()){
dimension.drawImage(PrintingImageBuffer.getImageBuffer().get(pageIndex), null, 0, 0);
return PAGE_EXISTS;
}
else{
return NO_SUCH_PAGE;
}
}
}
Now that is where I print the diagram:
public static void printDiagram(){
new Thread(new Runnable(){
public void run(){
TopDiagram diagram = null;
if(id.equalsIgnoreCase("GanttChart")){
diagram = ganttChartDiagram;
}
final PrinterJob printer = PrinterJob.getPrinte
rJob();
printer.setJobName("Printing the "+id+" Diagram");
PageFormat format = printer.pageDialog(page);
int nowWidth = (int)diagram.getPreferredSize().getWidth();
int nowHeight = (int)diagram.getPreferredSize().getHeight();
BufferedImage buffImg = new BufferedImage(nowWidth, nowHeight, BufferedImage.TYPE_INT_ARGB);//default type
Graphics2D d = (Graphics2D)buffImg.getGraphics();
....etc..................
}
}).start();
}
Now the interesting part is this:
int nowWidth = (int)diagram.getPreferredSize().getWidth();
int nowHeight = (int)diagram.getPreferredSize().getHeight();
On the same instance of diagram, on multiple invocations of print (or within the method), it may or may not return different values. So that it was causing me some sort of Raster exception. But I managed to get rid off of that exception by invoking size method only once and reusing that size value throughout the method. So that, the size value stays the same, cause it is read only once.
Bad solution, but it works.
I would like to solve this issue too. Firstly, how come that this invocation diagram.getPreferredSize().getWidth() on the same instance of diagram obj. returns different size value? One more thing, is that I overrode this method as has been presented above. And the diagram object is created only once, no recalculations.
This is where I create the diagram obj. on Swing Worker only once per application's life-cycle.
GanttChartSwingWorker ganttSwingWorker = new GanttChartSwingWorker(GanttChartDiagram::new, tabbedPane, showPerformanceDiagramTab, ganttChartDiagramTabReady);
ganttSwingWorker.execute();
new Thread(() -> {
try{
ganttChartDiagramTabReady.await();
ganttChartDiagram = ganttSwingWorker.getGanttChartDiagram();
}catch(InterruptedException ie){ie.printStackTrace();}
}
).start();
Swing Worker part:
diagram = this.get();
JScrollPane scrollPaneChart = addScrollPane(diagram);
tabbedPane.addTab("Gantt Chart", null, scrollPaneChart, "Displays Gantt Chart Diagram");
Some diagram objects can be time consuming to create, imposes delay, so I use Swing Worker to do that.
So, in summary:
How to make the diagram to appear clean when I print/save it on pdf file in the way that I explained?
How to make the diagram size to calculate consistently as per multiple invocations? What leads to different diagram size values retrieving it from the same diagram object instance on multiple calls?
I just figured out what the problem is.
I observed that when it comes to drawing the diagram, the
paintComponent(Graphics g) method has been invoked repeatedly. It keeps redrawing the diagram over and over again. It is invoked by the system implicitly, yet my implementation had been triggering it.
And that trigger comes in the form of this.setSize(width, height) method on derived JPanel object. So that each time the paintComponent(Graphics g) re-executes, it sets the size on JPanel which yet triggers additional execution of the painComponent method. In the end, it is an infinite loop.
And that infinite execution was the cause of the problem; it was producing distorted diagram image on pdf file.
Solution: execute the setSize method only when it is needed; on initial panel set-up, or resize.
I'm using Swing and JavaFX to render images to the screen, but getting unexpected timings: the aim is simply to render 1,000,000 images at random positions on a component. Why is JavaFX taking so long?
Results: Swing : 2.5 secs. JavaFX 8.5 secs. Code below.
In JavaFX.
public class JFXTest extends Application
{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage theStage)
{
Group root = new Group();
Scene theScene = new Scene( root );
theStage.setScene( theScene );
Canvas canvas = new Canvas( 1000, 1000);
root.getChildren().add( canvas );
GraphicsContext gc = canvas.getGraphicsContext2D();
new ResourceLoaderJFX();
System.out.println("Running test");
Random ran = new Random();
ClassLoader classLoader = getClass().getClassLoader();
URL url = classLoader.getResource("sky.png");
Image image = new Image(url.toString());
long t1 = System.nanoTime();
for (int j=0; j<1000000; j++ ) {
int x = ran.nextInt(1000);
int y = ran.nextInt(1000);
gc.drawImage(image, x, y);
}
System.out.println("\n");
long t2 = System.nanoTime()-t1;
System.out.println("Took " + (t2/1000000000.0) + " secs");
System.out.println("Done");
theStage.show();
}
}
Prism pipeline init order: d3d sw
Using native-based Pisces rasterizer
Using dirty region optimizations
Not using texture mask for primitives
Not forcing power of 2 sizes for textures
Using hardware CLAMP_TO_ZERO mode
Opting in for HiDPI pixel scaling
Prism pipeline name = com.sun.prism.d3d.D3DPipeline
Loading D3D native library ...
D3DPipelineManager: Created D3D9Ex device
succeeded.
Direct3D initialization succeeded
(X) Got class = class com.sun.prism.d3d.D3DPipeline
Initialized prism pipeline: com.sun.prism.d3d.D3DPipeline
OS Information:
Maximum supported texture size: 8192
Windows version 10.0 build 14393
Maximum texture size clamped to 4096
D3D Driver Information:
Intel(R) Iris(TM) Graphics 540
\\.\DISPLAY2
Driver igdumdim64.dll, version 20.19.15.4463
Pixel Shader version 3.0
Device : ven_8086, dev_1926, subsys_00151414
Max Multisamples supported: 4
vsync: true vpipe: true
Running test
Took 8.230974466 secs
In Swing:
public class SwingTest extends JPanel {
public void init() {
setVisible(true);
}
public void runTest() {
System.out.println("Running test");
BufferedImage bufferedImage=null;
try {
bufferedImage = ImageIO.read(new File("C:\\Users\\resources\\png\\sky.png"));
} catch (IOException e) {
e.printStackTrace();
}
long t1 = System.nanoTime();
Random ran = new Random();
for (int j=0; j<(1000000); j++ ) {
int x = ran.nextInt(1000);
int y = ran.nextInt(1000);
this.getGraphics().drawImage(bufferedImage, x, y, null);
}
long t2 = System.nanoTime()-t1;
System.out.println("Took " + (t2/1000000000.0) + " secs");
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
SwingTest view= new SwingTest();
view.init();
f.add(worldViewPanel);
f.pack();
f.setSize(new Dimension(1000,1000));
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
view.runTest();
}
});
}
}
Connected to the target VM, address: '127.0.0.1:53764', transport: 'socket'
Took 2.586923483 secs
Interestingly, for lower numbers
JAVAFX
Took 0.02173174 secs # 10,000 images, second run took 0.018200605 secs
SWING
Took 0.138639497 secs # 10,000 images, second run took 0.13744251 secs
I think what you are experiencing is the difference between the retained mode of JavaFX and the immediate mode of Swing. Swing is literally taking those images and blitting them to the screen then moving on to the next blitting position. When it needs to draw them again, it starts from scratch. As it happens, this is very fast.
JavaFX is creating a distinct Object every time you invoke drawImage (see GraphicsContext.writeImage() ) and then retaining those Objects in an internal buffer it grabs from Canvas. On top of that it's creating six doubles and putting them into exactly the same buffer (see GraphicsContext.updateTransform() ).
The sell of JavaFX is its retained mode. It will allow you to manipulate its Nodes on screen as if they were in a 2-D (in fact 3-D) coordinate system and it will do this "for free". This is very powerful if you want to position objects in a 2-D scene and move them around, as game programmers are well aware.
The price you pay for this is the scene is much heavier than the corresponding scene in Swing and the memory cost of Images is cumulative in the JavaFX application. In your JavaFX app, you have a Scene to which you're adding a Canvas and it's creating a scene graph. Swing is not doing this.
If you run your program in a profiler, you can see exactly where the time is being spent and if you run your program in a debugger you can see how large the Canvas buffer is becoming.
You are comparing apples with beens here. In Swing the image is actually rendered when you call drawImage. In JavaFX this command to draw an image is just added to a command buffer which will be executed later.
I am very new to JAVA, and am attempting to write a pharmacokinetics calculation program for my students to use.
The program reads a database of plasma concentration vs time values and calculates a number of pharmacokinetic parameters and outputs these into a number of frames, as well as drawing linear and exponential curves which are also output into frames.
I wrote the program initially as a console based, to make sure everything worked as required, before moving to GUI. Now comes the difficult steps of GUI building. I realize my console code will change considerably, I'm looking forward to the learning curve.
I am now attempting to output results via a separate 'GUI' class, and so pass frames between classes before output. I have tried to pass a frame from my 'GUI' class to my other class, without success.
In my attempt to learn GUI, and the class structure of JAVA, I have two classes.
A ProjectChart Class, that contains all the calculation and graphing methods.
A ProjectChartGUI class, which will eventually be used to output all data.
ProjectChartGUI currently defines a number of InternalFrames. I have been trying to pass one such internalframe to a function in ProjectChart, OutputInputData(). This constructs a table of data, read by a separate function, and will output this table into a frame for user validation.
I am trying to pass a JInternalFrame from ProjectChartGUI to OutputInputData().
Initially I passed the JInternalFrame using a .getFrame() function using OutputInputData (JInternalFrame frame).
I also tried to pass a ProjectchartGUI object. OutputInputData (ProjectChartGUI tProjectChartGUI). This also did not work.
In both cases, the program runs, all calculations are performed successfully, however the frame which opens via ProjectChartGUI is empty.
Here is my main:
public static void main(String[] args) throws IOException, InterruptedException, URISyntaxException{
ProjectChartGUI tGUI = new ProjectChartGUI();
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
ActionPharmacokinetics(tGUI);
tGUI.setVisible(true);
}
});
}
Here is a snippet of the function ActionPharmacokinetics():
public static void ActionPharmacokinetics(ProjectChartGUI tGUI){
DetermineUserInput();
if(CHECK == IVBOLUS_TRUE)
{
// Read database to access main information
Access_IVBOLUS_DatabaseList();
// Read database again to allow user to select desired entry for calculation
Access_IVBOLUS_Database();
// Output CP and T values to a new window for confirmation
OutputInputData(tGUI);
//Diplay Linear chart of Data
Display_IVBOLUS_LinearGraph();
// Display exponential chart of Data
Display_IVBOLUS_ExponentialGraph();
// Calculate pharmacokinetic parameters
Calculate_IVBOLUS_PharmacokineticParameters();
// Output pharmacokinetic paramaters
OutputPharmacokineticParamaters();
}}
Here is outputInputData(): Cp, lnCp and T are declared variables in ProjectChart. Numberofpoints is the numberofdata points read from the database.
static public void OutputInputData(ProjectChartGUI tGUI){
//create table objects
String[] ColumnNames = new String[]{"Data Point", "CP"+Units_CP,"ln CP","Time"+Units_T};
double SetDecimalPoint = 0.0;
Object[][] Data = new Object[NumberofPoints][4];
for (int k=0;k<NumberofPoints;k++)
{
Data[k][0] = k+1;//index
Data[k][1] = Cp[k];//plasma
SetDecimalPoint = lnCp[k];//log plasma
BigDecimal bd = BigDecimal.valueOf(SetDecimalPoint);
bd = bd.setScale(2, BigDecimal.ROUND_FLOOR); // rounds ln cp to 2 decimal places for output
Data[k][2] = bd;
Data[k][3] = T[k];//time
}
//create table
JTable jtable = new JTable(Data, ColumnNames);
//Create frame
//JFrame jFrame = new JFrame("Input Data");
//jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//add table to frame
tGUI.getframe().add(new JScrollPane(jtable));
tGUI.getframe().pack(); //auto size
tGUI.getframe().setVisible(true);
}
.getframe() returns an internal frame from ProjectChartGUI
public javax.swing.JInternalFrame getframe(){
return jInternalFrame1;
}
I used Netbeans GUI builder to define a frame, with an internal frame
private javax.swing.JInternalFrame jInternalFrame1;
All other code in ProjectChartGUI was autogenerated by Netbeans.
Does anyone have any suggestions on where I may be going wrong?
I'm using cocos2d-x v3.0 and in some test project I'm doing some custom drawing by overriding Node's draw method, but in the DrawPrimitives example provided they do something like this:
void DrawPrimitivesTest::draw()
{
_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(DrawPrimitivesTest::onDraw, this);
Director::getInstance()->getRenderer()->addCommand(&_customCommand);
}
void DrawPrimitivesTest::onDraw()
{
// drawing code here, why?
}
From reading the header and source files it seems like this may be some way of sending render commands straight to the renderer, is that correct?
Should I be using this method to do custom drawing? What's the difference between draw an onDraw?
EDIT:
As #Pedro Soares mentioned, since Cocos2D-X 3.0 you can't override draw() anymore. you have to use draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated) instead.
There is sample on cocos2d-x RC0 package that shows how to use the DrawPrimitives on top of other layers.
On your Layer .h add the following:
private:
void onDrawPrimitives(const kmMat4 &transform, bool transformUpdated);
CustomCommand _customCommand;
Now in the cpp of the Layer, override the layer draw method and include the onDrawPrimitives method:
void MyLayer::onDrawPrimitives(const kmMat4 &transform, bool transformUpdated)
{
kmGLPushMatrix();
kmGLLoadMatrix(&transform);
//add your primitive drawing code here
DrawPrimitives::drawLine(ccp(0,0), ccp(100, 100));
}
void MyLayer::draw(Renderer *renderer, const kmMat4& transform, bool transformUpdated)
{
_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(MyLayer::onDrawPrimitives, this, transform, transformUpdated);
renderer->addCommand(&_customCommand);
}
In future, cocos2d-x 3.x renderer will be multithreaded with command pool.
draw method called by visit method, to create new command. When command is performed by command pool, onDraw is called. At this moment, commands are performed in single thread, but in overloaded onDraw method you should assume, that it will be called in another thread to simplify future migration.
I use draw method for debugDraw Like this It may be helpful
void HelloWorld::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
Layer::draw(renderer, transform, flags);
Director* director = Director::getInstance();
GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION );
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
world->DrawDebugData();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
The draw() expression should be the same as the base class function.
The draw method of Node for cocos 3.3rc is:
virtual void draw(Renderer *renderer, const Mat4& transform, uint32_t flags);
I'm looking to create a Live Tile which is similar to the Iconic tile but allows me to use a custom Count value (i.e. non-integer string).
The closest I've come is that I must create the contents using a bitmap and then use that image as the tile. Unfortunately I don't know how this is commonly done.
I'm looking to create tiles similar to the one that's described in this question (though this question is orthogonal to my issue): Custom live tile rendering issue on Windows Phone (7/8)
In short,
Is WriteableBitmap the best way of creating Live Tile layouts?
Is there a mechanism by which I can convert XAML into the Live Tile?
An example of the layout I'd like to achieve is somewhat displayed in the Skype Live Tile seen here.
As far as I can tell, creating a custom bitmap is the way to go. I found this answer along with this article to be very helpful when I was doing my live tiles.
If you don't mind purchasing third-party controls you can check out Telerik's LiveTileHelper control (if you're a member of Nokia's developer program you already have access to this).
For my first app I opted to roll my own solution based on the first two links. I have a base class that handles the work of taking a FrameworkElement (each derived class is responsible for generating the FrameworkElement that contains the information to render) and creating the corresponding WritableBitmap instance which I then save as a .PNG using the ToolStack C# PNG Writer Library.
As an example, here's my code to generate the control that represents a small pinned secondary tile in one of my apps:
/// <summary>
/// Returns the fully populated and initialized control that displays
/// the information that should be included in the tile image.
/// </summary>
/// <remarks>
/// We manually create the control in code instead of using a user control
/// to avoid having to use the XAML parser when we do this work in our
/// background agent.
/// </remarks>
/// <returns>
/// The fully populated and initialized control that displays
/// the information that should be included in the tile image.
/// </returns>
protected override FrameworkElement GetPopulatedTileImageControl()
{
var layoutRoot = new Grid()
{
Background = new System.Windows.Media.SolidColorBrush( System.Windows.Media.Color.FromArgb( 0, 0, 0, 0 ) ),
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
Height = TileSize.Height,
Width = TileSize.Width,
Margin = new Thickness( 0, 12, 0, 0 )
};
var stopName = new TextBlock()
{
Text = Stop.Description,
TextTrimming = TextTrimming.WordEllipsis,
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness( 7, 0, 7, 12 ),
MaxHeight = 135,
Width = TileSize.Width - 14,
VerticalAlignment = VerticalAlignment.Bottom,
HorizontalAlignment = HorizontalAlignment.Stretch,
FontFamily = (System.Windows.Media.FontFamily) Application.Current.Resources[ "PhoneFontFamilySemiBold" ],
FontSize = (double) Application.Current.Resources[ "PhoneFontSizeMediumLarge" ],
Style = (Style) Application.Current.Resources[ "PhoneTextNormalStyle" ]
};
Grid.SetColumn( stopName, 0 );
Grid.SetRow( stopName, 0 );
layoutRoot.Children.Add( stopName );
return layoutRoot;
}
This is a super-simple control with just a TextBlock, but you can easily expand on this. Note that I don't use a UserControl here as I also run this code in a background agent where you have significant memory constraints.
Once I have a control I generate a WritableBitmap like this:
/// <summary>
/// Renders the tile image to a <see cref="WritableBitmap"/> instance.
/// </summary>
/// <returns>
/// A <see cref="WritableBitmap"/> instance that contains the rendered
/// tile image.
/// </returns>
private WriteableBitmap RenderTileImage()
{
var tileControl = GetPopulatedTileImageControl();
var controlSize = new Size( TileSize.Width, TileSize.Height );
var tileImage = new WriteableBitmap( (int) TileSize.Width, (int) TileSize.Height );
// The control we're rendering must never be smaller than the tile
// we're generating.
tileControl.MinHeight = TileSize.Height;
tileControl.MinWidth = TileSize.Width;
// Force layout to take place.
tileControl.UpdateLayout();
tileControl.Measure( TileSize );
tileControl.Arrange( new Rect( new Point( 0, 0 ), TileSize ) );
tileControl.UpdateLayout();
tileImage.Render( tileControl, null );
tileImage.Invalidate();
tileControl = null;
GC.Collect( 2, GCCollectionMode.Forced, true );
// Adjust the rendered bitmap to handle the alpha channel better.
CompensateForRender( tileImage );
return tileImage;
}
Again, I'm making explicit calls to GC.Collect to help keep my memory consumption under control when running this code as part of my background agent. The CompensateForRender method is based on the code in the linked article.
Hope this helps.