diff --git a/src/Greenshot.PerformanceTests/Capture/ScreenCapture.cs b/src/Greenshot.Addons/Core/GdiScreenCapture.cs
similarity index 64%
rename from src/Greenshot.PerformanceTests/Capture/ScreenCapture.cs
rename to src/Greenshot.Addons/Core/GdiScreenCapture.cs
index ae1c3d080..46aaa03ed 100644
--- a/src/Greenshot.PerformanceTests/Capture/ScreenCapture.cs
+++ b/src/Greenshot.Addons/Core/GdiScreenCapture.cs
@@ -28,26 +28,26 @@ using System.Drawing;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
-using Dapplo.Log;
using Dapplo.Windows.Common.Structs;
using Dapplo.Windows.Gdi32;
using Dapplo.Windows.Gdi32.Enums;
using Dapplo.Windows.Gdi32.SafeHandles;
using Dapplo.Windows.Gdi32.Structs;
-using Greenshot.Addons.Core;
+using Dapplo.Windows.User32;
#endregion
-namespace Greenshot.PerformanceTests.Capture
+namespace Greenshot.Addons.Core
{
///
- /// The screen Capture code
+ /// This allows us to repeatedly capture the screen via GDI
///
- public class ScreenCapture : IDisposable
+ public class GdiScreenCapture : IDisposable
{
+ private readonly bool _useStretch;
+ private bool _hasFrame;
private readonly SafeWindowDcHandle _desktopDcHandle;
private readonly SafeCompatibleDcHandle _safeCompatibleDcHandle;
- private readonly bool _useStretch;
private readonly SafeDibSectionHandle _safeDibSectionHandle;
private readonly SafeSelectObjectHandle _safeSelectObjectHandle;
@@ -61,10 +61,16 @@ namespace Greenshot.PerformanceTests.Capture
///
public NativeSize DestinationSize { get; }
- public ScreenCapture(NativeRect sourceCaptureBounds, NativeSize? requestedSize = null)
+ ///
+ /// The constructor
+ ///
+ /// NativeRect, optional, with the source area from the screen
+ /// NativeSize, optional, specifying the resulting size
+ public GdiScreenCapture(NativeRect? sourceCaptureBounds = null, NativeSize? requestedSize = null)
{
- SourceRect = sourceCaptureBounds;
+ SourceRect = sourceCaptureBounds ?? DisplayInfo.ScreenBounds;
+ // Check if a size was specified, if this differs we need to stretch / scale
if (requestedSize.HasValue && requestedSize.Value != SourceRect.Size)
{
DestinationSize = requestedSize.Value;
@@ -76,22 +82,30 @@ namespace Greenshot.PerformanceTests.Capture
_useStretch = false;
}
+ // Get a Device Context for the desktop
_desktopDcHandle = SafeWindowDcHandle.FromDesktop();
+
+ // Create a Device Context which is compatible with the desktop Device Context
_safeCompatibleDcHandle = Gdi32Api.CreateCompatibleDC(_desktopDcHandle);
- // Create BitmapInfoHeader for CreateDIBSection
+ // Create BitmapInfoHeader, which is later used in the CreateDIBSection
var bitmapInfoHeader = BitmapInfoHeader.Create(DestinationSize.Width, DestinationSize.Height, 32);
+ // Create a DibSection, a device-independent bitmap (DIB)
+ _safeDibSectionHandle = Gdi32Api.CreateDIBSection(_desktopDcHandle, ref bitmapInfoHeader, DibColors.RgbColors, out _, IntPtr.Zero, 0);
- _safeDibSectionHandle = Gdi32Api.CreateDIBSection(_desktopDcHandle, ref bitmapInfoHeader, DibColors.PalColors, out _, IntPtr.Zero, 0);
-
- // select the bitmap object and store the old handle
+ // select the device-independent bitmap in the device context, storing the previous.
+ // This is needed, so every interaction with the DC will go into the DIB.
_safeSelectObjectHandle = _safeCompatibleDcHandle.SelectObject(_safeDibSectionHandle);
}
+ ///
+ /// Capture a frame from the screen
+ ///
public void CaptureFrame()
{
if (_useStretch)
{
- // capture & blt over (make copy)
+ // capture from source and blt over (make copy) to the DIB (via the DC)
+ // use stretching as the source and destination have different sizes
Gdi32Api.StretchBlt(
_safeCompatibleDcHandle, 0, 0, DestinationSize.Width, DestinationSize.Height, // Destination
_desktopDcHandle, SourceRect.X, SourceRect.Y, SourceRect.Width, SourceRect.Height, // source
@@ -99,32 +113,45 @@ namespace Greenshot.PerformanceTests.Capture
}
else
{
- // capture & blt over (make copy)
+ // capture from source and blt over (make copy) to the DIB (via the DC)
Gdi32Api.BitBlt(
_safeCompatibleDcHandle, 0, 0, DestinationSize.Width, DestinationSize.Height, // Destination
_desktopDcHandle, SourceRect.X, SourceRect.Y, // Source
RasterOperations.SourceCopy | RasterOperations.CaptureBlt);
}
+
+ _hasFrame = true;
}
///
- /// Get the current frame as BitmapSource
+ /// Get the frame, captured with the previous CaptureFrame call, as BitmapSource
///
/// BitmapSource
public BitmapSource CurrentFrameAsBitmapSource()
{
+ if (!_hasFrame)
+ {
+ throw new NotSupportedException("No frame captured.");
+ }
return Imaging.CreateBitmapSourceFromHBitmap(_safeDibSectionHandle.DangerousGetHandle(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
///
- /// Get the current frame as Bitmap
+ /// Get the frame, captured with the previous CaptureFrame call, as
///
/// Bitmap
public Bitmap CurrentFrameAsBitmap()
{
+ if (!_hasFrame)
+ {
+ throw new NotSupportedException("No frame captured.");
+ }
return Image.FromHbitmap(_safeDibSectionHandle.DangerousGetHandle());
}
+ ///
+ /// Dispose all DC, DIB, handles etc
+ ///
public void Dispose()
{
_safeSelectObjectHandle.Dispose();
diff --git a/src/Greenshot.Addons/Core/ImageOutput.cs b/src/Greenshot.Addons/Core/ImageOutput.cs
index 7a01ae02f..360fb4383 100644
--- a/src/Greenshot.Addons/Core/ImageOutput.cs
+++ b/src/Greenshot.Addons/Core/ImageOutput.cs
@@ -573,7 +573,7 @@ namespace Greenshot.Addons.Core
AddTag(bitmapToSave);
// Added for OptiPNG
var processed = false;
- if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(CoreConfiguration.OptimizePNGCommand))
+ if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(CoreConfiguration?.OptimizePNGCommand))
{
processed = ProcessPngImageExternally(bitmapToSave, targetStream);
}
diff --git a/src/Greenshot.Addons/Interfaces/Plugin/SurfaceOutputSettings.cs b/src/Greenshot.Addons/Interfaces/Plugin/SurfaceOutputSettings.cs
index b06c53945..ef312d4f2 100644
--- a/src/Greenshot.Addons/Interfaces/Plugin/SurfaceOutputSettings.cs
+++ b/src/Greenshot.Addons/Interfaces/Plugin/SurfaceOutputSettings.cs
@@ -33,17 +33,24 @@ using Greenshot.Gfx.Effects;
namespace Greenshot.Addons.Interfaces.Plugin
{
+ ///
+ /// This contains the settings for outputting a surface
+ ///
public class SurfaceOutputSettings
{
private bool _disableReduceColors;
private bool _reduceColors;
+ ///
+ /// Constructor
+ ///
+ /// IFileConfiguration
public SurfaceOutputSettings(IFileConfiguration fileConfiguration)
{
_disableReduceColors = false;
- Format = fileConfiguration.OutputFileFormat;
- JPGQuality = fileConfiguration.OutputFileJpegQuality;
- ReduceColors = fileConfiguration.OutputFileReduceColors;
+ Format = fileConfiguration?.OutputFileFormat ?? OutputFormats.png;
+ JPGQuality = fileConfiguration?.OutputFileJpegQuality ?? 80;
+ ReduceColors = fileConfiguration?.OutputFileReduceColors ?? false;
}
public SurfaceOutputSettings(IFileConfiguration fileConfiguration, OutputFormats format) : this(fileConfiguration)
diff --git a/src/Greenshot.PerformanceTests/CapturePerformance.cs b/src/Greenshot.PerformanceTests/CapturePerformance.cs
index ef341917b..82e8c6a3b 100644
--- a/src/Greenshot.PerformanceTests/CapturePerformance.cs
+++ b/src/Greenshot.PerformanceTests/CapturePerformance.cs
@@ -26,7 +26,6 @@ using BenchmarkDotNet.Attributes;
using Dapplo.Windows.Common.Structs;
using Dapplo.Windows.User32;
using Greenshot.Addons.Core;
-using Greenshot.PerformanceTests.Capture;
namespace Greenshot.PerformanceTests
{
@@ -37,9 +36,9 @@ namespace Greenshot.PerformanceTests
public class CapturePerformance
{
// A ScreenCapture which captures the whole screen (multi-monitor)
- private readonly ScreenCapture _screenCapture = new ScreenCapture(DisplayInfo.ScreenBounds);
+ private readonly GdiScreenCapture _screenCapture = new GdiScreenCapture(DisplayInfo.ScreenBounds);
// A ScreenCapture which captures the whole screen (multi-monitor) but with half the destination size, uses stretch-blt
- private readonly ScreenCapture _screenCaptureResized = new ScreenCapture(DisplayInfo.ScreenBounds, new NativeSize(DisplayInfo.ScreenBounds.Width / 2, DisplayInfo.ScreenBounds.Height / 2));
+ private readonly GdiScreenCapture _screenCaptureResized = new GdiScreenCapture(DisplayInfo.ScreenBounds, new NativeSize(DisplayInfo.ScreenBounds.Width / 2, DisplayInfo.ScreenBounds.Height / 2));
///
/// This benchmarks a screen capture which does a lot of additional work
diff --git a/src/Greenshot.Tests/CaptureTests.cs b/src/Greenshot.Tests/CaptureTests.cs
index bbca527c5..d58e74073 100644
--- a/src/Greenshot.Tests/CaptureTests.cs
+++ b/src/Greenshot.Tests/CaptureTests.cs
@@ -75,6 +75,35 @@ namespace Greenshot.Tests
Assert.Equal(2, capture.CaptureElements.Count);
}
+ ///
+ /// Test if multiple captures with GdiScreenCapture work
+ ///
+ [Fact]
+ public void Test_GdiScreenCapture()
+ {
+ using (var gdiScreenCapture = new GdiScreenCapture())
+ {
+ gdiScreenCapture.CaptureFrame();
+ using (var bitmap = gdiScreenCapture.CurrentFrameAsBitmap())
+ {
+ Assert.True(bitmap.Width > 0);
+
+/*
+ // Write the capture to a file, for analysis
+ using (var stream = new FileStream(Path.Combine(Path.GetTempPath(), "test.png"), FileMode.Create, FileAccess.Write))
+ {
+ ImageOutput.SaveToStream(bitmap, null, stream, new SurfaceOutputSettings(null, OutputFormats.png));
+ }
+*/
+ }
+
+ var bitmapSource = gdiScreenCapture.CurrentFrameAsBitmapSource();
+ Assert.True(bitmapSource.Width > 0);
+
+ gdiScreenCapture.CaptureFrame();
+ }
+ }
+
///
/// Test if a capture from a window works
///