This feature will make it easier to support different file formats later on, and also adds a "VectorGraphicsContainer" base class to better support non pixel graphics in the editor. Examples are SVG, WMF/EMF and Emoji which scale automatically to the correct size and are not resized from pixels.

This commit is contained in:
Robin Krom 2022-01-31 13:09:15 +01:00
commit 7e77f3678a
No known key found for this signature in database
GPG key ID: BCC01364F1371490
39 changed files with 934 additions and 368 deletions

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// THis is the default .NET bitmap file format handler
/// </summary>
public class DefaultFileFormatHandler : IFileFormatHandler
{
private static readonly string [] OurExtensions = { "png", "bmp", "gif", "jpg", "jpeg", "tiff", "tif" };
/// <inheritdoc />
public IEnumerable<string> SupportedExtensions(FileFormatHandlerActions fileFormatHandlerAction)
{
if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream)
{
return Enumerable.Empty<string>();
}
return OurExtensions;
}
/// <inheritdoc />
public bool Supports(FileFormatHandlerActions fileFormatHandlerAction, string extension)
{
if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream)
{
return false;
}
return OurExtensions.Contains(extension);
}
/// <inheritdoc />
public int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension)
{
return int.MaxValue;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
public bool TryLoadDrawableFromStream(Stream stream, string extension, out IDrawableContainer drawableContainer, ISurface surface)
{
throw new NotImplementedException();
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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<Func<ISurface>>().Invoke();
return (Bitmap)surface.GetImageForExport();
}
public IDrawableContainer LoadDrawableFromStream(Stream stream, string extension, ISurface parent)
{
throw new NotImplementedException();
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// THis is the default .NET bitmap file format handler
/// </summary>
public class IconFileFormatHandler : IFileFormatHandler
{
private static readonly ILog Log = LogManager.GetLogger(typeof(ImageHelper));
private static readonly string[] OurExtensions = { "ico" };
/// <inheritdoc />
public IEnumerable<string> SupportedExtensions(FileFormatHandlerActions fileFormatHandlerAction)
{
if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream)
{
return Enumerable.Empty<string>();
}
return OurExtensions;
}
/// <inheritdoc />
public bool Supports(FileFormatHandlerActions fileFormatHandlerAction, string extension)
{
if (fileFormatHandlerAction == FileFormatHandlerActions.LoadDrawableFromStream)
{
return false;
}
return OurExtensions.Contains(extension);
}
/// <inheritdoc />
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();
}
/// <summary>
/// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx
/// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx
/// </summary>
/// <param name="iconStream">Stream with the icon information</param>
/// <returns>Bitmap with the Vista Icon (256x256)</returns>
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;
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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();
}
}
}

View file

@ -61,7 +61,7 @@ namespace Greenshot.Base.Core
float HorizontalResolution { get; }
/// <summary>
/// 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
/// </summary>
Image Image { get; }
}

View file

@ -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<CoreConfiguration>();
private const int ExifOrientationId = 0x0112;
public static readonly IList<IFileFormatHandler> FileFormatHandlers = new List<IFileFormatHandler>();
static ImageHelper()
{
StreamConverters["greenshot"] = (stream, s) =>
{
var surface = SimpleServiceProvider.Current.GetInstance<Func<ISurface>>().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<string, Func<Stream, string, Image>> StreamConverters { get; } = new Dictionary<string, Func<Stream, string, Image>>();
/// <summary>
/// Make sure the image is orientated correctly
/// </summary>
@ -563,8 +495,7 @@ namespace Greenshot.Base.Core
/// <returns>Changed bitmap</returns>
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
/// <param name="height"></param>
/// <param name="format"></param>
/// <param name="backgroundColor">The color to fill with, or Color.Empty to take the default depending on the pixel format</param>
/// <param name="horizontalResolution"></param>
/// <param name="verticalResolution"></param>
/// <param name="horizontalResolution">float</param>
/// <param name="verticalResolution">float</param>
/// <returns>Bitmap</returns>
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;
/// <summary>
/// Rotate the image
/// </summary>
/// <param name="image">Input image</param>
/// <param name="rotationAngle">Angle in degrees</param>
/// <returns>Rotated image</returns>
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;
}
}
}

View file

