Merge branch 'release/1.3' into fix/379-preferences-window

# Conflicts:
#	src/Greenshot/Forms/SettingsForm.Designer.cs
This commit is contained in:
Christian Schulz 2022-02-20 17:30:37 +01:00
commit 1585d1c2ea
90 changed files with 2795 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);
@ -204,7 +204,7 @@ namespace Greenshot.Editor.Drawing
EllipseContainer.DrawEllipse(rect, graphics, rm, 0, Color.Transparent, fillColor, false);
}
float fontSize = Math.Min(Width, Height) / 1.4f;
float fontSize = Math.Min(Math.Abs(Width), Math.Abs(Height)) / 1.4f;
using FontFamily fam = new FontFamily(FontFamily.GenericSansSerif.Name);
using Font font = new Font(fam, fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
TextContainer.DrawText(graphics, rect, 0, lineColor, false, _stringFormat, text, font);

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 {
@ -248,7 +250,7 @@ namespace Greenshot.Forms {
this.textbox_screenshotname.Location = new System.Drawing.Point(205, 42);
this.textbox_screenshotname.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(205, 20);
this.textbox_screenshotname.TabIndex = 3;
this.textbox_screenshotname.TextChanged += new System.EventHandler(this.FilenamePatternChanged);
@ -281,7 +283,7 @@ namespace Greenshot.Forms {
this.combobox_primaryimageformat.Location = new System.Drawing.Point(205, 66);
this.combobox_primaryimageformat.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(235, 21);
this.combobox_primaryimageformat.TabIndex = 5;
//
@ -333,7 +335,7 @@ namespace Greenshot.Forms {
this.checkbox_copypathtoclipboard.Location = new System.Drawing.Point(8, 93);
this.checkbox_copypathtoclipboard.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(358, 20);
this.checkbox_copypathtoclipboard.TabIndex = 6;
this.checkbox_copypathtoclipboard.Text = "Copy file path to clipboard every time an image is saved";
@ -345,7 +347,7 @@ namespace Greenshot.Forms {
this.checkbox_zoomer.Location = new System.Drawing.Point(4, 82);
this.checkbox_zoomer.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(362, 20);
this.checkbox_zoomer.TabIndex = 4;
this.checkbox_zoomer.Text = "Show magnifier";
@ -440,7 +442,7 @@ namespace Greenshot.Forms {
this.checkbox_reducecolors.Location = new System.Drawing.Point(8, 95);
this.checkbox_reducecolors.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(358, 20);
this.checkbox_reducecolors.TabIndex = 10;
this.checkbox_reducecolors.Text = "Reduce the amount of colors to a maximum of 256";
@ -452,7 +454,7 @@ namespace Greenshot.Forms {
this.checkbox_alwaysshowqualitydialog.Location = new System.Drawing.Point(8, 69);
this.checkbox_alwaysshowqualitydialog.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(358, 20);
this.checkbox_alwaysshowqualitydialog.TabIndex = 9;
this.checkbox_alwaysshowqualitydialog.Text = "Show quality dialog every time an image is saved";
@ -620,7 +622,7 @@ namespace Greenshot.Forms {
this.checkbox_usedefaultproxy.Location = new System.Drawing.Point(7, 19);
this.checkbox_usedefaultproxy.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(359, 20);
this.checkbox_usedefaultproxy.TabIndex = 7;
this.checkbox_usedefaultproxy.Text = "Use default system proxy";
@ -665,7 +667,7 @@ namespace Greenshot.Forms {
this.lastregion_hotkeyControl.Location = new System.Drawing.Point(280, 81);
this.lastregion_hotkeyControl.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(158, 20);
this.lastregion_hotkeyControl.TabIndex = 5;
//
@ -686,7 +688,7 @@ namespace Greenshot.Forms {
this.ie_hotkeyControl.Location = new System.Drawing.Point(280, 104);
this.ie_hotkeyControl.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(158, 20);
this.ie_hotkeyControl.TabIndex = 6;
//
@ -727,7 +729,7 @@ namespace Greenshot.Forms {
this.region_hotkeyControl.Location = new System.Drawing.Point(280, 59);
this.region_hotkeyControl.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(158, 20);
this.region_hotkeyControl.TabIndex = 4;
//
@ -738,7 +740,7 @@ namespace Greenshot.Forms {
this.window_hotkeyControl.Location = new System.Drawing.Point(280, 36);
this.window_hotkeyControl.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(158, 20);
this.window_hotkeyControl.TabIndex = 3;
//
@ -749,7 +751,7 @@ namespace Greenshot.Forms {
this.fullscreen_hotkeyControl.Location = new System.Drawing.Point(280, 14);
this.fullscreen_hotkeyControl.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(158, 20);
this.fullscreen_hotkeyControl.TabIndex = 2;
//
@ -787,7 +789,7 @@ namespace Greenshot.Forms {
this.checkbox_editor_match_capture_size.Location = new System.Drawing.Point(4, 17);
this.checkbox_editor_match_capture_size.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(362, 20);
this.checkbox_editor_match_capture_size.TabIndex = 11;
@ -813,7 +815,7 @@ namespace Greenshot.Forms {
this.checkbox_ie_capture.Location = new System.Drawing.Point(4, 16);
this.checkbox_ie_capture.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(362, 20);
this.checkbox_ie_capture.TabIndex = 10;
this.checkbox_ie_capture.Text = "Internet Explorer capture";
@ -864,7 +866,7 @@ namespace Greenshot.Forms {
this.radiobuttonInteractiveCapture.Location = new System.Drawing.Point(4, 17);
this.radiobuttonInteractiveCapture.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.radiobuttonInteractiveCapture.Name = "radiobuttonInteractiveCapture";
this.radiobuttonInteractiveCapture.PropertyName = "CaptureWindowsInteractive";
this.radiobuttonInteractiveCapture.PropertyName = nameof(coreConfiguration.CaptureWindowsInteractive);
this.radiobuttonInteractiveCapture.Size = new System.Drawing.Size(362, 20);
this.radiobuttonInteractiveCapture.TabIndex = 6;
this.radiobuttonInteractiveCapture.TabStop = true;
@ -910,7 +912,7 @@ namespace Greenshot.Forms {
this.checkbox_notifications.Location = new System.Drawing.Point(4, 60);
this.checkbox_notifications.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(362, 20);
this.checkbox_notifications.TabIndex = 3;
this.checkbox_notifications.Text = "Show notifications";
@ -922,7 +924,7 @@ namespace Greenshot.Forms {
this.checkbox_playsound.Location = new System.Drawing.Point(4, 38);
this.checkbox_playsound.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(362, 20);
this.checkbox_playsound.TabIndex = 2;
this.checkbox_playsound.Text = "Play camera sound";
@ -934,7 +936,7 @@ namespace Greenshot.Forms {
this.checkbox_capture_mousepointer.Location = new System.Drawing.Point(4, 16);
this.checkbox_capture_mousepointer.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(362, 20);
this.checkbox_capture_mousepointer.TabIndex = 1;
this.checkbox_capture_mousepointer.Text = "Capture mousepointer";
@ -1035,7 +1037,7 @@ namespace Greenshot.Forms {
this.checkboxPrintInverted.Location = new System.Drawing.Point(10, 94);
this.checkboxPrintInverted.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.checkboxPrintInverted.Name = "checkboxPrintInverted";
this.checkboxPrintInverted.PropertyName = "OutputPrintInverted";
this.checkboxPrintInverted.PropertyName = nameof(coreConfiguration.OutputPrintInverted);
this.checkboxPrintInverted.Size = new System.Drawing.Size(355, 20);
this.checkboxPrintInverted.TabIndex = 14;
this.checkboxPrintInverted.Text = "Print with inverted colors";
@ -1050,7 +1052,8 @@ namespace Greenshot.Forms {
this.radioBtnColorPrint.Location = new System.Drawing.Point(10, 16);
this.radioBtnColorPrint.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.radioBtnColorPrint.Name = "radioBtnColorPrint";
this.radioBtnColorPrint.PropertyName = "OutputPrintColor";
//TODO missing property in coreConfiguration
//this.radioBtnColorPrint.PropertyName = nameof(coreConfiguration.OutputPrintColor);
this.radioBtnColorPrint.Size = new System.Drawing.Size(355, 20);
this.radioBtnColorPrint.TabIndex = 11;
this.radioBtnColorPrint.Text = "Full color print";
@ -1065,7 +1068,7 @@ namespace Greenshot.Forms {
this.radioBtnGrayScale.Location = new System.Drawing.Point(10, 42);
this.radioBtnGrayScale.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.radioBtnGrayScale.Name = "radioBtnGrayScale";
this.radioBtnGrayScale.PropertyName = "OutputPrintGrayscale";
this.radioBtnGrayScale.PropertyName = nameof(coreConfiguration.OutputPrintGrayscale);
this.radioBtnGrayScale.Size = new System.Drawing.Size(355, 20);
this.radioBtnGrayScale.TabIndex = 12;
this.radioBtnGrayScale.Text = "Force grayscale printing";
@ -1080,7 +1083,7 @@ namespace Greenshot.Forms {
this.radioBtnMonochrome.Location = new System.Drawing.Point(10, 68);
this.radioBtnMonochrome.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.radioBtnMonochrome.Name = "radioBtnMonochrome";
this.radioBtnMonochrome.PropertyName = "OutputPrintMonochrome";
this.radioBtnMonochrome.PropertyName = nameof(coreConfiguration.OutputPrintMonochrome);
this.radioBtnMonochrome.Size = new System.Drawing.Size(355, 20);
this.radioBtnMonochrome.TabIndex = 13;
this.radioBtnMonochrome.Text = "Force black/white printing";
@ -1112,7 +1115,7 @@ namespace Greenshot.Forms {
this.checkboxDateTime.Location = new System.Drawing.Point(10, 116);
this.checkboxDateTime.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.checkboxDateTime.Name = "checkboxDateTime";
this.checkboxDateTime.PropertyName = "OutputPrintFooter";
this.checkboxDateTime.PropertyName = nameof(coreConfiguration.OutputPrintFooter);
this.checkboxDateTime.Size = new System.Drawing.Size(355, 20);
this.checkboxDateTime.TabIndex = 6;
this.checkboxDateTime.Text = "Print date / time at bottom of page";
@ -1127,7 +1130,7 @@ namespace Greenshot.Forms {
this.checkboxAllowShrink.Location = new System.Drawing.Point(10, 20);
this.checkboxAllowShrink.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.checkboxAllowShrink.Name = "checkboxAllowShrink";
this.checkboxAllowShrink.PropertyName = "OutputPrintAllowShrink";
this.checkboxAllowShrink.PropertyName = nameof(coreConfiguration.OutputPrintAllowShrink);
this.checkboxAllowShrink.Size = new System.Drawing.Size(355, 20);
this.checkboxAllowShrink.TabIndex = 2;
this.checkboxAllowShrink.Text = "Shrink printout to fit paper size";
@ -1142,7 +1145,7 @@ namespace Greenshot.Forms {
this.checkboxAllowEnlarge.Location = new System.Drawing.Point(10, 44);
this.checkboxAllowEnlarge.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.checkboxAllowEnlarge.Name = "checkboxAllowEnlarge";
this.checkboxAllowEnlarge.PropertyName = "OutputPrintAllowEnlarge";
this.checkboxAllowEnlarge.PropertyName = nameof(coreConfiguration.OutputPrintAllowEnlarge);
this.checkboxAllowEnlarge.Size = new System.Drawing.Size(355, 20);
this.checkboxAllowEnlarge.TabIndex = 3;
this.checkboxAllowEnlarge.Text = "Enlarge printout to fit paper size";
@ -1157,7 +1160,7 @@ namespace Greenshot.Forms {
this.checkboxAllowRotate.Location = new System.Drawing.Point(10, 68);
this.checkboxAllowRotate.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.checkboxAllowRotate.Name = "checkboxAllowRotate";
this.checkboxAllowRotate.PropertyName = "OutputPrintAllowRotate";
this.checkboxAllowRotate.PropertyName = nameof(coreConfiguration.OutputPrintAllowRotate);
this.checkboxAllowRotate.Size = new System.Drawing.Size(355, 20);
this.checkboxAllowRotate.TabIndex = 4;
this.checkboxAllowRotate.Text = "Rotate printout to page orientation";
@ -1172,7 +1175,7 @@ namespace Greenshot.Forms {
this.checkboxAllowCenter.Location = new System.Drawing.Point(10, 92);
this.checkboxAllowCenter.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
this.checkboxAllowCenter.Name = "checkboxAllowCenter";
this.checkboxAllowCenter.PropertyName = "OutputPrintCenter";
this.checkboxAllowCenter.PropertyName = nameof(coreConfiguration.OutputPrintCenter);
this.checkboxAllowCenter.Size = new System.Drawing.Size(355, 20);
this.checkboxAllowCenter.TabIndex = 5;
this.checkboxAllowCenter.Text = "Center printout on page";
@ -1185,7 +1188,7 @@ namespace Greenshot.Forms {
this.checkbox_alwaysshowprintoptionsdialog.Location = new System.Drawing.Point(14, 282);
this.checkbox_alwaysshowprintoptionsdialog.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(355, 20);
this.checkbox_alwaysshowprintoptionsdialog.TabIndex = 15;
this.checkbox_alwaysshowprintoptionsdialog.Text = "Show print options dialog every time an image is printed";
@ -1293,7 +1296,7 @@ namespace Greenshot.Forms {
this.checkbox_reuseeditor.Location = new System.Drawing.Point(8, 240);
this.checkbox_reuseeditor.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(410, 20);
this.checkbox_reuseeditor.TabIndex = 9;
@ -1306,7 +1309,7 @@ namespace Greenshot.Forms {
this.checkbox_minimizememoryfootprint.Location = new System.Drawing.Point(8, 216);
this.checkbox_minimizememoryfootprint.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(410, 20);
this.checkbox_minimizememoryfootprint.TabIndex = 8;
this.checkbox_minimizememoryfootprint.Text = "Minimize memory footprint, but with a performance penalty (not advised).";
@ -1318,7 +1321,7 @@ namespace Greenshot.Forms {
this.checkbox_checkunstableupdates.Location = new System.Drawing.Point(8, 192);
this.checkbox_checkunstableupdates.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(410, 20);
this.checkbox_checkunstableupdates.TabIndex = 7;
this.checkbox_checkunstableupdates.Text = "Check for unstable updates";
@ -1330,7 +1333,7 @@ namespace Greenshot.Forms {
this.checkbox_suppresssavedialogatclose.Location = new System.Drawing.Point(8, 168);
this.checkbox_suppresssavedialogatclose.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(410, 20);
this.checkbox_suppresssavedialogatclose.TabIndex = 6;
@ -1352,7 +1355,7 @@ namespace Greenshot.Forms {
this.textbox_counter.Location = new System.Drawing.Point(304, 288);
this.textbox_counter.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(134, 20);
this.textbox_counter.TabIndex = 11;
//
@ -1371,7 +1374,7 @@ namespace Greenshot.Forms {
this.textbox_footerpattern.Location = new System.Drawing.Point(155, 264);
this.textbox_footerpattern.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(283, 20);
this.textbox_footerpattern.TabIndex = 10;
//
@ -1381,7 +1384,7 @@ namespace Greenshot.Forms {
this.checkbox_thumbnailpreview.Location = new System.Drawing.Point(8, 144);
this.checkbox_thumbnailpreview.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(410, 20);
this.checkbox_thumbnailpreview.TabIndex = 5;
this.checkbox_thumbnailpreview.Text = "Show window thumbnails in context menu (for Vista and windows 7)";
@ -1393,7 +1396,7 @@ namespace Greenshot.Forms {
this.checkbox_optimizeforrdp.Location = new System.Drawing.Point(8, 120);
this.checkbox_optimizeforrdp.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(410, 20);
this.checkbox_optimizeforrdp.TabIndex = 4;
this.checkbox_optimizeforrdp.Text = "Make some optimizations for usage with remote desktop";
@ -1405,7 +1408,7 @@ namespace Greenshot.Forms {
this.checkbox_autoreducecolors.Location = new System.Drawing.Point(8, 102);
this.checkbox_autoreducecolors.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
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(410, 20);
this.checkbox_autoreducecolors.TabIndex = 3;
this.checkbox_autoreducecolors.Text = "Create an 8-bit image if the colors are less than 256 while having a > 8 bits ima" +

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();
@ -832,22 +831,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;