Direct2D / GDI+ and slow Windows forms drawing - What can be done? - c#

I'm working a lot with Visual Studio 2008, .NET C# 2.0-3.5 and Windows Forms and I have noticed, like many before me, that GDI+ is extremely slow in drawing Controls. Note that I do not deal with images (JPG, GIF etc) very much. Images are only as icons in certain places. This is actually Controls/Forms/etc that are slow to draw.
The issue is that you can see Controls being drawn and it can take several seconds for a seemingly easy set of Controls to be drawn. Ie, its lagging and horrible.
I have made tests where I just put a number of Labels (40-50) on a form, hitting F5 to run and have to wait for them to be drawn. Again, lag and not a very nice experience.
So, then there is WPF that might address this problem, but I/we are not ready to move to WPF. So I'm looking around for workarounds or fixes and I stumbled upon Direct2D, and when reading on that some other libraries.
Put Im a tad puzzled and thus these questions:
1)
First of, what I want is a fairly neat and simple way to just replace GDI+ with something faster and hardware accelerated approach. Is it possible to do that without going over to WPF and without having to rewrite all my Windows Forms code?
Whenever I read anything on Direct2D I see long blocks of usually horrible C++ code, telling me on how to manually write code to for drawing. I do not want that.
2)
While reading on the net, I stumbled upon SlimDX, but I cannot figure out how to use it (and I admit, I havent tried very much as of writing). Lets say I already have a GUI-application (Windows Forms, standard C# code) - can I somehow use SlimDX (or something like it) to just "replace" GDI+ without too much rewriting?
My problem is I cannot find any examples or such telling me if it is possible to use SlimDX, Direct2D or other similiar things in my already-created Windows Forms software, and if it is possible - how to do it.
Hope Im not too fuzzy =)
==EDIT== 2010-09-22
I have made some tests in my real app, and isolated one of the slow things to this:
When I add text to some Labels in a UserControl, the Controls resize themselves to fit the text. For example, the containing GroupControl adapts a bit to the size of the text that was just added to the .Text-property of the Labels.
There are about 10 Label controls. The first time the labels are updated, and thus sizes are changed, the whole process takes about 500 ms. The second time the labels are updated, and no size changes, it takes about 0 ms.
==EDIT 2== 2010-09-22
Found one of the slow-downs. Then adding a String to the Text-property it is slow if the text that is being added differs in string length from the text that was there before the update.
Im using DevExpress libraries, and the LabelControls can be set to AutoSizeMode. If I set that to "None" then the lag will go away when adding text that differs in length from the previous text. I guess this problem will be the same for the normal Label-control as it also has a AutoSize = true/false setting.
However, its a "workaround" but still proves my point - its really slow when resizing which is pretty lame.

Many of the posters above come with good points. I've created a 3D CAD application in GDI+ myself, and found it plenty fast enough if it's implemented correctly. Using Controls, however, immediately strikes me as a very awkward way to do things. A Control is a fairly big object and there are numerous reasons to make your own in this case.
I'd advise you to look into a retained mode drawing system. It's easy to implement and would cover your situation in most cases. You'd have to create the drawing logic yourself, but that's just fun and would give you more flexibility :)

On your first question, I had to use GDI to do some image processing stuff which was taking ages under GDI+. This was 4-5 years ago and working with GDI using managed C# was a pain - not sure how much it has changed now. There are many good and fast functions such as BitBlt which are very fast in drawing but you need to be very careful with releasing resources (handles) and memory. I also had another issue and that was saving the result as JPEG and it is non-existent in GDI so I had to use CxImage to read the HBitmap and then save it.
All in all, GDI is very fast and robust. If there better abstractions in DirectX, probably you are better off using them.