@ -1,77 +0,0 @@
using System.Drawing;
using System.Drawing.Imaging;
namespace Greenshot.Base.Core
{
/// <summary>
/// Wrap an image, make it resizeable
/// </summary>
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();
}
/// <summary>
/// Height of the image, can be set to change
/// </summary>
public int Height { get; set; }
/// <summary>
/// Width of the image, can be set to change.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Size of the image
/// </summary>
public Size Size => new Size(Width, Height);
/// <summary>
/// Pixelformat of the underlying image
/// </summary>
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;
}
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Svg;
namespace Greenshot.Base.Core
{
/// <summary>
/// Create an image look like of the SVG
/// </summary>
public sealed class SvgImage : IImage
{
private readonly SvgDocument _svgDocument;
private Image _imageClone;
/// <summary>
/// Factory to create via a stream
/// </summary>
/// <param name="stream">Stream</param>
/// <returns>IImage</returns>
public static IImage FromStream(Stream stream)
{
return new SvgImage(stream);
}
/// <summary>
/// Default constructor
/// </summary>
/// <param name="stream"></param>
public SvgImage(Stream stream)
{
_svgDocument = SvgDocument.Open<SvgDocument>(stream);
Height = (int) _svgDocument.ViewBox.Height;
Width = (int) _svgDocument.ViewBox.Width;
}
/// <summary>
/// Height of the image, can be set to change
/// </summary>
public int Height { get; set; }
/// <summary>
/// Width of the image, can be set to change.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Size of the image
/// </summary>
public Size Size => new Size(Width, Height);
/// <summary>
/// Pixelformat of the underlying image
/// </summary>
public PixelFormat PixelFormat => Image.PixelFormat;
/// <summary>
/// Horizontal resolution of the underlying image
/// </summary>
public float HorizontalResolution => Image.HorizontalResolution;
/// <summary>
/// Vertical resolution of the underlying image
/// </summary>
public float VerticalResolution => Image.VerticalResolution;
/// <summary>
/// Underlying image, or an on demand rendered version with different attributes as the original
/// </summary>
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;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_imageClone?.Dispose();
}
}
}

View file

@ -135,7 +135,7 @@ namespace Greenshot.Base.Interfaces.Drawing
public interface IImageContainer : IDrawableContainer
{
Image Image { get; set; }
Image Image { get; }
void Load(string filename);
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Greenshot.Base.Interfaces.Drawing;
namespace Greenshot.Base.Interfaces
{
/// <summary>
/// The possible actions a IFileFormatHandler might support
/// </summary>
public enum FileFormatHandlerActions
{
SaveToStream,
Load,
LoadDrawableFromStream
}
/// <summary>
/// This interface is for code to implement the loading and saving of certain file formats
/// </summary>
public interface IFileFormatHandler
{
/// <summary>
/// Delivers the extension that the supplied action supports
/// </summary>
/// <param name="fileFormatHandlerAction">FileFormatHandlerActions</param>
/// <returns>IEnumerable{string}</returns>
IEnumerable<string> SupportedExtensions(FileFormatHandlerActions fileFormatHandlerAction);
/// <summary>
///Does this IFileFormatHandler support the specified action for the specified extension?
/// </summary>
/// <param name="fileFormatHandlerAction">FileFormatHandlerActions</param>
/// <param name="extension">string</param>
/// <returns>bool true if this IFileFormatHandler can support the action for the extension</returns>
public bool Supports(FileFormatHandlerActions fileFormatHandlerAction, string extension);
/// <summary>
/// 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
/// </summary>
/// <param name="fileFormatHandlerAction">FileFormatHandlerActions</param>
/// <param name="extension">string</param>
/// <returns>int specifying the priority for the action and extension</returns>
public int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension);
/// <summary>
/// Try to save the specified bitmap to the stream in the format belonging to the extension
/// </summary>
/// <param name="bitmap">Bitmap</param>
/// <param name="destination">Stream</param>
/// <param name="extension">extension</param>
/// <returns>bool true if it was successful</returns>
public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension);
/// <summary>
///
/// </summary>
/// <param name="stream"></param>
/// <param name="extension"></param>
/// <param name="bitmap"></param>
/// <returns>bool true if it was successful</returns>
public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap);
/// <summary>
/// Try to load a drawable container from the stream
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension">string</param>
/// <param name="drawableContainer">IDrawableContainer out</param>
/// <param name="parent">ISurface</param>
/// <returns>bool true if it was successful</returns>
public bool TryLoadDrawableFromStream(Stream stream, string extension, out IDrawableContainer drawableContainer, ISurface parent);
}
}

