Memory leak loading imagebrush to grid.background - c#

I have one app sending screenshots to another app that needs to present them in the WPF window
it works great except for the only probelm which is that the code builds up in the memory each time i add a new background.
how do I solve this problem ?
Thanks!
private void GetSnapshots(object state)
{
using (var socket=new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.Connect(new IPEndPoint(IPAddress.Parse("10.0.0.9"), 8081));
while (Connected)
{
var lengthData = new byte[4];
var lengthBytesRead = 0;
while (lengthBytesRead < lengthData.Length)
{
var read = socket.Receive(lengthData, lengthBytesRead, lengthData.Length - lengthBytesRead, SocketFlags.None);
if (read == 0) return;
lengthBytesRead += read;
}
var length = BitConverter.ToInt32(lengthData, 0);
var imageData = new byte[length];
var imageBytesRead = 0;
while (imageBytesRead < imageData.Length)
{
var read = socket.Receive(imageData, imageBytesRead, imageData.Length - imageBytesRead, SocketFlags.None);
if (read == 0) return;
imageBytesRead += read;
}
using (var stream = new MemoryStream(imageData))
{
var bitmap = new Bitmap(stream);
Dispatcher.Invoke(new ImageCompleteDelegate(ImageComplete), new object[] { bitmap });
stream.Dispose();
bitmap.Dispose();
}
}
socket.Disconnect(false);
}
}
public static System.Windows.Media.Brush CreateBrushFromBitmap(Bitmap bmp)
{
return new ImageBrush(Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()));
}
private delegate void ImageCompleteDelegate(Bitmap bitmap);
private void ImageComplete(Bitmap bitmap)
{
if (_buffer != null)
{
_buffer = null;
}
_buffer = new Bitmap(bitmap);
bitmap.Dispose();
//ScreenShotG is a Grid Element inside the XAML
ScreenShotG.Background = CreateBrushFromBitmap(_buffer);
}

I finally fixed it after many hours of researching and testing.
The problem was in the following code :
public static System.Windows.Media.Brush CreateBrushFromBitmap(Bitmap bmp)
{
return new ImageBrush(Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()));
}
The solution was :
public static System.Windows.Media.Brush CreateBrushFromBitmap(Bitmap bmp)
{
IntPtr hBitMap = bmp.GetHbitmap();
ImageBrush b = new ImageBrush(Imaging.CreateBitmapSourceFromHBitmap(hBitMap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()));
DeleteObject(hBitMap);
return b;
}

Related

Easy way to clean metadata from an image?

I'm working on a service for a company project that handles image processing, and one of the methods is supposed to clean the metadata from an image passed to it.
I think implementation I currently have works, but I'm not sure if it's affecting the quality of images or if there's a better way to handle this task. Could you let me know if you know of a better way to do this?
Here's the method in question:
public byte[] CleanMetadata(byte[] data)
{
Image image;
if (tryGetImageFromBytes(data, out image))
{
Bitmap bitmap = new Bitmap(image);
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(image, new Point(0, 0));
}
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(image, typeof(byte[]));
}
return null;
}
And, for reference, the tryGetImageFromBytes method:
private bool tryGetImageFromBytes(byte[] data, out Image image)
{
try
{
using (var ms = new MemoryStream(data))
{
image = Image.FromStream(ms);
}
}
catch (ArgumentException)
{
image = null;
return false;
}
return true;
}
To reiterate: is there a better way to remove metadata from an image that doesn't involve redrawing it?
Thanks in advance.
The .NET way: You may want to try your hand at the System.Windows.Media.Imaging.BitmapEncoder class - more precisely, its Metadata collection. Quoting MSDN:
Metadata - Gets or sets the metadata that will be associated with this
bitmap during encoding.
The 'Oops, I (not so accidentally) forgot something way: Open the original bitmap file into a System.drawing.Bitmap object. Clone it to a new Bitmap object. Write the clone's contents to a new file. Like this one-liner:
((System.Drawing.Bitmap)System.Drawing.Image.FromFile(#"C:\file.png").Clone()).Save(#"C:\file-nometa.png");
The direct file manipulation way (only for JPEG): Blog post about removing the EXIF area.
I would suggest this, the source is here: Removing Exif-Data for jpg file
Changing a bit the 1st function
public Stream PatchAwayExif(Stream inStream)
{
Stream outStream = new MemoryStream();
byte[] jpegHeader = new byte[2];
jpegHeader[0] = (byte)inStream.ReadByte();
jpegHeader[1] = (byte)inStream.ReadByte();
if (jpegHeader[0] == 0xff && jpegHeader[1] == 0xd8) //check if it's a jpeg file
{
SkipAppHeaderSection(inStream);
}
outStream.WriteByte(0xff);
outStream.WriteByte(0xd8);
int readCount;
byte[] readBuffer = new byte[4096];
while ((readCount = inStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
outStream.Write(readBuffer, 0, readCount);
return outStream;
}
And the second function with no changes, as post
private void SkipAppHeaderSection(Stream inStream)
{
byte[] header = new byte[2];
header[0] = (byte)inStream.ReadByte();
header[1] = (byte)inStream.ReadByte();
while (header[0] == 0xff && (header[1] >= 0xe0 && header[1] <= 0xef))
{
int exifLength = inStream.ReadByte();
exifLength = exifLength << 8;
exifLength |= inStream.ReadByte();
for (int i = 0; i < exifLength - 2; i++)
{
inStream.ReadByte();
}
header[0] = (byte)inStream.ReadByte();
header[1] = (byte)inStream.ReadByte();
}
inStream.Position -= 2; //skip back two bytes
}
Creating a new bitmap will clear out all the exif data.
var newImage = new Bitmap(image);
If you want to remove only specific info:
private Image RemoveGpsExifInfo(Image image)
{
foreach (var item in image.PropertyItems)
{
// GPS range is from 0x0000 to 0x001F. Full list here -> https://exiftool.org/TagNames/EXIF.html (click on GPS tags)
if (item.Id <= 0x001F)
{
image.RemovePropertyItem(item.Id);
}
}
return image;
}

Out of Memory error during stream capture from camera

I have been exhausted from this error from last 2 weeks.I tried a lot to find out and tried the code in different way but not succeeded yet.I think the main problem is with bitmap, may be i am not using in right way.I am sharing my code for help to understand what i am doing.
First i tell you the scenario.In this app, i am using dslr camera for live view.The main code area from camera class is here below :
internal void Run()
{
LVrunning = true;
while (LVrunning)
{
Thread.Sleep(20);
if (LVrunning)
UpdatePicture();
}
}
private void UpdatePicture()
{
try
{
if (err == EDSDK.EDS_ERR_OK && LVrunning)
{
inSide = true;
// Download live view image data
err = EDSDK.EdsDownloadEvfImage(cameraDev, EvfImageRef);
if (err != EDSDK.EDS_ERR_OK)
{
Debug.WriteLine(String.Format("Download of Evf Image: {0:X}", err));
return;
}
IntPtr ipData;
err = EDSDK.EdsGetPointer(MemStreamRef, out ipData);
if (err != EDSDK.EDS_ERR_OK)
{
Debug.WriteLine(String.Format("EdsGetPointer failed: {0:X}", err));
return;
}
uint len;
err = EDSDK.EdsGetLength(MemStreamRef, out len);
if (err != EDSDK.EDS_ERR_OK)
{
Debug.WriteLine(String.Format("EdsGetLength failed:{0:X}", err));
EDSDK.EdsRelease(ipData);
return;
}
Byte[] data = new byte[len];
Marshal.Copy(ipData, data, 0, (int)len);
System.IO.MemoryStream memStream = new System.IO.MemoryStream(data);
// get the bitmap
Bitmap bitmap = null;
try
{
bitmap = new Bitmap(memStream);
}
catch (OutOfMemoryException ex)
{
GC.WaitForPendingFinalizers();
bitmap = new Bitmap(memStream); // sometimes error occur
}
NewFrame(bitmap, null); // this is event call back to form area.
memStream.Dispose();
EDSDK.EdsRelease(ipData);
}
}
catch (Exception ex)
{
}
}
private void getCapturedItem(IntPtr directoryItem)
{
uint err = EDSDK.EDS_ERR_OK;
IntPtr stream = IntPtr.Zero;
EDSDK.EdsDirectoryItemInfo dirItemInfo;
err = EDSDK.EdsGetDirectoryItemInfo(directoryItem, out dirItemInfo);
if (err != EDSDK.EDS_ERR_OK)
{
throw new CameraException("Unable to get captured item info!", err);
}
// Fill the stream with the resulting image
if (err == EDSDK.EDS_ERR_OK)
{
err = EDSDK.EdsCreateMemoryStream((uint)dirItemInfo.Size, out stream);
}
// Copy the stream to a byte[] and
if (err == EDSDK.EDS_ERR_OK)
{
err = EDSDK.EdsDownload(directoryItem, (uint)dirItemInfo.Size, stream);
}
// Create the returned item
//CapturedItem item = new CapturedItem();
if (dirItemInfo.szFileName.ToString().ToLower().Contains("jpg") || dirItemInfo.szFileName.ToString().ToLower().Contains("jpeg"))
{
if (err == EDSDK.EDS_ERR_OK)
{
IntPtr imageRef = IntPtr.Zero;
err = EDSDK.EdsCreateImageRef(stream, out imageRef);
if (err == EDSDK.EDS_ERR_OK)
{
EDSDK.EdsImageInfo info;
err = EDSDK.EdsGetImageInfo(imageRef, EDSDK.EdsImageSource.FullView, out info);
}
}
}
if (err == EDSDK.EDS_ERR_OK)
{
try
{
byte[] buffer = new byte[(int)dirItemInfo.Size];
GCHandle gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr address = gcHandle.AddrOfPinnedObject();
IntPtr streamPtr = IntPtr.Zero;
err = EDSDK.EdsGetPointer(stream, out streamPtr);
if (err != EDSDK.EDS_ERR_OK)
{
throw new CameraDownloadException("Unable to get resultant image.", err);
}
try
{
Marshal.Copy(streamPtr, buffer, 0, (int)dirItemInfo.Size);//sometimes error comes here
System.IO.MemoryStream memStream = new System.IO.MemoryStream(buffer);
Bitmap bitmap = null;
try
{
bitmap = new Bitmap(memStream);
}
catch (OutOfMemoryException ex)
{
GC.WaitForPendingFinalizers();
Bitmap b = new Bitmap(memStream);//sometimes error comes here
}
if (bitmap != null)
{
PhotoCaptured(bitmap, null);
}
}
catch (AccessViolationException ave)
{
throw new CameraDownloadException("Error copying unmanaged stream to managed byte[].", ave);
}
finally
{
gcHandle.Free();
EDSDK.EdsRelease(stream);
EDSDK.EdsRelease(streamPtr);
}
}
catch (OutOfMemoryException ex)
{
GC.WaitForPendingFinalizers();
IboothmeObject.minimizeMemory();
getCapturedItem(directoryItem);
}
}
else
{
throw new CameraDownloadException("Unable to get resultant image.", err);
}
}
On form side, image is updating in picture box simply
private void StartLiveView()
{
if (this.liveView.Connected)
{
this.liveView.PhotoCaptured += new EventHandler(liveView_PhotoCaptured);
this.liveView.NewFrame += new EventHandler(liveView_NewFrame);
this.liveView.StartLiveView();
}
}
void liveView_NewFrame(object sender, EventArgs e)
{
this.picMain.Image = sender as Image;
}
void liveView_PhotoCaptured(object sender, EventArgs e)
{
Image img = sender as Image;
// this image is big in size like 5000x3000.
Bitmap tempbitmap = new Bitmap(img.Width, img.Height);// now mostly error comes here
tempbitmap.SetResolution(img.HorizontalResolution, img.VerticalResolution);
using (Graphics g = Graphics.FromImage(tempbitmap))
{
g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height));
g.Save();
}
picMain.Image = tempbitmap;
tempbitmap.Save(path,ImageFormat.Jpeg);
}
Another area of code which uses the bitmap and live view from camera.This code get the frame from camera and write some objects on frame..In my case, i am writing some ballons on the frame
void liveView_NewFrame(object sender, EventArgs e)
{
using (Image<Bgr, byte> Frame = new Image<Bgr, byte>(new Bitmap(sender as Image)))
{
Frame._SmoothGaussian(3);
IntPtr hsvImage = CvInvoke.cvCreateImage(CvInvoke.cvGetSize(Frame), Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, 3);
CvInvoke.cvCvtColor(Frame, hsvImage, Emgu.CV.CvEnum.COLOR_CONVERSION.CV_BGR2HSV);
Image<Gray, byte> imgThresh = new Image<Gray, byte>(Frame.Size);
imgThresh.Ptr = GetThresholdedImage(hsvImage);
//CvInvoke.cvSmooth(imgThresh, imgThresh, Emgu.CV.CvEnum.SMOOTH_TYPE.CV_GAUSSIAN, 3, 3, 3, 3);
#region Draw the contours of difference
//this is tasken from the ShapeDetection Example
Rectangle largest = new Rectangle();
try
{
using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
//detect the contours and loop through each of them
for (Contour<Point> contours = imgThresh.Convert<Gray, Byte>().FindContours(
Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL,
storage);
contours != null;
contours = contours.HNext)
{
//Create a contour for the current variable for us to work with
Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.05, storage);
//Draw the detected contour on the image
if (currentContour.Area > ContourThresh) //only consider contours with area greater than 100 as default then take from form control
{
if (currentContour.BoundingRectangle.Width > largest.Width && currentContour.BoundingRectangle.Height > largest.Height)
{
largest = currentContour.BoundingRectangle;
}
}
//storage.Dispose();
}
}
catch (Exception)
{
}
#endregion
#region Draw Object
Random r = new Random();
//Bitmap bb = Frame.Bitmap;
foreach (var item in objectList)
{
using (Graphics g = Graphics.FromImage(Frame.Bitmap))
{
if (DrawAble(item, largest))
{
if (item.Y < 0)
{
if (item.X < picMain.Width)
{
g.DrawImage(item.image, new Rectangle(item.X, 0, item.image.Width, item.image.Height + item.Y),
new Rectangle(), GraphicsUnit.Pixel);
item.X += r.Next(-5, 5);
item.Y += 15;
}
}
else
{
if (item.X < picMain.Width && item.Y < picMain.Height)
{
g.DrawImage(item.image, new Rectangle(item.X, item.Y, item.image.Width, item.image.Height));
item.X += r.Next(-5, 5);
item.Y += 15;
}
else
{
item.X = r.Next(0, picMain.Width - 5);
item.Y = r.Next(-item.image.Height, -5);
}
}
}
else
{
item.X = r.Next(0, picMain.Width - 5);
item.Y = r.Next(-item.image.Height, -5);
}
}
}
#endregion
picMain.Image = Frame.ToBitmap();
}
minimizeMemory();
}
Now i share the whole problem in detail.
First on all i created a form for live view and by using opencv(Emgu) library , i am drawing balloons on the frame.In live view these balloons are moving.The other form is for capture the picture from camera with high resolution.
I noticed that, my application memory was increasing with every frame and after 2 live-view and 2 pictures caputured, it goes to 1+ GB.If i tried to show first form again for live view, error occured in UpdatePicture() function.
then i add the code to minimize the memory of the current application.now i am calling this function after every frame in live-view.
After this solution when i checked the memory of application it does not go over 100mb or 200mb.
But problem was still there.after few captures, error occurred in UpdatePicture() when get bitmap from stream ( bitmap = new Bitmap(memStream);).The error was same out of memory.
After some search i found this solution.
// get the bitmap
Bitmap bitmap = null;
try
{
bitmap = new Bitmap(memStream);
}
catch (OutOfMemoryException ex)
{
GC.WaitForPendingFinalizers();
bitmap = new Bitmap(memStream);
}
But not working error is still same.
Error sometimes shown in UpdatePicture() method, sometime occurred in liveView_NewFrame method.
Means problem is related to bitmap, bitmap size or memory is corrupt.
So please help me.I am worried, 2 weeks passed but i could not solve this.
you are calling CvInvoke.cvCreateImage the created data is sitting on the native heap
so it wont be collected by the GC.
you must call cvReleaseImage(IntPtr) to release the data
there are alot of memory profilers which can help understanding the issue
try using ANTS Memory Profiler 7

kinect - I can poll for frame data, but the events are not working

I was trying a basic "hello world" app for a first kinect project, to stream the video data to the stream. The code is here: https://github.com/fschwiet/HelloKinect/blob/8803b6b959ee6dba5f9284b9e732fb11a897dea4/HelloKinect/ShowCameraCommand.cs
What I find is that I can poll for frame data in a loop, but I am not receiving the frame-ready events. The sourcecode is below. When UsePolling is true, frame data is sent to the form. When UsePolling is false, there is console output "Hit return to exit." indicating everything has run, but no events are ever received.
I have a feeling this has to do with windows message pumps, that I need to wait in an alertable state and/or pump a message queue. I haven't been able to make it work though, anyone have any hints?
public class ShowCameraCommand : ConsoleCommand
{
static private Form EchoForm;
private bool UsePolling;
public ShowCameraCommand()
{
this.IsCommand("show-camera");
this.HasOption("p", "Use polling to check frame data", v => UsePolling = true);
}
public override int Run(string[] remainingArguments)
{
var sensor = KinectSensor.KinectSensors.Where(s => s.Status == KinectStatus.Connected).FirstOrDefault();
if (sensor == null)
{
Console.WriteLine("Kinect was not detected");
Console.WriteLine();
return -1;
}
EchoForm = new Form();
EchoForm.Width = 640;
EchoForm.Height = 480;
EchoForm.Show();
sensor.ColorStream.Enable(ColorImageFormat.RawYuvResolution640x480Fps15);
if (!UsePolling)
{
sensor.ColorFrameReady += sensor_ColorFrameReady;
}
sensor.Start();
if (UsePolling)
{
Console.WriteLine("Use any key to exit.");
while (!Console.KeyAvailable)
{
using (var frame = sensor.ColorStream.OpenNextFrame(10 * 1000))
{
HandleFrame(frame);
}
Thread.Sleep(50);
}
}
else
{
Console.WriteLine("Hit return to exit.");
Console.ReadLine();
}
return 0;
}
void sensor_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
Console.WriteLine("Frame received");
using (ColorImageFrame frame = e.OpenColorImageFrame())
{
HandleFrame(frame);
}
}
private void HandleFrame(ColorImageFrame frame)
{
var bitmap = ImageToBitmap(frame);
using (var g = EchoForm.CreateGraphics())
{
g.DrawImage(bitmap, 0, 0);
Console.WriteLine("Frame drawn");
}
}
// http://stackoverflow.com/questions/10848190/convert-kinect-colorframe-to-bitmap
Bitmap ImageToBitmap(ColorImageFrame Image)
{
byte[] pixeldata = new byte[Image.PixelDataLength];
Image.CopyPixelDataTo(pixeldata);
Bitmap bmap = new Bitmap(Image.Width, Image.Height, PixelFormat.Format32bppRgb);
BitmapData bmapdata = bmap.LockBits(
new Rectangle(0, 0, Image.Width, Image.Height),
ImageLockMode.WriteOnly,
bmap.PixelFormat);
IntPtr ptr = bmapdata.Scan0;
Marshal.Copy(pixeldata, 0, ptr, Image.PixelDataLength);
bmap.UnlockBits(bmapdata);
return bmap;
}
}
Oh, figured it out. I needed to call Application.Run() to start pumping events.

