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.
This commit is contained in:
Robin Krom 2022-02-20 13:21:32 +01:00 committed by GitHub
commit 60956771c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
90 changed files with 2793 additions and 1340 deletions

View file

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

View file

@ -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<IFileFormatHandler>();
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
/// </summary>
/// <param name="dataObject">IDataObject</param>
/// <returns>IEnumerable{MemoryStream}</returns>
private static IEnumerable<MemoryStream> IterateClipboardContent(IDataObject dataObject)
/// <returns>IEnumerable{(MemoryStream,string)}</returns>
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
/// </summary>
/// <param name="fileDescriptors">IEnumerable{FileDescriptor}</param>
/// <param name="dataObject">IDataObject</param>
/// <returns>IEnumerable{MemoryStream}</returns>
private static IEnumerable<MemoryStream> IterateFileDescriptors(IEnumerable<FileDescriptor> fileDescriptors, IDataObject dataObject)
/// <returns>IEnumerable{(MemoryStream stream, string filename)}</returns>
private static IEnumerable<(MemoryStream stream, string filename)> IterateFileDescriptors(IEnumerable<FileDescriptor> 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!
/// </summary>
/// <param name="dataObject"></param>
/// <returns>IEnumerable of Image</returns>
public static IEnumerable<Image> GetImages(IDataObject dataObject)
/// <returns>IEnumerable of Bitmap</returns>
public static IEnumerable<Bitmap> 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<IFileFormatHandler>();
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;
}
}
/// <summary>
/// Get all images (multiple if file names are available) from the dataObject
/// Returned images must be disposed by the calling code!
/// </summary>
/// <param name="dataObject"></param>
/// <returns>IEnumerable of IDrawableContainer</returns>
public static IEnumerable<IDrawableContainer> 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<IFileFormatHandler>();
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<IDrawableContainer> 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<IDrawableContainer> 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
/// </summary>
/// <param name="dataObject"></param>
/// <returns>Image or null</returns>
private static Image GetImage(IDataObject dataObject)
private static Bitmap GetImage(IDataObject dataObject)
{
Image returnImage = null;
if (dataObject != null)
{
IList<string> 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<string> 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
}
/// <summary>
/// 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
/// </summary>
/// <param name="dataObject"></param>
/// <returns>Image or null</returns>
private static IDrawableContainer GetDrawable(IDataObject dataObject)
{
if (dataObject == null) return null;
IDrawableContainer returnImage = null;
IList<string> 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;
}
/// <summary>
/// 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
/// </summary>
/// <param name="format">string with the format</param>
/// <param name="dataObject">IDataObject</param>
/// <returns>Image or null</returns>
private static Image GetImageForFormat(string format, IDataObject dataObject)
/// <returns>Bitmap or null</returns>
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<IFileFormatHandler>();
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;
}
/// <summary>
/// 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
/// </summary>
/// <param name="format">string with the format</param>
/// <param name="dataObject">IDataObject</param>
/// <returns>IDrawableContainer or null</returns>
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<BITMAPINFOHEADERV5>(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 <v5 format reader with biCompression {0}", infoHeader.biCompression);
int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
uint infoHeaderSize = infoHeader.biSize;
int fileSize = (int) (fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage);
var fileHeader = new BITMAPFILEHEADER
{
bfType = BITMAPFILEHEADER.BM,
bfSize = fileSize,
bfReserved1 = 0,
bfReserved2 = 0,
bfOffBits = (int) (fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4)
};
byte[] fileHeaderBytes = BinaryStructHelper.ToByteArray(fileHeader);
using var bitmapStream = new MemoryStream();
bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize);
bitmapStream.Write(dibBuffer, 0, dibBuffer.Length);
bitmapStream.Seek(0, SeekOrigin.Begin);
var image = ImageHelper.FromStream(bitmapStream);
if (image != null)
{
return image;
}
}
else
{
Log.Info("Using special DIBV5 / Format17 format reader");
// CF_DIBV5
IntPtr gcHandle = IntPtr.Zero;
try
{
GCHandle handle = GCHandle.Alloc(dibBuffer, GCHandleType.Pinned);
gcHandle = GCHandle.ToIntPtr(handle);
return
new Bitmap(infoHeader.biWidth, infoHeader.biHeight,
-(int) (infoHeader.biSizeImage / infoHeader.biHeight),
infoHeader.biBitCount == 32 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb,
new IntPtr(handle.AddrOfPinnedObject().ToInt32() + infoHeader.OffsetToPixels +
(infoHeader.biHeight - 1) * (int) (infoHeader.biSizeImage / infoHeader.biHeight))
);
}
catch (Exception ex)
{
Log.Error("Problem retrieving Format17 from clipboard.", ex);
}
finally
{
if (gcHandle == IntPtr.Zero)
{
GCHandle.FromIntPtr(gcHandle).Free();
}
}
return drawableContainer;
}
}
}
catch (Exception dibEx)
{
Log.Error("Problem retrieving DIB from clipboard.", dibEx);
}
}
}
else
{
Log.Info("Skipping special DIB format reader as it's disabled in the configuration.");
}
try
object clipboardObject = GetFromDataObject(dataObject, format);
var imageStream = clipboardObject as MemoryStream;
if (!IsValidStream(imageStream))
{
if (imageStream != null)
// TODO: add text based, like "HTML Format" support here...
// TODO: solve the issue that we do not have a factory for the ImageContainer
/*var image = clipboardObject as Image;
if (image != null)
{
imageStream.Seek(0, SeekOrigin.Begin);
var tmpImage = ImageHelper.FromStream(imageStream);
if (tmpImage != null)
return new ImageContainer(this)
{
Log.InfoFormat("Got image with clipboard format {0} from the clipboard.", format);
return tmpImage;
}
Image = image,
Left = x,
Top = y
};
}
}
catch (Exception streamImageEx)
{
Log.Error($"Problem retrieving {format} from clipboard.", streamImageEx);
return clipboardObject as Image;
*/
return null;
}
return null;
// From here, imageStream is a valid stream
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
return fileFormatHandlers.LoadDrawablesFromStream(imageStream, format).FirstOrDefault();
}
/// <summary>
@ -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<IFileFormatHandler>();
// 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<string> 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<string>();
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
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<string>();
}
/// <summary>

View file

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

View file

