diff --git a/Greenshot/Drawing/ImageContainer.cs b/Greenshot/Drawing/ImageContainer.cs index b9f08c28f..172ac7e9a 100644 --- a/Greenshot/Drawing/ImageContainer.cs +++ b/Greenshot/Drawing/ImageContainer.cs @@ -36,7 +36,7 @@ namespace Greenshot.Drawing { /// [Serializable] public class ImageContainer : DrawableContainer, IImageContainer { - private static readonly ILog LOG = LogManager.GetLogger(typeof(ImageContainer)); + private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer)); private Image image; @@ -80,7 +80,7 @@ namespace Greenshot.Drawing { protected void BitmapContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) { if (sender.Equals(this)) { - if (e.Field.FieldType == FieldType.SHADOW) { + if (FieldType.SHADOW.Equals(e.Field.FieldType)) { ChangeShadowField(); } } @@ -140,15 +140,11 @@ namespace Greenshot.Drawing { } private void DisposeImage() { - if (image != null) { - image.Dispose(); - } + image?.Dispose(); image = null; } private void DisposeShadow() { - if (_shadowBitmap != null) { - _shadowBitmap.Dispose(); - } + _shadowBitmap?.Dispose(); _shadowBitmap = null; } @@ -162,10 +158,11 @@ namespace Greenshot.Drawing { int rotateAngle = CalculateAngle(matrix); // we currently assume only one transformation has been made. if (rotateAngle != 0) { - LOG.DebugFormat("Rotating element with {0} degrees.", rotateAngle); + Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle); DisposeShadow(); using (var tmpMatrix = new Matrix()) { - using (Image tmpImage = image) { + using (image) + { image = ImageHelper.ApplyEffect(image, new RotateEffect(rotateAngle), tmpMatrix); } } @@ -178,14 +175,16 @@ namespace Greenshot.Drawing { /// /// public void Load(string filename) { - if (File.Exists(filename)) { - // Always make sure ImageHelper.LoadBitmap results are disposed some time, - // as we close the bitmap internally, we need to do it afterwards - using (Image tmpImage = ImageHelper.LoadImage(filename)) { - Image = tmpImage; - } - LOG.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); + if (!File.Exists(filename)) + { + return; } + // 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)) { + Image = tmpImage; + } + Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); } /// @@ -222,16 +221,8 @@ namespace Greenshot.Drawing { } } - public override bool HasDefaultSize { - get { - return true; - } - } + public override bool HasDefaultSize => true; - public override Size DefaultSize { - get { - return image.Size; - } - } + public override Size DefaultSize => image.Size; } } diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 7a4b368a9..12199733d 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -436,7 +436,7 @@ namespace Greenshot.Drawing /// /// Property for accessing the URL to which the surface was recently uploaded /// - public string UploadURL + public string UploadUrl { get; set; diff --git a/Greenshot/Forms/MainForm.cs b/Greenshot/Forms/MainForm.cs index 355fb4396..7beee15f5 100644 --- a/Greenshot/Forms/MainForm.cs +++ b/Greenshot/Forms/MainForm.cs @@ -41,6 +41,7 @@ using GreenshotPlugin.Controls; using GreenshotPlugin.Core; using Greenshot.IniFile; using Greenshot.Destinations; +using Greenshot.Drawing; using log4net; using Timer = System.Timers.Timer; @@ -320,6 +321,9 @@ namespace Greenshot { public MainForm(CopyDataTransport dataTransport) { _instance = this; + // Factory for surface objects + ImageHelper.SurfaceFactory = () => new Surface(); + // // The InitializeComponent() call is required for Windows Forms designer support. // diff --git a/Greenshot/Helpers/CaptureHelper.cs b/Greenshot/Helpers/CaptureHelper.cs index f31c1e81e..74975f82f 100644 --- a/Greenshot/Helpers/CaptureHelper.cs +++ b/Greenshot/Helpers/CaptureHelper.cs @@ -549,8 +549,8 @@ namespace Greenshot.Helpers { MessageBox.Show(string.Format("{0}\r\nexplorer.exe {1}", errorMessage, surface.LastSaveFullPath), "explorer.exe", MessageBoxButtons.OK, MessageBoxIcon.Error); } } - } else if (surface != null && !string.IsNullOrEmpty(surface.UploadURL)) { - Process.Start(surface.UploadURL); + } else if (surface != null && !string.IsNullOrEmpty(surface.UploadUrl)) { + Process.Start(surface.UploadUrl); } LOG.DebugFormat("Deregistering the BalloonTipClicked"); RemoveEventHandler(sender, e); diff --git a/GreenshotConfluencePlugin/ConfluenceDestination.cs b/GreenshotConfluencePlugin/ConfluenceDestination.cs index ea2594b29..fa9ccb7a1 100644 --- a/GreenshotConfluencePlugin/ConfluenceDestination.cs +++ b/GreenshotConfluencePlugin/ConfluenceDestination.cs @@ -36,11 +36,12 @@ namespace GreenshotConfluencePlugin { /// Description of ConfluenceDestination. /// public class ConfluenceDestination : AbstractDestination { - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluenceDestination)); - private static readonly ConfluenceConfiguration config = IniConfig.GetIniSection(); - private static readonly CoreConfiguration coreConfig = IniConfig.GetIniSection(); - private static readonly Image confluenceIcon = null; - private readonly Page page; + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceDestination)); + private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection(); + private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); + private static readonly Image ConfluenceIcon; + private readonly Page _page; + public static bool IsInitialized { get; private set; @@ -49,14 +50,14 @@ namespace GreenshotConfluencePlugin { IsInitialized = false; try { Uri confluenceIconUri = new Uri("/GreenshotConfluencePlugin;component/Images/Confluence.ico", UriKind.Relative); - using (Stream iconStream = Application.GetResourceStream(confluenceIconUri).Stream) { - using (Image tmpImage = Image.FromStream(iconStream)) { - confluenceIcon = ImageHelper.Clone(tmpImage); - } + using (Stream iconStream = Application.GetResourceStream(confluenceIconUri)?.Stream) + { + // TODO: Check what to do with the IImage + ConfluenceIcon = ImageHelper.FromStream(iconStream); } IsInitialized = true; } catch (Exception ex) { - LOG.ErrorFormat("Problem in the confluence static initializer: {0}", ex.Message); + Log.ErrorFormat("Problem in the confluence static initializer: {0}", ex.Message); } } @@ -64,7 +65,7 @@ namespace GreenshotConfluencePlugin { } public ConfluenceDestination(Page page) { - this.page = page; + _page = page; } public override string Designation { @@ -75,10 +76,10 @@ namespace GreenshotConfluencePlugin { public override string Description { get { - if (page == null) { + if (_page == null) { return Language.GetString("confluence", LangKey.upload_menu_item); } else { - return Language.GetString("confluence", LangKey.upload_menu_item) + ": \"" + page.Title + "\""; + return Language.GetString("confluence", LangKey.upload_menu_item) + ": \"" + _page.Title + "\""; } } } @@ -91,13 +92,13 @@ namespace GreenshotConfluencePlugin { public override bool isActive { get { - return base.isActive && !string.IsNullOrEmpty(config.Url); + return base.isActive && !string.IsNullOrEmpty(ConfluenceConfig.Url); } } public override Image DisplayIcon { get { - return confluenceIcon; + return ConfluenceIcon; } } @@ -121,9 +122,9 @@ namespace GreenshotConfluencePlugin { return exportInformation; } - Page selectedPage = page; - bool openPage = (page == null) && config.OpenPageAfterUpload; - string filename = FilenameHelper.GetFilenameWithoutExtensionFromPattern(coreConfig.OutputFileFilenamePattern, captureDetails); + Page selectedPage = _page; + bool openPage = (_page == null) && ConfluenceConfig.OpenPageAfterUpload; + string filename = FilenameHelper.GetFilenameWithoutExtensionFromPattern(CoreConfig.OutputFileFilenamePattern, captureDetails); if (selectedPage == null) { ConfluenceUpload confluenceUpload = new ConfluenceUpload(filename); Nullable dialogResult = confluenceUpload.ShowDialog(); @@ -135,18 +136,23 @@ namespace GreenshotConfluencePlugin { filename = confluenceUpload.Filename; } } - string extension = "." + config.UploadFormat; + string extension = "." + ConfluenceConfig.UploadFormat; if (!filename.ToLower().EndsWith(extension)) { filename = filename + extension; } if (selectedPage != null) { string errorMessage; - bool uploaded = upload(surface, selectedPage, filename, out errorMessage); + bool uploaded = Upload(surface, selectedPage, filename, out errorMessage); if (uploaded) { if (openPage) { - try { + try + { Process.Start(selectedPage.Url); - } catch { } + } + catch + { + // Ignore + } } exportInformation.ExportMade = true; exportInformation.Uri = selectedPage.Url; @@ -158,31 +164,33 @@ namespace GreenshotConfluencePlugin { return exportInformation; } - private bool upload(ISurface surfaceToUpload, Page page, string filename, out string errorMessage) { - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(config.UploadFormat, config.UploadJpegQuality, config.UploadReduceColors); + private bool Upload(ISurface surfaceToUpload, Page page, string filename, out string errorMessage) { + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(ConfluenceConfig.UploadFormat, ConfluenceConfig.UploadJpegQuality, ConfluenceConfig.UploadReduceColors); errorMessage = null; try { new PleaseWaitForm().ShowAndWait(Description, Language.GetString("confluence", LangKey.communication_wait), - delegate() { - ConfluencePlugin.ConfluenceConnector.AddAttachment(page.Id, "image/" + config.UploadFormat.ToString().ToLower(), null, filename, new SurfaceContainer(surfaceToUpload, outputSettings, filename)); + delegate { + ConfluencePlugin.ConfluenceConnector.AddAttachment(page.Id, "image/" + ConfluenceConfig.UploadFormat.ToString().ToLower(), null, filename, new SurfaceContainer(surfaceToUpload, outputSettings, filename)); } ); - LOG.Debug("Uploaded to Confluence."); - if (config.CopyWikiMarkupForImageToClipboard) { - int retryCount = 2; - while (retryCount >= 0) { - try { - Clipboard.SetText("!" + filename + "!"); - break; - } catch (Exception ee) { - if (retryCount == 0) { - LOG.Error(ee); - } else { - Thread.Sleep(100); - } - } finally { - --retryCount; + Log.Debug("Uploaded to Confluence."); + if (!ConfluenceConfig.CopyWikiMarkupForImageToClipboard) + { + return true; + } + int retryCount = 2; + while (retryCount >= 0) { + try { + Clipboard.SetText("!" + filename + "!"); + break; + } catch (Exception ee) { + if (retryCount == 0) { + Log.Error(ee); + } else { + Thread.Sleep(100); } + } finally { + --retryCount; } } return true; diff --git a/GreenshotImgurPlugin/ImgurUtils.cs b/GreenshotImgurPlugin/ImgurUtils.cs index ed430bb99..c32c0afba 100644 --- a/GreenshotImgurPlugin/ImgurUtils.cs +++ b/GreenshotImgurPlugin/ImgurUtils.cs @@ -32,7 +32,7 @@ namespace GreenshotImgurPlugin { /// Description of ImgurUtils. /// public static class ImgurUtils { - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ImgurUtils)); + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurUtils)); private const string SmallUrlPattern = "http://i.imgur.com/{0}s.jpg"; private static readonly ImgurConfiguration Config = IniConfig.GetIniSection(); private const string AuthUrlPattern = "https://api.imgur.com/oauth2/authorize?response_type=code&client_id={ClientId}&redirect_uri={RedirectUrl}&state={State}"; @@ -64,7 +64,7 @@ namespace GreenshotImgurPlugin { RetrieveImgurThumbnail(imgurInfo); Config.runtimeImgurHistory.Add(hash, imgurInfo); } else { - LOG.DebugFormat("Deleting not found ImgUr {0} from config.", hash); + Log.DebugFormat("Deleting not found ImgUr {0} from config.", hash); Config.ImgurUploadHistory.Remove(hash); saveNeeded = true; } @@ -74,16 +74,16 @@ namespace GreenshotImgurPlugin { HttpWebResponse response = ((HttpWebResponse)wE.Response); // Image no longer available if (response.StatusCode == HttpStatusCode.Redirect) { - LOG.InfoFormat("ImgUr image for hash {0} is no longer available", hash); + Log.InfoFormat("ImgUr image for hash {0} is no longer available", hash); Config.ImgurUploadHistory.Remove(hash); redirected = true; } } if (!redirected) { - LOG.Error("Problem loading ImgUr history for hash " + hash, wE); + Log.Error("Problem loading ImgUr history for hash " + hash, wE); } } catch (Exception e) { - LOG.Error("Problem loading ImgUr history for hash " + hash, e); + Log.Error("Problem loading ImgUr history for hash " + hash, e); } } if (saveNeeded) { @@ -146,7 +146,7 @@ namespace GreenshotImgurPlugin { } } } catch (Exception ex) { - LOG.Error("Upload to imgur gave an exeption: ", ex); + Log.Error("Upload to imgur gave an exeption: ", ex); throw; } } else { @@ -200,10 +200,10 @@ namespace GreenshotImgurPlugin { /// public static void RetrieveImgurThumbnail(ImgurInfo imgurInfo) { if (imgurInfo.SmallSquare == null) { - LOG.Warn("Imgur URL was null, not retrieving thumbnail."); + Log.Warn("Imgur URL was null, not retrieving thumbnail."); return; } - LOG.InfoFormat("Retrieving Imgur image for {0} with url {1}", imgurInfo.Hash, imgurInfo.SmallSquare); + Log.InfoFormat("Retrieving Imgur image for {0} with url {1}", imgurInfo.Hash, imgurInfo.SmallSquare); HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(string.Format(SmallUrlPattern, imgurInfo.Hash), HTTPMethod.GET); webRequest.ServicePoint.Expect100Continue = false; // Not for getting the thumbnail, in anonymous modus @@ -213,7 +213,7 @@ namespace GreenshotImgurPlugin { Stream responseStream = response.GetResponseStream(); if (responseStream != null) { - imgurInfo.Image = Image.FromStream(responseStream); + imgurInfo.Image = ImageHelper.FromStream(responseStream); } } } @@ -226,16 +226,21 @@ namespace GreenshotImgurPlugin { /// ImgurInfo public static ImgurInfo RetrieveImgurInfo(string hash, string deleteHash) { string url = Config.ImgurApi3Url + "/image/" + hash + ".xml"; - LOG.InfoFormat("Retrieving Imgur info for {0} with url {1}", hash, url); + Log.InfoFormat("Retrieving Imgur info for {0} with url {1}", hash, url); HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.GET); webRequest.ServicePoint.Expect100Continue = false; SetClientId(webRequest); - string responseString; + string responseString = null; try { using (WebResponse response = webRequest.GetResponse()) { LogRateLimitInfo(response); - using (StreamReader reader = new StreamReader(response.GetResponseStream(), true)) { - responseString = reader.ReadToEnd(); + var responseStream = response.GetResponseStream(); + if (responseStream != null) + { + using (StreamReader reader = new StreamReader(responseStream, true)) + { + responseString = reader.ReadToEnd(); + } } } } catch (WebException wE) { @@ -246,9 +251,13 @@ namespace GreenshotImgurPlugin { } throw; } - LOG.Debug(responseString); - ImgurInfo imgurInfo = ImgurInfo.ParseResponse(responseString); - imgurInfo.DeleteHash = deleteHash; + ImgurInfo imgurInfo = null; + if (responseString != null) + { + Log.Debug(responseString); + imgurInfo = ImgurInfo.ParseResponse(responseString); + imgurInfo.DeleteHash = deleteHash; + } return imgurInfo; } @@ -257,21 +266,26 @@ namespace GreenshotImgurPlugin { /// /// public static void DeleteImgurImage(ImgurInfo imgurInfo) { - LOG.InfoFormat("Deleting Imgur image for {0}", imgurInfo.DeleteHash); + Log.InfoFormat("Deleting Imgur image for {0}", imgurInfo.DeleteHash); try { string url = Config.ImgurApi3Url + "/image/" + imgurInfo.DeleteHash + ".xml"; HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.DELETE); webRequest.ServicePoint.Expect100Continue = false; SetClientId(webRequest); - string responseString; + string responseString = null; using (WebResponse response = webRequest.GetResponse()) { LogRateLimitInfo(response); - using (StreamReader reader = new StreamReader(response.GetResponseStream(), true)) { - responseString = reader.ReadToEnd(); + var responseStream = response.GetResponseStream(); + if (responseStream != null) + { + using (StreamReader reader = new StreamReader(responseStream, true)) + { + responseString = reader.ReadToEnd(); + } } } - LOG.InfoFormat("Delete result: {0}", responseString); + Log.InfoFormat("Delete result: {0}", responseString); } catch (WebException wE) { // Allow "Bad request" this means we already deleted it if (wE.Status == WebExceptionStatus.ProtocolError) { @@ -293,7 +307,7 @@ namespace GreenshotImgurPlugin { /// private static void LogHeader(IDictionary nameValues, string key) { if (nameValues.ContainsKey(key)) { - LOG.InfoFormat("key={0}", nameValues[key]); + Log.InfoFormat("key={0}", nameValues[key]); } } diff --git a/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj b/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj index 935db3188..cc52a7b68 100644 --- a/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj +++ b/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj @@ -95,6 +95,7 @@ + Always diff --git a/GreenshotJiraPlugin/JiraDestination.cs b/GreenshotJiraPlugin/JiraDestination.cs index 455fcd061..f45859841 100644 --- a/GreenshotJiraPlugin/JiraDestination.cs +++ b/GreenshotJiraPlugin/JiraDestination.cs @@ -118,12 +118,12 @@ namespace GreenshotJiraPlugin { { var surfaceContainer = new SurfaceContainer(surfaceToUpload, outputSettings, filename); await _jiraPlugin.JiraConnector.AttachAsync(_jiraIssue.Key, surfaceContainer); - surfaceToUpload.UploadURL = _jiraPlugin.JiraConnector.JiraBaseUri.AppendSegments("browse", _jiraIssue.Key).AbsoluteUri; + surfaceToUpload.UploadUrl = _jiraPlugin.JiraConnector.JiraBaseUri.AppendSegments("browse", _jiraIssue.Key).AbsoluteUri; } ); Log.DebugFormat("Uploaded to Jira {0}", _jiraIssue.Key); exportInformation.ExportMade = true; - exportInformation.Uri = surfaceToUpload.UploadURL; + exportInformation.Uri = surfaceToUpload.UploadUrl; } catch (Exception e) { MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message); } @@ -133,7 +133,7 @@ namespace GreenshotJiraPlugin { var dialogResult = jiraForm.ShowDialog(); if (dialogResult == DialogResult.OK) { try { - surfaceToUpload.UploadURL = _jiraPlugin.JiraConnector.JiraBaseUri.AppendSegments("browse", jiraForm.GetJiraIssue().Key).AbsoluteUri; + surfaceToUpload.UploadUrl = _jiraPlugin.JiraConnector.JiraBaseUri.AppendSegments("browse", jiraForm.GetJiraIssue().Key).AbsoluteUri; // Run upload in the background new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait), async () => @@ -143,7 +143,7 @@ namespace GreenshotJiraPlugin { ); Log.DebugFormat("Uploaded to Jira {0}", jiraForm.GetJiraIssue().Key); exportInformation.ExportMade = true; - exportInformation.Uri = surfaceToUpload.UploadURL; + exportInformation.Uri = surfaceToUpload.UploadUrl; } catch(Exception e) { MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message); } diff --git a/GreenshotJiraPlugin/JiraPlugin.cs b/GreenshotJiraPlugin/JiraPlugin.cs index 009ac9ce3..b61bbe907 100644 --- a/GreenshotJiraPlugin/JiraPlugin.cs +++ b/GreenshotJiraPlugin/JiraPlugin.cs @@ -23,9 +23,13 @@ using System.Windows.Forms; using Greenshot.IniFile; using Greenshot.Plugin; using System; +using System.Drawing; +using System.Drawing.Imaging; using System.Threading.Tasks; using Dapplo.Log.Facade; using GreenshotJiraPlugin.Forms; +using GreenshotPlugin.Core; +using Svg; namespace GreenshotJiraPlugin { /// @@ -95,6 +99,21 @@ namespace GreenshotJiraPlugin { _config = IniConfig.GetIniSection(); LogSettings.RegisterDefaultLogger(); + // Add a SVG converter, although it doesn't fit to the Jira plugin there is currently no other way + ImageHelper.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; + }; + return true; } diff --git a/GreenshotJiraPlugin/SvgBitmapHttpContentConverter.cs b/GreenshotJiraPlugin/SvgBitmapHttpContentConverter.cs index f8d995d99..47b3a24d1 100644 --- a/GreenshotJiraPlugin/SvgBitmapHttpContentConverter.cs +++ b/GreenshotJiraPlugin/SvgBitmapHttpContentConverter.cs @@ -22,7 +22,6 @@ using System; using System.Collections.Generic; using System.Drawing; -using System.Drawing.Imaging; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -33,8 +32,6 @@ using Dapplo.HttpExtensions.Support; using Dapplo.Log.Facade; using System.Net.Http; using System.Net.Http.Headers; -using GreenshotPlugin.Core; -using Svg; namespace GreenshotJiraPlugin { @@ -86,12 +83,12 @@ namespace GreenshotJiraPlugin using (var memoryStream = (MemoryStream) await StreamHttpContentConverter.Instance.ConvertFromHttpContentAsync(typeof(MemoryStream), httpContent, cancellationToken).ConfigureAwait(false)) { Log.Debug().WriteLine("Creating a Bitmap from the SVG."); - var bitmap = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent, 96, 96); - var svgDoc = SvgDocument.Open(memoryStream); - svgDoc.Width = Width; - svgDoc.Height = Height; - svgDoc.Draw(bitmap); - return bitmap; + var svgImage = new SvgImage(memoryStream) + { + Height = Height, + Width = Width + }; + return svgImage.Image; } } diff --git a/GreenshotJiraPlugin/SvgImage.cs b/GreenshotJiraPlugin/SvgImage.cs new file mode 100644 index 000000000..bb86e3264 --- /dev/null +++ b/GreenshotJiraPlugin/SvgImage.cs @@ -0,0 +1,118 @@ +/* + * 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 . + */ + +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using GreenshotPlugin.Core; +using Svg; + +namespace GreenshotJiraPlugin +{ + /// + /// Create an image look like of the SVG + /// + public class SvgImage : IImage + { + private readonly SvgDocument _svgDocument; + + private Image _imageClone; + + /// + /// Factory to create via a stream + /// + /// Stream + /// IImage + public static IImage FromStream(Stream stream) + { + return new SvgImage(stream); + } + + /// + /// Default constructor + /// + /// + public SvgImage(Stream stream) + { + _svgDocument = SvgDocument.Open(stream); + Height = (int)_svgDocument.ViewBox.Height; + Width = (int)_svgDocument.ViewBox.Width; + } + + /// + /// Height of the image, can be set to change + /// + public int Height { get; set; } + + /// + /// Width of the image, can be set to change. + /// + public int Width { get; set; } + + /// + /// Size of the image + /// + public Size Size => new Size(Width, Height); + + /// + /// Pixelformat of the underlying image + /// + public PixelFormat PixelFormat => Image.PixelFormat; + + /// + /// Horizontal resolution of the underlying image + /// + public float HorizontalResolution => Image.HorizontalResolution; + + /// + /// Vertical resolution of the underlying image + /// + public float VerticalResolution => Image.VerticalResolution; + + /// + /// Unterlying image, or an on demand rendered version with different attributes as the original + /// + public Image Image + { + get + { + if (_imageClone?.Height == Height && _imageClone?.Width == Width) + { + return _imageClone; + } + // Calculate new image clone + _imageClone?.Dispose(); + _imageClone = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent, 96, 96); + _svgDocument.Draw((Bitmap)_imageClone); + return _imageClone; + + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + _imageClone?.Dispose(); + } + } +} diff --git a/GreenshotPlugin/Core/AbstractDestination.cs b/GreenshotPlugin/Core/AbstractDestination.cs index 42dbf812c..b0a0bd965 100644 --- a/GreenshotPlugin/Core/AbstractDestination.cs +++ b/GreenshotPlugin/Core/AbstractDestination.cs @@ -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; diff --git a/GreenshotPlugin/Core/ClipboardHelper.cs b/GreenshotPlugin/Core/ClipboardHelper.cs index ece141ab8..84950c05c 100644 --- a/GreenshotPlugin/Core/ClipboardHelper.cs +++ b/GreenshotPlugin/Core/ClipboardHelper.cs @@ -39,9 +39,9 @@ namespace GreenshotPlugin.Core { /// Description of ClipboardHelper. /// 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(); + private static readonly ILog Log = LogManager.GetLogger(typeof(ClipboardHelper)); + private static readonly object ClipboardLockObject = new object(); + private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); 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 "; - 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 /// /// 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. /// 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 /// /// /// - 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(dibBuffer); - if (!infoHeader.IsDibV5) { - LOG.InfoFormat("Using special DIB (dibBuffer); + if (!infoHeader.IsDibV5) { + Log.InfoFormat("Using special DIB (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 /// string if there is text on the clipboard 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("") + "".Length).ToString("D8")); - sb.Replace("<<<<<<<2", (utf8EncodedHTMLString.IndexOf("")).ToString("D8")); - sb.Replace("<<<<<<<3", (utf8EncodedHTMLString.IndexOf("")+"".Length).ToString("D8")); - sb.Replace("<<<<<<<4", (utf8EncodedHTMLString.IndexOf("")).ToString("D8")); + sb.Append(utf8EncodedHtmlString); + sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("") + "".Length).ToString("D8")); + sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("")).ToString("D8")); + sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("")+"".Length).ToString("D8")); + sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("")).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("") + "".Length).ToString("D8")); - sb.Replace("<<<<<<<2", (utf8EncodedHTMLString.IndexOf("")).ToString("D8")); - sb.Replace("<<<<<<<3", (utf8EncodedHTMLString.IndexOf("")+"".Length).ToString("D8")); - sb.Replace("<<<<<<<4", (utf8EncodedHTMLString.IndexOf("")).ToString("D8")); + sb.Append(utf8EncodedHtmlString); + sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("") + "".Length).ToString("D8")); + sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("")).ToString("D8")); + sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("")+"".Length).ToString("D8")); + sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("")).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(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(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 /// /// Type /// object - 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(formats); } return new List(); @@ -784,7 +795,7 @@ EndSelection:<<<<<<<4 /// /// Type to get /// object from clipboard - 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 /// IDataObject /// Type to get /// object from IDataObject - 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 /// IDataObject /// format to get /// object from IDataObject - 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 /// /// format to get /// object from clipboard - public static Object GetClipboardData(string format) { + public static object GetClipboardData(string format) { return GetFromDataObject(GetDataObject(), format); } } diff --git a/GreenshotPlugin/Core/Func.cs b/GreenshotPlugin/Core/Func.cs new file mode 100644 index 000000000..9c15475ca --- /dev/null +++ b/GreenshotPlugin/Core/Func.cs @@ -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 . + */ + +namespace GreenshotPlugin.Core +{ + public delegate TResult Func(); + public delegate TResult Func(T arg); + public delegate TResult Func(T1 arg1, T2 arg2); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); +} diff --git a/GreenshotPlugin/Core/IImage.cs b/GreenshotPlugin/Core/IImage.cs new file mode 100644 index 000000000..ee5c9f367 --- /dev/null +++ b/GreenshotPlugin/Core/IImage.cs @@ -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 . + */ + +using System; +using System.Drawing; +using System.Drawing.Imaging; + +namespace GreenshotPlugin.Core +{ + /// + /// The image interface, this abstracts an image + /// + public interface IImage : IDisposable + { + /// + /// Height of the image, can be set to change + /// + int Height { get; set; } + + /// + /// Width of the image, can be set to change. + /// + int Width { get; set; } + + /// + /// Size of the image + /// + Size Size { get; } + + /// + /// Pixelformat of the underlying image + /// + PixelFormat PixelFormat { get; } + + /// + /// Vertical resolution of the underlying image + /// + float VerticalResolution { get; } + + /// + /// Horizontal resolution of the underlying image + /// + float HorizontalResolution { get; } + + /// + /// Unterlying image, or an on demand rendered version with different attributes as the original + /// + Image Image { get; } + } +} diff --git a/GreenshotPlugin/Core/ImageHelper.cs b/GreenshotPlugin/Core/ImageHelper.cs index 2918a6f4b..9473464f8 100644 --- a/GreenshotPlugin/Core/ImageHelper.cs +++ b/GreenshotPlugin/Core/ImageHelper.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Collections.Generic; using System.Drawing; @@ -27,6 +28,7 @@ using System.IO; using Greenshot.IniFile; using GreenshotPlugin.UnmanagedHelpers; using Greenshot.Core; +using Greenshot.Plugin; using log4net; namespace GreenshotPlugin.Core { @@ -46,30 +48,109 @@ namespace GreenshotPlugin.Core { /// Description of ImageHelper. /// public static class ImageHelper { - private static readonly ILog LOG = LogManager.GetLogger(typeof(ImageHelper)); - private static readonly CoreConfiguration conf = IniConfig.GetIniSection(); - private const int EXIF_ORIENTATION_ID = 0x0112; + private static readonly ILog Log = LogManager.GetLogger(typeof(ImageHelper)); + private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); + private const int ExifOrientationId = 0x0112; + + /// + /// This is a factory method to create a surface, set from the Greenshot main project + /// + public static Func SurfaceFactory { get; set; } + + static ImageHelper() + { + StreamConverters["greenshot"] = (stream, s) => + { + var surface = SurfaceFactory(); + return surface.GetImageForExport(); + }; + + Func defaultConverter = (stream, 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[""] = defaultConverter; + StreamConverters["gif"] = defaultConverter; + StreamConverters["bmp"] = defaultConverter; + StreamConverters["jpg"] = defaultConverter; + StreamConverters["jpeg"] = defaultConverter; + StreamConverters["png"] = defaultConverter; + StreamConverters["wmf"] = defaultConverter; + + StreamConverters["ico"] = (stream, extension) => + { + // Icon logic, try to get the Vista icon, else the biggest possible + try + { + using (Image tmpImage = ExtractVistaIcon(stream)) + { + if (tmpImage != null) + { + return Clone(tmpImage, PixelFormat.Format32bppArgb); + } + } + } + catch (Exception vistaIconException) + { + Log.Warn("Can't read icon", vistaIconException); + } + try + { + // No vista icon, try normal icon + stream.Position = 0; + // We create a copy of the bitmap, so everything else can be disposed + using (Icon tmpIcon = new Icon(stream, new Size(1024, 1024))) + { + using (Image tmpImage = tmpIcon.ToBitmap()) + { + return Clone(tmpImage, PixelFormat.Format32bppArgb); + } + } + } + catch (Exception iconException) + { + Log.Warn("Can't read icon", iconException); + } + + stream.Position = 0; + return defaultConverter(stream, extension); + }; + } + + public static IDictionary> StreamConverters { get; } = new Dictionary>(); /// /// Make sure the image is orientated correctly /// /// - public static void Orientate(Image image) { - if (!conf.ProcessEXIFOrientation) { + public static void Orientate(Image image) + { + if (!CoreConfig.ProcessEXIFOrientation) + { return; } - try { + try + { // Get the index of the orientation property. - int orientationIndex = Array.IndexOf(image.PropertyIdList, EXIF_ORIENTATION_ID); + int orientationIndex = Array.IndexOf(image.PropertyIdList, ExifOrientationId); // If there is no such property, return Unknown. - if (orientationIndex < 0) { + if (orientationIndex < 0) + { return; } - PropertyItem item = image.GetPropertyItem(EXIF_ORIENTATION_ID); + PropertyItem item = image.GetPropertyItem(ExifOrientationId); ExifOrientations orientation = (ExifOrientations)item.Value[0]; // Orient the image. - switch (orientation) { + switch (orientation) + { case ExifOrientations.Unknown: case ExifOrientations.TopLeft: break; @@ -98,20 +179,11 @@ namespace GreenshotPlugin.Core { // Set the orientation to be normal, as we rotated the image. item.Value[0] = (byte)ExifOrientations.TopLeft; image.SetPropertyItem(item); - } catch (Exception orientEx) { - LOG.Warn("Problem orientating the image: ", orientEx); } - } - - /// - /// Create a thumbnail from an image - /// - /// - /// - /// - /// - public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight) { - return CreateThumbnail(image, thumbWidth, thumbHeight, -1, -1); + catch (Exception orientEx) + { + Log.Warn("Problem orientating the image: ", orientEx); + } } /// @@ -123,53 +195,61 @@ namespace GreenshotPlugin.Core { /// /// /// - public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight, int maxWidth, int maxHeight) { - int srcWidth=image.Width; - int srcHeight=image.Height; - if (thumbHeight < 0) { + public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight, int maxWidth = -1, int maxHeight = -1) + { + int srcWidth = image.Width; + int srcHeight = image.Height; + if (thumbHeight < 0) + { thumbHeight = (int)(thumbWidth * (srcHeight / (float)srcWidth)); } - if (thumbWidth < 0) { + if (thumbWidth < 0) + { thumbWidth = (int)(thumbHeight * (srcWidth / (float)srcHeight)); } - if (maxWidth > 0 && thumbWidth > maxWidth) { + if (maxWidth > 0 && thumbWidth > maxWidth) + { thumbWidth = Math.Min(thumbWidth, maxWidth); thumbHeight = (int)(thumbWidth * (srcHeight / (float)srcWidth)); } - if (maxHeight > 0 && thumbHeight > maxHeight) { + if (maxHeight > 0 && thumbHeight > maxHeight) + { thumbHeight = Math.Min(thumbHeight, maxHeight); thumbWidth = (int)(thumbHeight * (srcWidth / (float)srcHeight)); } Bitmap bmp = new Bitmap(thumbWidth, thumbHeight); - using (Graphics graphics = Graphics.FromImage(bmp)) { + using (Graphics graphics = Graphics.FromImage(bmp)) + { graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; Rectangle rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight); - graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel); + graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel); } return bmp; } - + /// /// Crops the image to the specified rectangle /// /// Image to crop /// Rectangle with bitmap coordinates, will be "intersected" to the bitmap - public static bool Crop(ref Image image, ref Rectangle cropRectangle) { - Image returnImage = null; - if (image != null && image is Bitmap && ((image.Width * image.Height) > 0)) { - cropRectangle.Intersect(new Rectangle(0,0, image.Width, image.Height)); - if (cropRectangle.Width != 0 || cropRectangle.Height != 0) { - returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare); + public static bool Crop(ref Image image, ref Rectangle cropRectangle) + { + if (image is Bitmap && (image.Width * image.Height > 0)) + { + cropRectangle.Intersect(new Rectangle(0, 0, image.Width, image.Height)); + if (cropRectangle.Width != 0 || cropRectangle.Height != 0) + { + Image returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare); image.Dispose(); image = returnImage; return true; } } - LOG.Warn("Can't crop a null/zero size image!"); + Log.Warn("Can't crop a null/zero size image!"); return false; } @@ -180,20 +260,25 @@ namespace GreenshotPlugin.Core { /// /// /// Rectangle - private static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference) { + private static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference) + { Rectangle cropRectangle = Rectangle.Empty; Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y); Point min = new Point(int.MaxValue, int.MaxValue); Point max = new Point(int.MinValue, int.MinValue); - if (cropDifference > 0) { - for (int y = 0; y < fastBitmap.Height; y++) { - for (int x = 0; x < fastBitmap.Width; x++) { + if (cropDifference > 0) + { + for (int y = 0; y < fastBitmap.Height; y++) + { + for (int x = 0; x < fastBitmap.Width; x++) + { Color currentColor = fastBitmap.GetColorAt(x, y); int diffR = Math.Abs(currentColor.R - referenceColor.R); int diffG = Math.Abs(currentColor.G - referenceColor.G); int diffB = Math.Abs(currentColor.B - referenceColor.B); - if (((diffR + diffG + diffB)/3) <= cropDifference) { + if ((diffR + diffG + diffB) / 3 <= cropDifference) + { continue; } if (x < min.X) min.X = x; @@ -202,11 +287,16 @@ namespace GreenshotPlugin.Core { if (y > max.Y) max.Y = y; } } - } else { - for (int y = 0; y < fastBitmap.Height; y++) { - for (int x = 0; x < fastBitmap.Width; x++) { + } + else + { + for (int y = 0; y < fastBitmap.Height; y++) + { + for (int x = 0; x < fastBitmap.Width; x++) + { Color currentColor = fastBitmap.GetColorAt(x, y); - if (!referenceColor.Equals(currentColor)) { + if (!referenceColor.Equals(currentColor)) + { continue; } if (x < min.X) min.X = x; @@ -217,8 +307,10 @@ namespace GreenshotPlugin.Core { } } - if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1)))) { - if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) { + if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1)))) + { + if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) + { cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); } } @@ -231,89 +323,61 @@ namespace GreenshotPlugin.Core { /// /// /// Rectangle - public static Rectangle FindAutoCropRectangle(Image image, int cropDifference) { + public static Rectangle FindAutoCropRectangle(Image image, int cropDifference) + { Rectangle cropRectangle = Rectangle.Empty; - Rectangle currentRectangle; - List checkPoints = new List(); + var checkPoints = new List + { + new Point(0, 0), + new Point(0, image.Height - 1), + new Point(image.Width - 1, 0), + new Point(image.Width - 1, image.Height - 1) + }; // Top Left - checkPoints.Add(new Point(0, 0)); // Bottom Left - checkPoints.Add(new Point(0, image.Height - 1)); // Top Right - checkPoints.Add(new Point(image.Width - 1, 0)); // Bottom Right - checkPoints.Add(new Point(image.Width - 1, image.Height - 1)); - using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap)image)) { + using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap)image)) + { // find biggest area - foreach(Point checkPoint in checkPoints) { - currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference); - if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height) { + foreach (Point checkPoint in checkPoints) + { + var currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference); + if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height) + { cropRectangle = currentRectangle; } } } return cropRectangle; } - + /// /// Load an image from file /// /// /// - public static Image LoadImage(string filename) { - if (string.IsNullOrEmpty(filename)) { + public static Image LoadImage(string filename) + { + if (string.IsNullOrEmpty(filename)) + { return null; } - if (!File.Exists(filename)) { + if (!File.Exists(filename)) + { return null; } - Image fileImage = null; - LOG.InfoFormat("Loading image from file {0}", filename); + Image fileImage; + Log.InfoFormat("Loading image from file {0}", filename); // Fixed lock problem Bug #3431881 - using (Stream imageFileStream = File.OpenRead(filename)) { - // And fixed problem that the bitmap stream is disposed... by Cloning the image - // This also ensures the bitmap is correctly created - - if (filename.EndsWith(".ico")) { - // Icon logic, try to get the Vista icon, else the biggest possible - try { - using (Image tmpImage = ExtractVistaIcon(imageFileStream)) { - if (tmpImage != null) { - fileImage = Clone(tmpImage); - } - } - } catch (Exception vistaIconException) { - LOG.Warn("Can't read icon from " + filename, vistaIconException); - } - if (fileImage == null) { - try { - // No vista icon, try normal icon - imageFileStream.Position = 0; - // We create a copy of the bitmap, so everything else can be disposed - using (Icon tmpIcon = new Icon(imageFileStream, new Size(1024,1024))) { - using (Image tmpImage = tmpIcon.ToBitmap()) { - fileImage = Clone(tmpImage); - } - } - } catch (Exception iconException) { - LOG.Warn("Can't read icon from " + filename, iconException); - } - } - } - if (fileImage == null) { - // We create a copy of the bitmap, so everything else can be disposed - imageFileStream.Position = 0; - using (Image tmpImage = Image.FromStream(imageFileStream, true, true)) { - LOG.DebugFormat("Loaded {0} with Size {1}x{2} and PixelFormat {3}", filename, tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); - fileImage = Clone(tmpImage); - } - } + using (Stream imageFileStream = File.OpenRead(filename)) + { + fileImage = FromStream(imageFileStream); } - 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); + 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); } - // Make sure the orientation is set correctly so Greenshot can process the image correctly - Orientate(fileImage); return fileImage; } @@ -324,22 +388,26 @@ namespace GreenshotPlugin.Core { /// /// Stream with the icon information /// Bitmap with the Vista Icon (256x256) - private static Bitmap ExtractVistaIcon(Stream iconStream) { - const int SizeICONDIR = 6; - const int SizeICONDIRENTRY = 16; + private static Bitmap ExtractVistaIcon(Stream iconStream) + { + const int sizeIconDir = 6; + const int sizeIconDirEntry = 16; Bitmap bmpPngExtracted = null; - try { + try + { byte[] srcBuf = new byte[iconStream.Length]; iconStream.Read(srcBuf, 0, (int)iconStream.Length); int iCount = BitConverter.ToInt16(srcBuf, 4); - for (int iIndex=0; iIndexIndex of the icon /// true if the large icon is wanted /// Icon - public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge) { + public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge) + { IntPtr large; IntPtr small; Shell32.ExtractIconEx(location, index, out large, out small, 1); Icon returnIcon = null; bool isLarge = false; bool isSmall = false; - try { - if (takeLarge && !IntPtr.Zero.Equals(large)) { + try + { + if (takeLarge && !IntPtr.Zero.Equals(large)) + { returnIcon = Icon.FromHandle(large); isLarge = true; - } else if (!IntPtr.Zero.Equals(small)) { + } + else if (!IntPtr.Zero.Equals(small)) + { returnIcon = Icon.FromHandle(small); isSmall = true; - } else if (!IntPtr.Zero.Equals(large)) { + } + else if (!IntPtr.Zero.Equals(large)) + { returnIcon = Icon.FromHandle(large); isLarge = true; } - } finally { - if (isLarge && !IntPtr.Zero.Equals(small)) { + } + finally + { + if (isLarge && !IntPtr.Zero.Equals(small)) + { User32.DestroyIcon(small); } - if (isSmall && !IntPtr.Zero.Equals(large)) { + if (isSmall && !IntPtr.Zero.Equals(large)) + { User32.DestroyIcon(large); } } @@ -394,7 +475,8 @@ namespace GreenshotPlugin.Core { /// /// Location of the EXE or DLL /// - public static int CountAssociatedIcons(string location) { + public static int CountAssociatedIcons(string location) + { IntPtr large; IntPtr small; return Shell32.ExtractIconEx(location, -1, out large, out small, 0); @@ -407,8 +489,9 @@ namespace GreenshotPlugin.Core { /// IEffect /// /// Bitmap - public static Image ApplyEffect(Image sourceImage, IEffect effect, Matrix matrix) { - List effects = new List {effect}; + public static Image ApplyEffect(Image sourceImage, IEffect effect, Matrix matrix) + { + List effects = new List { effect }; return ApplyEffects(sourceImage, effects, matrix); } @@ -419,13 +502,17 @@ namespace GreenshotPlugin.Core { /// List of IEffect /// /// Bitmap - public static Image ApplyEffects(Image sourceImage, List effects, Matrix matrix) { + public static Image ApplyEffects(Image sourceImage, List effects, Matrix matrix) + { Image currentImage = sourceImage; bool disposeImage = false; - foreach (IEffect effect in effects) { + foreach (IEffect effect in effects) + { Image tmpImage = effect.Apply(currentImage, matrix); - if (tmpImage != null) { - if (disposeImage) { + if (tmpImage != null) + { + if (disposeImage) + { currentImage.Dispose(); } currentImage = tmpImage; @@ -442,10 +529,12 @@ namespace GreenshotPlugin.Core { /// /// Path to draw to /// Points for the lines to draw - private static void DrawLines(GraphicsPath path, List points) { + private static void DrawLines(GraphicsPath path, List points) + { path.AddLine(points[0], points[1]); - for (int i = 0; i < points.Count-1; i++) { - path.AddLine(points[i], points[i+1]); + for (int i = 0; i < points.Count - 1; i++) + { + path.AddLine(points[i], points[i + 1]); } } @@ -458,9 +547,11 @@ namespace GreenshotPlugin.Core { /// How wide is a vertical tooth /// bool[] with information on if the edge needs torn or not. Order is clockwise: 0=top,1=right,2=bottom,3=left /// Changed bitmap - public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange, bool[] edges) { + 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); - using (GraphicsPath path = new GraphicsPath()) { + using (GraphicsPath path = new GraphicsPath()) + { Random random = new Random(); int horizontalRegions = (int)Math.Round((float)sourceImage.Width / horizontalToothRange); int verticalRegions = (int)Math.Round((float)sourceImage.Height / verticalToothRange); @@ -472,53 +563,72 @@ namespace GreenshotPlugin.Core { List points = new List(); - if (edges[0]) { + if (edges[0]) + { // calculate starting point only if the left edge is torn - if (!edges[3]) { + if (!edges[3]) + { points.Add(topLeft); - } else { + } + else + { points.Add(new Point(random.Next(1, toothHeight), random.Next(1, toothHeight))); } - for (int i = 1; i < horizontalRegions-1; i++) { - points.Add(new Point(i*horizontalToothRange, random.Next(1, toothHeight))); + for (int i = 1; i < horizontalRegions - 1; i++) + { + points.Add(new Point(i * horizontalToothRange, random.Next(1, toothHeight))); } points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), random.Next(1, toothHeight))); - } else { + } + else + { // set start & endpoint to be the default "whole-line" points.Add(topLeft); points.Add(topRight); } // Right - if (edges[1]) { - for (int i = 1; i < verticalRegions-1; i++) { + if (edges[1]) + { + for (int i = 1; i < verticalRegions - 1; i++) + { points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), i * verticalToothRange)); } points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight))); - } else { + } + else + { // correct previous ending point points[points.Count - 1] = topRight; // set endpoint to be the default "whole-line" points.Add(bottomRight); } // Bottom - if (edges[2]) { - for (int i = 1; i < horizontalRegions -1; i++) { + if (edges[2]) + { + for (int i = 1; i < horizontalRegions - 1; i++) + { points.Add(new Point(sourceImage.Width - i * horizontalToothRange, sourceImage.Height - random.Next(1, toothHeight))); } points.Add(new Point(random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight))); - } else { + } + else + { // correct previous ending point points[points.Count - 1] = bottomRight; // set endpoint to be the default "whole-line" points.Add(bottomLeft); } // Left - if (edges[3]) { + if (edges[3]) + { // One fewer as the end point is the starting point - for (int i = 1; i < verticalRegions -1; i++) { + for (int i = 1; i < verticalRegions - 1; i++) + { points.Add(new Point(random.Next(1, toothHeight), points[points.Count - 1].Y - verticalToothRange)); } - } else { + } + else + { // correct previous ending point points[points.Count - 1] = bottomLeft; // set endpoint to be the default "whole-line" @@ -532,12 +642,14 @@ namespace GreenshotPlugin.Core { path.CloseFigure(); // Draw the created figure with the original image by using a TextureBrush so we have anti-aliasing - using (Graphics graphics = Graphics.FromImage(returnImage)) { + using (Graphics graphics = Graphics.FromImage(returnImage)) + { graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - using (Brush brush = new TextureBrush(sourceImage)) { + using (Brush brush = new TextureBrush(sourceImage)) + { // Important note: If the target wouldn't be at 0,0 we need to translate-transform!! graphics.FillPath(brush, path); } @@ -551,9 +663,11 @@ namespace GreenshotPlugin.Core { /// /// Bitmap to blur /// Must be ODD! - public static void ApplyBoxBlur(Bitmap destinationBitmap, int range) { + public static void ApplyBoxBlur(Bitmap destinationBitmap, int range) + { // We only need one fastbitmap as we use it as source and target (the reading is done for one line H/V, writing after "parsing" one line H/V) - using (IFastBitmap fastBitmap = FastBitmap.Create(destinationBitmap)) { + using (IFastBitmap fastBitmap = FastBitmap.Create(destinationBitmap)) + { ApplyBoxBlur(fastBitmap, range); } } @@ -563,24 +677,30 @@ namespace GreenshotPlugin.Core { /// /// IFastBitmap to blur /// Must be ODD! - public static void ApplyBoxBlur(IFastBitmap fastBitmap, int range) { + public static void ApplyBoxBlur(IFastBitmap fastBitmap, int range) + { // Range must be odd! - if ((range & 1) == 0) { + if ((range & 1) == 0) + { range++; } - if (range <= 1) { + if (range <= 1) + { return; } // Box blurs are frequently used to approximate a Gaussian blur. // By the central limit theorem, if applied 3 times on the same image, a box blur approximates the Gaussian kernel to within about 3%, yielding the same result as a quadratic convolution kernel. // This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur. // (Might also be a mistake in our blur, but for now it looks great) - if (fastBitmap.HasAlphaChannel) { + if (fastBitmap.HasAlphaChannel) + { BoxBlurHorizontalAlpha(fastBitmap, range); BoxBlurVerticalAlpha(fastBitmap, range); BoxBlurHorizontalAlpha(fastBitmap, range); BoxBlurVerticalAlpha(fastBitmap, range); - } else { + } + else + { BoxBlurHorizontal(fastBitmap, range); BoxBlurVertical(fastBitmap, range); BoxBlurHorizontal(fastBitmap, range); @@ -593,21 +713,26 @@ namespace GreenshotPlugin.Core { /// /// Target BitmapBuffer /// Range must be odd! - private static void BoxBlurHorizontal(IFastBitmap targetFastBitmap, int range) { - if (targetFastBitmap.HasAlphaChannel) { + private static void BoxBlurHorizontal(IFastBitmap targetFastBitmap, int range) + { + if (targetFastBitmap.HasAlphaChannel) + { throw new NotSupportedException("BoxBlurHorizontal should NOT be called for bitmaps with alpha channel"); } int halfRange = range / 2; Color[] newColors = new Color[targetFastBitmap.Width]; byte[] tmpColor = new byte[3]; - for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { + for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) + { int hits = 0; int r = 0; int g = 0; int b = 0; - for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) { + for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) + { int oldPixel = x - halfRange - 1; - if (oldPixel >= targetFastBitmap.Left) { + if (oldPixel >= targetFastBitmap.Left) + { targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); r -= tmpColor[FastBitmap.ColorIndexR]; g -= tmpColor[FastBitmap.ColorIndexG]; @@ -616,7 +741,8 @@ namespace GreenshotPlugin.Core { } int newPixel = x + halfRange; - if (newPixel < targetFastBitmap.Right) { + if (newPixel < targetFastBitmap.Right) + { targetFastBitmap.GetColorAt(newPixel, y, tmpColor); r += tmpColor[FastBitmap.ColorIndexR]; g += tmpColor[FastBitmap.ColorIndexG]; @@ -624,11 +750,13 @@ namespace GreenshotPlugin.Core { hits++; } - if (x >= targetFastBitmap.Left) { + if (x >= targetFastBitmap.Left) + { newColors[x - targetFastBitmap.Left] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } - for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { + for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) + { targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]); } } @@ -638,22 +766,27 @@ namespace GreenshotPlugin.Core { /// /// Target BitmapBuffer /// Range must be odd! - private static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range) { - if (!targetFastBitmap.HasAlphaChannel) { + private static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range) + { + if (!targetFastBitmap.HasAlphaChannel) + { throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel"); } int halfRange = range / 2; Color[] newColors = new Color[targetFastBitmap.Width]; byte[] tmpColor = new byte[4]; - for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { + for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) + { int hits = 0; int a = 0; int r = 0; int g = 0; int b = 0; - for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) { + for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) + { int oldPixel = x - halfRange - 1; - if (oldPixel >= targetFastBitmap.Left) { + if (oldPixel >= targetFastBitmap.Left) + { targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); a -= tmpColor[FastBitmap.ColorIndexA]; r -= tmpColor[FastBitmap.ColorIndexR]; @@ -663,7 +796,8 @@ namespace GreenshotPlugin.Core { } int newPixel = x + halfRange; - if (newPixel < targetFastBitmap.Right) { + if (newPixel < targetFastBitmap.Right) + { targetFastBitmap.GetColorAt(newPixel, y, tmpColor); a += tmpColor[FastBitmap.ColorIndexA]; r += tmpColor[FastBitmap.ColorIndexR]; @@ -672,11 +806,13 @@ namespace GreenshotPlugin.Core { hits++; } - if (x >= targetFastBitmap.Left) { + if (x >= targetFastBitmap.Left) + { newColors[x - targetFastBitmap.Left] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } - for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { + for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) + { targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]); } } @@ -687,21 +823,26 @@ namespace GreenshotPlugin.Core { /// /// BitmapBuffer which previously was created with BoxBlurHorizontal /// Range must be odd! - private static void BoxBlurVertical(IFastBitmap targetFastBitmap, int range) { - if (targetFastBitmap.HasAlphaChannel) { + private static void BoxBlurVertical(IFastBitmap targetFastBitmap, int range) + { + if (targetFastBitmap.HasAlphaChannel) + { throw new NotSupportedException("BoxBlurVertical should NOT be called for bitmaps with alpha channel"); } int halfRange = range / 2; Color[] newColors = new Color[targetFastBitmap.Height]; byte[] tmpColor = new byte[4]; - for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { + for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) + { int hits = 0; int r = 0; int g = 0; int b = 0; - for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) { + for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) + { int oldPixel = y - halfRange - 1; - if (oldPixel >= targetFastBitmap.Top) { + if (oldPixel >= targetFastBitmap.Top) + { targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); r -= tmpColor[FastBitmap.ColorIndexR]; g -= tmpColor[FastBitmap.ColorIndexG]; @@ -710,7 +851,8 @@ namespace GreenshotPlugin.Core { } int newPixel = y + halfRange; - if (newPixel < targetFastBitmap.Bottom) { + if (newPixel < targetFastBitmap.Bottom) + { targetFastBitmap.GetColorAt(x, newPixel, tmpColor); r += tmpColor[FastBitmap.ColorIndexR]; g += tmpColor[FastBitmap.ColorIndexG]; @@ -718,12 +860,14 @@ namespace GreenshotPlugin.Core { hits++; } - if (y >= targetFastBitmap.Top) { + if (y >= targetFastBitmap.Top) + { newColors[y - targetFastBitmap.Top] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } - for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { + for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) + { targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]); } } @@ -734,23 +878,28 @@ namespace GreenshotPlugin.Core { /// /// BitmapBuffer which previously was created with BoxBlurHorizontal /// Range must be odd! - private static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range) { - if (!targetFastBitmap.HasAlphaChannel) { + private static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range) + { + if (!targetFastBitmap.HasAlphaChannel) + { throw new NotSupportedException("BoxBlurVerticalAlpha should be called for bitmaps with alpha channel"); } int halfRange = range / 2; Color[] newColors = new Color[targetFastBitmap.Height]; byte[] tmpColor = new byte[4]; - for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { + for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) + { int hits = 0; int a = 0; int r = 0; int g = 0; int b = 0; - for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) { + for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) + { int oldPixel = y - halfRange - 1; - if (oldPixel >= targetFastBitmap.Top) { + if (oldPixel >= targetFastBitmap.Top) + { targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); a -= tmpColor[FastBitmap.ColorIndexA]; r -= tmpColor[FastBitmap.ColorIndexR]; @@ -760,7 +909,8 @@ namespace GreenshotPlugin.Core { } int newPixel = y + halfRange; - if (newPixel < targetFastBitmap.Bottom) { + if (newPixel < targetFastBitmap.Bottom) + { //int colorg = pixels[index + newPixelOffset]; targetFastBitmap.GetColorAt(x, newPixel, tmpColor); a += tmpColor[FastBitmap.ColorIndexA]; @@ -770,12 +920,14 @@ namespace GreenshotPlugin.Core { hits++; } - if (y >= targetFastBitmap.Top) { + if (y >= targetFastBitmap.Top) + { newColors[y - targetFastBitmap.Top] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } - for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { + for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) + { targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]); } } @@ -790,11 +942,15 @@ namespace GreenshotPlugin.Core { /// /// /// - public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert) { + public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert) + { Rectangle myRect; - if (invert) { + if (invert) + { myRect = new Rectangle(0, 0, applySize.Width, applySize.Height); - } else { + } + else + { Rectangle applyRect = new Rectangle(0, 0, applySize.Width, applySize.Height); myRect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height); myRect.Intersect(applyRect); @@ -812,51 +968,63 @@ namespace GreenshotPlugin.Core { /// /// The transform matrix which describes how the elements need to be transformed to stay at the same location /// Bitmap with the shadow, is bigger than the sourceBitmap!! - public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, Matrix matrix, PixelFormat targetPixelformat) { + public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, Matrix matrix, PixelFormat targetPixelformat) + { Point offset = shadowOffset; offset.X += shadowSize - 1; offset.Y += shadowSize - 1; matrix.Translate(offset.X, offset.Y, MatrixOrder.Append); // Create a new "clean" image - Bitmap returnImage = CreateEmpty(sourceBitmap.Width + (shadowSize * 2), sourceBitmap.Height + (shadowSize * 2), targetPixelformat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution); + Bitmap returnImage = CreateEmpty(sourceBitmap.Width + shadowSize * 2, sourceBitmap.Height + shadowSize * 2, targetPixelformat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution); // Make sure the shadow is odd, there is no reason for an even blur! - if ((shadowSize & 1) == 0) { + if ((shadowSize & 1) == 0) + { shadowSize++; } - bool useGDIBlur = GDIplus.IsBlurPossible(shadowSize); + bool useGdiBlur = GDIplus.IsBlurPossible(shadowSize); // Create "mask" for the shadow - ColorMatrix maskMatrix = new ColorMatrix(); - maskMatrix.Matrix00 = 0; - maskMatrix.Matrix11 = 0; - maskMatrix.Matrix22 = 0; - if (useGDIBlur) { + ColorMatrix maskMatrix = new ColorMatrix + { + Matrix00 = 0, + Matrix11 = 0, + Matrix22 = 0 + }; + if (useGdiBlur) + { maskMatrix.Matrix33 = darkness + 0.1f; - } else { + } + else + { maskMatrix.Matrix33 = darkness; } Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size); ApplyColorMatrix((Bitmap)sourceBitmap, Rectangle.Empty, returnImage, shadowRectangle, maskMatrix); // blur "shadow", apply to whole new image - if (useGDIBlur) { + if (useGdiBlur) + { // Use GDI Blur Rectangle newImageRectangle = new Rectangle(0, 0, returnImage.Width, returnImage.Height); - GDIplus.ApplyBlur(returnImage, newImageRectangle, shadowSize+1, false); - } else { + GDIplus.ApplyBlur(returnImage, newImageRectangle, shadowSize + 1, false); + } + else + { // try normal software blur //returnImage = CreateBlur(returnImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle); ApplyBoxBlur(returnImage, shadowSize); } // Draw the original image over the shadow - using (Graphics graphics = Graphics.FromImage(returnImage)) { + using (Graphics graphics = Graphics.FromImage(returnImage)) + { // Make sure we draw with the best quality! graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; // draw original with a TextureBrush so we have nice antialiasing! - using (Brush textureBrush = new TextureBrush(sourceBitmap, WrapMode.Clamp)) { + using (Brush textureBrush = new TextureBrush(sourceBitmap, WrapMode.Clamp)) + { // We need to do a translate-transform otherwise the image is wrapped graphics.TranslateTransform(offset.X, offset.Y); graphics.FillRectangle(textureBrush, 0, 0, sourceBitmap.Width, sourceBitmap.Height); @@ -870,14 +1038,15 @@ namespace GreenshotPlugin.Core { /// /// Bitmap to create a negative off /// Negative bitmap - public static Bitmap CreateNegative(Image sourceImage) { + public static Bitmap CreateNegative(Image sourceImage) + { Bitmap clone = (Bitmap)Clone(sourceImage); - ColorMatrix invertMatrix = new ColorMatrix(new[] { - new float[] {-1, 0, 0, 0, 0}, - new float[] {0, -1, 0, 0, 0}, - new float[] {0, 0, -1, 0, 0}, - new float[] {0, 0, 0, 1, 0}, - new float[] {1, 1, 1, 1, 1} + ColorMatrix invertMatrix = new ColorMatrix(new[] { + new float[] {-1, 0, 0, 0, 0}, + new float[] {0, -1, 0, 0, 0}, + new float[] {0, 0, -1, 0, 0}, + new float[] {0, 0, 0, 1, 0}, + new float[] {1, 1, 1, 1, 1} }); ApplyColorMatrix(clone, invertMatrix); return clone; @@ -887,7 +1056,8 @@ namespace GreenshotPlugin.Core { /// /// Image to apply matrix to /// ColorMatrix to apply - public static void ApplyColorMatrix(Bitmap source, ColorMatrix colorMatrix) { + public static void ApplyColorMatrix(Bitmap source, ColorMatrix colorMatrix) + { ApplyColorMatrix(source, Rectangle.Empty, source, Rectangle.Empty, colorMatrix); } @@ -899,20 +1069,23 @@ namespace GreenshotPlugin.Core { /// Rectangle to copy to /// Image to copy to /// ColorMatrix to apply - public static void ApplyColorMatrix(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ColorMatrix colorMatrix) { - using (ImageAttributes imageAttributes = new ImageAttributes()) { + public static void ApplyColorMatrix(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ColorMatrix colorMatrix) + { + using (ImageAttributes imageAttributes = new ImageAttributes()) + { imageAttributes.ClearColorMatrix(); imageAttributes.SetColorMatrix(colorMatrix); ApplyImageAttributes(source, sourceRect, dest, destRect, imageAttributes); } } - + /// /// Apply image attributes to the image /// /// Image to apply matrix to /// ImageAttributes to apply - public static void ApplyColorMatrix(Bitmap source, ImageAttributes imageAttributes) { + public static void ApplyColorMatrix(Bitmap source, ImageAttributes imageAttributes) + { ApplyImageAttributes(source, Rectangle.Empty, source, Rectangle.Empty, imageAttributes); } @@ -924,17 +1097,22 @@ namespace GreenshotPlugin.Core { /// Rectangle to copy to /// Image to copy to /// ImageAttributes to apply - public static void ApplyImageAttributes(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ImageAttributes imageAttributes) { - if (sourceRect == Rectangle.Empty) { + public static void ApplyImageAttributes(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ImageAttributes imageAttributes) + { + if (sourceRect == Rectangle.Empty) + { sourceRect = new Rectangle(0, 0, source.Width, source.Height); } - if (dest == null) { + if (dest == null) + { dest = source; } - if (destRect == Rectangle.Empty) { + if (destRect == Rectangle.Empty) + { destRect = new Rectangle(0, 0, dest.Width, dest.Height); } - using (Graphics graphics = Graphics.FromImage(dest)) { + using (Graphics graphics = Graphics.FromImage(dest)) + { // Make sure we draw with the best quality! graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; @@ -945,19 +1123,23 @@ namespace GreenshotPlugin.Core { graphics.DrawImage(source, destRect, sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height, GraphicsUnit.Pixel, imageAttributes); } } - + /// /// Returns a b/w of Bitmap /// /// Bitmap to create a b/w of /// Threshold for monochrome filter (0 - 255), lower value means less black /// b/w bitmap - public static Bitmap CreateMonochrome(Image sourceImage, byte threshold) { - using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage, sourceImage.PixelFormat)) { - for (int y = 0; y < fastBitmap.Height; y++) { - for (int x = 0; x < fastBitmap.Width; x++) { + public static Bitmap CreateMonochrome(Image sourceImage, byte threshold) + { + using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage, sourceImage.PixelFormat)) + { + for (int y = 0; y < fastBitmap.Height; y++) + { + for (int x = 0; x < fastBitmap.Width; x++) + { Color color = fastBitmap.GetColorAt(x, y); - int colorBrightness = ((color.R + color.G + color.B) / 3 > threshold) ? 255 : 0; + int colorBrightness = (color.R + color.G + color.B) / 3 > threshold ? 255 : 0; Color monoColor = Color.FromArgb(color.A, colorBrightness, colorBrightness, colorBrightness); fastBitmap.SetColorAt(x, y, monoColor); } @@ -975,22 +1157,26 @@ namespace GreenshotPlugin.Core { /// What pixel format must the returning bitmap have /// The transform matrix which describes how the elements need to be transformed to stay at the same location /// Bitmap with the shadow, is bigger than the sourceBitmap!! - public static Image CreateBorder(Image sourceImage, int borderSize, Color borderColor, PixelFormat targetPixelformat, Matrix matrix) { + public static Image CreateBorder(Image sourceImage, int borderSize, Color borderColor, PixelFormat targetPixelformat, Matrix matrix) + { // "return" the shifted offset, so the caller can e.g. move elements Point offset = new Point(borderSize, borderSize); matrix.Translate(offset.X, offset.Y, MatrixOrder.Append); // Create a new "clean" image - Bitmap newImage = CreateEmpty(sourceImage.Width + (borderSize * 2), sourceImage.Height + (borderSize * 2), targetPixelformat, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); - using (Graphics graphics = Graphics.FromImage(newImage)) { + Bitmap newImage = CreateEmpty(sourceImage.Width + borderSize * 2, sourceImage.Height + borderSize * 2, targetPixelformat, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + using (Graphics graphics = Graphics.FromImage(newImage)) + { // Make sure we draw with the best quality! graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - using (GraphicsPath path = new GraphicsPath()) { - path.AddRectangle(new Rectangle(borderSize >> 1, borderSize >> 1, newImage.Width - (borderSize), newImage.Height - (borderSize))); - using (Pen pen = new Pen(borderColor, borderSize)) { + using (GraphicsPath path = new GraphicsPath()) + { + path.AddRectangle(new Rectangle(borderSize >> 1, borderSize >> 1, newImage.Width - borderSize, newImage.Height - borderSize)); + using (Pen pen = new Pen(borderColor, borderSize)) + { pen.LineJoin = LineJoin.Round; pen.StartCap = LineCap.Round; pen.EndCap = LineCap.Round; @@ -998,7 +1184,8 @@ namespace GreenshotPlugin.Core { } } // draw original with a TextureBrush so we have nice antialiasing! - using (Brush textureBrush = new TextureBrush(sourceImage, WrapMode.Clamp)) { + using (Brush textureBrush = new TextureBrush(sourceImage, WrapMode.Clamp)) + { // We need to do a translate-tranform otherwise the image is wrapped graphics.TranslateTransform(offset.X, offset.Y); graphics.FillRectangle(textureBrush, 0, 0, sourceImage.Width, sourceImage.Height); @@ -1014,7 +1201,8 @@ namespace GreenshotPlugin.Core { /// /// /// ImageAttributes - public static ImageAttributes CreateAdjustAttributes(float brightness, float contrast, float gamma) { + public static ImageAttributes CreateAdjustAttributes(float brightness, float contrast, float gamma) + { float adjustedBrightness = brightness - 1.0f; ColorMatrix applyColorMatrix = new ColorMatrix( new[] @@ -1043,12 +1231,14 @@ namespace GreenshotPlugin.Core { /// /// /// Bitmap with grayscale - public static Image Adjust(Image sourceImage, float brightness, float contrast, float gamma) { + public static Image Adjust(Image sourceImage, float brightness, float contrast, float gamma) + { //create a blank bitmap the same size as original // If using 8bpp than the following exception comes: A Graphics object cannot be created from an image that has an indexed pixel format. Bitmap newBitmap = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format24bppRgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); - using (ImageAttributes adjustAttributes = CreateAdjustAttributes(brightness, contrast, gamma)) { - ApplyImageAttributes((Bitmap)sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, adjustAttributes); + using (ImageAttributes adjustAttributes = CreateAdjustAttributes(brightness, contrast, gamma)) + { + ApplyImageAttributes((Bitmap)sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, adjustAttributes); } return newBitmap; } @@ -1058,9 +1248,10 @@ namespace GreenshotPlugin.Core { /// /// Original bitmap /// Bitmap with grayscale - public static Image CreateGrayscale(Image sourceImage) { + public static Image CreateGrayscale(Image sourceImage) + { Bitmap clone = (Bitmap)Clone(sourceImage); - ColorMatrix grayscaleMatrix = new ColorMatrix( new[] + ColorMatrix grayscaleMatrix = new ColorMatrix(new[] { new[] {.3f, .3f, .3f, 0, 0}, new[] {.59f, .59f, .59f, 0, 0}, @@ -1077,7 +1268,8 @@ namespace GreenshotPlugin.Core { /// /// bitmap to check /// bool if we support it - public static bool SupportsPixelFormat(Image image) { + public static bool SupportsPixelFormat(Image image) + { return SupportsPixelFormat(image.PixelFormat); } @@ -1086,11 +1278,12 @@ namespace GreenshotPlugin.Core { /// /// PixelFormat to check /// bool if we support it - public static bool SupportsPixelFormat(PixelFormat pixelformat) { - return (pixelformat.Equals(PixelFormat.Format32bppArgb) || + public static bool SupportsPixelFormat(PixelFormat pixelformat) + { + return pixelformat.Equals(PixelFormat.Format32bppArgb) || pixelformat.Equals(PixelFormat.Format32bppPArgb) || pixelformat.Equals(PixelFormat.Format32bppRgb) || - pixelformat.Equals(PixelFormat.Format24bppRgb)); + pixelformat.Equals(PixelFormat.Format24bppRgb); } /// @@ -1098,8 +1291,10 @@ namespace GreenshotPlugin.Core { /// /// Image to clone /// Bitmap with clone image data - public static Image Clone(Image sourceImage) { - if (sourceImage is Metafile) { + public static Image Clone(Image sourceImage) + { + if (sourceImage is Metafile) + { return (Image)sourceImage.Clone(); } return CloneArea(sourceImage, Rectangle.Empty, PixelFormat.DontCare); @@ -1111,7 +1306,8 @@ namespace GreenshotPlugin.Core { /// Image to clone /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported) /// Bitmap with clone image data - public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat) { + public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat) + { return CloneArea(sourceBitmap, Rectangle.Empty, targetFormat); } @@ -1126,35 +1322,42 @@ namespace GreenshotPlugin.Core { /// Rectangle to copy from the source, use Rectangle.Empty for all /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported) /// - public static Bitmap CloneArea(Image sourceImage, Rectangle sourceRect, PixelFormat targetFormat) { - Bitmap newImage = null; + public static Bitmap CloneArea(Image sourceImage, Rectangle sourceRect, PixelFormat targetFormat) + { + Bitmap newImage; Rectangle bitmapRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height); // Make sure the source is not Rectangle.Empty - if (Rectangle.Empty.Equals(sourceRect)) { + if (Rectangle.Empty.Equals(sourceRect)) + { sourceRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height); - } else { + } + else + { sourceRect.Intersect(bitmapRect); } // If no pixelformat is supplied - if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat) { - if (SupportsPixelFormat(sourceImage.PixelFormat)) { + if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat) + { + if (SupportsPixelFormat(sourceImage.PixelFormat)) + { targetFormat = sourceImage.PixelFormat; - } else if (Image.IsAlphaPixelFormat(sourceImage.PixelFormat)) { + } + else if (Image.IsAlphaPixelFormat(sourceImage.PixelFormat)) + { targetFormat = PixelFormat.Format32bppArgb; - } else { + } + else + { targetFormat = PixelFormat.Format24bppRgb; } } // check the target format - if (!SupportsPixelFormat(targetFormat)) { - if (Image.IsAlphaPixelFormat(targetFormat)) { - targetFormat = PixelFormat.Format32bppArgb; - } else { - targetFormat = PixelFormat.Format24bppRgb; - } + if (!SupportsPixelFormat(targetFormat)) + { + targetFormat = Image.IsAlphaPixelFormat(targetFormat) ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb; } bool destinationIsTransparent = Image.IsAlphaPixelFormat(targetFormat); @@ -1162,64 +1365,77 @@ namespace GreenshotPlugin.Core { bool fromTransparentToNon = !destinationIsTransparent && sourceIsTransparent; bool isBitmap = sourceImage is Bitmap; bool isAreaEqual = sourceRect.Equals(bitmapRect); - if (isAreaEqual || fromTransparentToNon || !isBitmap) { + if (isAreaEqual || fromTransparentToNon || !isBitmap) + { // Rule 1: if the areas are equal, always copy ourselves newImage = new Bitmap(bitmapRect.Width, bitmapRect.Height, targetFormat); // Make sure both images have the same resolution newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution); - - using (Graphics graphics = Graphics.FromImage(newImage)) { - if (fromTransparentToNon) { + + using (Graphics graphics = Graphics.FromImage(newImage)) + { + if (fromTransparentToNon) + { // Rule 2: Make sure the background color is white graphics.Clear(Color.White); } // decide fastest copy method - if (isAreaEqual) { + if (isAreaEqual) + { graphics.DrawImageUnscaled(sourceImage, 0, 0); - } else { + } + else + { graphics.DrawImage(sourceImage, 0, 0, sourceRect, GraphicsUnit.Pixel); } } - } else { + } + else + { // Let GDI+ decide how to convert, need to test what is quicker... newImage = (sourceImage as Bitmap).Clone(sourceRect, targetFormat); // Make sure both images have the same resolution newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution); } // Clone property items (EXIF information etc) - if (sourceImage.PropertyItems != null) { - foreach (var propertyItem in sourceImage.PropertyItems) { - try { - newImage.SetPropertyItem(propertyItem); - } catch (Exception ex) { - LOG.Warn("Problem cloning a propertyItem.", ex); - } + foreach (var propertyItem in sourceImage.PropertyItems) + { + try + { + newImage.SetPropertyItem(propertyItem); + } + catch (Exception ex) + { + Log.Warn("Problem cloning a propertyItem.", ex); } } return newImage; } - + /// /// Rotate the bitmap /// /// /// /// - public static Image RotateFlip(Image sourceImage, RotateFlipType rotateFlipType) { + public static Image RotateFlip(Image sourceImage, RotateFlipType rotateFlipType) + { Image returnImage = Clone(sourceImage); returnImage.RotateFlip(rotateFlipType); return returnImage; } - + /// /// A generic way to create an empty image /// /// the source bitmap as the specifications for the new bitmap /// The color to fill with, or Color.Empty to take the default depending on the pixel format /// - public static Bitmap CreateEmptyLike(Image sourceImage, Color backgroundColor) { + public static Bitmap CreateEmptyLike(Image sourceImage, Color backgroundColor) + { PixelFormat pixelFormat = sourceImage.PixelFormat; - if (backgroundColor.A < 255) { + if (backgroundColor.A < 255) + { pixelFormat = PixelFormat.Format32bppArgb; } return CreateEmpty(sourceImage.Width, sourceImage.Height, pixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); @@ -1235,41 +1451,51 @@ namespace GreenshotPlugin.Core { /// /// /// Bitmap - public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution) { + public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution) + { // Create a new "clean" image Bitmap newImage = new Bitmap(width, height, format); newImage.SetResolution(horizontalResolution, verticalResolution); - if (format != PixelFormat.Format8bppIndexed) { - using (Graphics graphics = Graphics.FromImage(newImage)) { + if (format != PixelFormat.Format8bppIndexed) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { // Make sure the background color is what we want (transparent or white, depending on the pixel format) - if (!Color.Empty.Equals(backgroundColor)) { + if (!Color.Empty.Equals(backgroundColor)) + { graphics.Clear(backgroundColor); - } else if (Image.IsAlphaPixelFormat(format)) { + } + else if (Image.IsAlphaPixelFormat(format)) + { graphics.Clear(Color.Transparent); - } else { + } + else + { graphics.Clear(Color.White); } } } return newImage; } - + /// /// Get a scaled version of the sourceBitmap /// /// /// 1-99 to make smaller, use 101 and more to make the picture bigger /// - public static Bitmap ScaleByPercent(Bitmap sourceBitmap, int percent) { - float nPercent = ((float)percent/100); - + public static Bitmap ScaleByPercent(Bitmap sourceBitmap, int percent) + { + float nPercent = (float)percent / 100; + int sourceWidth = sourceBitmap.Width; int sourceHeight = sourceBitmap.Height; - int destWidth = (int)(sourceWidth * nPercent); + int destWidth = (int)(sourceWidth * nPercent); int destHeight = (int)(sourceHeight * nPercent); - + Bitmap scaledBitmap = CreateEmpty(destWidth, destHeight, sourceBitmap.PixelFormat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution); - using (Graphics graphics = Graphics.FromImage(scaledBitmap)) { + using (Graphics graphics = Graphics.FromImage(scaledBitmap)) + { graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, destWidth, destHeight), new Rectangle(0, 0, sourceWidth, sourceHeight), GraphicsUnit.Pixel); } @@ -1287,10 +1513,12 @@ namespace GreenshotPlugin.Core { /// /// /// a new bitmap with the source copied on it - public static Image ResizeCanvas(Image sourceImage, Color backgroundColor, int left, int right, int top, int bottom, Matrix matrix) { + public static Image ResizeCanvas(Image sourceImage, Color backgroundColor, int left, int right, int top, int bottom, Matrix matrix) + { matrix.Translate(left, top, MatrixOrder.Append); Bitmap newBitmap = CreateEmpty(sourceImage.Width + left + right, sourceImage.Height + top + bottom, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); - using (Graphics graphics = Graphics.FromImage(newBitmap)) { + using (Graphics graphics = Graphics.FromImage(newBitmap)) + { graphics.DrawImageUnscaled(sourceImage, left, top); } return newBitmap; @@ -1305,7 +1533,8 @@ namespace GreenshotPlugin.Core { /// /// /// - public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, int newWidth, int newHeight, Matrix matrix) { + public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, int newWidth, int newHeight, Matrix matrix) + { return ResizeImage(sourceImage, maintainAspectRatio, false, Color.Empty, newWidth, newHeight, matrix); } @@ -1316,20 +1545,27 @@ namespace GreenshotPlugin.Core { /// Color to count /// true if Alpha needs to be checked /// int with the number of pixels which have colorToCount - public static int CountColor(Image sourceImage, Color colorToCount, bool includeAlpha) { + public static int CountColor(Image sourceImage, Color colorToCount, bool includeAlpha) + { int colors = 0; int toCount = colorToCount.ToArgb(); - if (!includeAlpha) { + if (!includeAlpha) + { toCount = toCount & 0xffffff; } - using (IFastBitmap bb = FastBitmap.Create((Bitmap)sourceImage)) { - for (int y = 0; y < bb.Height; y++) { - for (int x = 0; x < bb.Width; x++) { + using (IFastBitmap bb = FastBitmap.Create((Bitmap)sourceImage)) + { + for (int y = 0; y < bb.Height; y++) + { + for (int x = 0; x < bb.Width; x++) + { int bitmapcolor = bb.GetColorAt(x, y).ToArgb(); - if (!includeAlpha) { + if (!includeAlpha) + { bitmapcolor = bitmapcolor & 0xffffff; } - if (bitmapcolor == toCount) { + if (bitmapcolor == toCount) + { colors++; } } @@ -1349,68 +1585,173 @@ namespace GreenshotPlugin.Core { /// new height /// /// a new bitmap with the specified size, the source-Image scaled to fit with aspect ratio locked - public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, bool canvasUseNewSize, Color backgroundColor, int newWidth, int newHeight, Matrix matrix) { + public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, bool canvasUseNewSize, Color backgroundColor, int newWidth, int newHeight, Matrix matrix) + { int destX = 0; int destY = 0; - - float nPercentW = 0; - float nPercentH = 0; - - nPercentW = (newWidth / (float)sourceImage.Width); - nPercentH = (newHeight / (float)sourceImage.Height); - if (maintainAspectRatio) { - if (nPercentW == 1) { + + var nPercentW = newWidth / (float)sourceImage.Width; + var nPercentH = newHeight / (float)sourceImage.Height; + if (maintainAspectRatio) + { + if ((int)nPercentW == 1) + { nPercentW = nPercentH; - if (canvasUseNewSize) { - destX = Math.Max(0, Convert.ToInt32((newWidth - (sourceImage.Width * nPercentW)) / 2)); - } - } else if (nPercentH == 1) { - nPercentH = nPercentW; - if (canvasUseNewSize) { - destY = Math.Max(0, Convert.ToInt32((newHeight - (sourceImage.Height * nPercentH)) / 2)); - } - } else if (nPercentH != 0 && nPercentH < nPercentW) { - nPercentW = nPercentH; - if (canvasUseNewSize) { - destX = Math.Max(0, Convert.ToInt32((newWidth - (sourceImage.Width * nPercentW)) / 2)); - } - } else { - nPercentH = nPercentW; - if (canvasUseNewSize) { - destY = Math.Max(0, Convert.ToInt32((newHeight - (sourceImage.Height * nPercentH)) / 2)); + if (canvasUseNewSize) + { + destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2)); } } - } - - int destWidth = (int)(sourceImage.Width * nPercentW); - int destHeight = (int)(sourceImage.Height * nPercentH); - if (newWidth == 0) { - newWidth = destWidth; - } - if (newHeight == 0) { - newHeight = destHeight; - } - Image newImage = null; - if (maintainAspectRatio && canvasUseNewSize) { - newImage = CreateEmpty(newWidth, newHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); - if (matrix != null) { - matrix.Scale((float)newWidth / sourceImage.Width, (float)newHeight / sourceImage.Height, MatrixOrder.Append); + else if ((int)nPercentH == 1) + { + nPercentH = nPercentW; + if (canvasUseNewSize) + { + destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2)); + } } - } else { - newImage = CreateEmpty(destWidth, destHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); - if (matrix != null) { - matrix.Scale((float)destWidth / sourceImage.Width, (float)destHeight / sourceImage.Height, MatrixOrder.Append); + else if ((int)nPercentH != 0 && nPercentH < nPercentW) + { + nPercentW = nPercentH; + if (canvasUseNewSize) + { + destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2)); + } + } + else + { + nPercentH = nPercentW; + if (canvasUseNewSize) + { + destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2)); + } } } - using (Graphics graphics = Graphics.FromImage(newImage)) { + int destWidth = (int)(sourceImage.Width * nPercentW); + int destHeight = (int)(sourceImage.Height * nPercentH); + if (newWidth == 0) + { + newWidth = destWidth; + } + if (newHeight == 0) + { + newHeight = destHeight; + } + Image newImage; + if (maintainAspectRatio && canvasUseNewSize) + { + newImage = CreateEmpty(newWidth, newHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + matrix?.Scale((float)newWidth / sourceImage.Width, (float)newHeight / sourceImage.Height, MatrixOrder.Append); + } + else + { + newImage = CreateEmpty(destWidth, destHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + matrix?.Scale((float)destWidth / sourceImage.Width, (float)destHeight / sourceImage.Height, MatrixOrder.Append); + } + + using (Graphics graphics = Graphics.FromImage(newImage)) + { graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - using (ImageAttributes wrapMode = new ImageAttributes()) { + using (ImageAttributes wrapMode = new ImageAttributes()) + { wrapMode.SetWrapMode(WrapMode.TileFlipXY); graphics.DrawImage(sourceImage, new Rectangle(destX, destY, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, wrapMode); } } return newImage; } + + /// + /// Load a Greenshot surface from a stream + /// + /// Stream + /// + /// ISurface + public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface) + { + Image fileImage; + // Fixed problem that the bitmap stream is disposed... by Cloning the image + // This also ensures the bitmap is correctly created + + // We create a copy of the bitmap, so everything else can be disposed + surfaceFileStream.Position = 0; + using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) + { + Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + fileImage = 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; + } + + /// + /// Create an image from a stream, if an extension is supplied more formats are supported. + /// + /// Stream + /// + /// Image + public static Image FromStream(Stream stream, string extension = null) + { + if (stream == null) + { + return null; + } + if (!string.IsNullOrEmpty(extension)) + { + extension = extension.Replace(".", ""); + } + + // Make sure we can try multiple times + if (!stream.CanSeek) + { + var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + stream = memoryStream; + } + + Image returnImage = null; + Func converter; + if (StreamConverters.TryGetValue(extension ?? "", out converter)) + { + returnImage = converter(stream, extension); + } + // Fallback + if (returnImage == null) + { + // We create a copy of the bitmap, so everything else can be disposed + stream.Position = 0; + using (var tmpImage = Image.FromStream(stream, true, true)) + { + Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + returnImage = Clone(tmpImage, PixelFormat.Format32bppArgb); + } + } + return returnImage; + } } } diff --git a/GreenshotPlugin/Core/ImageOutput.cs b/GreenshotPlugin/Core/ImageOutput.cs index 354db0e57..4ca912e11 100644 --- a/GreenshotPlugin/Core/ImageOutput.cs +++ b/GreenshotPlugin/Core/ImageOutput.cs @@ -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; } diff --git a/GreenshotPlugin/Core/ImageWrapper.cs b/GreenshotPlugin/Core/ImageWrapper.cs new file mode 100644 index 000000000..f4cc03247 --- /dev/null +++ b/GreenshotPlugin/Core/ImageWrapper.cs @@ -0,0 +1,86 @@ +using System.Drawing; +using System.Drawing.Imaging; + +namespace GreenshotPlugin.Core +{ + /// + /// Wrap an image, make it resizeable + /// + public class ImageWrapper : IImage + { + // Underlying image, is used to generate a resized version of it when needed + private readonly Image _image; + private Image _imageClone; + + /// + /// Factory method + /// + /// Image + /// IImage + 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(); + } + + /// + /// Height of the image, can be set to change + /// + public int Height { get; set; } + + /// + /// Width of the image, can be set to change. + /// + public int Width { get; set; } + + /// + /// Size of the image + /// + public Size Size => new Size(Width, Height); + + /// + /// Pixelformat of the underlying image + /// + public PixelFormat PixelFormat => Image.PixelFormat; + + public float HorizontalResolution => Image.HorizontalResolution; + public float VerticalResolution => Image.VerticalResolution; + + public Image Image + { + get + { + if (_imageClone == null) + { + if (_image.Height == Height && _image.Width == Width) + { + return _image; + } + } + if (_imageClone?.Height == Height && _imageClone?.Width == Width) + { + return _imageClone; + } + // Calculate new image clone + _imageClone?.Dispose(); + _imageClone = ImageHelper.ResizeImage(_image, false, Width, Height, null); + return _imageClone; + } + } + + } +} diff --git a/GreenshotPlugin/Core/NetworkHelper.cs b/GreenshotPlugin/Core/NetworkHelper.cs index e1d90c7fc..65f54baf6 100644 --- a/GreenshotPlugin/Core/NetworkHelper.cs +++ b/GreenshotPlugin/Core/NetworkHelper.cs @@ -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 { /// /// Of an image /// Bitmap - 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)://.*(?{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; } - + /// /// Helper method to create a web request with a lot of default settings /// @@ -347,7 +363,7 @@ namespace GreenshotPlugin.Core { /// HttpWebRequest to write the multipart form data to /// Parameters to include in the multipart form data public static void WriteMultipartFormData(HttpWebRequest webRequest, IDictionary 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 { /// HttpListenerResponse /// Parameters to include in the multipart form data public static void WriteMultipartFormData(HttpListenerResponse response, IDictionary 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; diff --git a/GreenshotPlugin/GreenshotPlugin.csproj b/GreenshotPlugin/GreenshotPlugin.csproj index 040062bc8..5dbed379b 100644 --- a/GreenshotPlugin/GreenshotPlugin.csproj +++ b/GreenshotPlugin/GreenshotPlugin.csproj @@ -42,6 +42,9 @@ + + + diff --git a/GreenshotPlugin/Interfaces/Generic.cs b/GreenshotPlugin/Interfaces/Generic.cs index a72142006..6f63972fb 100644 --- a/GreenshotPlugin/Interfaces/Generic.cs +++ b/GreenshotPlugin/Interfaces/Generic.cs @@ -226,7 +226,7 @@ namespace Greenshot.Plugin get; set; } - string UploadURL + string UploadUrl { get; set;