I'm looking at some of the same issues. I am writing an application that needs to be able to render 2d graphics very efficiently since some users could have 10 - 50 windows open simultaneously. One thing to consider that no one else talked about here is the fact that direct2d can only be used on computers with Vista with service pack 2 and up. Also, according to this link:
http://www.tomshardware.com/news/msft-windows-xp-windows-7-market-share-win7,13876.html
38.5% of all Windows users were still using XP as of Nov, 2011. So, if selling the app to a significant amount of users still running XP is a concern (or your marketbase is to 3rd world countries that are mainly using XP), then you should either go with:
Direct2d for newer operating systems and GDI+ for XP systems.
XNA - which is compatible with XP and can also be used with newer operating systems. See this link: http://msdn.microsoft.com/en-us/library/bb203925.aspx
SlimDX - mentioned in the first answer. Supports XP as well as newer operating systems. See:
http://slimdx.org/ and http://slimdx.org/features.php
OpenTK if you care about compatibility between Windows, Linux, Max, etc.
You should also be aware that there was a bug with GDI+ that caused it to suffer very poor performance when it was initially released. See the following link why some developers claim that Microsoft broke the gui for Windows7 for apps using GDI+:
http://www.windows7taskforce.com/view/3607
or do a web search from your favorite search engine with this string: "gdi+ bug slow on windows 7".

You could try managed directx, but they no longer support it (moved on to XNA). Honestly, unless you've got a shitty computer or a ton of controls, I don't know why it'd be lagging so bad. If you're doing some cpu intensive stuff on your main thread, move it to a separate thread. That's the only other reason I can think of that'd cause that kind of lag.

We use SlimDX in our C# app.... But we're actually doing 3D. We wrote our own 2D lib to be able to do simple 2D drawing. SlimDX is just a lightweight wrapper around DirectX. So you'll get all of the pro's and cons of DirectX. Like that it's your problem to emulate the videocard if its not present.
If you want something for drawing to offscreen bitmaps, I'd go for WPF, as it is well integrated with C#, works mostly everywhere, and accellerated when there's hardware available. You can copy the output to a bitmap and use that in regular GDI/Winforms. But it will only be faster than GDI+ if you do fairly complex stuff (lots of filters, mixing textures etc...).
Edit:
In response to the comments, I built a little sample form. There is a seconds long wait when switching the first time, but after that it's responsive. Slower than I'd expect, but by all means usable. Would like Ted to comment if this is about the performance he is seeing in his app.
public class Form1 : Form
{
public Form1()
{
InitializeComponent();
// *** EDIT ***
this.tabPage1.SuspendLayout();
this.tabPage2.SuspendLayout();
this.tabControl1.SuspendLayout();
this.SuspendLayout();
FillTab(tabPage1, Color.White);
FillTab(tabPage2, Color.Yellow);
// *** EDIT ***
this.tabPage1.ResumeLayout(false);
this.tabPage2.ResumeLayout(false);
this.tabControl1.ResumeLayout(false);
this.ResumeLayout(false);
}
private static void FillTab(TabPage tabPage, Color color)
{
for (int i = 0; i < 200; ++ i)
{
int left = (i % 5) * 320;
int topOffset = (i / 5) * 23;
tabPage.Controls.Add(new Label { Left = left, Top = topOffset, Width = 100, Text = "Label" });
tabPage.Controls.Add(new TextBox() { BackColor = color, Left = left + 100, Top = topOffset, Width = 100, Text = tabPage.Text });
tabPage.Controls.Add(new ComboBox { BackColor = color, Left = left + 200, Top = topOffset, Width = 100 });
}
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.tabControl1.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabControl1.Location = new System.Drawing.Point(0, 0);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(292, 266);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Location = new System.Drawing.Point(4, 22);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(284, 240);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "tabPage1";
this.tabPage1.UseVisualStyleBackColor = true;
//
// tabPage2
//
this.tabPage2.Location = new System.Drawing.Point(4, 22);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(245, 217);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "tabPage2";
this.tabPage2.UseVisualStyleBackColor = true;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.tabControl1);
this.Name = "Form1";
this.Text = "Form1";
this.tabControl1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.TabPage tabPage2;
}

Related

How to read text from 'simple' screenshot fast and effectively?