@ -31,6 +31,7 @@ namespace Greenshot.Base.Core.Enums
jpg,
png,
tiff,
jxr,
greenshot,
ico
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// This is the registry where all IFileFormatHandler are registered and can be used
/// </summary>
public static class FileFormatHandlerExtensions
{
/// <summary>
/// Make sure we handle the input extension always the same, by "normalizing" it
/// </summary>
/// <param name="extension">string</param>
/// <returns>string</returns>
public static string NormalizeExtension(string extension)
{
if (string.IsNullOrEmpty(extension))
{
return null;
}
extension = extension.ToLowerInvariant();
return !extension.StartsWith(".") ? $".{extension}" : extension;
}
/// <summary>
/// Return the extensions that the provided IFileFormatHandlers can accept for the specified action
/// </summary>
/// <param name="fileFormatHandlers">IEnumerable{IFileFormatHandler}</param>
/// <param name="fileFormatHandlerAction"></param>
/// <returns></returns>
public static IEnumerable<string> ExtensionsFor(this IEnumerable<IFileFormatHandler> fileFormatHandlers, FileFormatHandlerActions fileFormatHandlerAction)
{
return fileFormatHandlers.Where(ffh => ffh.SupportedExtensions.ContainsKey(fileFormatHandlerAction)).SelectMany(ffh => ffh.SupportedExtensions[fileFormatHandlerAction]).Distinct().OrderBy(e => e);
}
/// <summary>
/// Extension method to check if a certain IFileFormatHandler supports a certain action with a specific extension
/// </summary>
/// <param name="fileFormatHandler">IFileFormatHandler</param>
/// <param name="fileFormatHandlerAction">FileFormatHandlerActions</param>
/// <param name="extension">string</param>
/// <returns>bool</returns>
public static bool Supports(this IFileFormatHandler fileFormatHandler, FileFormatHandlerActions fileFormatHandlerAction, string extension)
{
extension = NormalizeExtension(extension);
return fileFormatHandler.SupportedExtensions.ContainsKey(fileFormatHandlerAction) && fileFormatHandler.SupportedExtensions[fileFormatHandlerAction].Contains(extension);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="fileFormatHandlers">IEnumerable{IFileFormatHandler}</param>
/// <param name="bitmap">Bitmap</param>
/// <param name="destination">Stream</param>
/// <param name="extension">string</param>
/// <param name="surface">ISurface</param>
/// <returns>bool</returns>
public static bool TrySaveToStream(this IEnumerable<IFileFormatHandler> 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;
}
/// <summary>
/// Try to load a drawable container from the stream
/// </summary>
/// <param name="fileFormatHandlers">IEnumerable{IFileFormatHandler}</param>
/// <param name="stream">Stream</param>
/// <param name="extension">string</param>
/// <param name="parentSurface">ISurface</param>
/// <returns>IEnumerable{IDrawableContainer}</returns>
public static IEnumerable<IDrawableContainer> LoadDrawablesFromStream(this IEnumerable<IFileFormatHandler> 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<IDrawableContainer>();
}
/// <summary>
/// Try to load a Bitmap from the stream
/// </summary>
/// <param name="fileFormatHandlers">IEnumerable{IFileFormatHandler}</param>
/// <param name="stream">Stream</param>
/// <param name="extension">string</param>
/// <param name="bitmap">Bitmap out</param>
/// <returns>bool true if it was successful</returns>
public static bool TryLoadFromStream(this IEnumerable<IFileFormatHandler> 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);
}
}
}

View file

@ -61,7 +61,7 @@ namespace Greenshot.Base.Core
float HorizontalResolution { get; }
/// <summary>
/// Unterlying image, or an on demand rendered version with different attributes as the original
/// Underlying image, or an on demand rendered version with different attributes as the original
/// </summary>
Image Image { get; }
}

View file

@ -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,
}
/// <summary>
/// Description of ImageHelper.
/// </summary>
@ -55,83 +51,6 @@ namespace Greenshot.Base.Core
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private const int ExifOrientationId = 0x0112;
static ImageHelper()
{
StreamConverters["greenshot"] = (stream, s) =>
{
var surface = SimpleServiceProvider.Current.GetInstance<Func<ISurface>>().Invoke();
return surface.GetImageForExport();
};
// Add a SVG converter
StreamConverters["svg"] = (stream, s) =>
{
stream.Position = 0;
try
{
return SvgImage.FromStream(stream).Image;
}
catch (Exception ex)
{
Log.Error("Can't load SVG", ex);
}
return null;
};
static Image DefaultConverter(Stream stream, string s)
{
stream.Position = 0;
using var tmpImage = Image.FromStream(stream, true, true);
Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
return Clone(tmpImage, PixelFormat.Format32bppArgb);
}
// Fallback
StreamConverters[string.Empty] = DefaultConverter;
StreamConverters["gif"] = DefaultConverter;
StreamConverters["bmp"] = DefaultConverter;
StreamConverters["jpg"] = DefaultConverter;
StreamConverters["jpeg"] = DefaultConverter;
StreamConverters["png"] = DefaultConverter;
StreamConverters["wmf"] = DefaultConverter;
StreamConverters["ico"] = (stream, extension) =>
{
// Icon logic, try to get the Vista icon, else the biggest possible
try
{
using Image tmpImage = ExtractVistaIcon(stream);
if (tmpImage != null)
{
return Clone(tmpImage, PixelFormat.Format32bppArgb);
}
}
catch (Exception vistaIconException)
{
Log.Warn("Can't read icon", vistaIconException);
}
try
{
// No vista icon, try normal icon
stream.Position = 0;
// We create a copy of the bitmap, so everything else can be disposed
using Icon tmpIcon = new Icon(stream, new Size(1024, 1024));
using Image tmpImage = tmpIcon.ToBitmap();
return Clone(tmpImage, PixelFormat.Format32bppArgb);
}
catch (Exception iconException)
{
Log.Warn("Can't read icon", iconException);
}
stream.Position = 0;
return DefaultConverter(stream, extension);
};
}
public static IDictionary<string, Func<Stream, string, Image>> StreamConverters { get; } = new Dictionary<string, Func<Stream, string, Image>>();
/// <summary>
/// Make sure the image is orientated correctly
@ -371,127 +290,6 @@ namespace Greenshot.Base.Core
return cropRectangle;
}
/// <summary>
/// Load an image from file
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx
/// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx
/// </summary>
/// <param name="iconStream">Stream with the icon information</param>
/// <returns>Bitmap with the Vista Icon (256x256)</returns>
private static Bitmap ExtractVistaIcon(Stream iconStream)
{
const int sizeIconDir = 6;
const int sizeIconDirEntry = 16;
Bitmap bmpPngExtracted = null;
try
{
byte[] srcBuf = new byte[iconStream.Length];
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;
}
/// <summary>
/// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx
/// </summary>
/// <param name="location">The file (EXE or DLL) to get the icon from</param>
/// <param name="index">Index of the icon</param>
/// <param name="takeLarge">true if the large icon is wanted</param>
/// <returns>Icon</returns>
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;
}
/// <summary>
/// Apply the effect to the bitmap
/// </summary>
@ -563,8 +361,7 @@ namespace Greenshot.Base.Core
/// <returns>Changed bitmap</returns>
public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange, bool[] edges)
{
Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution,
sourceImage.VerticalResolution);
Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
using (var path = new GraphicsPath())
{
Random random = new Random();
@ -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
/// <param name="height"></param>
/// <param name="format"></param>
/// <param name="backgroundColor">The color to fill with, or Color.Empty to take the default depending on the pixel format</param>
/// <param name="horizontalResolution"></param>
/// <param name="verticalResolution"></param>
/// <param name="horizontalResolution">float</param>
/// <param name="verticalResolution">float</param>
/// <returns>Bitmap</returns>
public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution)
public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution = 96f, float verticalResolution = 96f)
{
// Create a new "clean" image
Bitmap newImage = new Bitmap(width, height, format);
@ -1711,98 +1508,106 @@ namespace Greenshot.Base.Core
}
/// <summary>
/// Load a Greenshot surface from a stream
/// Rotate the image
/// </summary>
/// <param name="surfaceFileStream">Stream</param>
/// <param name="returnSurface"></param>
/// <returns>ISurface</returns>
public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface)
/// <param name="image">Input image</param>
/// <param name="rotationAngle">Angle in degrees</param>
/// <returns>Rotated image</returns>
public static Image Rotate(this Image image, float rotationAngle)
{
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;
}
/// <summary>
/// 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
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension"></param>
/// <returns>Image</returns>
public static Image FromStream(Stream stream, string extension = null)
/// <param name="pixelFormat">System.Drawing.Imaging.PixelFormat</param>
/// <returns>System.Windows.Media.PixelFormat</returns>
/// <exception cref="NotSupportedException"></exception>
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}.")
};
/// <summary>
/// Map a System.Windows.Media.PixelFormat to a System.Drawing.Imaging.PixelFormat
/// </summary>
/// <param name="pixelFormat">System.Windows.Media.PixelFormat</param>
/// <returns>System.Drawing.Imaging.PixelFormat</returns>
/// <exception cref="NotSupportedException"></exception>
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}.");
}
/// <summary>
/// Convert a Bitmap to a BitmapSource
/// </summary>
/// <param name="bitmap">Bitmap</param>
/// <returns>BitmapSource</returns>
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);
}
/// <summary>
/// Convert a BitmapSource to a Bitmap
/// </summary>
/// <param name="bitmapSource">BitmapSource</param>
/// <returns>Bitmap</returns>
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;
}
}
}

