Made the supported image formats extendable, and supplied a SVG implementation right away with the jira addon.

This commit is contained in:
Robin 2016-09-05 22:48:23 +02:00
commit fc192827f1
21 changed files with 1354 additions and 676 deletions

View file

@ -123,7 +123,7 @@ namespace GreenshotPlugin.Core {
public void ProcessExport(ExportInformation exportInformation, ISurface surface) {
if (exportInformation != null && exportInformation.ExportMade) {
if (!string.IsNullOrEmpty(exportInformation.Uri)) {
surface.UploadURL = exportInformation.Uri;
surface.UploadUrl = exportInformation.Uri;
surface.SendMessageEvent(this, SurfaceMessageTyp.UploadedUri, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
} else if (!string.IsNullOrEmpty(exportInformation.Filepath)) {
surface.LastSaveFullPath = exportInformation.Filepath;

View file

@ -39,9 +39,9 @@ namespace GreenshotPlugin.Core {
/// Description of ClipboardHelper.
/// </summary>
public static class ClipboardHelper {
private static readonly ILog LOG = LogManager.GetLogger(typeof(ClipboardHelper));
private static readonly object clipboardLockObject = new object();
private static readonly CoreConfiguration config = IniConfig.GetIniSection<CoreConfiguration>();
private static readonly ILog Log = LogManager.GetLogger(typeof(ClipboardHelper));
private static readonly object ClipboardLockObject = new object();
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private static readonly string FORMAT_FILECONTENTS = "FileContents";
private static readonly string FORMAT_PNG = "PNG";
private static readonly string FORMAT_PNG_OFFICEART = "PNG+Office Art";
@ -52,12 +52,11 @@ namespace GreenshotPlugin.Core {
private static readonly string FORMAT_GIF = "GIF";
private static readonly string FORMAT_BITMAP = "System.Drawing.Bitmap";
//private static readonly string FORMAT_HTML = "HTML Format";
private static IntPtr nextClipboardViewer = IntPtr.Zero;
// Template for the HTML Text on the clipboard
// see: http://msdn.microsoft.com/en-us/library/ms649015%28v=vs.85%29.aspx
// or: http://msdn.microsoft.com/en-us/library/Aa767917.aspx
private const string HTML_CLIPBOARD_STRING = @"Version:0.9
private const string HtmlClipboardString = @"Version:0.9
StartHTML:<<<<<<<1
EndHTML:<<<<<<<2
StartFragment:<<<<<<<3
@ -75,7 +74,7 @@ EndSelection:<<<<<<<4
<!--EndFragment -->
</BODY>
</HTML>";
private const string HTML_CLIPBOARD_BASE64_STRING = @"Version:0.9
private const string HtmlClipboardBase64String = @"Version:0.9
StartHTML:<<<<<<<1
EndHTML:<<<<<<<2
StartFragment:<<<<<<<3
@ -111,7 +110,7 @@ EndSelection:<<<<<<<4
using (Process ownerProcess = Process.GetProcessById(pid))
{
// Exclude myself
if (ownerProcess != null && me.Id != ownerProcess.Id)
if (me.Id != ownerProcess.Id)
{
// Get Process Name
owner = ownerProcess.ProcessName;
@ -122,20 +121,21 @@ EndSelection:<<<<<<<4
}
catch (Exception)
{
// Ignore
}
}
}
}
catch(Exception e)
{
LOG.Warn("Non critical error: Couldn't get clipboard process, trying to use the title.", e);
StringBuilder title = new StringBuilder(260, 260);
Log.Warn("Non critical error: Couldn't get clipboard process, trying to use the title.", e);
var title = new StringBuilder(260, 260);
User32.GetWindowText(hWnd, title, title.Capacity);
owner = title.ToString();
}
}
} catch (Exception e) {
LOG.Warn("Non critical error: Couldn't get clipboard owner.", e);
Log.Warn("Non critical error: Couldn't get clipboard owner.", e);
}
return owner;
}
@ -147,7 +147,7 @@ EndSelection:<<<<<<<4
/// <param name="ido"></param>
/// <param name="copy"></param>
private static void SetDataObject(IDataObject ido, bool copy) {
lock (clipboardLockObject) {
lock (ClipboardLockObject) {
// Clear first, this seems to solve some issues
try
{
@ -155,7 +155,7 @@ EndSelection:<<<<<<<4
}
catch (Exception clearException)
{
LOG.Warn(clearException.Message);
Log.Warn(clearException.Message);
}
try
{
@ -164,7 +164,7 @@ EndSelection:<<<<<<<4
}
catch (Exception clipboardSetException)
{
string messageText = null;
string messageText;
string clipboardOwner = GetClipboardOwner();
if (clipboardOwner != null)
{
@ -173,7 +173,7 @@ EndSelection:<<<<<<<4
else {
messageText = Language.GetString("clipboard_error");
}
LOG.Error(messageText, clipboardSetException);
Log.Error(messageText, clipboardSetException);
}
}
}
@ -182,21 +182,21 @@ EndSelection:<<<<<<<4
/// The GetDataObject will lock/try/catch clipboard operations making it save and not show exceptions.
/// </summary>
public static IDataObject GetDataObject() {
lock (clipboardLockObject) {
lock (ClipboardLockObject) {
int retryCount = 2;
while (retryCount >= 0) {
try {
return Clipboard.GetDataObject();
} catch (Exception ee) {
if (retryCount == 0) {
string messageText = null;
string messageText;
string clipboardOwner = GetClipboardOwner();
if (clipboardOwner != null) {
messageText = Language.GetFormattedString("clipboard_inuse", clipboardOwner);
} else {
messageText = Language.GetString("clipboard_error");
}
LOG.Error(messageText, ee);
Log.Error(messageText, ee);
} else {
Thread.Sleep(100);
}
@ -264,13 +264,15 @@ EndSelection:<<<<<<<4
if (dataObject.GetDataPresent(FORMAT_FILECONTENTS)) {
try {
MemoryStream imageStream = dataObject.GetData(FORMAT_FILECONTENTS) as MemoryStream;
if (isValidStream(imageStream)) {
using (Image tmpImage = Image.FromStream(imageStream)) {
if (IsValidStream(imageStream)) {
using (ImageHelper.FromStream(imageStream))
{
// If we get here, there is an image
return true;
}
}
} catch (Exception) {
// Ignore
}
}
}
@ -282,7 +284,7 @@ EndSelection:<<<<<<<4
/// </summary>
/// <param name="memoryStream"></param>
/// <returns></returns>
private static bool isValidStream(MemoryStream memoryStream) {
private static bool IsValidStream(MemoryStream memoryStream) {
return memoryStream != null && memoryStream.Length > 0;
}
@ -309,7 +311,7 @@ EndSelection:<<<<<<<4
// Get single image, this takes the "best" match
Image 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 image from clipboard with size {0} and format {1}", singleImage.Size, singleImage.PixelFormat);
yield return singleImage;
} else {
// check if files are supplied
@ -320,10 +322,10 @@ EndSelection:<<<<<<<4
try {
returnImage = ImageHelper.LoadImage(imageFile);
} catch (Exception streamImageEx) {
LOG.Error("Problem retrieving Image from clipboard.", streamImageEx);
Log.Error("Problem retrieving Image from clipboard.", streamImageEx);
}
if (returnImage != null) {
LOG.InfoFormat("Got image from clipboard with size {0} and format {1}", returnImage.Size, returnImage.PixelFormat);
Log.InfoFormat("Got image from clipboard with size {0} and format {1}", returnImage.Size, returnImage.PixelFormat);
yield return returnImage;
}
}
@ -346,20 +348,19 @@ EndSelection:<<<<<<<4
// So I build some special logik 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...");
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_JFIF, DataFormats.Tiff, FORMAT_GIF };
} else {
retrieveFormats = new[] { FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_GIF };
}
foreach (string currentFormat in retrieveFormats) {
if (formats.Contains(currentFormat)) {
LOG.InfoFormat("Found {0}, trying to retrieve.", currentFormat);
Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat);
returnImage = GetImageForFormat(currentFormat, dataObject);
} else {
LOG.DebugFormat("Couldn't find format {0}.", currentFormat);
Log.DebugFormat("Couldn't find format {0}.", currentFormat);
}
if (returnImage != null) {
ImageHelper.Orientate(returnImage);
return returnImage;
}
}
@ -378,78 +379,84 @@ EndSelection:<<<<<<<4
private static Image GetImageForFormat(string format, IDataObject dataObject) {
object clipboardObject = GetFromDataObject(dataObject, format);
MemoryStream imageStream = clipboardObject as MemoryStream;
if (!isValidStream(imageStream)) {
if (!IsValidStream(imageStream)) {
// TODO: add "HTML Format" support here...
return clipboardObject as Image;
} else {
if (config.EnableSpecialDIBClipboardReader) {
if (format == FORMAT_17 || format == DataFormats.Dib) {
LOG.Info("Found DIB stream, trying to process it.");
try {
byte[] dibBuffer = new byte[imageStream.Length];
imageStream.Read(dibBuffer, 0, dibBuffer.Length);
BITMAPINFOHEADER infoHeader = BinaryStructHelper.FromByteArray<BITMAPINFOHEADER>(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);
}
if (CoreConfig.EnableSpecialDIBClipboardReader) {
if (format == FORMAT_17 || format == DataFormats.Dib) {
Log.Info("Found DIB stream, trying to process it.");
try {
byte[] dibBuffer = new byte[imageStream.Length];
imageStream.Read(dibBuffer, 0, dibBuffer.Length);
BITMAPINFOHEADER infoHeader = BinaryStructHelper.FromByteArray<BITMAPINFOHEADER>(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);
BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER();
fileHeader.bfType = BITMAPFILEHEADER.BM;
fileHeader.bfSize = fileSize;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = (int)(fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4);
BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER
{
bfType = BITMAPFILEHEADER.BM,
bfSize = fileSize,
bfReserved1 = 0,
bfReserved2 = 0,
bfOffBits = (int) (fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed*4)
};
byte[] fileHeaderBytes = BinaryStructHelper.ToByteArray<BITMAPFILEHEADER>(fileHeader);
byte[] fileHeaderBytes = BinaryStructHelper.ToByteArray(fileHeader);
using (MemoryStream bitmapStream = new MemoryStream()) {
bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize);
bitmapStream.Write(dibBuffer, 0, dibBuffer.Length);
bitmapStream.Seek(0, SeekOrigin.Begin);
using (Image tmpImage = Image.FromStream(bitmapStream)) {
if (tmpImage != null) {
return ImageHelper.Clone(tmpImage);
}
}
}
} 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();
}
using (MemoryStream 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();
}
}
} catch (Exception dibEx) {
LOG.Error("Problem retrieving DIB from clipboard.", dibEx);
}
} 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 {
} else {
Log.Info("Skipping special DIB format reader as it's disabled in the configuration.");
}
try {
if (imageStream != null)
{
imageStream.Seek(0, SeekOrigin.Begin);
using (Image tmpImage = Image.FromStream(imageStream, true, true)) {
if (tmpImage != null) {
LOG.InfoFormat("Got image with clipboard format {0} from the clipboard.", format);
return ImageHelper.Clone(tmpImage);
}
var tmpImage = ImageHelper.FromStream(imageStream);
if (tmpImage != null) {
Log.InfoFormat("Got image with clipboard format {0} from the clipboard.", format);
return tmpImage;
}
} catch (Exception streamImageEx) {
LOG.Error(string.Format("Problem retrieving {0} from clipboard.", format), streamImageEx);
}
} catch (Exception streamImageEx) {
Log.Error(string.Format("Problem retrieving {0} from clipboard.", format), streamImageEx);
}
return null;
}
@ -468,7 +475,7 @@ EndSelection:<<<<<<<4
/// <returns>string if there is text on the clipboard</returns>
public static string GetText(IDataObject dataObject) {
if (ContainsText(dataObject)) {
return (String)dataObject.GetData(DataFormats.Text);
return (string)dataObject.GetData(DataFormats.Text);
}
return null;
}
@ -483,32 +490,32 @@ EndSelection:<<<<<<<4
SetDataObject(ido, true);
}
private static string getHTMLString(ISurface surface, string filename) {
string utf8EncodedHTMLString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HTML_CLIPBOARD_STRING));
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${width}", surface.Image.Width.ToString());
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${height}", surface.Image.Height.ToString());
utf8EncodedHTMLString= utf8EncodedHTMLString.Replace("${file}", filename.Replace("\\","/"));
private static string GetHtmlString(ISurface surface, string filename) {
string utf8EncodedHtmlString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HtmlClipboardString));
utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${width}", surface.Image.Width.ToString());
utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${height}", surface.Image.Height.ToString());
utf8EncodedHtmlString= utf8EncodedHtmlString.Replace("${file}", filename.Replace("\\","/"));
StringBuilder sb=new StringBuilder();
sb.Append(utf8EncodedHTMLString);
sb.Replace("<<<<<<<1", (utf8EncodedHTMLString.IndexOf("<HTML>") + "<HTML>".Length).ToString("D8"));
sb.Replace("<<<<<<<2", (utf8EncodedHTMLString.IndexOf("</HTML>")).ToString("D8"));
sb.Replace("<<<<<<<3", (utf8EncodedHTMLString.IndexOf("<!--StartFragment -->")+"<!--StartFragment -->".Length).ToString("D8"));
sb.Replace("<<<<<<<4", (utf8EncodedHTMLString.IndexOf("<!--EndFragment -->")).ToString("D8"));
sb.Append(utf8EncodedHtmlString);
sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("<HTML>") + "<HTML>".Length).ToString("D8"));
sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("</HTML>")).ToString("D8"));
sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("<!--StartFragment -->")+"<!--StartFragment -->".Length).ToString("D8"));
sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("<!--EndFragment -->")).ToString("D8"));
return sb.ToString();
}
private static string getHTMLDataURLString(ISurface surface, MemoryStream pngStream) {
string utf8EncodedHTMLString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HTML_CLIPBOARD_BASE64_STRING));
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${width}", surface.Image.Width.ToString());
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${height}", surface.Image.Height.ToString());
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${format}", "png");
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${data}", Convert.ToBase64String(pngStream.GetBuffer(),0, (int)pngStream.Length));
private static string GetHtmlDataUrlString(ISurface surface, MemoryStream pngStream) {
string utf8EncodedHtmlString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HtmlClipboardBase64String));
utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${width}", surface.Image.Width.ToString());
utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${height}", surface.Image.Height.ToString());
utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${format}", "png");
utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${data}", Convert.ToBase64String(pngStream.GetBuffer(),0, (int)pngStream.Length));
StringBuilder sb=new StringBuilder();
sb.Append(utf8EncodedHTMLString);
sb.Replace("<<<<<<<1", (utf8EncodedHTMLString.IndexOf("<HTML>") + "<HTML>".Length).ToString("D8"));
sb.Replace("<<<<<<<2", (utf8EncodedHTMLString.IndexOf("</HTML>")).ToString("D8"));
sb.Replace("<<<<<<<3", (utf8EncodedHTMLString.IndexOf("<!--StartFragment -->")+"<!--StartFragment -->".Length).ToString("D8"));
sb.Replace("<<<<<<<4", (utf8EncodedHTMLString.IndexOf("<!--EndFragment -->")).ToString("D8"));
sb.Append(utf8EncodedHtmlString);
sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("<HTML>") + "<HTML>".Length).ToString("D8"));
sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("</HTML>")).ToString("D8"));
sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("<!--StartFragment -->")+"<!--StartFragment -->".Length).ToString("D8"));
sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("<!--EndFragment -->")).ToString("D8"));
return sb.ToString();
}
@ -539,7 +546,7 @@ EndSelection:<<<<<<<4
disposeImage = ImageOutput.CreateImageFromSurface(surface, outputSettings, out imageToSave);
try {
// Create PNG stream
if (config.ClipboardFormats.Contains(ClipboardFormat.PNG)) {
if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.PNG)) {
pngStream = new MemoryStream();
// PNG works for e.g. Powerpoint
SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
@ -549,11 +556,11 @@ EndSelection:<<<<<<<4
dataObject.SetData(FORMAT_PNG, false, pngStream);
}
} catch (Exception pngEX) {
LOG.Error("Error creating PNG for the Clipboard.", pngEX);
Log.Error("Error creating PNG for the Clipboard.", pngEX);
}
try {
if (config.ClipboardFormats.Contains(ClipboardFormat.DIB)) {
if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.DIB)) {
using (MemoryStream tmpBmpStream = new MemoryStream()) {
// Save image as BMP
SurfaceOutputSettings bmpOutputSettings = new SurfaceOutputSettings(OutputFormat.bmp, 100, false);
@ -568,21 +575,23 @@ EndSelection:<<<<<<<4
dataObject.SetData(DataFormats.Dib, true, dibStream);
}
} catch (Exception dibEx) {
LOG.Error("Error creating DIB for the Clipboard.", dibEx);
Log.Error("Error creating DIB for the Clipboard.", dibEx);
}
// CF_DibV5
try {
if (config.ClipboardFormats.Contains(ClipboardFormat.DIBV5)) {
if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.DIBV5)) {
// Create the stream for the clipboard
dibV5Stream = new MemoryStream();
// Create the BITMAPINFOHEADER
BITMAPINFOHEADER header = new BITMAPINFOHEADER(imageToSave.Width, imageToSave.Height, 32);
// Make sure we have BI_BITFIELDS, this seems to be normal for Format17?
header.biCompression = BI_COMPRESSION.BI_BITFIELDS;
BITMAPINFOHEADER header = new BITMAPINFOHEADER(imageToSave.Width, imageToSave.Height, 32)
{
// Make sure we have BI_BITFIELDS, this seems to be normal for Format17?
biCompression = BI_COMPRESSION.BI_BITFIELDS
};
// Create a byte[] to write
byte[] headerBytes = BinaryStructHelper.ToByteArray<BITMAPINFOHEADER>(header);
byte[] headerBytes = BinaryStructHelper.ToByteArray(header);
// Write the BITMAPINFOHEADER to the stream
dibV5Stream.Write(headerBytes, 0, headerBytes.Length);
@ -591,7 +600,7 @@ EndSelection:<<<<<<<4
// Make sure the values are set
colorMask.InitValues();
// Create the byte[] from the struct
byte[] colorMaskBytes = BinaryStructHelper.ToByteArray<BitfieldColorMask>(colorMask);
byte[] colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask);
Array.Reverse(colorMaskBytes);
// Write to the stream
dibV5Stream.Write(colorMaskBytes, 0, colorMaskBytes.Length);
@ -605,35 +614,37 @@ EndSelection:<<<<<<<4
dataObject.SetData(FORMAT_17, true, dibV5Stream);
}
} catch (Exception dibEx) {
LOG.Error("Error creating DIB for the Clipboard.", dibEx);
Log.Error("Error creating DIB for the Clipboard.", dibEx);
}
// Set the HTML
if (config.ClipboardFormats.Contains(ClipboardFormat.HTML)) {
if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML)) {
string tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null);
string html = getHTMLString(surface, tmpFile);
string html = GetHtmlString(surface, tmpFile);
dataObject.SetText(html, TextDataFormat.Html);
} else if (config.ClipboardFormats.Contains(ClipboardFormat.HTMLDATAURL)) {
} else if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTMLDATAURL)) {
string html;
using (MemoryStream tmpPNGStream = new MemoryStream()) {
SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
// Do not allow to reduce the colors, some applications dislike 256 color images
// reported with bug #3594681
pngOutputSettings.DisableReduceColors = true;
using (MemoryStream tmpPngStream = new MemoryStream()) {
SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false)
{
// Do not allow to reduce the colors, some applications dislike 256 color images
// reported with bug #3594681
DisableReduceColors = true
};
// Check if we can use the previously used image
if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed) {
ImageOutput.SaveToStream(imageToSave, surface, tmpPNGStream, pngOutputSettings);
ImageOutput.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings);
} else {
ImageOutput.SaveToStream(surface, tmpPNGStream, pngOutputSettings);
ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings);
}
html = getHTMLDataURLString(surface, tmpPNGStream);
html = GetHtmlDataUrlString(surface, tmpPngStream);
}
dataObject.SetText(html, TextDataFormat.Html);
}
} finally {
// we need to use the SetDataOject before the streams are closed otherwise the buffer will be gone!
// Check if Bitmap is wanted
if (config.ClipboardFormats.Contains(ClipboardFormat.BITMAP)) {
if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.BITMAP)) {
dataObject.SetImage(imageToSave);
// Place the DataObject to the clipboard
SetDataObject(dataObject, true);
@ -656,8 +667,8 @@ EndSelection:<<<<<<<4
dibV5Stream = null;
}
// cleanup if needed
if (disposeImage && imageToSave != null) {
imageToSave.Dispose();
if (disposeImage) {
imageToSave?.Dispose();
}
}
}
@ -695,7 +706,7 @@ EndSelection:<<<<<<<4
/// </summary>
/// <param name="type">Type</param>
/// <param name="obj">object</param>
public static void SetClipboardData(Type type, Object obj) {
public static void SetClipboardData(Type type, object obj) {
DataFormats.Format format = DataFormats.GetFormat(type.FullName);
//now copy to clipboard
@ -724,7 +735,7 @@ EndSelection:<<<<<<<4
formats = dataObj.GetFormats();
}
if (formats != null) {
LOG.DebugFormat("Got clipboard formats: {0}", String.Join(",", formats));
Log.DebugFormat("Got clipboard formats: {0}", string.Join(",", formats));
return new List<string>(formats);
}
return new List<string>();
@ -784,7 +795,7 @@ EndSelection:<<<<<<<4
/// </summary>
/// <param name="type">Type to get</param>
/// <returns>object from clipboard</returns>
public static Object GetClipboardData(Type type) {
public static object GetClipboardData(Type type) {
string format = type.FullName;
return GetClipboardData(format);
}
@ -795,7 +806,7 @@ EndSelection:<<<<<<<4
/// <param name="dataObj">IDataObject</param>
/// <param name="type">Type to get</param>
/// <returns>object from IDataObject</returns>
public static Object GetFromDataObject(IDataObject dataObj, Type type) {
public static object GetFromDataObject(IDataObject dataObj, Type type) {
if (type != null) {
return GetFromDataObject(dataObj, type.FullName);
}
@ -820,7 +831,7 @@ EndSelection:<<<<<<<4
}
}
} catch (Exception ex) {
LOG.Warn("Ignoring an issue with getting the dropFilenames from the clipboard: ", ex);
Log.Warn("Ignoring an issue with getting the dropFilenames from the clipboard: ", ex);
}
return filenames;
}
@ -831,12 +842,12 @@ EndSelection:<<<<<<<4
/// <param name="dataObj">IDataObject</param>
/// <param name="format">format to get</param>
/// <returns>object from IDataObject</returns>
public static Object GetFromDataObject(IDataObject dataObj, string format) {
public static object GetFromDataObject(IDataObject dataObj, string format) {
if (dataObj != null) {
try {
return dataObj.GetData(format);
} catch (Exception e) {
LOG.Error("Error in GetClipboardData.", e);
Log.Error("Error in GetClipboardData.", e);
}
}
return null;
@ -847,7 +858,7 @@ EndSelection:<<<<<<<4
/// </summary>
/// <param name="format">format to get</param>
/// <returns>object from clipboard</returns>
public static Object GetClipboardData(string format) {
public static object GetClipboardData(string format) {
return GetFromDataObject(GetDataObject(), format);
}
}

View file

@ -0,0 +1,29 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2016 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://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 <http://www.gnu.org/licenses/>.
*/
namespace GreenshotPlugin.Core
{
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T, out TResult>(T arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
public delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
}

View file

@ -0,0 +1,68 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2016 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace GreenshotPlugin.Core
{
/// <summary>
/// The image interface, this abstracts an image
/// </summary>
public interface IImage : IDisposable
{
/// <summary>
/// Height of the image, can be set to change
/// </summary>
int Height { get; set; }
/// <summary>
/// Width of the image, can be set to change.
/// </summary>
int Width { get; set; }
/// <summary>
/// Size of the image
/// </summary>
Size Size { get; }
/// <summary>
/// Pixelformat of the underlying image
/// </summary>
PixelFormat PixelFormat { get; }
/// <summary>
/// Vertical resolution of the underlying image
/// </summary>
float VerticalResolution { get; }
/// <summary>
/// Horizontal resolution of the underlying image
/// </summary>
float HorizontalResolution { get; }
/// <summary>
/// Unterlying image, or an on demand rendered version with different attributes as the original
/// </summary>
Image Image { get; }
}
}

File diff suppressed because it is too large Load diff

View file

@ -390,41 +390,13 @@ namespace GreenshotPlugin.Core {
if (string.IsNullOrEmpty(fullPath)) {
return null;
}
Image fileImage;
LOG.InfoFormat("Loading image from file {0}", fullPath);
// Fixed lock problem Bug #3431881
using (Stream surfaceFileStream = File.OpenRead(fullPath)) {
// And 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 {0} with Size {1}x{2} and PixelFormat {3}", fullPath, 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);
string greenshotMarker;
using (StreamReader streamReader = new StreamReader(surfaceFileStream)) {
greenshotMarker = streamReader.ReadToEnd();
if (!greenshotMarker.StartsWith("Greenshot")) {
throw new ArgumentException(string.Format("{0} is not a Greenshot file!", fullPath));
}
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);
}
}
returnSurface = ImageHelper.LoadGreenshotSurface(surfaceFileStream, returnSurface);
}
if (fileImage != null) {
returnSurface.Image = fileImage;
LOG.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", fullPath, fileImage.Width, fileImage.Height, fileImage.PixelFormat, fileImage.HorizontalResolution, fileImage.VerticalResolution);
if (returnSurface != null) {
LOG.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", fullPath, returnSurface.Image.Width, returnSurface.Image.Height, returnSurface.Image.PixelFormat, returnSurface.Image.HorizontalResolution, returnSurface.Image.VerticalResolution);
}
return returnSurface;
}

View file

@ -0,0 +1,86 @@
using System.Drawing;
using System.Drawing.Imaging;
namespace GreenshotPlugin.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;
/// <summary>
/// Factory method
/// </summary>
/// <param name="image">Image</param>
/// <returns>IImage</returns>
public static IImage FromImage(Image image)
{
return image == null ? null : new ImageWrapper(image);
}
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

@ -25,7 +25,6 @@ using log4net;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Net;
@ -91,9 +90,9 @@ namespace GreenshotPlugin.Core {
{
if (responseStream != null)
{
using (Image image = Image.FromStream(responseStream))
using (Image image = ImageHelper.FromStream(responseStream))
{
return (image.Height > 16 && image.Width > 16) ? new Bitmap(image, 16, 16) : new Bitmap(image);
return image.Height > 16 && image.Width > 16 ? new Bitmap(image, 16, 16) : new Bitmap(image);
}
}
}
@ -130,43 +129,60 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="url">Of an image</param>
/// <returns>Bitmap</returns>
public static Image DownloadImage(string url) {
try {
string content;
using (MemoryStream memoryStream = GetAsMemoryStream(url)) {
try {
using (Image image = Image.FromStream(memoryStream)) {
return ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
}
} catch (Exception) {
public static Image DownloadImage(string url)
{
StringBuilder extensions = new StringBuilder();
foreach (var extension in ImageHelper.StreamConverters.Keys)
{
if (string.IsNullOrEmpty(extension))
{
continue;
}
extensions.AppendFormat(@"\.{0}|", extension);
}
extensions.Length--;
var imageUrlRegex = new Regex($@"(http|https)://.*(?<extension>{extensions})");
var match = imageUrlRegex.Match(url);
try
{
using (var memoryStream = GetAsMemoryStream(url))
{
try
{
return ImageHelper.FromStream(memoryStream, match.Success ? match.Groups["extension"]?.Value : null);
}
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.
using (StreamReader streamReader = new StreamReader(memoryStream, Encoding.UTF8, true)) {
string content;
using (StreamReader streamReader = new StreamReader(memoryStream, Encoding.UTF8, true))
{
content = streamReader.ReadLine();
}
if (!string.IsNullOrEmpty(content))
if (string.IsNullOrEmpty(content))
{
Regex imageUrlRegex = new Regex(@"(http|https)://.*(\.png|\.gif|\.jpg|\.tiff|\.jpeg|\.bmp)");
Match match = imageUrlRegex.Match(content);
if (match.Success)
{
using (MemoryStream memoryStream2 = GetAsMemoryStream(match.Value))
{
using (Image image = Image.FromStream(memoryStream2))
{
return ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
}
}
}
throw;
}
match = imageUrlRegex.Match(content);
if (!match.Success)
{
throw;
}
using (var memoryStream2 = GetAsMemoryStream(match.Value))
{
return ImageHelper.FromStream(memoryStream2, match.Groups["extension"]?.Value);
}
throw;
}
}
} catch (Exception e) {
}
catch (Exception e)
{
LOG.Error("Problem downloading the image from: " + url, e);
}
return null;
}
/// <summary>
/// Helper method to create a web request with a lot of default settings
/// </summary>
@ -347,7 +363,7 @@ namespace GreenshotPlugin.Core {
/// <param name="webRequest">HttpWebRequest to write the multipart form data to</param>
/// <param name="postParameters">Parameters to include in the multipart form data</param>
public static void WriteMultipartFormData(HttpWebRequest webRequest, IDictionary<string, object> postParameters) {
string boundary = String.Format("----------{0:N}", Guid.NewGuid());
string boundary = string.Format("----------{0:N}", Guid.NewGuid());
webRequest.ContentType = "multipart/form-data; boundary=" + boundary;
using (Stream formDataStream = webRequest.GetRequestStream()) {
WriteMultipartFormData(formDataStream, boundary, postParameters);
@ -360,7 +376,7 @@ namespace GreenshotPlugin.Core {
/// <param name="response">HttpListenerResponse</param>
/// <param name="postParameters">Parameters to include in the multipart form data</param>
public static void WriteMultipartFormData(HttpListenerResponse response, IDictionary<string, object> postParameters) {
string boundary = String.Format("----------{0:N}", Guid.NewGuid());
string boundary = string.Format("----------{0:N}", Guid.NewGuid());
response.ContentType = "multipart/form-data; boundary=" + boundary;
WriteMultipartFormData(response.OutputStream, boundary, postParameters);
}
@ -474,7 +490,7 @@ namespace GreenshotPlugin.Core {
string responseData = null;
HttpWebResponse response = null;
bool isHttpError = false;
try {
try {
response = (HttpWebResponse)webRequest.GetResponse();
LOG.InfoFormat("Response status: {0}", response.StatusCode);
isHttpError = (int)response.StatusCode >= 300;