From 60956771c85b6a104e9547d033d71cf76557056d Mon Sep 17 00:00:00 2001 From: Robin Krom Date: Sun, 20 Feb 2022 13:21:32 +0100 Subject: [PATCH] Feature Improve file format support (#385) 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. Multiple new formats have been added for reading, Jpeg XR for writing. --- .../Controls/SaveImageFileDialog.cs | 3 +- src/Greenshot.Base/Core/ClipboardHelper.cs | 518 +++++++++++------- .../Core/Enums/ExifOrientations.cs | 36 ++ src/Greenshot.Base/Core/Enums/OutputFormat.cs | 1 + .../FileFormatHandlerExtensions.cs | 163 ++++++ src/Greenshot.Base/Core/IImage.cs | 2 +- src/Greenshot.Base/Core/ImageHelper.cs | 391 ++++--------- .../Core/{ImageOutput.cs => ImageIO.cs} | 508 ++++++++--------- src/Greenshot.Base/Core/ImageWrapper.cs | 77 --- src/Greenshot.Base/Core/NetworkHelper.cs | 104 +++- src/Greenshot.Base/Core/PluginUtils.cs | 2 +- .../Core/SimpleServiceProvider.cs | 11 +- src/Greenshot.Base/Core/SvgImage.cs | 117 ---- src/Greenshot.Base/Core/WindowDetails.cs | 4 +- .../Interfaces/Drawing/IDrawableContainer.cs | 17 + .../Interfaces/Drawing/IFieldAggregator.cs | 33 ++ .../Interfaces/IFileFormatHandler.cs | 88 +++ .../Interfaces/IServiceLocator.cs | 2 +- src/Greenshot.Base/Interfaces/ISurface.cs | 10 + .../Plugin/SurfaceOutputSettings.cs | 23 +- .../Enums/WindowStyleFlags.cs | 2 +- src/Greenshot.Base/UnmanagedHelpers/User32.cs | 12 +- .../Drawing/ArrowContainer.cs | 3 +- src/Greenshot.Editor/Drawing/CropContainer.cs | 3 +- .../Drawing/CursorContainer.cs | 5 +- .../Drawing/DrawableContainer.cs | 15 +- .../Drawing/EllipseContainer.cs | 3 +- .../Drawing/Fields/FieldAggregator.cs | 11 +- .../Drawing/FilterContainer.cs | 3 +- .../Drawing/FreehandContainer.cs | 3 +- .../Drawing/HighlightContainer.cs | 3 +- src/Greenshot.Editor/Drawing/IconContainer.cs | 22 +- .../Drawing/ImageContainer.cs | 38 +- src/Greenshot.Editor/Drawing/LineContainer.cs | 3 +- .../Drawing/MetafileContainer.cs | 81 +++ .../Drawing/ObfuscateContainer.cs | 5 +- .../Drawing/RectangleContainer.cs | 3 +- .../Drawing/SpeechbubbleContainer.cs | 4 +- .../Drawing/StepLabelContainer.cs | 26 +- src/Greenshot.Editor/Drawing/Surface.cs | 109 ++-- src/Greenshot.Editor/Drawing/SvgContainer.cs | 61 +++ src/Greenshot.Editor/Drawing/TextContainer.cs | 31 +- .../Drawing/VectorGraphicsContainer.cs | 117 ++++ src/Greenshot.Editor/EditorInitialize.cs | 50 ++ .../AbstractFileFormatHandler.cs | 66 +++ .../DefaultFileFormatHandler.cs | 137 +++++ .../DibFileFormatHandler.cs} | 105 +++- .../GreenshotFileFormatHandler.cs | 133 +++++ .../IconFileFormatHandler.cs | 218 ++++++++ .../MetaFileFormatHandler.cs | 81 +++ .../SvgFileFormatHandler.cs | 89 +++ .../WpfFileFormatHandler.cs | 144 +++++ src/Greenshot.Editor/Forms/ImageEditorForm.cs | 24 +- src/Greenshot.Editor/Greenshot.Editor.csproj | 3 + .../Memento/AddElementMemento.cs | 7 +- .../Memento/AddElementsMemento.cs | 6 +- .../Memento/DeleteElementMemento.cs | 6 +- .../Memento/DeleteElementsMemento.cs | 6 +- .../Memento/SurfaceBackgroundChangeMemento.cs | 6 +- .../Forms/SettingsForm.Designer.cs | 4 +- .../ConfluenceDestination.cs | 4 +- ...nfiguration.cs => DropboxConfiguration.cs} | 145 +++-- .../DropboxDestination.cs | 2 +- src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs | 4 +- src/Greenshot.Plugin.Dropbox/DropboxUtils.cs | 2 +- .../Forms/SettingsForm.Designer.cs | 4 +- .../ExternalCommandDestination.cs | 2 +- .../Forms/SettingsForm.Designer.cs | 14 +- .../Forms/SettingsForm.Designer.cs | 4 +- .../Forms/SettingsForm.Designer.cs | 6 +- src/Greenshot.Plugin.Imgur/ImgurUtils.cs | 5 +- .../Forms/SettingsForm.Designer.cs | 4 +- .../Destinations/ExcelDestination.cs | 4 +- .../Destinations/OutlookDestination.cs | 2 +- .../Destinations/PowerpointDestination.cs | 2 +- .../Destinations/WordDestination.cs | 2 +- .../OfficeExport/OneNoteExporter.cs | 2 +- .../Forms/SettingsForm.Designer.cs | 4 +- .../Destinations/Win10ShareDestination.cs | 6 +- .../Win10OcrProvider.cs | 4 +- src/Greenshot/App.config | 1 - src/Greenshot/Destinations/FileDestination.cs | 8 +- .../Destinations/FileWithDialogDestination.cs | 2 +- src/Greenshot/Forms/MainForm.cs | 23 +- .../Forms/PrintOptionsDialog.Designer.cs | 17 +- src/Greenshot/Forms/SettingsForm.Designer.cs | 77 +-- src/Greenshot/Forms/SettingsForm.cs | 14 +- src/Greenshot/Helpers/CaptureHelper.cs | 7 +- src/Greenshot/Helpers/MailHelper.cs | 2 +- src/Greenshot/Helpers/PrintHelper.cs | 2 +- 90 files changed, 2793 insertions(+), 1340 deletions(-) create mode 100644 src/Greenshot.Base/Core/Enums/ExifOrientations.cs create mode 100644 src/Greenshot.Base/Core/FileFormatHandlers/FileFormatHandlerExtensions.cs rename src/Greenshot.Base/Core/{ImageOutput.cs => ImageIO.cs} (60%) delete mode 100644 src/Greenshot.Base/Core/ImageWrapper.cs delete mode 100644 src/Greenshot.Base/Core/SvgImage.cs create mode 100644 src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs create mode 100644 src/Greenshot.Base/Interfaces/IFileFormatHandler.cs create mode 100644 src/Greenshot.Editor/Drawing/MetafileContainer.cs create mode 100644 src/Greenshot.Editor/Drawing/SvgContainer.cs create mode 100644 src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs create mode 100644 src/Greenshot.Editor/EditorInitialize.cs create mode 100644 src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs create mode 100644 src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs rename src/{Greenshot.Base/Core/DibHelper.cs => Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs} (59%) create mode 100644 src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs create mode 100644 src/Greenshot.Editor/FileFormatHandlers/IconFileFormatHandler.cs create mode 100644 src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs create mode 100644 src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs create mode 100644 src/Greenshot.Editor/FileFormatHandlers/WpfFileFormatHandler.cs rename src/Greenshot.Plugin.Dropbox/{DropboxPluginConfiguration.cs => DropboxConfiguration.cs} (92%) diff --git a/src/Greenshot.Base/Controls/SaveImageFileDialog.cs b/src/Greenshot.Base/Controls/SaveImageFileDialog.cs index 3c9b2780e..091152d64 100644 --- a/src/Greenshot.Base/Controls/SaveImageFileDialog.cs +++ b/src/Greenshot.Base/Controls/SaveImageFileDialog.cs @@ -120,6 +120,7 @@ namespace Greenshot.Base.Controls private void PrepareFilterOptions() { + // TODO: Change to the FileFormatHandlerRegistry to look for all the supported extensions OutputFormat[] supportedImageFormats = (OutputFormat[]) Enum.GetValues(typeof(OutputFormat)); _filterOptions = new FilterOption[supportedImageFormats.Length]; for (int i = 0; i < _filterOptions.Length; i++) @@ -166,7 +167,7 @@ namespace Greenshot.Base.Controls // if the filename contains a valid extension, which is the same like the selected filter item's extension, the filename is okay if (fn.EndsWith(Extension, StringComparison.CurrentCultureIgnoreCase)) return fn; // otherwise we just add the selected filter item's extension - else return fn + "." + Extension; + return fn + "." + Extension; } set { diff --git a/src/Greenshot.Base/Core/ClipboardHelper.cs b/src/Greenshot.Base/Core/ClipboardHelper.cs index a990beca3..a6e1da2a0 100644 --- a/src/Greenshot.Base/Core/ClipboardHelper.cs +++ b/src/Greenshot.Base/Core/ClipboardHelper.cs @@ -31,8 +31,10 @@ using System.Text; using System.Threading; using System.Windows.Forms; using Greenshot.Base.Core.Enums; +using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.UnmanagedHelpers; using log4net; @@ -63,7 +65,8 @@ namespace Greenshot.Base.Core //private static readonly string FORMAT_HTML = "HTML Format"; // Template for the HTML Text on the clipboard - // see: https://msdn.microsoft.com/en-us/library/ms649015%28v=vs.85%29.aspx + // see: https://msdn.microsoft.com/en-us/library/ms649015%28v=v + // s.85%29.aspx // or: https://msdn.microsoft.com/en-us/library/Aa767917.aspx private const string HtmlClipboardString = @"Version:0.9 StartHTML:<<<<<<<1 @@ -298,14 +301,15 @@ EndSelection:<<<<<<<4 { return true; } - - foreach (var fileData in IterateClipboardContent(dataObject)) + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList(); + foreach (var (stream, filename) in IterateClipboardContent(dataObject)) { try { - using (ImageHelper.FromStream(fileData)) + var extension = Path.GetExtension(filename)?.ToLowerInvariant(); + if (supportedExtensions.Contains(extension)) { - // If we get here, there is an image return true; } } @@ -315,7 +319,7 @@ EndSelection:<<<<<<<4 } finally { - fileData?.Dispose(); + stream?.Dispose(); } } @@ -327,7 +331,8 @@ EndSelection:<<<<<<<4 var imageStream = clipboardContent as MemoryStream; if (IsValidStream(imageStream)) { - using (ImageHelper.FromStream(imageStream)) + // TODO: How to check if we support "just a stream"? + using (ImageIO.FromStream(imageStream)) { // If we get here, there is an image return true; @@ -373,8 +378,8 @@ EndSelection:<<<<<<<4 /// Iterate the clipboard content /// /// IDataObject - /// IEnumerable{MemoryStream} - private static IEnumerable IterateClipboardContent(IDataObject dataObject) + /// IEnumerable{(MemoryStream,string)} + private static IEnumerable<(MemoryStream stream,string filename)> IterateClipboardContent(IDataObject dataObject) { var fileDescriptors = AvailableFileDescriptors(dataObject); if (fileDescriptors == null) yield break; @@ -413,8 +418,8 @@ EndSelection:<<<<<<<4 /// /// IEnumerable{FileDescriptor} /// IDataObject - /// IEnumerable{MemoryStream} - private static IEnumerable IterateFileDescriptors(IEnumerable fileDescriptors, IDataObject dataObject) + /// IEnumerable{(MemoryStream stream, string filename)} + private static IEnumerable<(MemoryStream stream, string filename)> IterateFileDescriptors(IEnumerable fileDescriptors, IDataObject dataObject) { if (fileDescriptors == null) { @@ -445,7 +450,7 @@ EndSelection:<<<<<<<4 if (fileData?.Length > 0) { fileData.Position = 0; - yield return fileData; + yield return (fileData, fileDescriptor.FileName); } fileIndex++; @@ -490,7 +495,7 @@ EndSelection:<<<<<<<4 { IDataObject clipboardData = GetDataObject(); // Return the first image - foreach (Image clipboardImage in GetImages(clipboardData)) + foreach (var clipboardImage in GetImages(clipboardData)) { return clipboardImage; } @@ -503,57 +508,154 @@ EndSelection:<<<<<<<4 /// Returned images must be disposed by the calling code! /// /// - /// IEnumerable of Image - public static IEnumerable GetImages(IDataObject dataObject) + /// IEnumerable of Bitmap + public static IEnumerable GetImages(IDataObject dataObject) { // Get single image, this takes the "best" match - Image singleImage = GetImage(dataObject); + Bitmap singleImage = GetImage(dataObject); if (singleImage != null) { - Log.InfoFormat("Got image from clipboard with size {0} and format {1}", singleImage.Size, singleImage.PixelFormat); + Log.InfoFormat($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}"); yield return singleImage; + yield break; } - else + + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList(); + + foreach (var (stream, filename) in IterateClipboardContent(dataObject)) { - foreach (var fileData in IterateClipboardContent(dataObject)) + var extension = Path.GetExtension(filename)?.ToLowerInvariant(); + if (!supportedExtensions.Contains(extension)) { - Image image; - try - { - image = ImageHelper.FromStream(fileData); - } - catch (Exception ex) - { - Log.Error("Couldn't read file contents", ex); - continue; - } - finally - { - fileData?.Dispose(); - } - // If we get here, there is an image - yield return image; + continue; } - // check if files are supplied - foreach (string imageFile in GetImageFilenames(dataObject)) + Bitmap bitmap = null; + + try { - Image returnImage = null; - try + if (!fileFormatHandlers.TryLoadFromStream(stream, extension, out bitmap)) { - returnImage = ImageHelper.LoadImage(imageFile); - } - catch (Exception streamImageEx) - { - Log.Error("Problem retrieving Image from clipboard.", streamImageEx); + continue; } - if (returnImage != null) + } + catch (Exception ex) + { + Log.Error("Couldn't read file contents", ex); + continue; + } + finally + { + stream?.Dispose(); + } + // If we get here, there is an image + yield return bitmap; + } + + // check if files are supplied + foreach (string imageFile in GetImageFilenames(dataObject)) + { + var extension = Path.GetExtension(imageFile)?.ToLowerInvariant(); + if (!supportedExtensions.Contains(extension)) + { + continue; + } + + Bitmap bitmap = null; + using FileStream fileStream = new FileStream(imageFile, FileMode.Open, FileAccess.Read, FileShare.Read); + try + { + if (!fileFormatHandlers.TryLoadFromStream(fileStream, extension, out bitmap)) { - Log.InfoFormat("Got image from clipboard with size {0} and format {1}", returnImage.Size, returnImage.PixelFormat); - yield return returnImage; + continue; } } + catch (Exception ex) + { + Log.Error("Couldn't read file contents", ex); + continue; + } + // If we get here, there is an image + yield return bitmap; + } + } + + /// + /// Get all images (multiple if file names are available) from the dataObject + /// Returned images must be disposed by the calling code! + /// + /// + /// IEnumerable of IDrawableContainer + public static IEnumerable GetDrawables(IDataObject dataObject) + { + // Get single image, this takes the "best" match + IDrawableContainer singleImage = GetDrawable(dataObject); + if (singleImage != null) + { + Log.InfoFormat($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}"); + yield return singleImage; + yield break; + } + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList(); + + foreach (var (stream, filename) in IterateClipboardContent(dataObject)) + { + var extension = Path.GetExtension(filename)?.ToLowerInvariant(); + if (!supportedExtensions.Contains(extension)) + { + continue; + } + + IEnumerable drawableContainers; + try + { + drawableContainers = fileFormatHandlers.LoadDrawablesFromStream(stream, extension); + } + catch (Exception ex) + { + Log.Error("Couldn't read file contents", ex); + continue; + } + finally + { + stream?.Dispose(); + } + // If we get here, there is an image + foreach (var container in drawableContainers) + { + yield return container; + } + } + + // check if files are supplied + foreach (string imageFile in GetImageFilenames(dataObject)) + { + var extension = Path.GetExtension(imageFile)?.ToLowerInvariant(); + if (!supportedExtensions.Contains(extension)) + { + continue; + } + + IDrawableContainer drawableContainer = null; + using FileStream fileStream = new FileStream(imageFile, FileMode.Open, FileAccess.Read, FileShare.Read); + IEnumerable drawableContainers; + try + { + drawableContainers = fileFormatHandlers.LoadDrawablesFromStream(fileStream, extension); + } + catch (Exception ex) + { + Log.Error("Couldn't read file contents", ex); + continue; + } + // If we get here, there is an image + foreach (var container in drawableContainers) + { + yield return container; + } } } @@ -562,51 +664,50 @@ EndSelection:<<<<<<<4 /// /// /// Image or null - private static Image GetImage(IDataObject dataObject) + private static Bitmap GetImage(IDataObject dataObject) { - Image returnImage = null; - if (dataObject != null) - { - IList formats = GetFormats(dataObject); - string[] retrieveFormats; + if (dataObject == null) return null; - // Found a weird bug, where PNG's from Outlook 2010 are clipped - // So I build some special logic to get the best format: - if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) + Bitmap returnImage = null; + IList formats = GetFormats(dataObject); + string[] retrieveFormats; + + // Found a weird bug, where PNG's from Outlook 2010 are clipped + // So I build some special logic to get the best format: + if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) + { + // Outlook ?? + Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front..."); + retrieveFormats = new[] { - // Outlook ?? - Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front..."); - retrieveFormats = new[] - { - DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, - DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML - }; + DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, + DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML + }; + } + else + { + retrieveFormats = new[] + { + FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP, + FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML + }; + } + + foreach (string currentFormat in retrieveFormats) + { + if (formats != null && formats.Contains(currentFormat)) + { + Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat); + returnImage = GetImageForFormat(currentFormat, dataObject); } else { - retrieveFormats = new[] - { - FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP, - FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML - }; + Log.DebugFormat("Couldn't find format {0}.", currentFormat); } - foreach (string currentFormat in retrieveFormats) + if (returnImage != null) { - if (formats != null && formats.Contains(currentFormat)) - { - Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat); - returnImage = GetImageForFormat(currentFormat, dataObject); - } - else - { - Log.DebugFormat("Couldn't find format {0}.", currentFormat); - } - - if (returnImage != null) - { - return returnImage; - } + return returnImage; } } @@ -614,15 +715,72 @@ EndSelection:<<<<<<<4 } /// - /// Helper method to try to get an image in the specified format from the dataObject + /// Get an IDrawableContainer from the IDataObject, don't check for FileDrop + /// + /// + /// Image or null + private static IDrawableContainer GetDrawable(IDataObject dataObject) + { + if (dataObject == null) return null; + + IDrawableContainer returnImage = null; + IList formats = GetFormats(dataObject); + string[] retrieveFormats; + + // Found a weird bug, where PNG's from Outlook 2010 are clipped + // So I build some special logic to get the best format: + if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) + { + // Outlook ?? + Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front..."); + retrieveFormats = new[] + { + DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, + DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML + }; + } + else + { + retrieveFormats = new[] + { + FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP, + FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML + }; + } + + foreach (string currentFormat in retrieveFormats) + { + if (formats != null && formats.Contains(currentFormat)) + { + Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat); + returnImage = GetDrawableForFormat(currentFormat, dataObject); + } + else + { + Log.DebugFormat("Couldn't find format {0}.", currentFormat); + } + + if (returnImage != null) + { + return returnImage; + } + } + + return null; + } + + /// + /// Helper method to try to get an Bitmap in the specified format from the dataObject /// the DIB reader should solve some issues /// It also supports Format17/DibV5, by using the following information: https://stackoverflow.com/a/14335591 /// /// string with the format /// IDataObject - /// Image or null - private static Image GetImageForFormat(string format, IDataObject dataObject) + /// Bitmap or null + private static Bitmap GetImageForFormat(string format, IDataObject dataObject) { + Bitmap bitmap = null; + if (format == FORMAT_HTML) { var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8); @@ -638,10 +796,10 @@ EndSelection:<<<<<<<4 var srcAttribute = imgNode.Attributes["src"]; var imageUrl = srcAttribute.Value; Log.Debug(imageUrl); - var image = NetworkHelper.DownloadImage(imageUrl); - if (image != null) + bitmap = NetworkHelper.DownloadImage(imageUrl); + if (bitmap != null) { - return image; + return bitmap; } } } @@ -652,111 +810,80 @@ EndSelection:<<<<<<<4 var imageStream = clipboardObject as MemoryStream; if (!IsValidStream(imageStream)) { - // TODO: add "HTML Format" support here... - return clipboardObject as Image; + return clipboardObject as Bitmap; } + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - if (CoreConfig.EnableSpecialDIBClipboardReader) + // From here, imageStream is a valid stream + if (!fileFormatHandlers.TryLoadFromStream(imageStream, format, out bitmap)) { - if (format == FORMAT_17 || format == DataFormats.Dib) + return bitmap; + } + return null; + } + + /// + /// Helper method to try to get an IDrawableContainer in the specified format from the dataObject + /// the DIB reader should solve some issues + /// It also supports Format17/DibV5, by using the following information: https://stackoverflow.com/a/14335591 + /// + /// string with the format + /// IDataObject + /// IDrawableContainer or null + private static IDrawableContainer GetDrawableForFormat(string format, IDataObject dataObject) + { + IDrawableContainer drawableContainer = null; + + if (format == FORMAT_HTML) + { + var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8); + if (textObject != null) { - Log.Info("Found DIB stream, trying to process it."); - try + var doc = new HtmlDocument(); + doc.LoadHtml(textObject); + var imgNodes = doc.DocumentNode.SelectNodes("//img"); + if (imgNodes != null) { - if (imageStream != null) + foreach (var imgNode in imgNodes) { - byte[] dibBuffer = new byte[imageStream.Length]; - _ = imageStream.Read(dibBuffer, 0, dibBuffer.Length); - var infoHeader = BinaryStructHelper.FromByteArray(dibBuffer); - if (!infoHeader.IsDibV5) + var srcAttribute = imgNode.Attributes["src"]; + var imageUrl = srcAttribute.Value; + Log.Debug(imageUrl); + drawableContainer = NetworkHelper.DownloadImageAsDrawableContainer(imageUrl); + if (drawableContainer != null) { - Log.InfoFormat("Using special DIB (); + + return fileFormatHandlers.LoadDrawablesFromStream(imageStream, format).FirstOrDefault(); } /// @@ -840,7 +967,7 @@ EndSelection:<<<<<<<4 { SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); // Create the image which is going to be saved so we don't create it multiple times - disposeImage = ImageOutput.CreateImageFromSurface(surface, outputSettings, out imageToSave); + disposeImage = ImageIO.CreateImageFromSurface(surface, outputSettings, out imageToSave); try { // Create PNG stream @@ -849,7 +976,7 @@ EndSelection:<<<<<<<4 pngStream = new MemoryStream(); // PNG works for e.g. Powerpoint SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); - ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings); + ImageIO.SaveToStream(imageToSave, null, pngStream, pngOutputSettings); pngStream.Seek(0, SeekOrigin.Begin); // Set the PNG stream dataObject.SetData(FORMAT_PNG, false, pngStream); @@ -866,11 +993,18 @@ EndSelection:<<<<<<<4 { // Create the stream for the clipboard dibStream = new MemoryStream(); - var dibBytes = ((Bitmap)imageToSave).ConvertToDib(); - dibStream.Write(dibBytes,0, dibBytes.Length); + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); - // Set the DIB to the clipboard DataObject - dataObject.SetData(DataFormats.Dib, false, dibStream); + if (!fileFormatHandlers.TrySaveToStream((Bitmap)imageToSave, dibStream, DataFormats.Dib)) + { + dibStream.Dispose(); + dibStream = null; + } + else + { + // Set the DIB to the clipboard DataObject + dataObject.SetData(DataFormats.Dib, false, dibStream); + } } } catch (Exception dibEx) @@ -924,7 +1058,7 @@ EndSelection:<<<<<<<4 // Set the HTML if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML)) { - string tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null); + string tmpFile = ImageIO.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null); string html = GetHtmlString(surface, tmpFile); dataObject.SetText(html, TextDataFormat.Html); } @@ -942,11 +1076,11 @@ EndSelection:<<<<<<<4 // Check if we can use the previously used image if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed) { - ImageOutput.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings); + ImageIO.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings); } else { - ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings); + ImageIO.SaveToStream(surface, tmpPngStream, pngOutputSettings); } html = GetHtmlDataUrlString(surface, tmpPngStream); @@ -1125,15 +1259,15 @@ EndSelection:<<<<<<<4 public static IEnumerable GetImageFilenames(IDataObject dataObject) { string[] dropFileNames = (string[])dataObject.GetData(DataFormats.FileDrop); - if (dropFileNames != null && dropFileNames.Length > 0) - { - return dropFileNames - .Where(filename => !string.IsNullOrEmpty(filename)) - .Where(Path.HasExtension) - .Where(filename => ImageHelper.StreamConverters.Keys.Contains(Path.GetExtension(filename).ToLowerInvariant().Substring(1))); - } + if (dropFileNames is not { Length: > 0 }) return Enumerable.Empty(); + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + + var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream).ToList(); + return dropFileNames + .Where(filename => !string.IsNullOrEmpty(filename)) + .Where(Path.HasExtension) + .Where(filename => supportedExtensions.Contains(Path.GetExtension(filename))); - return Enumerable.Empty(); } /// diff --git a/src/Greenshot.Base/Core/Enums/ExifOrientations.cs b/src/Greenshot.Base/Core/Enums/ExifOrientations.cs new file mode 100644 index 000000000..b566619da --- /dev/null +++ b/src/Greenshot.Base/Core/Enums/ExifOrientations.cs @@ -0,0 +1,36 @@ +/* + * 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.Core.Enums +{ + internal enum ExifOrientations : byte + { + Unknown = 0, + TopLeft = 1, + TopRight = 2, + BottomRight = 3, + BottomLeft = 4, + LeftTop = 5, + RightTop = 6, + RightBottom = 7, + LeftBottom = 8, + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Enums/OutputFormat.cs b/src/Greenshot.Base/Core/Enums/OutputFormat.cs index 64d8a614b..6e2353eb8 100644 --- a/src/Greenshot.Base/Core/Enums/OutputFormat.cs +++ b/src/Greenshot.Base/Core/Enums/OutputFormat.cs @@ -31,6 +31,7 @@ namespace Greenshot.Base.Core.Enums jpg, png, tiff, + jxr, greenshot, ico } diff --git a/src/Greenshot.Base/Core/FileFormatHandlers/FileFormatHandlerExtensions.cs b/src/Greenshot.Base/Core/FileFormatHandlers/FileFormatHandlerExtensions.cs new file mode 100644 index 000000000..8941c7184 --- /dev/null +++ b/src/Greenshot.Base/Core/FileFormatHandlers/FileFormatHandlerExtensions.cs @@ -0,0 +1,163 @@ +/* + * 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 System.Linq; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.Interfaces.Plugin; + +namespace Greenshot.Base.Core.FileFormatHandlers +{ + /// + /// This is the registry where all IFileFormatHandler are registered and can be used + /// + public static class FileFormatHandlerExtensions + { + /// + /// Make sure we handle the input extension always the same, by "normalizing" it + /// + /// string + /// string + public static string NormalizeExtension(string extension) + { + if (string.IsNullOrEmpty(extension)) + { + return null; + } + + extension = extension.ToLowerInvariant(); + return !extension.StartsWith(".") ? $".{extension}" : extension; + } + + /// + /// Return the extensions that the provided IFileFormatHandlers can accept for the specified action + /// + /// IEnumerable{IFileFormatHandler} + /// + /// + public static IEnumerable ExtensionsFor(this IEnumerable fileFormatHandlers, FileFormatHandlerActions fileFormatHandlerAction) + { + return fileFormatHandlers.Where(ffh => ffh.SupportedExtensions.ContainsKey(fileFormatHandlerAction)).SelectMany(ffh => ffh.SupportedExtensions[fileFormatHandlerAction]).Distinct().OrderBy(e => e); + } + + /// + /// Extension method to check if a certain IFileFormatHandler supports a certain action with a specific extension + /// + /// IFileFormatHandler + /// FileFormatHandlerActions + /// string + /// bool + public static bool Supports(this IFileFormatHandler fileFormatHandler, FileFormatHandlerActions fileFormatHandlerAction, string extension) + { + extension = NormalizeExtension(extension); + return fileFormatHandler.SupportedExtensions.ContainsKey(fileFormatHandlerAction) && fileFormatHandler.SupportedExtensions[fileFormatHandlerAction].Contains(extension); + } + + /// + /// This wrapper method for TrySaveToStream will do: + /// Find all the IFileFormatHandler which support the action for the supplied extension. + /// Take the first, to call the TrySaveToStream on. + /// + /// IEnumerable{IFileFormatHandler} + /// Bitmap + /// Stream + /// string + /// ISurface + /// bool + public static bool TrySaveToStream(this IEnumerable fileFormatHandlers, Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + extension = NormalizeExtension(extension); + + var saveFileFormatHandlers = fileFormatHandlers + .Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension)) + .OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension)).ToList(); + + if (!saveFileFormatHandlers.Any()) + { + return false; + } + + foreach (var fileFormatHandler in saveFileFormatHandlers) + { + if (fileFormatHandler.TrySaveToStream(bitmap, destination, extension, surface)) + { + return true; + } + } + + return false; + } + + /// + /// Try to load a drawable container from the stream + /// + /// IEnumerable{IFileFormatHandler} + /// Stream + /// string + /// ISurface + /// IEnumerable{IDrawableContainer} + public static IEnumerable LoadDrawablesFromStream(this IEnumerable fileFormatHandlers, Stream stream, string extension, ISurface parentSurface = null) + { + extension = NormalizeExtension(extension); + + var loadfileFormatHandler = fileFormatHandlers + .Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadDrawableFromStream, extension)) + .OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadDrawableFromStream, extension)) + .FirstOrDefault(); + + if (loadfileFormatHandler != null) + { + return loadfileFormatHandler.LoadDrawablesFromStream(stream, extension, parentSurface); + } + + return Enumerable.Empty(); + } + + /// + /// Try to load a Bitmap from the stream + /// + /// IEnumerable{IFileFormatHandler} + /// Stream + /// string + /// Bitmap out + /// bool true if it was successful + public static bool TryLoadFromStream(this IEnumerable fileFormatHandlers, Stream stream, string extension, out Bitmap bitmap) + { + extension = NormalizeExtension(extension); + + var loadFileFormatHandler = fileFormatHandlers + .Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension)) + .OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension)) + .FirstOrDefault(); + + if (loadFileFormatHandler == null) + { + bitmap = null; + return false; + } + + return loadFileFormatHandler.TryLoadFromStream(stream, extension, out bitmap); + } + } +} 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 7565694e3..a4055f2c9 100644 --- a/src/Greenshot.Base/Core/ImageHelper.cs +++ b/src/Greenshot.Base/Core/ImageHelper.cs @@ -24,28 +24,24 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using System.IO; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using Greenshot.Base.Core.Enums; using Greenshot.Base.Effects; using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces; using Greenshot.Base.UnmanagedHelpers; using log4net; +using Brush = System.Drawing.Brush; +using Color = System.Drawing.Color; +using Matrix = System.Drawing.Drawing2D.Matrix; +using Pen = System.Drawing.Pen; +using PixelFormat = System.Drawing.Imaging.PixelFormat; +using Point = System.Drawing.Point; +using Size = System.Drawing.Size; namespace Greenshot.Base.Core { - internal enum ExifOrientations : byte - { - Unknown = 0, - TopLeft = 1, - TopRight = 2, - BottomRight = 3, - BottomLeft = 4, - LeftTop = 5, - RightTop = 6, - RightBottom = 7, - LeftBottom = 8, - } - /// /// Description of ImageHelper. /// @@ -55,83 +51,6 @@ namespace Greenshot.Base.Core private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); private const int ExifOrientationId = 0x0112; - 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); - }; - } - - public static IDictionary> StreamConverters { get; } = new Dictionary>(); /// /// Make sure the image is orientated correctly @@ -371,127 +290,6 @@ namespace Greenshot.Base.Core return cropRectangle; } - /// - /// Load an image from file - /// - /// - /// - public static Image LoadImage(string filename) - { - if (string.IsNullOrEmpty(filename)) - { - return null; - } - - if (!File.Exists(filename)) - { - return null; - } - - Image fileImage; - Log.InfoFormat("Loading image from file {0}", filename); - // Fixed lock problem Bug #3431881 - using (Stream imageFileStream = File.OpenRead(filename)) - { - fileImage = FromStream(imageFileStream, Path.GetExtension(filename)); - } - - if (fileImage != null) - { - Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat, - fileImage.HorizontalResolution, fileImage.VerticalResolution); - } - - return fileImage; - } - - /// - /// 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]; - 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) - { - 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; - } - - /// - /// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx - /// - /// The file (EXE or DLL) to get the icon from - /// Index of the icon - /// true if the large icon is wanted - /// Icon - public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge) - { - Shell32.ExtractIconEx(location, index, out var large, out var small, 1); - Icon returnIcon = null; - bool isLarge = false; - bool isSmall = false; - try - { - if (takeLarge && !IntPtr.Zero.Equals(large)) - { - returnIcon = Icon.FromHandle(large); - isLarge = true; - } - else if (!IntPtr.Zero.Equals(small)) - { - returnIcon = Icon.FromHandle(small); - isSmall = true; - } - else if (!IntPtr.Zero.Equals(large)) - { - returnIcon = Icon.FromHandle(large); - isLarge = true; - } - } - finally - { - if (isLarge && !IntPtr.Zero.Equals(small)) - { - User32.DestroyIcon(small); - } - - if (isSmall && !IntPtr.Zero.Equals(large)) - { - User32.DestroyIcon(large); - } - } - - return returnIcon; - } - /// /// Apply the effect to the bitmap /// @@ -563,8 +361,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(); @@ -1396,7 +1193,7 @@ namespace Greenshot.Base.Core } // If no pixelformat is supplied - if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat) + if (targetFormat is PixelFormat.DontCare or PixelFormat.Undefined) { if (SupportsPixelFormat(sourceImage.PixelFormat)) { @@ -1517,10 +1314,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); @@ -1709,100 +1506,108 @@ namespace Greenshot.Base.Core return newImage; } - + /// - /// Load a Greenshot surface from a stream + /// Rotate the image /// - /// Stream - /// - /// ISurface - public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface) + /// Input image + /// Angle in degrees + /// Rotated image + public static Image Rotate(this Image image, float rotationAngle) { - Image fileImage; - // Fixed problem that the bitmap stream is disposed... by Cloning the image - // This also ensures the bitmap is correctly created + var bitmap = CreateEmptyLike(image, Color.Transparent); - // We create a copy of the bitmap, so everything else can be disposed - surfaceFileStream.Position = 0; - using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) - { - Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); - fileImage = Clone(tmpImage); - } + using var graphics = Graphics.FromImage(bitmap); + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) - const int markerSize = 14; - surfaceFileStream.Seek(-markerSize, SeekOrigin.End); - using (StreamReader streamReader = new StreamReader(surfaceFileStream)) - { - var greenshotMarker = streamReader.ReadToEnd(); - if (!greenshotMarker.StartsWith("Greenshot")) - { - throw new ArgumentException("Stream is not a Greenshot file!"); - } + graphics.TranslateTransform((float)bitmap.Width / 2, (float)bitmap.Height / 2); + graphics.RotateTransform(rotationAngle); + graphics.TranslateTransform(-(float)bitmap.Width / 2, -(float)bitmap.Height / 2); - Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); - const int filesizeLocation = 8 + markerSize; - surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End); - using BinaryReader reader = new BinaryReader(surfaceFileStream); - long bytesWritten = reader.ReadInt64(); - surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End); - returnSurface.LoadElementsFromStream(surfaceFileStream); - } + graphics.DrawImage(image, new Point(0, 0)); - if (fileImage != null) - { - returnSurface.Image = fileImage; - Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat, - fileImage.HorizontalResolution, fileImage.VerticalResolution); - } - - return returnSurface; + return bitmap; } /// - /// Create an image from a stream, if an extension is supplied more formats are supported. + /// Map a System.Drawing.Imaging.PixelFormat to a System.Windows.Media.PixelFormat /// - /// Stream - /// - /// Image - public static Image FromStream(Stream stream, string extension = null) + /// System.Drawing.Imaging.PixelFormat + /// System.Windows.Media.PixelFormat + /// + public static System.Windows.Media.PixelFormat Map(this PixelFormat pixelFormat) => + pixelFormat switch + { + PixelFormat.Format32bppArgb => PixelFormats.Bgra32, + PixelFormat.Format24bppRgb => PixelFormats.Bgr24, + PixelFormat.Format32bppRgb => PixelFormats.Bgr32, + _ => throw new NotSupportedException($"Can't map {pixelFormat}.") + }; + + /// + /// Map a System.Windows.Media.PixelFormat to a System.Drawing.Imaging.PixelFormat + /// + /// System.Windows.Media.PixelFormat + /// System.Drawing.Imaging.PixelFormat + /// + public static PixelFormat Map(this System.Windows.Media.PixelFormat pixelFormat) { - if (stream == null) + if (pixelFormat == PixelFormats.Bgra32) { - return null; + return PixelFormat.Format32bppArgb; + } + if (pixelFormat == PixelFormats.Bgr24) + { + return PixelFormat.Format24bppRgb; + } + if (pixelFormat == PixelFormats.Bgr32) + { + return PixelFormat.Format32bppRgb; } - if (!string.IsNullOrEmpty(extension)) + throw new NotSupportedException($"Can't map {pixelFormat}."); + } + + /// + /// Convert a Bitmap to a BitmapSource + /// + /// Bitmap + /// BitmapSource + public static BitmapSource ToBitmapSource(this Bitmap bitmap) + { + var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); + + BitmapSource bitmapSource; + try { - extension = extension.Replace(".", string.Empty); + bitmapSource = BitmapSource.Create( + bitmapData.Width, bitmapData.Height, + bitmap.HorizontalResolution, bitmap.VerticalResolution, + bitmap.PixelFormat.Map(), null, + bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); + } + finally + { + bitmap.UnlockBits(bitmapData); } - // Make sure we can try multiple times - if (!stream.CanSeek) - { - var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - stream = memoryStream; - } + return bitmapSource; + } - Image returnImage = null; - if (StreamConverters.TryGetValue(extension ?? string.Empty, out var converter)) - { - returnImage = converter(stream, extension); - } + /// + /// Convert a BitmapSource to a Bitmap + /// + /// BitmapSource + /// Bitmap + public static Bitmap ToBitmap(this BitmapSource bitmapSource) + { + var pixelFormat = bitmapSource.Format.Map(); - // 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 returnImage; + Bitmap bitmap = new Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, pixelFormat); + BitmapData data = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.WriteOnly, pixelFormat); + bitmapSource.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride); + bitmap.UnlockBits(data); + return bitmap; } } } \ No newline at end of file diff --git a/src/Greenshot.Base/Core/ImageOutput.cs b/src/Greenshot.Base/Core/ImageIO.cs similarity index 60% rename from src/Greenshot.Base/Core/ImageOutput.cs rename to src/Greenshot.Base/Core/ImageIO.cs index 39c4896c1..16f69c0fe 100644 --- a/src/Greenshot.Base/Core/ImageOutput.cs +++ b/src/Greenshot.Base/Core/ImageIO.cs @@ -20,12 +20,11 @@ */ using System; -using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; +using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -33,20 +32,21 @@ using System.Text.RegularExpressions; using System.Windows.Forms; using Greenshot.Base.Controls; using Greenshot.Base.Core.Enums; +using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; +using Greenshot.Base.UnmanagedHelpers; using log4net; -using Encoder = System.Drawing.Imaging.Encoder; namespace Greenshot.Base.Core { /// /// Description of ImageOutput. /// - public static class ImageOutput + public static class ImageIO { - private static readonly ILog Log = LogManager.GetLogger(typeof(ImageOutput)); + private static readonly ILog Log = LogManager.GetLogger(typeof(ImageIO)); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131; private static readonly Cache TmpFileCache = new Cache(10 * 60 * 60, RemoveExpiredTmpFile); @@ -54,7 +54,7 @@ namespace Greenshot.Base.Core /// /// Creates a PropertyItem (Metadata) to store with the image. /// For the possible ID's see: https://msdn.microsoft.com/de-de/library/system.drawing.imaging.propertyitem.id(v=vs.80).aspx - /// This code uses Reflection to create a PropertyItem, although it's not adviced it's not as stupid as having a image in the project so we can read a PropertyItem from that! + /// This code uses Reflection to create a PropertyItem, although it's not advised it's not as stupid as having a image in the project so we can read a PropertyItem from that! /// /// ID /// Text @@ -124,102 +124,21 @@ namespace Greenshot.Base.Core try { - var imageFormat = outputSettings.Format switch - { - OutputFormat.bmp => ImageFormat.Bmp, - OutputFormat.gif => ImageFormat.Gif, - OutputFormat.jpg => ImageFormat.Jpeg, - OutputFormat.tiff => ImageFormat.Tiff, - OutputFormat.ico => ImageFormat.Icon, - _ => ImageFormat.Png - }; - Log.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat); - - // Check if we want to use a memory stream, to prevent issues with non seakable streams + // Check if we want to use a memory stream, to prevent issues with non seekable streams // The save is made to the targetStream, this is directed to either the MemoryStream or the original Stream targetStream = stream; if (!stream.CanSeek) { useMemoryStream = true; - Log.Warn("Using memorystream prevent an issue with saving to a non seekable stream."); + Log.Warn("Using a memory stream prevent an issue with saving to a non seekable stream."); memoryStream = new MemoryStream(); targetStream = memoryStream; } - if (Equals(imageFormat, ImageFormat.Jpeg)) + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + if (!fileFormatHandlers.TrySaveToStream(imageToSave as Bitmap, targetStream, outputSettings.Format.ToString(), surface, outputSettings)) { - bool foundEncoder = false; - foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders()) - { - if (imageCodec.FormatID == imageFormat.Guid) - { - EncoderParameters parameters = new EncoderParameters(1) - { - Param = - { - [0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality) - } - }; - // Removing transparency if it's not supported in the output - if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) - { - Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); - AddTag(nonAlphaImage); - nonAlphaImage.Save(targetStream, imageCodec, parameters); - nonAlphaImage.Dispose(); - } - else - { - AddTag(imageToSave); - imageToSave.Save(targetStream, imageCodec, parameters); - } - - foundEncoder = true; - break; - } - } - - if (!foundEncoder) - { - throw new ApplicationException("No JPG encoder found, this should not happen."); - } - } - else if (Equals(imageFormat, ImageFormat.Icon)) - { - // FEATURE-916: Added Icon support - IList images = new List - { - imageToSave - }; - WriteIcon(stream, images); - } - else - { - bool needsDispose = false; - // Removing transparency if it's not supported in the output - if (!Equals(imageFormat, ImageFormat.Png) && Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) - { - imageToSave = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); - needsDispose = true; - } - - AddTag(imageToSave); - // Added for OptiPNG - bool processed = false; - if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand)) - { - processed = ProcessPngImageExternally(imageToSave, targetStream); - } - - if (!processed) - { - imageToSave.Save(targetStream, imageFormat); - } - - if (needsDispose) - { - imageToSave.Dispose(); - } + return; } // If we used a memory stream, we need to stream the memory stream to the original stream. @@ -227,21 +146,6 @@ namespace Greenshot.Base.Core { memoryStream.WriteTo(stream); } - - // Output the surface elements, size and marker to the stream - if (outputSettings.Format != OutputFormat.greenshot) - { - return; - } - - using MemoryStream tmpStream = new MemoryStream(); - long bytesWritten = surface.SaveElementsToStream(tmpStream); - using BinaryWriter writer = new BinaryWriter(tmpStream); - writer.Write(bytesWritten); - Version v = Assembly.GetExecutingAssembly().GetName().Version; - byte[] marker = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}"); - writer.Write(marker); - tmpStream.WriteTo(stream); } finally { @@ -249,89 +153,6 @@ namespace Greenshot.Base.Core } } - /// - /// Write the passed Image to a tmp-file and call an external process, than read the file back and write it to the targetStream - /// - /// Image to pass to the external process - /// stream to write the processed image to - /// - private static bool ProcessPngImageExternally(Image imageToProcess, Stream targetStream) - { - if (string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand)) - { - return false; - } - - if (!File.Exists(CoreConfig.OptimizePNGCommand)) - { - Log.WarnFormat("Can't find 'OptimizePNGCommand' {0}", CoreConfig.OptimizePNGCommand); - return false; - } - - string tmpFileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".png"); - try - { - using (FileStream tmpStream = File.Create(tmpFileName)) - { - Log.DebugFormat("Writing png to tmp file: {0}", tmpFileName); - imageToProcess.Save(tmpStream, ImageFormat.Png); - if (Log.IsDebugEnabled) - { - Log.DebugFormat("File size before processing {0}", new FileInfo(tmpFileName).Length); - } - } - - if (Log.IsDebugEnabled) - { - Log.DebugFormat("Starting : {0}", CoreConfig.OptimizePNGCommand); - } - - ProcessStartInfo processStartInfo = new ProcessStartInfo(CoreConfig.OptimizePNGCommand) - { - Arguments = string.Format(CoreConfig.OptimizePNGCommandArguments, tmpFileName), - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false - }; - using Process process = Process.Start(processStartInfo); - if (process != null) - { - process.WaitForExit(); - if (process.ExitCode == 0) - { - if (Log.IsDebugEnabled) - { - Log.DebugFormat("File size after processing {0}", new FileInfo(tmpFileName).Length); - Log.DebugFormat("Reading back tmp file: {0}", tmpFileName); - } - - byte[] processedImage = File.ReadAllBytes(tmpFileName); - targetStream.Write(processedImage, 0, processedImage.Length); - return true; - } - - Log.ErrorFormat("Error while processing PNG image: {0}", process.ExitCode); - Log.ErrorFormat("Output: {0}", process.StandardOutput.ReadToEnd()); - Log.ErrorFormat("Error: {0}", process.StandardError.ReadToEnd()); - } - } - catch (Exception e) - { - Log.Error("Error while processing PNG image: ", e); - } - finally - { - if (File.Exists(tmpFileName)) - { - Log.DebugFormat("Cleaning up tmp file: {0}", tmpFileName); - File.Delete(tmpFileName); - } - } - - return false; - } - /// /// Create an image from a surface with the settings from the output settings applied /// @@ -429,20 +250,18 @@ namespace Greenshot.Base.Core /// Add the greenshot property! /// /// - private static void AddTag(Image imageToSave) + public static void AddTag(this Image imageToSave) { // Create meta-data PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot"); - if (softwareUsedPropertyItem != null) + if (softwareUsedPropertyItem == null) return; + try { - try - { - imageToSave.SetPropertyItem(softwareUsedPropertyItem); - } - catch (Exception) - { - Log.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id); - } + imageToSave.SetPropertyItem(softwareUsedPropertyItem); + } + catch (Exception) + { + Log.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id); } } @@ -463,7 +282,7 @@ namespace Greenshot.Base.Core // Fixed lock problem Bug #3431881 using (Stream surfaceFileStream = File.OpenRead(fullPath)) { - returnSurface = ImageHelper.LoadGreenshotSurface(surfaceFileStream, returnSurface); + returnSurface = LoadGreenshotSurface(surfaceFileStream, returnSurface); } if (returnSurface != null) @@ -547,27 +366,25 @@ namespace Greenshot.Base.Core using (SaveImageFileDialog saveImageFileDialog = new SaveImageFileDialog(captureDetails)) { DialogResult dialogResult = saveImageFileDialog.ShowDialog(); - if (dialogResult.Equals(DialogResult.OK)) + if (!dialogResult.Equals(DialogResult.OK)) return returnValue; + try { - try + string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension; + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension)); + if (CoreConfig.OutputFilePromptQuality) { - string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension; - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension)); - if (CoreConfig.OutputFilePromptQuality) - { - QualityDialog qualityDialog = new QualityDialog(outputSettings); - qualityDialog.ShowDialog(); - } + QualityDialog qualityDialog = new QualityDialog(outputSettings); + qualityDialog.ShowDialog(); + } - // TODO: For now we always overwrite, should be changed - Save(surface, fileNameWithExtension, true, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); - returnValue = fileNameWithExtension; - IniConfig.Save(); - } - catch (ExternalException) - { - MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error")); - } + // TODO: For now we always overwrite, should be changed + Save(surface, fileNameWithExtension, true, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); + returnValue = fileNameWithExtension; + IniConfig.Save(); + } + catch (ExternalException) + { + MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error")); } } @@ -709,91 +526,218 @@ namespace Greenshot.Base.Core } /// - /// Write the images to the stream as icon - /// Every image is resized to 256x256 (but the content maintains the aspect ratio) + /// Load an image from file /// - /// Stream to write to - /// List of images - public static void WriteIcon(Stream stream, IList images) + /// + /// + public static Image LoadImage(string filename) { - var binaryWriter = new BinaryWriter(stream); - // - // ICONDIR structure - // - binaryWriter.Write((short) 0); // reserved - binaryWriter.Write((short) 1); // image type (icon) - binaryWriter.Write((short) images.Count); // number of images - - IList imageSizes = new List(); - IList encodedImages = new List(); - foreach (var image in images) + if (string.IsNullOrEmpty(filename)) { - // Pick the best fit - var sizes = new[] + return null; + } + + if (!File.Exists(filename)) + { + return null; + } + + Image fileImage; + Log.InfoFormat("Loading image from file {0}", filename); + // Fixed lock problem Bug #3431881 + using (Stream imageFileStream = File.OpenRead(filename)) + { + fileImage = FromStream(imageFileStream, Path.GetExtension(filename)); + } + + if (fileImage != null) + { + Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat, + fileImage.HorizontalResolution, fileImage.VerticalResolution); + } + + return fileImage; + } + + /// + /// 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]; + iconStream.Read(srcBuf, 0, (int)iconStream.Length); + int iCount = BitConverter.ToInt16(srcBuf, 4); + for (int iIndex = 0; iIndex < iCount; iIndex++) { - 16, 32, 48 - }; - int size = 256; - foreach (var possibleSize in sizes) - { - if (image.Width <= possibleSize && image.Height <= possibleSize) + int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex]; + int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1]; + if (iWidth == 0 && iHeight == 0) { - size = possibleSize; + 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; + } - var imageStream = new MemoryStream(); - if (image.Width == size && image.Height == size) + return bmpPngExtracted; + } + + /// + /// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx + /// + /// The file (EXE or DLL) to get the icon from + /// Index of the icon + /// true if the large icon is wanted + /// Icon + public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge) + { + Shell32.ExtractIconEx(location, index, out var large, out var small, 1); + Icon returnIcon = null; + bool isLarge = false; + bool isSmall = false; + try + { + if (takeLarge && !IntPtr.Zero.Equals(large)) { - using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb); - clonedImage.Save(imageStream, ImageFormat.Png); - imageSizes.Add(new Size(size, size)); + returnIcon = Icon.FromHandle(large); + isLarge = true; } - else + else if (!IntPtr.Zero.Equals(small)) { - // Resize to the specified size, first make sure the image is 32bpp - using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb); - using var resizedImage = ImageHelper.ResizeImage(clonedImage, true, true, Color.Empty, size, size, null); - resizedImage.Save(imageStream, ImageFormat.Png); - imageSizes.Add(resizedImage.Size); + returnIcon = Icon.FromHandle(small); + isSmall = true; + } + else if (!IntPtr.Zero.Equals(large)) + { + returnIcon = Icon.FromHandle(large); + isLarge = true; + } + } + finally + { + if (isLarge && !IntPtr.Zero.Equals(small)) + { + User32.DestroyIcon(small); } - imageStream.Seek(0, SeekOrigin.Begin); - encodedImages.Add(imageStream); + if (isSmall && !IntPtr.Zero.Equals(large)) + { + User32.DestroyIcon(large); + } } - // - // ICONDIRENTRY structure - // - const int iconDirSize = 6; - const int iconDirEntrySize = 16; + return returnIcon; + } - var offset = iconDirSize + (images.Count * iconDirEntrySize); - for (int i = 0; i < images.Count; i++) + /// + /// Create an image from a stream, if an extension is supplied more formats are supported. + /// + /// Stream + /// + /// Image + public static Image FromStream(Stream stream, string extension = null) + { + if (stream == null) { - var imageSize = imageSizes[i]; - // Write the width / height, 0 means 256 - binaryWriter.Write(imageSize.Width == 256 ? (byte) 0 : (byte) imageSize.Width); - binaryWriter.Write(imageSize.Height == 256 ? (byte) 0 : (byte) imageSize.Height); - binaryWriter.Write((byte) 0); // no pallete - binaryWriter.Write((byte) 0); // reserved - binaryWriter.Write((short) 0); // no color planes - binaryWriter.Write((short) 32); // 32 bpp - binaryWriter.Write((int) encodedImages[i].Length); // image data length - binaryWriter.Write(offset); - offset += (int) encodedImages[i].Length; + return null; } - binaryWriter.Flush(); - // - // Write image data - // - foreach (var encodedImage in encodedImages) + if (!string.IsNullOrEmpty(extension)) { - encodedImage.WriteTo(stream); - encodedImage.Dispose(); + 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; + } + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + foreach (var fileFormatHandler in fileFormatHandlers + .Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension)) + .OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension))) + { + stream.Seek(startingPosition, SeekOrigin.Begin); + if (fileFormatHandler.TryLoadFromStream(stream, extension, out var bitmap)) + { + return bitmap; + } + } + + return null; + } + + /// + /// Load a Greenshot surface from a stream + /// + /// Stream + /// + /// ISurface + public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface) + { + Image fileImage; + // Fixed problem that the bitmap stream is disposed... by Cloning the image + // This also ensures the bitmap is correctly created + + // We create a copy of the bitmap, so everything else can be disposed + surfaceFileStream.Position = 0; + using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) + { + Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + fileImage = ImageHelper.Clone(tmpImage); + } + + // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) + const int markerSize = 14; + surfaceFileStream.Seek(-markerSize, SeekOrigin.End); + using (StreamReader streamReader = new StreamReader(surfaceFileStream)) + { + var greenshotMarker = streamReader.ReadToEnd(); + if (!greenshotMarker.StartsWith("Greenshot")) + { + throw new ArgumentException("Stream is not a Greenshot file!"); + } + + Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); + const int filesizeLocation = 8 + markerSize; + surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End); + using BinaryReader reader = new BinaryReader(surfaceFileStream); + long bytesWritten = reader.ReadInt64(); + surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End); + returnSurface.LoadElementsFromStream(surfaceFileStream); + } + + if (fileImage != null) + { + returnSurface.Image = fileImage; + Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat, + fileImage.HorizontalResolution, fileImage.VerticalResolution); + } + + return returnSurface; } } } \ 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/NetworkHelper.cs b/src/Greenshot.Base/Core/NetworkHelper.cs index 5170db504..88cfff569 100644 --- a/src/Greenshot.Base/Core/NetworkHelper.cs +++ b/src/Greenshot.Base/Core/NetworkHelper.cs @@ -24,11 +24,14 @@ using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; +using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; +using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Plugin; using log4net; @@ -87,24 +90,14 @@ namespace Greenshot.Base.Core } /// - /// Download the uri to Bitmap + /// Download the uri to build an IDrawableContainer /// /// Of an image - /// Bitmap - public static Image DownloadImage(string url) + /// IDrawableContainer + public static IDrawableContainer DownloadImageAsDrawableContainer(string url) { - var extensions = new StringBuilder(); - foreach (var extension in ImageHelper.StreamConverters.Keys) - { - if (string.IsNullOrEmpty(extension)) - { - continue; - } - - extensions.AppendFormat(@"\.{0}|", extension); - } - - extensions.Length--; + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + var extensions = string.Join("|", fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream)); var imageUrlRegex = new Regex($@"(http|https)://.*(?{extensions})"); var match = imageUrlRegex.Match(url); @@ -113,7 +106,12 @@ namespace Greenshot.Base.Core using var memoryStream = GetAsMemoryStream(url); try { - return ImageHelper.FromStream(memoryStream, match.Success ? match.Groups["extension"]?.Value : null); + var extension = match.Success ? match.Groups["extension"]?.Value : null; + var drawableContainer = fileFormatHandlers.LoadDrawablesFromStream(memoryStream, extension).FirstOrDefault(); + if (drawableContainer != null) + { + return drawableContainer; + } } catch (Exception) { @@ -136,7 +134,71 @@ namespace Greenshot.Base.Core } using var memoryStream2 = GetAsMemoryStream(match.Value); - return ImageHelper.FromStream(memoryStream2, match.Groups["extension"]?.Value); + + var extension = match.Success ? match.Groups["extension"]?.Value : null; + var drawableContainer = fileFormatHandlers.LoadDrawablesFromStream(memoryStream2, extension).FirstOrDefault(); + if (drawableContainer != null) + { + return drawableContainer; + } + } + } + catch (Exception e) + { + Log.Error("Problem downloading the image from: " + url, e); + } + + return null; + } + + /// + /// Download the uri to create a Bitmap + /// + /// Of an image + /// Bitmap + public static Bitmap DownloadImage(string url) + { + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + + var extensions = string.Join("|", fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream)); + + var imageUrlRegex = new Regex($@"(http|https)://.*(?{extensions})"); + var match = imageUrlRegex.Match(url); + try + { + using var memoryStream = GetAsMemoryStream(url); + try + { + if (fileFormatHandlers.TryLoadFromStream(memoryStream, match.Success ? match.Groups["extension"]?.Value : null, out var bitmap)) + { + return bitmap; + } + } + catch (Exception) + { + // If we arrive here, the image loading didn't work, try to see if the response has a http(s) URL to an image and just take this instead. + string content; + using (var streamReader = new StreamReader(memoryStream, Encoding.UTF8, true)) + { + content = streamReader.ReadLine(); + } + + if (string.IsNullOrEmpty(content)) + { + throw; + } + + match = imageUrlRegex.Match(content); + if (!match.Success) + { + throw; + } + + using var memoryStream2 = GetAsMemoryStream(match.Value); + if (fileFormatHandlers.TryLoadFromStream(memoryStream2, match.Success ? match.Groups["extension"]?.Value : null, out var bitmap)) + { + return bitmap; + } } } catch (Exception e) @@ -670,7 +732,7 @@ namespace Greenshot.Base.Core public string ToBase64String(Base64FormattingOptions formattingOptions) { using MemoryStream stream = new MemoryStream(); - ImageOutput.SaveToStream(_surface, stream, _outputSettings); + ImageIO.SaveToStream(_surface, stream, _outputSettings); return Convert.ToBase64String(stream.GetBuffer(), 0, (int) stream.Length, formattingOptions); } @@ -682,7 +744,7 @@ namespace Greenshot.Base.Core public byte[] ToByteArray() { using MemoryStream stream = new MemoryStream(); - ImageOutput.SaveToStream(_surface, stream, _outputSettings); + ImageIO.SaveToStream(_surface, stream, _outputSettings); return stream.ToArray(); } @@ -698,7 +760,7 @@ namespace Greenshot.Base.Core string header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{Filename ?? name}\";\r\nContent-Type: {ContentType}\r\n\r\n"; formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, Encoding.UTF8.GetByteCount(header)); - ImageOutput.SaveToStream(_surface, formDataStream, _outputSettings); + ImageIO.SaveToStream(_surface, formDataStream, _outputSettings); } /// @@ -708,7 +770,7 @@ namespace Greenshot.Base.Core public void WriteToStream(Stream dataStream) { // Write the file data directly to the Stream, rather than serializing it to a string. - ImageOutput.SaveToStream(_surface, dataStream, _outputSettings); + ImageIO.SaveToStream(_surface, dataStream, _outputSettings); } /// diff --git a/src/Greenshot.Base/Core/PluginUtils.cs b/src/Greenshot.Base/Core/PluginUtils.cs index 3d3ec79f2..cb09cd8af 100644 --- a/src/Greenshot.Base/Core/PluginUtils.cs +++ b/src/Greenshot.Base/Core/PluginUtils.cs @@ -158,7 +158,7 @@ namespace Greenshot.Base.Core try { - using (Icon appIcon = ImageHelper.ExtractAssociatedIcon(path, index, CoreConfig.UseLargeIcons)) + using (Icon appIcon = ImageIO.ExtractAssociatedIcon(path, index, CoreConfig.UseLargeIcons)) { if (appIcon != null) { diff --git a/src/Greenshot.Base/Core/SimpleServiceProvider.cs b/src/Greenshot.Base/Core/SimpleServiceProvider.cs index bff814540..084f5b1fd 100644 --- a/src/Greenshot.Base/Core/SimpleServiceProvider.cs +++ b/src/Greenshot.Base/Core/SimpleServiceProvider.cs @@ -10,22 +10,19 @@ namespace Greenshot.Base.Core /// public class SimpleServiceProvider : IServiceLocator { - private readonly Dictionary> _services = new Dictionary>(); + private readonly Dictionary> _services = new(); public static IServiceLocator Current { get; } = new SimpleServiceProvider(); - public IEnumerable GetAllInstances() + public IReadOnlyList GetAllInstances() { var typeOfService = typeof(TService); if (!_services.TryGetValue(typeOfService, out var results)) { - yield break; + return Array.Empty(); } - foreach (TService result in results) - { - yield return result; - } + return results.Cast().ToArray(); } public TService GetInstance() 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/Core/WindowDetails.cs b/src/Greenshot.Base/Core/WindowDetails.cs index 0063e3edb..905ab1099 100644 --- a/src/Greenshot.Base/Core/WindowDetails.cs +++ b/src/Greenshot.Base/Core/WindowDetails.cs @@ -782,7 +782,9 @@ namespace Greenshot.Base.Core /// public WindowStyleFlags WindowStyle { - get => (WindowStyleFlags) User32.GetWindowLongWrapper(Handle, (int) WindowLongIndex.GWL_STYLE); + get => unchecked( + (WindowStyleFlags)User32.GetWindowLongWrapper(Handle, (int)WindowLongIndex.GWL_STYLE).ToInt64() + ); set => User32.SetWindowLongWrapper(Handle, (int) WindowLongIndex.GWL_STYLE, new IntPtr((long) value)); } diff --git a/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs b/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs index 1742b8054..24abc2d25 100644 --- a/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs +++ b/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs @@ -30,7 +30,14 @@ namespace Greenshot.Base.Interfaces.Drawing { public interface IDrawableContainer : INotifyPropertyChanged, IDisposable { + /// + /// The parent surface where this IDrawableContainer is on + /// ISurface Parent { get; set; } + + /// + /// Is this IDrawableContainer selected on the surface + /// bool Selected { get; set; } int Left { get; set; } @@ -54,15 +61,25 @@ namespace Greenshot.Base.Interfaces.Drawing bool HasFilters { get; } EditStatus Status { get; set; } + void Invalidate(); + bool ClickableAt(int x, int y); + void MoveBy(int x, int y); + void Transform(Matrix matrix); + bool HandleMouseDown(int x, int y); + void HandleMouseUp(int x, int y); + bool HandleMouseMove(int x, int y); + bool InitContent(); + void MakeBoundsChangeUndoable(bool allowMerge); + EditStatus DefaultEditMode { get; } /// 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..bd5558857 --- /dev/null +++ b/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs @@ -0,0 +1,88 @@ +/* + * 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; +using Greenshot.Base.Interfaces.Plugin; + +namespace Greenshot.Base.Interfaces +{ + /// + /// The possible actions a IFileFormatHandler might support + /// + public enum FileFormatHandlerActions + { + SaveToStream, + LoadFromStream, + LoadDrawableFromStream + } + + /// + /// This interface is for code to implement the loading and saving of certain file formats + /// + public interface IFileFormatHandler + { + /// + /// Registry for all the extensions this IFileFormatHandler support + /// + IDictionary> SupportedExtensions { get; } + + /// + /// 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 + /// ISurface with the elements for those file types which can store a surface (.greenshot) + /// SurfaceOutputSettings + /// bool true if it was successful + public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null); + + /// + /// + /// + /// + /// + /// + /// 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 + /// ISurface + /// IEnumerable{IDrawableContainer} + public IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parentSurface = null); + } +} diff --git a/src/Greenshot.Base/Interfaces/IServiceLocator.cs b/src/Greenshot.Base/Interfaces/IServiceLocator.cs index c22e3463b..5cf1fb529 100644 --- a/src/Greenshot.Base/Interfaces/IServiceLocator.cs +++ b/src/Greenshot.Base/Interfaces/IServiceLocator.cs @@ -32,7 +32,7 @@ namespace Greenshot.Base.Interfaces /// /// Service to find /// IEnumerable{TService} - IEnumerable GetAllInstances(); + IReadOnlyList GetAllInstances(); /// /// Get the only instance of the specified service 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.Base/Interfaces/Plugin/SurfaceOutputSettings.cs b/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs index ef2a58e2f..efce47bbb 100644 --- a/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs +++ b/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs @@ -1,4 +1,25 @@ -using System.Collections.Generic; +/* + * 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 Greenshot.Base.Core; using Greenshot.Base.Core.Enums; using Greenshot.Base.Effects; diff --git a/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowStyleFlags.cs b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowStyleFlags.cs index 603e58196..3efca68e0 100644 --- a/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowStyleFlags.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/Enums/WindowStyleFlags.cs @@ -32,7 +32,7 @@ namespace Greenshot.Base.UnmanagedHelpers.Enums public enum WindowStyleFlags : int { //WS_OVERLAPPED = 0x00000000, - WS_POPUP = -2147483648, + WS_POPUP = -2147483648, // 0x80000000 WS_CHILD = 0x40000000, WS_MINIMIZE = 0x20000000, WS_VISIBLE = 0x10000000, diff --git a/src/Greenshot.Base/UnmanagedHelpers/User32.cs b/src/Greenshot.Base/UnmanagedHelpers/User32.cs index 9764c3a89..58e89a115 100644 --- a/src/Greenshot.Base/UnmanagedHelpers/User32.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/User32.cs @@ -127,7 +127,7 @@ namespace Greenshot.Base.UnmanagedHelpers public static extern IntPtr SendMessage(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam); [DllImport("user32", SetLastError = true, EntryPoint = "GetWindowLong")] - public static extern int GetWindowLong(IntPtr hWnd, int index); + public static extern IntPtr GetWindowLong(IntPtr hWnd, int index); [DllImport("user32", SetLastError = true, EntryPoint = "GetWindowLongPtr")] public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex); @@ -276,16 +276,14 @@ namespace Greenshot.Base.UnmanagedHelpers /// /// /// - public static long GetWindowLongWrapper(IntPtr hWnd, int nIndex) + public static IntPtr GetWindowLongWrapper(IntPtr hWnd, int nIndex) { if (IntPtr.Size == 8) { - return GetWindowLongPtr(hWnd, nIndex).ToInt64(); - } - else - { - return GetWindowLong(hWnd, nIndex); + return GetWindowLongPtr(hWnd, nIndex); } + + return GetWindowLong(hWnd, nIndex); } /// 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 029a48d43..08abba3e2 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; @@ -505,7 +510,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; @@ -529,7 +534,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..dd6668ce9 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,11 +56,16 @@ namespace Greenshot.Editor.Drawing CreateDefaultAdorners(); } - public IconContainer(Surface parent, string filename) : base(parent) + public IconContainer(ISurface parent, string filename) : base(parent) { Load(filename); } + public IconContainer(ISurface parent, Stream stream) : base(parent) + { + Load(stream); + } + public Icon Icon { set @@ -99,6 +105,18 @@ namespace Greenshot.Editor.Drawing Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); } + public void Load(Stream iconStream) + { + if (iconStream == null) + { + return; + } + + using Icon fileIcon = new Icon(iconStream); + Icon = fileIcon; + Log.Debug("Loaded stream: with resolution: " + Height + "," + Width); + } + public override void Draw(Graphics graphics, RenderMode rm) { if (icon == null) diff --git a/src/Greenshot.Editor/Drawing/ImageContainer.cs b/src/Greenshot.Editor/Drawing/ImageContainer.cs index a46b0dae5..5def5578b 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; @@ -40,7 +41,7 @@ namespace Greenshot.Editor.Drawing { private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer)); - private Image image; + private Image _image; /// /// This is the shadow version of the bitmap, rendered once to save performance @@ -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(); @@ -107,8 +108,8 @@ namespace Greenshot.Editor.Drawing } else { - Width = image.Width; - Height = image.Height; + Width = _image.Width; + Height = _image.Height; if (_shadowBitmap != null) { Left += _shadowOffset.X; @@ -124,13 +125,13 @@ namespace Greenshot.Editor.Drawing // Remove all current bitmaps DisposeImage(); DisposeShadow(); - image = ImageHelper.Clone(value); + _image = ImageHelper.Clone(value); bool shadow = GetFieldValueAsBool(FieldType.SHADOW); CheckShadow(shadow); if (!shadow) { - Width = image.Width; - Height = image.Height; + Width = _image.Width; + Height = _image.Height; } else { @@ -140,7 +141,7 @@ namespace Greenshot.Editor.Drawing Top -= _shadowOffset.Y; } } - get { return image; } + get { return _image; } } /// @@ -162,8 +163,8 @@ namespace Greenshot.Editor.Drawing private void DisposeImage() { - image?.Dispose(); - image = null; + _image?.Dispose(); + _image = null; } private void DisposeShadow() @@ -186,9 +187,9 @@ namespace Greenshot.Editor.Drawing Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle); DisposeShadow(); using var tmpMatrix = new Matrix(); - using (image) + using (_image) { - image = ImageHelper.ApplyEffect(image, new RotateEffect(rotateAngle), tmpMatrix); + _image = ImageHelper.ApplyEffect(_image, new RotateEffect(rotateAngle), tmpMatrix); } } @@ -208,7 +209,8 @@ namespace Greenshot.Editor.Drawing // Always make sure ImageHelper.LoadBitmap results are disposed some time, // as we close the bitmap internally, we need to do it afterwards - using (var tmpImage = ImageHelper.LoadImage(filename)) + // TODO: Replace with some other code, like the file format handler, or move it completely outside of this class + using (var tmpImage = ImageIO.LoadImage(filename)) { Image = tmpImage; } @@ -228,7 +230,7 @@ namespace Greenshot.Editor.Drawing } using var matrix = new Matrix(); - _shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), matrix); + _shadowBitmap = ImageHelper.ApplyEffect(_image, new DropShadowEffect(), matrix); } /// @@ -238,7 +240,7 @@ namespace Greenshot.Editor.Drawing /// public override void Draw(Graphics graphics, RenderMode rm) { - if (image == null) + if (_image == null) { return; } @@ -256,12 +258,12 @@ namespace Greenshot.Editor.Drawing } else { - graphics.DrawImage(image, Bounds); + graphics.DrawImage(_image, Bounds); } } public override bool HasDefaultSize => true; - public override Size DefaultSize => image?.Size ?? new Size(32, 32); + public override Size DefaultSize => _image?.Size ?? new Size(32, 32); } } \ No newline at end of file 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/MetafileContainer.cs b/src/Greenshot.Editor/Drawing/MetafileContainer.cs new file mode 100644 index 000000000..4372ce24d --- /dev/null +++ b/src/Greenshot.Editor/Drawing/MetafileContainer.cs @@ -0,0 +1,81 @@ +/* + * 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 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 MetafileContainer : VectorGraphicsContainer + { + private readonly Metafile _metafile; + + public MetafileContainer(Metafile metafile, ISurface parent) : base(parent) + { + _metafile = metafile; + Size = new Size(metafile.Width/4, metafile.Height/4); + } + + protected override Image ComputeBitmap() + { + var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent); + + var dstRect = new Rectangle(0, 0, Width, Height); + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.DrawImage(_metafile, dstRect); + } + + if (RotationAngle == 0) return image; + + var newImage = image.Rotate(RotationAngle); + image.Dispose(); + return newImage; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _metafile?.Dispose(); + } + + base.Dispose(disposing); + } + + public override bool HasDefaultSize => true; + + public override Size DefaultSize => new Size(_metafile.Width, _metafile.Height); + } +} \ No newline at end of file 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 4f95e0437..c6812f12b 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..7c27c8ce1 100644 --- a/src/Greenshot.Editor/Drawing/Surface.cs +++ b/src/Greenshot.Editor/Drawing/Surface.cs @@ -49,7 +49,6 @@ namespace Greenshot.Editor.Drawing public sealed class Surface : Control, ISurface, INotifyPropertyChanged { private static readonly ILog LOG = LogManager.GetLogger(typeof(Surface)); - public static int Count; private static readonly CoreConfiguration conf = IniConfig.GetIniSection(); // Property to identify the Surface ID @@ -207,7 +206,7 @@ namespace Greenshot.Editor.Drawing [NonSerialized] private IDrawableContainer _undrawnElement; /// - /// the cropcontainer, when cropping this is set, do not serialize + /// the crop container, when cropping this is set, do not serialize /// [NonSerialized] private IDrawableContainer _cropContainer; @@ -294,7 +293,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 +354,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; @@ -467,7 +466,6 @@ namespace Greenshot.Editor.Drawing public Surface() { _fieldAggregator = new FieldAggregator(this); - Count++; _elements = new DrawableContainerList(_uniqueId); selectedElements = new DrawableContainerList(_uniqueId); LOG.Debug("Creating surface!"); @@ -550,7 +548,6 @@ namespace Greenshot.Editor.Drawing { if (disposing) { - Count--; LOG.Debug("Disposing surface!"); if (_buffer != null) { @@ -913,6 +910,27 @@ namespace Greenshot.Editor.Drawing } } + /// + /// This will help to fit the container to the surface + /// + /// IDrawableContainer + private void FitContainer(IDrawableContainer drawableContainer) + { + double factor = 1; + if (drawableContainer.Width > this.Width) + { + factor = drawableContainer.Width / (double)Width; + } + if (drawableContainer.Height > this.Height) + { + var otherFactor = drawableContainer.Height / (double)Height; + factor = Math.Max(factor, otherFactor); + } + + drawableContainer.Width = (int)(drawableContainer.Width / factor); + drawableContainer.Height = (int)(drawableContainer.Height / factor); + } + /// /// Handle the drag/drop /// @@ -927,20 +945,25 @@ namespace Greenshot.Editor.Drawing // Test if it's an url and try to download the image so we have it in the original form if (possibleUrl != null && possibleUrl.StartsWith("http")) { - using Image image = NetworkHelper.DownloadImage(possibleUrl); - if (image != null) + var drawableContainer = NetworkHelper.DownloadImageAsDrawableContainer(possibleUrl); + if (drawableContainer != null) { - AddImageContainer(image, mouse.X, mouse.Y); + drawableContainer.Left = Location.X; + drawableContainer.Top = Location.Y; + FitContainer(drawableContainer); + AddElement(drawableContainer); return; } } } - foreach (Image image in ClipboardHelper.GetImages(e.Data)) + foreach (var drawableContainer in ClipboardHelper.GetDrawables(e.Data)) { - AddImageContainer(image, mouse.X, mouse.Y); + drawableContainer.Left = mouse.X; + drawableContainer.Top = mouse.Y; + FitContainer(drawableContainer); + AddElement(drawableContainer); mouse.Offset(10, 10); - image.Dispose(); } } @@ -987,13 +1010,11 @@ namespace Greenshot.Editor.Drawing { //create a blank bitmap the same size as original Bitmap newBitmap = ImageHelper.CreateEmptyLike(Image, Color.Empty); - if (newBitmap != null) - { - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false); - SetImage(newBitmap, false); - Invalidate(); - } + if (newBitmap == null) return; + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false); + SetImage(newBitmap, false); + Invalidate(); } /// @@ -2057,17 +2078,16 @@ namespace Greenshot.Editor.Drawing { Point pasteLocation = GetPasteLocation(0.1f, 0.1f); - foreach (Image clipboardImage in ClipboardHelper.GetImages(clipboard)) + foreach (var drawableContainer in ClipboardHelper.GetDrawables(clipboard)) { - if (clipboardImage != null) - { - DeselectAllElements(); - IImageContainer container = AddImageContainer(clipboardImage as Bitmap, pasteLocation.X, pasteLocation.Y); - SelectElement(container); - clipboardImage.Dispose(); - pasteLocation.X += 10; - pasteLocation.Y += 10; - } + if (drawableContainer == null) continue; + DeselectAllElements(); + drawableContainer.Left = pasteLocation.X; + drawableContainer.Top = pasteLocation.Y; + AddElement(drawableContainer); + SelectElement(drawableContainer); + pasteLocation.X += 10; + pasteLocation.Y += 10; } } else if (ClipboardHelper.ContainsText(clipboard)) @@ -2208,24 +2228,23 @@ namespace Greenshot.Editor.Drawing /// false to skip event generation public void SelectElement(IDrawableContainer container, bool invalidate = true, bool generateEvents = true) { - if (!selectedElements.Contains(container)) - { - selectedElements.Add(container); - container.Selected = true; - FieldAggregator.BindElement(container); - if (generateEvents && _movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs - { - Elements = selectedElements - }; - _movingElementChanged(this, eventArgs); - } + if (selectedElements.Contains(container)) return; - if (invalidate) + selectedElements.Add(container); + container.Selected = true; + FieldAggregator.BindElement(container); + if (generateEvents && _movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs { - container.Invalidate(); - } + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } + + if (invalidate) + { + container.Invalidate(); } } diff --git a/src/Greenshot.Editor/Drawing/SvgContainer.cs b/src/Greenshot.Editor/Drawing/SvgContainer.cs new file mode 100644 index 000000000..f98f03a32 --- /dev/null +++ b/src/Greenshot.Editor/Drawing/SvgContainer.cs @@ -0,0 +1,61 @@ +/* + * 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 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.Format32bppArgb, Color.Transparent); + + var image = _svgDocument.Draw(Width, Height); + + if (RotationAngle == 0) return image; + + var newImage = image.Rotate(RotationAngle); + image.Dispose(); + return newImage; + } + + public override bool HasDefaultSize => true; + + public override Size DefaultSize => new Size((int)_svgDocument.Width, (int)_svgDocument.Height); + } +} \ 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..0c467c0b8 --- /dev/null +++ b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs @@ -0,0 +1,117 @@ +/* + * 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.Runtime.Serialization; +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) + { + if (_cachedImage != null && _cachedImage.Size != Bounds.Size) + { + ResetCachedBitmap(); + } + + _cachedImage ??= ComputeBitmap(); + + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + + graphics.DrawImage(_cachedImage, Bounds); + } + + protected abstract Image ComputeBitmap(); + + private void ResetCachedBitmap() + { + _cachedImage?.Dispose(); + _cachedImage = null; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/EditorInitialize.cs b/src/Greenshot.Editor/EditorInitialize.cs new file mode 100644 index 000000000..c1ee27db8 --- /dev/null +++ b/src/Greenshot.Editor/EditorInitialize.cs @@ -0,0 +1,50 @@ +/* + * 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.FileFormatHandlers; + +namespace Greenshot.Editor +{ + public static class EditorInitialize + { + public static void Initialize() + { + SimpleServiceProvider.Current.AddService( + // All generic things, like gif, png, jpg etc. + new DefaultFileFormatHandler(), + // Greenshot format + new GreenshotFileFormatHandler(), + // For .svg support + new SvgFileFormatHandler(), + // For clipboard support + new DibFileFormatHandler(), + // .ico + new IconFileFormatHandler(), + // EMF & WMF + new MetaFileFormatHandler(), + // JPG XR + new WpfFileFormatHandler() + ); + } + } +} diff --git a/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs new file mode 100644 index 000000000..fff576267 --- /dev/null +++ b/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs @@ -0,0 +1,66 @@ +/* + * 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; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.Interfaces.Plugin; +using Greenshot.Editor.Drawing; + +namespace Greenshot.Editor.FileFormatHandlers +{ + public abstract class AbstractFileFormatHandler : IFileFormatHandler + { + /// + public IDictionary> SupportedExtensions { get; } = new Dictionary>(); + + /// + public virtual int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension) + { + return 0; + } + + public abstract bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null); + + public abstract bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap); + + /// + /// Default implementation taking the TryLoadFromStream image and placing it in an ImageContainer + /// + /// Stream + /// string + /// ISurface + /// IEnumerable{IDrawableContainer} + public virtual IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null) + { + if (TryLoadFromStream(stream, extension, out var bitmap)) + { + var imageContainer = new ImageContainer(parent) + { + Image = bitmap + }; + yield return imageContainer; + } + } + } +} diff --git a/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs new file mode 100644 index 000000000..30d785bb5 --- /dev/null +++ b/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs @@ -0,0 +1,137 @@ +/* + * 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.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Plugin; +using log4net; + +namespace Greenshot.Editor.FileFormatHandlers +{ + /// + /// This is the default .NET bitmap file format handler + /// + public class DefaultFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(DefaultFileFormatHandler)); + private readonly IReadOnlyCollection _ourExtensions = new[] { ".png", ".bmp", ".gif", ".jpg", ".jpeg", ".tiff", ".tif" }; + public DefaultFileFormatHandler() + { + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; + } + + /// + public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + ImageFormat imageFormat = extension 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; + } + surfaceOutputSettings ??= new SurfaceOutputSettings(); + var imageEncoder = ImageCodecInfo.GetImageEncoders().FirstOrDefault(ie => ie.FilenameExtension.ToLowerInvariant().Contains(extension)); + if (imageEncoder == null) + { + return false; + } + EncoderParameters parameters = new EncoderParameters(1) + { + Param = + { + [0] = new EncoderParameter(Encoder.Quality, surfaceOutputSettings.JPGQuality) + } + }; + // For those images which are with Alpha, but the format doesn't support this, change it to 24bpp + if (imageFormat.Guid == ImageFormat.Jpeg.Guid && Image.IsAlphaPixelFormat(bitmap.PixelFormat)) + { + var nonAlphaImage = ImageHelper.Clone(bitmap, PixelFormat.Format24bppRgb) as Bitmap; + try + { + // Set that this file was written by Greenshot + nonAlphaImage.AddTag(); + } + catch (Exception ex) + { + Log.Warn("Couldn't set 'software used' tag on image.", ex); + } + + try + { + nonAlphaImage.Save(destination, imageEncoder, parameters); + } + catch (Exception ex) + { + Log.Error("Couldn't save image: ", ex); + } + finally + { + nonAlphaImage.Dispose(); + } + } + else + { + // Set that this file was written by Greenshot + bitmap.AddTag(); + bitmap.Save(destination, imageEncoder, parameters); + } + + return true; + } + + /// + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + try + { + using var tmpImage = Image.FromStream(stream, true, true); + bitmap = ImageHelper.Clone(tmpImage, PixelFormat.DontCare); + + return true; + } + catch (Exception ex) + { + Log.Error("Couldn't load image: ", ex); + } + + bitmap = null; + return false; + } + } +} diff --git a/src/Greenshot.Base/Core/DibHelper.cs b/src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs similarity index 59% rename from src/Greenshot.Base/Core/DibHelper.cs rename to src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs index 59af37414..8d7ad7790 100644 --- a/src/Greenshot.Base/Core/DibHelper.cs +++ b/src/Greenshot.Editor/FileFormatHandlers/DibFileFormatHandler.cs @@ -1,48 +1,131 @@ /* * 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.Runtime.InteropServices; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.UnmanagedHelpers; +using log4net; -namespace Greenshot.Base.Core +namespace Greenshot.Editor.FileFormatHandlers { /// - /// Though Greenshot implements the specs for the DIB image format, - /// it seems to cause a lot of issues when using the clipboard. - /// There is some research done about the DIB on the clipboard, this code is based upon the information - /// here + /// This handles creating a DIB (Device Independent Bitmap) on the clipboard /// - internal static class DibHelper + public class DibFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler { private const double DpiToPelsPerMeter = 39.3701; + private static readonly ILog Log = LogManager.GetLogger(typeof(DibFileFormatHandler)); + + private readonly IReadOnlyCollection _ourExtensions = new[] { ".dib", ".format17" }; + + public DibFileFormatHandler() + { + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; + } + + /// + public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + var dibBytes = ConvertToDib(bitmap); + destination.Write(dibBytes, 0, dibBytes.Length); + return true; + } + + /// + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + byte[] dibBuffer = new byte[stream.Length]; + _ = stream.Read(dibBuffer, 0, dibBuffer.Length); + var infoHeader = BinaryStructHelper.FromByteArray(dibBuffer); + if (!infoHeader.IsDibV5) + { + Log.InfoFormat("Using special DIB /// Converts the Bitmap to a Device Independent Bitmap format of type BITFIELDS. /// /// Bitmap to convert to DIB /// byte{} with the image converted to DIB - public static byte[] ConvertToDib(this Bitmap sourceBitmap) + private static byte[] ConvertToDib(Bitmap sourceBitmap) { if (sourceBitmap == null) throw new ArgumentNullException(nameof(sourceBitmap)); diff --git a/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs new file mode 100644 index 000000000..0b956c001 --- /dev/null +++ b/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs @@ -0,0 +1,133 @@ +/* + * 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.Reflection; +using System.Text; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Plugin; +using log4net; + +namespace Greenshot.Editor.FileFormatHandlers +{ + public class GreenshotFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileFormatHandler)); + private readonly IReadOnlyCollection _ourExtensions = new [] { ".greenshot" }; + public GreenshotFileFormatHandler() + { + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; + } + + public override bool TrySaveToStream(Bitmap bitmap, Stream stream, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + if (surface == null) + { + return false; + } + + try + { + bitmap.Save(stream, ImageFormat.Png); + using MemoryStream tmpStream = new MemoryStream(); + long bytesWritten = surface.SaveElementsToStream(tmpStream); + using BinaryWriter writer = new BinaryWriter(tmpStream); + writer.Write(bytesWritten); + Version v = Assembly.GetExecutingAssembly().GetName().Version; + byte[] marker = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}"); + writer.Write(marker); + tmpStream.WriteTo(stream); + return true; + } + catch (Exception ex) + { + Log.Error("Couldn't save surface as .greenshot: ", ex); + } + + return false; + } + + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + try + { + var surface = LoadSurface(stream); + bitmap = (Bitmap)surface.GetImageForExport(); + return true; + } + catch (Exception ex) + { + Log.Error("Couldn't load .greenshot: ", ex); + } + bitmap = null; + return false; + } + + private ISurface LoadSurface(Stream surfaceFileStream) + { + var returnSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + Bitmap captureBitmap; + + // Fixed problem that the bitmap stream is disposed... by Cloning the image + // This also ensures the bitmap is correctly created + using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) + { + Log.DebugFormat("Loaded capture from .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + captureBitmap = ImageHelper.Clone(tmpImage) as Bitmap; + } + + // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) + const int markerSize = 14; + surfaceFileStream.Seek(-markerSize, SeekOrigin.End); + using (var streamReader = new StreamReader(surfaceFileStream)) + { + var greenshotMarker = streamReader.ReadToEnd(); + if (!greenshotMarker.StartsWith("Greenshot")) + { + throw new ArgumentException("Stream is not a Greenshot file!"); + } + + Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); + const int fileSizeLocation = 8 + markerSize; + surfaceFileStream.Seek(-fileSizeLocation, SeekOrigin.End); + using BinaryReader reader = new BinaryReader(surfaceFileStream); + long bytesWritten = reader.ReadInt64(); + surfaceFileStream.Seek(-(bytesWritten + fileSizeLocation), SeekOrigin.End); + returnSurface.LoadElementsFromStream(surfaceFileStream); + } + + if (captureBitmap != null) + { + returnSurface.Image = captureBitmap; + Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", captureBitmap.Width, captureBitmap.Height, captureBitmap.PixelFormat, captureBitmap.HorizontalResolution, captureBitmap.VerticalResolution); + } + + return returnSurface; + } + } +} diff --git a/src/Greenshot.Editor/FileFormatHandlers/IconFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/IconFileFormatHandler.cs new file mode 100644 index 000000000..d2769d5f0 --- /dev/null +++ b/src/Greenshot.Editor/FileFormatHandlers/IconFileFormatHandler.cs @@ -0,0 +1,218 @@ +/* + * 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Plugin; +using log4net; + +namespace Greenshot.Editor.FileFormatHandlers +{ + /// + /// THis is the .ico format handler + /// + public class IconFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(IconFileFormatHandler)); + + private readonly IReadOnlyCollection _ourExtensions = new[] { ".ico" }; + + public IconFileFormatHandler() + { + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; + } + + public override bool TrySaveToStream(Bitmap bitmap, Stream stream, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + IList images = new List + { + bitmap + }; + + var binaryWriter = new BinaryWriter(stream); + // + // ICONDIR structure + // + binaryWriter.Write((short)0); // reserved + binaryWriter.Write((short)1); // image type (icon) + binaryWriter.Write((short)images.Count); // number of images + + IList imageSizes = new List(); + IList encodedImages = new List(); + foreach (var image in images) + { + // Pick the best fit + var sizes = new[] + { + 16, 32, 48 + }; + int size = 256; + foreach (var possibleSize in sizes) + { + if (image.Width <= possibleSize && image.Height <= possibleSize) + { + size = possibleSize; + break; + } + } + + var imageStream = new MemoryStream(); + if (image.Width == size && image.Height == size) + { + using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb); + clonedImage.Save(imageStream, ImageFormat.Png); + imageSizes.Add(new Size(size, size)); + } + else + { + // Resize to the specified size, first make sure the image is 32bpp + using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb); + using var resizedImage = ImageHelper.ResizeImage(clonedImage, true, true, Color.Empty, size, size, null); + resizedImage.Save(imageStream, ImageFormat.Png); + imageSizes.Add(resizedImage.Size); + } + + imageStream.Seek(0, SeekOrigin.Begin); + encodedImages.Add(imageStream); + } + + // + // ICONDIRENTRY structure + // + const int iconDirSize = 6; + const int iconDirEntrySize = 16; + + var offset = iconDirSize + (images.Count * iconDirEntrySize); + for (int i = 0; i < images.Count; i++) + { + var imageSize = imageSizes[i]; + // Write the width / height, 0 means 256 + binaryWriter.Write(imageSize.Width == 256 ? (byte)0 : (byte)imageSize.Width); + binaryWriter.Write(imageSize.Height == 256 ? (byte)0 : (byte)imageSize.Height); + binaryWriter.Write((byte)0); // no pallete + binaryWriter.Write((byte)0); // reserved + binaryWriter.Write((short)0); // no color planes + binaryWriter.Write((short)32); // 32 bpp + binaryWriter.Write((int)encodedImages[i].Length); // image data length + binaryWriter.Write(offset); + offset += (int)encodedImages[i].Length; + } + + binaryWriter.Flush(); + // + // Write image data + // + foreach (var encodedImage in encodedImages) + { + encodedImage.WriteTo(stream); + encodedImage.Dispose(); + } + + // TODO: Implement this + return true; + } + + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + _ = 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; + } + + /// + /// 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.Editor/FileFormatHandlers/MetaFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs new file mode 100644 index 000000000..04c3825b3 --- /dev/null +++ b/src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs @@ -0,0 +1,81 @@ +/* + * 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.Drawing.Imaging; +using System.IO; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.Interfaces.Plugin; +using Greenshot.Editor.Drawing; + +namespace Greenshot.Editor.FileFormatHandlers +{ + /// + /// This handles the Windows metafile files + /// + public class MetaFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler + { + private readonly IReadOnlyCollection _ourExtensions = new[] { ".wmf", ".emf" }; + + public MetaFileFormatHandler() + { + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; + } + + /// + public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + return false; + } + + /// + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + try + { + if (Image.FromStream(stream, true, true) is Metafile metaFile) + { + bitmap = ImageHelper.Clone(metaFile, PixelFormat.Format32bppArgb); + return true; + } + } + catch + { + // Ignore + } + bitmap = null; + return false; + } + + /// + public override IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface surface = null) + { + if (Image.FromStream(stream, true, true) is Metafile metaFile) + { + yield return new MetafileContainer(metaFile, surface); + } + } + } +} diff --git a/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs new file mode 100644 index 000000000..14a33246e --- /dev/null +++ b/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs @@ -0,0 +1,89 @@ +/* + * 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.IO; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.Interfaces.Plugin; +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 : AbstractFileFormatHandler, IFileFormatHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(SvgFileFormatHandler)); + private readonly IReadOnlyCollection _ourExtensions = new[] { ".svg" }; + + public SvgFileFormatHandler() + { + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; + } + + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + var svgDocument = SvgDocument.Open(stream); + + try + { + bitmap = svgDocument.Draw(); + return true; + } + catch (Exception ex) + { + Log.Error("Can't load SVG", ex); + } + bitmap = null; + return false; + } + + public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + // TODO: Implement this + return false; + } + + public override IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null) + { + SvgDocument svgDocument = null; + try + { + svgDocument = SvgDocument.Open(stream); + } + catch (Exception ex) + { + Log.Error("Can't load SVG", ex); + } + if (svgDocument != null) + { + yield return new SvgContainer(svgDocument, parent); + } + } + } +} diff --git a/src/Greenshot.Editor/FileFormatHandlers/WpfFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/WpfFileFormatHandler.cs new file mode 100644 index 000000000..2d655bb1b --- /dev/null +++ b/src/Greenshot.Editor/FileFormatHandlers/WpfFileFormatHandler.cs @@ -0,0 +1,144 @@ +/* + * 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.IO; +using System.Linq; +using System.Windows.Media.Imaging; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces.Plugin; +using log4net; +using Microsoft.Win32; + +namespace Greenshot.Editor.FileFormatHandlers +{ + /// + /// This is the System.Windows.Media.Imaging (WPF) file format handler, which uses WIC + /// + public class WpfFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(WpfFileFormatHandler)); + private const string HeifDecoder = "{E9A4A80A-44FE-4DE4-8971-7150B10A5199}"; + private const string WicDecoderCategory = "{7ED96837-96F0-4812-B211-F13C24117ED3}"; + + private IReadOnlyCollection LoadFromStreamExtensions { get; } = new []{ ".jxr", ".dds", ".hdp", ".wdp", ".wmp"}; + private IReadOnlyCollection SaveToStreamExtensions { get; } = new[] { ".jxr" }; + + public WpfFileFormatHandler() + { + LoadFromStreamExtensions = LoadFromStreamExtensions.ToList().Concat(RetrieveSupportedExtensions()).OrderBy(e => e).Distinct().ToArray(); + + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = LoadFromStreamExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = LoadFromStreamExtensions; + SupportedExtensions[FileFormatHandlerActions.SaveToStream] = SaveToStreamExtensions; + } + + /// + /// Detect all the formats WIC supports + /// + /// IEnumerable{string} + private IEnumerable RetrieveSupportedExtensions() + { + string baseKeyPath; + if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess) + { + baseKeyPath = "Wow6432Node\\CLSID"; + } + else + { + baseKeyPath = "CLSID"; + } + + using RegistryKey baseKey = Registry.ClassesRoot.OpenSubKey(baseKeyPath, false); + if (baseKey == null) yield break; + + var wicDecoderCategoryPath = Path.Combine(baseKeyPath, WicDecoderCategory, "instance"); + using RegistryKey categoryKey = Registry.ClassesRoot.OpenSubKey(wicDecoderCategoryPath, false); + if (categoryKey == null) + { + yield break; + } + foreach (var codecGuid in categoryKey.GetSubKeyNames()) + { + // Read the properties of the single registered decoder + using var codecKey = baseKey.OpenSubKey(codecGuid); + if (codecKey == null) continue; + + var fileExtensions = Convert.ToString(codecKey.GetValue("FileExtensions", "")).ToLowerInvariant(); + foreach (var fileExtension in fileExtensions.Split(',')) + { + yield return fileExtension; + } + } + var heifDecoderPath = Path.Combine(baseKeyPath, HeifDecoder); + + using RegistryKey heifKey = Registry.ClassesRoot.OpenSubKey(heifDecoderPath, false); + if (heifKey == null) yield break; + + yield return ".heic"; + yield return ".heif"; + } + + /// + public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + surfaceOutputSettings ??= new SurfaceOutputSettings(); + try + { + var bitmapSource = bitmap.ToBitmapSource(); + var bitmapFrame = BitmapFrame.Create(bitmapSource); + var jpegXrEncoder = new WmpBitmapEncoder(); + jpegXrEncoder.Frames.Add(bitmapFrame); + // TODO: Support supplying a quality + jpegXrEncoder.ImageQualityLevel = surfaceOutputSettings.JPGQuality / 100f; + jpegXrEncoder.Save(destination); + return true; + } + catch (Exception ex) + { + Log.Error("Couldn't save image as JPEG XR: ", ex); + return false; + } + } + + /// + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + try + { + var bitmapDecoder = BitmapDecoder.Create(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None); + var bitmapSource = bitmapDecoder.Frames[0]; + bitmap = bitmapSource.ToBitmap(); + return true; + } + catch (Exception ex) + { + Log.Error("Couldn't load image: ", ex); + } + + bitmap = null; + return false; + } + } +} diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.cs index 3e1be9489..8ab283d0b 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; /// @@ -181,6 +181,20 @@ namespace Greenshot.Editor.Forms UpdateUi(); + // Use best fit, for those capture modes where we can get huge images + bool useBestFit = _surface.CaptureDetails.CaptureMode switch + { + CaptureMode.File => true, + CaptureMode.Clipboard => true, + CaptureMode.IE => true, + _ => false + }; + + if (useBestFit) + { + ZoomBestFitMenuItemClick(this, EventArgs.Empty); + } + // Workaround: As the cursor is (mostly) selected on the surface a funny artifact is visible, this fixes it. HideToolstripItems(); } @@ -1290,7 +1304,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 +1364,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/Greenshot.Editor.csproj b/src/Greenshot.Editor/Greenshot.Editor.csproj index 768bdb99d..7da9d555d 100644 --- a/src/Greenshot.Editor/Greenshot.Editor.csproj +++ b/src/Greenshot.Editor/Greenshot.Editor.csproj @@ -1,4 +1,7 @@  + + True + PreserveNewest 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; diff --git a/src/Greenshot.Plugin.Box/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Box/Forms/SettingsForm.Designer.cs index de492dde4..940eaccb6 100644 --- a/src/Greenshot.Plugin.Box/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Box/Forms/SettingsForm.Designer.cs @@ -87,7 +87,7 @@ namespace Greenshot.Plugin.Box.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(208, 12); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = "UploadFormat"; + this.combobox_uploadimageformat.PropertyName = nameof(BoxConfiguration.UploadFormat); this.combobox_uploadimageformat.SectionName = "Box"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(215, 21); this.combobox_uploadimageformat.TabIndex = 5; @@ -115,7 +115,7 @@ namespace Greenshot.Plugin.Box.Forms { this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "box.label_AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(208, 45); this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard"; - this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard"; + this.checkboxAfterUploadLinkToClipBoard.PropertyName = nameof(BoxConfiguration.AfterUploadLinkToClipBoard); this.checkboxAfterUploadLinkToClipBoard.SectionName = "Box"; this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17); this.checkboxAfterUploadLinkToClipBoard.TabIndex = 10; diff --git a/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs b/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs index ddcb66530..669087901 100644 --- a/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs +++ b/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs @@ -54,8 +54,8 @@ namespace Greenshot.Plugin.Confluence Uri confluenceIconUri = new Uri("/Greenshot.Plugin.Confluence;component/Images/Confluence.ico", UriKind.Relative); using (Stream iconStream = Application.GetResourceStream(confluenceIconUri)?.Stream) { - // TODO: Check what to do with the IImage - ConfluenceIcon = ImageHelper.FromStream(iconStream); + // TODO: Replace with FileFormatHandler + ConfluenceIcon = ImageIO.FromStream(iconStream); } IsInitialized = true; diff --git a/src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs b/src/Greenshot.Plugin.Dropbox/DropboxConfiguration.cs similarity index 92% rename from src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs rename to src/Greenshot.Plugin.Dropbox/DropboxConfiguration.cs index 996b964f0..b9dd8bf0c 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxConfiguration.cs @@ -1,74 +1,73 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel - * - * 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.Windows.Forms; -using Greenshot.Base.Core; -using Greenshot.Base.Core.Enums; -using Greenshot.Base.IniFile; -using Greenshot.Plugin.Dropbox.Forms; - -namespace Greenshot.Plugin.Dropbox -{ - /// - /// Description of ImgurConfiguration. - /// - [IniSection("Dropbox", Description = "Greenshot Dropbox Plugin configuration")] - public class DropboxPluginConfiguration : IniSection - { - [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] - public OutputFormat UploadFormat { get; set; } - - [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] - public int UploadJpegQuality { get; set; } - - [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Dropbox link to clipboard.", DefaultValue = "true")] - public bool AfterUploadLinkToClipBoard { get; set; } - - [IniProperty("RefreshToken", Description = "Dropbox refresh Token", Encrypted = true, ExcludeIfNull = true)] - public string RefreshToken { get; set; } - - /// - /// AccessToken, not stored - /// - public string AccessToken { get; set; } - - /// - /// AccessTokenExpires, not stored - /// - public DateTimeOffset AccessTokenExpires { get; set; } - - /// - /// A form for token - /// - /// bool true if OK was pressed, false if cancel - public bool ShowConfigDialog() - { - DialogResult result = new SettingsForm().ShowDialog(); - if (result == DialogResult.OK) - { - return true; - } - - return false; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel + * + * 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.Windows.Forms; +using Greenshot.Base.Core.Enums; +using Greenshot.Base.IniFile; +using Greenshot.Plugin.Dropbox.Forms; + +namespace Greenshot.Plugin.Dropbox +{ + /// + /// The configuration for Dropbox + /// + [IniSection("Dropbox", Description = "Greenshot Dropbox Plugin configuration")] + public class DropboxConfiguration : IniSection + { + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } + + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } + + [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Dropbox link to clipboard.", DefaultValue = "true")] + public bool AfterUploadLinkToClipBoard { get; set; } + + [IniProperty("RefreshToken", Description = "Dropbox refresh Token", Encrypted = true, ExcludeIfNull = true)] + public string RefreshToken { get; set; } + + /// + /// AccessToken, not stored + /// + public string AccessToken { get; set; } + + /// + /// AccessTokenExpires, not stored + /// + public DateTimeOffset AccessTokenExpires { get; set; } + + /// + /// A form for token + /// + /// bool true if OK was pressed, false if cancel + public bool ShowConfigDialog() + { + DialogResult result = new SettingsForm().ShowDialog(); + if (result == DialogResult.OK) + { + return true; + } + + return false; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs b/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs index 59f6c45b1..d29c3cbd0 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs @@ -29,7 +29,7 @@ namespace Greenshot.Plugin.Dropbox { internal class DropboxDestination : AbstractDestination { - private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection(); + private static readonly DropboxConfiguration DropboxConfig = IniConfig.GetIniSection(); private readonly DropboxPlugin _plugin; diff --git a/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs b/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs index 9a82b0f40..69026066c 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs @@ -37,7 +37,7 @@ namespace Greenshot.Plugin.Dropbox public class DropboxPlugin : IGreenshotPlugin { private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxPlugin)); - private static DropboxPluginConfiguration _config; + private static DropboxConfiguration _config; private ComponentResourceManager _resources; private ToolStripMenuItem _itemPlugInConfig; @@ -71,7 +71,7 @@ namespace Greenshot.Plugin.Dropbox public bool Initialize() { // Register configuration (don't need the configuration itself) - _config = IniConfig.GetIniSection(); + _config = IniConfig.GetIniSection(); _resources = new ComponentResourceManager(typeof(DropboxPlugin)); SimpleServiceProvider.Current.AddService(new DropboxDestination(this)); _itemPlugInConfig = new ToolStripMenuItem diff --git a/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs b/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs index 4b5eb4d60..c7f6dcfe9 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs @@ -37,7 +37,7 @@ namespace Greenshot.Plugin.Dropbox public class DropboxUtils { private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxUtils)); - private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection(); + private static readonly DropboxConfiguration DropboxConfig = IniConfig.GetIniSection(); private DropboxUtils() { diff --git a/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.Designer.cs index 174342a90..e65cba1ec 100644 --- a/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.Designer.cs @@ -86,7 +86,7 @@ namespace Greenshot.Plugin.Dropbox.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(116, 9); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = "UploadFormat"; + this.combobox_uploadimageformat.PropertyName = nameof(DropboxConfiguration.UploadFormat); this.combobox_uploadimageformat.SectionName = "Dropbox"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(309, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -112,7 +112,7 @@ namespace Greenshot.Plugin.Dropbox.Forms { this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "dropbox.label_AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(116, 37); this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard"; - this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard"; + this.checkboxAfterUploadLinkToClipBoard.PropertyName = nameof(DropboxConfiguration.AfterUploadLinkToClipBoard); this.checkboxAfterUploadLinkToClipBoard.SectionName = "Dropbox"; this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17); this.checkboxAfterUploadLinkToClipBoard.TabIndex = 2; diff --git a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs index 273535952..4083827e3 100644 --- a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs +++ b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs @@ -77,7 +77,7 @@ namespace Greenshot.Plugin.ExternalCommand } bool runInBackground = config.RunInbackground[_presetCommand]; - string fullPath = captureDetails.Filename ?? ImageOutput.SaveNamedTmpFile(surface, captureDetails, outputSettings); + string fullPath = captureDetails.Filename ?? ImageIO.SaveNamedTmpFile(surface, captureDetails, outputSettings); string output; string error; diff --git a/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.Designer.cs index 46549f943..5eed52b57 100644 --- a/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.Designer.cs @@ -93,7 +93,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(174, 6); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = "UploadFormat"; + this.combobox_uploadimageformat.PropertyName = nameof(FlickrConfiguration.UploadFormat); this.combobox_uploadimageformat.SectionName = "Flickr"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(251, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -111,7 +111,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkBoxPublic.LanguageKey = "flickr.public"; this.checkBoxPublic.Location = new System.Drawing.Point(174, 88); this.checkBoxPublic.Name = "checkBoxPublic"; - this.checkBoxPublic.PropertyName = "flickrIsPublic"; + this.checkBoxPublic.PropertyName = nameof(FlickrConfiguration.IsPublic); this.checkBoxPublic.SectionName = "Flickr"; this.checkBoxPublic.Size = new System.Drawing.Size(55, 17); this.checkBoxPublic.TabIndex = 4; @@ -122,7 +122,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkBoxFamily.LanguageKey = "flickr.family"; this.checkBoxFamily.Location = new System.Drawing.Point(265, 88); this.checkBoxFamily.Name = "checkBoxFamily"; - this.checkBoxFamily.PropertyName = "flickrIsFamily"; + this.checkBoxFamily.PropertyName = nameof(FlickrConfiguration.IsFamily); this.checkBoxFamily.SectionName = "Flickr"; this.checkBoxFamily.Size = new System.Drawing.Size(55, 17); this.checkBoxFamily.TabIndex = 5; @@ -133,7 +133,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkBoxFriend.LanguageKey = "flickr.friend"; this.checkBoxFriend.Location = new System.Drawing.Point(350, 88); this.checkBoxFriend.Name = "checkBoxFriend"; - this.checkBoxFriend.PropertyName = "flickrIsFriend"; + this.checkBoxFriend.PropertyName = nameof(FlickrConfiguration.IsFriend); this.checkBoxFriend.SectionName = "Flickr"; this.checkBoxFriend.Size = new System.Drawing.Size(55, 17); this.checkBoxFriend.TabIndex = 6; @@ -155,7 +155,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.combobox_safetyLevel.FormattingEnabled = true; this.combobox_safetyLevel.Location = new System.Drawing.Point(174, 33); this.combobox_safetyLevel.Name = "combobox_safetyLevel"; - this.combobox_safetyLevel.PropertyName = "SafetyLevel"; + this.combobox_safetyLevel.PropertyName = nameof(FlickrConfiguration.SafetyLevel); this.combobox_safetyLevel.SectionName = "Flickr"; this.combobox_safetyLevel.Size = new System.Drawing.Size(251, 21); this.combobox_safetyLevel.TabIndex = 2; @@ -173,7 +173,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "flickr.label_AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(173, 116); this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard"; - this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard"; + this.checkboxAfterUploadLinkToClipBoard.PropertyName = nameof(FlickrConfiguration.AfterUploadLinkToClipBoard); this.checkboxAfterUploadLinkToClipBoard.SectionName = "Flickr"; this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17); this.checkboxAfterUploadLinkToClipBoard.TabIndex = 7; @@ -184,7 +184,7 @@ namespace Greenshot.Plugin.Flickr.Forms { this.checkBox_hiddenfromsearch.LanguageKey = "flickr.label_HiddenFromSearch"; this.checkBox_hiddenfromsearch.Location = new System.Drawing.Point(174, 60); this.checkBox_hiddenfromsearch.Name = "checkBox_hiddenfromsearch"; - this.checkBox_hiddenfromsearch.PropertyName = "HiddenFromSearch"; + this.checkBox_hiddenfromsearch.PropertyName = nameof(FlickrConfiguration.HiddenFromSearch); this.checkBox_hiddenfromsearch.SectionName = "Flickr"; this.checkBox_hiddenfromsearch.Size = new System.Drawing.Size(118, 17); this.checkBox_hiddenfromsearch.TabIndex = 3; diff --git a/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs index 7b0325981..23e68012f 100644 --- a/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.Designer.cs @@ -86,7 +86,7 @@ namespace Greenshot.Plugin.GooglePhotos.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(197, 12); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = "UploadFormat"; + this.combobox_uploadimageformat.PropertyName = nameof(GooglePhotosConfiguration.UploadFormat); this.combobox_uploadimageformat.SectionName = "GooglePhotos"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(225, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -114,7 +114,7 @@ namespace Greenshot.Plugin.GooglePhotos.Forms { this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "googlephotos.label_AfterUploadLinkToClipBoard"; this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(197, 50); this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard"; - this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard"; + this.checkboxAfterUploadLinkToClipBoard.PropertyName = nameof(GooglePhotosConfiguration.AfterUploadLinkToClipBoard); this.checkboxAfterUploadLinkToClipBoard.SectionName = "GooglePhotos"; this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17); this.checkboxAfterUploadLinkToClipBoard.TabIndex = 2; diff --git a/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.Designer.cs index f40ee6460..1b9b52c42 100644 --- a/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.Designer.cs @@ -86,7 +86,7 @@ namespace Greenshot.Plugin.Imgur.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(168, 7); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = "UploadFormat"; + this.combobox_uploadimageformat.PropertyName = nameof(ImgurConfiguration.UploadFormat); this.combobox_uploadimageformat.SectionName = "Imgur"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(210, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -115,7 +115,7 @@ namespace Greenshot.Plugin.Imgur.Forms { this.checkbox_anonymous_access.LanguageKey = "imgur.anonymous_access"; this.checkbox_anonymous_access.Location = new System.Drawing.Point(15, 38); this.checkbox_anonymous_access.Name = "checkbox_anonymous_access"; - this.checkbox_anonymous_access.PropertyName = "AnonymousAccess"; + this.checkbox_anonymous_access.PropertyName = nameof(ImgurConfiguration.AnonymousAccess); this.checkbox_anonymous_access.SectionName = "Imgur"; this.checkbox_anonymous_access.Size = new System.Drawing.Size(139, 17); this.checkbox_anonymous_access.TabIndex = 2; @@ -126,7 +126,7 @@ namespace Greenshot.Plugin.Imgur.Forms { this.checkbox_usepagelink.LanguageKey = "imgur.use_page_link"; this.checkbox_usepagelink.Location = new System.Drawing.Point(15, 57); this.checkbox_usepagelink.Name = "checkbox_usepagelink"; - this.checkbox_usepagelink.PropertyName = "UsePageLink"; + this.checkbox_usepagelink.PropertyName = nameof(ImgurConfiguration.UsePageLink); this.checkbox_usepagelink.SectionName = "Imgur"; this.checkbox_usepagelink.Size = new System.Drawing.Size(251, 17); this.checkbox_usepagelink.TabIndex = 3; diff --git a/src/Greenshot.Plugin.Imgur/ImgurUtils.cs b/src/Greenshot.Plugin.Imgur/ImgurUtils.cs index a2156d6d0..02af34e56 100644 --- a/src/Greenshot.Plugin.Imgur/ImgurUtils.cs +++ b/src/Greenshot.Plugin.Imgur/ImgurUtils.cs @@ -179,7 +179,7 @@ namespace Greenshot.Plugin.Imgur { using (var requestStream = webRequest.GetRequestStream()) { - ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings); + ImageIO.SaveToStream(surfaceToUpload, requestStream, outputSettings); } using WebResponse response = webRequest.GetResponse(); @@ -265,7 +265,8 @@ namespace Greenshot.Plugin.Imgur Stream responseStream = response.GetResponseStream(); if (responseStream != null) { - imgurInfo.Image = ImageHelper.FromStream(responseStream); + // TODO: Replace with some other code, like the file format handler + imgurInfo.Image = ImageIO.FromStream(responseStream); } } diff --git a/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs index 945abe034..6903f74fd 100644 --- a/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Jira/Forms/SettingsForm.Designer.cs @@ -94,7 +94,7 @@ namespace Greenshot.Plugin.Jira.Forms { this.textBoxUrl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.textBoxUrl.Location = new System.Drawing.Point(164, 21); this.textBoxUrl.Name = "textBoxUrl"; - this.textBoxUrl.PropertyName = "Url"; + this.textBoxUrl.PropertyName = nameof(JiraConfiguration.Url); this.textBoxUrl.SectionName = "Jira"; this.textBoxUrl.Size = new System.Drawing.Size(214, 20); this.textBoxUrl.TabIndex = 6; @@ -105,7 +105,7 @@ namespace Greenshot.Plugin.Jira.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(164, 47); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = "UploadFormat"; + this.combobox_uploadimageformat.PropertyName = nameof(JiraConfiguration.UploadFormat); this.combobox_uploadimageformat.SectionName = "Jira"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(214, 21); this.combobox_uploadimageformat.TabIndex = 8; diff --git a/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs b/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs index cf09272e2..fb1930c8c 100644 --- a/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs @@ -89,7 +89,7 @@ namespace Greenshot.Plugin.Office.Destinations string imageFile = captureDetails.Filename; if (imageFile == null || surface.Modified || !Regex.IsMatch(imageFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - imageFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + imageFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); createdFile = true; } @@ -107,7 +107,7 @@ namespace Greenshot.Plugin.Office.Destinations // Cleanup imageFile if we created it here, so less tmp-files are generated and left if (createdFile) { - ImageOutput.DeleteNamedTmpFile(imageFile); + ImageIO.DeleteNamedTmpFile(imageFile); } return exportInformation; diff --git a/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs b/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs index da694be62..6d93c4096 100644 --- a/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs @@ -160,7 +160,7 @@ namespace Greenshot.Plugin.Office.Destinations string tmpFile = captureDetails.Filename; if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); } else { diff --git a/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs b/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs index a151d3388..3b9ce4bc8 100644 --- a/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs @@ -114,7 +114,7 @@ namespace Greenshot.Plugin.Office.Destinations Size imageSize = Size.Empty; if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); imageSize = surface.Image.Size; } diff --git a/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs b/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs index fd91295be..99cf4693c 100644 --- a/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs @@ -88,7 +88,7 @@ namespace Greenshot.Plugin.Office.Destinations string tmpFile = captureDetails.Filename; if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); } if (_documentCaption != null) diff --git a/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs b/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs index 305888ee2..645623780 100644 --- a/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs +++ b/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs @@ -97,7 +97,7 @@ namespace Greenshot.Plugin.Office.OfficeExport using var pngStream = new MemoryStream(); var pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); - ImageOutput.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings); + ImageIO.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings); var base64String = Convert.ToBase64String(pngStream.GetBuffer()); var imageXmlStr = string.Format(XmlImageContent, base64String, surfaceToUpload.Image.Width, surfaceToUpload.Image.Height); var pageChangesXml = string.Format(XmlOutline, imageXmlStr, page.Id, OnenoteNamespace2010, page.Name); diff --git a/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs b/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs index 9376f693e..1fff9f5c3 100644 --- a/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.Designer.cs @@ -84,7 +84,7 @@ namespace Greenshot.Plugin.Photobucket.Forms { this.combobox_uploadimageformat.FormattingEnabled = true; this.combobox_uploadimageformat.Location = new System.Drawing.Point(102, 11); this.combobox_uploadimageformat.Name = "combobox_uploadimageformat"; - this.combobox_uploadimageformat.PropertyName = "UploadFormat"; + this.combobox_uploadimageformat.PropertyName = nameof(PhotobucketConfiguration.UploadFormat); this.combobox_uploadimageformat.SectionName = "Photobucket"; this.combobox_uploadimageformat.Size = new System.Drawing.Size(276, 21); this.combobox_uploadimageformat.TabIndex = 1; @@ -102,7 +102,7 @@ namespace Greenshot.Plugin.Photobucket.Forms { this.checkbox_usepagelink.LanguageKey = "photobucket.use_page_link"; this.checkbox_usepagelink.Location = new System.Drawing.Point(15, 43); this.checkbox_usepagelink.Name = "checkbox_usepagelink"; - this.checkbox_usepagelink.PropertyName = "UsePageLink"; + this.checkbox_usepagelink.PropertyName = nameof(PhotobucketConfiguration.UsePageLink); this.checkbox_usepagelink.SectionName = "Photobucket"; this.checkbox_usepagelink.Size = new System.Drawing.Size(251, 17); this.checkbox_usepagelink.TabIndex = 2; diff --git a/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs b/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs index 0f2557da2..0574a6d0c 100644 --- a/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs +++ b/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs @@ -151,7 +151,7 @@ namespace Greenshot.Plugin.Win10.Destinations outputSettings.PreventGreenshotFormat(); // Create capture for export - ImageOutput.SaveToStream(surface, imageStream, outputSettings); + ImageIO.SaveToStream(surface, imageStream, outputSettings); imageStream.Position = 0; Log.Debug("Created RandomAccessStreamReference for the image"); var imageRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(imageStream); @@ -161,7 +161,7 @@ namespace Greenshot.Plugin.Win10.Destinations using (var tmpImageForThumbnail = surface.GetImageForExport()) using (var thumbnail = ImageHelper.CreateThumbnail(tmpImageForThumbnail, 240, 160)) { - ImageOutput.SaveToStream(thumbnail, null, thumbnailStream, outputSettings); + ImageIO.SaveToStream(thumbnail, null, thumbnailStream, outputSettings); thumbnailStream.Position = 0; thumbnailRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(thumbnailStream); Log.Debug("Created RandomAccessStreamReference for the thumbnail"); @@ -172,7 +172,7 @@ namespace Greenshot.Plugin.Win10.Destinations using (var logo = GreenshotResources.GetGreenshotIcon().ToBitmap()) using (var logoThumbnail = ImageHelper.CreateThumbnail(logo, 30, 30)) { - ImageOutput.SaveToStream(logoThumbnail, null, logoStream, outputSettings); + ImageIO.SaveToStream(logoThumbnail, null, logoStream, outputSettings); logoStream.Position = 0; logoRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(logoStream); Log.Info("Created RandomAccessStreamReference for the logo"); diff --git a/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs b/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs index 77aa0d663..fdc0c07fa 100644 --- a/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs +++ b/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs @@ -97,7 +97,7 @@ namespace Greenshot.Plugin.Win10 IEffect effect = new ResizeCanvasEffect(addedWidth, addedWidth, addedHeight, addedHeight); outputSettings.Effects.Add(effect); } - ImageOutput.SaveToStream(surface, imageStream, outputSettings); + ImageIO.SaveToStream(surface, imageStream, outputSettings); imageStream.Position = 0; var randomAccessStream = imageStream.AsRandomAccessStream(); @@ -117,7 +117,7 @@ namespace Greenshot.Plugin.Win10 OcrInformation result; using (var imageStream = new MemoryStream()) { - ImageOutput.SaveToStream(image, null, imageStream, new SurfaceOutputSettings()); + ImageIO.SaveToStream(image, null, imageStream, new SurfaceOutputSettings()); imageStream.Position = 0; var randomAccessStream = imageStream.AsRandomAccessStream(); diff --git a/src/Greenshot/App.config b/src/Greenshot/App.config index 3b81b058d..80b985a5c 100644 --- a/src/Greenshot/App.config +++ b/src/Greenshot/App.config @@ -1,7 +1,6 @@ - diff --git a/src/Greenshot/Destinations/FileDestination.cs b/src/Greenshot/Destinations/FileDestination.cs index 9389c3d26..64f8f21a9 100644 --- a/src/Greenshot/Destinations/FileDestination.cs +++ b/src/Greenshot/Destinations/FileDestination.cs @@ -68,7 +68,7 @@ namespace Greenshot.Destinations overwrite = true; Log.InfoFormat("Using previous filename"); fullPath = captureDetails.Filename; - outputSettings.Format = ImageOutput.FormatForFilename(fullPath); + outputSettings.Format = ImageIO.FormatForFilename(fullPath); } else { @@ -87,7 +87,7 @@ namespace Greenshot.Destinations // This is done for e.g. bugs #2974608, #2963943, #2816163, #2795317, #2789218, #3004642 try { - ImageOutput.Save(surface, fullPath, overwrite, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); + ImageIO.Save(surface, fullPath, overwrite, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); outputMade = true; } catch (ArgumentException ex1) @@ -95,7 +95,7 @@ namespace Greenshot.Destinations // Our generated filename exists, display 'save-as' Log.InfoFormat("Not overwriting: {0}", ex1.Message); // when we don't allow to overwrite present a new SaveWithDialog - fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); + fullPath = ImageIO.SaveWithDialog(surface, captureDetails); outputMade = fullPath != null; } catch (Exception ex2) @@ -104,7 +104,7 @@ namespace Greenshot.Destinations // Show the problem MessageBox.Show(Language.GetString(LangKey.error_save), Language.GetString(LangKey.error)); // when save failed we present a SaveWithDialog - fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); + fullPath = ImageIO.SaveWithDialog(surface, captureDetails); outputMade = fullPath != null; } diff --git a/src/Greenshot/Destinations/FileWithDialogDestination.cs b/src/Greenshot/Destinations/FileWithDialogDestination.cs index 3a8520fb0..72924689f 100644 --- a/src/Greenshot/Destinations/FileWithDialogDestination.cs +++ b/src/Greenshot/Destinations/FileWithDialogDestination.cs @@ -57,7 +57,7 @@ namespace Greenshot.Destinations { ExportInformation exportInformation = new ExportInformation(Designation, Description); // Bug #2918756 don't overwrite path if SaveWithDialog returns null! - var savedTo = ImageOutput.SaveWithDialog(surface, captureDetails); + var savedTo = ImageIO.SaveWithDialog(surface, captureDetails); if (savedTo != null) { exportInformation.ExportMade = true; diff --git a/src/Greenshot/Forms/MainForm.cs b/src/Greenshot/Forms/MainForm.cs index f1c4f8bbc..fc7fb046d 100644 --- a/src/Greenshot/Forms/MainForm.cs +++ b/src/Greenshot/Forms/MainForm.cs @@ -36,6 +36,7 @@ using Greenshot.Base; using Greenshot.Base.Controls; using Greenshot.Base.Core; using Greenshot.Base.Core.Enums; +using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.Help; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; @@ -43,6 +44,7 @@ using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.UnmanagedHelpers; using Greenshot.Configuration; using Greenshot.Destinations; +using Greenshot.Editor; using Greenshot.Editor.Destinations; using Greenshot.Editor.Drawing; using Greenshot.Editor.Forms; @@ -162,7 +164,7 @@ namespace Greenshot.Forms if (argument.ToLower().Equals("/exit")) { - // unregister application on uninstall (allow uninstall) + // un-register application on uninstall (allow uninstall) try { LOG.Info("Sending all instances the exit command."); @@ -387,6 +389,8 @@ namespace Greenshot.Forms _instance = this; + EditorInitialize.Initialize(); + // Factory for surface objects ISurface SurfaceFactory() => new Surface(); @@ -922,11 +926,11 @@ namespace Greenshot.Forms { Hide(); ShowInTaskbar = false; - - - using var loProcess = Process.GetCurrentProcess(); - loProcess.MaxWorkingSet = (IntPtr)750000; - loProcess.MinWorkingSet = (IntPtr)300000; + + // TODO: Do we really need this? + //using var loProcess = Process.GetCurrentProcess(); + //loProcess.MaxWorkingSet = (IntPtr)750000; + //loProcess.MinWorkingSet = (IntPtr)300000; } private void CaptureRegion() @@ -936,9 +940,12 @@ namespace Greenshot.Forms private void CaptureFile(IDestination destination = null) { + var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); + var extensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream).Select(e => $"*{e}").ToList(); + var openFileDialog = new OpenFileDialog { - Filter = @"Image files (*.greenshot, *.png, *.jpg, *.gif, *.bmp, *.ico, *.tiff, *.wmf)|*.greenshot; *.png; *.jpg; *.jpeg; *.gif; *.bmp; *.ico; *.tiff; *.tif; *.wmf" + Filter = @$"Image files ({string.Join(", ", extensions)})|{string.Join("; ", extensions)}" }; if (openFileDialog.ShowDialog() != DialogResult.OK) { @@ -1904,7 +1911,7 @@ namespace Greenshot.Forms LOG.Error("Error closing application!", e); } - ImageOutput.RemoveTmpFiles(); + ImageIO.RemoveTmpFiles(); // Store any open configuration changes try diff --git a/src/Greenshot/Forms/PrintOptionsDialog.Designer.cs b/src/Greenshot/Forms/PrintOptionsDialog.Designer.cs index 9cb5cc43d..9f9d0a13a 100644 --- a/src/Greenshot/Forms/PrintOptionsDialog.Designer.cs +++ b/src/Greenshot/Forms/PrintOptionsDialog.Designer.cs @@ -89,7 +89,7 @@ namespace Greenshot.Forms this.checkboxAllowShrink.LanguageKey = "printoptions_allowshrink"; this.checkboxAllowShrink.Location = new System.Drawing.Point(13, 23); this.checkboxAllowShrink.Name = "checkboxAllowShrink"; - this.checkboxAllowShrink.PropertyName = "OutputPrintAllowShrink"; + this.checkboxAllowShrink.PropertyName = nameof(coreConfiguration.OutputPrintAllowShrink); this.checkboxAllowShrink.Size = new System.Drawing.Size(168, 17); this.checkboxAllowShrink.TabIndex = 2; this.checkboxAllowShrink.Text = "Shrink printout to fit paper size"; @@ -103,7 +103,7 @@ namespace Greenshot.Forms this.checkboxAllowEnlarge.LanguageKey = "printoptions_allowenlarge"; this.checkboxAllowEnlarge.Location = new System.Drawing.Point(13, 46); this.checkboxAllowEnlarge.Name = "checkboxAllowEnlarge"; - this.checkboxAllowEnlarge.PropertyName = "OutputPrintAllowEnlarge"; + this.checkboxAllowEnlarge.PropertyName = nameof(coreConfiguration.OutputPrintAllowEnlarge); this.checkboxAllowEnlarge.Size = new System.Drawing.Size(174, 17); this.checkboxAllowEnlarge.TabIndex = 3; this.checkboxAllowEnlarge.Text = "Enlarge printout to fit paper size"; @@ -117,7 +117,7 @@ namespace Greenshot.Forms this.checkboxAllowCenter.LanguageKey = "printoptions_allowcenter"; this.checkboxAllowCenter.Location = new System.Drawing.Point(13, 92); this.checkboxAllowCenter.Name = "checkboxAllowCenter"; - this.checkboxAllowCenter.PropertyName = "OutputPrintCenter"; + this.checkboxAllowCenter.PropertyName = nameof(coreConfiguration.OutputPrintCenter); this.checkboxAllowCenter.Size = new System.Drawing.Size(137, 17); this.checkboxAllowCenter.TabIndex = 5; this.checkboxAllowCenter.Text = "Center printout on page"; @@ -131,7 +131,7 @@ namespace Greenshot.Forms this.checkboxAllowRotate.LanguageKey = "printoptions_allowrotate"; this.checkboxAllowRotate.Location = new System.Drawing.Point(13, 69); this.checkboxAllowRotate.Name = "checkboxAllowRotate"; - this.checkboxAllowRotate.PropertyName = "OutputPrintAllowRotate"; + this.checkboxAllowRotate.PropertyName = nameof(coreConfiguration.OutputPrintAllowRotate); this.checkboxAllowRotate.Size = new System.Drawing.Size(187, 17); this.checkboxAllowRotate.TabIndex = 4; this.checkboxAllowRotate.Text = "Rotate printout to page orientation"; @@ -158,7 +158,7 @@ namespace Greenshot.Forms this.checkboxDateTime.LanguageKey = "printoptions_timestamp"; this.checkboxDateTime.Location = new System.Drawing.Point(13, 115); this.checkboxDateTime.Name = "checkboxDateTime"; - this.checkboxDateTime.PropertyName = "OutputPrintFooter"; + this.checkboxDateTime.PropertyName = nameof(coreConfiguration.OutputPrintFooter); this.checkboxDateTime.Size = new System.Drawing.Size(187, 17); this.checkboxDateTime.TabIndex = 6; this.checkboxDateTime.Text = "Print date / time at bottom of page"; @@ -184,7 +184,7 @@ namespace Greenshot.Forms this.checkboxPrintInverted.LanguageKey = "printoptions_inverted"; this.checkboxPrintInverted.Location = new System.Drawing.Point(13, 88); this.checkboxPrintInverted.Name = "checkboxPrintInverted"; - this.checkboxPrintInverted.PropertyName = "OutputPrintInverted"; + this.checkboxPrintInverted.PropertyName = nameof(coreConfiguration.OutputPrintInverted); this.checkboxPrintInverted.Size = new System.Drawing.Size(141, 17); this.checkboxPrintInverted.TabIndex = 14; this.checkboxPrintInverted.Text = "Print with inverted colors"; @@ -198,7 +198,7 @@ namespace Greenshot.Forms this.radioBtnGrayScale.LanguageKey = "printoptions_printgrayscale"; this.radioBtnGrayScale.Location = new System.Drawing.Point(13, 42); this.radioBtnGrayScale.Name = "radioBtnGrayScale"; - this.radioBtnGrayScale.PropertyName = "OutputPrintGrayscale"; + this.radioBtnGrayScale.PropertyName = nameof(coreConfiguration.OutputPrintGrayscale); this.radioBtnGrayScale.Size = new System.Drawing.Size(137, 17); this.radioBtnGrayScale.TabIndex = 12; this.radioBtnGrayScale.Text = "Force grayscale printing"; @@ -212,7 +212,7 @@ namespace Greenshot.Forms this.radioBtnMonochrome.LanguageKey = "printoptions_printmonochrome"; this.radioBtnMonochrome.Location = new System.Drawing.Point(13, 65); this.radioBtnMonochrome.Name = "radioBtnMonochrome"; - this.radioBtnMonochrome.PropertyName = "OutputPrintMonochrome"; + this.radioBtnMonochrome.PropertyName = nameof(coreConfiguration.OutputPrintMonochrome); this.radioBtnMonochrome.Size = new System.Drawing.Size(148, 17); this.radioBtnMonochrome.TabIndex = 13; this.radioBtnMonochrome.Text = "Force black/white printing"; @@ -255,7 +255,6 @@ namespace Greenshot.Forms this.radioBtnColorPrint.LanguageKey = "printoptions_printcolor"; this.radioBtnColorPrint.Location = new System.Drawing.Point(13, 19); this.radioBtnColorPrint.Name = "radioBtnColorPrint"; - this.radioBtnColorPrint.PropertyName = "OutputPrintColor"; this.radioBtnColorPrint.Size = new System.Drawing.Size(90, 17); this.radioBtnColorPrint.TabIndex = 11; this.radioBtnColorPrint.TextAlign = System.Drawing.ContentAlignment.TopLeft; diff --git a/src/Greenshot/Forms/SettingsForm.Designer.cs b/src/Greenshot/Forms/SettingsForm.Designer.cs index e8f284850..6bea356df 100644 --- a/src/Greenshot/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot/Forms/SettingsForm.Designer.cs @@ -20,6 +20,8 @@ */ using Greenshot.Base.Controls; +using Greenshot.Base.Core; +using Greenshot.Editor.Configuration; using Greenshot.Editor.Controls; namespace Greenshot.Forms { @@ -235,7 +237,7 @@ namespace Greenshot.Forms { // this.textbox_screenshotname.Location = new System.Drawing.Point(138, 41); this.textbox_screenshotname.Name = "textbox_screenshotname"; - this.textbox_screenshotname.PropertyName = "OutputFileFilenamePattern"; + this.textbox_screenshotname.PropertyName = nameof(CoreConfiguration.OutputFileFilenamePattern); this.textbox_screenshotname.Size = new System.Drawing.Size(233, 20); this.textbox_screenshotname.TabIndex = 3; this.textbox_screenshotname.TextChanged += new System.EventHandler(this.FilenamePatternChanged); @@ -264,7 +266,7 @@ namespace Greenshot.Forms { this.combobox_primaryimageformat.FormattingEnabled = true; this.combobox_primaryimageformat.Location = new System.Drawing.Point(138, 64); this.combobox_primaryimageformat.Name = "combobox_primaryimageformat"; - this.combobox_primaryimageformat.PropertyName = "OutputFileFormat"; + this.combobox_primaryimageformat.PropertyName = nameof(CoreConfiguration.OutputFileFormat); this.combobox_primaryimageformat.Size = new System.Drawing.Size(268, 21); this.combobox_primaryimageformat.TabIndex = 5; // @@ -309,7 +311,7 @@ namespace Greenshot.Forms { this.checkbox_copypathtoclipboard.LanguageKey = "settings_copypathtoclipboard"; this.checkbox_copypathtoclipboard.Location = new System.Drawing.Point(12, 89); this.checkbox_copypathtoclipboard.Name = "checkbox_copypathtoclipboard"; - this.checkbox_copypathtoclipboard.PropertyName = "OutputFileCopyPathToClipboard"; + this.checkbox_copypathtoclipboard.PropertyName = nameof(CoreConfiguration.OutputFileCopyPathToClipboard); this.checkbox_copypathtoclipboard.Size = new System.Drawing.Size(394, 24); this.checkbox_copypathtoclipboard.TabIndex = 6; this.checkbox_copypathtoclipboard.UseVisualStyleBackColor = true; @@ -374,7 +376,7 @@ namespace Greenshot.Forms { this.checkbox_reducecolors.LanguageKey = "settings_reducecolors"; this.checkbox_reducecolors.Location = new System.Drawing.Point(12, 72); this.checkbox_reducecolors.Name = "checkbox_reducecolors"; - this.checkbox_reducecolors.PropertyName = "OutputFileReduceColors"; + this.checkbox_reducecolors.PropertyName = nameof(CoreConfiguration.OutputFileReduceColors); this.checkbox_reducecolors.Size = new System.Drawing.Size(394, 25); this.checkbox_reducecolors.TabIndex = 10; this.checkbox_reducecolors.UseVisualStyleBackColor = true; @@ -384,7 +386,7 @@ namespace Greenshot.Forms { this.checkbox_alwaysshowqualitydialog.LanguageKey = "settings_alwaysshowqualitydialog"; this.checkbox_alwaysshowqualitydialog.Location = new System.Drawing.Point(12, 50); this.checkbox_alwaysshowqualitydialog.Name = "checkbox_alwaysshowqualitydialog"; - this.checkbox_alwaysshowqualitydialog.PropertyName = "OutputFilePromptQuality"; + this.checkbox_alwaysshowqualitydialog.PropertyName = nameof(CoreConfiguration.OutputFilePromptQuality); this.checkbox_alwaysshowqualitydialog.Size = new System.Drawing.Size(394, 25); this.checkbox_alwaysshowqualitydialog.TabIndex = 9; this.checkbox_alwaysshowqualitydialog.UseVisualStyleBackColor = true; @@ -526,7 +528,7 @@ namespace Greenshot.Forms { this.checkbox_usedefaultproxy.LanguageKey = "settings_usedefaultproxy"; this.checkbox_usedefaultproxy.Location = new System.Drawing.Point(7, 11); this.checkbox_usedefaultproxy.Name = "checkbox_usedefaultproxy"; - this.checkbox_usedefaultproxy.PropertyName = "UseProxy"; + this.checkbox_usedefaultproxy.PropertyName = nameof(CoreConfiguration.UseProxy); this.checkbox_usedefaultproxy.Size = new System.Drawing.Size(397, 25); this.checkbox_usedefaultproxy.TabIndex = 7; this.checkbox_usedefaultproxy.UseVisualStyleBackColor = true; @@ -564,7 +566,7 @@ namespace Greenshot.Forms { this.lastregion_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.lastregion_hotkeyControl.Location = new System.Drawing.Point(224, 94); this.lastregion_hotkeyControl.Name = "lastregion_hotkeyControl"; - this.lastregion_hotkeyControl.PropertyName = "LastregionHotkey"; + this.lastregion_hotkeyControl.PropertyName = nameof(CoreConfiguration.LastregionHotkey); this.lastregion_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.lastregion_hotkeyControl.TabIndex = 5; // @@ -582,7 +584,7 @@ namespace Greenshot.Forms { this.ie_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.ie_hotkeyControl.Location = new System.Drawing.Point(224, 120); this.ie_hotkeyControl.Name = "ie_hotkeyControl"; - this.ie_hotkeyControl.PropertyName = "IEHotkey"; + this.ie_hotkeyControl.PropertyName = nameof(CoreConfiguration.IEHotkey); this.ie_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.ie_hotkeyControl.TabIndex = 6; // @@ -616,7 +618,7 @@ namespace Greenshot.Forms { this.region_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.region_hotkeyControl.Location = new System.Drawing.Point(224, 68); this.region_hotkeyControl.Name = "region_hotkeyControl"; - this.region_hotkeyControl.PropertyName = "RegionHotkey"; + this.region_hotkeyControl.PropertyName = nameof(CoreConfiguration.RegionHotkey); this.region_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.region_hotkeyControl.TabIndex = 4; // @@ -626,7 +628,7 @@ namespace Greenshot.Forms { this.window_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.window_hotkeyControl.Location = new System.Drawing.Point(224, 42); this.window_hotkeyControl.Name = "window_hotkeyControl"; - this.window_hotkeyControl.PropertyName = "WindowHotkey"; + this.window_hotkeyControl.PropertyName = nameof(CoreConfiguration.WindowHotkey); this.window_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.window_hotkeyControl.TabIndex = 3; // @@ -636,7 +638,7 @@ namespace Greenshot.Forms { this.fullscreen_hotkeyControl.HotkeyModifiers = System.Windows.Forms.Keys.None; this.fullscreen_hotkeyControl.Location = new System.Drawing.Point(224, 16); this.fullscreen_hotkeyControl.Name = "fullscreen_hotkeyControl"; - this.fullscreen_hotkeyControl.PropertyName = "FullscreenHotkey"; + this.fullscreen_hotkeyControl.PropertyName = nameof(CoreConfiguration.FullscreenHotkey); this.fullscreen_hotkeyControl.Size = new System.Drawing.Size(179, 20); this.fullscreen_hotkeyControl.TabIndex = 2; // @@ -668,7 +670,7 @@ namespace Greenshot.Forms { this.checkbox_editor_match_capture_size.LanguageKey = "editor_match_capture_size"; this.checkbox_editor_match_capture_size.Location = new System.Drawing.Point(6, 19); this.checkbox_editor_match_capture_size.Name = "checkbox_editor_match_capture_size"; - this.checkbox_editor_match_capture_size.PropertyName = "MatchSizeToCapture"; + this.checkbox_editor_match_capture_size.PropertyName = nameof(EditorConfiguration.MatchSizeToCapture); this.checkbox_editor_match_capture_size.SectionName = "Editor"; this.checkbox_editor_match_capture_size.Size = new System.Drawing.Size(397, 24); this.checkbox_editor_match_capture_size.TabIndex = 11; @@ -689,7 +691,7 @@ namespace Greenshot.Forms { this.checkbox_ie_capture.LanguageKey = "settings_iecapture"; this.checkbox_ie_capture.Location = new System.Drawing.Point(6, 19); this.checkbox_ie_capture.Name = "checkbox_ie_capture"; - this.checkbox_ie_capture.PropertyName = "IECapture"; + this.checkbox_ie_capture.PropertyName = nameof(CoreConfiguration.IECapture); this.checkbox_ie_capture.Size = new System.Drawing.Size(404, 24); this.checkbox_ie_capture.TabIndex = 10; this.checkbox_ie_capture.UseVisualStyleBackColor = true; @@ -732,7 +734,7 @@ namespace Greenshot.Forms { this.radiobuttonInteractiveCapture.LanguageKey = "settings_capture_windows_interactive"; this.radiobuttonInteractiveCapture.Location = new System.Drawing.Point(11, 20); this.radiobuttonInteractiveCapture.Name = "radiobuttonInteractiveCapture"; - this.radiobuttonInteractiveCapture.PropertyName = "CaptureWindowsInteractive"; + this.radiobuttonInteractiveCapture.PropertyName = nameof(CoreConfiguration.CaptureWindowsInteractive); this.radiobuttonInteractiveCapture.Size = new System.Drawing.Size(203, 17); this.radiobuttonInteractiveCapture.TabIndex = 6; this.radiobuttonInteractiveCapture.TabStop = true; @@ -770,7 +772,7 @@ namespace Greenshot.Forms { this.checkbox_zoomer.LanguageKey = "settings_zoom"; this.checkbox_zoomer.Location = new System.Drawing.Point(11, 79); this.checkbox_zoomer.Name = "checkbox_zoomer"; - this.checkbox_zoomer.PropertyName = "ZoomerEnabled"; + this.checkbox_zoomer.PropertyName = nameof(CoreConfiguration.ZoomerEnabled); this.checkbox_zoomer.Size = new System.Drawing.Size(399, 24); this.checkbox_zoomer.TabIndex = 4; this.checkbox_zoomer.UseVisualStyleBackColor = true; @@ -780,7 +782,7 @@ namespace Greenshot.Forms { this.checkbox_notifications.LanguageKey = "settings_shownotify"; this.checkbox_notifications.Location = new System.Drawing.Point(11, 59); this.checkbox_notifications.Name = "checkbox_notifications"; - this.checkbox_notifications.PropertyName = "ShowTrayNotification"; + this.checkbox_notifications.PropertyName = nameof(CoreConfiguration.ShowTrayNotification); this.checkbox_notifications.Size = new System.Drawing.Size(399, 24); this.checkbox_notifications.TabIndex = 3; this.checkbox_notifications.UseVisualStyleBackColor = true; @@ -790,7 +792,7 @@ namespace Greenshot.Forms { this.checkbox_playsound.LanguageKey = "settings_playsound"; this.checkbox_playsound.Location = new System.Drawing.Point(11, 39); this.checkbox_playsound.Name = "checkbox_playsound"; - this.checkbox_playsound.PropertyName = "PlayCameraSound"; + this.checkbox_playsound.PropertyName = nameof(CoreConfiguration.PlayCameraSound); this.checkbox_playsound.Size = new System.Drawing.Size(399, 24); this.checkbox_playsound.TabIndex = 2; this.checkbox_playsound.UseVisualStyleBackColor = true; @@ -800,7 +802,7 @@ namespace Greenshot.Forms { this.checkbox_capture_mousepointer.LanguageKey = "settings_capture_mousepointer"; this.checkbox_capture_mousepointer.Location = new System.Drawing.Point(11, 19); this.checkbox_capture_mousepointer.Name = "checkbox_capture_mousepointer"; - this.checkbox_capture_mousepointer.PropertyName = "CaptureMousepointer"; + this.checkbox_capture_mousepointer.PropertyName = nameof(CoreConfiguration.CaptureMousepointer); this.checkbox_capture_mousepointer.Size = new System.Drawing.Size(394, 24); this.checkbox_capture_mousepointer.TabIndex = 1; this.checkbox_capture_mousepointer.UseVisualStyleBackColor = true; @@ -887,7 +889,7 @@ namespace Greenshot.Forms { this.checkboxPrintInverted.LanguageKey = "printoptions_inverted"; this.checkboxPrintInverted.Location = new System.Drawing.Point(13, 88); this.checkboxPrintInverted.Name = "checkboxPrintInverted"; - this.checkboxPrintInverted.PropertyName = "OutputPrintInverted"; + this.checkboxPrintInverted.PropertyName = nameof(CoreConfiguration.OutputPrintInverted); this.checkboxPrintInverted.Size = new System.Drawing.Size(141, 17); this.checkboxPrintInverted.TabIndex = 14; this.checkboxPrintInverted.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -900,7 +902,6 @@ namespace Greenshot.Forms { this.radioBtnColorPrint.LanguageKey = "printoptions_printcolor"; this.radioBtnColorPrint.Location = new System.Drawing.Point(13, 19); this.radioBtnColorPrint.Name = "radioBtnColorPrint"; - this.radioBtnColorPrint.PropertyName = "OutputPrintColor"; this.radioBtnColorPrint.Size = new System.Drawing.Size(90, 17); this.radioBtnColorPrint.TabIndex = 11; this.radioBtnColorPrint.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -913,7 +914,7 @@ namespace Greenshot.Forms { this.radioBtnGrayScale.LanguageKey = "printoptions_printgrayscale"; this.radioBtnGrayScale.Location = new System.Drawing.Point(13, 42); this.radioBtnGrayScale.Name = "radioBtnGrayScale"; - this.radioBtnGrayScale.PropertyName = "OutputPrintGrayscale"; + this.radioBtnGrayScale.PropertyName = nameof(coreConfiguration.OutputPrintGrayscale); this.radioBtnGrayScale.Size = new System.Drawing.Size(137, 17); this.radioBtnGrayScale.TabIndex = 12; this.radioBtnGrayScale.Text = "Force grayscale printing"; @@ -927,7 +928,7 @@ namespace Greenshot.Forms { this.radioBtnMonochrome.LanguageKey = "printoptions_printmonochrome"; this.radioBtnMonochrome.Location = new System.Drawing.Point(13, 65); this.radioBtnMonochrome.Name = "radioBtnMonochrome"; - this.radioBtnMonochrome.PropertyName = "OutputPrintMonochrome"; + this.radioBtnMonochrome.PropertyName = nameof(coreConfiguration.OutputPrintMonochrome); this.radioBtnMonochrome.Size = new System.Drawing.Size(148, 17); this.radioBtnMonochrome.TabIndex = 13; this.radioBtnMonochrome.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -954,7 +955,7 @@ namespace Greenshot.Forms { this.checkboxDateTime.LanguageKey = "printoptions_timestamp"; this.checkboxDateTime.Location = new System.Drawing.Point(13, 115); this.checkboxDateTime.Name = "checkboxDateTime"; - this.checkboxDateTime.PropertyName = "OutputPrintFooter"; + this.checkboxDateTime.PropertyName = nameof(coreConfiguration.OutputPrintFooter); this.checkboxDateTime.Size = new System.Drawing.Size(187, 17); this.checkboxDateTime.TabIndex = 6; this.checkboxDateTime.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -967,7 +968,7 @@ namespace Greenshot.Forms { this.checkboxAllowShrink.LanguageKey = "printoptions_allowshrink"; this.checkboxAllowShrink.Location = new System.Drawing.Point(13, 23); this.checkboxAllowShrink.Name = "checkboxAllowShrink"; - this.checkboxAllowShrink.PropertyName = "OutputPrintAllowShrink"; + this.checkboxAllowShrink.PropertyName = nameof(coreConfiguration.OutputPrintAllowShrink); this.checkboxAllowShrink.Size = new System.Drawing.Size(168, 17); this.checkboxAllowShrink.TabIndex = 2; this.checkboxAllowShrink.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -980,7 +981,7 @@ namespace Greenshot.Forms { this.checkboxAllowEnlarge.LanguageKey = "printoptions_allowenlarge"; this.checkboxAllowEnlarge.Location = new System.Drawing.Point(13, 46); this.checkboxAllowEnlarge.Name = "checkboxAllowEnlarge"; - this.checkboxAllowEnlarge.PropertyName = "OutputPrintAllowEnlarge"; + this.checkboxAllowEnlarge.PropertyName = nameof(coreConfiguration.OutputPrintAllowEnlarge); this.checkboxAllowEnlarge.Size = new System.Drawing.Size(174, 17); this.checkboxAllowEnlarge.TabIndex = 3; this.checkboxAllowEnlarge.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -993,7 +994,7 @@ namespace Greenshot.Forms { this.checkboxAllowRotate.LanguageKey = "printoptions_allowrotate"; this.checkboxAllowRotate.Location = new System.Drawing.Point(13, 69); this.checkboxAllowRotate.Name = "checkboxAllowRotate"; - this.checkboxAllowRotate.PropertyName = "OutputPrintAllowRotate"; + this.checkboxAllowRotate.PropertyName = nameof(coreConfiguration.OutputPrintAllowRotate); this.checkboxAllowRotate.Size = new System.Drawing.Size(187, 17); this.checkboxAllowRotate.TabIndex = 4; this.checkboxAllowRotate.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -1006,7 +1007,7 @@ namespace Greenshot.Forms { this.checkboxAllowCenter.LanguageKey = "printoptions_allowcenter"; this.checkboxAllowCenter.Location = new System.Drawing.Point(13, 92); this.checkboxAllowCenter.Name = "checkboxAllowCenter"; - this.checkboxAllowCenter.PropertyName = "OutputPrintCenter"; + this.checkboxAllowCenter.PropertyName = nameof(coreConfiguration.OutputPrintCenter); this.checkboxAllowCenter.Size = new System.Drawing.Size(137, 17); this.checkboxAllowCenter.TabIndex = 5; this.checkboxAllowCenter.TextAlign = System.Drawing.ContentAlignment.TopLeft; @@ -1017,7 +1018,7 @@ namespace Greenshot.Forms { this.checkbox_alwaysshowprintoptionsdialog.LanguageKey = "settings_alwaysshowprintoptionsdialog"; this.checkbox_alwaysshowprintoptionsdialog.Location = new System.Drawing.Point(19, 293); this.checkbox_alwaysshowprintoptionsdialog.Name = "checkbox_alwaysshowprintoptionsdialog"; - this.checkbox_alwaysshowprintoptionsdialog.PropertyName = "OutputPrintPromptOptions"; + this.checkbox_alwaysshowprintoptionsdialog.PropertyName = nameof(coreConfiguration.OutputPrintPromptOptions); this.checkbox_alwaysshowprintoptionsdialog.Size = new System.Drawing.Size(394, 20); this.checkbox_alwaysshowprintoptionsdialog.TabIndex = 15; this.checkbox_alwaysshowprintoptionsdialog.Text = "Show print options dialog every time an image is printed"; @@ -1114,7 +1115,7 @@ namespace Greenshot.Forms { this.checkbox_reuseeditor.LanguageKey = "expertsettings_reuseeditorifpossible"; this.checkbox_reuseeditor.Location = new System.Drawing.Point(10, 225); this.checkbox_reuseeditor.Name = "checkbox_reuseeditor"; - this.checkbox_reuseeditor.PropertyName = "ReuseEditor"; + this.checkbox_reuseeditor.PropertyName = nameof(EditorConfiguration.ReuseEditor); this.checkbox_reuseeditor.SectionName = "Editor"; this.checkbox_reuseeditor.Size = new System.Drawing.Size(394, 24); this.checkbox_reuseeditor.TabIndex = 9; @@ -1125,7 +1126,7 @@ namespace Greenshot.Forms { this.checkbox_minimizememoryfootprint.LanguageKey = "expertsettings_minimizememoryfootprint"; this.checkbox_minimizememoryfootprint.Location = new System.Drawing.Point(10, 206); this.checkbox_minimizememoryfootprint.Name = "checkbox_minimizememoryfootprint"; - this.checkbox_minimizememoryfootprint.PropertyName = "MinimizeWorkingSetSize"; + this.checkbox_minimizememoryfootprint.PropertyName = nameof(coreConfiguration.MinimizeWorkingSetSize); this.checkbox_minimizememoryfootprint.Size = new System.Drawing.Size(394, 24); this.checkbox_minimizememoryfootprint.TabIndex = 8; this.checkbox_minimizememoryfootprint.UseVisualStyleBackColor = true; @@ -1135,7 +1136,7 @@ namespace Greenshot.Forms { this.checkbox_checkunstableupdates.LanguageKey = "expertsettings_checkunstableupdates"; this.checkbox_checkunstableupdates.Location = new System.Drawing.Point(10, 187); this.checkbox_checkunstableupdates.Name = "checkbox_checkunstableupdates"; - this.checkbox_checkunstableupdates.PropertyName = "CheckForUnstable"; + this.checkbox_checkunstableupdates.PropertyName = nameof(coreConfiguration.CheckForUnstable); this.checkbox_checkunstableupdates.Size = new System.Drawing.Size(394, 24); this.checkbox_checkunstableupdates.TabIndex = 7; this.checkbox_checkunstableupdates.UseVisualStyleBackColor = true; @@ -1145,7 +1146,7 @@ namespace Greenshot.Forms { this.checkbox_suppresssavedialogatclose.LanguageKey = "expertsettings_suppresssavedialogatclose"; this.checkbox_suppresssavedialogatclose.Location = new System.Drawing.Point(10, 168); this.checkbox_suppresssavedialogatclose.Name = "checkbox_suppresssavedialogatclose"; - this.checkbox_suppresssavedialogatclose.PropertyName = "SuppressSaveDialogAtClose"; + this.checkbox_suppresssavedialogatclose.PropertyName = nameof(EditorConfiguration.SuppressSaveDialogAtClose); this.checkbox_suppresssavedialogatclose.SectionName = "Editor"; this.checkbox_suppresssavedialogatclose.Size = new System.Drawing.Size(394, 24); this.checkbox_suppresssavedialogatclose.TabIndex = 6; @@ -1163,7 +1164,7 @@ namespace Greenshot.Forms { // this.textbox_counter.Location = new System.Drawing.Point(259, 282); this.textbox_counter.Name = "textbox_counter"; - this.textbox_counter.PropertyName = "OutputFileIncrementingNumber"; + this.textbox_counter.PropertyName = nameof(coreConfiguration.OutputFileIncrementingNumber); this.textbox_counter.Size = new System.Drawing.Size(141, 20); this.textbox_counter.TabIndex = 11; // @@ -1180,7 +1181,7 @@ namespace Greenshot.Forms { // this.textbox_footerpattern.Location = new System.Drawing.Point(138, 256); this.textbox_footerpattern.Name = "textbox_footerpattern"; - this.textbox_footerpattern.PropertyName = "OutputPrintFooterPattern"; + this.textbox_footerpattern.PropertyName = nameof(coreConfiguration.OutputPrintFooterPattern); this.textbox_footerpattern.Size = new System.Drawing.Size(262, 20); this.textbox_footerpattern.TabIndex = 10; // @@ -1189,7 +1190,7 @@ namespace Greenshot.Forms { this.checkbox_thumbnailpreview.LanguageKey = "expertsettings_thumbnailpreview"; this.checkbox_thumbnailpreview.Location = new System.Drawing.Point(10, 149); this.checkbox_thumbnailpreview.Name = "checkbox_thumbnailpreview"; - this.checkbox_thumbnailpreview.PropertyName = "ThumnailPreview"; + this.checkbox_thumbnailpreview.PropertyName = nameof(coreConfiguration.ThumnailPreview); this.checkbox_thumbnailpreview.Size = new System.Drawing.Size(394, 24); this.checkbox_thumbnailpreview.TabIndex = 5; this.checkbox_thumbnailpreview.UseVisualStyleBackColor = true; @@ -1199,7 +1200,7 @@ namespace Greenshot.Forms { this.checkbox_optimizeforrdp.LanguageKey = "expertsettings_optimizeforrdp"; this.checkbox_optimizeforrdp.Location = new System.Drawing.Point(10, 130); this.checkbox_optimizeforrdp.Name = "checkbox_optimizeforrdp"; - this.checkbox_optimizeforrdp.PropertyName = "OptimizeForRDP"; + this.checkbox_optimizeforrdp.PropertyName = nameof(coreConfiguration.OptimizeForRDP); this.checkbox_optimizeforrdp.Size = new System.Drawing.Size(394, 24); this.checkbox_optimizeforrdp.TabIndex = 4; this.checkbox_optimizeforrdp.UseVisualStyleBackColor = true; @@ -1209,7 +1210,7 @@ namespace Greenshot.Forms { this.checkbox_autoreducecolors.LanguageKey = "expertsettings_autoreducecolors"; this.checkbox_autoreducecolors.Location = new System.Drawing.Point(10, 111); this.checkbox_autoreducecolors.Name = "checkbox_autoreducecolors"; - this.checkbox_autoreducecolors.PropertyName = "OutputFileAutoReduceColors"; + this.checkbox_autoreducecolors.PropertyName = nameof(coreConfiguration.OutputFileAutoReduceColors); this.checkbox_autoreducecolors.Size = new System.Drawing.Size(408, 24); this.checkbox_autoreducecolors.TabIndex = 3; this.checkbox_autoreducecolors.UseVisualStyleBackColor = true; @@ -1257,7 +1258,7 @@ namespace Greenshot.Forms { // // SettingsForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 14F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(451, 431); this.Controls.Add(this.tabcontrol); diff --git a/src/Greenshot/Forms/SettingsForm.cs b/src/Greenshot/Forms/SettingsForm.cs index 7fecda8e2..272c2fde1 100644 --- a/src/Greenshot/Forms/SettingsForm.cs +++ b/src/Greenshot/Forms/SettingsForm.cs @@ -37,7 +37,6 @@ using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.UnmanagedHelpers; using Greenshot.Configuration; -using Greenshot.Destinations; using Greenshot.Helpers; using log4net; @@ -320,7 +319,7 @@ namespace Greenshot.Forms combobox_language.SelectedValue = Language.CurrentLanguage; } - // Delaying the SelectedIndexChanged events untill all is initiated + // Delaying the SelectedIndexChanged events until all is initiated combobox_language.SelectedIndexChanged += Combobox_languageSelectedIndexChanged; UpdateDestinationDescriptions(); UpdateClipboardFormatDescriptions(); @@ -829,22 +828,19 @@ namespace Greenshot.Forms { public int Compare(object x, object y) { - if (!(x is ListViewItem)) + if (x is not ListViewItem listViewItemX) { return 0; } - if (!(y is ListViewItem)) + if (y is not ListViewItem listViewItemY) { return 0; } - ListViewItem l1 = (ListViewItem) x; - ListViewItem l2 = (ListViewItem) y; + IDestination firstDestination = listViewItemX.Tag as IDestination; - IDestination firstDestination = l1.Tag as IDestination; - - if (!(l2.Tag is IDestination secondDestination)) + if (listViewItemY.Tag is not IDestination secondDestination) { return 1; } diff --git a/src/Greenshot/Helpers/CaptureHelper.cs b/src/Greenshot/Helpers/CaptureHelper.cs index 3d0d3385d..20496a839 100644 --- a/src/Greenshot/Helpers/CaptureHelper.cs +++ b/src/Greenshot/Helpers/CaptureHelper.cs @@ -389,6 +389,7 @@ namespace Greenshot.Helpers HandleCapture(); break; case CaptureMode.Clipboard: + // TODO: Fix getting image vs. drawablecontainer Image clipboardImage = ClipboardHelper.GetImage(); if (clipboardImage != null) { @@ -430,12 +431,13 @@ namespace Greenshot.Helpers if (!string.IsNullOrEmpty(filename)) { + // TODO: Fix that the Greenshot format needs a separate code path try { if (filename.ToLower().EndsWith("." + OutputFormat.greenshot)) { ISurface surface = new Surface(); - surface = ImageOutput.LoadGreenshotSurface(filename, surface); + surface = ImageIO.LoadGreenshotSurface(filename, surface); surface.CaptureDetails = _capture.CaptureDetails; DestinationHelper.GetDestination(EditorDestination.DESIGNATION).ExportCapture(true, surface, _capture.CaptureDetails); break; @@ -447,9 +449,10 @@ namespace Greenshot.Helpers MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename)); } + // TODO: Remove Image loading for here try { - fileImage = ImageHelper.LoadImage(filename); + fileImage = ImageIO.LoadImage(filename); } catch (Exception e) { diff --git a/src/Greenshot/Helpers/MailHelper.cs b/src/Greenshot/Helpers/MailHelper.cs index f3975a205..39aa3c150 100644 --- a/src/Greenshot/Helpers/MailHelper.cs +++ b/src/Greenshot/Helpers/MailHelper.cs @@ -82,7 +82,7 @@ namespace Greenshot.Helpers /// ICaptureDetails public static void SendImage(ISurface surface, ICaptureDetails captureDetails) { - string tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings()); + string tmpFile = ImageIO.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings()); if (tmpFile == null) return; diff --git a/src/Greenshot/Helpers/PrintHelper.cs b/src/Greenshot/Helpers/PrintHelper.cs index 9e6a43a7f..36bf8257b 100644 --- a/src/Greenshot/Helpers/PrintHelper.cs +++ b/src/Greenshot/Helpers/PrintHelper.cs @@ -187,7 +187,7 @@ namespace Greenshot.Helpers ApplyEffects(printOutputSettings); - bool disposeImage = ImageOutput.CreateImageFromSurface(_surface, printOutputSettings, out var image); + bool disposeImage = ImageIO.CreateImageFromSurface(_surface, printOutputSettings, out var image); try { ContentAlignment alignment = CoreConfig.OutputPrintCenter ? ContentAlignment.MiddleCenter : ContentAlignment.TopLeft;