Improved the UnmanagedBitmap, and all IBitmapWithNativeSupport to support both Bitmap and BitmapSource with minimal copying. This should make integrations of newer code a lot easier.

This commit is contained in:
Robin 2019-03-28 13:42:31 +01:00
commit ee779cc97b
No known key found for this signature in database
GPG key ID: CBBB6557491B1140
15 changed files with 330 additions and 68 deletions

View file

@ -48,8 +48,8 @@
<DebugSymbols>True</DebugSymbols> <DebugSymbols>True</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="!$(MSBuildProjectName.Contains('Tests')) And $(MSBuildProjectName.StartsWith('Greenshot'))"> <!--ItemGroup Condition="!$(MSBuildProjectName.Contains('Tests')) And $(MSBuildProjectName.StartsWith('Greenshot'))">
<PackageReference Include="Nerdbank.GitVersioning" Version="2.3.136"> <PackageReference Include="Nerdbank.GitVersioning" Version="2.3.138">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>
@ -57,7 +57,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup-->
<Target Name="PostBuild" BeforeTargets="PostBuildEvent" Condition="$(MSBuildProjectName.Contains('Addon.')) And !$(MSBuildProjectName.Contains('Test')) And !$(MSBuildProjectName.Contains('Demo')) And ($(OsProductName.Contains('Windows 10')) Or (!$(OsProductName.Contains('Windows 10')) And !$(MSBuildProjectName.Contains('Win10'))))"> <Target Name="PostBuild" BeforeTargets="PostBuildEvent" Condition="$(MSBuildProjectName.Contains('Addon.')) And !$(MSBuildProjectName.Contains('Test')) And !$(MSBuildProjectName.Contains('Demo')) And ($(OsProductName.Contains('Windows 10')) Or (!$(OsProductName.Contains('Windows 10')) And !$(MSBuildProjectName.Contains('Win10'))))">
<Exec Command=" <Exec Command="

View file

@ -0,0 +1,138 @@
// Greenshot - a free and open source screenshot tool
// Copyright (C) 2007-2018 Thomas Braun, Jens Klingen, Robin Krom
//
// For more information see: http://getgreenshot.org/
// The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
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 Dapplo.Windows.User32;
using Greenshot.Gfx;
using Greenshot.Gfx.Structs;
namespace Greenshot.Addons.Core
{
/// <summary>
/// This allows us to repeatedly capture the screen via GDI
/// </summary>
public class BitmapScreenCapture : IDisposable
{
private readonly bool _useStretch;
private bool _hasFrame;
private readonly SafeWindowDcHandle _desktopDcHandle;
private readonly SafeCompatibleDcHandle _safeCompatibleDcHandle;
private readonly SafeDibSectionHandle _safeDibSectionHandle;
private readonly SafeSelectObjectHandle _safeSelectObjectHandle;
private readonly IBitmapWithNativeSupport _bitmap;
/// <summary>
/// Return the source rectangle
/// </summary>
public NativeRect SourceRect { get; }
/// <summary>
/// Return the source rectangle
/// </summary>
public NativeSize DestinationSize { get; }
/// <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 BitmapScreenCapture(NativeRect? sourceCaptureBounds = null, NativeSize? requestedSize = null)
{
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;
_useStretch = true;
}
else
{
DestinationSize = SourceRect.Size;
_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, 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 var bits, 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.
_safeSelectObjectHandle = _safeCompatibleDcHandle.SelectObject(_safeDibSectionHandle);
// Create a wrapper around the bitmap data
_bitmap = new UnmanagedBitmap<Bgr32>(bits, DestinationSize.Width, DestinationSize.Height);
}
/// <summary>
/// Capture a frame from the screen
/// </summary>
public void CaptureFrame()
{
if (_useStretch)
{
// 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
RasterOperations.SourceCopy | RasterOperations.CaptureBlt);
}
else
{
// 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;
}
/// <summary>
/// Get the frame, captured with the previous CaptureFrame call
/// </summary>
/// <returns>IBitmapWithNativeSupport</returns>
public IBitmapWithNativeSupport CurrentFrameAsBitmap() => _hasFrame ? _bitmap : null;
/// <summary>
/// Dispose all DC, DIB, handles etc
/// </summary>
public void Dispose()
{
_safeSelectObjectHandle.Dispose();
_safeDibSectionHandle.Dispose();
_safeCompatibleDcHandle.Dispose();
_desktopDcHandle.Dispose();
}
}
}

View file

@ -346,8 +346,7 @@ namespace Greenshot.Addons.Core
} }
// get a .NET image object for it // get a .NET image object for it
// A suggestion for the "A generic error occurred in GDI+." E_FAIL/0<>80004005 error is to re-try... // A suggestion for the "A generic error occurred in GDI+." E_FAIL/0x80004005 error is to re-try...
var success = false;
ExternalException exception = null; ExternalException exception = null;
for (var i = 0; i < 3; i++) for (var i = 0; i < 3; i++)
{ {
@ -408,9 +407,6 @@ namespace Greenshot.Addons.Core
// TODO: Optimize? // TODO: Optimize?
return BitmapWrapper.FromBitmap(Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle())); return BitmapWrapper.FromBitmap(Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle()));
} }
// We got through the capture without exception
success = true;
break;
} }
catch (ExternalException ee) catch (ExternalException ee)
{ {
@ -418,8 +414,6 @@ namespace Greenshot.Addons.Core
exception = ee; exception = ee;
} }
} }
if (!success)
{
Log.Error().WriteLine(null, "Still couldn't create Bitmap!"); Log.Error().WriteLine(null, "Still couldn't create Bitmap!");
if (exception != null) if (exception != null)
{ {
@ -428,7 +422,6 @@ namespace Greenshot.Addons.Core
} }
} }
} }
}
return null; return null;
} }

View file

@ -19,6 +19,8 @@
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Greenshot.Gfx namespace Greenshot.Gfx
{ {
@ -49,7 +51,7 @@ namespace Greenshot.Gfx
public int Width => _bitmap.Width; public int Width => _bitmap.Width;
/// <inheritdoc /> /// <inheritdoc />
public PixelFormat PixelFormat => _bitmap.PixelFormat; public System.Drawing.Imaging.PixelFormat PixelFormat => _bitmap.PixelFormat;
/// <inheritdoc /> /// <inheritdoc />
public float HorizontalResolution => _bitmap.HorizontalResolution; public float HorizontalResolution => _bitmap.HorizontalResolution;
@ -60,6 +62,24 @@ namespace Greenshot.Gfx
/// <inheritdoc /> /// <inheritdoc />
public Bitmap NativeBitmap => _bitmap; public Bitmap NativeBitmap => _bitmap;
/// <inheritdoc />
public BitmapSource NativeBitmapSource
{
get
{
var bitmapData = _bitmap.LockBits(new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), ImageLockMode.ReadOnly, _bitmap.PixelFormat);
try
{
return BitmapSource.Create(bitmapData.Width, bitmapData.Height, _bitmap.HorizontalResolution, _bitmap.VerticalResolution, PixelFormats.Bgr24, null, bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
}
finally
{
_bitmap.UnlockBits(bitmapData);
}
}
}
/// <inheritdoc />
public Size Size => new Size(Width, Height); public Size Size => new Size(Width, Height);
/// <summary> /// <summary>

View file

@ -87,8 +87,7 @@ namespace Greenshot.Gfx.FastBitmap
/// <param name="horizontalResolution">float for horizontal DPI</param> /// <param name="horizontalResolution">float for horizontal DPI</param>
/// <param name="verticalResolution">float for horizontal DPI</param> /// <param name="verticalResolution">float for horizontal DPI</param>
/// <returns>IFastBitmap</returns> /// <returns>IFastBitmap</returns>
public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat = PixelFormat.DontCare, Color? backgroundColor = null, float horizontalResolution = 96f, public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat = PixelFormat.DontCare, Color? backgroundColor = null, float horizontalResolution = 96f, float verticalResolution = 96f)
float verticalResolution = 96f)
{ {
var destination = BitmapFactory.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, horizontalResolution, verticalResolution); var destination = BitmapFactory.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, horizontalResolution, verticalResolution);
var fastBitmap = Create(destination); var fastBitmap = Create(destination);

View file

@ -20,6 +20,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Windows.Media.Imaging;
namespace Greenshot.Gfx namespace Greenshot.Gfx
{ {
@ -61,10 +62,16 @@ namespace Greenshot.Gfx
public interface IBitmapWithNativeSupport : IBitmap public interface IBitmapWithNativeSupport : IBitmap
{ {
/// <summary> /// <summary>
/// Underlying image, or an on demand rendered version with different attributes as the original /// Retrieves a Bitmap which only can be used as long as the underlying implementation is not disposed.
/// Do not dispose this.
/// </summary> /// </summary>
Bitmap NativeBitmap { get; } Bitmap NativeBitmap { get; }
/// <summary>
/// Retrieves a BitmapSource which only can be used as long as the underlying implementation is not disposed.
/// </summary>
BitmapSource NativeBitmapSource { get; }
/// <summary> /// <summary>
/// Return the Size /// Return the Size
/// </summary> /// </summary>

View file

@ -149,12 +149,17 @@ namespace Greenshot.Gfx.Quantizer
} }
} }
} }
/// <inheritdoc/>
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
} }
/// <summary>
/// Dispose implementation
/// </summary>
/// <param name="disposing">bool</param>
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!disposing) if (!disposing)
@ -308,7 +313,7 @@ namespace Greenshot.Gfx.Quantizer
_tag = new byte[Maxvolume]; _tag = new byte[Maxvolume];
// precalculates lookup tables // pre-calculates lookup tables
for (var k = 0; k < allowedColorCount; ++k) for (var k = 0; k < allowedColorCount; ++k)
{ {
Mark(_cubes[k], k, _tag); Mark(_cubes[k], k, _tag);

View file

@ -20,7 +20,11 @@
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Svg; using Svg;
using Color = System.Drawing.Color;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
namespace Greenshot.Gfx namespace Greenshot.Gfx
@ -111,6 +115,24 @@ namespace Greenshot.Gfx
/// </summary> /// </summary>
public Bitmap NativeBitmap => GenerateNativeBitmap(); public Bitmap NativeBitmap => GenerateNativeBitmap();
/// <inheritdoc />
public BitmapSource NativeBitmapSource
{
get
{
GenerateNativeBitmap();
var bitmapData = _imageClone.LockBits(new Rectangle(0, 0, _imageClone.Width, _imageClone.Height), ImageLockMode.ReadOnly, _imageClone.PixelFormat);
try
{
return BitmapSource.Create(bitmapData.Width, bitmapData.Height, _imageClone.HorizontalResolution, _imageClone.VerticalResolution, PixelFormats.Bgr24, null, bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
}
finally
{
_imageClone.UnlockBits(bitmapData);
}
}
}
private Bitmap GenerateNativeBitmap() private Bitmap GenerateNativeBitmap()
{ {
if (_imageClone?.Height == Height && _imageClone?.Width == Width) if (_imageClone?.Height == Height && _imageClone?.Width == Width)
@ -128,11 +150,6 @@ namespace Greenshot.Gfx
public Size Size => new Size(Width, Height); public Size Size => new Size(Width, Height);
public void DisposeNativeBitmap(Bitmap nativeBitmap)
{
// do nothing, we need this
}
/// <summary> /// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary> /// </summary>

View file

@ -19,10 +19,12 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Greenshot.Gfx.Structs; using Greenshot.Gfx.Structs;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
namespace Greenshot.Gfx namespace Greenshot.Gfx
{ {
@ -34,8 +36,13 @@ namespace Greenshot.Gfx
{ {
private readonly float _horizontalPixelsPerInch; private readonly float _horizontalPixelsPerInch;
private readonly float _verticalPixelsPerInch; private readonly float _verticalPixelsPerInch;
// Bytes per line
private readonly int _stride; private readonly int _stride;
// IntPtr to a global handle with the bitmap data, this will be freed on dispose
private IntPtr _hGlobal; private IntPtr _hGlobal;
// IntPtr to the bits of the bitmap, if using the constructor with the IntPtr this will not be freed
private readonly IntPtr _bits;
// Optionally created when the user calls NativeBitmap
private Bitmap _nativeBitmap; private Bitmap _nativeBitmap;
/// <summary> /// <summary>
@ -64,10 +71,30 @@ namespace Greenshot.Gfx
Height = height; Height = height;
_stride = bytesPerPixel * width; _stride = bytesPerPixel * width;
var bytesAllocated = height * _stride; var bytesAllocated = height * _stride;
_hGlobal = Marshal.AllocHGlobal(bytesAllocated); _bits = _hGlobal = Marshal.AllocHGlobal(bytesAllocated);
GC.AddMemoryPressure(bytesAllocated); GC.AddMemoryPressure(bytesAllocated);
} }
/// <summary>
/// The constructor for the UnmanagedBitmap with already initialized bits
/// </summary>
/// <param name="bits">IntPtr to the bits, this will not be freed</param>
/// <param name="width">int</param>
/// <param name="height">int</param>
/// <param name="horizontalPixelsPerInch">float</param>
/// <param name="verticalPixelsPerInch">float</param>
public UnmanagedBitmap(IntPtr bits, int width, int height, float horizontalPixelsPerInch = 0.96f, float verticalPixelsPerInch = 0.96f)
{
_horizontalPixelsPerInch = horizontalPixelsPerInch;
_verticalPixelsPerInch = verticalPixelsPerInch;
var bytesPerPixel = Marshal.SizeOf<TPixelLayout>();
Width = width;
Height = height;
_stride = bytesPerPixel * width;
_bits = bits;
}
/// <summary> /// <summary>
/// This returns a span with a the pixels for the specified line /// This returns a span with a the pixels for the specified line
/// </summary> /// </summary>
@ -79,7 +106,7 @@ namespace Greenshot.Gfx
{ {
unsafe unsafe
{ {
var pixelRowPointer = _hGlobal + (y * _stride); var pixelRowPointer = _bits + (y * _stride);
return new Span<TPixelLayout>(pixelRowPointer.ToPointer(), Width); return new Span<TPixelLayout>(pixelRowPointer.ToPointer(), Width);
} }
} }
@ -96,7 +123,7 @@ namespace Greenshot.Gfx
{ {
unsafe unsafe
{ {
return new Span<TPixelLayout>(_hGlobal.ToPointer(), Height * Width); return new Span<TPixelLayout>(_bits.ToPointer(), Height * Width);
} }
} }
} }
@ -127,6 +154,25 @@ namespace Greenshot.Gfx
} }
} }
public System.Windows.Media.PixelFormat WpfPixelFormat
{
get
{
TPixelLayout empty = default;
switch (empty)
{
case Bgr24 _:
return PixelFormats.Bgr24;
case Bgra32 _:
return PixelFormats.Bgra32;
case Bgr32 _:
return PixelFormats.Bgr32;
default:
throw new NotSupportedException("Unknown pixel format");
}
}
}
/// <inheritdoc /> /// <inheritdoc />
public float VerticalResolution => _verticalPixelsPerInch; public float VerticalResolution => _verticalPixelsPerInch;
@ -141,7 +187,19 @@ namespace Greenshot.Gfx
{ {
get get
{ {
return _nativeBitmap ??= new Bitmap(Width, Height, _stride, PixelFormat, _hGlobal); return _nativeBitmap ??= new Bitmap(Width, Height, _stride, PixelFormat, _bits);
}
}
/// <summary>
/// Convert this to a real bitmap
/// </summary>
/// <returns>BitmapSource</returns>
public BitmapSource NativeBitmapSource
{
get
{
return BitmapSource.Create(Width, Height, HorizontalResolution, VerticalResolution, WpfPixelFormat, null, _bits, _stride * Height, _stride);
} }
} }

View file

@ -18,6 +18,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
using System; using System;
using System.Drawing.Imaging;
using System.IO; using System.IO;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Dapplo.Log; using Dapplo.Log;
@ -32,7 +33,7 @@ namespace Greenshot.PerformanceTests
/// <summary> /// <summary>
/// This defines the benchmarks which can be done /// This defines the benchmarks which can be done
/// </summary> /// </summary>
[MinColumn, MaxColumn, MemoryDiagnoser] [MinColumn, MaxColumn, MemoryDiagnoser, CoreJob, ClrJob]
public class CapturePerformance public class CapturePerformance
{ {
private static readonly LogSource Log = new LogSource(); private static readonly LogSource Log = new LogSource();
@ -40,6 +41,8 @@ namespace Greenshot.PerformanceTests
private GdiScreenCapture _screenCapture; private GdiScreenCapture _screenCapture;
// 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 GdiScreenCapture _screenCaptureResized; private GdiScreenCapture _screenCaptureResized;
private BitmapScreenCapture _screenBitmapCapture;
private BitmapScreenCapture _screenBitmapCaptureResized;
private AviWriter _aviWriter; private AviWriter _aviWriter;
private IAviVideoStream _aviVideoStream; private IAviVideoStream _aviVideoStream;
@ -47,8 +50,10 @@ namespace Greenshot.PerformanceTests
public void Setup() public void Setup()
{ {
_screenCapture = new GdiScreenCapture(DisplayInfo.ScreenBounds); _screenCapture = new GdiScreenCapture(DisplayInfo.ScreenBounds);
_screenBitmapCapture = new BitmapScreenCapture();
var resizedSize = new NativeSize(DisplayInfo.ScreenBounds.Width / 2, DisplayInfo.ScreenBounds.Height / 2); var resizedSize = new NativeSize(DisplayInfo.ScreenBounds.Width / 2, DisplayInfo.ScreenBounds.Height / 2);
_screenCaptureResized = new GdiScreenCapture(DisplayInfo.ScreenBounds, resizedSize); _screenCaptureResized = new GdiScreenCapture(DisplayInfo.ScreenBounds, resizedSize);
_screenBitmapCaptureResized = new BitmapScreenCapture(DisplayInfo.ScreenBounds, resizedSize);
var aviFile = Path.Combine(Path.GetTempPath(), @"test.avi"); var aviFile = Path.Combine(Path.GetTempPath(), @"test.avi");
Log.Info().WriteLine("Writing AVI to {0}", aviFile); Log.Info().WriteLine("Writing AVI to {0}", aviFile);
@ -61,12 +66,13 @@ namespace Greenshot.PerformanceTests
EmitIndex1 = true EmitIndex1 = true
}; };
_aviVideoStream = _aviWriter.AddVideoStream(resizedSize.Width, resizedSize.Height, BitsPerPixel.Bpp24); _aviVideoStream = _aviWriter.AddVideoStream(resizedSize.Width, resizedSize.Height, BitsPerPixel.Bpp24);
} }
/// <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
/// </summary> /// </summary>
[Benchmark] //[Benchmark]
public void Capture() public void Capture()
{ {
using (var capture = WindowCapture.CaptureScreen()) using (var capture = WindowCapture.CaptureScreen())
@ -91,6 +97,15 @@ namespace Greenshot.PerformanceTests
_screenCapture.CaptureFrame(); _screenCapture.CaptureFrame();
} }
/// <summary>
/// Capture the screen directly into a bitmap
/// </summary>
[Benchmark]
public void CaptureBitmap()
{
_screenBitmapCapture.CaptureFrame();
}
/// <summary> /// <summary>
/// Capture the screen with buffered settings, but resized (smaller) destination /// Capture the screen with buffered settings, but resized (smaller) destination
/// </summary> /// </summary>
@ -100,6 +115,17 @@ namespace Greenshot.PerformanceTests
_screenCaptureResized.CaptureFrame(); _screenCaptureResized.CaptureFrame();
} }
/// <summary>
/// Capture the screen with buffered settings, but resized (smaller) destination
/// </summary>
//[Benchmark]
public void CapturebitmapResized()
{
_screenBitmapCaptureResized.CaptureFrame();
}
/// <summary> /// <summary>
/// Capture the screen with buffered settings, but resized (smaller) destination /// Capture the screen with buffered settings, but resized (smaller) destination
/// </summary> /// </summary>
@ -115,6 +141,8 @@ namespace Greenshot.PerformanceTests
public void Cleanup() public void Cleanup()
{ {
_screenCapture.Dispose(); _screenCapture.Dispose();
_screenBitmapCapture.Dispose();
_screenBitmapCaptureResized.Dispose();
_aviWriter.Close(); _aviWriter.Close();
} }
} }