View file

@ -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; }
/// <summary>
/// This reverses a change of the background image
/// </summary>
/// <param name="previous">Image</param>
/// <param name="matrix">Matrix</param>
void UndoBackgroundChange(Image previous, Matrix matrix);
}
}

View file

@ -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)
{
}

View file

@ -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
/// </summary>
public class CropContainer : DrawableContainer
{
public CropContainer(Surface parent) : base(parent)
public CropContainer(ISurface parent) : base(parent)
{
Init();
}

View file

@ -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);
}

View file

@ -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)
{

View file

@ -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();
}

View file

@ -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.
/// </summary>
[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()

View file

@ -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();
}

View file

@ -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
/// <summary>
/// Constructor
/// </summary>
public FreehandContainer(Surface parent) : base(parent)
public FreehandContainer(ISurface parent) : base(parent)
{
Width = parent.Image.Width;
Height = parent.Image.Height;

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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
/// </summary>
[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();

View file

@ -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();
}

View file

@ -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
{
/// <summary>
/// Description of ObfuscateContainer.
/// This is a FilterContainer for the obfuscator filters like blur and pixelate.
/// </summary>
[Serializable]
public class ObfuscateContainer : FilterContainer
{
public ObfuscateContainer(Surface parent) : base(parent)
public ObfuscateContainer(ISurface parent) : base(parent)
{
Init();
}

View file

@ -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();
}

View file

@ -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)
{
}

View file

@ -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;
}
/// <summary>
@ -97,23 +97,23 @@ namespace Greenshot.Editor.Drawing
/// Add the StepLabel to the parent
/// </summary>
/// <param name="newParent"></param>
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);

View file

@ -294,7 +294,7 @@ namespace Greenshot.Editor.Drawing
/// <summary>
/// all elements on the surface, needed with serialization
/// </summary>
private FieldAggregator _fieldAggregator;
private IFieldAggregator _fieldAggregator;
/// <summary>
/// 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.
/// </summary>
public FieldAggregator FieldAggregator
public IFieldAggregator FieldAggregator
{
get => _fieldAggregator;
set => _fieldAggregator = value;

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Imaging;
using Greenshot.Base.Core;
using Greenshot.Base.Interfaces;
using Svg;
namespace Greenshot.Editor.Drawing
{
/// <summary>
/// This provides a resizable SVG container, redrawing the SVG in the size the container takes.
/// </summary>
[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;
}
}
}

View file

@ -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);
}
/// <summary>

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// 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)
/// </summary>
[Serializable]
public abstract class VectorGraphicsContainer : DrawableContainer
{
protected int RotationAngle;
/// <summary>
/// This is the cached version of the bitmap, pre-rendered to save performance
/// Do not serialized, it can be rebuild with some other information.
/// </summary>
[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();
}
/// <summary>
/// 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!
/// </summary>
/// <param name="disposing"></param>
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;
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// This handled the loading of SVG images to the editor
/// </summary>
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<SvgDocument>(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<SvgDocument>(stream);
return new SvgContainer(svgDocument, parent);
}
}
}

View file

@ -56,13 +56,13 @@ namespace Greenshot.Editor.Forms
private static readonly ILog Log = LogManager.GetLogger(typeof(ImageEditorForm));
private static readonly EditorConfiguration EditorConfiguration = IniConfig.GetIniSection<EditorConfiguration>();
private static readonly List<string> IgnoreDestinations = new List<string>
private static readonly List<string> IgnoreDestinations = new()
{
nameof(WellKnownDestinations.Picker),
EditorDestination.DESIGNATION
};
private static readonly List<IImageEditor> EditorList = new List<IImageEditor>();
private static readonly List<IImageEditor> 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;
/// <summary>
@ -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)

View file

@ -20,9 +20,8 @@
*/
using System;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing;
namespace Greenshot.Editor.Memento
{
/// <summary>
@ -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;

View file

@ -19,8 +19,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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;

View file

@ -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;

View file

@ -19,8 +19,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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;

View file

@ -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;