mirror of
https://github.com/greenshot/greenshot
synced 2025-07-16 10:03:44 -07:00
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:
parent
b04be588e5
commit
9bee8290a0
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