FEATURE-916: Added some support for .ico files when writing, this will currently only write 256x256 .ico files!!! These files are not supported by Windows XP and older!

This commit is contained in:
Robin 2016-05-25 22:56:01 +02:00
parent b04be588e5
commit 9bee8290a0
3 changed files with 110 additions and 21 deletions

View file

@ -34,7 +34,7 @@ namespace GreenshotPlugin.Core {
PNG, DIB, HTML, HTMLDATAURL, BITMAP, DIBV5 PNG, DIB, HTML, HTMLDATAURL, BITMAP, DIBV5
} }
public enum OutputFormat { public enum OutputFormat {
bmp, gif, jpg, png, tiff, greenshot bmp, gif, jpg, png, tiff, greenshot, ico
} }
public enum WindowCaptureMode { public enum WindowCaptureMode {
Screen, GDI, Aero, AeroTransparent, Auto Screen, GDI, Aero, AeroTransparent, Auto

View file

@ -1345,7 +1345,7 @@ namespace GreenshotPlugin.Core {
/// </summary> /// </summary>
/// <param name="sourceImage">Image to scale</param> /// <param name="sourceImage">Image to scale</param>
/// <param name="maintainAspectRatio">true to maintain the aspect ratio</param> /// <param name="maintainAspectRatio">true to maintain the aspect ratio</param>
/// <param name="canvasUseNewSize"></param> /// <param name="canvasUseNewSize">Makes the image maintain aspect ratio, but the canvas get's the specified size</param>
/// <param name="backgroundColor">The color to fill with, or Color.Empty to take the default depending on the pixel format</param> /// <param name="backgroundColor">The color to fill with, or Color.Empty to take the default depending on the pixel format</param>
/// <param name="newWidth">new width</param> /// <param name="newWidth">new width</param>
/// <param name="newHeight">new height</param> /// <param name="newHeight">new height</param>

View file

@ -24,6 +24,7 @@ using Greenshot.Plugin;
using GreenshotPlugin.Controls; using GreenshotPlugin.Controls;
using log4net; using log4net;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
@ -101,7 +102,6 @@ namespace GreenshotPlugin.Core {
/// <param name="stream">Stream to save to</param> /// <param name="stream">Stream to save to</param>
/// <param name="outputSettings">SurfaceOutputSettings</param> /// <param name="outputSettings">SurfaceOutputSettings</param>
public static void SaveToStream(Image imageToSave, ISurface surface, Stream stream, SurfaceOutputSettings outputSettings) { public static void SaveToStream(Image imageToSave, ISurface surface, Stream stream, SurfaceOutputSettings outputSettings) {
ImageFormat imageFormat;
bool useMemoryStream = false; bool useMemoryStream = false;
MemoryStream memoryStream = null; MemoryStream memoryStream = null;
if (outputSettings.Format == OutputFormat.greenshot && surface == null) { if (outputSettings.Format == OutputFormat.greenshot && surface == null) {
@ -109,6 +109,7 @@ namespace GreenshotPlugin.Core {
} }
try { try {
ImageFormat imageFormat;
switch (outputSettings.Format) { switch (outputSettings.Format) {
case OutputFormat.bmp: case OutputFormat.bmp:
imageFormat = ImageFormat.Bmp; imageFormat = ImageFormat.Bmp;
@ -122,13 +123,15 @@ namespace GreenshotPlugin.Core {
case OutputFormat.tiff: case OutputFormat.tiff:
imageFormat = ImageFormat.Tiff; imageFormat = ImageFormat.Tiff;
break; break;
case OutputFormat.ico:
imageFormat = ImageFormat.Icon;
break;
default: default:
// Problem with non-seekable streams most likely doesn't happen with Windows 7 (OS Version 6.1 and later) // Problem with non-seekable streams most likely doesn't happen with Windows 7 (OS Version 6.1 and later)
// http://stackoverflow.com/questions/8349260/generic-gdi-error-on-one-machine-but-not-the-other // http://stackoverflow.com/questions/8349260/generic-gdi-error-on-one-machine-but-not-the-other
if (!stream.CanSeek) { if (!stream.CanSeek) {
int majorVersion = Environment.OSVersion.Version.Major; if (!Environment.OSVersion.IsWindows7OrLater())
int minorVersion = Environment.OSVersion.Version.Minor; {
if (majorVersion < 6 || (majorVersion == 6 && minorVersion == 0)) {
useMemoryStream = true; useMemoryStream = true;
LOG.Warn("Using memorystream prevent an issue with saving to a non seekable stream."); LOG.Warn("Using memorystream prevent an issue with saving to a non seekable stream.");
} }
@ -146,20 +149,26 @@ namespace GreenshotPlugin.Core {
targetStream = memoryStream; targetStream = memoryStream;
} }
if (Equals(imageFormat, ImageFormat.Jpeg)) { if (Equals(imageFormat, ImageFormat.Jpeg))
{
bool foundEncoder = false; bool foundEncoder = false;
foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders()) { foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders())
if (imageCodec.FormatID == imageFormat.Guid) { {
if (imageCodec.FormatID == imageFormat.Guid)
{
EncoderParameters parameters = new EncoderParameters(1); EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality); parameters.Param[0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality);
// Removing transparency if it's not supported in the output // Removing transparency if it's not supported in the output
if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) { if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat))
{
Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
AddTag(nonAlphaImage); AddTag(nonAlphaImage);
nonAlphaImage.Save(targetStream, imageCodec, parameters); nonAlphaImage.Save(targetStream, imageCodec, parameters);
nonAlphaImage.Dispose(); nonAlphaImage.Dispose();
nonAlphaImage = null; nonAlphaImage = null;
} else { }
else
{
AddTag(imageToSave); AddTag(imageToSave);
imageToSave.Save(targetStream, imageCodec, parameters); imageToSave.Save(targetStream, imageCodec, parameters);
} }
@ -167,9 +176,15 @@ namespace GreenshotPlugin.Core {
break; break;
} }
} }
if (!foundEncoder) { if (!foundEncoder)
{
throw new ApplicationException("No JPG encoder found, this should not happen."); throw new ApplicationException("No JPG encoder found, this should not happen.");
} }
} else if (Equals(imageFormat, ImageFormat.Icon)) {
// FEATURE-916: Added Icon support
IList<Image> images = new List<Image>();
images.Add(imageToSave);
WriteIcon(stream, images);
} else { } else {
bool needsDispose = false; bool needsDispose = false;
// Removing transparency if it's not supported in the output // Removing transparency if it's not supported in the output
@ -181,7 +196,7 @@ namespace GreenshotPlugin.Core {
// Added for OptiPNG // Added for OptiPNG
bool processed = false; bool processed = false;
if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(conf.OptimizePNGCommand)) { if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(conf.OptimizePNGCommand)) {
processed = ProcessPNGImageExternally(imageToSave, targetStream); processed = ProcessPngImageExternally(imageToSave, targetStream);
} }
if (!processed) { if (!processed) {
imageToSave.Save(targetStream, imageFormat); imageToSave.Save(targetStream, imageFormat);
@ -223,7 +238,7 @@ namespace GreenshotPlugin.Core {
/// <param name="imageToProcess">Image to pass to the external process</param> /// <param name="imageToProcess">Image to pass to the external process</param>
/// <param name="targetStream">stream to write the processed image to</param> /// <param name="targetStream">stream to write the processed image to</param>
/// <returns></returns> /// <returns></returns>
private static bool ProcessPNGImageExternally(Image imageToProcess, Stream targetStream) { private static bool ProcessPngImageExternally(Image imageToProcess, Stream targetStream) {
if (string.IsNullOrEmpty(conf.OptimizePNGCommand)) { if (string.IsNullOrEmpty(conf.OptimizePNGCommand)) {
return false; return false;
} }
@ -244,12 +259,14 @@ namespace GreenshotPlugin.Core {
LOG.DebugFormat("Starting : {0}", conf.OptimizePNGCommand); LOG.DebugFormat("Starting : {0}", conf.OptimizePNGCommand);
} }
ProcessStartInfo processStartInfo = new ProcessStartInfo(conf.OptimizePNGCommand); ProcessStartInfo processStartInfo = new ProcessStartInfo(conf.OptimizePNGCommand)
processStartInfo.Arguments = string.Format(conf.OptimizePNGCommandArguments, tmpFileName); {
processStartInfo.CreateNoWindow = true; Arguments = string.Format(conf.OptimizePNGCommandArguments, tmpFileName),
processStartInfo.RedirectStandardOutput = true; CreateNoWindow = true,
processStartInfo.RedirectStandardError = true; RedirectStandardOutput = true,
processStartInfo.UseShellExecute = false; RedirectStandardError = true,
UseShellExecute = false
};
using (Process process = Process.Start(processStartInfo)) { using (Process process = Process.Start(processStartInfo)) {
if (process != null) { if (process != null) {
process.WaitForExit(); process.WaitForExit();
@ -450,7 +467,7 @@ namespace GreenshotPlugin.Core {
/// <returns>OutputFormat</returns> /// <returns>OutputFormat</returns>
public static OutputFormat FormatForFilename(string fullPath) { public static OutputFormat FormatForFilename(string fullPath) {
// Fix for bug 2912959 // Fix for bug 2912959
string extension = fullPath.Substring(fullPath.LastIndexOf(".") + 1); string extension = fullPath.Substring(fullPath.LastIndexOf(".", StringComparison.Ordinal) + 1);
OutputFormat format = OutputFormat.png; OutputFormat format = OutputFormat.png;
try { try {
format = (OutputFormat)Enum.Parse(typeof(OutputFormat), extension.ToLower()); format = (OutputFormat)Enum.Parse(typeof(OutputFormat), extension.ToLower());
@ -601,5 +618,77 @@ namespace GreenshotPlugin.Core {
File.Delete(path); File.Delete(path);
} }
} }
#region Icon
/// <summary>
/// Write the images to the stream as icon
/// Every image is resized to 256x256 (but the content maintains the aspect ratio)
/// </summary>
/// <param name="stream">Stream to write to</param>
/// <param name="images">List of images</param>
public static void WriteIcon(Stream stream, IList<Image> images)
{
var binaryWriter = new BinaryWriter(stream);
//
// ICONDIR structure
//
binaryWriter.Write((short)0); // reserved
binaryWriter.Write((short)1); // image type (icon)
binaryWriter.Write((short)images.Count); // number of images
IList<Size> imageSizes = new List<Size>();
IList<MemoryStream> encodedImages = new List<MemoryStream>();
foreach (var image in images)
{
var imageStream = new MemoryStream();
// Always size to 256x256, first make sure the image is 32bpp
using (var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb))
{
using (var resizedImage = ImageHelper.ResizeImage(clonedImage, true, true, Color.Empty, 256, 256, null))
{
resizedImage.Save(imageStream, ImageFormat.Png);
imageSizes.Add(resizedImage.Size);
}
}
imageStream.Seek(0, SeekOrigin.Begin);
encodedImages.Add(imageStream);
}
//
// ICONDIRENTRY structure
//
const int iconDirSize = 6;
const int iconDirEntrySize = 16;
var offset = iconDirSize + (images.Count * iconDirEntrySize);
for (int i = 0; i < images.Count; i++)
{
var imageSize = imageSizes[i];
// Write the width / height, 0 means 256
binaryWriter.Write(imageSize.Width == 256 ? (byte)0 : (byte)imageSize.Width);
binaryWriter.Write(imageSize.Height == 256 ? (byte)0 : (byte)imageSize.Height);
binaryWriter.Write((byte)0); // no pallete
binaryWriter.Write((byte)0); // reserved
binaryWriter.Write((short)0); // no color planes
binaryWriter.Write((short)32); // 32 bpp
binaryWriter.Write((int)encodedImages[i].Length); // image data length
binaryWriter.Write(offset);
offset += (int)encodedImages[i].Length;
}
binaryWriter.Flush();
//
// Write image data
//
foreach (var encodedImage in encodedImages)
{
encodedImage.WriteTo(stream);
encodedImage.Dispose();
}
}
#endregion
} }
} }