PageRenderTime 42ms CodeModel.GetById 27ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 1ms

/Platters/classes/screenshot.cs

http://skimpt.googlecode.com/
C# | 333 lines | 70 code | 64 blank | 199 comment | 1 complexity | 83824b796547d36303695807c6cb9c97 MD5 | raw file
  1#region "License Agreement"
  2/* Skimpt, an open source screenshot utility.
  3      Copyright (C) <year>  <name of author>
  4
  5      This program is free software: you can redistribute it and/or modify
  6      it under the terms of the GNU General Public License as published by
  7      the Free Software Foundation, either version 3 of the License, or
  8      (at your option) any later version.
  9
 10      this program is distributed in the hope that it will be useful,
 11      but WITHOUT ANY WARRANTY; without even the implied warranty of
 12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13      GNU General Public License for more details.
 14
 15      You should have received a copy of the GNU General Public License
 16      along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 17#endregion
 18using System;
 19using System.Runtime.InteropServices;
 20using System.Drawing;
 21using System.Drawing.Imaging;
 22using System.Diagnostics;
 23using System.Windows.Forms;
 24
 25/* Please note a C++ copy is commented out at the bottom of this class */
 26
 27/// <summary>
 28/// This is a static class which exposes two methods
 29/// It allows us to take a screen capture.
 30/// It is sealed so it can not be inherited.
 31/// </summary>
 32public sealed class ScreenCapture
 33{
 34
 35    /// Intro to DCs:  
 36    /// If you are familiar, skip ahead. If not, read on.
 37    /// Device contexts are just generic ways to represent devices,
 38    /// or things you can draw on or interact with graphically. 
 39    /// This is hugely over simplified, and I'm going to get flamed, but for the sake of brevity,
 40    /// let's just go with that for now. You can get device context's, or dc's as 
 41    /// they are commonly referred to, from images, windows, monitors, and even printers. 
 42    /// Every window has them, and you can use them to draw with. Everything you see on your screen is being 
 43    /// drawn upon a device context. The desktop, every window, your taskbar, and anything you see. 
 44    /// You can draw upon them, or copy what they have drawn upon them. If you can get a hold of them, 
 45    /// you can pretty much draw or steal whatever you want graphically speaking. 
 46    /// Working with device contexts is fast, and both GDI and GDI+ are based on them.
 47    /// in the case of capturing the screen, we know that somewhere Windows is drawing upon a device context, so that we can see it. 
 48    /// In fact, there's one for each monitor you have attached to your system, and that desktop that you are seeing on it, 
 49    /// is being drawn on that monitor's device context. 
 50    /// All we have to do is grab a hold of that device context, create another one of our own, 
 51    /// and copy the screen's device context image data to our own, and we've got a screen capture
 52
 53
 54    public static Bitmap GetDesktopWindowCaptureAsBitmap()
 55    {
 56        //SEE HOW THIS FUNCTION WORKS AT THE END OF THIS FUNCTION.
 57        Rectangle rcScreen = Rectangle.Empty;
 58        Screen[] screens = Screen.AllScreens;
 59
 60
 61        // Create a rectangle encompassing all screens...
 62        foreach (Screen screen in Screen.AllScreens)
 63            rcScreen = Rectangle.Union(rcScreen, screen.Bounds);
 64
 65        // Create a composite bitmap of the size of all screens...
 66        Bitmap finalBitmap = new Bitmap(rcScreen.Width, rcScreen.Height);
 67
 68        // Get a graphics object for the composite bitmap and initialize it...
 69        Graphics g = Graphics.FromImage(finalBitmap);
 70        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
 71        g.FillRectangle(
 72           SystemBrushes.Desktop,
 73           0,
 74           0,
 75           rcScreen.Width - rcScreen.X,
 76           rcScreen.Height - rcScreen.Y);
 77
 78        // Get an HDC for the composite area...
 79        IntPtr hdcDestination = g.GetHdc();
 80
 81        // Now, loop through screens, 
 82        // Blting each to the composite HDC created above...
 83        foreach (Screen screen in Screen.AllScreens)
 84        {
 85            // Create DC for each source monitor...
 86            IntPtr hdcSource = Win32.CreateDC(
 87              IntPtr.Zero,
 88              screen.DeviceName,
 89              IntPtr.Zero,
 90              IntPtr.Zero);
 91
 92            // Blt the source directly to the composite destination...
 93            int xDest = screen.Bounds.X - rcScreen.X;
 94            int yDest = screen.Bounds.Y - rcScreen.Y;
 95
 96            bool success = Win32.StretchBlt(
 97              hdcDestination,
 98              xDest,
 99              yDest,
100              screen.Bounds.Width,
101              screen.Bounds.Height,
102              hdcSource,
103              0,
104              0,
105              screen.Bounds.Width,
106              screen.Bounds.Height,
107              (int)Win32.TernaryRasterOperations.SRCCOPY);
108
109            //  System.Diagnostics.Trace.WriteLine(screen.Bounds);
110
111            if (!success)
112            {
113                System.ComponentModel.Win32Exception win32Exception =
114                    new System.ComponentModel.Win32Exception();
115                System.Diagnostics.Trace.WriteLine(win32Exception);
116            }
117
118            // Cleanup source HDC...
119            Win32.DeleteDC(hdcSource);
120        }
121
122        // Cleanup destination HDC and Graphics...
123        g.ReleaseHdc(hdcDestination);
124        g.Dispose();
125
126        // Return composite bitmap which will become our Form's PictureBox's image...
127        return finalBitmap;
128
129        ///Looking at the code, the first thing you'll see is that I'm using a mixture of GDI and GDI+. 
130        ///This is due largely to the fact that there is a bug present in GDI+ and the BtBlt API.           
131        ///This issue only manifests itself on systems with multiple monitors, and if I remember correctly,
132        ///the system had to have a NVida display adapter on the non-primary monitor, and of course, our old friend Windows 98 running as the OS.
133        ///What happens is the primary monitor captures fine, the secondary (or any non-primary) monitor stands a chance of returning garbage for an image. 
134        ///It looks like cable channel with no signal.  
135
136        ///Instead of relying on purely managed code, do copy the images, 
137        ///or backing up to the BtBlt API, we instead fall back to it's somewhat slower cousin, StretchBlt.       
138
139        ///first up we just grab all of the monitors using the Screen class' AllScreens property. 
140        ///This does two things for us. First it allows us to figure out how big the entire desktop is, 
141        ///and create an image just big enough to hold all of the screens inside. 
142        ///And secondly, it allows us to figure out just where each monitor is positioned in relation to the other.
143        ///Remember, with multiple monitor support you can "arrange" your monitors in different ways,
144        ///and with different resolutions, so don't think in terms of a pure rectangle when you think of
145        ///how your monitors are positioned.
146
147        ///Once we have those screens, it's a trivial matter to calculate the size of the entire bitmap
148        ///by using the Rectangle.Union method to build up the size of the overall image. 
149        ///After we've figured out the size of the final image, we'll grab a Graphics object from the image.
150        ///The GDI+ Graphics object is just the .NET wrapper around a device context.
151        ///Using that graphics context, we can draw on the bitmap with the graphics object.
152
153        ///Next, we'll enumerate through each monitor, and draw what that monitor has on it's device context,
154        ///upon the image we just created that will hold the final screen shot. 
155        ///Well draw it using it's coordinates so that in case the monitors have different resolutions or positioning
156        ///we'll be able to see them as the Display Control Panel applet sees them.
157        ///Go check it out if you have multiple monitors, and you didn't know you could move them.
158        ///Chances are there if you have multiple monitors, you know this already, but if not so harm no foul.
159        ///Open the settings tab and drag one of the monitors around and 
160        ///you'll see you can reposition it in relation to the other monitors.
161
162        ///For each monitor, we'll simply use the StretchBlt API to copy that monitor's device context contents,
163        ///to the bitmap that will serve as the screen capture of the desktop. 
164        ///Notice that I'm creating a device context each time, this gives us access to that monitor's device context so 
165        ///that we can copy from it. Keep in mind that if we create it, we must destroy it, 
166        ///so we delete the device context when we are finished with it. If you don't, you'll have a memory leak,
167        ///so keep a watchful eye on your dc's and make sure to release or destroy them.
168        ///A simple rule is, if you "acquire" it, you're required to "release" it. 
169        ///And if you "create" it, then you must "destroy" it. 
170        ///I quote those because if you look at the GDI APIs, with that in mind you'll find the necessary APIs to do exactly what you want.
171
172        ///Finally, after copying the contents of each device context to that bitmap we created, 
173        ///we'll release the Graphics object we acquired from the bitmap, and dispose it.
174        ///That's the proper way to clean up a graphics object, if you've acquired a device context from it.
175
176
177    }
178
179    public static Bitmap CaptureDeskTopRectangle(Rectangle CapRect, int CapRectWidth, int CapRectHeight)
180    {
181
182        //This function just calls the GetDesktop, and just crops it.
183        Bitmap bmpImage = new Bitmap(GetDesktopWindowCaptureAsBitmap());
184        Bitmap bmpCrop = new Bitmap(CapRectWidth, CapRectHeight, bmpImage.PixelFormat);
185        Rectangle recCrop = new Rectangle(CapRect.X, CapRect.Y, CapRectWidth, CapRectHeight);
186        Graphics gphCrop = Graphics.FromImage(bmpCrop);
187        Rectangle recDest = new Rectangle(0, 0, CapRectWidth, CapRectHeight);
188        gphCrop.DrawImage(bmpImage, recDest, recCrop.X, recCrop.Y, recCrop.Width, recCrop.Height, GraphicsUnit.Pixel);
189        return bmpCrop;
190    }
191}
192
193
194// 
195// Credits: Mark (Code6) Belles C# version
196//               Me :+)) for the porting code
197//               Thanks to EVA for g->ReleaseHDC or you may have a black screen
198//               and MSDN http://msdn2.microsoft.com/en-us/library/ms533843(VS.85).aspx
199// Enjoy: Feel free to use it....
200//
201//#include "stdafx.h"
202//#include <windows.h>
203//#include <gdiplus.h>
204
205//#pragma comment(lib, "gdiplus.lib")
206
207//#define SAFE_DELETE(ptrToDelete) if (ptrToDelete != NULL) { delete ptrToDelete; ptrToDelete = NULL; }
208
209//ULONG_PTR     gdiplusToken;
210//int m_nScreenCount;
211//RECT m_ScreensRect[8];
212//TCHAR m_ScreensName[8][CCHDEVICENAME];
213
214//BOOL CALLBACK MyInfoEnumProcs( HMONITOR hMonitor, HDC hdcMonitor,
215//                                                                      LPRECT lprcMonitor, LPARAM dwData )
216//{
217//     MONITORINFOEX monitorInfo;
218//     memcpy(&m_ScreensRect[m_nScreenCount], lprcMonitor, sizeof(RECT));
219
220//     memset(&monitorInfo, 0x0, sizeof(MONITORINFOEX));
221//     monitorInfo.cbSize = sizeof(MONITORINFOEX);
222//     GetMonitorInfo(hMonitor, &monitorInfo);
223
224//     strcpy_s((char *) &m_ScreensName[m_nScreenCount], CCHDEVICENAME, monitorInfo.szDevice);
225
226//     m_nScreenCount++;
227//     return TRUE;
228//}
229
230//int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
231//{
232//   UINT   num = 0;               // number of image encoders
233//   UINT   size = 0;            // size of the image encoder array
234//                                       // in bytes
235
236//   Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
237
238//   Gdiplus::GetImageEncodersSize(&num, &size);
239//   if(size == 0)
240//      return -1;
241
242//   pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
243//   if(pImageCodecInfo == NULL)
244//      return -1;
245
246//   GetImageEncoders(num, size, pImageCodecInfo);
247
248//   for(UINT j = 0; j < num; ++j)
249//   {
250//      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
251//      {
252//         *pClsid = pImageCodecInfo[j].Clsid;
253//         free(pImageCodecInfo);
254//         return j;   // Success
255//      }
256//   }
257
258//   free(pImageCodecInfo);
259//   return -1;
260//}
261
262//int _tmain(int argc, _TCHAR* argv[])
263//{
264//     Gdiplus::GdiplusStartupInput gdiplusStartupInput;
265//     gdiplusToken = NULL;
266
267//     if (GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Gdiplus::Ok)
268//     {
269//          gdiplusToken = NULL;
270//     }
271
272//     memset(&m_ScreensRect, 0x0, sizeof(m_ScreensRect));
273//     memset(&m_ScreensName, 0x0, sizeof(m_ScreensName));
274//     EnumDisplayMonitors(NULL, NULL, MyInfoEnumProcs, 0);
275
276//     HDC hdcDestination = NULL;
277
278//     RECT rcScreen;
279//     memset(&rcScreen, 0x0, sizeof(rcScreen));
280
281//     for(int screenCount=0; screenCount < m_nScreenCount; screenCount++) 
282//     {
283//          UnionRect(&rcScreen, &rcScreen, &m_ScreensRect[screenCount]);
284//     }
285
286//     // Create a composite bitmap of the size of all screens...
287//     Gdiplus::Bitmap* finalBitmap = new Gdiplus::Bitmap(rcScreen.right, rcScreen.bottom);
288
289//     // Get a graphics object for the composite bitmap and initialize it...
290//     Gdiplus::Graphics * g = Gdiplus::Graphics::FromImage(finalBitmap);     
291
292//     g->SetCompositingQuality(Gdiplus::CompositingQualityHighSpeed);
293
294//     Gdiplus::SolidBrush solidBrush(Gdiplus::Color(0,0,0));
295//     g->FillRectangle(&solidBrush, 0, 0, rcScreen.right, rcScreen.bottom);
296
297//     // Get an HDC for the composite area...
298//     hdcDestination = g->GetHDC();
299
300//     // Now, loop through screens, BitBlting each to the composite HDC created above...
301//     for(int screenCount=0; screenCount < m_nScreenCount; screenCount++) 
302//     {
303//          // Create DC for each source monitor...
304//          HDC hdcSource = CreateDC(NULL, m_ScreensName[screenCount], NULL, NULL);
305
306//          // Blt the source directly to the composite destination...
307//          int xDest = m_ScreensRect[screenCount].left - rcScreen.left;
308//          int yDest = m_ScreensRect[screenCount].top - rcScreen.top;
309
310//          StretchBlt(hdcDestination, xDest, yDest, m_ScreensRect[screenCount].right, m_ScreensRect[screenCount].bottom, 
311//                        hdcSource, 0, 0, m_ScreensRect[screenCount].right, m_ScreensRect[screenCount].bottom, SRCCOPY | CAPTUREBLT);
312          
313//          // Cleanup source HDC...
314//          DeleteDC(hdcSource);                    
315//     }
316
317//     g->ReleaseHDC(hdcDestination);
318
319//     CLSID clsidJPEG;
320//     GetEncoderClsid(L"image/jpeg", &clsidJPEG);
321//     finalBitmap->Save(L"c:\\screenshot.jpg", &clsidJPEG); 
322//     GetEncoderClsid(L"image/gif", &clsidJPEG);
323//     finalBitmap->Save(L"c:\\screenshot.gif", &clsidJPEG); 
324//     GetEncoderClsid(L"image/bmp", &clsidJPEG);
325//     finalBitmap->Save(L"c:\\screenshot.bmp", &clsidJPEG); 
326
327//     // Cleanup destination HDC and Graphics...
328//     DeleteDC(hdcDestination);
329//     SAFE_DELETE(g);
330//     SAFE_DELETE(finalBitmap);
331
332//     return 0;
333//}