converting a string to image in c# for windows phone 7 application

i want to convert a string entered by user to an image..how can it be done?
i tried the following code but i get an argument exception in the line :
WriteableBitmap wbimg = PictureDecoder.DecodeJpeg(memStream);
static public string EncodeTo64(string toEncode)
{
byte[] toEncodeAsBytes
= StringToAscii(toEncode);
string returnValue
= System.Convert.ToBase64String(toEncodeAsBytes);
return returnValue;
}
public static byte[] StringToAscii(string s)
{
byte[] retval = new byte[s.Length];
for (int ix = 0; ix < s.Length; ++ix)
{
char ch = s[ix];
if (ch <= 0x7f) retval[ix] = (byte)ch;
else retval[ix] = (byte)'?';
}
return retval;
}
void convert()
{
String s = textBox1.Text;
byte[] data = Convert.FromBase64String(EncodeTo64(s));
for (int i = 0; i < data.Length; i++)
{
System.Diagnostics.Debug.WriteLine(data[i]);
}
Stream memStream = new MemoryStream();
memStream.Write(data, 0, data.Length);
try
{
WriteableBitmap wbimg = PictureDecoder.DecodeJpeg(memStream);
image1.Source = wbimg;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
I got what i wanted in the following links.. How can I render text on a WriteableBitmap on a background thread, in Windows Phone 7? and http://blogs.u2u.be/michael/post/2011/04/20/Adding-a-text-to-an-image-in-WP7.aspx Thanks to all those who replied for the initial help! :)
It is the simple way you can convert TextBlock Text into Image
private void convert_Click(object sender, RoutedEventArgs e)
{
Canvas c1 = new Canvas();
TextBlock t = new TextBlock();
t.Text = text1.Text;
t.FontFamily = text1.FontFamily;
t.Foreground = text1.Foreground;
t.FontSize = text1.FontSize;
c1.Children.Add(t);
WriteableBitmap wbmp = new WriteableBitmap(c1, null);
im = new Image();
im.Source = wbmp;
im.Height = 200;
im.Width = 200;
Canvas.SetTop(im, 10);
Canvas.SetLeft(im, 10);
Main_Canvas.Children.Add(im);
}
Here I convert the Textblock Text into Bitmap and then assign it to the image source.
Here is how to writte a string to a bitmap:
Bitmap b = new Bitmap(200, 100);
Graphics g = Graphics.FromImage(b);
g.DrawString("My sample string", new Font("Tahoma",10), Brushes.Red, new Point(0, 0));
b.Save("mypic.png", System.Drawing.Imaging.ImageFormat.Png);
g.Dispose();
b.Dispose();
Shubhi1910 let me know if you need any details to be explained.

Converting WebBrowser.Document To A Bitmap?

Is it possible to draw a WebBrowser.Document to a Bitmap? Basically taking a screenshot of a WebBrowser control (note, this is with a WebBrowser that doesn't live on a form, but just in code).
WebBrowser w = new WebBrowser();
w.Document = "<b>Hello</b> world.";
w.Document.DrawToBitmap ???
Thanks!
I use the following code to capture a screenshot of a web page loaded in a WebBrowser control:
class NativeMethods
{
[ComImport]
[Guid("0000010D-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IViewObject
{
void Draw([MarshalAs(UnmanagedType.U4)] uint dwAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, IntPtr hdcTargetDev, IntPtr hdcDraw, [MarshalAs(UnmanagedType.Struct)] ref RECT lprcBounds, [In] IntPtr lprcWBounds, IntPtr pfnContinue, [MarshalAs(UnmanagedType.U4)] uint dwContinue);
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public static void GetImage(object obj, Image destination, Color backgroundColor)
{
using(Graphics graphics = Graphics.FromImage(destination))
{
IntPtr deviceContextHandle = IntPtr.Zero;
RECT rectangle = new RECT();
rectangle.Right = destination.Width;
rectangle.Bottom = destination.Height;
graphics.Clear(backgroundColor);
try
{
deviceContextHandle = graphics.GetHdc();
IViewObject viewObject = obj as IViewObject;
viewObject.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, deviceContextHandle, ref rectangle, IntPtr.Zero, IntPtr.Zero, 0);
}
finally
{
if(deviceContextHandle != IntPtr.Zero)
{
graphics.ReleaseHdc(deviceContextHandle);
}
}
}
}
}
Example:
Bitmap screenshot = new Bitmap(1024, 768);
NativeMethods.GetImage(webBrowser.ActiveXInstance, screenshot, Color.White);
public void HTMLScreenShot()
{
WebBrowser wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.Size = new Size(800, 600);
// Add html as string
wb.Navigate("about:blank");
wb.Document.Write("<b>Hellow World!</b>");
// Add html from website
// wb.Navigate("http://myurl.com");
}
void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser wb = sender as WebBrowser;
using (Bitmap bitmap = new Bitmap(wb.Width, wb.Height))
{
Rectangle bounds = new Rectangle(new Point(0, 0), wb.Size);
wb.DrawToBitmap(bitmap, bounds);
bitmap.Save("C:\WebsiteScreenshot.png");
}
}
http://www.bryancook.net/2006/03/screen-capture-for-invisible-windows.html
and here:
http://www.codeproject.com/KB/graphics/screen_capturing.aspx
I believe you should get the handle of your WebBrowser control and save it's content as image like suggested in those links.
//
// If you want to take a snap from existing webBrowser Control
//
private void button1_Click(object sender, EventArgs e)
{
using (FileDialog fd = new SaveFileDialog())
{
fd.Filter = "Image (*.png)|*.png";
if (fd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
new WebPageSnap(webBrowser1.Url.ToString(), fd.FileName);
//might take 3 or 4 seconds to save cauz it has to load again.
}
}
}
//
// Or if you want to take a snap without showing up
//
private void button2_Click(object sender, EventArgs e)
{
using (FileDialog fd = new SaveFileDialog())
{
fd.Filter = "Image (*.png)|*.png";
if (fd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string url = "http://www.google.com";
// or
url = textBox1.Text;
new WebPageSnap(url, fd.FileName); }
}
}
class WebPageSnap
{
WebBrowser wb;
string outFile;
public WebPageSnap(string url, string outputFile)
{
wb = new WebBrowser();
wb.ProgressChanged += wb_ProgressChanged;
outFile = outputFile;
wb.ScriptErrorsSuppressed = true;
wb.ScrollBarsEnabled = false;
wb.Navigate(url);
}
void wb_ProgressChanged(object sender, WebBrowserProgressChangedEventArgs e)
{
if (e.CurrentProgress == e.MaximumProgress)
{
wb.ProgressChanged -= wb_ProgressChanged;
try
{
int scrollWidth = 0;
int scrollHeight = 0;
scrollHeight = wb.Document.Body.ScrollRectangle.Height;
scrollWidth = wb.Document.Body.ScrollRectangle.Width;
wb.Size = new Size(scrollWidth, scrollHeight);
Bitmap bitmap = new Bitmap(wb.Width, wb.Height);
for (int Xcount = 0; Xcount < bitmap.Width; Xcount++)
for (int Ycount = 0; Ycount < bitmap.Height; Ycount++)
bitmap.SetPixel(Xcount, Ycount, Color.Black);
wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
bitmap.Save(outFile, ImageFormat.Png);
}
catch { }
}
}
}

Categories