mirror of
https://github.com/greenshot/greenshot
synced 2025-08-14 18:57:28 -07:00
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:
parent
efda1e3025
commit
09400ccdbe
2 changed files with 216 additions and 127 deletions
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue