From 7823138adc0c9c71582f6236c2282d6c82a31fa4 Mon Sep 17 00:00:00 2001 From: RKrom Date: Mon, 25 Mar 2013 13:42:21 +0000 Subject: [PATCH] Added the possibility to call optipng, or eventually other optimizers, on the PNG file. This can be used with any PNG file, it's not important if there is an upload or saving to file, as the optimizer runs on a temporary file which is read back and than deleted. Was requested with feature request [#267]. This change only enables the functionality, there is not yet a GUI entry for it. git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2535 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4 --- GreenshotPlugin/Core/CoreConfiguration.cs | 5 ++ GreenshotPlugin/Core/ImageOutput.cs | 84 ++++++++++++++++++++--- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/GreenshotPlugin/Core/CoreConfiguration.cs b/GreenshotPlugin/Core/CoreConfiguration.cs index f6bc84ed7..51d09da11 100644 --- a/GreenshotPlugin/Core/CoreConfiguration.cs +++ b/GreenshotPlugin/Core/CoreConfiguration.cs @@ -237,6 +237,11 @@ namespace GreenshotPlugin.Core { [IniProperty("MailApiBCC", Description = "The 'BCC' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")] public string MailApiBCC; + [IniProperty("OptimizePNGCommand", Description = "Optional command to execute on a temporary PNG file, the command should overwrite the file and Greenshot will read it back. Note: this command is also executed when uploading PNG's!", DefaultValue = "")] + public string OptimizePNGCommand; + [IniProperty("OptimizePNGCommandArguments", Description = "Arguments for the optional command to execute on a PNG, {0} is replaced by the temp-filename from Greenshot. Note: Temp-file is deleted afterwards by Greenshot.", DefaultValue = "\"{0}\"")] + public string OptimizePNGCommandArguments; + // Specifies what THIS build is public BuildStates BuildState = BuildStates.UNSTABLE; diff --git a/GreenshotPlugin/Core/ImageOutput.cs b/GreenshotPlugin/Core/ImageOutput.cs index 4407a00da..9aaddd30c 100644 --- a/GreenshotPlugin/Core/ImageOutput.cs +++ b/GreenshotPlugin/Core/ImageOutput.cs @@ -29,6 +29,8 @@ using Greenshot.IniFile; using Greenshot.Plugin; using GreenshotPlugin.Controls; using Greenshot.Core; +using System.Diagnostics; +using System.Security.AccessControl; namespace GreenshotPlugin.Core { /// @@ -167,17 +169,25 @@ namespace GreenshotPlugin.Core { throw new ApplicationException("No JPG encoder found, this should not happen."); } } else { + bool needsDispose = false; // Removing transparency if it's not supported in the output if (imageFormat != ImageFormat.Png && Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) { - Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); - AddTag(nonAlphaImage); - nonAlphaImage.Save(targetStream, imageFormat); - nonAlphaImage.Dispose(); - nonAlphaImage = null; - } else { - AddTag(imageToSave); + imageToSave = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); + needsDispose = true; + } + AddTag(imageToSave); + // Added for OptiPNG + bool processed = false; + if (imageFormat == ImageFormat.Png && !string.IsNullOrEmpty(conf.OptimizePNGCommand)) { + processed = ProcessPNGImageExternally(imageToSave, targetStream); + } + if (!processed) { imageToSave.Save(targetStream, imageFormat); } + if (needsDispose) { + imageToSave.Dispose(); + imageToSave = null; + } } // If we used a memory stream, we need to stream the memory stream to the original stream. @@ -204,7 +214,65 @@ namespace GreenshotPlugin.Core { } } } - + + /// + /// Write the passed Image to a tmp-file and call an external process, than read the file back and write it to the targetStream + /// + /// Image to pass to the external process + /// stream to write the processed image to + /// + private static bool ProcessPNGImageExternally(Image imageToProcess, Stream targetStream) { + if (string.IsNullOrEmpty(conf.OptimizePNGCommand)) { + return false; + } + if (!File.Exists(conf.OptimizePNGCommand)) { + LOG.WarnFormat("Can't find 'OptimizePNGCommand' {0}", conf.OptimizePNGCommand); + return false; + } + string tmpFileName = Path.Combine(Path.GetTempPath(),Path.GetRandomFileName() + ".png"); + try { + using (FileStream tmpStream = File.Create(tmpFileName)) { + LOG.DebugFormat("Writing png to tmp file: {0}", tmpFileName); + imageToProcess.Save(tmpStream, ImageFormat.Png); + if (LOG.IsDebugEnabled) { + LOG.DebugFormat("File size before processing {0}", new FileInfo(tmpFileName).Length); + } + } + if (LOG.IsDebugEnabled) { + LOG.DebugFormat("Starting : {0}", conf.OptimizePNGCommand); + } + + ProcessStartInfo processStartInfo = new ProcessStartInfo(conf.OptimizePNGCommand); + processStartInfo.Arguments = string.Format(conf.OptimizePNGCommandArguments, tmpFileName); + processStartInfo.CreateNoWindow = true; + processStartInfo.RedirectStandardOutput = true; + processStartInfo.RedirectStandardError = true; + processStartInfo.UseShellExecute = false; + Process process = Process.Start(processStartInfo); + process.WaitForExit(); + if (process.ExitCode == 0) { + if (LOG.IsDebugEnabled) { + LOG.DebugFormat("File size after processing {0}", new FileInfo(tmpFileName).Length); + LOG.DebugFormat("Reading back tmp file: {0}", tmpFileName); + } + byte[] processedImage = File.ReadAllBytes(tmpFileName); + targetStream.Write(processedImage, 0, processedImage.Length); + return true; + } + LOG.ErrorFormat("Error while processing PNG image: {0}", process.ExitCode); + LOG.ErrorFormat("Output: {0}", process.StandardOutput.ReadToEnd()); + LOG.ErrorFormat("Error: {0}", process.StandardError.ReadToEnd()); + } catch (Exception e) { + LOG.Error("Error while processing PNG image: ", e); + } finally { + if (File.Exists(tmpFileName)) { + LOG.DebugFormat("Cleaning up tmp file: {0}", tmpFileName); + File.Delete(tmpFileName); + } + } + return false; + } + /// /// Create an image from a surface with the settings from the output settings applied ///