diff --git a/src/Greenshot.Core/Capture.cs b/src/Greenshot.Core/Capture.cs
index b77fe6f6e..2043fdffd 100644
--- a/src/Greenshot.Core/Capture.cs
+++ b/src/Greenshot.Core/Capture.cs
@@ -31,13 +31,22 @@ using Greenshot.Core.Interfaces;
namespace Greenshot.Core
{
///
- public class Capture : ICapture
+ public class Capture : ICapture
{
+ public void Dispose()
+ {
+ foreach (var captureElement in CaptureElements)
+ {
+ captureElement?.Dispose();
+ }
+ CaptureElements.Clear();
+ }
+
///
public DateTimeOffset Taken { get; } = DateTimeOffset.Now;
///
- public IList CaptureElements { get; } = new List();
+ public IList> CaptureElements { get; } = new List>();
///
public NativeRect Bounds => CaptureElements.Select(element => element.Bounds).Aggregate((b1, b2) => b1.Union(b2));
diff --git a/src/Greenshot.Core/CaptureElement.cs b/src/Greenshot.Core/CaptureElement.cs
index 789a55937..fda4b0cb1 100644
--- a/src/Greenshot.Core/CaptureElement.cs
+++ b/src/Greenshot.Core/CaptureElement.cs
@@ -21,8 +21,10 @@
#endregion
+using System;
using System.Collections.Generic;
-using System.Windows.Media;
+using System.Drawing;
+using System.Windows.Media.Imaging;
using Dapplo.Windows.Common.Structs;
using Greenshot.Core.Enums;
using Greenshot.Core.Interfaces;
@@ -32,11 +34,25 @@ namespace Greenshot.Core
///
/// The CaptureElement contains the information of an element in a capture, e.g the window and mouse
///
- public class CaptureElement : ICaptureElement
+ public class CaptureElement : ICaptureElement
{
- public CaptureElement(NativePoint location, ImageSource content)
+ public CaptureElement(NativePoint location, TContent content)
{
- Bounds = new NativeRect(location, new NativeSize((int)content.Width, (int)content.Height));
+ NativeSize size;
+ if (content is BitmapSource bitmapSource)
+ {
+ size = new NativeSize((int)bitmapSource.Width, (int)bitmapSource.Height);
+ }
+ else if (content is Bitmap bitmap)
+ {
+ size = new NativeSize(bitmap.Width, bitmap.Height);
+ }
+ else
+ {
+ throw new NotSupportedException(typeof(TContent).ToString());
+ }
+
+ Bounds = new NativeRect(location, size);
Content = content;
}
@@ -44,12 +60,20 @@ namespace Greenshot.Core
public NativeRect Bounds { get; set; }
///
- public ImageSource Content { get; set; }
+ public TContent Content { get; set; }
///
public CaptureElementType ElementType { get; set; } = CaptureElementType.Unknown;
///
public IDictionary MetaData { get; } = new Dictionary();
+
+ public void Dispose()
+ {
+ if (Content is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
+ }
}
}
diff --git a/src/Greenshot.Core/CaptureFlow.cs b/src/Greenshot.Core/CaptureFlow.cs
index cfaa84bb8..42d3df2be 100644
--- a/src/Greenshot.Core/CaptureFlow.cs
+++ b/src/Greenshot.Core/CaptureFlow.cs
@@ -31,40 +31,40 @@ namespace Greenshot.Core
///
/// This describes a capture flow, from source via processor to destination
///
- public class CaptureFlow
+ public class CaptureFlow
{
///
/// The ISource this capture flow contains
///
- public IList Sources
+ public IList> Sources
{
get;
- } = new List();
+ } = new List>();
///
/// The IProcessor this capture flow contains
///
- public IList Processors
+ public IList> Processors
{
get;
- } = new List();
+ } = new List>();
///
/// The IDestination this capture flow contains
///
- public IList Destinations
+ public IList> Destinations
{
get;
- } = new List();
+ } = new List>();
///
/// Execute this capture flow, to create a capture
///
/// CancellationToken
/// ICapture
- public async Task Execute(CancellationToken cancellationToken = default)
+ public async Task> Execute(CancellationToken cancellationToken = default)
{
- var capture = new Capture();
+ var capture = new Capture();
// Import the capture from the sources
foreach (var source in Sources)
diff --git a/src/Greenshot.Core/Extensions/BitmapSourceExtensions.cs b/src/Greenshot.Core/Extensions/BitmapSourceExtensions.cs
index ec6aabd85..7a4af3688 100644
--- a/src/Greenshot.Core/Extensions/BitmapSourceExtensions.cs
+++ b/src/Greenshot.Core/Extensions/BitmapSourceExtensions.cs
@@ -24,6 +24,7 @@
using System;
using System.IO;
using System.Windows.Media.Imaging;
+using Dapplo.Windows.Common.Structs;
using Greenshot.Core.Enums;
namespace Greenshot.Core.Extensions
@@ -66,5 +67,15 @@ namespace Greenshot.Core.Extensions
return returnStream;
}
+
+ ///
+ /// Create a NativeSizeFloat object from a BitmapSource
+ ///
+ ///
+ ///
+ public static NativeSizeFloat Size(this BitmapSource bitmapSource)
+ {
+ return new NativeSizeFloat(bitmapSource.Width, bitmapSource.Height);
+ }
}
}
diff --git a/src/Greenshot.Core/Extensions/InteropWindowCaptureExtensions.cs b/src/Greenshot.Core/Extensions/InteropWindowCaptureExtensions.cs
index b3daf1738..bace08038 100644
--- a/src/Greenshot.Core/Extensions/InteropWindowCaptureExtensions.cs
+++ b/src/Greenshot.Core/Extensions/InteropWindowCaptureExtensions.cs
@@ -71,10 +71,10 @@ namespace Greenshot.Core.Extensions
/// InteropWindow
/// true to use the client bounds
/// ICaptureElement
- public static ICaptureElement CaptureFromScreen(this IInteropWindow interopWindow, bool clientBounds = false)
+ public static ICaptureElement CaptureFromScreen(this IInteropWindow interopWindow, bool clientBounds = false)
{
var bounds = clientBounds ? interopWindow.GetInfo().ClientBounds: interopWindow.GetInfo().Bounds;
- ICaptureElement result = ScreenSource.CaptureRectangle(bounds);
+ ICaptureElement result = ScreenSource.CaptureRectangle(bounds);
return result;
}
@@ -84,18 +84,18 @@ namespace Greenshot.Core.Extensions
/// TODO: If there is a parent, this could be removed with SetParent, and set back afterwards.
///
/// ICaptureElement
- public static ICaptureElement PrintWindow(this IInteropWindow interopWindow)
+ public static ICaptureElement PrintWindow(this IInteropWindow interopWindow)
{
var returnBitmap = interopWindow.PrintWindow();
if (interopWindow.HasParent || !interopWindow.IsMaximized())
{
- return new CaptureElement(interopWindow.GetInfo().Bounds.Location, returnBitmap);
+ return new CaptureElement(interopWindow.GetInfo().Bounds.Location, returnBitmap);
}
Log.Debug().WriteLine("Correcting for maximalization");
var borderSize = interopWindow.GetInfo().BorderSize;
var bounds = interopWindow.GetInfo().Bounds;
var borderRectangle = new NativeRect(borderSize.Width, borderSize.Height, bounds.Width - 2 * borderSize.Width, bounds.Height - 2 * borderSize.Height);
- return new CaptureElement(interopWindow.GetInfo().Bounds.Location, new CroppedBitmap(returnBitmap, borderRectangle));
+ return new CaptureElement(interopWindow.GetInfo().Bounds.Location, new CroppedBitmap(returnBitmap, borderRectangle));
}
@@ -103,6 +103,7 @@ namespace Greenshot.Core.Extensions
/// Helper method to check if it is allowed to capture the process using GDI
///
/// Process owning the window
+ /// ICaptureConfiguration
/// true if it's allowed
public static bool IsGdiAllowed(Process process, ICaptureConfiguration captureConfiguration)
{
@@ -137,10 +138,10 @@ namespace Greenshot.Core.Extensions
/// IInteropWindow
/// ICaptureConfiguration configuration for the settings
/// ICaptureElement with the capture
- public static async ValueTask CaptureDwmWindow(this IInteropWindow interopWindow, ICaptureConfiguration captureConfiguration)
+ public static async ValueTask> CaptureDwmWindow(this IInteropWindow interopWindow, ICaptureConfiguration captureConfiguration)
{
// The capture
- ICaptureElement capturedBitmap = null;
+ ICaptureElement capturedBitmap = null;
var thumbnailHandle = IntPtr.Zero;
Form tempForm = null;
var tempFormShown = false;
@@ -387,12 +388,11 @@ namespace Greenshot.Core.Extensions
/// ICaptureElement with the black image
/// ICaptureElement with the white image
/// ICaptureElement with transparency
- private static ICaptureElement ApplyTransparency(ICaptureElement blackBitmap, ICaptureElement whiteBitmap)
+ private static ICaptureElement ApplyTransparency(ICaptureElement blackBitmap, ICaptureElement whiteBitmap)
{
- var blackBitmapSource = blackBitmap.Content as BitmapSource ?? throw new ArgumentException("Not a BitmapSource", nameof(blackBitmap));
- var whitkBitmapSource = whiteBitmap.Content as BitmapSource ?? throw new ArgumentException("Not a BitmapSource", nameof(whiteBitmap));
- var blackBuffer = new WriteableBitmap(blackBitmapSource);
- var whiteBuffer = new WriteableBitmap(whitkBitmapSource);
+ var blackBuffer = new WriteableBitmap(blackBitmap.Content);
+ var whiteBuffer = new WriteableBitmap(whiteBitmap.Content);
+ var blackBitmapSource = blackBitmap.Content;
var result = new WriteableBitmap((int)blackBitmapSource.Width, (int)blackBitmapSource.Height, blackBitmapSource.DpiX, blackBitmapSource.DpiY, PixelFormats.Bgra32, null);
try
@@ -458,7 +458,7 @@ namespace Greenshot.Core.Extensions
blackBuffer.Unlock();
whiteBuffer.Unlock();
}
- return new CaptureElement(blackBitmap.Bounds.Location, result);
+ return new CaptureElement(blackBitmap.Bounds.Location, result);
}
}
}
\ No newline at end of file
diff --git a/src/Greenshot.Core/Interfaces/ICapture.cs b/src/Greenshot.Core/Interfaces/ICapture.cs
index 4f4e1dab6..bcaa230ae 100644
--- a/src/Greenshot.Core/Interfaces/ICapture.cs
+++ b/src/Greenshot.Core/Interfaces/ICapture.cs
@@ -30,7 +30,7 @@ namespace Greenshot.Core.Interfaces
///
/// This contains all the capture information
///
- public interface ICapture
+ public interface ICapture : IDisposable
{
///
/// The calculated bounds for this capture
@@ -55,6 +55,6 @@ namespace Greenshot.Core.Interfaces
///
/// the actual capture elements, making up the capture
///
- IList CaptureElements { get; }
+ IList> CaptureElements { get; }
}
}
\ No newline at end of file
diff --git a/src/Greenshot.Core/Interfaces/ICaptureElement.cs b/src/Greenshot.Core/Interfaces/ICaptureElement.cs
index 6ac681efb..3095d3437 100644
--- a/src/Greenshot.Core/Interfaces/ICaptureElement.cs
+++ b/src/Greenshot.Core/Interfaces/ICaptureElement.cs
@@ -21,8 +21,8 @@
#endregion
+using System;
using System.Collections.Generic;
-using System.Windows.Media;
using Dapplo.Windows.Common.Structs;
using Greenshot.Core.Enums;
@@ -31,7 +31,7 @@ namespace Greenshot.Core.Interfaces
///
/// This specifies a single element of a capture, making it possible to have a capture contain mouse, window, popup etc information
///
- public interface ICaptureElement
+ public interface ICaptureElement : IDisposable
{
///
/// The location or bounds of the content
@@ -45,7 +45,7 @@ namespace Greenshot.Core.Interfaces
///
/// The actual content
///
- ImageSource Content
+ TContent Content
{
get;
set;
diff --git a/src/Greenshot.Core/Interfaces/IDestination.cs b/src/Greenshot.Core/Interfaces/IDestination.cs
index 361acd0b3..868e6f85b 100644
--- a/src/Greenshot.Core/Interfaces/IDestination.cs
+++ b/src/Greenshot.Core/Interfaces/IDestination.cs
@@ -29,7 +29,7 @@ namespace Greenshot.Core.Interfaces
///
/// This IDestination describes things which can export a capture
///
- public interface IDestination
+ public interface IDestination
{
///
/// This is called when the a capture needs to be exported
@@ -37,6 +37,6 @@ namespace Greenshot.Core.Interfaces
/// ICapture
/// CancellationToken
/// Task of bool
- Task Export(ICapture capture, CancellationToken cancellationToken = default);
+ Task Export(ICapture capture, CancellationToken cancellationToken = default);
}
}
diff --git a/src/Greenshot.Core/Interfaces/IProcessor.cs b/src/Greenshot.Core/Interfaces/IProcessor.cs
index 4025d5534..b5eea06a6 100644
--- a/src/Greenshot.Core/Interfaces/IProcessor.cs
+++ b/src/Greenshot.Core/Interfaces/IProcessor.cs
@@ -30,7 +30,7 @@ namespace Greenshot.Core.Interfaces
/// This IProcessor describes things which can process a capture
/// An example would be to add a watermark to the capture
///
- public interface IProcessor
+ public interface IProcessor
{
///
/// Process the capture
@@ -38,6 +38,6 @@ namespace Greenshot.Core.Interfaces
/// ICapture
/// CancellationToken
/// Task of bool
- Task Process(ICapture capture, CancellationToken cancellationToken = default);
+ Task Process(ICapture capture, CancellationToken cancellationToken = default);
}
}
diff --git a/src/Greenshot.Core/Interfaces/ISource.cs b/src/Greenshot.Core/Interfaces/ISource.cs
index 13e3bf007..f18d1e367 100644
--- a/src/Greenshot.Core/Interfaces/ISource.cs
+++ b/src/Greenshot.Core/Interfaces/ISource.cs
@@ -30,13 +30,13 @@ namespace Greenshot.Core.Interfaces
/// This interface defines sources, which can be used to get information from.
/// For instance the screen, IE, a file etc.
///
- public interface ISource
+ public interface ISource
{
///
/// Import an ICaptureElement from the source
///
/// CancellationToken
/// Task with ICaptureElement
- ValueTask Import(CancellationToken cancellationToken = default);
+ ValueTask> Import(CancellationToken cancellationToken = default);
}
}
diff --git a/src/Greenshot.Core/Interfaces/ITemplate.cs b/src/Greenshot.Core/Interfaces/ITemplate.cs
index 88765c95c..9385d36a4 100644
--- a/src/Greenshot.Core/Interfaces/ITemplate.cs
+++ b/src/Greenshot.Core/Interfaces/ITemplate.cs
@@ -28,13 +28,13 @@ namespace Greenshot.Core.Interfaces
///
/// This defines a template which is applied to a capture, so we can output it to the screen or to disk.
///
- public interface ITemplate
+ public interface ITemplate
{
///
/// This applies a template, to generate a framework element
///
/// ICapture
/// FrameworkElement
- FrameworkElement Apply(ICapture capture);
+ FrameworkElement Apply(ICapture capture);
}
}
diff --git a/src/Greenshot.Core/Sources/DwmWindowSource.cs b/src/Greenshot.Core/Sources/DwmWindowSource.cs
index 7a3086719..21d5ae708 100644
--- a/src/Greenshot.Core/Sources/DwmWindowSource.cs
+++ b/src/Greenshot.Core/Sources/DwmWindowSource.cs
@@ -24,6 +24,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
using Dapplo.Windows.Desktop;
using Greenshot.Core.Configuration;
using Greenshot.Core.Extensions;
@@ -34,7 +35,7 @@ namespace Greenshot.Core.Sources
///
/// This does the screen capture of a Window via DWM
///
- public class DwmWindowSource : ISource
+ public class DwmWindowSource : ISource
{
private readonly ICaptureConfiguration _captureConfiguration;
private readonly Func _retrieveWindowFunc;
@@ -45,7 +46,7 @@ namespace Greenshot.Core.Sources
_retrieveWindowFunc = retrieveWindowFunc ?? InteropWindowQuery.GetActiveWindow;
}
- public ValueTask Import(CancellationToken cancellationToken = default)
+ public ValueTask> Import(CancellationToken cancellationToken = default)
{
var window = _retrieveWindowFunc();
return window.CaptureDwmWindow(_captureConfiguration);
diff --git a/src/Greenshot.Core/Sources/MouseSource.cs b/src/Greenshot.Core/Sources/MouseSource.cs
index 65d5b2eef..5f8d106b5 100644
--- a/src/Greenshot.Core/Sources/MouseSource.cs
+++ b/src/Greenshot.Core/Sources/MouseSource.cs
@@ -23,6 +23,7 @@
using System.Threading;
using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
using Dapplo.Windows.Icons;
using Greenshot.Core.Enums;
using Greenshot.Core.Interfaces;
@@ -32,21 +33,21 @@ namespace Greenshot.Core.Sources
///
/// A source to capture the mouse cursor
///
- public class MouseSource : ISource
+ public class MouseSource : ISource
{
///
- public ValueTask Import(CancellationToken cancellationToken = default)
+ public ValueTask> Import(CancellationToken cancellationToken = default)
{
- ICaptureElement result = null;
+ ICaptureElement result = null;
if (CursorHelper.TryGetCurrentCursor(out var bitmapSource, out var location))
{
- result = new CaptureElement(location, bitmapSource)
+ result = new CaptureElement(location, bitmapSource)
{
ElementType = CaptureElementType.Mouse
};
}
- return new ValueTask(result);
+ return new ValueTask>(result);
}
}
diff --git a/src/Greenshot.Core/Sources/ScreenSource.cs b/src/Greenshot.Core/Sources/ScreenSource.cs
index e512778b2..b524d7bf5 100644
--- a/src/Greenshot.Core/Sources/ScreenSource.cs
+++ b/src/Greenshot.Core/Sources/ScreenSource.cs
@@ -30,7 +30,6 @@ using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
-using Dapplo.Log;
using Dapplo.Windows.Common;
using Dapplo.Windows.Common.Extensions;
using Dapplo.Windows.Common.Structs;
@@ -49,14 +48,13 @@ namespace Greenshot.Core.Sources
///
/// This does the screen capture
///
- public class ScreenSource : ISource
+ public class ScreenSource : ISource
{
- private static readonly LogSource Log = new LogSource();
- public ValueTask Import(CancellationToken cancellationToken = default)
+ public ValueTask> Import(CancellationToken cancellationToken = default)
{
var screenbounds = DisplayInfo.GetAllScreenBounds();
var result = CaptureRectangle(screenbounds);
- return new ValueTask(result);
+ return new ValueTask>(result);
}
///
@@ -83,9 +81,9 @@ namespace Greenshot.Core.Sources
///
/// NativeRect
/// ICaptureElement
- internal static ICaptureElement CaptureRectangle(NativeRect captureBounds)
+ internal static ICaptureElement CaptureRectangle(NativeRect captureBounds)
{
- BitmapSource capturedBitmapSource = null;
+ BitmapSource capturedBitmapSource;
if (captureBounds.IsEmpty)
{
return null;
@@ -183,7 +181,7 @@ namespace Greenshot.Core.Sources
}
}
}
- ICaptureElement result = new CaptureElement(captureBounds.Location, capturedBitmapSource)
+ var result = new CaptureElement(captureBounds.Location, capturedBitmapSource)
{
ElementType = CaptureElementType.Screen
};
diff --git a/src/Greenshot.Core/Templates/CroppedTemplate.cs b/src/Greenshot.Core/Templates/CroppedTemplate.cs
index 496ac60ef..6813d3281 100644
--- a/src/Greenshot.Core/Templates/CroppedTemplate.cs
+++ b/src/Greenshot.Core/Templates/CroppedTemplate.cs
@@ -24,6 +24,7 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
+using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Greenshot.Core.Enums;
using Greenshot.Core.Interfaces;
@@ -33,10 +34,10 @@ namespace Greenshot.Core.Templates
///
/// A template to create a FrameworkElement from a capture, with a crop applied
///
- public class CroppedTemplate : ITemplate
+ public class CroppedTemplate : ITemplate
{
public bool DisplayMouse { get; set; } = true;
- public FrameworkElement Apply(ICapture capture)
+ public FrameworkElement Apply(ICapture capture)
{
var width = (int)(capture.CropRect.Width + 0.5);
var height = (int)(capture.CropRect.Height + 0.5);
diff --git a/src/Greenshot.Core/Templates/SimpleTemplate.cs b/src/Greenshot.Core/Templates/SimpleTemplate.cs
index 6099ea500..4e850dc1f 100644
--- a/src/Greenshot.Core/Templates/SimpleTemplate.cs
+++ b/src/Greenshot.Core/Templates/SimpleTemplate.cs
@@ -21,8 +21,10 @@
#endregion
+
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Media.Imaging;
using Greenshot.Core.Enums;
using Greenshot.Core.Interfaces;
@@ -31,10 +33,10 @@ namespace Greenshot.Core.Templates
///
/// A template to create a FrameworkElement from a capture
///
- public class SimpleTemplate : ITemplate
+ public class SimpleTemplate : ITemplate
{
public bool DisplayMouse { get; set; } = true;
- public FrameworkElement Apply(ICapture capture)
+ public FrameworkElement Apply(ICapture capture)
{
var canvas = new Canvas
{
diff --git a/src/Greenshot.Tests/CaptureTests.cs b/src/Greenshot.Tests/CaptureTests.cs
index 106220e45..b5b97176a 100644
--- a/src/Greenshot.Tests/CaptureTests.cs
+++ b/src/Greenshot.Tests/CaptureTests.cs
@@ -24,6 +24,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
using Dapplo.CaliburnMicro.Extensions;
using Dapplo.Ini;
using Dapplo.Windows.Desktop;
@@ -48,7 +49,7 @@ namespace Greenshot.Tests
[Fact]
public async Task Test_CaptureFlow_ScreenSource()
{
- var captureFlow = new CaptureFlow
+ var captureFlow = new CaptureFlow
{
Sources = {new ScreenSource()}
};
@@ -64,7 +65,7 @@ namespace Greenshot.Tests
[Fact]
public async Task Test_CaptureFlow_ScreenSource_MouseSource()
{
- var captureFlow = new CaptureFlow
+ var captureFlow = new CaptureFlow
{
Sources = { new ScreenSource() , new MouseSource()}
};
@@ -82,16 +83,21 @@ namespace Greenshot.Tests
{
var iniConfig = new IniConfig("Greenshot.Tests", "Greenshot.Tests");
var config = iniConfig.Get();
- var captureFlow = new CaptureFlow
+
+ var windowToCapture = InteropWindowQuery.GetTopLevelWindows().First(window => window.GetCaption().Contains("Notepad"));
+ var bounds = windowToCapture.GetInfo().Bounds;
+ var captureFlow = new CaptureFlow
{
- Sources = { new DwmWindowSource(config, () => InteropWindowQuery.GetTopLevelWindows().First(window => window.GetCaption().Contains("Notepad"))) }
+ Sources = { new DwmWindowSource(config, () => windowToCapture) }
};
var capture = await captureFlow.Execute();
Assert.NotNull(capture);
Assert.NotNull(capture.CaptureElements);
var template = new SimpleTemplate();
- using (var outputStream = template.Apply(capture).ToBitmapSource().ToStream(OutputFormats.png))
+ var bitmapSource = template.Apply(capture).ToBitmapSource();
+ Assert.Equal(bounds.Size, bitmapSource.Size());
+ using (var outputStream = bitmapSource.ToStream(OutputFormats.png))
using (var fileStream = File.Create("Test_CaptureFlow_DwmWindowSource.png"))
{
outputStream.Seek(0, SeekOrigin.Begin);