mirror of
https://github.com/greenshot/greenshot
synced 2025-08-23 06:36:20 -07:00
Some small fixes preventing NREs and wrote a capture test.
This commit is contained in:
parent
08e68ad61d
commit
cdf3428758
5 changed files with 85 additions and 23 deletions
|
@ -28,26 +28,26 @@ using System.Drawing;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using Dapplo.Log;
|
|
||||||
using Dapplo.Windows.Common.Structs;
|
using Dapplo.Windows.Common.Structs;
|
||||||
using Dapplo.Windows.Gdi32;
|
using Dapplo.Windows.Gdi32;
|
||||||
using Dapplo.Windows.Gdi32.Enums;
|
using Dapplo.Windows.Gdi32.Enums;
|
||||||
using Dapplo.Windows.Gdi32.SafeHandles;
|
using Dapplo.Windows.Gdi32.SafeHandles;
|
||||||
using Dapplo.Windows.Gdi32.Structs;
|
using Dapplo.Windows.Gdi32.Structs;
|
||||||
using Greenshot.Addons.Core;
|
using Dapplo.Windows.User32;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
namespace Greenshot.PerformanceTests.Capture
|
namespace Greenshot.Addons.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The screen Capture code
|
/// This allows us to repeatedly capture the screen via GDI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ScreenCapture : IDisposable
|
public class GdiScreenCapture : IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly bool _useStretch;
|
||||||
|
private bool _hasFrame;
|
||||||
private readonly SafeWindowDcHandle _desktopDcHandle;
|
private readonly SafeWindowDcHandle _desktopDcHandle;
|
||||||
private readonly SafeCompatibleDcHandle _safeCompatibleDcHandle;
|
private readonly SafeCompatibleDcHandle _safeCompatibleDcHandle;
|
||||||
private readonly bool _useStretch;
|
|
||||||
private readonly SafeDibSectionHandle _safeDibSectionHandle;
|
private readonly SafeDibSectionHandle _safeDibSectionHandle;
|
||||||
private readonly SafeSelectObjectHandle _safeSelectObjectHandle;
|
private readonly SafeSelectObjectHandle _safeSelectObjectHandle;
|
||||||
|
|
||||||
|
@ -61,10 +61,16 @@ namespace Greenshot.PerformanceTests.Capture
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NativeSize DestinationSize { get; }
|
public NativeSize DestinationSize { get; }
|
||||||
|
|
||||||
public ScreenCapture(NativeRect sourceCaptureBounds, NativeSize? requestedSize = null)
|
/// <summary>
|
||||||
|
/// The constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceCaptureBounds">NativeRect, optional, with the source area from the screen</param>
|
||||||
|
/// <param name="requestedSize">NativeSize, optional, specifying the resulting size</param>
|
||||||
|
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)
|
if (requestedSize.HasValue && requestedSize.Value != SourceRect.Size)
|
||||||
{
|
{
|
||||||
DestinationSize = requestedSize.Value;
|
DestinationSize = requestedSize.Value;
|
||||||
|
@ -76,22 +82,30 @@ namespace Greenshot.PerformanceTests.Capture
|
||||||
_useStretch = false;
|
_useStretch = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a Device Context for the desktop
|
||||||
_desktopDcHandle = SafeWindowDcHandle.FromDesktop();
|
_desktopDcHandle = SafeWindowDcHandle.FromDesktop();
|
||||||
|
|
||||||
|
// Create a Device Context which is compatible with the desktop Device Context
|
||||||
_safeCompatibleDcHandle = Gdi32Api.CreateCompatibleDC(_desktopDcHandle);
|
_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);
|
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 device-independent bitmap in the device context, storing the previous.
|
||||||
|
// This is needed, so every interaction with the DC will go into the DIB.
|
||||||
// select the bitmap object and store the old handle
|
|
||||||
_safeSelectObjectHandle = _safeCompatibleDcHandle.SelectObject(_safeDibSectionHandle);
|
_safeSelectObjectHandle = _safeCompatibleDcHandle.SelectObject(_safeDibSectionHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Capture a frame from the screen
|
||||||
|
/// </summary>
|
||||||
public void CaptureFrame()
|
public void CaptureFrame()
|
||||||
{
|
{
|
||||||
if (_useStretch)
|
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(
|
Gdi32Api.StretchBlt(
|
||||||
_safeCompatibleDcHandle, 0, 0, DestinationSize.Width, DestinationSize.Height, // Destination
|
_safeCompatibleDcHandle, 0, 0, DestinationSize.Width, DestinationSize.Height, // Destination
|
||||||
_desktopDcHandle, SourceRect.X, SourceRect.Y, SourceRect.Width, SourceRect.Height, // source
|
_desktopDcHandle, SourceRect.X, SourceRect.Y, SourceRect.Width, SourceRect.Height, // source
|
||||||
|
@ -99,32 +113,45 @@ namespace Greenshot.PerformanceTests.Capture
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// capture & blt over (make copy)
|
// capture from source and blt over (make copy) to the DIB (via the DC)
|
||||||
Gdi32Api.BitBlt(
|
Gdi32Api.BitBlt(
|
||||||
_safeCompatibleDcHandle, 0, 0, DestinationSize.Width, DestinationSize.Height, // Destination
|
_safeCompatibleDcHandle, 0, 0, DestinationSize.Width, DestinationSize.Height, // Destination
|
||||||
_desktopDcHandle, SourceRect.X, SourceRect.Y, // Source
|
_desktopDcHandle, SourceRect.X, SourceRect.Y, // Source
|
||||||
RasterOperations.SourceCopy | RasterOperations.CaptureBlt);
|
RasterOperations.SourceCopy | RasterOperations.CaptureBlt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_hasFrame = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the current frame as BitmapSource
|
/// Get the frame, captured with the previous CaptureFrame call, as BitmapSource
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>BitmapSource</returns>
|
/// <returns>BitmapSource</returns>
|
||||||
public BitmapSource CurrentFrameAsBitmapSource()
|
public BitmapSource CurrentFrameAsBitmapSource()
|
||||||
{
|
{
|
||||||
|
if (!_hasFrame)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("No frame captured.");
|
||||||
|
}
|
||||||
return Imaging.CreateBitmapSourceFromHBitmap(_safeDibSectionHandle.DangerousGetHandle(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
|
return Imaging.CreateBitmapSourceFromHBitmap(_safeDibSectionHandle.DangerousGetHandle(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the current frame as Bitmap
|
/// Get the frame, captured with the previous CaptureFrame call, as
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Bitmap</returns>
|
/// <returns>Bitmap</returns>
|
||||||
public Bitmap CurrentFrameAsBitmap()
|
public Bitmap CurrentFrameAsBitmap()
|
||||||
{
|
{
|
||||||
|
if (!_hasFrame)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("No frame captured.");
|
||||||
|
}
|
||||||
return Image.FromHbitmap(_safeDibSectionHandle.DangerousGetHandle());
|
return Image.FromHbitmap(_safeDibSectionHandle.DangerousGetHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose all DC, DIB, handles etc
|
||||||
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_safeSelectObjectHandle.Dispose();
|
_safeSelectObjectHandle.Dispose();
|
|
@ -573,7 +573,7 @@ namespace Greenshot.Addons.Core
|
||||||
AddTag(bitmapToSave);
|
AddTag(bitmapToSave);
|
||||||
// Added for OptiPNG
|
// Added for OptiPNG
|
||||||
var processed = false;
|
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);
|
processed = ProcessPngImageExternally(bitmapToSave, targetStream);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,17 +33,24 @@ using Greenshot.Gfx.Effects;
|
||||||
|
|
||||||
namespace Greenshot.Addons.Interfaces.Plugin
|
namespace Greenshot.Addons.Interfaces.Plugin
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This contains the settings for outputting a surface
|
||||||
|
/// </summary>
|
||||||
public class SurfaceOutputSettings
|
public class SurfaceOutputSettings
|
||||||
{
|
{
|
||||||
private bool _disableReduceColors;
|
private bool _disableReduceColors;
|
||||||
private bool _reduceColors;
|
private bool _reduceColors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileConfiguration">IFileConfiguration</param>
|
||||||
public SurfaceOutputSettings(IFileConfiguration fileConfiguration)
|
public SurfaceOutputSettings(IFileConfiguration fileConfiguration)
|
||||||
{
|
{
|
||||||
_disableReduceColors = false;
|
_disableReduceColors = false;
|
||||||
Format = fileConfiguration.OutputFileFormat;
|
Format = fileConfiguration?.OutputFileFormat ?? OutputFormats.png;
|
||||||
JPGQuality = fileConfiguration.OutputFileJpegQuality;
|
JPGQuality = fileConfiguration?.OutputFileJpegQuality ?? 80;
|
||||||
ReduceColors = fileConfiguration.OutputFileReduceColors;
|
ReduceColors = fileConfiguration?.OutputFileReduceColors ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SurfaceOutputSettings(IFileConfiguration fileConfiguration, OutputFormats format) : this(fileConfiguration)
|
public SurfaceOutputSettings(IFileConfiguration fileConfiguration, OutputFormats format) : this(fileConfiguration)
|
||||||
|
|
|
@ -26,7 +26,6 @@ using BenchmarkDotNet.Attributes;
|
||||||
using Dapplo.Windows.Common.Structs;
|
using Dapplo.Windows.Common.Structs;
|
||||||
using Dapplo.Windows.User32;
|
using Dapplo.Windows.User32;
|
||||||
using Greenshot.Addons.Core;
|
using Greenshot.Addons.Core;
|
||||||
using Greenshot.PerformanceTests.Capture;
|
|
||||||
|
|
||||||
namespace Greenshot.PerformanceTests
|
namespace Greenshot.PerformanceTests
|
||||||
{
|
{
|
||||||
|
@ -37,9 +36,9 @@ namespace Greenshot.PerformanceTests
|
||||||
public class CapturePerformance
|
public class CapturePerformance
|
||||||
{
|
{
|
||||||
// A ScreenCapture which captures the whole screen (multi-monitor)
|
// 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
|
// 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));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This benchmarks a screen capture which does a lot of additional work
|
/// This benchmarks a screen capture which does a lot of additional work
|
||||||
|
|
|
@ -75,6 +75,35 @@ namespace Greenshot.Tests
|
||||||
Assert.Equal(2, capture.CaptureElements.Count);
|
Assert.Equal(2, capture.CaptureElements.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test if multiple captures with GdiScreenCapture work
|
||||||
|
/// </summary>
|
||||||
|
[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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test if a capture from a window works
|
/// Test if a capture from a window works
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue