private static void SmartResizeWindow(ref ScreenshotTask data, out WindowsRect oldWindowSize) { oldWindowSize = new WindowsRect(0); if ((WindowsApi.GetWindowLong(data.WindowHandle, GWL_STYLE) & WS_SIZEBOX) != WS_SIZEBOX) { return; } var r = new WindowsRect(); WindowsApi.GetWindowRect(data.WindowHandle, ref r); oldWindowSize = r; Bitmap f = CaptureCompositeScreenshot(ref data); if (f != null) { WindowsApi.SetWindowPos(data.WindowHandle, (IntPtr)0, r.Left, r.Top, data.ResizeX - (f.Width - (r.Right - r.Left)), data.ResizeY - (f.Height - (r.Bottom - r.Top)), SWP_SHOWWINDOW); f.Dispose(); } else { WindowsApi.SetWindowPos(data.WindowHandle, (IntPtr)0, r.Left, r.Top, data.ResizeX, data.ResizeY, SWP_SHOWWINDOW); } }
private static unsafe Bitmap CaptureCompositeScreenshot(ref ScreenshotTask data) { Color tmpColor = data.BackgroundColor; if (data.Background == ScreenshotTask.BackgroundType.Transparent || data.Background == ScreenshotTask.BackgroundType.Checkerboard) { tmpColor = Color.White; } var backdrop = new Form { BackColor = tmpColor, FormBorderStyle = FormBorderStyle.None, ShowInTaskbar = false, Opacity = 0 }; // Generate a rectangle with the size of all monitors combined Rectangle totalSize = Rectangle.Empty; foreach (Screen s in Screen.AllScreens) { totalSize = Rectangle.Union(totalSize, s.Bounds); } var rct = new WindowsRect(); if (WindowsApi.DwmGetWindowAttribute(data.WindowHandle, DwmWindowAttribute.ExtendedFrameBounds, ref rct, sizeof(WindowsRect)) != 0) { // DwmGetWindowAttribute() failed, usually means Aero is disabled so we fall back to GetWindowRect() WindowsApi.GetWindowRect(data.WindowHandle, ref rct); } else { // DwmGetWindowAttribute() succeeded // Add a 100px margin for window shadows. Excess transparency is trimmed out later rct.Left -= 100; rct.Right += 100; rct.Top -= 100; rct.Bottom += 100; } // These next 4 checks handle if the window is outside of the visible screen if (rct.Left < totalSize.Left) { rct.Left = totalSize.Left; } if (rct.Right > totalSize.Right) { rct.Right = totalSize.Right; } if (rct.Top < totalSize.Top) { rct.Top = totalSize.Top; } if (rct.Bottom > totalSize.Bottom) { rct.Bottom = totalSize.Bottom; } WindowsApi.ShowWindow(backdrop.Handle, 4); WindowsApi.SetWindowPos(backdrop.Handle, data.WindowHandle, rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top, SWP_NOACTIVATE); backdrop.Opacity = 1; Application.DoEvents(); // Capture screenshot with white background Bitmap whiteShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); if (data.Background == ScreenshotTask.BackgroundType.SolidColor) { backdrop.Dispose(); if (data.CaptureMouse) { DrawCursorToBitmap(whiteShot, new Point(rct.Left, rct.Top)); } Bitmap final = CropEmptyEdges(whiteShot, tmpColor); whiteShot.Dispose(); return(final); } backdrop.BackColor = Color.Black; Application.DoEvents(); // Capture screenshot with black background Bitmap blackShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); backdrop.Dispose(); Bitmap transparentImage = DifferentiateAlpha(whiteShot, blackShot); if (data.CaptureMouse) { DrawCursorToBitmap(transparentImage, new Point(rct.Left, rct.Top)); } transparentImage = CropEmptyEdges(transparentImage, Color.FromArgb(0, 0, 0, 0)); whiteShot.Dispose(); blackShot.Dispose(); if (data.Background == ScreenshotTask.BackgroundType.Checkerboard) { var final = new Bitmap(transparentImage.Width, transparentImage.Height, PixelFormat.Format24bppRgb); Graphics finalGraphics = Graphics.FromImage(final); var brush = new TextureBrush(GenerateChecker(data.CheckerboardSize)); finalGraphics.FillRectangle(brush, finalGraphics.ClipBounds); finalGraphics.DrawImageUnscaled(transparentImage, 0, 0); finalGraphics.Dispose(); transparentImage.Dispose(); return(final); } // Returns a bitmap with transparency, calculated by differentiating the white and black screenshots return(transparentImage); }
private static unsafe Bitmap[] CaptureCompositeScreenshot(ref ScreenshotTask data) { // Generate a rectangle with the size of all monitors combined Rectangle totalSize = Rectangle.Empty; foreach (Screen s in Screen.AllScreens) { totalSize = Rectangle.Union(totalSize, s.Bounds); } var rct = new WindowsRect(); if (WindowsApi.DwmGetWindowAttribute(data.WindowHandle, DwmWindowAttribute.ExtendedFrameBounds, ref rct, sizeof(WindowsRect)) != 0) { // DwmGetWindowAttribute() failed, usually means Aero is disabled so we fall back to GetWindowRect() WindowsApi.GetWindowRect(data.WindowHandle, ref rct); } else { // DwmGetWindowAttribute() succeeded } // Get DPI of the window (this only works properly on Win 10 1607+) but it is not really needed until Win 11 anyway int DPI = 96; try { DPI = WindowsApi.GetDpiForWindow(data.WindowHandle); } catch { } // Adjust margin for DPI double scalingFactor = DPI / 96; int backdropOffset = Convert.ToInt32(100 * scalingFactor); // Add a 100px margin for window shadows. Excess transparency is trimmed out later rct.Left -= backdropOffset; rct.Right += backdropOffset; rct.Top -= backdropOffset; rct.Bottom += backdropOffset; // These next 4 checks handle if the window is outside of the visible screen if (rct.Left < totalSize.Left) { rct.Left = totalSize.Left; } if (rct.Right > totalSize.Right) { rct.Right = totalSize.Right; } if (rct.Top < totalSize.Top) { rct.Top = totalSize.Top; } if (rct.Bottom > totalSize.Bottom) { rct.Bottom = totalSize.Bottom; } // Spawning backdrop // Handling as much as possible in the constructor makes this easier to render, which makes capture less likely to fail on underpowered PCs Color tmpColor = Color.White; var backdrop = new Form { BackColor = tmpColor, FormBorderStyle = FormBorderStyle.None, ShowInTaskbar = false, //Opacity = 0, Size = new Size(rct.Right - rct.Left, rct.Bottom - rct.Top), StartPosition = FormStartPosition.Manual, Location = new Point(rct.Left, rct.Top) }; WindowsApi.ShowWindow(backdrop.Handle, 4); if (!WindowsApi.SetWindowPos(backdrop.Handle, data.WindowHandle, rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top, SWP_NOACTIVATE)) { // We are unable to put backdrop directly behind the window, so we will put it into the foreground and then put the original window on top of it // This likely happens because the program we're trying to capture is running as administrator WindowsApi.SetWindowPos(backdrop.Handle, IntPtr.Zero, rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top, SWP_NOACTIVATE); WindowsApi.SetForegroundWindow(data.WindowHandle).ToString(); } RefreshBackdrop(); // Capture screenshot with white background Bitmap whiteShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); backdrop.BackColor = Color.Black; RefreshBackdrop(); // Capture screenshot with black background Bitmap blackShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); Bitmap transparentImage; Bitmap transparentInactiveImage = null; Bitmap transparentWhiteImage = null; Bitmap transparentWhiteInactiveImage = null; Bitmap transparentMaskImage = null; Bitmap transparentTransparentImage = null; Bitmap transparentTransparentInactiveImage = null; transparentImage = DifferentiateAlpha(whiteShot, blackShot, false); if (data.SaveActiveLight) { transparentWhiteImage = DifferentiateAlpha(whiteShot, blackShot, true); } whiteShot.Dispose(); blackShot.Dispose(); //Capture black mask screenshot if (data.SaveMask) { int minAlpha = 0; bool isCompositing = false; bool ShadowToggled = false; bool ColorToggled = false; try { WindowsApi.DwmIsCompositionEnabled(out isCompositing); } catch (Exception) { //OS doesn't have a supported version of DWM } //We can't disable shadows on Vista without disabling DWM, which would cause the mask to be inaccurate UInt32 ColorizationColor = 0; bool fOpaque = true; if (isCompositing && (VersionHelpers.IsWindowsVista() || VersionHelpers.IsWindows11())) { minAlpha = 254; WindowsApi.DwmGetColorizationColor(out ColorizationColor, out fOpaque); if (fOpaque == false) { WindowsApi.DwmpSetColorization(ColorizationColor, true, 0xFF); ColorToggled = true; } } else if (ShadowEnabled()) { WindowsApi.SystemParametersInfo(SPI_SETDROPSHADOW, 0, false, 0); ShadowToggled = true; } backdrop.BackColor = Color.White; RefreshBackdrop(); Bitmap whiteMaskShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); backdrop.BackColor = Color.Black; RefreshBackdrop(); Bitmap blackMaskShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); transparentMaskImage = CreateMask(DifferentiateAlpha(whiteMaskShot, blackMaskShot, false), minAlpha); if (ShadowToggled) { WindowsApi.SystemParametersInfo(SPI_SETDROPSHADOW, 0, true, 0); } if (ColorToggled) { WindowsApi.DwmpSetColorization(ColorizationColor, fOpaque, 0xFF); } whiteMaskShot.Dispose(); blackMaskShot.Dispose(); } //Capture active fully transparent if (data.SaveActiveTransparent) { try { //win 7 WindowsApi.DWM_COLORIZATION_PARAMS parameters, originalParameters = new WindowsApi.DWM_COLORIZATION_PARAMS(); //win vista UInt32 ColorizationColor = 0; bool fOpaque = true; if (VersionHelpers.IsWindowsVista()) { WindowsApi.DwmGetColorizationColor(out ColorizationColor, out fOpaque); if (fOpaque == false) { WindowsApi.DwmpSetColorization(0xFFFFFF, false, 0xFF); } } else { WindowsApi.DwmGetColorizationParameters(out parameters); WindowsApi.DwmGetColorizationParameters(out originalParameters); //Set custom fully transparent parameters parameters.clrAfterGlowBalance = 0; parameters.clrBlurBalance = 100; parameters.nIntensity = 0; // Call the DwmSetColorizationParameters to make the change take effect. WindowsApi.DwmSetColorizationParameters(ref parameters, false); } backdrop.BackColor = Color.White; RefreshBackdrop(); Bitmap whiteTransparentShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); backdrop.BackColor = Color.Black; RefreshBackdrop(); Bitmap blackTransparentShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); transparentTransparentImage = DifferentiateAlpha(whiteTransparentShot, blackTransparentShot, false); whiteTransparentShot.Dispose(); blackTransparentShot.Dispose(); if (VersionHelpers.IsWindowsVista()) { WindowsApi.DwmpSetColorization(ColorizationColor, fOpaque, 0xFF); } else { WindowsApi.DwmSetColorizationParameters(ref originalParameters, false); } } catch (Exception) { transparentTransparentImage = new Bitmap(transparentImage); } } //Show form to steal focus var emptyForm = new Form { FormBorderStyle = FormBorderStyle.None, ShowInTaskbar = false, Opacity = 0, }; WindowsApi.ShowWindow(emptyForm.Handle, 5); WindowsApi.SetWindowPos(emptyForm.Handle, data.WindowHandle, rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top, 0); WindowsApi.SetForegroundWindow(emptyForm.Handle); // Capture inactive screenshots if (data.SaveInactiveDark || data.SaveInactiveLight) { backdrop.BackColor = Color.White; RefreshBackdrop(); // Capture inactive screenshot with white background Bitmap whiteInactiveShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); backdrop.BackColor = Color.Black; RefreshBackdrop(); // Capture inactive screenshot with black background Bitmap blackInactiveShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); if (data.SaveInactiveDark) { transparentInactiveImage = DifferentiateAlpha(whiteInactiveShot, blackInactiveShot, false); } if (data.SaveInactiveLight) { transparentWhiteInactiveImage = DifferentiateAlpha(whiteInactiveShot, blackInactiveShot, true); } whiteInactiveShot.Dispose(); blackInactiveShot.Dispose(); } //Capture inactive fully transparent if (data.SaveInactiveTransparent) { try { //Get original colorization parameters WindowsApi.DWM_COLORIZATION_PARAMS parameters, originalParameters; WindowsApi.DwmGetColorizationParameters(out parameters); WindowsApi.DwmGetColorizationParameters(out originalParameters); //Set custom fully transparent parameters parameters.clrAfterGlowBalance = 0; parameters.clrBlurBalance = 100; parameters.nIntensity = 0; // Call the DwmSetColorizationParameters to make the change take effect. WindowsApi.DwmSetColorizationParameters(ref parameters, false); backdrop.BackColor = Color.White; RefreshBackdrop(); Bitmap whiteTransparentInactiveShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); backdrop.BackColor = Color.Black; RefreshBackdrop(); Bitmap blackTransparentInactiveShot = CaptureScreenRegion(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); transparentTransparentInactiveImage = DifferentiateAlpha(whiteTransparentInactiveShot, blackTransparentInactiveShot, false); whiteTransparentInactiveShot.Dispose(); blackTransparentInactiveShot.Dispose(); WindowsApi.DwmSetColorizationParameters(ref originalParameters, false); } catch (Exception) { transparentTransparentInactiveImage = new Bitmap(transparentInactiveImage); } } backdrop.Dispose(); emptyForm.Dispose(); if (data.CaptureMouse) { DrawCursorToBitmap(transparentImage, new Point(rct.Left, rct.Top)); } Bitmap[] final = CropEmptyEdges(new[] { transparentImage, transparentInactiveImage, transparentWhiteImage, transparentWhiteInactiveImage, transparentMaskImage, transparentTransparentImage, transparentTransparentInactiveImage }, Color.FromArgb(0, 0, 0, 0), ref data); // Returns a bitmap with transparency, calculated by differentiating the white and black screenshots return(final); }