diff --git a/src/Greenshot.Base/Core/FileFormatHandlers/DefaultFileFormatHandler.cs b/src/Greenshot.Base/Core/FileFormatHandlers/DefaultFileFormatHandler.cs new file mode 100644 index 000000000..866bc3f97 --- /dev/null +++ b/src/Greenshot.Base/Core/FileFormatHandlers/DefaultFileFormatHandler.cs @@ -0,0 +1,105 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://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 . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Base.Core.FileFormatHandlers +{ + /// + /// THis is the default .NET bitmap file format handler + /// + public class DefaultFileFormatHandler : IFileFormatHandler + { + private static readonly string [] OurExtensions = { "png", "bmp", "gif", "jpg", "jpeg", "tiff", "tif" }; + + /// + public IEnumerable SupportedExtensions(FileFormatHandlerActions fileFormatHandlerAction) + { + if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream) + { + return Enumerable.Empty(); + } + + return OurExtensions; + } + + /// + public bool Supports(FileFormatHandlerActions fileFormatHandlerAction, string extension) + { + if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream) + { + return false; + } + + return OurExtensions.Contains(extension); + } + + /// + public int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension) + { + return int.MaxValue; + } + + /// + public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension) + { + ImageFormat imageFormat = extension?.ToLowerInvariant() switch + { + "png" => ImageFormat.Png, + "bmp" => ImageFormat.Bmp, + "gif" => ImageFormat.Gif, + "jpg" => ImageFormat.Jpeg, + "jpeg" => ImageFormat.Jpeg, + "tiff" => ImageFormat.Tiff, + "tif" => ImageFormat.Tiff, + _ => null + }; + + if (imageFormat == null) + { + return false; + } + bitmap.Save(destination, imageFormat); + return true; + } + + /// + public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + using var tmpImage = Image.FromStream(stream, true, true); + bitmap = ImageHelper.Clone(tmpImage, PixelFormat.Format32bppArgb); + return true; + } + + /// + public bool TryLoadDrawableFromStream(Stream stream, string extension, out IDrawableContainer drawableContainer, ISurface surface) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Greenshot.Base/Core/FileFormatHandlers/GreenshotFileFormatHandler.cs b/src/Greenshot.Base/Core/FileFormatHandlers/GreenshotFileFormatHandler.cs new file mode 100644 index 000000000..f43d8ff54 --- /dev/null +++ b/src/Greenshot.Base/Core/FileFormatHandlers/GreenshotFileFormatHandler.cs @@ -0,0 +1,60 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://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 . + */ + +using System; +using System.Drawing; +using System.IO; +using System.Linq; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Base.Core.FileFormatHandlers +{ + public class GreenshotFileFormatHandler : IFileFormatHandler + { + private static readonly string[] SupportedExtensions = { "greenshot" }; + + public bool CanDoActionForExtension(FileFormatHandlerActions fileFormatHandlerAction, string extension) + { + if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream) + { + return false; + } + return SupportedExtensions.Contains(extension); + } + + public void SaveToStream(Bitmap bitmap, Stream destination, string extension) + { + throw new NotImplementedException(); + } + + public Bitmap Load(Stream stream, string extension) + { + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + return (Bitmap)surface.GetImageForExport(); + } + + public IDrawableContainer LoadDrawableFromStream(Stream stream, string extension, ISurface parent) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Greenshot.Base/Core/FileFormatHandlers/IconFileFormatHandler.cs b/src/Greenshot.Base/Core/FileFormatHandlers/IconFileFormatHandler.cs new file mode 100644 index 000000000..92a21a1ac --- /dev/null +++ b/src/Greenshot.Base/Core/FileFormatHandlers/IconFileFormatHandler.cs @@ -0,0 +1,162 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://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 . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using log4net; + +namespace Greenshot.Base.Core.FileFormatHandlers +{ + /// + /// THis is the default .NET bitmap file format handler + /// + public class IconFileFormatHandler : IFileFormatHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(ImageHelper)); + + private static readonly string[] OurExtensions = { "ico" }; + + /// + public IEnumerable SupportedExtensions(FileFormatHandlerActions fileFormatHandlerAction) + { + if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream) + { + return Enumerable.Empty(); + } + + return OurExtensions; + } + + /// + public bool Supports(FileFormatHandlerActions fileFormatHandlerAction, string extension) + { + if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream) + { + return false; + } + + return OurExtensions.Contains(extension); + } + + /// + public int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension) + { + return int.MaxValue; + } + + public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension) + { + // TODO: Implement this + return false; + } + + public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + var startingPosition = stream.Seek(0, SeekOrigin.Current); + + // Icon logic, try to get the Vista icon, else the biggest possible + try + { + using Image tmpImage = ExtractVistaIcon(stream); + if (tmpImage != null) + { + bitmap = ImageHelper.Clone(tmpImage, PixelFormat.Format32bppArgb); + return true; + } + } + catch (Exception vistaIconException) + { + Log.Warn("Can't read icon", vistaIconException); + } + + try + { + // No vista icon, try normal icon + stream.Position = stream.Seek(0, SeekOrigin.Begin); + // We create a copy of the bitmap, so everything else can be disposed + using Icon tmpIcon = new Icon(stream, new Size(1024, 1024)); + using Image tmpImage = tmpIcon.ToBitmap(); + bitmap = ImageHelper.Clone(tmpImage, PixelFormat.Format32bppArgb); + return true; + } + catch (Exception iconException) + { + Log.Warn("Can't read icon", iconException); + } + + bitmap = null; + return false; + } + + + public bool TryLoadDrawableFromStream(Stream stream, string extension, out IDrawableContainer drawableContainer, ISurface parent) + { + // TODO: Implement this + throw new NotImplementedException(); + } + + /// + /// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx + /// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx + /// + /// Stream with the icon information + /// Bitmap with the Vista Icon (256x256) + private static Bitmap ExtractVistaIcon(Stream iconStream) + { + const int sizeIconDir = 6; + const int sizeIconDirEntry = 16; + Bitmap bmpPngExtracted = null; + try + { + byte[] srcBuf = new byte[iconStream.Length]; + // TODO: Check if there is a need to process the result + _ = iconStream.Read(srcBuf, 0, (int)iconStream.Length); + int iCount = BitConverter.ToInt16(srcBuf, 4); + for (int iIndex = 0; iIndex < iCount; iIndex++) + { + int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex]; + int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1]; + if (iWidth != 0 || iHeight != 0) continue; + + int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8); + int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12); + using MemoryStream destStream = new MemoryStream(); + destStream.Write(srcBuf, iImageOffset, iImageSize); + destStream.Seek(0, SeekOrigin.Begin); + bmpPngExtracted = new Bitmap(destStream); // This is PNG! :) + break; + } + } + catch + { + return null; + } + + return bmpPngExtracted; + } + } +} diff --git a/src/Greenshot.Base/Core/FileFormatHandlers/PngFileFormatHandler.cs b/src/Greenshot.Base/Core/FileFormatHandlers/PngFileFormatHandler.cs new file mode 100644 index 000000000..6eab0ae6a --- /dev/null +++ b/src/Greenshot.Base/Core/FileFormatHandlers/PngFileFormatHandler.cs @@ -0,0 +1,59 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://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 . + */ + +using System; +using System.Drawing; +using System.IO; +using System.Linq; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Base.Core.FileFormatHandlers +{ + public class PngFileFormatHandler : IFileFormatHandler + { + private static readonly string[] SupportedExtensions = { "png" }; + + public bool CanDoActionForExtension(FileFormatHandlerActions fileFormatHandlerAction, string extension) + { + if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream) + { + return false; + } + return SupportedExtensions.Contains(extension); + } + + public void SaveToStream(Bitmap bitmap, Stream destination, string extension) + { + throw new NotImplementedException(); + } + + public Bitmap Load(Stream stream, string extension) + { + throw new NotImplementedException(); + } + + public IDrawableContainer LoadDrawableFromStream(Stream stream, string extension, ISurface parent) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Greenshot.Base/Core/IImage.cs b/src/Greenshot.Base/Core/IImage.cs index d6b30c667..20aa320d9 100644 --- a/src/Greenshot.Base/Core/IImage.cs +++ b/src/Greenshot.Base/Core/IImage.cs @@ -61,7 +61,7 @@ namespace Greenshot.Base.Core float HorizontalResolution { get; } /// - /// Unterlying image, or an on demand rendered version with different attributes as the original + /// Underlying image, or an on demand rendered version with different attributes as the original /// Image Image { get; } } diff --git a/src/Greenshot.Base/Core/ImageHelper.cs b/src/Greenshot.Base/Core/ImageHelper.cs index 60d846eac..4aa0207fc 100644 --- a/src/Greenshot.Base/Core/ImageHelper.cs +++ b/src/Greenshot.Base/Core/ImageHelper.cs @@ -25,6 +25,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; +using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.Effects; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; @@ -55,84 +56,15 @@ namespace Greenshot.Base.Core private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); private const int ExifOrientationId = 0x0112; + public static readonly IList FileFormatHandlers = new List(); + static ImageHelper() { - StreamConverters["greenshot"] = (stream, s) => - { - var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); - return surface.GetImageForExport(); - }; - - // Add a SVG converter - StreamConverters["svg"] = (stream, s) => - { - stream.Position = 0; - try - { - return SvgImage.FromStream(stream).Image; - } - catch (Exception ex) - { - Log.Error("Can't load SVG", ex); - } - - return null; - }; - - static Image DefaultConverter(Stream stream, string s) - { - stream.Position = 0; - using var tmpImage = Image.FromStream(stream, true, true); - Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); - return Clone(tmpImage, PixelFormat.Format32bppArgb); - } - - // Fallback - StreamConverters[string.Empty] = DefaultConverter; - StreamConverters["gif"] = DefaultConverter; - StreamConverters["bmp"] = DefaultConverter; - StreamConverters["jpg"] = DefaultConverter; - StreamConverters["jpeg"] = DefaultConverter; - StreamConverters["png"] = DefaultConverter; - StreamConverters["wmf"] = DefaultConverter; - - StreamConverters["ico"] = (stream, extension) => - { - // Icon logic, try to get the Vista icon, else the biggest possible - try - { - using Image tmpImage = ExtractVistaIcon(stream); - if (tmpImage != null) - { - return Clone(tmpImage, PixelFormat.Format32bppArgb); - } - } - catch (Exception vistaIconException) - { - Log.Warn("Can't read icon", vistaIconException); - } - - try - { - // No vista icon, try normal icon - stream.Position = 0; - // We create a copy of the bitmap, so everything else can be disposed - using Icon tmpIcon = new Icon(stream, new Size(1024, 1024)); - using Image tmpImage = tmpIcon.ToBitmap(); - return Clone(tmpImage, PixelFormat.Format32bppArgb); - } - catch (Exception iconException) - { - Log.Warn("Can't read icon", iconException); - } - - stream.Position = 0; - return DefaultConverter(stream, extension); - }; + FileFormatHandlers.Add(new IconFileFormatHandler()); + FileFormatHandlers.Add(new GreenshotFileFormatHandler()); + FileFormatHandlers.Add(new DefaultFileFormatHandler()); } - public static IDictionary> StreamConverters { get; } = new Dictionary>(); - /// /// Make sure the image is orientated correctly /// @@ -563,8 +495,7 @@ namespace Greenshot.Base.Core /// Changed bitmap public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange, bool[] edges) { - Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution, - sourceImage.VerticalResolution); + Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); using (var path = new GraphicsPath()) { Random random = new Random(); @@ -1516,10 +1447,10 @@ namespace Greenshot.Base.Core /// /// /// The color to fill with, or Color.Empty to take the default depending on the pixel format - /// - /// + /// float + /// float /// Bitmap - public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution) + public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution = 96f, float verticalResolution = 96f) { // Create a new "clean" image Bitmap newImage = new Bitmap(width, height, format); @@ -1777,31 +1708,50 @@ namespace Greenshot.Base.Core extension = extension.Replace(".", string.Empty); } + var startingPosition = stream.Position; + // Make sure we can try multiple times if (!stream.CanSeek) { var memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); stream = memoryStream; + // As we are if a different stream, which starts at 0, change the starting position + startingPosition = 0; } - Image returnImage = null; - if (StreamConverters.TryGetValue(extension ?? string.Empty, out var converter)) + foreach (var fileFormatHandler in FileFormatHandlers) { - returnImage = converter(stream, extension); + if (!fileFormatHandler.CanDoActionForExtension(FileFormatHandlerActions.Load, extension)) continue; + + stream.Seek(startingPosition, SeekOrigin.Begin); + return fileFormatHandler.Load(stream, extension); } - // Fallback - if (returnImage == null) - { - // We create a copy of the bitmap, so everything else can be disposed - stream.Position = 0; - using var tmpImage = Image.FromStream(stream, true, true); - Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); - returnImage = Clone(tmpImage, PixelFormat.Format32bppArgb); - } + return null; + } - return returnImage; + + /// + /// Rotate the image + /// + /// Input image + /// Angle in degrees + /// Rotated image + public static Image Rotate(this Image image, float rotationAngle) + { + var bitmap = CreateEmptyLike(image, Color.Transparent); + + using var gfx = Graphics.FromImage(bitmap); + gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; + + gfx.TranslateTransform((float)bitmap.Width / 2, (float)bitmap.Height / 2); + gfx.RotateTransform(rotationAngle); + gfx.TranslateTransform(-(float)bitmap.Width / 2, -(float)bitmap.Height / 2); + + gfx.DrawImage(image, new Point(0, 0)); + + return bitmap; } } } \ No newline at end of file diff --git a/src/Greenshot.Base/Core/ImageWrapper.cs b/src/Greenshot.Base/Core/ImageWrapper.cs deleted file mode 100644 index 517249534..000000000 --- a/src/Greenshot.Base/Core/ImageWrapper.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Drawing; -using System.Drawing.Imaging; - -namespace Greenshot.Base.Core -{ - /// - /// Wrap an image, make it resizeable - /// - public class ImageWrapper : IImage - { - // Underlying image, is used to generate a resized version of it when needed - private readonly Image _image; - private Image _imageClone; - - public ImageWrapper(Image image) - { - // Make sure the orientation is set correctly so Greenshot can process the image correctly - ImageHelper.Orientate(image); - _image = image; - Width = _image.Width; - Height = _image.Height; - } - - public void Dispose() - { - _image.Dispose(); - _imageClone?.Dispose(); - } - - /// - /// Height of the image, can be set to change - /// - public int Height { get; set; } - - /// - /// Width of the image, can be set to change. - /// - public int Width { get; set; } - - /// - /// Size of the image - /// - public Size Size => new Size(Width, Height); - - /// - /// Pixelformat of the underlying image - /// - public PixelFormat PixelFormat => Image.PixelFormat; - - public float HorizontalResolution => Image.HorizontalResolution; - public float VerticalResolution => Image.VerticalResolution; - - public Image Image - { - get - { - if (_imageClone == null) - { - if (_image.Height == Height && _image.Width == Width) - { - return _image; - } - } - - if (_imageClone?.Height == Height && _imageClone?.Width == Width) - { - return _imageClone; - } - - // Calculate new image clone - _imageClone?.Dispose(); - _imageClone = ImageHelper.ResizeImage(_image, false, Width, Height, null); - return _imageClone; - } - } - } -} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/SvgImage.cs b/src/Greenshot.Base/Core/SvgImage.cs deleted file mode 100644 index 25f98b212..000000000 --- a/src/Greenshot.Base/Core/SvgImage.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://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 . - */ - -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using Svg; - -namespace Greenshot.Base.Core -{ - /// - /// Create an image look like of the SVG - /// - public sealed class SvgImage : IImage - { - private readonly SvgDocument _svgDocument; - - private Image _imageClone; - - /// - /// Factory to create via a stream - /// - /// Stream - /// IImage - public static IImage FromStream(Stream stream) - { - return new SvgImage(stream); - } - - /// - /// Default constructor - /// - /// - public SvgImage(Stream stream) - { - _svgDocument = SvgDocument.Open(stream); - Height = (int) _svgDocument.ViewBox.Height; - Width = (int) _svgDocument.ViewBox.Width; - } - - /// - /// Height of the image, can be set to change - /// - public int Height { get; set; } - - /// - /// Width of the image, can be set to change. - /// - public int Width { get; set; } - - /// - /// Size of the image - /// - public Size Size => new Size(Width, Height); - - /// - /// Pixelformat of the underlying image - /// - public PixelFormat PixelFormat => Image.PixelFormat; - - /// - /// Horizontal resolution of the underlying image - /// - public float HorizontalResolution => Image.HorizontalResolution; - - /// - /// Vertical resolution of the underlying image - /// - public float VerticalResolution => Image.VerticalResolution; - - /// - /// Underlying image, or an on demand rendered version with different attributes as the original - /// - public Image Image - { - get - { - if (_imageClone?.Height == Height && _imageClone?.Width == Width) - { - return _imageClone; - } - - // Calculate new image clone - _imageClone?.Dispose(); - _imageClone = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent, 96, 96); - _svgDocument.Draw((Bitmap) _imageClone); - return _imageClone; - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - _imageClone?.Dispose(); - } - } -} \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/Drawing/Container.cs b/src/Greenshot.Base/Interfaces/Drawing/Container.cs index 4f653ac8a..6eb0e2b35 100644 --- a/src/Greenshot.Base/Interfaces/Drawing/Container.cs +++ b/src/Greenshot.Base/Interfaces/Drawing/Container.cs @@ -135,7 +135,7 @@ namespace Greenshot.Base.Interfaces.Drawing public interface IImageContainer : IDrawableContainer { - Image Image { get; set; } + Image Image { get; } void Load(string filename); } diff --git a/src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs b/src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs new file mode 100644 index 000000000..fdbf72847 --- /dev/null +++ b/src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs @@ -0,0 +1,33 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://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 . + */ + +namespace Greenshot.Base.Interfaces.Drawing +{ + public interface IFieldAggregator + { + void UnbindElement(IDrawableContainer dc); + void BindElements(IDrawableContainerList dcs); + void BindElement(IDrawableContainer dc); + IField GetField(IFieldType fieldType); + + event FieldChangedEventHandler FieldChanged; + } +} diff --git a/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs b/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs new file mode 100644 index 000000000..e7d5c7a83 --- /dev/null +++ b/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs @@ -0,0 +1,96 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://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 . + */ + +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Base.Interfaces +{ + /// + /// The possible actions a IFileFormatHandler might support + /// + public enum FileFormatHandlerActions + { + SaveToStream, + Load, + LoadDrawableFromStream + } + + /// + /// This interface is for code to implement the loading and saving of certain file formats + /// + public interface IFileFormatHandler + { + /// + /// Delivers the extension that the supplied action supports + /// + /// FileFormatHandlerActions + /// IEnumerable{string} + IEnumerable SupportedExtensions(FileFormatHandlerActions fileFormatHandlerAction); + + /// + ///Does this IFileFormatHandler support the specified action for the specified extension? + /// + /// FileFormatHandlerActions + /// string + /// bool true if this IFileFormatHandler can support the action for the extension + public bool Supports(FileFormatHandlerActions fileFormatHandlerAction, string extension); + + /// + /// Priority (from high int.MinValue, low int.MaxValue) of this IFileFormatHandler for the specified action and extension + /// This should be used to sort the possible IFileFormatHandler + /// + /// FileFormatHandlerActions + /// string + /// int specifying the priority for the action and extension + public int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension); + + /// + /// Try to save the specified bitmap to the stream in the format belonging to the extension + /// + /// Bitmap + /// Stream + /// extension + /// bool true if it was successful + public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension); + + /// + /// + /// + /// + /// + /// + /// bool true if it was successful + public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap); + + /// + /// Try to load a drawable container from the stream + /// + /// Stream + /// string + /// IDrawableContainer out + /// ISurface + /// bool true if it was successful + public bool TryLoadDrawableFromStream(Stream stream, string extension, out IDrawableContainer drawableContainer, ISurface parent); + } +} diff --git a/src/Greenshot.Base/Interfaces/ISurface.cs b/src/Greenshot.Base/Interfaces/ISurface.cs index d5df7ff44..85cefe452 100644 --- a/src/Greenshot.Base/Interfaces/ISurface.cs +++ b/src/Greenshot.Base/Interfaces/ISurface.cs @@ -21,6 +21,7 @@ using System; using System.Drawing; +using System.Drawing.Drawing2D; using System.IO; using System.Windows.Forms; using Greenshot.Base.Core; @@ -201,5 +202,14 @@ namespace Greenshot.Base.Interfaces Rectangle ToImageCoordinates(Rectangle rc); void MakeUndoable(IMemento memento, bool allowMerge); + + IFieldAggregator FieldAggregator { get; } + + /// + /// This reverses a change of the background image + /// + /// Image + /// Matrix + void UndoBackgroundChange(Image previous, Matrix matrix); } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/ArrowContainer.cs b/src/Greenshot.Editor/Drawing/ArrowContainer.cs index f285d28bc..eb1e10e2f 100644 --- a/src/Greenshot.Editor/Drawing/ArrowContainer.cs +++ b/src/Greenshot.Editor/Drawing/ArrowContainer.cs @@ -22,6 +22,7 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; @@ -43,7 +44,7 @@ namespace Greenshot.Editor.Drawing private static readonly AdjustableArrowCap ARROW_CAP = new AdjustableArrowCap(4, 6); - public ArrowContainer(Surface parent) : base(parent) + public ArrowContainer(ISurface parent) : base(parent) { } diff --git a/src/Greenshot.Editor/Drawing/CropContainer.cs b/src/Greenshot.Editor/Drawing/CropContainer.cs index 1aa4e06c2..2324331f0 100644 --- a/src/Greenshot.Editor/Drawing/CropContainer.cs +++ b/src/Greenshot.Editor/Drawing/CropContainer.cs @@ -21,6 +21,7 @@ using System.Drawing; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -32,7 +33,7 @@ namespace Greenshot.Editor.Drawing /// public class CropContainer : DrawableContainer { - public CropContainer(Surface parent) : base(parent) + public CropContainer(ISurface parent) : base(parent) { Init(); } diff --git a/src/Greenshot.Editor/Drawing/CursorContainer.cs b/src/Greenshot.Editor/Drawing/CursorContainer.cs index d2e19adba..0eda46085 100644 --- a/src/Greenshot.Editor/Drawing/CursorContainer.cs +++ b/src/Greenshot.Editor/Drawing/CursorContainer.cs @@ -25,6 +25,7 @@ using System.Drawing.Drawing2D; using System.IO; using System.Runtime.Serialization; using System.Windows.Forms; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using log4net; @@ -40,7 +41,7 @@ namespace Greenshot.Editor.Drawing protected Cursor cursor; - public CursorContainer(Surface parent) : base(parent) + public CursorContainer(ISurface parent) : base(parent) { Init(); } @@ -56,7 +57,7 @@ namespace Greenshot.Editor.Drawing CreateDefaultAdorners(); } - public CursorContainer(Surface parent, string filename) : this(parent) + public CursorContainer(ISurface parent, string filename) : this(parent) { Load(filename); } diff --git a/src/Greenshot.Editor/Drawing/DrawableContainer.cs b/src/Greenshot.Editor/Drawing/DrawableContainer.cs index b7202b23e..08f6ae18e 100644 --- a/src/Greenshot.Editor/Drawing/DrawableContainer.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainer.cs @@ -126,12 +126,17 @@ namespace Greenshot.Editor.Drawing } } - [NonSerialized] internal Surface _parent; + [NonSerialized] internal ISurface _parent; public ISurface Parent { get => _parent; - set => SwitchParent((Surface) value); + set => SwitchParent(value); + } + + protected Surface InternalParent + { + get => (Surface)_parent; } [NonSerialized] private TargetAdorner _targetAdorner; @@ -277,7 +282,7 @@ namespace Greenshot.Editor.Drawing Height = Round(newBounds.Height); } - public DrawableContainer(Surface parent) + public DrawableContainer(ISurface parent) { InitializeFields(); _parent = parent; @@ -512,7 +517,7 @@ namespace Greenshot.Editor.Drawing { Invalidate(); - // reset "workrbench" rectangle to current bounds + // reset "workbench" rectangle to current bounds _boundsAfterResize.X = _boundsBeforeResize.Left; _boundsAfterResize.Y = _boundsBeforeResize.Top; _boundsAfterResize.Width = x - _boundsAfterResize.Left; @@ -536,7 +541,7 @@ namespace Greenshot.Editor.Drawing { } - protected virtual void SwitchParent(Surface newParent) + protected virtual void SwitchParent(ISurface newParent) { if (newParent == Parent) { diff --git a/src/Greenshot.Editor/Drawing/EllipseContainer.cs b/src/Greenshot.Editor/Drawing/EllipseContainer.cs index f7799ac71..b32ecffd3 100644 --- a/src/Greenshot.Editor/Drawing/EllipseContainer.cs +++ b/src/Greenshot.Editor/Drawing/EllipseContainer.cs @@ -23,6 +23,7 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -35,7 +36,7 @@ namespace Greenshot.Editor.Drawing [Serializable()] public class EllipseContainer : DrawableContainer { - public EllipseContainer(Surface parent) : base(parent) + public EllipseContainer(ISurface parent) : base(parent) { Init(); } diff --git a/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs b/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs index 2df16d46f..96a19b7b9 100644 --- a/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs +++ b/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs @@ -42,7 +42,7 @@ namespace Greenshot.Editor.Drawing.Fields /// If the property values of the selected elements differ, the value of the last bound element wins. /// [Serializable] - public sealed class FieldAggregator : AbstractFieldHolder + public sealed class FieldAggregator : AbstractFieldHolder, IFieldAggregator { private readonly IDrawableContainerList _boundContainers; private bool _internalUpdateRunning; @@ -117,11 +117,10 @@ namespace Greenshot.Editor.Drawing.Fields public void UnbindElement(IDrawableContainer dc) { - if (_boundContainers.Contains(dc)) - { - _boundContainers.Remove(dc); - UpdateFromBoundElements(); - } + if (!_boundContainers.Contains(dc)) return; + + _boundContainers.Remove(dc); + UpdateFromBoundElements(); } public void Clear() diff --git a/src/Greenshot.Editor/Drawing/FilterContainer.cs b/src/Greenshot.Editor/Drawing/FilterContainer.cs index 3bdb5a5fd..c6d1f8f26 100644 --- a/src/Greenshot.Editor/Drawing/FilterContainer.cs +++ b/src/Greenshot.Editor/Drawing/FilterContainer.cs @@ -23,6 +23,7 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -51,7 +52,7 @@ namespace Greenshot.Editor.Drawing MAGNIFICATION }; - public FilterContainer(Surface parent) : base(parent) + public FilterContainer(ISurface parent) : base(parent) { Init(); } diff --git a/src/Greenshot.Editor/Drawing/FreehandContainer.cs b/src/Greenshot.Editor/Drawing/FreehandContainer.cs index e37221551..ef1c69ff3 100644 --- a/src/Greenshot.Editor/Drawing/FreehandContainer.cs +++ b/src/Greenshot.Editor/Drawing/FreehandContainer.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -50,7 +51,7 @@ namespace Greenshot.Editor.Drawing /// /// Constructor /// - public FreehandContainer(Surface parent) : base(parent) + public FreehandContainer(ISurface parent) : base(parent) { Width = parent.Image.Width; Height = parent.Image.Height; diff --git a/src/Greenshot.Editor/Drawing/HighlightContainer.cs b/src/Greenshot.Editor/Drawing/HighlightContainer.cs index 417725322..58cf792ba 100644 --- a/src/Greenshot.Editor/Drawing/HighlightContainer.cs +++ b/src/Greenshot.Editor/Drawing/HighlightContainer.cs @@ -21,6 +21,7 @@ using System; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Drawing.Filters; @@ -33,7 +34,7 @@ namespace Greenshot.Editor.Drawing [Serializable] public class HighlightContainer : FilterContainer { - public HighlightContainer(Surface parent) : base(parent) + public HighlightContainer(ISurface parent) : base(parent) { Init(); } diff --git a/src/Greenshot.Editor/Drawing/IconContainer.cs b/src/Greenshot.Editor/Drawing/IconContainer.cs index e98b37f82..adda1cc23 100644 --- a/src/Greenshot.Editor/Drawing/IconContainer.cs +++ b/src/Greenshot.Editor/Drawing/IconContainer.cs @@ -24,6 +24,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.IO; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using log4net; @@ -39,7 +40,7 @@ namespace Greenshot.Editor.Drawing protected Icon icon; - public IconContainer(Surface parent) : base(parent) + public IconContainer(ISurface parent) : base(parent) { Init(); } @@ -55,7 +56,7 @@ namespace Greenshot.Editor.Drawing CreateDefaultAdorners(); } - public IconContainer(Surface parent, string filename) : base(parent) + public IconContainer(ISurface parent, string filename) : base(parent) { Load(filename); } diff --git a/src/Greenshot.Editor/Drawing/ImageContainer.cs b/src/Greenshot.Editor/Drawing/ImageContainer.cs index a46b0dae5..68b4cd797 100644 --- a/src/Greenshot.Editor/Drawing/ImageContainer.cs +++ b/src/Greenshot.Editor/Drawing/ImageContainer.cs @@ -26,6 +26,7 @@ using System.IO; using System.Runtime.Serialization; using Greenshot.Base.Core; using Greenshot.Base.Effects; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using log4net; @@ -54,12 +55,12 @@ namespace Greenshot.Editor.Drawing /// [NonSerialized] private Point _shadowOffset = new Point(-1, -1); - public ImageContainer(Surface parent, string filename) : this(parent) + public ImageContainer(ISurface parent, string filename) : this(parent) { Load(filename); } - public ImageContainer(Surface parent) : base(parent) + public ImageContainer(ISurface parent) : base(parent) { FieldChanged += BitmapContainer_OnFieldChanged; Init(); diff --git a/src/Greenshot.Editor/Drawing/LineContainer.cs b/src/Greenshot.Editor/Drawing/LineContainer.cs index 1bcf25a81..729872be6 100644 --- a/src/Greenshot.Editor/Drawing/LineContainer.cs +++ b/src/Greenshot.Editor/Drawing/LineContainer.cs @@ -23,6 +23,7 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Adorners; using Greenshot.Editor.Drawing.Fields; @@ -36,7 +37,7 @@ namespace Greenshot.Editor.Drawing [Serializable()] public class LineContainer : DrawableContainer { - public LineContainer(Surface parent) : base(parent) + public LineContainer(ISurface parent) : base(parent) { Init(); } diff --git a/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs b/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs index 1754ba650..b0c890ffc 100644 --- a/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs +++ b/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs @@ -21,6 +21,7 @@ using System; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Drawing.Filters; @@ -28,12 +29,12 @@ using Greenshot.Editor.Drawing.Filters; namespace Greenshot.Editor.Drawing { /// - /// Description of ObfuscateContainer. + /// This is a FilterContainer for the obfuscator filters like blur and pixelate. /// [Serializable] public class ObfuscateContainer : FilterContainer { - public ObfuscateContainer(Surface parent) : base(parent) + public ObfuscateContainer(ISurface parent) : base(parent) { Init(); } diff --git a/src/Greenshot.Editor/Drawing/RectangleContainer.cs b/src/Greenshot.Editor/Drawing/RectangleContainer.cs index 5e38fcf4b..4dd6543a7 100644 --- a/src/Greenshot.Editor/Drawing/RectangleContainer.cs +++ b/src/Greenshot.Editor/Drawing/RectangleContainer.cs @@ -23,6 +23,7 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -35,7 +36,7 @@ namespace Greenshot.Editor.Drawing [Serializable] public class RectangleContainer : DrawableContainer { - public RectangleContainer(Surface parent) : base(parent) + public RectangleContainer(ISurface parent) : base(parent) { Init(); } diff --git a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs index 2b189945d..0a19805e7 100644 --- a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs +++ b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs @@ -24,6 +24,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -64,8 +65,7 @@ namespace Greenshot.Editor.Drawing InitAdorner(Color.Green, _storedTargetGripperLocation); } - public SpeechbubbleContainer(Surface parent) - : base(parent) + public SpeechbubbleContainer(ISurface parent) : base(parent) { } diff --git a/src/Greenshot.Editor/Drawing/StepLabelContainer.cs b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs index 0e7c27516..8e9217721 100644 --- a/src/Greenshot.Editor/Drawing/StepLabelContainer.cs +++ b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs @@ -24,6 +24,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Runtime.Serialization; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -41,9 +42,9 @@ namespace Greenshot.Editor.Drawing private readonly bool _drawAsRectangle = false; - public StepLabelContainer(Surface parent) : base(parent) + public StepLabelContainer(ISurface parent) : base(parent) { - parent.AddStepLabel(this); + InternalParent?.AddStepLabel(this); InitContent(); Init(); } @@ -72,11 +73,10 @@ namespace Greenshot.Editor.Drawing [OnSerializing] private void SetValuesOnSerializing(StreamingContext context) { - if (Parent != null) - { - Number = ((Surface) Parent).CountStepLabels(this); - _counterStart = ((Surface) Parent).CounterStart; - } + if (InternalParent == null) return; + + Number = InternalParent.CountStepLabels(this); + _counterStart = InternalParent.CounterStart; } /// @@ -97,23 +97,23 @@ namespace Greenshot.Editor.Drawing /// Add the StepLabel to the parent /// /// - protected override void SwitchParent(Surface newParent) + protected override void SwitchParent(ISurface newParent) { if (newParent == Parent) { return; } - ((Surface) Parent)?.RemoveStepLabel(this); - base.SwitchParent(newParent); - if (newParent == null) + if (newParent is not Surface newParentSurface) { return; } + InternalParent?.RemoveStepLabel(this); + base.SwitchParent(newParent); // Make sure the counter start is restored (this unfortunately happens multiple times... -> hack) - newParent.CounterStart = _counterStart; - newParent.AddStepLabel(this); + newParentSurface.CounterStart = _counterStart; + newParentSurface.AddStepLabel(this); } public override Size DefaultSize => new Size(30, 30); diff --git a/src/Greenshot.Editor/Drawing/Surface.cs b/src/Greenshot.Editor/Drawing/Surface.cs index 5093ed201..2445cb486 100644 --- a/src/Greenshot.Editor/Drawing/Surface.cs +++ b/src/Greenshot.Editor/Drawing/Surface.cs @@ -294,7 +294,7 @@ namespace Greenshot.Editor.Drawing /// /// all elements on the surface, needed with serialization /// - private FieldAggregator _fieldAggregator; + private IFieldAggregator _fieldAggregator; /// /// the cursor container, needed with serialization as we need a direct acces to it. @@ -355,7 +355,7 @@ namespace Greenshot.Editor.Drawing /// The field aggregator is that which is used to have access to all the fields inside the currently selected elements. /// e.g. used to decided if and which line thickness is shown when multiple elements are selected. /// - public FieldAggregator FieldAggregator + public IFieldAggregator FieldAggregator { get => _fieldAggregator; set => _fieldAggregator = value; diff --git a/src/Greenshot.Editor/Drawing/SvgContainer.cs b/src/Greenshot.Editor/Drawing/SvgContainer.cs new file mode 100644 index 000000000..d1a3d1e20 --- /dev/null +++ b/src/Greenshot.Editor/Drawing/SvgContainer.cs @@ -0,0 +1,57 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://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 . + */ + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Svg; + +namespace Greenshot.Editor.Drawing +{ + /// + /// This provides a resizable SVG container, redrawing the SVG in the size the container takes. + /// + [Serializable] + public class SvgContainer : VectorGraphicsContainer + { + private SvgDocument _svgDocument; + + public SvgContainer(SvgDocument svgDocument, ISurface parent) : base(parent) + { + _svgDocument = svgDocument; + Size = new Size((int)svgDocument.Width, (int)svgDocument.Height); + } + + protected override Image ComputeBitmap() + { + var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppRgb, Color.Transparent); + + _svgDocument.Draw(image); + if (RotationAngle == 0) return image; + + var newImage = image.Rotate(RotationAngle); + image.Dispose(); + return newImage; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/TextContainer.cs b/src/Greenshot.Editor/Drawing/TextContainer.cs index 4d670b4e0..fdb718cbe 100644 --- a/src/Greenshot.Editor/Drawing/TextContainer.cs +++ b/src/Greenshot.Editor/Drawing/TextContainer.cs @@ -28,6 +28,7 @@ using System.Drawing.Text; using System.Runtime.Serialization; using System.Windows.Forms; using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Helpers; @@ -83,7 +84,7 @@ namespace Greenshot.Editor.Drawing OnPropertyChanged("Text"); } - public TextContainer(Surface parent) : base(parent) + public TextContainer(ISurface parent) : base(parent) { Init(); } @@ -154,17 +155,17 @@ namespace Greenshot.Editor.Drawing FieldChanged += TextContainer_FieldChanged; } - protected override void SwitchParent(Surface newParent) + protected override void SwitchParent(ISurface newParent) { - if (_parent != null) + if (InternalParent != null) { - _parent.SizeChanged -= Parent_SizeChanged; + InternalParent.SizeChanged -= Parent_SizeChanged; } base.SwitchParent(newParent); - if (_parent != null) + if (InternalParent != null) { - _parent.SizeChanged += Parent_SizeChanged; + InternalParent.SizeChanged += Parent_SizeChanged; } } @@ -221,10 +222,10 @@ namespace Greenshot.Editor.Drawing { ShowTextBox(); } - else if (_parent != null && Selected && Status == EditStatus.IDLE && _textBox.Visible) + else if (InternalParent != null && Selected && Status == EditStatus.IDLE && _textBox.Visible) { // Fix (workaround) for BUG-1698 - _parent.KeysLocked = true; + InternalParent.KeysLocked = true; } } @@ -295,10 +296,10 @@ namespace Greenshot.Editor.Drawing private void ShowTextBox() { - if (_parent != null) + if (InternalParent != null) { - _parent.KeysLocked = true; - _parent.Controls.Add(_textBox); + InternalParent.KeysLocked = true; + InternalParent.Controls.Add(_textBox); } EnsureTextBoxContrast(); @@ -332,15 +333,15 @@ namespace Greenshot.Editor.Drawing private void HideTextBox() { - _parent?.Focus(); + InternalParent?.Focus(); _textBox?.Hide(); - if (_parent == null) + if (InternalParent == null) { return; } - _parent.KeysLocked = false; - _parent.Controls.Remove(_textBox); + InternalParent.KeysLocked = false; + InternalParent.Controls.Remove(_textBox); } /// diff --git a/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs new file mode 100644 index 000000000..2ad97e093 --- /dev/null +++ b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs @@ -0,0 +1,126 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://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 . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Runtime.Serialization; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Editor.Drawing +{ + /// + /// This is the base container for vector graphics, these ae graphics which can resize without loss of quality. + /// Examples for this are SVG, WMF or EMF, but also graphics based on fonts (e.g. Emoji) + /// + [Serializable] + public abstract class VectorGraphicsContainer : DrawableContainer + { + protected int RotationAngle; + + /// + /// This is the cached version of the bitmap, pre-rendered to save performance + /// Do not serialized, it can be rebuild with some other information. + /// + [NonSerialized] private Image _cachedImage; + + public VectorGraphicsContainer(ISurface parent) : base(parent) + { + Init(); + } + + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } + + private void Init() + { + CreateDefaultAdorners(); + } + + /// + /// The bulk of the clean-up code is implemented in Dispose(bool) + /// This Dispose is called from the Dispose and the Destructor. + /// When disposing==true all non-managed resources should be freed too! + /// + /// + + protected override void Dispose(bool disposing) + { + if (disposing) + { + ResetCachedBitmap(); + } + + base.Dispose(disposing); + } + + public override void Transform(Matrix matrix) + { + RotationAngle += CalculateAngle(matrix); + RotationAngle %= 360; + + ResetCachedBitmap(); + + base.Transform(matrix); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + if (_cachedImage != null && _cachedImage.Size != Bounds.Size) + { + ResetCachedBitmap(); + } + + _cachedImage ??= ComputeBitmap(); + + graphics.DrawImage(_cachedImage, Bounds); + } + + protected virtual Image ComputeBitmap() + { + var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppRgb, Color.Transparent); + + if (RotationAngle == 0) return image; + + var newImage = image.Rotate(RotationAngle); + image.Dispose(); + return newImage; + + } + + private void ResetCachedBitmap() + { + _cachedImage?.Dispose(); + _cachedImage = null; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs new file mode 100644 index 000000000..95a683240 --- /dev/null +++ b/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs @@ -0,0 +1,86 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://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 . + */ + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using log4net; +using Svg; + +namespace Greenshot.Editor.FileFormatHandlers +{ + /// + /// This handled the loading of SVG images to the editor + /// + public class SvgFileFormatHandler : IFileFormatHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(ImageHelper)); + + private static readonly string[] SupportedExtensions = { "svg" }; + + public bool CanDoActionForExtension(FileFormatHandlerActions fileFormatHandlerAction, string extension) + { + if (fileFormatHandlerAction == FileFormatHandlerActions.SaveToStream) + { + return false; + } + return SupportedExtensions.Contains(extension); + } + + public void SaveToStream(Bitmap bitmap, Stream destination, string extension) + { + throw new NotImplementedException(); + } + + public Bitmap Load(Stream stream, string extension) + { + + var svgDocument = SvgDocument.Open(stream); + int width = (int)svgDocument.ViewBox.Width; + int height = (int)svgDocument.ViewBox.Height; + + try + { + var result = ImageHelper.CreateEmpty(width, height, PixelFormat.Format32bppArgb, Color.Transparent); + svgDocument.Draw(result); + return result; + } + catch (Exception ex) + { + Log.Error("Can't load SVG", ex); + } + + return null; + } + + public IDrawableContainer LoadDrawableFromStream(Stream stream, string extension, ISurface parent) + { + var svgDocument = SvgDocument.Open(stream); + return new SvgContainer(svgDocument, parent); + } + } +} diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.cs index 3e1be9489..1b83dee9c 100644 --- a/src/Greenshot.Editor/Forms/ImageEditorForm.cs +++ b/src/Greenshot.Editor/Forms/ImageEditorForm.cs @@ -56,13 +56,13 @@ namespace Greenshot.Editor.Forms private static readonly ILog Log = LogManager.GetLogger(typeof(ImageEditorForm)); private static readonly EditorConfiguration EditorConfiguration = IniConfig.GetIniSection(); - private static readonly List IgnoreDestinations = new List + private static readonly List IgnoreDestinations = new() { nameof(WellKnownDestinations.Picker), EditorDestination.DESIGNATION }; - private static readonly List EditorList = new List(); + private static readonly List EditorList = new(); private Surface _surface; private GreenshotToolStripButton[] _toolbarButtons; @@ -78,7 +78,7 @@ namespace Greenshot.Editor.Forms // whether part of the editor controls are disabled depending on selected item(s) private bool _controlsDisabledDueToConfirmable; - // Used for tracking the mouse scrollwheel changes + // Used for tracking the mouse scroll wheel changes private DateTime _zoomStartTime = DateTime.Now; /// @@ -1290,7 +1290,7 @@ namespace Greenshot.Editor.Forms propertiesToolStrip.SuspendLayout(); if (_surface.HasSelectedElements || _surface.DrawingMode != DrawingModes.None) { - FieldAggregator props = _surface.FieldAggregator; + var props = (FieldAggregator)_surface.FieldAggregator; btnFillColor.Visible = props.HasFieldValue(FieldType.FILL_COLOR); btnLineColor.Visible = props.HasFieldValue(FieldType.LINE_COLOR); lineThicknessLabel.Visible = lineThicknessUpDown.Visible = props.HasFieldValue(FieldType.LINE_THICKNESS); @@ -1350,7 +1350,7 @@ namespace Greenshot.Editor.Forms btnStepLabel.Image = icon; addCounterToolStripMenuItem.Image = icon; - FieldAggregator props = _surface.FieldAggregator; + FieldAggregator props = (FieldAggregator)_surface.FieldAggregator; // if a confirmable element is selected, we must disable most of the controls // since we demand confirmation or cancel for confirmable element if (props.HasFieldValue(FieldType.FLAGS) && ((FieldFlag) props.GetFieldValue(FieldType.FLAGS) & FieldFlag.CONFIRMABLE) == FieldFlag.CONFIRMABLE) diff --git a/src/Greenshot.Editor/Memento/AddElementMemento.cs b/src/Greenshot.Editor/Memento/AddElementMemento.cs index dbee523a6..1a95d19ce 100644 --- a/src/Greenshot.Editor/Memento/AddElementMemento.cs +++ b/src/Greenshot.Editor/Memento/AddElementMemento.cs @@ -20,9 +20,8 @@ */ using System; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing; - namespace Greenshot.Editor.Memento { /// @@ -31,9 +30,9 @@ namespace Greenshot.Editor.Memento public class AddElementMemento : IMemento { private IDrawableContainer _drawableContainer; - private Surface _surface; + private ISurface _surface; - public AddElementMemento(Surface surface, IDrawableContainer drawableContainer) + public AddElementMemento(ISurface surface, IDrawableContainer drawableContainer) { _surface = surface; _drawableContainer = drawableContainer; diff --git a/src/Greenshot.Editor/Memento/AddElementsMemento.cs b/src/Greenshot.Editor/Memento/AddElementsMemento.cs index 80c9d3d99..8649dec9b 100644 --- a/src/Greenshot.Editor/Memento/AddElementsMemento.cs +++ b/src/Greenshot.Editor/Memento/AddElementsMemento.cs @@ -19,8 +19,8 @@ * along with this program. If not, see . */ +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing; namespace Greenshot.Editor.Memento { @@ -30,9 +30,9 @@ namespace Greenshot.Editor.Memento public class AddElementsMemento : IMemento { private IDrawableContainerList _containerList; - private Surface _surface; + private ISurface _surface; - public AddElementsMemento(Surface surface, IDrawableContainerList containerList) + public AddElementsMemento(ISurface surface, IDrawableContainerList containerList) { _surface = surface; _containerList = containerList; diff --git a/src/Greenshot.Editor/Memento/DeleteElementMemento.cs b/src/Greenshot.Editor/Memento/DeleteElementMemento.cs index ade27b23a..0acc19f83 100644 --- a/src/Greenshot.Editor/Memento/DeleteElementMemento.cs +++ b/src/Greenshot.Editor/Memento/DeleteElementMemento.cs @@ -20,8 +20,8 @@ */ using System; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing; namespace Greenshot.Editor.Memento { @@ -31,9 +31,9 @@ namespace Greenshot.Editor.Memento public class DeleteElementMemento : IMemento { private IDrawableContainer _drawableContainer; - private readonly Surface _surface; + private readonly ISurface _surface; - public DeleteElementMemento(Surface surface, IDrawableContainer drawableContainer) + public DeleteElementMemento(ISurface surface, IDrawableContainer drawableContainer) { _surface = surface; _drawableContainer = drawableContainer; diff --git a/src/Greenshot.Editor/Memento/DeleteElementsMemento.cs b/src/Greenshot.Editor/Memento/DeleteElementsMemento.cs index d4d4a3be1..e4b0b747e 100644 --- a/src/Greenshot.Editor/Memento/DeleteElementsMemento.cs +++ b/src/Greenshot.Editor/Memento/DeleteElementsMemento.cs @@ -19,8 +19,8 @@ * along with this program. If not, see . */ +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing; namespace Greenshot.Editor.Memento { @@ -30,9 +30,9 @@ namespace Greenshot.Editor.Memento public class DeleteElementsMemento : IMemento { private IDrawableContainerList _containerList; - private Surface _surface; + private ISurface _surface; - public DeleteElementsMemento(Surface surface, IDrawableContainerList containerList) + public DeleteElementsMemento(ISurface surface, IDrawableContainerList containerList) { _surface = surface; _containerList = containerList; diff --git a/src/Greenshot.Editor/Memento/SurfaceBackgroundChangeMemento.cs b/src/Greenshot.Editor/Memento/SurfaceBackgroundChangeMemento.cs index 71d2e9f36..c6ba814c3 100644 --- a/src/Greenshot.Editor/Memento/SurfaceBackgroundChangeMemento.cs +++ b/src/Greenshot.Editor/Memento/SurfaceBackgroundChangeMemento.cs @@ -21,8 +21,8 @@ using System.Drawing; using System.Drawing.Drawing2D; +using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing; namespace Greenshot.Editor.Memento { @@ -32,10 +32,10 @@ namespace Greenshot.Editor.Memento public class SurfaceBackgroundChangeMemento : IMemento { private Image _image; - private Surface _surface; + private ISurface _surface; private Matrix _matrix; - public SurfaceBackgroundChangeMemento(Surface surface, Matrix matrix) + public SurfaceBackgroundChangeMemento(ISurface surface, Matrix matrix) { _surface = surface; _image = surface.Image;