/// <summary>Captures a screenshot of a window using Windows GDI</summary> /// <param name="handle">handle of the window to capture</param> /// <returns>the captured window image</returns> private static Image CaptureWithGDI(Workflow wfgdi, IntPtr handle, out Rectangle windowRect) { DebugHelper.WriteLine("Capturing with GDI"); windowRect = CaptureHelpers.GetWindowRectangle(handle); Image windowImageGdi = null; if (wfgdi.ActiveWindowClearBackground) { windowImageGdi = CaptureWindowWithGDI(wfgdi, handle, out windowRect); } if (windowImageGdi == null) { using (new Freeze(wfgdi, handle)) { windowImageGdi = Screenshot.CaptureRectangleNative(windowRect); } Image result = RemoveCorners(handle, windowImageGdi, null, windowRect); if (result != null) { windowImageGdi = result; } } return(windowImageGdi); }
/// <summary>Remove the corners of a window by replacing the background of these corners by transparency.</summary> private static Image RemoveCorners(IntPtr handle, Image windowImage, Bitmap redBGImage, Rectangle windowRect) { if (ZAppHelper.IsWindows8()) { // Windows 8 UI does not have round corners return(windowImage); } const int cornerSize = 5; if (windowRect.Width > cornerSize * 2 && windowRect.Height > cornerSize * 2) { DebugHelper.WriteLine("Clean transparent corners"); if (redBGImage == null) { using (Form form = new Form()) { form.FormBorderStyle = FormBorderStyle.None; form.ShowInTaskbar = false; form.BackColor = Color.Red; NativeMethods.ShowWindow(form.Handle, (int)WindowShowStyle.ShowNormalNoActivate); NativeMethods.SetWindowPos(form.Handle, handle, windowRect.X, windowRect.Y, windowRect.Width, windowRect.Height, NativeMethods.SWP_NOACTIVATE); Application.DoEvents(); redBGImage = Screenshot.CaptureRectangleNative(windowRect) as Bitmap; } } return(HelpersLib.GraphicsHelper.Core.RemoveCorners(windowImage, redBGImage)); } return(null); }
/// <summary>Captures a screenshot of a window using the Windows DWM</summary> /// <param name="handle">handle of the window to capture</param> /// <returns>the captured window image with or without cursor</returns> public static Image CaptureWithDWM(Workflow wfdwm, IntPtr handle) { DebugHelper.WriteLine("Capturing with DWM"); Image windowImageDwm = null; Bitmap redBGImage = null; Rectangle windowRect = CaptureHelpers.GetWindowRectangle(handle); if (windowRect.Width == 0) { System.Threading.Thread.Sleep(250); windowRect = CaptureHelpers.GetWindowRectangle(handle); // try again } if (windowRect.Width > 0 && NativeMethods.IsDWMEnabled()) { if (wfdwm.ActiveWindowDwmUseCustomBackground) { windowImageDwm = CaptureWindowWithDWM(handle, windowRect, out redBGImage, wfdwm.ActiveWindowDwmBackColor); } else if (wfdwm.ActiveWindowClearBackground) { windowImageDwm = CaptureWindowWithDWM(handle, windowRect, out redBGImage, Color.White); } } if (windowImageDwm == null) { DebugHelper.WriteLine("Standard capture (no transparency)"); windowImageDwm = Screenshot.CaptureRectangleNative(windowRect); } Image result = RemoveCorners(handle, windowImageDwm, redBGImage, windowRect); if (result != null) { windowImageDwm = result; } if (wfdwm.ActiveWindowIncludeShadows) { // Draw shadow manually to be able to have shadows in every case windowImageDwm = HelpersLib.GraphicsHelper.Core.AddBorderShadow((Bitmap)windowImageDwm, true); if (wfdwm.DrawCursor) { Point shadowOffset = HelpersLib.GraphicsHelper.Core.ShadowOffset; #if DEBUG DebugHelper.WriteLine("Fixed cursor position (before): " + windowRect.ToString()); #endif windowRect.X -= shadowOffset.X; windowRect.Y -= shadowOffset.Y; #if DEBUG DebugHelper.WriteLine("Fixed cursor position (after): " + windowRect.ToString()); #endif } } if (wfdwm.DrawCursor) { CaptureHelpers.DrawCursorToImage(windowImageDwm, windowRect.Location); } return(windowImageDwm); }
/// <summary> /// Make a full-size thumbnail of the captured window on a new topmost form, and capture /// this new form with a black and then white background. Then compute the transparency by /// difference between the black and white versions. /// This method has these advantages: /// - the full form is captured even if it is obscured on the Windows desktop /// - there is no problem with unpredictable Z-order anymore (the background and /// the window to capture are drawn on the same window) /// </summary> /// <param name="handle">handle of the window to capture</param> /// <param name="windowRect">the bounds of the window</param> /// <param name="redBGImage">the window captured with a red background</param> /// <param name="captureRedBGImage">whether to do the capture of the window with a red background</param> /// <returns>the captured window image</returns> private static Image CaptureWindowWithDWM(IntPtr handle, Rectangle windowRect, out Bitmap redBGImage, Color backColor) { Image windowImage = null; redBGImage = null; if (backColor != Color.White) { backColor = Color.FromArgb(255, backColor.R, backColor.G, backColor.B); } using (Form form = new Form()) { form.StartPosition = FormStartPosition.Manual; form.FormBorderStyle = FormBorderStyle.None; form.ShowInTaskbar = false; form.BackColor = backColor; form.TopMost = true; form.Bounds = CaptureHelpers.GetWindowRectangle(handle, false); IntPtr thumb; NativeMethods.DwmRegisterThumbnail(form.Handle, handle, out thumb); SIZE size; NativeMethods.DwmQueryThumbnailSourceSize(thumb, out size); #if DEBUG DebugHelper.WriteLine("Rectangle Size: " + windowRect.ToString()); DebugHelper.WriteLine("Window Size: " + size.ToString()); #endif if (size.Width <= 0 || size.Height <= 0) { return(null); } DWM_THUMBNAIL_PROPERTIES props = new DWM_THUMBNAIL_PROPERTIES(); props.dwFlags = NativeMethods.DWM_TNP_VISIBLE | NativeMethods.DWM_TNP_RECTDESTINATION | NativeMethods.DWM_TNP_OPACITY; props.fVisible = true; props.opacity = (byte)255; props.rcDestination = new RECT(0, 0, size.Width, size.Height); NativeMethods.DwmUpdateThumbnailProperties(thumb, ref props); form.Show(); System.Threading.Thread.Sleep(250); if (form.BackColor != Color.White) { // no need for transparency; user has requested custom background color NativeMethods.ActivateWindowRepeat(form.Handle, 250); windowImage = Screenshot.CaptureRectangleNative(windowRect) as Bitmap; } else if (form.BackColor == Color.White) { // transparent capture NativeMethods.ActivateWindowRepeat(handle, 250); Bitmap whiteBGImage = Screenshot.CaptureRectangleNative(windowRect) as Bitmap; form.BackColor = Color.Black; form.Refresh(); NativeMethods.ActivateWindowRepeat(handle, 250); Bitmap blackBGImage = Screenshot.CaptureRectangleNative(windowRect) as Bitmap; // Capture rounded corners with except for Windows 8 if (ZAppHelper.IsWindows8()) { form.BackColor = Color.Red; form.Refresh(); NativeMethods.ActivateWindowRepeat(handle, 250); redBGImage = Screenshot.CaptureRectangleNative(windowRect) as Bitmap; } form.BackColor = Color.White; form.Refresh(); NativeMethods.ActivateWindowRepeat(handle, 250); Bitmap whiteBGImage2 = Screenshot.CaptureRectangleNative(windowRect) as Bitmap; // Don't do transparency calculation if an animated picture is detected if (whiteBGImage.AreBitmapsEqual(whiteBGImage2)) { windowImage = HelpersLib.GraphicsHelper.Core.ComputeOriginal(whiteBGImage, blackBGImage); } else { DebugHelper.WriteLine("Detected animated image => cannot compute transparency"); form.Close(); Application.DoEvents(); Image result = new Bitmap(whiteBGImage.Width, whiteBGImage.Height, PixelFormat.Format32bppArgb); using (Graphics g = Graphics.FromImage(result)) { // Redraw the image on a black background to avoid transparent pixels artifacts g.Clear(Color.Black); g.DrawImage(whiteBGImage, 0, 0); } windowImage = result; } blackBGImage.Dispose(); whiteBGImage.Dispose(); whiteBGImage2.Dispose(); } NativeMethods.DwmUnregisterThumbnail(thumb); } return(windowImage); }