I'm working on a small personal application that should read some text (2 sentences at most) from a really simple Android screenshot. The text is always the same size, same font, and in approx. the same location. The background is very plain, usually a few shades of 1 color (think like bright orange fading into a little darker orange). I'm trying to figure out what would be the best way (and most importantly, the fastest way) to do this.
My first attempt involved the IronOcr C# library, and to be fair, it worked quite well! But I've noticed a few issues with it:
It's not 100% accurate
Despite having a community/trial version, it sometimes throws exceptions telling you to get a license
It takes ~400ms to read a ~600x300 pixel image, which in the case of my simple image, I consider to be rather long
As strange as it sounds, I have a feeling that libraries like IronOcr and Tesseract may just be too advanced for my needs. To improve speeds I have even written a piece of code to "treshold" my image first, making it completely black and white.
My current IronOcr settings look like this:
ImageReader = new AdvancedOcr()
{
CleanBackgroundNoise = false,
EnhanceContrast = false,
EnhanceResolution = false,
Strategy = AdvancedOcr.OcrStrategy.Fast,
ColorSpace = AdvancedOcr.OcrColorSpace.GrayScale,
DetectWhiteTextOnDarkBackgrounds = true,
InputImageType = AdvancedOcr.InputTypes.Snippet,
RotateAndStraighten = false,
ReadBarCodes = false,
ColorDepth = 1
};
And I could totally live with the results I've been getting using IronOcr, but the licensing exceptions ruin it. I also don't have $399 USD to spend on a private hobby project that won't even leave my own PC :(
But my main goal with this question is to find a better, faster or more efficient way to do this. It doesn't necessarily have to be an existing library, I'd be more than willing to make my own kind of letter-detection code that would work (only?) for screenshots like mine if someone can point me in the right direction.
I have researched about this topic and the best solution which I could find is Azure cognitive services. You can use Computer vision API to read text from an image. Here is the complete document.
How fast does it have to be?
If you are using C# I recommend the Google Cloud Vision API. You pay per request but the first 1000 per month are free (check pricing here). However, it does require a web request but I find it to be very quick
using Google.Cloud.Vision.V1;
using System;
namespace GoogleCloudSamples
{
public class QuickStart
{
public static void Main(string[] args)
{
// Instantiates a client
var client = ImageAnnotatorClient.Create();
// Load the image file into memory
var image = Image.FromFile("wakeupcat.jpg");
// Performs label detection on the image file
var response = client.DetectText(image);
foreach (var annotation in response)
{
if (annotation.Description != null)
Console.WriteLine(annotation.Description);
}
}
}
}
I find it works well for pictures and scanned documents so it should work perfectly for your situation. The SDK is also available in other languages too like Java, Python, and Node

Fellow Oak DICOM - changing image window level

I am not an experienced programmer, just need to add a DICOM viewer to my VS2010 project. I can display the image in Windows Forms, however can't figure out how to change the window center and width. Here is my script:
DicomImage image = new DicomImage(_filename);
int maxV = image.NumberOfFrames;
sbSlice.Maximum = maxV - 1;
image.WindowCenter = 7.0;
double wc = image.WindowCenter;
double ww = image.WindowWidth;
Image result = image.RenderImage(0);
DisplayImage(result);
It did not work. I don't know if this is the right approach.
The DicomImage class was not created with the intention of it being used to implement an image viewer. It was created to render preview images in the DICOM Dump utility and to test the image compression/decompression codecs. Maybe it was a mistake to include it in the library at all?
It is difficult for me to find fault in the code as being buggy when it is being used for something far beyond its intended functionality.
That said, I have taken some time to modify the code so that the WindowCenter/WindowWidth properties apply to the rendered image. You can find these modifications in the Git repo.
var img = new DicomImage(fileName);
img.WindowCenter = 2048.0;
img.WindowWidth = 4096.0;
DisplayImage(img.RenderImage(0));
I looked at the code and it looked extremely buggy. https://github.com/rcd/fo-dicom/blob/master/DICOM/Imaging/DicomImage.cs
In the current buggy implementation setting the WindowCenter or WindowWidth properties has no effect unless Dataset.Get(DicomTag.PhotometricInterpretation) is either Monochrome1 or Monochrome2 during Load(). This is already ridiculous, but it still cannot be used because the _renderOptions variable is only set in a single place and is immediately used for the _pipeline creation (not giving you chance to change it using the WindowCenter property). Your only chance is the grayscale _renderOptions initialization: _renderOptions = GrayscaleRenderOptions.FromDataset(Dataset);.
The current solution: Your dataset should have
DicomTag.WindowCenter set appropriately
DicomTag.WindowWidth != 0.0
DicomTag.PhotometricInterpretation == Monochrome1 or Monochrome2
The following code accomplishes that:
DicomDataset dataset = DicomFile.Open(fileName).Dataset;
//dataset.Set(DicomTag.WindowWidth, 200.0); //the WindowWidth must be non-zero
dataset.Add(DicomTag.WindowCenter, "100.0");
//dataset.Add(DicomTag.PhotometricInterpretation, "MONOCHROME1"); //ValueRepresentations tag is broken
dataset.Add(new DicomCodeString(DicomTag.PhotometricInterpretation, "MONOCHROME1"));
DicomImage image = new DicomImage(dataset);
image.RenderImage();
The best solution: Wait while this buggy library is fixed.

