/// <summary> /// Helper method to try to get an image in the specified format from the dataObject /// </summary> /// <param name="format">string with the format</param> /// <param name="dataObject">IDataObject</param> /// <returns>Image or null</returns> private static Image GetImageFormat(string format, IDataObject dataObject) { MemoryStream imageStream = GetFromDataObject(dataObject, format) as MemoryStream; if (isValidStream(imageStream)) { try { using (FileStream fs = new FileStream(@"C:\Localdata\test.png", FileMode.OpenOrCreate)) { imageStream.WriteTo(fs); } imageStream.Seek(0, SeekOrigin.Begin); using (Image tmpImage = Image.FromStream(imageStream, true, true)) { if (tmpImage != null) { LOG.InfoFormat("Got image with clipboard format {0} from the clipboard.", format); return(ImageHelper.Clone(tmpImage)); } } } catch (Exception streamImageEx) { LOG.Error(string.Format("Problem retrieving {0} from clipboard.", format), streamImageEx); } } return(null); }
/// <summary> /// Download the uri to Bitmap /// </summary> /// <param name="url">Of an image</param> /// <returns>Bitmap</returns> public static Image DownloadImage(string url) { try { string content; using (MemoryStream memoryStream = GetAsMemoryStream(url)) { try { using (Image image = Image.FromStream(memoryStream)) { return(ImageHelper.Clone(image, PixelFormat.Format32bppArgb)); } } catch (Exception) { // If we arrive here, the image loading didn't work, try to see if the response has a http(s) URL to an image and just take this instead. using (StreamReader streamReader = new StreamReader(memoryStream, Encoding.UTF8, true)) { content = streamReader.ReadLine(); } Regex imageUrlRegex = new Regex(@"(http|https)://.*(\.png|\.gif|\.jpg|\.tiff|\.jpeg|\.bmp)"); Match match = imageUrlRegex.Match(content); if (match.Success) { using (MemoryStream memoryStream2 = GetAsMemoryStream(match.Value)) { using (Image image = Image.FromStream(memoryStream2)) { return(ImageHelper.Clone(image, PixelFormat.Format32bppArgb)); } } } throw; } } } catch (Exception e) { LOG.Error("Problem downloading the image from: " + url, e); } return(null); }
/// <summary> /// Load a Greenshot surface /// </summary> /// <param name="fullPath"></param> /// <returns></returns> public static ISurface LoadGreenshotSurface(string fullPath, ISurface returnSurface) { if (string.IsNullOrEmpty(fullPath)) { return(null); } Image fileImage = null; LOG.InfoFormat("Loading image from file {0}", fullPath); // Fixed lock problem Bug #3431881 using (Stream surfaceFileStream = File.OpenRead(fullPath)) { // And fixed problem that the bitmap stream is disposed... by Cloning the image // This also ensures the bitmap is correctly created // We create a copy of the bitmap, so everything else can be disposed surfaceFileStream.Position = 0; using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) { LOG.DebugFormat("Loaded {0} with Size {1}x{2} and PixelFormat {3}", fullPath, tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); fileImage = ImageHelper.Clone(tmpImage); } // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) const int markerSize = 14; surfaceFileStream.Seek(-markerSize, SeekOrigin.End); string greenshotMarker; using (StreamReader streamReader = new StreamReader(surfaceFileStream)) { greenshotMarker = streamReader.ReadToEnd(); if (greenshotMarker == null || !greenshotMarker.StartsWith("Greenshot")) { throw new ArgumentException(string.Format("{0} is not a Greenshot file!", fullPath)); } LOG.InfoFormat("Greenshot file format: {0}", greenshotMarker); const int filesizeLocation = 8 + markerSize; surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End); long bytesWritten = 0; using (BinaryReader reader = new BinaryReader(surfaceFileStream)) { bytesWritten = reader.ReadInt64(); surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End); returnSurface.LoadElementsFromStream(surfaceFileStream); } } } if (fileImage != null) { returnSurface.Image = fileImage; LOG.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", fullPath, fileImage.Width, fileImage.Height, fileImage.PixelFormat, fileImage.HorizontalResolution, fileImage.VerticalResolution); } return(returnSurface); }
/// <summary> /// the DIB readed should solve the issue reported here: https://sourceforge.net/projects/greenshot/forums/forum/676083/topic/6354353/index/page/1 /// </summary> /// <returns>Image</returns> private static Image GetDIBImage(IDataObject dataObejct) { try { // If the EnableSpecialDIBClipboardReader flag in the config is set, use the code from: // http://www.thomaslevesque.com/2009/02/05/wpf-paste-an-image-from-the-clipboard/ // to read the DeviceIndependentBitmap from the clipboard, this might fix bug 3576125 if (config.EnableSpecialDIBClipboardReader) { MemoryStream dibStream = GetFromDataObject(dataObejct, DataFormats.Dib) as MemoryStream; if (isValidStream(dibStream)) { LOG.Info("Found valid DIB stream, trying to process it."); byte[] dibBuffer = new byte[dibStream.Length]; dibStream.Read(dibBuffer, 0, dibBuffer.Length); BitmapInfoHeader infoHeader = BinaryStructHelper.FromByteArray <BitmapInfoHeader>(dibBuffer); LOG.InfoFormat("Using special DIB format reader for biCompression {0}", infoHeader.biCompression); int fileHeaderSize = Marshal.SizeOf(typeof(BitmapFileHeader)); uint infoHeaderSize = infoHeader.biSize; int fileSize = (int)(fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage); BitmapFileHeader fileHeader = new BitmapFileHeader(); fileHeader.bfType = BitmapFileHeader.BM; fileHeader.bfSize = fileSize; fileHeader.bfReserved1 = 0; fileHeader.bfReserved2 = 0; fileHeader.bfOffBits = (int)(fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4); byte[] fileHeaderBytes = BinaryStructHelper.ToByteArray <BitmapFileHeader>(fileHeader); using (MemoryStream bitmapStream = new MemoryStream()) { bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize); bitmapStream.Write(dibBuffer, 0, dibBuffer.Length); bitmapStream.Seek(0, SeekOrigin.Begin); using (Image tmpImage = Image.FromStream(bitmapStream)) { if (tmpImage != null) { return(ImageHelper.Clone(tmpImage)); } } } } } else { LOG.Info("Skipping special DIB format reader as it's disabled in the configuration."); } } catch (Exception dibEx) { LOG.Error("Problem retrieving DIB from clipboard.", dibEx); } return(null); }
/// <summary> /// Download the url to Bitmap /// </summary> /// <param name="baseUri"></param> /// <returns>Bitmap</returns> public static Image DownloadImage(string url) { try { HttpWebRequest request = (HttpWebRequest)NetworkHelper.CreateWebRequest(url); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); if (request.HaveResponse) { using (Image image = Image.FromStream(response.GetResponseStream())) { return(ImageHelper.Clone(image)); } } } catch (Exception e) { LOG.Error("Problem downloading the image from: " + url, e); } return(null); }
/// <summary> /// Download the uri to Bitmap /// </summary> /// <param name="url">Of an image</param> /// <returns>Bitmap</returns> public static Image DownloadImage(string url) { try { HttpWebRequest request = CreateWebRequest(url); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); if (request.HaveResponse) { using (Stream responseStream = response.GetResponseStream()) { if (responseStream != null) { using (Image image = Image.FromStream(responseStream)) { return(ImageHelper.Clone(image, PixelFormat.Format32bppArgb)); } } } } } catch (Exception e) { LOG.Error("Problem downloading the image from: " + url, e); } return(null); }
/// <summary> /// Helper method to try to get an image in the specified format from the dataObject /// the DIB readed should solve the issue reported here: https://sourceforge.net/projects/greenshot/forums/forum/676083/topic/6354353/index/page/1 /// It also supports Format17/DibV5, by using the following information: http://stackoverflow.com/a/14335591 /// </summary> /// <param name="format">string with the format</param> /// <param name="dataObject">IDataObject</param> /// <returns>Image or null</returns> private static Image GetImageForFormat(string format, IDataObject dataObject) { object clipboardObject = GetFromDataObject(dataObject, format); MemoryStream imageStream = clipboardObject as MemoryStream; if (!isValidStream(imageStream)) { // TODO: add "HTML Format" support here... return(clipboardObject as Image); } else { if (config.EnableSpecialDIBClipboardReader) { if (format == FORMAT_17 || format == DataFormats.Dib) { LOG.Info("Found DIB stream, trying to process it."); try { byte[] dibBuffer = new byte[imageStream.Length]; imageStream.Read(dibBuffer, 0, dibBuffer.Length); BITMAPINFOHEADER infoHeader = BinaryStructHelper.FromByteArray <BITMAPINFOHEADER>(dibBuffer); if (!infoHeader.IsDibV5) { LOG.InfoFormat("Using special DIB <v5 format reader with biCompression {0}", infoHeader.biCompression); int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER)); uint infoHeaderSize = infoHeader.biSize; int fileSize = (int)(fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage); BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER(); fileHeader.bfType = BITMAPFILEHEADER.BM; fileHeader.bfSize = fileSize; fileHeader.bfReserved1 = 0; fileHeader.bfReserved2 = 0; fileHeader.bfOffBits = (int)(fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4); byte[] fileHeaderBytes = BinaryStructHelper.ToByteArray <BITMAPFILEHEADER>(fileHeader); using (MemoryStream bitmapStream = new MemoryStream()) { bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize); bitmapStream.Write(dibBuffer, 0, dibBuffer.Length); bitmapStream.Seek(0, SeekOrigin.Begin); using (Image tmpImage = Image.FromStream(bitmapStream)) { if (tmpImage != null) { return(ImageHelper.Clone(tmpImage)); } } } } else { LOG.Info("Using special DIBV5 / Format17 format reader"); // CF_DIBV5 IntPtr gcHandle = IntPtr.Zero; try { GCHandle handle = GCHandle.Alloc(dibBuffer, GCHandleType.Pinned); gcHandle = GCHandle.ToIntPtr(handle); return(new Bitmap(infoHeader.biWidth, infoHeader.biHeight, -(int)(infoHeader.biSizeImage / infoHeader.biHeight), infoHeader.biBitCount == 32 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb, new IntPtr(handle.AddrOfPinnedObject().ToInt32() + infoHeader.OffsetToPixels + (infoHeader.biHeight - 1) * (int)(infoHeader.biSizeImage / infoHeader.biHeight)))); } catch (Exception ex) { LOG.Error("Problem retrieving Format17 from clipboard.", ex); } finally { if (gcHandle == IntPtr.Zero) { GCHandle.FromIntPtr(gcHandle).Free(); } } } } catch (Exception dibEx) { LOG.Error("Problem retrieving DIB from clipboard.", dibEx); } } } else { LOG.Info("Skipping special DIB format reader as it's disabled in the configuration."); } try { imageStream.Seek(0, SeekOrigin.Begin); using (Image tmpImage = Image.FromStream(imageStream, true, true)) { if (tmpImage != null) { LOG.InfoFormat("Got image with clipboard format {0} from the clipboard.", format); return(ImageHelper.Clone(tmpImage)); } } } catch (Exception streamImageEx) { LOG.Error(string.Format("Problem retrieving {0} from clipboard.", format), streamImageEx); } } return(null); }
/// <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> /// <param name="imageToSave">image to save</param> /// <param name="surface">surface for the elements, needed 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 useMemoryStream = false; MemoryStream memoryStream = null; if (outputSettings.Format == OutputFormat.greenshot && surface == null) { throw new ArgumentException("Surface needs to be se when using OutputFormat.Greenshot"); } try { 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; } LOG.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat); // Check if we want to use a memory stream, to prevent a issue which happens with Windows before "7". // The save is made to the targetStream, this is directed to either the MemoryStream or the original Stream targetStream = stream; if (useMemoryStream) { memoryStream = new MemoryStream(); targetStream = memoryStream; } if (imageFormat == ImageFormat.Jpeg) { bool foundEncoder = false; foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders()) { if (imageCodec.FormatID == imageFormat.Guid) { EncoderParameters parameters = new EncoderParameters(1); parameters.Param[0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality); // 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; } } if (!foundEncoder) { throw new ApplicationException("No JPG encoder found, this should not happen."); } } else { // 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 (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 = 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(); } } }
/// <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) { // Pick the best fit var sizes = new[] { 16, 32, 48 }; int size = 256; foreach (var possibleSize in sizes) { if (image.Width <= possibleSize && image.Height <= possibleSize) { size = possibleSize; break; } } var imageStream = new MemoryStream(); if (image.Width == size && image.Height == size) { using (var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb)) { clonedImage.Save(imageStream, ImageFormat.Png); imageSizes.Add(new Size(size, size)); } } else { // Resize to the specified size, 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, size, size, 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(); } }
/// <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> /// <param name="imageToSave">image to save</param> /// <param name="surface">surface for the elements, needed 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) { bool useMemoryStream = false; MemoryStream memoryStream = null; if (outputSettings.Format == OutputFormat.greenshot && surface == null) { throw new ArgumentException("Surface needs to be set when using OutputFormat.Greenshot"); } try { ImageFormat imageFormat; 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.ico: imageFormat = ImageFormat.Icon; break; default: imageFormat = ImageFormat.Png; break; } Log.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat); // Check if we want to use a memory stream, to prevent issues with non seakable streams // The save is made to the targetStream, this is directed to either the MemoryStream or the original Stream targetStream = stream; if (!stream.CanSeek) { useMemoryStream = true; Log.Warn("Using memorystream prevent an issue with saving to a non seekable stream."); memoryStream = new MemoryStream(); targetStream = memoryStream; } if (Equals(imageFormat, ImageFormat.Jpeg)) { bool foundEncoder = false; foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders()) { if (imageCodec.FormatID == imageFormat.Guid) { EncoderParameters parameters = new EncoderParameters(1) { Param = { [0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality) } }; // 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(); } else { AddTag(imageToSave); imageToSave.Save(targetStream, imageCodec, parameters); } foundEncoder = true; break; } } if (!foundEncoder) { 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 { bool needsDispose = false; // Removing transparency if it's not supported in the output if (!Equals(imageFormat, ImageFormat.Png) && Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) { imageToSave = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); needsDispose = true; } AddTag(imageToSave); // Added for OptiPNG bool processed = false; if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand)) { processed = ProcessPngImageExternally(imageToSave, targetStream); } if (!processed) { imageToSave.Save(targetStream, imageFormat); } if (needsDispose) { imageToSave.Dispose(); } } // 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) { return; } 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 = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}"); writer.Write(marker); tmpStream.WriteTo(stream); } } } finally { memoryStream?.Dispose(); } }