View file

@ -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
{
/// <summary>
/// Description of ImageOutput.
/// </summary>
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<CoreConfiguration>();
private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131;
private static readonly Cache<string, string> TmpFileCache = new Cache<string, string>(10 * 60 * 60, RemoveExpiredTmpFile);
@ -54,7 +54,7 @@ namespace Greenshot.Base.Core
/// <summary>
/// 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!
/// </summary>
/// <param name="id">ID</param>
/// <param name="text">Text</param>
@ -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<IFileFormatHandler>();
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<Image> images = new List<Image>
{
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
}
}
/// <summary>
/// Write the passed Image to a tmp-file and call an external process, than read the file back and write it to the targetStream
/// </summary>
/// <param name="imageToProcess">Image to pass to the external process</param>
/// <param name="targetStream">stream to write the processed image to</param>
/// <returns></returns>
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;
}
/// <summary>
/// Create an image from a surface with the settings from the output settings applied
/// </summary>
@ -429,20 +250,18 @@ namespace Greenshot.Base.Core
/// Add the greenshot property!
/// </summary>
/// <param name="imageToSave"></param>
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
}
/// <summary>
/// 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
/// </summary>
/// <param name="stream">Stream to write to</param>
/// <param name="images">List of images</param>
public static void WriteIcon(Stream stream, IList<Image> images)
/// <param name="filename"></param>
/// <returns></returns>
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<Size> imageSizes = new List<Size>();
IList<MemoryStream> encodedImages = new List<MemoryStream>();
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;
}
/// <summary>
/// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx
/// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx
/// </summary>
/// <param name="iconStream">Stream with the icon information</param>
/// <returns>Bitmap with the Vista Icon (256x256)</returns>
private static Bitmap ExtractVistaIcon(Stream iconStream)
{
const int sizeIconDir = 6;
const int sizeIconDirEntry = 16;
Bitmap bmpPngExtracted = null;
try
{
byte[] srcBuf = new byte[iconStream.Length];
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;
}
/// <summary>
/// See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx
/// </summary>
/// <param name="location">The file (EXE or DLL) to get the icon from</param>
/// <param name="index">Index of the icon</param>
/// <param name="takeLarge">true if the large icon is wanted</param>
/// <returns>Icon</returns>
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++)
/// <summary>
/// Create an image from a stream, if an extension is supplied more formats are supported.
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension"></param>
/// <returns>Image</returns>
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<IFileFormatHandler>();
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;
}
/// <summary>
/// Load a Greenshot surface from a stream
/// </summary>
/// <param name="surfaceFileStream">Stream</param>
/// <param name="returnSurface"></param>
/// <returns>ISurface</returns>
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;
}
}
}

View file

@ -1,77 +0,0 @@
using System.Drawing;
using System.Drawing.Imaging;
namespace Greenshot.Base.Core
{
/// <summary>
/// Wrap an image, make it resizeable
/// </summary>
public class ImageWrapper : IImage
{
// Underlying image, is used to generate a resized version of it when needed
private readonly Image _image;
private Image _imageClone;
public ImageWrapper(Image image)
{
// Make sure the orientation is set correctly so Greenshot can process the image correctly
ImageHelper.Orientate(image);
_image = image;
Width = _image.Width;
Height = _image.Height;
}
public void Dispose()
{
_image.Dispose();
_imageClone?.Dispose();
}
/// <summary>
/// Height of the image, can be set to change
/// </summary>
public int Height { get; set; }
/// <summary>
/// Width of the image, can be set to change.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Size of the image
/// </summary>
public Size Size => new Size(Width, Height);
/// <summary>
/// Pixelformat of the underlying image
/// </summary>
public PixelFormat PixelFormat => Image.PixelFormat;
public float HorizontalResolution => Image.HorizontalResolution;
public float VerticalResolution => Image.VerticalResolution;
public Image Image
{
get
{
if (_imageClone == null)
{
if (_image.Height == Height && _image.Width == Width)
{
return _image;
}
}
if (_imageClone?.Height == Height && _imageClone?.Width == Width)
{
return _imageClone;
}
// Calculate new image clone
_imageClone?.Dispose();
_imageClone = ImageHelper.ResizeImage(_image, false, Width, Height, null);
return _imageClone;
}
}
}
}

View file

@ -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
}
/// <summary>
/// Download the uri to Bitmap
/// Download the uri to build an IDrawableContainer
/// </summary>
/// <param name="url">Of an image</param>
/// <returns>Bitmap</returns>
public static Image DownloadImage(string url)
/// <returns>IDrawableContainer</returns>
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<IFileFormatHandler>();
var extensions = string.Join("|", fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream));
var imageUrlRegex = new Regex($@"(http|https)://.*(?<extension>{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;
}
/// <summary>
/// Download the uri to create a Bitmap
/// </summary>
/// <param name="url">Of an image</param>
/// <returns>Bitmap</returns>
public static Bitmap DownloadImage(string url)
{
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
var extensions = string.Join("|", fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadFromStream));
var imageUrlRegex = new Regex($@"(http|https)://.*(?<extension>{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);
}
/// <summary>
@ -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);
}
/// <summary>

View file

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

View file

@ -10,22 +10,19 @@ namespace Greenshot.Base.Core
/// </summary>
public class SimpleServiceProvider : IServiceLocator
{
private readonly Dictionary<Type, List<object>> _services = new Dictionary<Type, List<object>>();
private readonly Dictionary<Type, IList<object>> _services = new();
public static IServiceLocator Current { get; } = new SimpleServiceProvider();
public IEnumerable<TService> GetAllInstances<TService>()
public IReadOnlyList<TService> GetAllInstances<TService>()
{
var typeOfService = typeof(TService);
if (!_services.TryGetValue(typeOfService, out var results))
{
yield break;
return Array.Empty<TService>();
}
foreach (TService result in results)
{
yield return result;
}
return results.Cast<TService>().ToArray();
}
public TService GetInstance<TService>()

View file

@ -1,117 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Svg;
namespace Greenshot.Base.Core
{
/// <summary>
/// Create an image look like of the SVG
/// </summary>
public sealed class SvgImage : IImage
{
private readonly SvgDocument _svgDocument;
private Image _imageClone;
/// <summary>
/// Factory to create via a stream
/// </summary>
/// <param name="stream">Stream</param>
/// <returns>IImage</returns>
public static IImage FromStream(Stream stream)
{
return new SvgImage(stream);
}
/// <summary>
/// Default constructor
/// </summary>
/// <param name="stream"></param>
public SvgImage(Stream stream)
{
_svgDocument = SvgDocument.Open<SvgDocument>(stream);
Height = (int) _svgDocument.ViewBox.Height;
Width = (int) _svgDocument.ViewBox.Width;
}
/// <summary>
/// Height of the image, can be set to change
/// </summary>
public int Height { get; set; }
/// <summary>
/// Width of the image, can be set to change.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Size of the image
/// </summary>
public Size Size => new Size(Width, Height);
/// <summary>
/// Pixelformat of the underlying image
/// </summary>
public PixelFormat PixelFormat => Image.PixelFormat;
/// <summary>
/// Horizontal resolution of the underlying image
/// </summary>
public float HorizontalResolution => Image.HorizontalResolution;
/// <summary>
/// Vertical resolution of the underlying image
/// </summary>
public float VerticalResolution => Image.VerticalResolution;
/// <summary>
/// Underlying image, or an on demand rendered version with different attributes as the original
/// </summary>
public Image Image
{
get
{
if (_imageClone?.Height == Height && _imageClone?.Width == Width)
{
return _imageClone;
}
// Calculate new image clone
_imageClone?.Dispose();
_imageClone = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent, 96, 96);
_svgDocument.Draw((Bitmap) _imageClone);
return _imageClone;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_imageClone?.Dispose();
}
}
}

View file

@ -782,7 +782,9 @@ namespace Greenshot.Base.Core
/// </summary>
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));
}

View file

@ -30,7 +30,14 @@ namespace Greenshot.Base.Interfaces.Drawing
{
public interface IDrawableContainer : INotifyPropertyChanged, IDisposable
{
/// <summary>
/// The parent surface where this IDrawableContainer is on
/// </summary>
ISurface Parent { get; set; }
/// <summary>
/// Is this IDrawableContainer selected on the surface
/// </summary>
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; }
/// <summary>

View file

@ -0,0 +1,33 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: https://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace Greenshot.Base.Interfaces.Drawing
{
public interface IFieldAggregator
{
void UnbindElement(IDrawableContainer dc);
void BindElements(IDrawableContainerList dcs);
void BindElement(IDrawableContainer dc);
IField GetField(IFieldType fieldType);
event FieldChangedEventHandler FieldChanged;
}
}

View file

@ -0,0 +1,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 <https://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Base.Interfaces.Plugin;
namespace Greenshot.Base.Interfaces
{
/// <summary>
/// The possible actions a IFileFormatHandler might support
/// </summary>
public enum FileFormatHandlerActions
{
SaveToStream,
LoadFromStream,
LoadDrawableFromStream
}
/// <summary>
/// This interface is for code to implement the loading and saving of certain file formats
/// </summary>
public interface IFileFormatHandler
{
/// <summary>
/// Registry for all the extensions this IFileFormatHandler support
/// </summary>
IDictionary<FileFormatHandlerActions, IReadOnlyCollection<string>> SupportedExtensions { get; }
/// <summary>
/// Priority (from high int.MinValue, low int.MaxValue) of this IFileFormatHandler for the specified action and extension
/// This should be used to sort the possible IFileFormatHandler
/// </summary>
/// <param name="fileFormatHandlerAction">FileFormatHandlerActions</param>
/// <param name="extension">string</param>
/// <returns>int specifying the priority for the action and extension</returns>
public int PriorityFor(FileFormatHandlerActions fileFormatHandlerAction, string extension);
/// <summary>
/// Try to save the specified bitmap to the stream in the format belonging to the extension
/// </summary>
/// <param name="bitmap">Bitmap</param>
/// <param name="destination">Stream</param>
/// <param name="extension">extension</param>
/// <param name="surface">ISurface with the elements for those file types which can store a surface (.greenshot)</param>
/// <param name="surfaceOutputSettings">SurfaceOutputSettings</param>
/// <returns>bool true if it was successful</returns>
public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null);
/// <summary>
///
/// </summary>
/// <param name="stream"></param>
/// <param name="extension"></param>
/// <param name="bitmap"></param>
/// <returns>bool true if it was successful</returns>
public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap);
/// <summary>
/// Try to load a drawable container from the stream
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension">string</param>
/// <param name="parentSurface">ISurface</param>
/// <returns>IEnumerable{IDrawableContainer}</returns>
public IEnumerable<IDrawableContainer> LoadDrawablesFromStream(Stream stream, string extension, ISurface parentSurface = null);
}
}

View file

@ -32,7 +32,7 @@ namespace Greenshot.Base.Interfaces
/// </summary>
/// <typeparam name="TService">Service to find</typeparam>
/// <returns>IEnumerable{TService}</returns>
IEnumerable<TService> GetAllInstances<TService>();
IReadOnlyList<TService> GetAllInstances<TService>();
/// <summary>
/// Get the only instance of the specified service

View file

@ -21,6 +21,7 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;
using Greenshot.Base.Core;
@ -201,5 +202,14 @@ namespace Greenshot.Base.Interfaces
Rectangle ToImageCoordinates(Rectangle rc);
void MakeUndoable(IMemento memento, bool allowMerge);
IFieldAggregator FieldAggregator { get; }
/// <summary>
/// This reverses a change of the background image
/// </summary>
/// <param name="previous">Image</param>
/// <param name="matrix">Matrix</param>
void UndoBackgroundChange(Image previous, Matrix matrix);
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using Greenshot.Base.Core;
using Greenshot.Base.Core.Enums;
using Greenshot.Base.Effects;

View file

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

View file

@ -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
/// <param name="hWnd"></param>
/// <param name="nIndex"></param>
/// <returns></returns>
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);
}
/// <summary>

View file

@ -22,6 +22,7 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
@ -43,7 +44,7 @@ namespace Greenshot.Editor.Drawing
private static readonly AdjustableArrowCap ARROW_CAP = new AdjustableArrowCap(4, 6);
public ArrowContainer(Surface parent) : base(parent)
public ArrowContainer(ISurface parent) : base(parent)
{
}

View file

@ -21,6 +21,7 @@
using System.Drawing;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
@ -32,7 +33,7 @@ namespace Greenshot.Editor.Drawing
/// </summary>
public class CropContainer : DrawableContainer
{
public CropContainer(Surface parent) : base(parent)
public CropContainer(ISurface parent) : base(parent)
{
Init();
}

View file

@ -25,6 +25,7 @@ using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.Serialization;
using System.Windows.Forms;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using log4net;
@ -40,7 +41,7 @@ namespace Greenshot.Editor.Drawing
protected Cursor cursor;
public CursorContainer(Surface parent) : base(parent)
public CursorContainer(ISurface parent) : base(parent)
{
Init();
}
@ -56,7 +57,7 @@ namespace Greenshot.Editor.Drawing
CreateDefaultAdorners();
}
public CursorContainer(Surface parent, string filename) : this(parent)
public CursorContainer(ISurface parent, string filename) : this(parent)
{
Load(filename);
}

View file

@ -126,12 +126,17 @@ namespace Greenshot.Editor.Drawing
}
}
[NonSerialized] internal Surface _parent;
[NonSerialized] internal ISurface _parent;
public ISurface Parent
{
get => _parent;
set => SwitchParent((Surface) value);
set => SwitchParent(value);
}
protected Surface InternalParent
{
get => (Surface)_parent;
}
[NonSerialized] private TargetAdorner _targetAdorner;
@ -277,7 +282,7 @@ namespace Greenshot.Editor.Drawing
Height = Round(newBounds.Height);
}
public DrawableContainer(Surface parent)
public DrawableContainer(ISurface parent)
{
InitializeFields();
_parent = parent;
@ -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)
{

View file

@ -23,6 +23,7 @@ using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
@ -35,7 +36,7 @@ namespace Greenshot.Editor.Drawing
[Serializable()]
public class EllipseContainer : DrawableContainer
{
public EllipseContainer(Surface parent) : base(parent)
public EllipseContainer(ISurface parent) : base(parent)
{
Init();
}

View file

@ -42,7 +42,7 @@ namespace Greenshot.Editor.Drawing.Fields
/// If the property values of the selected elements differ, the value of the last bound element wins.
/// </summary>
[Serializable]
public sealed class FieldAggregator : AbstractFieldHolder
public sealed class FieldAggregator : AbstractFieldHolder, IFieldAggregator
{
private readonly IDrawableContainerList _boundContainers;
private bool _internalUpdateRunning;
@ -117,11 +117,10 @@ namespace Greenshot.Editor.Drawing.Fields
public void UnbindElement(IDrawableContainer dc)
{
if (_boundContainers.Contains(dc))
{
_boundContainers.Remove(dc);
UpdateFromBoundElements();
}
if (!_boundContainers.Contains(dc)) return;
_boundContainers.Remove(dc);
UpdateFromBoundElements();
}
public void Clear()

View file

@ -23,6 +23,7 @@ using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
@ -51,7 +52,7 @@ namespace Greenshot.Editor.Drawing
MAGNIFICATION
};
public FilterContainer(Surface parent) : base(parent)
public FilterContainer(ISurface parent) : base(parent)
{
Init();
}

View file

@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
@ -50,7 +51,7 @@ namespace Greenshot.Editor.Drawing
/// <summary>
/// Constructor
/// </summary>
public FreehandContainer(Surface parent) : base(parent)
public FreehandContainer(ISurface parent) : base(parent)
{
Width = parent.Image.Width;
Height = parent.Image.Height;

View file

@ -21,6 +21,7 @@
using System;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Drawing.Filters;
@ -33,7 +34,7 @@ namespace Greenshot.Editor.Drawing
[Serializable]
public class HighlightContainer : FilterContainer
{
public HighlightContainer(Surface parent) : base(parent)
public HighlightContainer(ISurface parent) : base(parent)
{
Init();
}

View file

@ -24,6 +24,7 @@ using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using log4net;
@ -39,7 +40,7 @@ namespace Greenshot.Editor.Drawing
protected Icon icon;
public IconContainer(Surface parent) : base(parent)
public IconContainer(ISurface parent) : base(parent)
{
Init();
}
@ -55,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)

View file

@ -26,6 +26,7 @@ using System.IO;
using System.Runtime.Serialization;
using Greenshot.Base.Core;
using Greenshot.Base.Effects;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using log4net;
@ -40,7 +41,7 @@ namespace Greenshot.Editor.Drawing
{
private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer));
private Image image;
private Image _image;
/// <summary>
/// This is the shadow version of the bitmap, rendered once to save performance
@ -54,12 +55,12 @@ namespace Greenshot.Editor.Drawing
/// </summary>
[NonSerialized] private Point _shadowOffset = new Point(-1, -1);
public ImageContainer(Surface parent, string filename) : this(parent)
public ImageContainer(ISurface parent, string filename) : this(parent)
{
Load(filename);
}
public ImageContainer(Surface parent) : base(parent)
public ImageContainer(ISurface parent) : base(parent)
{
FieldChanged += BitmapContainer_OnFieldChanged;
Init();
@ -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; }
}
/// <summary>
@ -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);
}
/// <summary>
@ -238,7 +240,7 @@ namespace Greenshot.Editor.Drawing
/// <param name="rm"></param>
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);
}
}

View file

@ -23,6 +23,7 @@ using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Adorners;
using Greenshot.Editor.Drawing.Fields;
@ -36,7 +37,7 @@ namespace Greenshot.Editor.Drawing
[Serializable()]
public class LineContainer : DrawableContainer
{
public LineContainer(Surface parent) : base(parent)
public LineContainer(ISurface parent) : base(parent)
{
Init();
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// This provides a resizable SVG container, redrawing the SVG in the size the container takes.
/// </summary>
[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);
}
}

View file