How to get selected text from an active application, without using a clipboard

What am I doing:
My main intent is to enable user friendly text to speech for personal use on Win 7. Approach should work in Google Chrome, VS and Eclipse.
Code example:
Following code creates global keyboard hook for ctrl + alt + space, called hookEvent. If event fires, it starts/stops speaking clipboard contents ( that can be updated with ctrl + c ).
/// <summary>
/// KeyboardHook from: http://www.liensberger.it/web/blog/?p=207
/// </summary>
private readonly KeyboardHook hook = new KeyboardHook();
private readonly SpeechSynthesizer speaker = //
new SpeechSynthesizer { Rate = 3, Volume = 100 };
private void doSpeaking(string text)
{
// starts / stops speaking, while not blocking UI
if (speaker.State != SynthesizerState.Speaking)
speaker.SpeakAsync(text);
else
speaker.SpeakAsyncCancelAll();
}
private void hookEvent(object sender, KeyPressedEventArgs e)
{
this.doSpeaking(Convert.ToString(Clipboard.GetText()));
}
public Form1()
{
InitializeComponent();
hook.KeyPressed += new EventHandler<KeyPressedEventArgs>(hookEvent);
hook.RegisterHotKey(ModifierKeysx.Control|ModifierKeysx.Alt, Keys.Space);
}
Question:
I would prefer not using the clipboard. Or at least, restoring the value after, something like:
[MethodImpl(MethodImplOptions.Synchronized)]
private string getSelectedTextHACK()
{
object restorePoint = Clipboard.GetData(DataFormats.UnicodeText);
SendKeys.SendWait("^c");
string result = Convert.ToString(Clipboard.GetText());
Clipboard.SetData(DataFormats.UnicodeText, restorePoint);
return result;
}
What are my options?
Edit:
To my surprise, I found that my clipboard reader is the best way to go. I created a notification area app, that responds to left click (speaking clipboard) and right click (menu opens up). In menu the user can chance speed, speak or create a audio file.
MS provide accessibility tools that do cover what you're trying to do. If you take a look at documents about screen scraping. In short, every component is accessible in some manner, if you use some of the windows debugging tools you can get to see the component names/structures within. You can then use that, however, its complicated as most times you would need to be very specific for each application you intend to scrape from.
If you manage to scrape you dont need to use the clipboard, as you can access the text property of the apps direct. Its not something I've had to do, hence, Ive no code to offer off the top of my head, but the term "screen scraping" should point you in the right direction.
If to expand a little on what Bugfinder said, Microsoft provider a UI Automation Framework to solve problems like the one you mentioned:
In particular you can use the TextSelectionChangedEvent of TextPattern:
The problem with this solution is that it only works on supported operating systems and applications - and not all support this.
Your clipboard solution is acceptable for applications that do not provide a good automation interface.
But for many applications the UI Automation Framework will work well and will provide you with a far better solution.

