Refactoring to prevent that the image for the clipboard is created multiple times. Code could most likely be cleaner and simpler but for now it should be okay.

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2401 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2012-12-29 15:20:26 +00:00
commit 09400ccdbe
2 changed files with 216 additions and 127 deletions

View file

@ -22,6 +22,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -311,25 +312,43 @@ EndSelection:<<<<<<<4
try { try {
MemoryStream imageStream = null; MemoryStream imageStream = null;
if (!isValidStream(imageStream) && formats.Contains(FORMAT_PNG)) { if (formats.Contains(FORMAT_PNG)) {
imageStream = GetFromDataObject(dataObject, FORMAT_PNG) as MemoryStream; imageStream = GetFromDataObject(dataObject, FORMAT_PNG) as MemoryStream;
} if (isValidStream(imageStream)) {
if (!isValidStream(imageStream) && formats.Contains(FORMAT_JPG)) { try {
imageStream = GetFromDataObject(dataObject, FORMAT_JPG) as MemoryStream; using (Image tmpImage = Image.FromStream(imageStream)) {
} return ImageHelper.Clone(tmpImage);
if (!isValidStream(imageStream) && formats.Contains(DataFormats.Tiff)) { }
imageStream = GetFromDataObject(dataObject, DataFormats.Tiff) as MemoryStream; } catch (Exception streamImageEx) {
} LOG.Error("Problem retrieving PNG image from clipboard.", streamImageEx);
if (isValidStream(imageStream)) {
try {
using (Image tmpImage = Image.FromStream(imageStream)) {
return ImageHelper.Clone(tmpImage);
} }
} catch (Exception streamImageEx) {
LOG.Error("Problem retrieving Image from clipboard.", streamImageEx);
} }
} }
if (formats.Contains(FORMAT_JPG)) {
imageStream = GetFromDataObject(dataObject, FORMAT_JPG) as MemoryStream;
if (isValidStream(imageStream)) {
try {
using (Image tmpImage = Image.FromStream(imageStream)) {
return ImageHelper.Clone(tmpImage);
}
} catch (Exception streamImageEx) {
LOG.Error("Problem retrieving JPG image from clipboard.", streamImageEx);
}
}
}
if (formats.Contains(DataFormats.Tiff)) {
imageStream = GetFromDataObject(dataObject, DataFormats.Tiff) as MemoryStream;
if (isValidStream(imageStream)) {
try {
using (Image tmpImage = Image.FromStream(imageStream)) {
return ImageHelper.Clone(tmpImage);
}
} catch (Exception streamImageEx) {
LOG.Error("Problem retrieving TIFF image from clipboard.", streamImageEx);
}
}
}
// the DIB readed should solve the issue reported here: https://sourceforge.net/projects/greenshot/forums/forum/676083/topic/6354353/index/page/1 // the DIB readed should solve the issue reported here: https://sourceforge.net/projects/greenshot/forums/forum/676083/topic/6354353/index/page/1
try { try {
// If the EnableSpecialDIBClipboardReader flag in the config is set, use the code from: // If the EnableSpecialDIBClipboardReader flag in the config is set, use the code from:
@ -466,14 +485,19 @@ EndSelection:<<<<<<<4
MemoryStream dibStream = null; MemoryStream dibStream = null;
MemoryStream pngStream = null; MemoryStream pngStream = null;
Image imageToSave = null;
bool disposeImage = false;
try { try {
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
// Create the image which is going to be saved so we don't create it multiple times
disposeImage = ImageOutput.CreateImageFromSurface(surface, outputSettings, out imageToSave);
try { try {
// Create PNG stream // Create PNG stream
if (config.ClipboardFormats.Contains(ClipboardFormat.PNG)) { if (config.ClipboardFormats.Contains(ClipboardFormat.PNG)) {
pngStream = new MemoryStream(); pngStream = new MemoryStream();
// PNG works for e.g. Powerpoint // PNG works for e.g. Powerpoint
SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
ImageOutput.SaveToStream(surface, pngStream, pngOutputSettings); ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings);
pngStream.Seek(0, SeekOrigin.Begin); pngStream.Seek(0, SeekOrigin.Begin);
// Set the PNG stream // Set the PNG stream
ido.SetData(FORMAT_PNG, false, pngStream); ido.SetData(FORMAT_PNG, false, pngStream);
@ -482,13 +506,12 @@ EndSelection:<<<<<<<4
LOG.Error("Error creating PNG for the Clipboard.", pngEX); LOG.Error("Error creating PNG for the Clipboard.", pngEX);
} }
try { try {
if (config.ClipboardFormats.Contains(ClipboardFormat.DIB)) { if (config.ClipboardFormats.Contains(ClipboardFormat.DIB)) {
using (MemoryStream tmpBmpStream = new MemoryStream()) { using (MemoryStream tmpBmpStream = new MemoryStream()) {
// Save image as BMP // Save image as BMP
SurfaceOutputSettings bmpOutputSettings = new SurfaceOutputSettings(OutputFormat.bmp, 100, false); SurfaceOutputSettings bmpOutputSettings = new SurfaceOutputSettings(OutputFormat.bmp, 100, false);
ImageOutput.SaveToStream(surface, tmpBmpStream, bmpOutputSettings); ImageOutput.SaveToStream(imageToSave, null, tmpBmpStream, bmpOutputSettings);
dibStream = new MemoryStream(); dibStream = new MemoryStream();
// Copy the source, but skip the "BITMAPFILEHEADER" which has a size of 14 // Copy the source, but skip the "BITMAPFILEHEADER" which has a size of 14
@ -514,7 +537,12 @@ EndSelection:<<<<<<<4
// Do not allow to reduce the colors, some applications dislike 256 color images // Do not allow to reduce the colors, some applications dislike 256 color images
// reported with bug #3594681 // reported with bug #3594681
pngOutputSettings.DisableReduceColors = true; pngOutputSettings.DisableReduceColors = true;
ImageOutput.SaveToStream(surface, tmpPNGStream, pngOutputSettings); // Check if we can use the previously used image
if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed) {
ImageOutput.SaveToStream(imageToSave, surface, tmpPNGStream, pngOutputSettings);
} else {
ImageOutput.SaveToStream(surface, tmpPNGStream, pngOutputSettings);
}
html = getHTMLDataURLString(surface, tmpPNGStream); html = getHTMLDataURLString(surface, tmpPNGStream);
} }
ido.SetText(html, TextDataFormat.Html); ido.SetText(html, TextDataFormat.Html);
@ -523,11 +551,9 @@ EndSelection:<<<<<<<4
// we need to use the SetDataOject before the streams are closed otherwise the buffer will be gone! // we need to use the SetDataOject before the streams are closed otherwise the buffer will be gone!
// Check if Bitmap is wanted // Check if Bitmap is wanted
if (config.ClipboardFormats.Contains(ClipboardFormat.BITMAP)) { if (config.ClipboardFormats.Contains(ClipboardFormat.BITMAP)) {
using (Image tmpImage = surface.GetImageForExport()) { ido.SetImage(imageToSave);
ido.SetImage(tmpImage); // Place the DataObject to the clipboard
// Place the DataObject to the clipboard SetDataObject(ido, true);
SetDataObject(ido, true);
}
} else { } else {
// Place the DataObject to the clipboard // Place the DataObject to the clipboard
SetDataObject(ido, true); SetDataObject(ido, true);
@ -542,6 +568,10 @@ EndSelection:<<<<<<<4
dibStream.Dispose(); dibStream.Dispose();
dibStream = null; dibStream = null;
} }
// cleanup if needed
if (disposeImage && imageToSave != null) {
imageToSave.Dispose();
}
} }
} }

View file

@ -69,117 +69,65 @@ namespace GreenshotPlugin.Core {
return propertyItem; return propertyItem;
} }
#region save #region save
/// <summary>
/// Saves ISurface to stream with specified output settings
/// </summary>
/// <param name="surface">ISurface to save</param>
/// <param name="stream">Stream to save to</param>
/// <param name="outputSettings">SurfaceOutputSettings</param>
public static void SaveToStream(ISurface surface, Stream stream, SurfaceOutputSettings outputSettings) {
Image imageToSave = null;
bool disposeImage = CreateImageFromSurface(surface, outputSettings, out imageToSave);
SaveToStream(imageToSave, surface, stream, outputSettings);
// cleanup if needed
if (disposeImage && imageToSave != null) {
imageToSave.Dispose();
}
}
/// <summary> /// <summary>
/// Saves image to stream with specified quality /// Saves image to stream with specified quality
/// To prevent problems with GDI version of before Windows 7: /// To prevent problems with GDI version of before Windows 7:
/// the stream is checked if it's seekable and if needed a MemoryStream as "cache" is used. /// the stream is checked if it's seekable and if needed a MemoryStream as "cache" is used.
/// </summary> /// </summary>
public static void SaveToStream(ISurface surface, Stream stream, SurfaceOutputSettings outputSettings) { /// <param name="imageToSave">image to save</param>
/// <param name="surface">surface for the elements, if the greenshot format is used</param>
/// <param name="stream">Stream to save to</param>
/// <param name="outputSettings">SurfaceOutputSettings</param>
public static void SaveToStream(Image imageToSave, ISurface surface, Stream stream, SurfaceOutputSettings outputSettings) {
ImageFormat imageFormat = null; ImageFormat imageFormat = null;
bool disposeImage = false;
bool useMemoryStream = false; bool useMemoryStream = false;
MemoryStream memoryStream = null; MemoryStream memoryStream = null;
switch (outputSettings.Format) {
case OutputFormat.bmp:
imageFormat = ImageFormat.Bmp;
break;
case OutputFormat.gif:
imageFormat = ImageFormat.Gif;
break;
case OutputFormat.jpg:
imageFormat = ImageFormat.Jpeg;
break;
case OutputFormat.tiff:
imageFormat = ImageFormat.Tiff;
break;
case OutputFormat.greenshot:
case OutputFormat.png:
default:
// 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
if (!stream.CanSeek) {
int majorVersion = Environment.OSVersion.Version.Major;
int minorVersion = Environment.OSVersion.Version.Minor;
if (majorVersion < 6 || (majorVersion == 6 && minorVersion == 0)) {
useMemoryStream = true;
LOG.Warn("Using memorystream prevent an issue with saving to a non seekable stream.");
}
}
imageFormat = ImageFormat.Png;
break;
}
// check what image we want to save
Image imageToSave = null;
if (outputSettings.Format == OutputFormat.greenshot || outputSettings.SaveBackgroundOnly) {
// We save the image of the surface, this should not be disposed
imageToSave = surface.Image;
} else {
// We create the export image of the surface to save
imageToSave = surface.GetImageForExport();
disposeImage = true;
}
try { try {
// The following block of modifications should be skipped when saving the greenshot format, no effects or otherwise! switch (outputSettings.Format) {
if (outputSettings.Format != OutputFormat.greenshot) { case OutputFormat.bmp:
Image tmpImage; imageFormat = ImageFormat.Bmp;
if (outputSettings.Effects != null && outputSettings.Effects.Count > 0) { break;
// apply effects, if there are any case OutputFormat.gif:
Point ignoreOffset; imageFormat = ImageFormat.Gif;
tmpImage = ImageHelper.ApplyEffects((Bitmap)imageToSave, outputSettings.Effects, out ignoreOffset); break;
if (tmpImage != null) { case OutputFormat.jpg:
if (disposeImage) { imageFormat = ImageFormat.Jpeg;
imageToSave.Dispose(); break;
} case OutputFormat.tiff:
imageToSave = tmpImage; imageFormat = ImageFormat.Tiff;
disposeImage = true; break;
} case OutputFormat.greenshot:
} case OutputFormat.png:
default:
// Removing transparency if it's not supported in the output // Problem with non-seekable streams most likely doesn't happen with Windows 7 (OS Version 6.1 and later)
if (imageFormat != ImageFormat.Png && Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) { // http://stackoverflow.com/questions/8349260/generic-gdi-error-on-one-machine-but-not-the-other
Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); if (!stream.CanSeek) {
if (disposeImage) { int majorVersion = Environment.OSVersion.Version.Major;
imageToSave.Dispose(); int minorVersion = Environment.OSVersion.Version.Minor;
} if (majorVersion < 6 || (majorVersion == 6 && minorVersion == 0)) {
// Make sure the image is disposed! useMemoryStream = true;
disposeImage = true; LOG.Warn("Using memorystream prevent an issue with saving to a non seekable stream.");
imageToSave = nonAlphaImage;
}
// check for color reduction, forced or automatically, only when the DisableReduceColors is false
if (!outputSettings.DisableReduceColors && (conf.OutputFileAutoReduceColors || outputSettings.ReduceColors)) {
WuQuantizer quantizer = new WuQuantizer((Bitmap)imageToSave);
int colorCount = quantizer.GetColorCount();
LOG.InfoFormat("Image with format {0} has {1} colors", imageToSave.PixelFormat, colorCount);
if (outputSettings.ReduceColors || colorCount < 256) {
try {
LOG.Info("Reducing colors on bitmap to 255.");
tmpImage = quantizer.GetQuantizedImage(255);
if (disposeImage) {
imageToSave.Dispose();
}
imageToSave = tmpImage;
// Make sure the "new" image is disposed
disposeImage = true;
} catch (Exception e) {
LOG.Warn("Error occurred while Quantizing the image, ignoring and using original. Error: ", e);
} }
} }
} imageFormat = ImageFormat.Png;
break;
// Create meta-data
PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot");
if (softwareUsedPropertyItem != null) {
try {
imageToSave.SetPropertyItem(softwareUsedPropertyItem);
} catch (ArgumentException) {
LOG.WarnFormat("Image of type {0} do not support property {1}", imageFormat, softwareUsedPropertyItem.Id);
}
}
} }
LOG.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat); LOG.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat);
@ -197,7 +145,17 @@ namespace GreenshotPlugin.Core {
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);
imageToSave.Save(targetStream, imageCodec, parameters); // Removing transparency if it's not supported in the output
if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) {
Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
AddTag(nonAlphaImage);
nonAlphaImage.Save(targetStream, imageCodec, parameters);
nonAlphaImage.Dispose();
nonAlphaImage = null;
} else {
AddTag(imageToSave);
imageToSave.Save(targetStream, imageCodec, parameters);
}
foundEncoder = true; foundEncoder = true;
break; break;
} }
@ -206,7 +164,17 @@ namespace GreenshotPlugin.Core {
throw new ApplicationException("No JPG encoder found, this should not happen."); throw new ApplicationException("No JPG encoder found, this should not happen.");
} }
} else { } else {
imageToSave.Save(targetStream, imageFormat); // 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.Save(targetStream, imageFormat);
}
} }
// If we used a memory stream, we need to stream the memory stream to the original stream. // If we used a memory stream, we need to stream the memory stream to the original stream.
@ -231,9 +199,100 @@ namespace GreenshotPlugin.Core {
if (memoryStream != null) { if (memoryStream != null) {
memoryStream.Dispose(); memoryStream.Dispose();
} }
// cleanup if needed }
if (disposeImage && imageToSave != null) { }
imageToSave.Dispose();
/// <summary>
/// Create an image from a surface with the settings from the output settings applied
/// </summary>
/// <param name="surface"></param>
/// <param name="outputSettings"></param>
/// <param name="imageToSave"></param>
/// <returns>true if the image must be disposed</returns>
public static bool CreateImageFromSurface(ISurface surface, SurfaceOutputSettings outputSettings, out Image imageToSave) {
bool disposeImage = false;
ImageFormat imageFormat = null;
switch (outputSettings.Format) {
case OutputFormat.bmp:
imageFormat = ImageFormat.Bmp;
break;
case OutputFormat.gif:
imageFormat = ImageFormat.Gif;
break;
case OutputFormat.jpg:
imageFormat = ImageFormat.Jpeg;
break;
case OutputFormat.tiff:
imageFormat = ImageFormat.Tiff;
break;
case OutputFormat.greenshot:
case OutputFormat.png:
default:
imageFormat = ImageFormat.Png;
break;
}
if (outputSettings.Format == OutputFormat.greenshot || outputSettings.SaveBackgroundOnly) {
// We save the image of the surface, this should not be disposed
imageToSave = surface.Image;
} else {
// We create the export image of the surface to save
imageToSave = surface.GetImageForExport();
disposeImage = true;
}
// The following block of modifications should be skipped when saving the greenshot format, no effects or otherwise!
if (outputSettings.Format != OutputFormat.greenshot) {
Image tmpImage;
if (outputSettings.Effects != null && outputSettings.Effects.Count > 0) {
// apply effects, if there are any
Point ignoreOffset;
tmpImage = ImageHelper.ApplyEffects((Bitmap)imageToSave, outputSettings.Effects, out ignoreOffset);
if (tmpImage != null) {
if (disposeImage) {
imageToSave.Dispose();
}
imageToSave = tmpImage;
disposeImage = true;
}
}
// check for color reduction, forced or automatically, only when the DisableReduceColors is false
if (!outputSettings.DisableReduceColors && (conf.OutputFileAutoReduceColors || outputSettings.ReduceColors)) {
WuQuantizer quantizer = new WuQuantizer((Bitmap)imageToSave);
int colorCount = quantizer.GetColorCount();
LOG.InfoFormat("Image with format {0} has {1} colors", imageToSave.PixelFormat, colorCount);
if (outputSettings.ReduceColors || colorCount < 256) {
try {
LOG.Info("Reducing colors on bitmap to 255.");
tmpImage = quantizer.GetQuantizedImage(255);
if (disposeImage) {
imageToSave.Dispose();
}
imageToSave = tmpImage;
// Make sure the "new" image is disposed
disposeImage = true;
} catch (Exception e) {
LOG.Warn("Error occurred while Quantizing the image, ignoring and using original. Error: ", e);
}
}
}
}
return disposeImage;
}
/// <summary>
/// Add the greenshot property!
/// </summary>
/// <param name="imageToSave"></param>
private static void AddTag(Image imageToSave) {
// Create meta-data
PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot");
if (softwareUsedPropertyItem != null) {
try {
imageToSave.SetPropertyItem(softwareUsedPropertyItem);
} catch (Exception) {
LOG.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id);
} }
} }
} }