mirror of
https://github.com/greenshot/greenshot
synced 2025-08-20 13:33:27 -07:00
Merge pull request #30 from greenshot/feature/FEATURE-916_Ico
FEATURE-916: Added support for .ico files
This commit is contained in:
commit
cd7555099f
3 changed files with 110 additions and 21 deletions
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue