Refactoring which replaces the Image in the ImageOutput for an ISurface, this makes it possible to save the Surface to any possible file format and even make it possible to pass some additional information in the OutputSettings. (Except reducing the colors, there are no other settings yet)

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2376 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2012-12-11 14:36:20 +00:00
commit 5541e2e1c7
27 changed files with 191 additions and 290 deletions

View file

@ -335,10 +335,10 @@ EndSelection:<<<<<<<4
SetDataObject(ido);
}
private static string getHTMLString(Image image, string filename) {
private static string getHTMLString(ISurface surface, string filename) {
string utf8EncodedHTMLString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HTML_CLIPBOARD_STRING));
utf8EncodedHTMLString= utf8EncodedHTMLString.Replace("${width}", image.Width.ToString());
utf8EncodedHTMLString= utf8EncodedHTMLString.Replace("${height}", image.Height.ToString());
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${width}", surface.Image.Width.ToString());
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${height}", surface.Image.Height.ToString());
utf8EncodedHTMLString= utf8EncodedHTMLString.Replace("${file}", filename);
StringBuilder sb=new StringBuilder();
sb.Append(utf8EncodedHTMLString);
@ -349,10 +349,10 @@ EndSelection:<<<<<<<4
return sb.ToString();
}
private static string getHTMLDataURLString(Image image, MemoryStream pngStream) {
private static string getHTMLDataURLString(ISurface surface, MemoryStream pngStream) {
string utf8EncodedHTMLString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HTML_CLIPBOARD_BASE64_STRING));
utf8EncodedHTMLString= utf8EncodedHTMLString.Replace("${width}", image.Width.ToString());
utf8EncodedHTMLString= utf8EncodedHTMLString.Replace("${height}", image.Height.ToString());
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${width}", surface.Image.Width.ToString());
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${height}", surface.Image.Height.ToString());
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${format}", "png");
utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${data}", Convert.ToBase64String(pngStream.GetBuffer(),0, (int)pngStream.Length));
StringBuilder sb=new StringBuilder();
@ -374,7 +374,7 @@ EndSelection:<<<<<<<4
/// For this problem the user should not use the direct paste (=Dib), but select Bitmap
/// </summary>
private const int BITMAPFILEHEADER_LENGTH = 14;
public static void SetClipboardData(Image image) {
public static void SetClipboardData(ISurface surface) {
DataObject ido = new DataObject();
// This will work for Office and most other applications
@ -389,7 +389,7 @@ EndSelection:<<<<<<<4
pngStream = new MemoryStream();
// PNG works for Powerpoint
OutputSettings pngOutputSettings = new OutputSettings(OutputFormat.png, 100, false);
ImageOutput.SaveToStream(image, pngStream, pngOutputSettings);
ImageOutput.SaveToStream(surface, pngStream, pngOutputSettings);
pngStream.Seek(0, SeekOrigin.Begin);
}
@ -402,7 +402,7 @@ EndSelection:<<<<<<<4
bmpStream = new MemoryStream();
// Save image as BMP
OutputSettings bmpOutputSettings = new OutputSettings(OutputFormat.bmp, 100, false);
ImageOutput.SaveToStream(image, bmpStream, bmpOutputSettings);
ImageOutput.SaveToStream(surface, bmpStream, bmpOutputSettings);
imageStream = new MemoryStream();
// Copy the source, but skip the "BITMAPFILEHEADER" which has a size of 14
@ -414,11 +414,11 @@ EndSelection:<<<<<<<4
// Set the HTML
if (config.ClipboardFormats.Contains(ClipboardFormat.HTML)) {
string tmpFile = ImageOutput.SaveToTmpFile(image, new OutputSettings(OutputFormat.png), null);
string html = getHTMLString(image, tmpFile);
string tmpFile = ImageOutput.SaveToTmpFile(surface, new OutputSettings(OutputFormat.png), null);
string html = getHTMLString(surface, tmpFile);
ido.SetText(html, TextDataFormat.Html);
} else if (config.ClipboardFormats.Contains(ClipboardFormat.HTMLDATAURL)) {
string html = getHTMLDataURLString(image, pngStream);
string html = getHTMLDataURLString(surface, pngStream);
ido.SetText(html, TextDataFormat.Html);
}
} finally {

View file

@ -74,7 +74,7 @@ namespace GreenshotPlugin.Core {
/// 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(Image imageToSave, Stream stream, OutputSettings outputSettings) {
public static void SaveToStream(ISurface surface, Stream stream, OutputSettings outputSettings) {
ImageFormat imageFormat = null;
bool disposeImage = false;
bool useMemoryStream = false;
@ -93,6 +93,7 @@ namespace GreenshotPlugin.Core {
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)
@ -109,36 +110,52 @@ namespace GreenshotPlugin.Core {
break;
}
// Removing transparency if it's not supported
if (imageFormat != ImageFormat.Png) {
imageToSave = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
// check what image we want to save
Image imageToSave = null;
if (outputSettings.Format == OutputFormat.greenshot) {
// 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;
}
// check for color reduction, forced or automatically
if (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.");
Image 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);
}
}
} else {
LOG.Info("Skipping color reduction test, OutputFileAutoReduceColors is set to false.");
}
try {
// Removing transparency if it's not supported
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
if (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.");
Image 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);
}
}
} else {
LOG.Info("Skipping color reduction test, OutputFileAutoReduceColors is set to false.");
}
// Create meta-data
PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot");
if (softwareUsedPropertyItem != null) {
@ -172,18 +189,27 @@ namespace GreenshotPlugin.Core {
if (!foundEncoder) {
throw new ApplicationException("No JPG encoder found, this should not happen.");
}
} else if (imageFormat != ImageFormat.Png && Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) {
// No transparency in target format
using (Bitmap tmpBitmap = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb)) {
tmpBitmap.Save(targetStream, imageFormat);
}
} else {
imageToSave.Save(targetStream, imageFormat);
}
// If we used a memory stream, we need to stream the memory stream to the original stream.
if (useMemoryStream) {
memoryStream.WriteTo(stream);
}
// Output the surface elements, size and marker to the stream
if (outputSettings.Format == OutputFormat.greenshot) {
using (MemoryStream tmpStream = new MemoryStream()) {
long bytesWritten = surface.SaveElementsToStream(tmpStream);
using (BinaryWriter writer = new BinaryWriter(tmpStream)) {
writer.Write(bytesWritten);
Version v = Assembly.GetExecutingAssembly().GetName().Version;
byte[] marker = System.Text.Encoding.ASCII.GetBytes(String.Format("Greenshot{0:00}.{1:00}", v.Major, v.Minor));
writer.Write(marker);
tmpStream.WriteTo(stream);
}
}
}
} finally {
if (memoryStream != null) {
memoryStream.Dispose();
@ -195,36 +221,6 @@ namespace GreenshotPlugin.Core {
}
}
/// <summary>
/// Save a Greenshot surface
/// </summary>
/// <param name="surface">Surface to save</param>
/// <param name="fullPath">Path to file</param>
public static void SaveGreenshotSurface(ISurface surface, string fullPath) {
fullPath = FilenameHelper.MakeFQFilenameSafe(fullPath);
string path = Path.GetDirectoryName(fullPath);
// Get output settings from the configuration
OutputSettings outputSettings = new OutputSettings(OutputFormat.png);
// check whether path exists - if not create it
DirectoryInfo di = new DirectoryInfo(path);
if (!di.Exists) {
Directory.CreateDirectory(di.FullName);
}
using (FileStream stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write)) {
SaveToStream(surface.Image, stream, outputSettings);
long bytesWritten = surface.SaveElementsToStream(stream);
using (BinaryWriter writer = new BinaryWriter(stream)) {
writer.Write(bytesWritten);
Version v = Assembly.GetExecutingAssembly().GetName().Version;
string marker = String.Format("Greenshot{0:00}.{1:00}", v.Major, v.Minor);
using (StreamWriter streamWriter = new StreamWriter(stream)) {
streamWriter.Write(marker);
}
}
}
}
/// <summary>
/// Load a Greenshot surface
/// </summary>
@ -274,46 +270,6 @@ namespace GreenshotPlugin.Core {
return returnSurface;
}
/// <summary>
/// Saves image to specific path with specified quality
/// </summary>
public static void Save(Image image, string fullPath, bool allowOverwrite, OutputSettings outputSettings, bool copyPathToClipboard) {
fullPath = FilenameHelper.MakeFQFilenameSafe(fullPath);
string path = Path.GetDirectoryName(fullPath);
// check whether path exists - if not create it
DirectoryInfo di = new DirectoryInfo(path);
if (!di.Exists) {
Directory.CreateDirectory(di.FullName);
}
string extension = Path.GetExtension(fullPath);
if (extension != null && extension.StartsWith(".")) {
extension = extension.Substring(1);
}
OutputFormat format = OutputFormat.png;
try {
if (extension != null) {
format = (OutputFormat)Enum.Parse(typeof(OutputFormat), extension.ToLower());
}
} catch (ArgumentException ae) {
LOG.Warn("Couldn't parse extension: " + extension, ae);
}
if (!allowOverwrite && File.Exists(fullPath)) {
ArgumentException throwingException = new ArgumentException("File '" + fullPath + "' already exists.");
throwingException.Data.Add("fullPath", fullPath);
throw throwingException;
}
LOG.DebugFormat("Saving image to {0}", fullPath);
// Create the stream and call SaveToStream
using (FileStream stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write)) {
SaveToStream(image, stream, outputSettings);
}
if (copyPathToClipboard) {
ClipboardHelper.SetClipboardData(fullPath);
}
}
/// <summary>
/// Saves image to specific path with specified quality
/// </summary>
@ -332,16 +288,10 @@ namespace GreenshotPlugin.Core {
throwingException.Data.Add("fullPath", fullPath);
throw throwingException;
}
LOG.DebugFormat("Saving image to {0}", fullPath);
LOG.DebugFormat("Saving surface to {0}", fullPath);
// Create the stream and call SaveToStream
if (outputSettings.Format == OutputFormat.greenshot) {
SaveGreenshotSurface(surface, fullPath);
} else {
using (FileStream stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write)) {
using (Image image = surface.GetImageForExport()) {
SaveToStream(image, stream, outputSettings);
}
}
using (FileStream stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write)) {
SaveToStream(surface, stream, outputSettings);
}
if (copyPathToClipboard) {
@ -367,23 +317,6 @@ namespace GreenshotPlugin.Core {
}
return format;
}
/// <summary>
/// saves img to fullpath
/// </summary>
/// <param name="img">the image to save</param>
/// <param name="fullPath">the absolute destination path including file name</param>
/// <param name="allowOverwrite">true if overwrite is allowed, false if not</param>
public static void Save(Image img, string fullPath, bool allowOverwrite) {
OutputFormat format = FormatForFilename(fullPath);
// Get output settings from the configuration
OutputSettings outputSettings = new OutputSettings(format);
if (conf.OutputFilePromptQuality) {
QualityDialog qualityDialog = new QualityDialog(outputSettings);
qualityDialog.ShowDialog();
}
Save(img, fullPath, allowOverwrite, outputSettings, conf.OutputFileCopyPathToClipboard);
}
#endregion
#region save-as
@ -460,7 +393,7 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
public static string SaveToTmpFile(Image image, OutputSettings outputSettings, string destinationPath) {
public static string SaveToTmpFile(ISurface surface, OutputSettings outputSettings, string destinationPath) {
string tmpFile = Path.GetRandomFileName() + "." + outputSettings.Format.ToString();
// Prevent problems with "other characters", which could cause problems
tmpFile = Regex.Replace(tmpFile, @"[^\d\w\.]", "");
@ -471,7 +404,7 @@ namespace GreenshotPlugin.Core {
LOG.Debug("Creating TMP File : " + tmpPath);
try {
ImageOutput.Save(image, tmpPath, true, outputSettings, false);
ImageOutput.Save(surface, tmpPath, true, outputSettings, false);
tmpFileCache.Add(tmpPath, tmpPath);
} catch (Exception) {
return null;

View file

@ -413,13 +413,13 @@ namespace GreenshotPlugin.Core {
/// <summary>
/// A container to supply images to a Multi-part form data upload
/// </summary>
public class ImageContainer : IBinaryContainer {
private Image image;
public class SurfaceContainer : IBinaryContainer {
private ISurface surface;
private OutputSettings outputSettings;
private string fileName;
public ImageContainer(Image image, OutputSettings outputSettings, string filename) {
this.image = image;
public SurfaceContainer(ISurface surface, OutputSettings outputSettings, string filename) {
this.surface = surface;
this.outputSettings = outputSettings;
this.fileName = filename;
}
@ -431,7 +431,7 @@ namespace GreenshotPlugin.Core {
/// <returns>string</returns>
public string ToBase64String(Base64FormattingOptions formattingOptions) {
using (MemoryStream stream = new MemoryStream()) {
ImageOutput.SaveToStream(image, stream, outputSettings);
ImageOutput.SaveToStream(surface, stream, outputSettings);
return System.Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length, formattingOptions);
}
}
@ -443,7 +443,7 @@ namespace GreenshotPlugin.Core {
/// <returns>byte[]</returns>
public byte[] ToByteArray() {
using (MemoryStream stream = new MemoryStream()) {
ImageOutput.SaveToStream(image, stream, outputSettings);
ImageOutput.SaveToStream(surface, stream, outputSettings);
return stream.ToArray();
}
}
@ -462,7 +462,7 @@ namespace GreenshotPlugin.Core {
"image/" + outputSettings.Format.ToString());
formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, Encoding.UTF8.GetByteCount(header));
ImageOutput.SaveToStream(image, formDataStream, outputSettings);
ImageOutput.SaveToStream(surface, formDataStream, outputSettings);
}
/// <summary>
@ -471,7 +471,7 @@ namespace GreenshotPlugin.Core {
/// <param name="dataStream"></param>
public void WriteToStream(Stream dataStream) {
// Write the file data directly to the Stream, rather than serializing it to a string.
ImageOutput.SaveToStream(image, dataStream, outputSettings);
ImageOutput.SaveToStream(surface, dataStream, outputSettings);
}
/// <summary>