From dede5cf29282582f41bb8811f8e0541073dfdd70 Mon Sep 17 00:00:00 2001 From: RKrom Date: Fri, 10 Feb 2012 11:55:21 +0000 Subject: [PATCH] Fixed and optimized some drawing routines, still didn't find a solution for shadowing when target format has transparency. git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@1648 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4 --- Greenshot/Drawing/ArrowContainer.cs | 11 +- Greenshot/Drawing/BitmapContainer.cs | 10 +- Greenshot/Drawing/CursorContainer.cs | 5 + Greenshot/Drawing/DrawableContainer.cs | 2 +- Greenshot/Drawing/EllipseContainer.cs | 5 +- Greenshot/Drawing/FreehandContainer.cs | 4 +- Greenshot/Drawing/IconContainer.cs | 5 + Greenshot/Drawing/LineContainer.cs | 6 +- Greenshot/Drawing/MetafileContainer.cs | 5 + Greenshot/Drawing/RectangleContainer.cs | 13 +- Greenshot/Drawing/Surface.cs | 32 +-- Greenshot/Drawing/TextContainer.cs | 17 +- GreenshotNetworkImportPlugin/HTTPReceiver.cs | 2 +- GreenshotPlugin/Core/BitmapBuffer.cs | 82 +++---- GreenshotPlugin/Core/ImageHelper.cs | 230 ++++++++++++------- GreenshotPlugin/Interop/COMWrapper.cs | 7 +- 16 files changed, 275 insertions(+), 161 deletions(-) diff --git a/Greenshot/Drawing/ArrowContainer.cs b/Greenshot/Drawing/ArrowContainer.cs index 64926b8e5..67a90bc6d 100644 --- a/Greenshot/Drawing/ArrowContainer.cs +++ b/Greenshot/Drawing/ArrowContainer.cs @@ -44,12 +44,15 @@ namespace Greenshot.Drawing { AddField(GetType(), FieldType.ARROWHEADS, Greenshot.Drawing.ArrowContainer.ArrowHeadCombination.END_POINT); } - public override void Draw(Graphics g, RenderMode rm) { + public override void Draw(Graphics graphics, RenderMode rm) { int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); bool shadow = GetFieldValueAsBool(FieldType.SHADOW); if (lineThickness > 0 ) { - g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); ArrowHeadCombination heads = (ArrowHeadCombination)GetFieldValue(FieldType.ARROWHEADS); if (shadow) { @@ -63,7 +66,7 @@ namespace Greenshot.Drawing { shadowCapPen.Width = lineThickness; SetArrowHeads(heads, shadowCapPen); - g.DrawLine(shadowCapPen, + graphics.DrawLine(shadowCapPen, this.Left + currentStep, this.Top + currentStep, this.Left + currentStep + this.Width, @@ -80,7 +83,7 @@ namespace Greenshot.Drawing { if ( pen.Width > 0 ) { SetArrowHeads(heads, pen); - g.DrawLine(pen, this.Left, this.Top, this.Left + this.Width, this.Top + this.Height); + graphics.DrawLine(pen, this.Left, this.Top, this.Left + this.Width, this.Top + this.Height); } } } diff --git a/Greenshot/Drawing/BitmapContainer.cs b/Greenshot/Drawing/BitmapContainer.cs index d94b8e6e2..e9fab703f 100644 --- a/Greenshot/Drawing/BitmapContainer.cs +++ b/Greenshot/Drawing/BitmapContainer.cs @@ -28,6 +28,7 @@ using Greenshot.Drawing.Fields; using Greenshot.Helpers; using Greenshot.Plugin.Drawing; using GreenshotPlugin.Core; +using System.Drawing.Drawing2D; namespace Greenshot.Drawing { /// @@ -54,7 +55,7 @@ namespace Greenshot.Drawing { if (bitmap != null) { bitmap.Dispose(); } - bitmap = (Bitmap)value.Clone(); + bitmap = ImageHelper.Clone(value); Width = value.Width; Height = value.Height; } @@ -103,6 +104,11 @@ namespace Greenshot.Drawing { public override void Draw(Graphics graphics, RenderMode rm) { if (bitmap != null) { bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + if (shadow) { ImageAttributes ia = new ImageAttributes(); ColorMatrix cm = new ColorMatrix(); @@ -111,7 +117,7 @@ namespace Greenshot.Drawing { cm.Matrix22 = 0; cm.Matrix33 = 0.25f; ia.SetColorMatrix(cm); - graphics.DrawImage(bitmap, new Rectangle(Bounds.Left+2, Bounds.Top+2, Bounds.Width, Bounds.Height), 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, ia); + graphics.DrawImage(bitmap, new Rectangle(Bounds.Left + 2, Bounds.Top + 2, Bounds.Width, Bounds.Height), 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, ia); } graphics.DrawImage(bitmap, Bounds); } diff --git a/Greenshot/Drawing/CursorContainer.cs b/Greenshot/Drawing/CursorContainer.cs index b03888555..f82acfe74 100644 --- a/Greenshot/Drawing/CursorContainer.cs +++ b/Greenshot/Drawing/CursorContainer.cs @@ -24,6 +24,7 @@ using System.IO; using System.Windows.Forms; using Greenshot.Plugin.Drawing; +using System.Drawing.Drawing2D; namespace Greenshot.Drawing { /// @@ -98,6 +99,10 @@ namespace Greenshot.Drawing { public override void Draw(Graphics graphics, RenderMode rm) { if (cursor != null) { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.Default; + graphics.PixelOffsetMode = PixelOffsetMode.None; cursor.DrawStretched(graphics, Bounds); } } diff --git a/Greenshot/Drawing/DrawableContainer.cs b/Greenshot/Drawing/DrawableContainer.cs index abcbdd30f..936dead96 100644 --- a/Greenshot/Drawing/DrawableContainer.cs +++ b/Greenshot/Drawing/DrawableContainer.cs @@ -408,7 +408,7 @@ namespace Greenshot.Drawing { public virtual void DrawContent(Graphics graphics, Bitmap bmp, RenderMode renderMode, Rectangle clipRectangle) { if (Children.Count > 0) { - if(Status != EditStatus.IDLE) { + if (Status != EditStatus.IDLE) { DrawSelectionBorder(graphics, Bounds); } else { if (clipRectangle.Width != 0 && clipRectangle.Height != 0) { diff --git a/Greenshot/Drawing/EllipseContainer.cs b/Greenshot/Drawing/EllipseContainer.cs index 5e40cefc6..416bc4853 100644 --- a/Greenshot/Drawing/EllipseContainer.cs +++ b/Greenshot/Drawing/EllipseContainer.cs @@ -40,7 +40,10 @@ namespace Greenshot.Drawing { } public override void Draw(Graphics graphics, RenderMode renderMode) { - graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); diff --git a/Greenshot/Drawing/FreehandContainer.cs b/Greenshot/Drawing/FreehandContainer.cs index eea8e9fba..ab0a5dadd 100644 --- a/Greenshot/Drawing/FreehandContainer.cs +++ b/Greenshot/Drawing/FreehandContainer.cs @@ -174,7 +174,9 @@ namespace Greenshot.Drawing { /// /// public override void Draw(Graphics graphics, RenderMode renderMode) { - graphics.SmoothingMode = SmoothingMode.AntiAlias; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); diff --git a/Greenshot/Drawing/IconContainer.cs b/Greenshot/Drawing/IconContainer.cs index b58f1163f..a711ae90f 100644 --- a/Greenshot/Drawing/IconContainer.cs +++ b/Greenshot/Drawing/IconContainer.cs @@ -24,6 +24,7 @@ using System.IO; using System.Windows.Forms; using Greenshot.Plugin.Drawing; +using System.Drawing.Drawing2D; namespace Greenshot.Drawing { /// @@ -97,6 +98,10 @@ namespace Greenshot.Drawing { public override void Draw(Graphics graphics, RenderMode rm) { if (icon != null) { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.Default; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.DrawIcon(icon, Bounds); } } diff --git a/Greenshot/Drawing/LineContainer.cs b/Greenshot/Drawing/LineContainer.cs index 889b7c51a..d070a9c78 100644 --- a/Greenshot/Drawing/LineContainer.cs +++ b/Greenshot/Drawing/LineContainer.cs @@ -56,9 +56,11 @@ namespace Greenshot.Drawing { } public override void Draw(Graphics graphics, RenderMode rm) { - graphics.SmoothingMode = SmoothingMode.AntiAlias; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); bool shadow = GetFieldValueAsBool(FieldType.SHADOW); diff --git a/Greenshot/Drawing/MetafileContainer.cs b/Greenshot/Drawing/MetafileContainer.cs index 8057c9b1e..329a98fd6 100644 --- a/Greenshot/Drawing/MetafileContainer.cs +++ b/Greenshot/Drawing/MetafileContainer.cs @@ -24,6 +24,7 @@ using System.Drawing.Imaging; using System.IO; using Greenshot.Plugin.Drawing; +using System.Drawing.Drawing2D; namespace Greenshot.Drawing { /// @@ -111,6 +112,10 @@ namespace Greenshot.Drawing { public override void Draw(Graphics graphics, RenderMode rm) { if (metafile != null) { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.DrawImage(metafile, Bounds); } } diff --git a/Greenshot/Drawing/RectangleContainer.cs b/Greenshot/Drawing/RectangleContainer.cs index 7f28848a8..9ed173395 100644 --- a/Greenshot/Drawing/RectangleContainer.cs +++ b/Greenshot/Drawing/RectangleContainer.cs @@ -41,7 +41,12 @@ namespace Greenshot.Drawing { } - public override void Draw(Graphics g, RenderMode rm) { + public override void Draw(Graphics graphics, RenderMode rm) { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); @@ -61,7 +66,7 @@ namespace Greenshot.Drawing { this.Top + currentStep, this.Width, this.Height); - g.DrawRectangle(shadowPen, shadowRect); + graphics.DrawRectangle(shadowPen, shadowRect); currentStep++; alpha = alpha - (basealpha / steps); } @@ -72,14 +77,14 @@ namespace Greenshot.Drawing { if (!Color.Transparent.Equals(fillColor)) { using (Brush brush = new SolidBrush(fillColor)) { - g.FillRectangle(brush, rect); + graphics.FillRectangle(brush, rect); } } if (lineThickness > 0) { using (Pen pen = new Pen(lineColor)) { pen.Width = lineThickness; - g.DrawRectangle(pen, rect); + graphics.DrawRectangle(pen, rect); } } } diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 9a29be75e..f08eb086d 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -576,6 +576,9 @@ namespace Greenshot.Drawing { MakeUndoable(new SurfaceBackgroundChangeMemento(this, offset), false); SetImage(newImage, false); Invalidate(); + if (SurfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, newImage.Size))) { + SurfaceSizeChanged(this); + } } } @@ -604,8 +607,9 @@ namespace Greenshot.Drawing { public bool ApplyCrop(Rectangle cropRectangle) { if (isCropPossible(ref cropRectangle)) { + Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); // we should not forget to Dispose the images!! - Bitmap tmpImage = ((Bitmap)Image).Clone(cropRectangle, Image.PixelFormat); + Bitmap tmpImage = ImageHelper.CloneArea(Image, cropRectangle, PixelFormat.DontCare); tmpImage.SetResolution(Image.HorizontalResolution, Image.VerticalResolution); Point offset = new Point(-cropRectangle.Left, -cropRectangle.Top); @@ -614,7 +618,7 @@ namespace Greenshot.Drawing { SetImage(tmpImage, false); elements.MoveBy(offset.X, offset.Y); - if (SurfaceSizeChanged != null) { + if (SurfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, tmpImage.Size))) { SurfaceSizeChanged(this); } Invalidate(); @@ -799,14 +803,15 @@ namespace Greenshot.Drawing { private Image GetImage(RenderMode renderMode) { // Generate a copy of the original image with a dpi equal to the default... - Bitmap clone = ImageHelper.CloneImageToBitmap(Image); + Bitmap clone = ImageHelper.Clone(Image); // otherwise we would have a problem drawing the image to the surface... :( using (Graphics graphics = Graphics.FromImage(clone)) { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - elements.Draw(graphics, (Bitmap)clone, renderMode, new Rectangle(Point.Empty, clone.Size)); + // Do not set the following, the containers need to decide themselves + //graphics.SmoothingMode = SmoothingMode.HighQuality; + //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + //graphics.CompositingQuality = CompositingQuality.HighQuality; + //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + elements.Draw(graphics, clone, renderMode, new Rectangle(Point.Empty, clone.Size)); } return clone; } @@ -841,10 +846,11 @@ namespace Greenshot.Drawing { } // Elements might need the bitmap, so we copy the part we need using (Graphics graphics = Graphics.FromImage(buffer)) { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + // do not set the following, the containers need to decide this themselves! + //graphics.SmoothingMode = SmoothingMode.HighQuality; + //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + //graphics.CompositingQuality = CompositingQuality.HighQuality; + //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel); graphics.SetClip(targetGraphics); elements.Draw(graphics, buffer, RenderMode.EDIT, clipRectangle); @@ -852,7 +858,7 @@ namespace Greenshot.Drawing { targetGraphics.DrawImage(buffer, clipRectangle, clipRectangle, GraphicsUnit.Pixel); } else { // Only "simple" elements need to be redrawn, as the image is already drawn before getting the event we don't need the next line: - //targetGraphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel); + // targetGraphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel); elements.Draw(targetGraphics, null, RenderMode.EDIT, clipRectangle); } } diff --git a/Greenshot/Drawing/TextContainer.cs b/Greenshot/Drawing/TextContainer.cs index c788c4e5f..f58a7cc43 100644 --- a/Greenshot/Drawing/TextContainer.cs +++ b/Greenshot/Drawing/TextContainer.cs @@ -28,6 +28,8 @@ using Greenshot.Drawing.Fields; using Greenshot.Helpers; using Greenshot.Plugin.Drawing; using Greenshot.Memento; +using System.Drawing.Drawing2D; +using System.Drawing.Text; namespace Greenshot.Drawing { /// @@ -264,13 +266,18 @@ namespace Greenshot.Drawing { HideTextBox(); } - public override void Draw(Graphics g, RenderMode rm) { - base.Draw(g, rm); + public override void Draw(Graphics graphics, RenderMode rm) { + base.Draw(graphics, rm); UpdateFont(); + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.TextRenderingHint = TextRenderingHint.SystemDefault; Rectangle rect = GuiRectangle.GetGuiRectangle(this.Left, this.Top, this.Width, this.Height); if (Selected && rm == RenderMode.EDIT) { - DrawSelectionBorder(g, rect); + DrawSelectionBorder(graphics, rect); } if (text == null || text.Length == 0 ) { @@ -295,7 +302,7 @@ namespace Greenshot.Drawing { shadowRect.Inflate(-textOffset, -textOffset); } using (Brush fontBrush = new SolidBrush(Color.FromArgb(alpha, 100, 100, 100))) { - g.DrawString(text, font, fontBrush, shadowRect); + graphics.DrawString(text, font, fontBrush, shadowRect); currentStep++; alpha = alpha - basealpha / steps; } @@ -308,7 +315,7 @@ namespace Greenshot.Drawing { fontRect.Inflate(-textOffset,-textOffset); } using (Brush fontBrush = new SolidBrush(lineColor)) { - g.DrawString(text, font, fontBrush, fontRect); + graphics.DrawString(text, font, fontBrush, fontRect); } } diff --git a/GreenshotNetworkImportPlugin/HTTPReceiver.cs b/GreenshotNetworkImportPlugin/HTTPReceiver.cs index 0120fb157..2c272f422 100644 --- a/GreenshotNetworkImportPlugin/HTTPReceiver.cs +++ b/GreenshotNetworkImportPlugin/HTTPReceiver.cs @@ -129,7 +129,7 @@ namespace GreenshotNetworkImportPlugin { // Convert byte[] to Image memoryStream.Write(imageBytes, 0, imageBytes.Length); using (Image image = Bitmap.FromStream(memoryStream, true)) { - ICapture capture = host.GetCapture(ImageHelper.CloneImageToBitmap(image)); + ICapture capture = host.GetCapture(ImageHelper.Clone(image)); capture.CaptureDetails.Title = title; host.ImportCapture(capture); } diff --git a/GreenshotPlugin/Core/BitmapBuffer.cs b/GreenshotPlugin/Core/BitmapBuffer.cs index f53d38896..4132941dc 100644 --- a/GreenshotPlugin/Core/BitmapBuffer.cs +++ b/GreenshotPlugin/Core/BitmapBuffer.cs @@ -33,9 +33,7 @@ namespace GreenshotPlugin.Core { private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(BitmapBuffer)); private bool clone; private Bitmap bitmap; - public Bitmap Bitmap { - get {return bitmap;} - } + [NonSerialized] private BitmapData bmData; [NonSerialized] @@ -113,31 +111,18 @@ namespace GreenshotPlugin.Core { if (sourceRect.Height <= 0 || sourceRect.Width <= 0) { return; } - - if (SupportsPixelFormat(sourceBmp)) { - if (clone) { - // Create copy with supported format - this.bitmap = sourceBmp.Clone(sourceRect, sourceBmp.PixelFormat); - } else { - this.bitmap = sourceBmp; - } + + if (clone) { + this.bitmap = ImageHelper.CloneArea(sourceBmp, sourceRect, PixelFormat.DontCare); + // Set "this" rect to location 0,0 + // as the Cloned Bitmap is only the part we want to work with + this.rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); + } else if (!ImageHelper.SupportsPixelFormat(sourceBmp)) { + throw new ArgumentException("Unsupported pixel format: " + sourceBmp.PixelFormat + " set clone to true!"); } else { - // We can only clone, as we don't support the pixel format! - if (!clone) { - throw new ArgumentException("Not supported pixel format: " + sourceBmp.PixelFormat); - } - // When sourceRect is the whole bitmap there is a GDI+ bug in Clone - // Clone will than return the same PixelFormat as the source - // a quick workaround is using new Bitmap which uses a default of Format32bppArgb - if (sourceRect.Equals(bitmapRect)) { - this.bitmap = new Bitmap(sourceBmp); - } else { - this.bitmap = sourceBmp.Clone(sourceRect, PixelFormat.Format32bppArgb); - } + this.bitmap = sourceBmp; + this.rect = sourceRect; } - // Set "this" rect to location 0,0 - // as the Cloned Bitmap is only the part we want to work with - this.rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); } /** @@ -187,23 +172,29 @@ namespace GreenshotPlugin.Core { * This is called when serializing the object */ public void GetObjectData(SerializationInfo info, StreamingContext ctxt) { - Unlock(); + bool isLocked = bitsLocked; + if (isLocked) { + Unlock(); + } info.AddValue("bitmap", this.bitmap); + if (isLocked) { + Lock(); + } } /** * Lock the bitmap so we have direct access to the memory */ public void Lock() { - if(rect.Width > 0 && rect.Height > 0) { + if(rect.Width > 0 && rect.Height > 0 && !bitsLocked) { bmData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat); bitsLocked = true; - System.IntPtr Scan0 = bmData.Scan0; - pointer = (byte*)(void*)Scan0; + IntPtr Scan0 = bmData.Scan0; + pointer = (byte*)(void*)Scan0; - PrepareForPixelFormat(); - stride = bmData.Stride; + PrepareForPixelFormat(); + stride = bmData.Stride; } } @@ -211,7 +202,7 @@ namespace GreenshotPlugin.Core { * Unlock the System Memory */ private void Unlock() { - if(bitsLocked) { + if (bitsLocked) { bitmap.UnlockBits(bmData); bitsLocked = false; } @@ -242,14 +233,20 @@ namespace GreenshotPlugin.Core { return; } } - // Make sure this.bitmap is unlocked - Unlock(); - + // Make sure this.bitmap is unlocked, if it was locked + bool isLocked = bitsLocked; + if (isLocked) { + Unlock(); + } if (destinationRect.HasValue) { graphics.DrawImage(this.bitmap, destinationRect.Value); } else if (destination.HasValue) { - graphics.DrawImage(this.bitmap, destination.Value); + graphics.DrawImageUnscaled(this.bitmap, destination.Value); + } + // If it was locked, lock it again + if (isLocked) { + Lock(); } } @@ -304,7 +301,7 @@ namespace GreenshotPlugin.Core { int a = (aIndex==-1) ? 255 : (int)pointer[aIndex+offset]; return new int[]{a, pointer[rIndex+offset], pointer[gIndex+offset], pointer[bIndex+offset]}; } else { - return new int[]{0,0,0,0}; + return new int[]{255,255,255,255}; } } @@ -322,15 +319,6 @@ namespace GreenshotPlugin.Core { } } - /** - * Checks if the supplied Bitmap has a PixelFormat we support - */ - private bool SupportsPixelFormat(Bitmap bitmap) { - return (bitmap.PixelFormat.Equals(PixelFormat.Format32bppArgb) || - bitmap.PixelFormat.Equals(PixelFormat.Format32bppRgb) || - bitmap.PixelFormat.Equals(PixelFormat.Format24bppRgb)); - } - /** * Set some internal values for accessing the bitmap according to the PixelFormat */ diff --git a/GreenshotPlugin/Core/ImageHelper.cs b/GreenshotPlugin/Core/ImageHelper.cs index 14fcf3ceb..76a0a3107 100644 --- a/GreenshotPlugin/Core/ImageHelper.cs +++ b/GreenshotPlugin/Core/ImageHelper.cs @@ -80,7 +80,7 @@ namespace GreenshotPlugin.Core { if (image != null && image is Bitmap && ((image.Width * image.Height) > 0)) { cropRectangle.Intersect(new Rectangle(0,0, image.Width, image.Height)); if (cropRectangle.Width != 0 || cropRectangle.Height != 0) { - returnImage = (image as Bitmap).Clone(cropRectangle, image.PixelFormat); + returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare); image.Dispose(); image = returnImage; return true; @@ -90,6 +90,12 @@ namespace GreenshotPlugin.Core { return false; } + /// + /// Helper method for the FindAutoCropRectangle + /// + /// + /// + /// private static Rectangle FindAutoCropRectangle(BitmapBuffer buffer, Point colorPoint) { Rectangle cropRectangle = Rectangle.Empty; Color referenceColor = buffer.GetColorAtWithoutAlpha(colorPoint.X,colorPoint.Y); @@ -163,6 +169,11 @@ namespace GreenshotPlugin.Core { return cropRectangle; } + /// + /// Load an image from file + /// + /// + /// public static Bitmap LoadBitmap(string filename) { if (string.IsNullOrEmpty(filename)) { return null; @@ -179,7 +190,7 @@ namespace GreenshotPlugin.Core { try { using (Image tmpImage = ExtractVistaIcon(imageFileStream)) { if (tmpImage != null) { - fileBitmap = CloneImageToBitmap(tmpImage); + fileBitmap = Clone(tmpImage); } } } catch (Exception vistaIconException) { @@ -192,7 +203,7 @@ namespace GreenshotPlugin.Core { // We create a copy of the bitmap, so everything else can be disposed using (Icon tmpIcon = new Icon(imageFileStream, new Size(1024,1024))) { using (Image tmpImage = tmpIcon.ToBitmap()) { - fileBitmap = ImageHelper.CloneImageToBitmap(tmpImage); + fileBitmap = Clone(tmpImage); } } } catch (Exception iconException) { @@ -204,45 +215,13 @@ namespace GreenshotPlugin.Core { // We create a copy of the bitmap, so everything else can be disposed imageFileStream.Position = 0; using (Image tmpImage = Image.FromStream(imageFileStream, true, true)) { - fileBitmap = ImageHelper.CloneImageToBitmap(tmpImage); + fileBitmap = Clone(tmpImage); } } } return fileBitmap; } - /// - /// Clone the image to a bitmap - /// - /// Image to clone - /// Bitmap - public static Bitmap CloneImageToBitmap(Image srcImage) { - Bitmap returnImage; - int width = srcImage.Width; - int height = srcImage.Height; - float horizontalResolution = srcImage.HorizontalResolution; - float verticalResolution = srcImage.VerticalResolution; - PixelFormat pixelFormat = srcImage.PixelFormat; - if (srcImage is Metafile) { - pixelFormat = PixelFormat.Format32bppArgb; - } - // Make sure Greenshot supports the pixelformat, if not convert to one we support - if (!isSupported(pixelFormat)) { - pixelFormat = PixelFormat.Format24bppRgb; - } - returnImage = new Bitmap(width, height, pixelFormat); - returnImage.SetResolution(horizontalResolution, verticalResolution); - using (Graphics graphics = Graphics.FromImage(returnImage)) { - if (Image.IsAlphaPixelFormat(pixelFormat)) { - graphics.Clear(Color.Transparent); - } else { - graphics.Clear(Color.White); - } - graphics.DrawImageUnscaled(srcImage, 0, 0); - } - return returnImage; - } - /** * Checks if we support the supplied PixelFormat */ @@ -414,9 +393,9 @@ namespace GreenshotPlugin.Core { nullColor = Color.Transparent; } - using (BitmapBuffer bbbDest = new BitmapBuffer(sourceBitmap, applyRect)) { + using (BitmapBuffer bbbDest = new BitmapBuffer(sourceBitmap, applyRect, true)) { bbbDest.Lock(); - using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, applyRect)) { + using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, applyRect, false)) { bbbSrc.Lock(); Random rand = new Random(); @@ -426,25 +405,25 @@ namespace GreenshotPlugin.Core { long[] waSums = new long[wlen]; long[] wcSums = new long[wlen]; long[] aSums = new long[wlen]; - long[] bSums = new long[wlen]; - long[] gSums = new long[wlen]; long[] rSums = new long[wlen]; + long[] gSums = new long[wlen]; + long[] bSums = new long[wlen]; for (int y = 0; y < applyRect.Height; ++y) { long waSum = 0; long wcSum = 0; long aSum = 0; - long bSum = 0; - long gSum = 0; long rSum = 0; + long gSum = 0; + long bSum = 0; for (int wx = 0; wx < wlen; ++wx) { int srcX = wx - r; waSums[wx] = 0; wcSums[wx] = 0; aSums[wx] = 0; - bSums[wx] = 0; - gSums[wx] = 0; rSums[wx] = 0; + gSums[wx] = 0; + bSums[wx] = 0; if (srcX >= 0 && srcX < bbbDest.Width) { for (int wy = 0; wy < wlen; ++wy) { @@ -460,9 +439,9 @@ namespace GreenshotPlugin.Core { wp >>= 8; aSums[wx] += wp * colors[0]; - bSums[wx] += wp * colors[3]; - gSums[wx] += wp * colors[2]; rSums[wx] += wp * colors[1]; + gSums[wx] += wp * colors[2]; + bSums[wx] += wp * colors[3]; } } @@ -470,24 +449,22 @@ namespace GreenshotPlugin.Core { waSum += wwx * waSums[wx]; wcSum += wwx * wcSums[wx]; aSum += wwx * aSums[wx]; - bSum += wwx * bSums[wx]; - gSum += wwx * gSums[wx]; rSum += wwx * rSums[wx]; + gSum += wwx * gSums[wx]; + bSum += wwx * bSums[wx]; } } wcSum >>= 8; - if (waSum == 0 || wcSum == 0) { - if (parentBounds.Contains(applyRect.Left, applyRect.Top + y) ^ invert) { + if (parentBounds.Contains(applyRect.Left, applyRect.Top + y) ^ invert) { + if (waSum == 0 || wcSum == 0) { bbbDest.SetColorAt(0, y, nullColor); - } - } else { - int alpha = (int)(aSum / waSum); - int blue = (int)(bSum / wcSum); - int green = (int)(gSum / wcSum); - int red = (int)(rSum / wcSum); - if (parentBounds.Contains(applyRect.Left, applyRect.Top + y) ^ invert) { + } else { + int alpha = (int)(aSum / waSum); + int red = (int)(rSum / wcSum); + int green = (int)(gSum / wcSum); + int blue = (int)(bSum / wcSum); bbbDest.SetColorAt(0, y, Color.FromArgb(alpha, red, green, blue)); } } @@ -497,17 +474,17 @@ namespace GreenshotPlugin.Core { waSums[i] = waSums[i + 1]; wcSums[i] = wcSums[i + 1]; aSums[i] = aSums[i + 1]; - bSums[i] = bSums[i + 1]; - gSums[i] = gSums[i + 1]; rSums[i] = rSums[i + 1]; + gSums[i] = gSums[i + 1]; + bSums[i] = bSums[i + 1]; } waSum = 0; wcSum = 0; aSum = 0; - bSum = 0; - gSum = 0; rSum = 0; + gSum = 0; + bSum = 0; int wx; for (wx = 0; wx < wlen - 1; ++wx) { @@ -515,9 +492,9 @@ namespace GreenshotPlugin.Core { waSum += wwx * waSums[wx]; wcSum += wwx * wcSums[wx]; aSum += wwx * aSums[wx]; - bSum += wwx * bSums[wx]; - gSum += wwx * gSums[wx]; rSum += wwx * rSums[wx]; + gSum += wwx * gSums[wx]; + bSum += wwx * bSums[wx]; } wx = wlen - 1; @@ -525,9 +502,9 @@ namespace GreenshotPlugin.Core { waSums[wx] = 0; wcSums[wx] = 0; aSums[wx] = 0; - bSums[wx] = 0; - gSums[wx] = 0; rSums[wx] = 0; + gSums[wx] = 0; + bSums[wx] = 0; int srcX = x + wx - r; @@ -545,9 +522,9 @@ namespace GreenshotPlugin.Core { wp >>= 8; aSums[wx] += wp * (long)colors[0]; - bSums[wx] += wp * (long)colors[3]; - gSums[wx] += wp * (long)colors[2]; rSums[wx] += wp * (long)colors[1]; + gSums[wx] += wp * (long)colors[2]; + bSums[wx] += wp * (long)colors[3]; } } @@ -555,23 +532,20 @@ namespace GreenshotPlugin.Core { waSum += (long)wr * waSums[wx]; wcSum += (long)wr * wcSums[wx]; aSum += (long)wr * aSums[wx]; - bSum += (long)wr * bSums[wx]; - gSum += (long)wr * gSums[wx]; rSum += (long)wr * rSums[wx]; + gSum += (long)wr * gSums[wx]; + bSum += (long)wr * bSums[wx]; } wcSum >>= 8; - - if (waSum == 0 || wcSum == 0) { - if (parentBounds.Contains(applyRect.Left + x, applyRect.Top + y) ^ invert) { + if (parentBounds.Contains(applyRect.Left, applyRect.Top + y) ^ invert) { + if (waSum == 0 || wcSum == 0) { bbbDest.SetColorAt(x, y, nullColor); - } - } else { - int alpha = (int)(aSum / waSum); - int blue = (int)(bSum / wcSum); - int green = (int)(gSum / wcSum); - int red = (int)(rSum / wcSum); - if (parentBounds.Contains(applyRect.Left + x, applyRect.Top + y) ^ invert) { + } else { + int alpha = (int)(aSum / waSum); + int red = (int)(rSum / wcSum); + int green = (int)(gSum / wcSum); + int blue = (int)(bSum / wcSum); bbbDest.SetColorAt(x, y, Color.FromArgb(alpha, red, green, blue)); } } @@ -731,5 +705,103 @@ namespace GreenshotPlugin.Core { return newBitmap; } + + /// + /// Checks if the supplied Bitmap has a PixelFormat we support + /// + /// bitmap to check + /// bool if we support it + public static bool SupportsPixelFormat(Bitmap bitmap) { + return SupportsPixelFormat(bitmap.PixelFormat); + } + + /// + /// Checks if we support the pixel format + /// + /// PixelFormat to check + /// bool if we support it + public static bool SupportsPixelFormat(PixelFormat pixelformat) { + return (pixelformat.Equals(PixelFormat.Format32bppArgb) || + pixelformat.Equals(PixelFormat.Format32bppRgb) || + pixelformat.Equals(PixelFormat.Format24bppRgb)); + } + + /// + /// Wrapper for just cloning which calls the CloneArea + /// + /// Image to clone + /// Bitmap with clone image data + public static Bitmap Clone(Image sourceBitmap) { + return CloneArea(sourceBitmap, Rectangle.Empty, PixelFormat.DontCare); + } + + /// + /// Clone an image, taking some rules into account: + /// 1) When sourceRect is the whole bitmap there is a GDI+ bug in Clone + /// Clone will than return the same PixelFormat as the source + /// a quick workaround is using new Bitmap which uses a default of Format32bppArgb + /// 2) When going from a transparent to a non transparent bitmap, we draw the background white! + /// + /// Source bitmap to clone + /// Rectangle to copy from the source, use Rectangle.Empty for all + /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported) + /// + public static Bitmap CloneArea(Image sourceBitmap, Rectangle sourceRect, PixelFormat targetFormat) { + Bitmap newImage = null; + Rectangle bitmapRect = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height); + + // Make sure the source is not Rectangle.Empty + if (Rectangle.Empty.Equals(sourceRect)) { + sourceRect = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height); + } + + // If no pixelformat is supplied + if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat) { + if (SupportsPixelFormat(sourceBitmap.PixelFormat)) { + targetFormat = sourceBitmap.PixelFormat; + } else if (Image.IsAlphaPixelFormat(sourceBitmap.PixelFormat)) { + targetFormat = PixelFormat.Format32bppArgb; + } else { + targetFormat = PixelFormat.Format24bppRgb; + } + } + + // check the target format + if (!SupportsPixelFormat(targetFormat)) { + if (Image.IsAlphaPixelFormat(targetFormat)) { + targetFormat = PixelFormat.Format32bppArgb; + } else { + targetFormat = PixelFormat.Format24bppRgb; + } + } + + bool destinationIsTransparent = Image.IsAlphaPixelFormat(targetFormat); + bool sourceIsTransparent = Image.IsAlphaPixelFormat(sourceBitmap.PixelFormat); + bool fromTransparentToNon = !destinationIsTransparent && sourceIsTransparent; + bool isBitmap = sourceBitmap is Bitmap; + bool isAreaEqual = sourceRect.Equals(bitmapRect); + if (isAreaEqual || fromTransparentToNon || !isBitmap) { + // Rule 1: if the areas are equal, always copy ourselves + newImage = new Bitmap(bitmapRect.Width, bitmapRect.Height, targetFormat); + using (Graphics graphics = Graphics.FromImage(newImage)) { + if (fromTransparentToNon) { + // Rule 2: Make sure the background color is white + graphics.Clear(Color.White); + } + // decide fastest copy method + if (isAreaEqual) { + graphics.DrawImageUnscaled(sourceBitmap, 0, 0); + } else { + graphics.DrawImage(sourceBitmap, 0, 0, sourceRect, GraphicsUnit.Pixel); + } + } + } else { + // Let GDI+ decide how to convert, need to test what is quicker... + newImage = (sourceBitmap as Bitmap).Clone(sourceRect, targetFormat); + } + // Make sure both images have the same resolution + newImage.SetResolution(sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution); + return newImage; + } } } diff --git a/GreenshotPlugin/Interop/COMWrapper.cs b/GreenshotPlugin/Interop/COMWrapper.cs index aac261d77..fa14bd551 100644 --- a/GreenshotPlugin/Interop/COMWrapper.cs +++ b/GreenshotPlugin/Interop/COMWrapper.cs @@ -209,7 +209,12 @@ namespace Greenshot.Interop { private void Dispose(bool disposing) { if (null != this._COMObject) { if(Marshal.IsComObject(this._COMObject)) { - while( Marshal.ReleaseComObject(this._COMObject) > 0 ); + try { + while (Marshal.ReleaseComObject(this._COMObject) > 0) ; + } catch (Exception ex) { + LOG.WarnFormat("Problem releasing {0}", _COMType); + LOG.Warn("Error: ", ex); + } } this._COMObject = null;