@ -21,6 +21,7 @@
using System;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Drawing.Filters;
@ -28,12 +29,12 @@ using Greenshot.Editor.Drawing.Filters;
namespace Greenshot.Editor.Drawing
{
/// <summary>
/// Description of ObfuscateContainer.
/// This is a FilterContainer for the obfuscator filters like blur and pixelate.
/// </summary>
[Serializable]
public class ObfuscateContainer : FilterContainer
{
public ObfuscateContainer(Surface parent) : base(parent)
public ObfuscateContainer(ISurface parent) : base(parent)
{
Init();
}

View file

@ -23,6 +23,7 @@ using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
@ -35,7 +36,7 @@ namespace Greenshot.Editor.Drawing
[Serializable]
public class RectangleContainer : DrawableContainer
{
public RectangleContainer(Surface parent) : base(parent)
public RectangleContainer(ISurface parent) : base(parent)
{
Init();
}

View file

@ -24,6 +24,7 @@ using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
@ -64,8 +65,7 @@ namespace Greenshot.Editor.Drawing
InitAdorner(Color.Green, _storedTargetGripperLocation);
}
public SpeechbubbleContainer(Surface parent)
: base(parent)
public SpeechbubbleContainer(ISurface parent) : base(parent)
{
}

View file

@ -24,6 +24,7 @@ using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
@ -41,9 +42,9 @@ namespace Greenshot.Editor.Drawing
private readonly bool _drawAsRectangle = false;
public StepLabelContainer(Surface parent) : base(parent)
public StepLabelContainer(ISurface parent) : base(parent)
{
parent.AddStepLabel(this);
InternalParent?.AddStepLabel(this);
InitContent();
Init();
}
@ -72,11 +73,10 @@ namespace Greenshot.Editor.Drawing
[OnSerializing]
private void SetValuesOnSerializing(StreamingContext context)
{
if (Parent != null)
{
Number = ((Surface) Parent).CountStepLabels(this);
_counterStart = ((Surface) Parent).CounterStart;
}
if (InternalParent == null) return;
Number = InternalParent.CountStepLabels(this);
_counterStart = InternalParent.CounterStart;
}
/// <summary>
@ -97,23 +97,23 @@ namespace Greenshot.Editor.Drawing
/// Add the StepLabel to the parent
/// </summary>
/// <param name="newParent"></param>
protected override void SwitchParent(Surface newParent)
protected override void SwitchParent(ISurface newParent)
{
if (newParent == Parent)
{
return;
}
((Surface) Parent)?.RemoveStepLabel(this);
base.SwitchParent(newParent);
if (newParent == null)
if (newParent is not Surface newParentSurface)
{
return;
}
InternalParent?.RemoveStepLabel(this);
base.SwitchParent(newParent);
// Make sure the counter start is restored (this unfortunately happens multiple times... -> hack)
newParent.CounterStart = _counterStart;
newParent.AddStepLabel(this);
newParentSurface.CounterStart = _counterStart;
newParentSurface.AddStepLabel(this);
}
public override Size DefaultSize => new Size(30, 30);

View file

@ -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<CoreConfiguration>();
// Property to identify the Surface ID
@ -207,7 +206,7 @@ namespace Greenshot.Editor.Drawing
[NonSerialized] private IDrawableContainer _undrawnElement;
/// <summary>
/// the cropcontainer, when cropping this is set, do not serialize
/// the crop container, when cropping this is set, do not serialize
/// </summary>
[NonSerialized] private IDrawableContainer _cropContainer;
@ -294,7 +293,7 @@ namespace Greenshot.Editor.Drawing
/// <summary>
/// all elements on the surface, needed with serialization
/// </summary>
private FieldAggregator _fieldAggregator;
private IFieldAggregator _fieldAggregator;
/// <summary>
/// the cursor container, needed with serialization as we need a direct acces to it.
@ -355,7 +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.
/// </summary>
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
}
}
/// <summary>
/// This will help to fit the container to the surface
/// </summary>
/// <param name="drawableContainer">IDrawableContainer</param>
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);
}
/// <summary>
/// Handle the drag/drop
/// </summary>
@ -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();
}
/// <summary>
@ -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
/// <param name="generateEvents">false to skip event generation</param>
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();
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using Greenshot.Base.Core;
using Greenshot.Base.Interfaces;
using Svg;
namespace Greenshot.Editor.Drawing
{
/// <summary>
/// This provides a resizable SVG container, redrawing the SVG in the size the container takes.
/// </summary>
[Serializable]
public class SvgContainer : VectorGraphicsContainer
{
private SvgDocument _svgDocument;
public SvgContainer(SvgDocument svgDocument, ISurface parent) : base(parent)
{
_svgDocument = svgDocument;
Size = new Size((int)svgDocument.Width, (int)svgDocument.Height);
}
protected override Image ComputeBitmap()
{
//var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.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);
}
}

View file

@ -28,6 +28,7 @@ using System.Drawing.Text;
using System.Runtime.Serialization;
using System.Windows.Forms;
using Greenshot.Base.Core;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
@ -83,7 +84,7 @@ namespace Greenshot.Editor.Drawing
OnPropertyChanged("Text");
}
public TextContainer(Surface parent) : base(parent)
public TextContainer(ISurface parent) : base(parent)
{
Init();
}
@ -154,17 +155,17 @@ namespace Greenshot.Editor.Drawing
FieldChanged += TextContainer_FieldChanged;
}
protected override void SwitchParent(Surface newParent)
protected override void SwitchParent(ISurface newParent)
{
if (_parent != null)
if (InternalParent != null)
{
_parent.SizeChanged -= Parent_SizeChanged;
InternalParent.SizeChanged -= Parent_SizeChanged;
}
base.SwitchParent(newParent);
if (_parent != null)
if (InternalParent != null)
{
_parent.SizeChanged += Parent_SizeChanged;
InternalParent.SizeChanged += Parent_SizeChanged;
}
}
@ -221,10 +222,10 @@ namespace Greenshot.Editor.Drawing
{
ShowTextBox();
}
else if (_parent != null && Selected && Status == EditStatus.IDLE && _textBox.Visible)
else if (InternalParent != null && Selected && Status == EditStatus.IDLE && _textBox.Visible)
{
// Fix (workaround) for BUG-1698
_parent.KeysLocked = true;
InternalParent.KeysLocked = true;
}
}
@ -295,10 +296,10 @@ namespace Greenshot.Editor.Drawing
private void ShowTextBox()
{
if (_parent != null)
if (InternalParent != null)
{
_parent.KeysLocked = true;
_parent.Controls.Add(_textBox);
InternalParent.KeysLocked = true;
InternalParent.Controls.Add(_textBox);
}
EnsureTextBoxContrast();
@ -332,15 +333,15 @@ namespace Greenshot.Editor.Drawing
private void HideTextBox()
{
_parent?.Focus();
InternalParent?.Focus();
_textBox?.Hide();
if (_parent == null)
if (InternalParent == null)
{
return;
}
_parent.KeysLocked = false;
_parent.Controls.Remove(_textBox);
InternalParent.KeysLocked = false;
InternalParent.Controls.Remove(_textBox);
}
/// <summary>

View file

@ -0,0 +1,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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// This is the base container for vector graphics, these ae graphics which can resize without loss of quality.
/// Examples for this are SVG, WMF or EMF, but also graphics based on fonts (e.g. Emoji)
/// </summary>
[Serializable]
public abstract class VectorGraphicsContainer : DrawableContainer
{
protected int RotationAngle;
/// <summary>
/// This is the cached version of the bitmap, pre-rendered to save performance
/// Do not serialized, it can be rebuild with some other information.
/// </summary>
[NonSerialized] private Image _cachedImage;
public VectorGraphicsContainer(ISurface parent) : base(parent)
{
Init();
}
protected override void OnDeserialized(StreamingContext streamingContext)
{
base.OnDeserialized(streamingContext);
Init();
}
private void Init()
{
CreateDefaultAdorners();
}
/// <summary>
/// The bulk of the clean-up code is implemented in Dispose(bool)
/// This Dispose is called from the Dispose and the Destructor.
/// When disposing==true all non-managed resources should be freed too!
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
ResetCachedBitmap();
}
base.Dispose(disposing);
}
/// <inheritdoc cref="IDrawableContainer"/>
public override void Transform(Matrix matrix)
{
RotationAngle += CalculateAngle(matrix);
RotationAngle %= 360;
ResetCachedBitmap();
base.Transform(matrix);
}
/// <inheritdoc cref="IDrawableContainer"/>
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;
}
}
}