View file

@ -11,7 +11,7 @@ namespace Greenshot.PerformanceTests
/// <summary> /// <summary>
/// This defines the benchmarks which can be done /// This defines the benchmarks which can be done
/// </summary> /// </summary>
[MinColumn, MaxColumn, MemoryDiagnoser] [MinColumn, MaxColumn, MemoryDiagnoser, CoreJob, ClrJob]
public class GfxPerformance public class GfxPerformance
{ {
private UnmanagedBitmap<Bgr32> _unmanagedTestBitmap; private UnmanagedBitmap<Bgr32> _unmanagedTestBitmap;

View file

@ -1,31 +0,0 @@
using System.Drawing;
using System.Drawing.Imaging;
using BenchmarkDotNet.Attributes;
using Greenshot.Gfx;
namespace Greenshot.PerformanceTests
{
/// <summary>
/// This defines the benchmarks which can be done
/// </summary>
[MinColumn, MaxColumn, MemoryDiagnoser]
public class GfxPerformanceShort
{
[Benchmark]
[Arguments(PixelFormat.Format24bppRgb)]
[Arguments(PixelFormat.Format32bppRgb)]
[Arguments(PixelFormat.Format32bppArgb)]
public void Blur(PixelFormat pixelFormat)
{
using (var bitmap = BitmapFactory.CreateEmpty(400, 400, pixelFormat, Color.White))
{
using (var graphics = Graphics.FromImage(bitmap.NativeBitmap))
using (var pen = new SolidBrush(Color.Blue))
{
graphics.FillRectangle(pen, new Rectangle(30, 30, 340, 340));
}
bitmap.ApplyBoxBlur(10);
}
}
}
}

View file

@ -64,9 +64,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.4" /> <PackageReference Include="BenchmarkDotNet" Version="0.11.4" />
<PackageReference Include="ClrHeapAllocationAnalyzer" Version="1.0.0.9" />
<PackageReference Include="CommandLineParser" Version="2.4.3" /> <PackageReference Include="CommandLineParser" Version="2.4.3" />
<PackageReference Include="JeremyAnsel.ColorQuant" Version="1.0.55" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers"> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers">
<Version>2.6.3</Version> <Version>2.6.3</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
@ -75,7 +73,6 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.10.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.10.0" />
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="2.1.0" />
<PackageReference Include="SharpAvi" Version="2.1.1" /> <PackageReference Include="SharpAvi" Version="2.1.1" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0006" />
<PackageReference Include="System.Memory" Version="4.5.2" /> <PackageReference Include="System.Memory" Version="4.5.2" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.2" /> <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.2" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />

View file

@ -30,7 +30,8 @@ namespace Greenshot.PerformanceTests
// ReSharper disable once UnusedParameter.Local // ReSharper disable once UnusedParameter.Local
private static void Main(string[] args) private static void Main(string[] args)
{ {
BenchmarkRunner.Run<GfxPerformance>(); //BenchmarkRunner.Run<GfxPerformance>();
BenchmarkRunner.Run<CapturePerformance>();
Console.ReadLine(); Console.ReadLine();
} }
} }

View file

@ -17,6 +17,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -128,5 +129,34 @@ namespace Greenshot.Tests
await outputStream.CopyToAsync(fileStream); await outputStream.CopyToAsync(fileStream);
} }
} }
/// <summary>
/// Test if capturing works
/// </summary>
[Fact]
public void Test_BitmapCapture()
{
using (var screenBitmapCapture = new BitmapScreenCapture())
{
screenBitmapCapture.CaptureFrame();
Assert.NotNull(screenBitmapCapture.CurrentFrameAsBitmap());
var testFile1 = Path.Combine(Path.GetTempPath(), @"test-bitmap.png");
screenBitmapCapture.CurrentFrameAsBitmap().NativeBitmap.Save(testFile1, ImageFormat.Png);
var testFile2 = Path.Combine(Path.GetTempPath(), @"test-bitmapsource.png");
using (var fileStream = new FileStream(testFile2, FileMode.Create))
{
var encoder = new PngBitmapEncoder
{
Interlace = PngInterlaceOption.Off
};
encoder.Frames.Add(BitmapFrame.Create(screenBitmapCapture.CurrentFrameAsBitmap().NativeBitmapSource));
encoder.Save(fileStream);
}
}
}
} }
} }