Alternatives to System.Drawing for use with ASP.NET?

After several days of tracking down bizarre GDI+ errors, I've stumbled across this little gem on MSDN:
Classes within the System.Drawing namespace are not supported for use within a Windows or ASP.NET service. Attempting to use these classes from within one of these application types may produce unexpected problems, such as diminished service performance and run-time exceptions.
I don't know whether "ASP.NET service" means "web application" in this context, but "diminished service performance" certainly seems to cover the random assortment of "A generic error occurred in GDI+" and "Out of memory" errors that my app is throwing - intermittent, non-reproducible errors reading and writing JPEG images that - in many cases - were actually created by System.Drawing.Imaging in the first place.
So - if GDI+ can't read and write JPEG files reliably in a Web app, what should I be using instead?
I want users to be able to upload images (JPEG required, other formats nice-to-have), resample them reliably, and display useful error messages if anything goes wrong. Any ideas? Are the System.Media namespaces from WPF worth considering?
EDIT: Yeah, I know GDI+ works "most of the time". That's not good enough, because when it fails, it does so in a way that's impossible to isolate or recover from gracefully. I am not interested in examples of GDI+ code that works for you: I am looking for alternative libraries to use for image processing.
There is an excellent blog post including C# code about using the ImageMagick graphics library through Interop over at TopTen Software Blog. This post deals specifically with running ASP.net on linux under mono; however, the C# code should be perfectly copy-paste-able, the only thing you'll need to change is the Interop attributes if you are running under windows referencing a window binary (DLL).
ImageMagick® is a software suite to create, edit, compose, or convert
bitmap images. It can read and write images in a variety of formats
(over 100) including DPX, EXR, GIF, JPEG, JPEG-2000, PDF, PhotoCD,
PNG, Postscript, SVG, and TIFF. Use ImageMagick to resize, flip,
mirror, rotate, distort, shear and transform images, adjust image
colors, apply various special effects, or draw text, lines, polygons,
ellipses and Bézier curves.
There is also an ImageMagick .Net development project on codeplex that wraps up everything for you. But it doesn't show active development since 2009, so it may be lagging behind the current ImageMagick library version. For a small trivial resizing routine, I'd probably stick with the interop. You just need to watch your implementation carefully for your own memory leak or unreleased resources (the library itself is well tested and vetted by the community).
The library is free and open source. The Apache 2 license appears to be compatible with both personal and commercial purposes. See ImageMagick License Page.
The library is totally cross platform and implements many powerful image handling and transformation routines that are not found in GDI+ (or not implemented under mono) and has a good reputation as an alternative for ASP.net image processing.
Update: Looks like there is an updated version of a .NET wrapper here: http://magick.codeplex.com/
Yes, use the WPF System.Windows.Media classes. Being fully managed they don't suffer the same problems as the GDI stuff.
Here's an excerpt from some MVC code I use to render gradients, to give you an idea how to get from a WPF Visual to a PNG:
using System;
using System.IO;
using System.Web.Mvc;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace MyMvcWebApp.Controllers
{
public class ImageGenController : Controller
{
// GET: ~/ImageGen/Gradient?color1=red&color2=pink
[OutputCache(CacheProfile = "Image")]
public ActionResult Gradient(Color color1, Color color2, int width = 1, int height = 30, double angle = 90)
{
var visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
Brush brush = new LinearGradientBrush(color1, color2, angle);
dc.DrawRectangle(brush, null, new Rect(0, 0, width, height));
}
return new FileStreamResult(renderPng(visual, width, height), "image/png");
}
static Stream renderPng(Visual visual, int width, int height)
{
var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default);
rtb.Render(visual);
var frame = BitmapFrame.Create(rtb);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(frame);
var stream = new MemoryStream();
encoder.Save(stream);
stream.Position = 0;
return stream;
}
}
}
You can find a very good article from a Microsoft Employee here: Resizing images from the server using WPF/WIC instead of GDI+ that proposes to use WPF instead of GDI+. It's more about thumbnailing but it's overall the same issues.
Anyway, at the end it states this:
I contacted the WPF team to have the final word on whether this is
supported. Unfortunately, it's not, and the documentation is being
updated accordingly. I apologize about any confusion this may have
caused. We're looking at ways to make that story more acceptable in
the future.
So WPF is also unsupported in web apps and still is I believe :-S
ImageSharp
ImageSharp is an open-source cross-platform 2D graphics library. It's written in C# on top of the new .NET Standard, with no dependency on any OS-specific API.
It's currently still in pre-release on MyGet (you'll have to add the package source in the VS options or a NuGet.config file), but we are already using it with some very positive results.
Most of the issues I have read about pertain to resources not being disposed properly.
I have used variants of this code time and time again with no issues from web applications:
public void GenerateThumbNail(HttpPostedFile fil, string sPhysicalPath,
string sOrgFileName,string sThumbNailFileName,
System.Drawing.Imaging.ImageFormat oFormat, int rez)
{
try
{
System.Drawing.Image oImg = System.Drawing.Image.FromStream(fil.InputStream);
decimal pixtosubstract = 0;
decimal percentage;
//default
Size ThumbNailSizeToUse = new Size();
if (ThumbNailSize.Width < oImg.Size.Width || ThumbNailSize.Height < oImg.Size.Height)
{
if (oImg.Size.Width > oImg.Size.Height)
{
percentage = (((decimal)oImg.Size.Width - (decimal)ThumbNailSize.Width) / (decimal)oImg.Size.Width);
pixtosubstract = percentage * oImg.Size.Height;
ThumbNailSizeToUse.Width = ThumbNailSize.Width;
ThumbNailSizeToUse.Height = oImg.Size.Height - (int)pixtosubstract;
}
else
{
percentage = (((decimal)oImg.Size.Height - (decimal)ThumbNailSize.Height) / (decimal)oImg.Size.Height);
pixtosubstract = percentage * (decimal)oImg.Size.Width;
ThumbNailSizeToUse.Height = ThumbNailSize.Height;
ThumbNailSizeToUse.Width = oImg.Size.Width - (int)pixtosubstract;
}
}
else
{
ThumbNailSizeToUse.Width = oImg.Size.Width;
ThumbNailSizeToUse.Height = oImg.Size.Height;
}
Bitmap bmp = new Bitmap(ThumbNailSizeToUse.Width, ThumbNailSizeToUse.Height);
bmp.SetResolution(rez, rez);
System.Drawing.Image oThumbNail = bmp;
bmp = null;
Graphics oGraphic = Graphics.FromImage(oThumbNail);
oGraphic.CompositingQuality = CompositingQuality.HighQuality;
oGraphic.SmoothingMode = SmoothingMode.HighQuality;
oGraphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle oRectangle = new Rectangle(0, 0, ThumbNailSizeToUse.Width, ThumbNailSizeToUse.Height);
oGraphic.DrawImage(oImg, oRectangle);
oThumbNail.Save(sPhysicalPath + sThumbNailFileName, oFormat);
oImg.Dispose();
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
}
You may have a look at http://gd-sharp.sourceforge.net/ which is a wrapper for the GD library. I haven't tested it but it seems promising.
I've had good behavior from the Cairo library (http://www.cairographics.org) in an ASP.Net webserver environment. I actually moved to cairo from WPF due to WPF's poor memory usage model for web-based stuff.
WPF actually tends to run your worker process out of memory. None of the WPF objects implement IDisposable, and many of them reference unmanaged memory that's only freed via a finalizer. Heavy use of WPF (especially if your server is significantly CPU-taxed) will eventually run you out of memory because your finalizer queue gets saturated. When I was profiling my app, for instance, the finalization queue had upwards of 50,000 objects on it, many of them holding references to unmanaged memory. Cairo has behaved much better for me, and its memory usage pattern has been much more predictable than WPF's.
If you're interested in using cairo, grab the libs from GTK+'s website. They have an x86 as well as an x64 set of binaries.
The only downside is that cairo can't read/write JPG natively; however, you could easily adapt WPF's stuff for reading/writing JPG and do the resampling/scaling/drawing/whatever else using Cairo.
Aspose.Drawing is a drop-in replacement for System.Drawing that is fully managed and can be safely used in web applications. (I'm one of the developers.)

Is there a way to take a screenshot of the user's Windows desktop?

I want to provide the user with a scaled-down screenshot of their desktop in my application.
Is there a way to take a screenshot of the current user's Windows desktop?
I'm writing in C#, but if there's a better solution in another language, I'm open to it.
To clarify, I need a screenshot of the Windows Desktop - that's the wallpaper and icons only; no applications or anything that's got focus.
You're looking for Graphics.CopyFromScreen. Create a new Bitmap of the right size and pass the Bitmap's Graphics object screen coordinates of the region to copy.
There's also an article describing how to programmatically take snapshots.
Response to edit: I misunderstood what you meant by "desktop". If you want to take a picture of the desktop, you'll have to:
Minimize all windows using the Win32API (send a MIN_ALL message) first
Take the snapshot
Then undo the minimize all (send a MIN_ALL_UNDO message).
A better way to do this would be not to disturb the other windows, but to copy the image directly from the desktop window. GetDesktopWindow in User32 will return a handle to the desktop. Once you have the window handle, get it's device context and copy the image to a new Bitmap.
There's an excellent example on CodeProject of how to copy the image from it. Look for the sample code about getting and creating the device context in the "Capturing the window content" section.
I get the impression that you are shooting for taking a picture of the actual desktop (with wallpaper and icons), and nothing else.
1) Call ToggleDesktop() in Shell32 using COM
2) Use Graphics.CopyFromScreen to copy the current desktop area
3) Call ToggleDesktop() to restore previous desktop state
Edit: Yes, calling MinimizeAll() is belligerent.
Here's an updated version that I whipped together:
/// <summary>
/// Minimizes all running applications and captures desktop as image
/// Note: Requires reference to "Microsoft Shell Controls and Automation"
/// </summary>
/// <returns>Image of desktop</returns>
private Image CaptureDesktopImage() {
//May want to play around with the delay.
TimeSpan ToggleDesktopDelay = new TimeSpan(0, 0, 0, 0, 150);
Shell32.ShellClass ShellReference = null;
Bitmap WorkingImage = null;
Graphics WorkingGraphics = null;
Rectangle TargetArea = Screen.PrimaryScreen.WorkingArea;
Image ReturnImage = null;
try
{
ShellReference = new Shell32.ShellClass();
ShellReference.ToggleDesktop();
System.Threading.Thread.Sleep(ToggleDesktopDelay);
WorkingImage = new Bitmap(TargetArea.Width,
TargetArea.Height);
WorkingGraphics = Graphics.FromImage(WorkingImage);
WorkingGraphics.CopyFromScreen(TargetArea.X, TargetArea.X, 0, 0, TargetArea.Size);
System.Threading.Thread.Sleep(ToggleDesktopDelay);
ShellReference.ToggleDesktop();
ReturnImage = (Image)WorkingImage.Clone();
}
catch
{
System.Diagnostics.Debugger.Break();
//...
}
finally
{
WorkingGraphics.Dispose();
WorkingImage.Dispose();
}
return ReturnImage;
}
Adjust to taste for multiple monitor scenarios (although it sounds like this should work just fine for your application).
You might look at using GetDesktopWindow through p/invoke, then get the device context and take your snapshot. There is a very detailed tutorial here. It might be better than perturbing the existing windows.
You can always catch screenshot on windows startup before applications run
or use other desktop by using desktop switching. Take a look here:
http://www.codeproject.com/Articles/7666/Desktop-Switching
Switch to other desktop, take picture and remember you dont need to show it just create it "DESKTOP"
All former answers are completely wrong (minimizing apps is absurd)
Simply use SHW api : one line of code (the desktop bitmap being cached, the api simply blits it to your HDC...)

Categories