View file

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

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <inheritdoc />
public IDictionary<FileFormatHandlerActions, IReadOnlyCollection<string>> SupportedExtensions { get; } = new Dictionary<FileFormatHandlerActions, IReadOnlyCollection<string>>();
/// <inheritdoc />
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);
/// <summary>
/// Default implementation taking the TryLoadFromStream image and placing it in an ImageContainer
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension">string</param>
/// <param name="parent">ISurface</param>
/// <returns>IEnumerable{IDrawableContainer}</returns>
public virtual IEnumerable<IDrawableContainer> 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;
}
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using Greenshot.Base.Core;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Plugin;
using log4net;
namespace Greenshot.Editor.FileFormatHandlers
{
/// <summary>
/// This is the default .NET bitmap file format handler
/// </summary>
public class DefaultFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler
{
private static readonly ILog Log = LogManager.GetLogger(typeof(DefaultFileFormatHandler));
private readonly IReadOnlyCollection<string> _ourExtensions = new[] { ".png", ".bmp", ".gif", ".jpg", ".jpeg", ".tiff", ".tif" };
public DefaultFileFormatHandler()
{
SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions;
SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions;
SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
}
}

View file

@ -20,29 +20,112 @@
*/
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
{
/// <summary>
/// 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
/// <a href="https://stackoverflow.com/questions/44177115/copying-from-and-to-clipboard-loses-image-transparency">here</a>
/// This handles creating a DIB (Device Independent Bitmap) on the clipboard
/// </summary>
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<string> _ourExtensions = new[] { ".dib", ".format17" };
public DibFileFormatHandler()
{
SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions;
SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions;
SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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<BITMAPINFOHEADERV5>(dibBuffer);
if (!infoHeader.IsDibV5)
{
Log.InfoFormat("Using special DIB <v5 format reader with biCompression {0}", infoHeader.biCompression);
int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
uint infoHeaderSize = infoHeader.biSize;
int fileSize = (int)(fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage);
var fileHeader = new BITMAPFILEHEADER
{
bfType = BITMAPFILEHEADER.BM,
bfSize = fileSize,
bfReserved1 = 0,
bfReserved2 = 0,
bfOffBits = (int)(fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4)
};
byte[] fileHeaderBytes = BinaryStructHelper.ToByteArray(fileHeader);
using var bitmapStream = new MemoryStream();
bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize);
bitmapStream.Write(dibBuffer, 0, dibBuffer.Length);
bitmapStream.Seek(0, SeekOrigin.Begin);
// TODO: Replace with a FileFormatHandler
bitmap = ImageIO.FromStream(bitmapStream) as Bitmap;
return true;
}
Log.Info("Using special DIBV5 / Format17 format reader");
// CF_DIBV5
IntPtr gcHandle = IntPtr.Zero;
try
{
GCHandle handle = GCHandle.Alloc(dibBuffer, GCHandleType.Pinned);
gcHandle = GCHandle.ToIntPtr(handle);
bitmap = new Bitmap(infoHeader.biWidth, infoHeader.biHeight,
-(int)(infoHeader.biSizeImage / infoHeader.biHeight),
infoHeader.biBitCount == 32 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb,
new IntPtr(handle.AddrOfPinnedObject().ToInt32() + infoHeader.OffsetToPixels +
(infoHeader.biHeight - 1) * (int)(infoHeader.biSizeImage / infoHeader.biHeight))
);
}
catch (Exception ex)
{
Log.Error("Problem retrieving Format17 from clipboard.", ex);
bitmap = null;
}
finally
{
if (gcHandle == IntPtr.Zero)
{
GCHandle.FromIntPtr(gcHandle).Free();
}
}
return true;
}
/// <summary>
/// Converts the Bitmap to a Device Independent Bitmap format of type BITFIELDS.
/// </summary>
/// <param name="sourceBitmap">Bitmap to convert to DIB</param>
/// <returns>byte{} with the image converted to DIB</returns>
public static byte[] ConvertToDib(this Bitmap sourceBitmap)
private static byte[] ConvertToDib(Bitmap sourceBitmap)
{
if (sourceBitmap == null) throw new ArgumentNullException(nameof(sourceBitmap));

View file

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

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// THis is the .ico format handler
/// </summary>
public class IconFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler
{
private static readonly ILog Log = LogManager.GetLogger(typeof(IconFileFormatHandler));
private readonly IReadOnlyCollection<string> _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<Image> images = new List<Image>
{
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<Size> imageSizes = new List<Size>();
IList<MemoryStream> encodedImages = new List<MemoryStream>();
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;
}
/// <summary>
/// Based on: https://www.codeproject.com/KB/cs/IconExtractor.aspx
/// And a hint from: https://www.codeproject.com/KB/cs/IconLib.aspx
/// </summary>
/// <param name="iconStream">Stream with the icon information</param>
/// <returns>Bitmap with the Vista Icon (256x256)</returns>
private static Bitmap ExtractVistaIcon(Stream iconStream)
{
const int sizeIconDir = 6;
const int sizeIconDirEntry = 16;
Bitmap bmpPngExtracted = null;
try
{
byte[] srcBuf = new byte[iconStream.Length];
// TODO: Check if there is a need to process the result
_ = iconStream.Read(srcBuf, 0, (int)iconStream.Length);
int iCount = BitConverter.ToInt16(srcBuf, 4);
for (int iIndex = 0; iIndex < iCount; iIndex++)
{
int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex];
int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1];
if (iWidth != 0 || iHeight != 0) continue;
int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8);
int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12);
using MemoryStream destStream = new MemoryStream();
destStream.Write(srcBuf, iImageOffset, iImageSize);
destStream.Seek(0, SeekOrigin.Begin);
bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
break;
}
}
catch
{
return null;
}
return bmpPngExtracted;
}
}
}

View file

