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.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using System.Threading;
@ -311,25 +312,43 @@ EndSelection:<<<<<<<4
try {
MemoryStream imageStream = null;
if (!isValidStream(imageStream) && formats.Contains(FORMAT_PNG)) {
if (formats.Contains(FORMAT_PNG)) {
imageStream = GetFromDataObject(dataObject, FORMAT_PNG) as MemoryStream;
}
if (!isValidStream(imageStream) && formats.Contains(FORMAT_JPG)) {
imageStream = GetFromDataObject(dataObject, FORMAT_JPG) as MemoryStream;
}
if (!isValidStream(imageStream) && 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);
if (isValidStream(imageStream)) {
try {
using (Image tmpImage = Image.FromStream(imageStream)) {
return ImageHelper.Clone(tmpImage);
}
} catch (Exception streamImageEx) {
LOG.Error("Problem retrieving PNG image from clipboard.", streamImageEx);
}
} 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
try {
// If the EnableSpecialDIBClipboardReader flag in the config is set, use the code from:
@ -466,14 +485,19 @@ EndSelection:<<<<<<<4
MemoryStream dibStream = null;
MemoryStream pngStream = null;
Image imageToSave = null;
bool disposeImage = false;
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 {
// Create PNG stream
if (config.ClipboardFormats.Contains(ClipboardFormat.PNG)) {
pngStream = new MemoryStream();
// PNG works for e.g. Powerpoint
SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
ImageOutput.SaveToStream(surface, pngStream, pngOutputSettings);
ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings);
pngStream.Seek(0, SeekOrigin.Begin);
// Set the PNG stream
ido.SetData(FORMAT_PNG, false, pngStream);
@ -482,13 +506,12 @@ EndSelection:<<<<<<<4
LOG.Error("Error creating PNG for the Clipboard.", pngEX);
}
try {
if (config.ClipboardFormats.Contains(ClipboardFormat.DIB)) {
using (MemoryStream tmpBmpStream = new MemoryStream()) {
// Save image as BMP
SurfaceOutputSettings bmpOutputSettings = new SurfaceOutputSettings(OutputFormat.bmp, 100, false);
ImageOutput.SaveToStream(surface, tmpBmpStream, bmpOutputSettings);
ImageOutput.SaveToStream(imageToSave, null, tmpBmpStream, bmpOutputSettings);
dibStream = new MemoryStream();
// 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
// reported with bug #3594681
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);
}
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!
// Check if Bitmap is wanted
if (config.ClipboardFormats.Contains(ClipboardFormat.BITMAP)) {
using (Image tmpImage = surface.GetImageForExport()) {
ido.SetImage(tmpImage);
// Place the DataObject to the clipboard
SetDataObject(ido, true);
}
ido.SetImage(imageToSave);
// Place the DataObject to the clipboard
SetDataObject(ido, true);
} else {
// Place the DataObject to the clipboard
SetDataObject(ido, true);
@ -542,6 +568,10 @@ EndSelection:<<<<<<<4
dibStream.Dispose();
dibStream = null;
}
// cleanup if needed
if (disposeImage && imageToSave != null) {
imageToSave.Dispose();
}
}
}

View file

@ -69,117 +69,65 @@ namespace GreenshotPlugin.Core {
return propertyItem;
}
#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>
/// Saves image to stream with specified quality
/// 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.
/// </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;
bool disposeImage = false;
bool useMemoryStream = false;
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 {
// 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;
}
}
// 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);
if (disposeImage) {
imageToSave.Dispose();
}
// Make sure the image is disposed!
disposeImage = true;
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);
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.");
}
}
}
// 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);
}
}
imageFormat = ImageFormat.Png;
break;
}
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) {
EncoderParameters parameters = new EncoderParameters(1);
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;
break;
}
@ -206,7 +164,17 @@ namespace GreenshotPlugin.Core {
throw new ApplicationException("No JPG encoder found, this should not happen.");
}
} 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.
@ -231,9 +199,100 @@ namespace GreenshotPlugin.Core {
if (memoryStream != null) {
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);
}
}
}