@ -0,0 +1,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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// This handles the Windows metafile files
/// </summary>
public class MetaFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler
{
private readonly IReadOnlyCollection<string> _ourExtensions = new[] { ".wmf", ".emf" };
public MetaFileFormatHandler()
{
SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions;
SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions;
}
/// <inheritdoc />
public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null)
{
return false;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
public override IEnumerable<IDrawableContainer> LoadDrawablesFromStream(Stream stream, string extension, ISurface surface = null)
{
if (Image.FromStream(stream, true, true) is Metafile metaFile)
{
yield return new MetafileContainer(metaFile, surface);
}
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// This handled the loading of SVG images to the editor
/// </summary>
public class SvgFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler
{
private static readonly ILog Log = LogManager.GetLogger(typeof(SvgFileFormatHandler));
private readonly IReadOnlyCollection<string> _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<SvgDocument>(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<IDrawableContainer> LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null)
{
SvgDocument svgDocument = null;
try
{
svgDocument = SvgDocument.Open<SvgDocument>(stream);
}
catch (Exception ex)
{
Log.Error("Can't load SVG", ex);
}
if (svgDocument != null)
{
yield return new SvgContainer(svgDocument, parent);
}
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// This is the System.Windows.Media.Imaging (WPF) file format handler, which uses WIC
/// </summary>
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<string> LoadFromStreamExtensions { get; } = new []{ ".jxr", ".dds", ".hdp", ".wdp", ".wmp"};
private IReadOnlyCollection<string> 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;
}
/// <summary>
/// Detect all the formats WIC supports
/// </summary>
/// <returns>IEnumerable{string}</returns>
private IEnumerable<string> 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";
}
/// <inheritdoc />
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;
}
}
/// <inheritdoc />
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;
}
}
}

View file

@ -56,13 +56,13 @@ namespace Greenshot.Editor.Forms
private static readonly ILog Log = LogManager.GetLogger(typeof(ImageEditorForm));
private static readonly EditorConfiguration EditorConfiguration = IniConfig.GetIniSection<EditorConfiguration>();
private static readonly List<string> IgnoreDestinations = new List<string>
private static readonly List<string> IgnoreDestinations = new()
{
nameof(WellKnownDestinations.Picker),
EditorDestination.DESIGNATION
};
private static readonly List<IImageEditor> EditorList = new List<IImageEditor>();
private static readonly List<IImageEditor> EditorList = new();
private Surface _surface;
private GreenshotToolStripButton[] _toolbarButtons;
@ -78,7 +78,7 @@ namespace Greenshot.Editor.Forms
// whether part of the editor controls are disabled depending on selected item(s)
private bool _controlsDisabledDueToConfirmable;
// Used for tracking the mouse scrollwheel changes
// Used for tracking the mouse scroll wheel changes
private DateTime _zoomStartTime = DateTime.Now;
/// <summary>
@ -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)

View file

@ -1,4 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -20,9 +20,8 @@
*/
using System;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing;
namespace Greenshot.Editor.Memento
{
/// <summary>
@ -31,9 +30,9 @@ namespace Greenshot.Editor.Memento
public class AddElementMemento : IMemento
{
private IDrawableContainer _drawableContainer;
private Surface _surface;
private ISurface _surface;
public AddElementMemento(Surface surface, IDrawableContainer drawableContainer)
public AddElementMemento(ISurface surface, IDrawableContainer drawableContainer)
{
_surface = surface;
_drawableContainer = drawableContainer;

View file

@ -19,8 +19,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing;
namespace Greenshot.Editor.Memento
{
@ -30,9 +30,9 @@ namespace Greenshot.Editor.Memento
public class AddElementsMemento : IMemento
{
private IDrawableContainerList _containerList;
private Surface _surface;
private ISurface _surface;
public AddElementsMemento(Surface surface, IDrawableContainerList containerList)
public AddElementsMemento(ISurface surface, IDrawableContainerList containerList)
{
_surface = surface;
_containerList = containerList;

View file

@ -20,8 +20,8 @@
*/
using System;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing;
namespace Greenshot.Editor.Memento
{
@ -31,9 +31,9 @@ namespace Greenshot.Editor.Memento
public class DeleteElementMemento : IMemento
{
private IDrawableContainer _drawableContainer;
private readonly Surface _surface;
private readonly ISurface _surface;
public DeleteElementMemento(Surface surface, IDrawableContainer drawableContainer)
public DeleteElementMemento(ISurface surface, IDrawableContainer drawableContainer)
{
_surface = surface;
_drawableContainer = drawableContainer;

View file

@ -19,8 +19,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing;
namespace Greenshot.Editor.Memento
{
@ -30,9 +30,9 @@ namespace Greenshot.Editor.Memento
public class DeleteElementsMemento : IMemento
{
private IDrawableContainerList _containerList;
private Surface _surface;
private ISurface _surface;
public DeleteElementsMemento(Surface surface, IDrawableContainerList containerList)
public DeleteElementsMemento(ISurface surface, IDrawableContainerList containerList)
{
_surface = surface;
_containerList = containerList;

View file

@ -21,8 +21,8 @@
using System.Drawing;
using System.Drawing.Drawing2D;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing;
namespace Greenshot.Editor.Memento
{
@ -32,10 +32,10 @@ namespace Greenshot.Editor.Memento
public class SurfaceBackgroundChangeMemento : IMemento
{
private Image _image;
private Surface _surface;
private ISurface _surface;
private Matrix _matrix;
public SurfaceBackgroundChangeMemento(Surface surface, Matrix matrix)
public SurfaceBackgroundChangeMemento(ISurface surface, Matrix matrix)
{
_surface = surface;
_image = surface.Image;

View file

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

View file

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

View file

@ -21,7 +21,6 @@
using System;
using System.Windows.Forms;
using Greenshot.Base.Core;
using Greenshot.Base.Core.Enums;
using Greenshot.Base.IniFile;
using Greenshot.Plugin.Dropbox.Forms;
@ -29,10 +28,10 @@ using Greenshot.Plugin.Dropbox.Forms;
namespace Greenshot.Plugin.Dropbox
{
/// <summary>
/// Description of ImgurConfiguration.
/// The configuration for Dropbox
/// </summary>
[IniSection("Dropbox", Description = "Greenshot Dropbox Plugin configuration")]
public class DropboxPluginConfiguration : IniSection
public class DropboxConfiguration : IniSection
{
[IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")]
public OutputFormat UploadFormat { get; set; }

View file

@ -29,7 +29,7 @@ namespace Greenshot.Plugin.Dropbox
{
internal class DropboxDestination : AbstractDestination
{
private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection<DropboxPluginConfiguration>();
private static readonly DropboxConfiguration DropboxConfig = IniConfig.GetIniSection<DropboxConfiguration>();
private readonly DropboxPlugin _plugin;

View file

@ -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<DropboxPluginConfiguration>();
_config = IniConfig.GetIniSection<DropboxConfiguration>();
_resources = new ComponentResourceManager(typeof(DropboxPlugin));
SimpleServiceProvider.Current.AddService<IDestination>(new DropboxDestination(this));
_itemPlugInConfig = new ToolStripMenuItem

View file

@ -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<DropboxPluginConfiguration>();
private static readonly DropboxConfiguration DropboxConfig = IniConfig.GetIniSection<DropboxConfiguration>();
private DropboxUtils()
{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--<system.windows.forms jitDebugging="true" />-->
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>

View file

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

View file

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

View file

@ -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();
@ -923,10 +927,10 @@ 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<IFileFormatHandler>();
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

View file

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

View file

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

View file

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

View file

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

View file

@ -82,7 +82,7 @@ namespace Greenshot.Helpers
/// <param name="captureDetails">ICaptureDetails</param>
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;

View file

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