diff --git a/Greenshot/Drawing/Adorners/AbstractAdorner.cs b/Greenshot/Drawing/Adorners/AbstractAdorner.cs
index 0612753aa..fc3d72371 100644
--- a/Greenshot/Drawing/Adorners/AbstractAdorner.cs
+++ b/Greenshot/Drawing/Adorners/AbstractAdorner.cs
@@ -116,6 +116,23 @@ namespace Greenshot.Drawing.Adorners
}
}
+ ///
+ /// Return the bounds of the Adorner as displayed on the parent Surface
+ ///
+ protected virtual Rectangle SurfaceBounds
+ {
+ get
+ {
+ Point[] points = { Location };
+ var matrix = Owner.Parent.ZoomMatrix;
+ if (!matrix.IsIdentity)
+ {
+ matrix.TransformPoints(points);
+ }
+ return new Rectangle(points[0].X - _size.Width / 2, points[0].Y - _size.Height / 2, _size.Width, _size.Height);
+ }
+ }
+
///
/// The adorner is active if the edit status is not idle or undrawn
///
diff --git a/Greenshot/Drawing/Adorners/MoveAdorner.cs b/Greenshot/Drawing/Adorners/MoveAdorner.cs
index ec3dcf2d3..21c42a9fa 100644
--- a/Greenshot/Drawing/Adorners/MoveAdorner.cs
+++ b/Greenshot/Drawing/Adorners/MoveAdorner.cs
@@ -142,7 +142,7 @@ namespace Greenshot.Drawing.Adorners
{
Graphics targetGraphics = paintEventArgs.Graphics;
- var bounds = Bounds;
+ var bounds = SurfaceBounds;
GraphicsState state = targetGraphics.Save();
targetGraphics.SmoothingMode = SmoothingMode.None;
diff --git a/Greenshot/Drawing/Adorners/ResizeAdorner.cs b/Greenshot/Drawing/Adorners/ResizeAdorner.cs
index e75126f10..ef61b17bc 100644
--- a/Greenshot/Drawing/Adorners/ResizeAdorner.cs
+++ b/Greenshot/Drawing/Adorners/ResizeAdorner.cs
@@ -169,7 +169,7 @@ namespace Greenshot.Drawing.Adorners
{
Graphics targetGraphics = paintEventArgs.Graphics;
- var bounds = Bounds;
+ var bounds = SurfaceBounds;
GraphicsState state = targetGraphics.Save();
targetGraphics.SmoothingMode = SmoothingMode.None;
diff --git a/Greenshot/Drawing/Adorners/TargetAdorner.cs b/Greenshot/Drawing/Adorners/TargetAdorner.cs
index 8ca0a91ef..23cf29d09 100644
--- a/Greenshot/Drawing/Adorners/TargetAdorner.cs
+++ b/Greenshot/Drawing/Adorners/TargetAdorner.cs
@@ -96,7 +96,7 @@ namespace Greenshot.Drawing.Adorners
{
Graphics targetGraphics = paintEventArgs.Graphics;
- var bounds = Bounds;
+ var bounds = SurfaceBounds;
targetGraphics.FillRectangle(Brushes.Green, bounds.X, bounds.Y, bounds.Width, bounds.Height);
}
diff --git a/Greenshot/Drawing/CropContainer.cs b/Greenshot/Drawing/CropContainer.cs
index 2dc9761c0..f5288074d 100644
--- a/Greenshot/Drawing/CropContainer.cs
+++ b/Greenshot/Drawing/CropContainer.cs
@@ -56,7 +56,15 @@ namespace Greenshot.Drawing {
/// We need to override the DrawingBound, return a rectangle in the size of the image, to make sure this element is always draw
/// (we create a transparent brown over the complete picture)
///
- public override Rectangle DrawingBounds => new Rectangle(0,0,_parent?.Width??0, _parent?.Height ?? 0);
+ public override Rectangle DrawingBounds {
+ get {
+ if (_parent?.Image is Image image) {
+ return new Rectangle(0, 0, image.Width, image.Height);
+ } else {
+ return new Rectangle();
+ }
+ }
+ }
public override void Draw(Graphics g, RenderMode rm) {
if (_parent == null)
@@ -67,17 +75,18 @@ namespace Greenshot.Drawing {
using Brush cropBrush = new SolidBrush(Color.FromArgb(100, 150, 150, 100));
Rectangle cropRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
Rectangle selectionRect = new Rectangle(cropRectangle.Left - 1, cropRectangle.Top - 1, cropRectangle.Width + 1, cropRectangle.Height + 1);
+ Size imageSize = _parent.Image.Size;
DrawSelectionBorder(g, selectionRect);
// top
- g.FillRectangle(cropBrush, new Rectangle(0, 0, _parent.Width, cropRectangle.Top));
+ g.FillRectangle(cropBrush, new Rectangle(0, 0, imageSize.Width, cropRectangle.Top));
// left
g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top, cropRectangle.Left, cropRectangle.Height));
// right
- g.FillRectangle(cropBrush, new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, _parent.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height));
+ g.FillRectangle(cropBrush, new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height));
// bottom
- g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, _parent.Width, _parent.Height - (cropRectangle.Top + cropRectangle.Height)));
+ g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height)));
}
public override bool HasContextMenu {
diff --git a/Greenshot/Drawing/DrawableContainer.cs b/Greenshot/Drawing/DrawableContainer.cs
index 8d805b1f0..65ff42de3 100644
--- a/Greenshot/Drawing/DrawableContainer.cs
+++ b/Greenshot/Drawing/DrawableContainer.cs
@@ -298,7 +298,7 @@ namespace Greenshot.Drawing
public virtual void Invalidate() {
if (Status != EditStatus.UNDRAWN) {
- _parent?.Invalidate(DrawingBounds);
+ _parent?.InvalidateElements(DrawingBounds);
}
}
diff --git a/Greenshot/Drawing/DrawableContainerList.cs b/Greenshot/Drawing/DrawableContainerList.cs
index d3c325f5b..17b879538 100644
--- a/Greenshot/Drawing/DrawableContainerList.cs
+++ b/Greenshot/Drawing/DrawableContainerList.cs
@@ -286,7 +286,7 @@ namespace Greenshot.Drawing {
{
region = Rectangle.Union(region, dc.DrawingBounds);
}
- Parent.Invalidate(region);
+ Parent.InvalidateElements(region);
}
///
/// Indicates whether the given list of elements can be pulled up,
diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs
index bd56db3a2..5c38bea8e 100644
--- a/Greenshot/Drawing/Surface.cs
+++ b/Greenshot/Drawing/Surface.cs
@@ -298,10 +298,34 @@ namespace Greenshot.Drawing
set
{
_image = value;
- Size = _image.Size;
+ UpdateSize();
}
}
+ [NonSerialized]
+ private float _zoomFactor = 1.0f;
+ public float ZoomFactor
+ {
+ get => _zoomFactor;
+ set
+ {
+ _zoomFactor = value;
+ ZoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0);
+ UpdateSize();
+ }
+ }
+
+ public Matrix ZoomMatrix { get; private set; } = new Matrix(1, 0, 0, 1, 0, 0);
+
+ ///
+ /// Sets the surface size as zoomed image size.
+ ///
+ private void UpdateSize()
+ {
+ var size = _image.Size;
+ Size = new Size((int)(size.Width * _zoomFactor), (int)(size.Height * _zoomFactor));
+ }
+
///
/// The field aggregator is that which is used to have access to all the fields inside the currently selected elements.
/// e.g. used to decided if and which line thickness is shown when multiple elements are selected.
@@ -447,7 +471,6 @@ namespace Greenshot.Drawing
// Set new values
Image = newImage;
- Size = newImage.Size;
_modified = true;
}
@@ -1086,6 +1109,12 @@ namespace Greenshot.Drawing
return null;
}
+ ///
+ /// Translate mouse coordinates as if they were applied directly to unscaled image.
+ ///
+ private MouseEventArgs InverseZoomMouseCoordinates(MouseEventArgs e)
+ => new MouseEventArgs(e.Button, e.Clicks, (int)(e.X / _zoomFactor), (int)(e.Y / _zoomFactor), e.Delta);
+
///
/// This event handler is called when someone presses the mouse on a surface.
///
@@ -1093,6 +1122,7 @@ namespace Greenshot.Drawing
///
private void SurfaceMouseDown(object sender, MouseEventArgs e)
{
+ e = InverseZoomMouseCoordinates(e);
// Handle Adorners
var adorner = FindActiveAdorner(e);
@@ -1187,6 +1217,7 @@ namespace Greenshot.Drawing
///
private void SurfaceMouseUp(object sender, MouseEventArgs e)
{
+ e = InverseZoomMouseCoordinates(e);
// Handle Adorners
var adorner = FindActiveAdorner(e);
@@ -1276,6 +1307,8 @@ namespace Greenshot.Drawing
///
private void SurfaceMouseMove(object sender, MouseEventArgs e)
{
+ e = InverseZoomMouseCoordinates(e);
+
// Handle Adorners
var adorner = FindActiveAdorner(e);
if (adorner != null)
@@ -1371,6 +1404,25 @@ namespace Greenshot.Drawing
return GetImage(RenderMode.EXPORT);
}
+ private Rectangle ZoomClipRectangle(Rectangle rc, double scale)
+ => new Rectangle(
+ (int)(rc.X * scale),
+ (int)(rc.Y * scale),
+ (int)((rc.Width + 1) * scale) + 1, // making sure to redraw enough pixels when moving scaled image
+ (int)((rc.Height + 1) * scale) + 1
+ );
+
+ private RectangleF ZoomClipRectangle(RectangleF rc, double scale)
+ => new RectangleF(
+ (float)Math.Floor(rc.X * scale),
+ (float)Math.Floor(rc.Y * scale),
+ (float)Math.Ceiling(rc.Width * scale),
+ (float)Math.Ceiling(rc.Height * scale)
+ );
+
+ public void InvalidateElements(Rectangle rc)
+ => Invalidate(ZoomClipRectangle(rc, _zoomFactor));
+
///
/// This is the event handler for the Paint Event, try to draw as little as possible!
///
@@ -1379,14 +1431,16 @@ namespace Greenshot.Drawing
private void SurfacePaint(object sender, PaintEventArgs paintEventArgs)
{
Graphics targetGraphics = paintEventArgs.Graphics;
- Rectangle clipRectangle = paintEventArgs.ClipRectangle;
- if (Rectangle.Empty.Equals(clipRectangle))
+ Rectangle targetClipRectangle = paintEventArgs.ClipRectangle;
+ if (Rectangle.Empty.Equals(targetClipRectangle))
{
LOG.Debug("Empty cliprectangle??");
return;
}
+ Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, 1.0 / _zoomFactor);
- if (_elements.HasIntersectingFilters(clipRectangle))
+ bool isZoomedIn = ZoomFactor > 1f;
+ if (_elements.HasIntersectingFilters(imageClipRectangle) || isZoomedIn)
{
if (_buffer != null)
{
@@ -1409,18 +1463,38 @@ namespace Greenshot.Drawing
//graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
//graphics.CompositingQuality = CompositingQuality.HighQuality;
//graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- DrawBackground(graphics, clipRectangle);
- graphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel);
- graphics.SetClip(targetGraphics);
- _elements.Draw(graphics, _buffer, RenderMode.EDIT, clipRectangle);
+ DrawBackground(graphics, imageClipRectangle);
+ graphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel);
+ graphics.SetClip(ZoomClipRectangle(targetGraphics.ClipBounds, 1.0 / _zoomFactor));
+ _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle);
}
- targetGraphics.DrawImage(_buffer, clipRectangle, clipRectangle, GraphicsUnit.Pixel);
+ targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor);
+ if (isZoomedIn)
+ {
+ var state = targetGraphics.Save();
+ targetGraphics.SmoothingMode = SmoothingMode.None;
+ targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
+ targetGraphics.CompositingQuality = CompositingQuality.HighQuality;
+ targetGraphics.PixelOffsetMode = PixelOffsetMode.None;
+
+ targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel);
+
+ targetGraphics.Restore(state);
+ }
+ else
+ {
+ targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel);
+ }
+ targetGraphics.ResetTransform();
}
else
{
- DrawBackground(targetGraphics, clipRectangle);
- targetGraphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel);
- _elements.Draw(targetGraphics, null, RenderMode.EDIT, clipRectangle);
+ DrawBackground(targetGraphics, targetClipRectangle);
+
+ targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor);
+ targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel);
+ _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle);
+ targetGraphics.ResetTransform();
}
// No clipping for the adorners
diff --git a/Greenshot/Drawing/TextContainer.cs b/Greenshot/Drawing/TextContainer.cs
index 0f4866d71..bd4c75274 100644
--- a/Greenshot/Drawing/TextContainer.cs
+++ b/Greenshot/Drawing/TextContainer.cs
@@ -442,14 +442,20 @@ namespace Greenshot.Drawing
correction = -1;
}
Rectangle absRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
- _textBox.Left = absRectangle.Left + lineWidth;
- _textBox.Top = absRectangle.Top + lineWidth;
+ Point[] points = { absRectangle.Location, absRectangle.Location + absRectangle.Size };
+ var matrix = Parent.ZoomMatrix;
+ if (!matrix.IsIdentity)
+ {
+ matrix.TransformPoints(points);
+ }
+ _textBox.Left = points[0].X + lineWidth;
+ _textBox.Top = points[0].Y + lineWidth;
if (lineThickness <= 1)
{
lineWidth = 0;
}
- _textBox.Width = absRectangle.Width - 2 * lineWidth + correction;
- _textBox.Height = absRectangle.Height - 2 * lineWidth + correction;
+ _textBox.Width = points[1].X - points[0].X - 2 * lineWidth + correction;
+ _textBox.Height = points[1].Y - points[0].Y - 2 * lineWidth + correction;
}
public override void ApplyBounds(RectangleF newBounds)
diff --git a/Greenshot/Forms/ImageEditorForm.Designer.cs b/Greenshot/Forms/ImageEditorForm.Designer.cs
index 9a653cc21..5789a19a6 100644
--- a/Greenshot/Forms/ImageEditorForm.Designer.cs
+++ b/Greenshot/Forms/ImageEditorForm.Designer.cs
@@ -194,6 +194,26 @@ namespace Greenshot {
this.alignLeftToolStripMenuItem = new GreenshotPlugin.Controls.GreenshotToolStripMenuItem();
this.alignCenterToolStripMenuItem = new GreenshotPlugin.Controls.GreenshotToolStripMenuItem();
this.alignRightToolStripMenuItem = new GreenshotPlugin.Controls.GreenshotToolStripMenuItem();
+ this.zoomMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
+ this.zoomInMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoomOutMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoomMenuSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+ this.zoomBestFitMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoomMenuSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+ this.zoom25MenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoom50MenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoom66MenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoom75MenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoomMenuSeparator3 = new System.Windows.Forms.ToolStripSeparator();
+ this.zoomActualSizeMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoomMenuSeparator4 = new System.Windows.Forms.ToolStripSeparator();
+ this.zoom200MenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoom300MenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoom400MenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoom600MenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.zoomStatusDropDownBtn = new System.Windows.Forms.ToolStripDropDownButton();
+ this.zoomMainMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.statusStripSpacer = new System.Windows.Forms.ToolStripStatusLabel();
this.topToolStripContainer.BottomToolStripPanel.SuspendLayout();
this.topToolStripContainer.ContentPanel.SuspendLayout();
this.topToolStripContainer.LeftToolStripPanel.SuspendLayout();
@@ -203,6 +223,7 @@ namespace Greenshot {
this.tableLayoutPanel1.SuspendLayout();
this.toolsToolStrip.SuspendLayout();
this.menuStrip1.SuspendLayout();
+ this.zoomMenuStrip.SuspendLayout();
this.destinationsToolStrip.SuspendLayout();
this.propertiesToolStrip.SuspendLayout();
this.fileSavedStatusContextMenu.SuspendLayout();
@@ -238,7 +259,9 @@ namespace Greenshot {
this.statusStrip1.Dock = System.Windows.Forms.DockStyle.None;
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.dimensionsLabel,
- this.statusLabel});
+ this.statusLabel,
+ this.statusStripSpacer,
+ this.zoomStatusDropDownBtn});
this.statusStrip1.Location = new System.Drawing.Point(0, 0);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(785, 24);
@@ -538,6 +561,7 @@ namespace Greenshot {
this.editToolStripMenuItem,
this.objectToolStripMenuItem,
this.pluginToolStripMenuItem,
+ this.zoomMainMenuItem,
this.helpToolStripMenuItem});
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.BackColor = System.Drawing.SystemColors.Control;
@@ -1624,6 +1648,171 @@ namespace Greenshot {
this.alignRightToolStripMenuItem.Name = "alignRightToolStripMenuItem";
this.alignRightToolStripMenuItem.Tag = System.Drawing.StringAlignment.Far;
//
+ // zoomMainMenuItem
+ //
+ this.zoomMainMenuItem.DropDown = this.zoomMenuStrip;
+ this.zoomMainMenuItem.Name = "zoomMainMenuItem";
+ this.zoomMainMenuItem.Size = new System.Drawing.Size(51, 20);
+ this.zoomMainMenuItem.Text = "Zoom";
+ //
+ // zoomMenuStrip
+ //
+ this.zoomMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.zoomInMenuItem,
+ this.zoomOutMenuItem,
+ this.zoomMenuSeparator1,
+ this.zoomBestFitMenuItem,
+ this.zoomMenuSeparator2,
+ this.zoom25MenuItem,
+ this.zoom50MenuItem,
+ this.zoom66MenuItem,
+ this.zoom75MenuItem,
+ this.zoomMenuSeparator3,
+ this.zoomActualSizeMenuItem,
+ this.zoomMenuSeparator4,
+ this.zoom200MenuItem,
+ this.zoom300MenuItem,
+ this.zoom400MenuItem,
+ this.zoom600MenuItem});
+ this.zoomMenuStrip.Name = "zoomMenuStrip";
+ this.zoomMenuStrip.Size = new System.Drawing.Size(210, 292);
+ //
+ // zoomInMenuItem
+ //
+ this.zoomInMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomInMenuItem.Image")));
+ this.zoomInMenuItem.Name = "zoomInMenuItem";
+ this.zoomInMenuItem.ShortcutKeyDisplayString = "Ctrl++";
+ this.zoomInMenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoomInMenuItem.Text = "Zoom In";
+ this.zoomInMenuItem.Click += new System.EventHandler(this.ZoomInMenuItemClick);
+ //
+ // zoomOutMenuItem
+ //
+ this.zoomOutMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomOutMenuItem.Image")));
+ this.zoomOutMenuItem.Name = "zoomOutMenuItem";
+ this.zoomOutMenuItem.ShortcutKeyDisplayString = "Ctrl+-";
+ this.zoomOutMenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoomOutMenuItem.Text = "Zoom Out";
+ this.zoomOutMenuItem.Click += new System.EventHandler(this.ZoomOutMenuItemClick);
+ //
+ // zoomMenuSeparator1
+ //
+ this.zoomMenuSeparator1.Name = "zoomMenuSeparator1";
+ this.zoomMenuSeparator1.Size = new System.Drawing.Size(206, 6);
+ //
+ // zoomBestFitMenuItem
+ //
+ this.zoomBestFitMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomBestFitMenuItem.Image")));
+ this.zoomBestFitMenuItem.Name = "zoomBestFitMenuItem";
+ this.zoomBestFitMenuItem.ShortcutKeyDisplayString = "Ctrl+*";
+ this.zoomBestFitMenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoomBestFitMenuItem.Text = "Best Fit";
+ this.zoomBestFitMenuItem.Click += new System.EventHandler(this.ZoomBestFitMenuItemClick);
+ //
+ // zoomMenuSeparator2
+ //
+ this.zoomMenuSeparator2.Name = "zoomMenuSeparator2";
+ this.zoomMenuSeparator2.Size = new System.Drawing.Size(206, 6);
+ //
+ // zoom25MenuItem
+ //
+ this.zoom25MenuItem.Name = "zoom25MenuItem";
+ this.zoom25MenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoom25MenuItem.Tag = "25";
+ this.zoom25MenuItem.Text = "25%";
+ this.zoom25MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick);
+ //
+ // zoom50MenuItem
+ //
+ this.zoom50MenuItem.Name = "zoom50MenuItem";
+ this.zoom50MenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoom50MenuItem.Tag = "50";
+ this.zoom50MenuItem.Text = "50%";
+ this.zoom50MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick);
+ //
+ // zoom66MenuItem
+ //
+ this.zoom66MenuItem.Name = "zoom66MenuItem";
+ this.zoom66MenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoom66MenuItem.Tag = "66";
+ this.zoom66MenuItem.Text = "66%";
+ this.zoom66MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick);
+ //
+ // zoom75MenuItem
+ //
+ this.zoom75MenuItem.Name = "zoom75MenuItem";
+ this.zoom75MenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoom75MenuItem.Tag = "75";
+ this.zoom75MenuItem.Text = "75%";
+ this.zoom75MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick);
+ //
+ // zoomMenuSeparator3
+ //
+ this.zoomMenuSeparator3.Name = "zoomMenuSeparator3";
+ this.zoomMenuSeparator3.Size = new System.Drawing.Size(206, 6);
+ //
+ // zoomActualSizeMenuItem
+ //
+ this.zoomActualSizeMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomActualSizeMenuItem.Image")));
+ this.zoomActualSizeMenuItem.Name = "zoomActualSizeMenuItem";
+ this.zoomActualSizeMenuItem.ShortcutKeyDisplayString = "Ctrl+/";
+ this.zoomActualSizeMenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoomActualSizeMenuItem.Tag = "100";
+ this.zoomActualSizeMenuItem.Text = "100% - Actual Size";
+ this.zoomActualSizeMenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick);
+ //
+ // zoomMenuSeparator4
+ //
+ this.zoomMenuSeparator4.Name = "zoomMenuSeparator4";
+ this.zoomMenuSeparator4.Size = new System.Drawing.Size(206, 6);
+ //
+ // zoom200MenuItem
+ //
+ this.zoom200MenuItem.Name = "zoom200MenuItem";
+ this.zoom200MenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoom200MenuItem.Tag = "200";
+ this.zoom200MenuItem.Text = "200%";
+ this.zoom200MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick);
+ //
+ // zoom300MenuItem
+ //
+ this.zoom300MenuItem.Name = "zoom300MenuItem";
+ this.zoom300MenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoom300MenuItem.Tag = "300";
+ this.zoom300MenuItem.Text = "300%";
+ this.zoom300MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick);
+ //
+ // zoom400MenuItem
+ //
+ this.zoom400MenuItem.Name = "zoom400MenuItem";
+ this.zoom400MenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoom400MenuItem.Tag = "400";
+ this.zoom400MenuItem.Text = "400%";
+ this.zoom400MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick);
+ //
+ // zoom600MenuItem
+ //
+ this.zoom600MenuItem.Name = "zoom600MenuItem";
+ this.zoom600MenuItem.Size = new System.Drawing.Size(209, 22);
+ this.zoom600MenuItem.Tag = "600";
+ this.zoom600MenuItem.Text = "600%";
+ this.zoom600MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick);
+ //
+ // statusStripSpacer
+ //
+ this.statusStripSpacer.Name = "statusStripSpacer";
+ this.statusStripSpacer.Size = new System.Drawing.Size(599, 19);
+ this.statusStripSpacer.Spring = true;
+ //
+ // zoomStatusDropDownBtn
+ //
+ this.zoomStatusDropDownBtn.DropDown = this.zoomMenuStrip;
+ this.zoomStatusDropDownBtn.Image = ((System.Drawing.Image)(resources.GetObject("zoomStatusDropDownBtn.Image")));
+ this.zoomStatusDropDownBtn.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.zoomStatusDropDownBtn.Name = "zoomStatusDropDownBtn";
+ this.zoomStatusDropDownBtn.Size = new System.Drawing.Size(64, 22);
+ this.zoomStatusDropDownBtn.Text = "100%";
+ //
// ImageEditorForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
@@ -1645,6 +1834,7 @@ namespace Greenshot {
this.statusStrip1.ResumeLayout(true);
this.tableLayoutPanel1.ResumeLayout(true);
this.toolsToolStrip.ResumeLayout(true);
+ this.zoomMenuStrip.ResumeLayout(false);
this.menuStrip1.ResumeLayout(true);
this.destinationsToolStrip.ResumeLayout(true);
this.propertiesToolStrip.ResumeLayout(true);
@@ -1794,5 +1984,25 @@ namespace Greenshot {
private Greenshot.Controls.ToolStripColorButton btnLineColor;
private GreenshotPlugin.Controls.GreenshotToolStripMenuItem autoCropToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator17;
+ private System.Windows.Forms.ContextMenuStrip zoomMenuStrip;
+ private System.Windows.Forms.ToolStripMenuItem zoomInMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem zoomOutMenuItem;
+ private System.Windows.Forms.ToolStripSeparator zoomMenuSeparator1;
+ private System.Windows.Forms.ToolStripMenuItem zoomBestFitMenuItem;
+ private System.Windows.Forms.ToolStripSeparator zoomMenuSeparator2;
+ private System.Windows.Forms.ToolStripMenuItem zoom25MenuItem;
+ private System.Windows.Forms.ToolStripMenuItem zoom50MenuItem;
+ private System.Windows.Forms.ToolStripMenuItem zoom66MenuItem;
+ private System.Windows.Forms.ToolStripMenuItem zoom75MenuItem;
+ private System.Windows.Forms.ToolStripSeparator zoomMenuSeparator3;
+ private System.Windows.Forms.ToolStripMenuItem zoomActualSizeMenuItem;
+ private System.Windows.Forms.ToolStripSeparator zoomMenuSeparator4;
+ private System.Windows.Forms.ToolStripMenuItem zoom200MenuItem;
+ private System.Windows.Forms.ToolStripMenuItem zoom300MenuItem;
+ private System.Windows.Forms.ToolStripMenuItem zoom400MenuItem;
+ private System.Windows.Forms.ToolStripMenuItem zoom600MenuItem;
+ private System.Windows.Forms.ToolStripDropDownButton zoomStatusDropDownBtn;
+ private System.Windows.Forms.ToolStripMenuItem zoomMainMenuItem;
+ private System.Windows.Forms.ToolStripStatusLabel statusStripSpacer;
}
}
diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs
index bcb8c275c..dd9ec94e1 100644
--- a/Greenshot/Forms/ImageEditorForm.cs
+++ b/Greenshot/Forms/ImageEditorForm.cs
@@ -66,6 +66,12 @@ namespace Greenshot {
// whether part of the editor controls are disabled depending on selected item(s)
private bool _controlsDisabledDueToConfirmable;
+ ///
+ /// All provided zoom values (in percents) in ascending order.
+ ///
+ private readonly int[] ZOOM_VALUES = new[] { 25, 50, 66, 75, 100, 200, 300, 400, 600 };
+ private int _zoomValue = 100;
+
///
/// An Implementation for the IImageEditor, this way Plugins have access to the HWND handles wich can be used with Win32 API calls.
///
@@ -420,20 +426,14 @@ namespace Greenshot {
///
///
///
- private void SurfaceSizeChanged(object sender, EventArgs e) {
- if (EditorConfiguration.MatchSizeToCapture) {
- // Set editor's initial size to the size of the surface plus the size of the chrome
- Size imageSize = Surface.Image.Size;
- Size currentFormSize = Size;
- Size currentImageClientSize = panel1.ClientSize;
- int minimumFormWidth = 650;
- int minimumFormHeight = 530;
- int newWidth = Math.Max(minimumFormWidth, currentFormSize.Width - currentImageClientSize.Width + imageSize.Width);
- int newHeight = Math.Max(minimumFormHeight, currentFormSize.Height - currentImageClientSize.Height + imageSize.Height);
- Size = new Size(newWidth, newHeight);
+ private void SurfaceSizeChanged(object sender, EventArgs e)
+ {
+ if (EditorConfiguration.MatchSizeToCapture)
+ {
+ Size = GetOptimalWindowSize();
}
dimensionsLabel.Text = Surface.Image.Width + "x" + Surface.Image.Height;
- ImageEditorFormResize(sender, new EventArgs());
+ AlignCanvasPositionAfterResize();
}
public ISurface Surface {
@@ -860,15 +860,34 @@ namespace Greenshot {
case Keys.Oemcomma: // Rotate CCW Ctrl + ,
RotateCcwToolstripButtonClick(sender, e);
break;
- case Keys.OemPeriod: // Rotate CW Ctrl + .
+ case Keys.OemPeriod: // Rotate CW Ctrl + .
RotateCwToolstripButtonClick(sender, e);
break;
- case Keys.Add: // Ctrl + +
- case Keys.Oemplus: // Ctrl + +
+ case Keys.Add: // Ctrl + Num+
+ case Keys.Oemplus: // Ctrl + +
+ ZoomInMenuItemClick(sender, e);
+ break;
+ case Keys.Subtract: // Ctrl + Num-
+ case Keys.OemMinus: // Ctrl + -
+ ZoomOutMenuItemClick(sender, e);
+ break;
+ case Keys.Divide: // Ctrl + Num/
+ case Keys.OemQuestion: // Ctrl + / (?)
+ ZoomSetValueMenuItemClick(zoomActualSizeMenuItem, e);
+ break;
+ case Keys.Multiply: // Ctrl + Num*
+ case Keys.D8: // Ctrl + 8 (*)
+ ZoomBestFitMenuItemClick(sender, e);
+ break;
+ }
+ } else if (e.Modifiers.Equals(Keys.Control | Keys.Shift)) {
+ switch (e.KeyCode) {
+ case Keys.Add: // Ctrl + Shift + Num+
+ case Keys.Oemplus: // Ctrl + Shift + +
EnlargeCanvasToolStripMenuItemClick(sender, e);
break;
- case Keys.Subtract: // Ctrl + -
- case Keys.OemMinus: // Ctrl + -
+ case Keys.Subtract: // Ctrl + Shift + Num-
+ case Keys.OemMinus: // Ctrl + Shift + -
ShrinkCanvasToolStripMenuItemClick(sender, e);
break;
}
@@ -1444,28 +1463,130 @@ namespace Greenshot {
}
private void ImageEditorFormResize(object sender, EventArgs e) {
+ AlignCanvasPositionAfterResize();
+ }
+
+ private void AlignCanvasPositionAfterResize() {
if (Surface?.Image == null || panel1 == null) {
return;
}
- Size imageSize = Surface.Image.Size;
- Size currentClientSize = panel1.ClientSize;
var canvas = Surface as Control;
+ Size canvasSize = canvas.Size;
+ Size currentClientSize = panel1.ClientSize;
Panel panel = (Panel) canvas?.Parent;
if (panel == null) {
return;
}
int offsetX = -panel.HorizontalScroll.Value;
int offsetY = -panel.VerticalScroll.Value;
- if (currentClientSize.Width > imageSize.Width) {
- canvas.Left = offsetX + (currentClientSize.Width - imageSize.Width) / 2;
+ if (currentClientSize.Width > canvasSize.Width) {
+ canvas.Left = offsetX + (currentClientSize.Width - canvasSize.Width) / 2;
} else {
canvas.Left = offsetX + 0;
}
- if (currentClientSize.Height > imageSize.Height) {
- canvas.Top = offsetY + (currentClientSize.Height - imageSize.Height) / 2;
+ if (currentClientSize.Height > canvasSize.Height) {
+ canvas.Top = offsetY + (currentClientSize.Height - canvasSize.Height) / 2;
} else {
canvas.Top = offsetY + 0;
}
}
+
+ ///
+ /// Compute a size as a sum of surface size and chrome.
+ /// Upper bound is working area of the screen. Lower bound is fixed value.
+ ///
+ private Size GetOptimalWindowSize() {
+ var surfaceSize = (Surface as Control).Size;
+ var chromeSize = GetChromeSize();
+ var newWidth = chromeSize.Width + surfaceSize.Width;
+ var newHeight = chromeSize.Height + surfaceSize.Height;
+
+ // Upper bound. Don't make it bigger than the available working area.
+ var screen = Screen.FromControl(this);
+ var workingArea = screen.WorkingArea;
+ newWidth = Math.Min(newWidth, workingArea.Width);
+ newHeight = Math.Min(newHeight, workingArea.Height);
+
+ // Lower bound. Don't make it smaller than a fixed value.
+ int minimumFormWidth = 650;
+ int minimumFormHeight = 530;
+ newWidth = Math.Max(minimumFormWidth, newWidth);
+ newHeight = Math.Max(minimumFormHeight, newHeight);
+
+ return new Size(newWidth, newHeight);
+ }
+
+ private Size GetChromeSize()
+ => Size - panel1.ClientSize;
+
+ ///
+ /// Compute a size that the form can take without getting out of working area of the screen.
+ ///
+ private Size GetAvailableScreenSpace() {
+ var screen = Screen.FromControl(this);
+ var screenBounds = screen.Bounds;
+ var workingArea = screen.WorkingArea;
+ if (Left > screenBounds.Left && Top > screenBounds.Top) {
+ return new Size(workingArea.Width - Left, workingArea.Height - Top);
+ } else {
+ return workingArea.Size;
+ }
+ }
+
+ private void ZoomInMenuItemClick(object sender, EventArgs e) {
+ var nextIndex = Array.FindIndex(ZOOM_VALUES, v => v > _zoomValue);
+ var nextValue = nextIndex < 0 ? ZOOM_VALUES[ZOOM_VALUES.Length - 1] : ZOOM_VALUES[nextIndex];
+
+ ZoomSetValue(nextValue);
+ }
+
+ private void ZoomOutMenuItemClick(object sender, EventArgs e) {
+ var nextIndex = Array.FindLastIndex(ZOOM_VALUES, v => v < _zoomValue);
+ var nextValue = nextIndex < 0 ? ZOOM_VALUES[0] : ZOOM_VALUES[nextIndex];
+
+ ZoomSetValue(nextValue);
+ }
+
+ private void ZoomSetValueMenuItemClick(object sender, EventArgs e) {
+ var senderMenuItem = (ToolStripMenuItem)sender;
+ int zoomPercent = int.Parse((string)senderMenuItem.Tag);
+
+ ZoomSetValue(zoomPercent);
+ }
+
+ private void ZoomBestFitMenuItemClick(object sender, EventArgs e) {
+ var maxWindowSize = GetAvailableScreenSpace();
+ var chromeSize = GetChromeSize();
+ var maxImageSize = maxWindowSize - chromeSize;
+ var imageSize = Surface.Image.Size;
+
+ static bool isFit(int zoom, int source, int boundary)
+ => source * zoom / 100 <= boundary;
+
+ var nextValue = Array.FindLast(
+ ZOOM_VALUES,
+ zoom => isFit(zoom, imageSize.Width, maxImageSize.Width)
+ && isFit(zoom, imageSize.Height, maxImageSize.Height)
+ );
+
+ ZoomSetValue(nextValue);
+ }
+
+ private void ZoomSetValue(int value) {
+ _zoomValue = value;
+ Surface.ZoomFactor = 1f * value / 100;
+
+ // Update zoom controls
+ string valueString = value.ToString();
+ zoomStatusDropDownBtn.Text = valueString + "%";
+ foreach (var item in zoomMenuStrip.Items) {
+ if (item is ToolStripMenuItem menuItem) {
+ menuItem.Checked = menuItem.Tag as string == valueString;
+ }
+ }
+
+ Size = GetOptimalWindowSize();
+ AlignCanvasPositionAfterResize();
+ }
}
}
diff --git a/Greenshot/Forms/ImageEditorForm.resx b/Greenshot/Forms/ImageEditorForm.resx
index e4188604b..025543b10 100644
--- a/Greenshot/Forms/ImageEditorForm.resx
+++ b/Greenshot/Forms/ImageEditorForm.resx
@@ -937,6 +937,84 @@
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
+ AAAAAElFTkSuQmCC
+
+
+
+
+ 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
@@ -1026,4 +1104,7 @@
17, 17
+
+ 782, 17
+
\ No newline at end of file
diff --git a/Greenshot/icons/fugue/magnifier-zoom-actual.png b/Greenshot/icons/fugue/magnifier-zoom-actual.png
new file mode 100644
index 000000000..21cf71d63
Binary files /dev/null and b/Greenshot/icons/fugue/magnifier-zoom-actual.png differ
diff --git a/Greenshot/icons/fugue/magnifier-zoom-fit.png b/Greenshot/icons/fugue/magnifier-zoom-fit.png
new file mode 100644
index 000000000..8364359fd
Binary files /dev/null and b/Greenshot/icons/fugue/magnifier-zoom-fit.png differ
diff --git a/Greenshot/icons/fugue/magnifier-zoom-in.png b/Greenshot/icons/fugue/magnifier-zoom-in.png
new file mode 100644
index 000000000..800ec1205
Binary files /dev/null and b/Greenshot/icons/fugue/magnifier-zoom-in.png differ
diff --git a/Greenshot/icons/fugue/magnifier-zoom-out.png b/Greenshot/icons/fugue/magnifier-zoom-out.png
new file mode 100644
index 000000000..3ec2b7a5d
Binary files /dev/null and b/Greenshot/icons/fugue/magnifier-zoom-out.png differ
diff --git a/Greenshot/icons/fugue/magnifier-zoom.png b/Greenshot/icons/fugue/magnifier-zoom.png
new file mode 100644
index 000000000..941b10356
Binary files /dev/null and b/Greenshot/icons/fugue/magnifier-zoom.png differ
diff --git a/GreenshotPlugin/Interfaces/ISurface.cs b/GreenshotPlugin/Interfaces/ISurface.cs
index 44a150364..8ab44c810 100644
--- a/GreenshotPlugin/Interfaces/ISurface.cs
+++ b/GreenshotPlugin/Interfaces/ISurface.cs
@@ -21,6 +21,7 @@
using System;
using System.Drawing;
+using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;
using GreenshotPlugin.Effects;
@@ -147,8 +148,13 @@ namespace GreenshotPlugin.Interfaces
///
/// This returns false if the container is deleted but still in the undo stack
bool IsOnSurface(IDrawableContainer container);
- void Invalidate(Rectangle rectangleToInvalidate);
void Invalidate();
+ ///
+ /// Invalidates the specified region of the Surface.
+ /// Takes care of the Surface zoom level, accepts rectangle in the coordinate space of the Image.
+ ///
+ /// Bounding rectangle for updated elements, in the coordinate space of the Image.
+ void InvalidateElements(Rectangle rectangleToInvalidate);
bool Modified
{
get;
@@ -189,6 +195,15 @@ namespace GreenshotPlugin.Interfaces
int Width { get; }
int Height { get; }
+ ///
+ /// Zoom value applied to the surface. 1.0f for actual size (100%).
+ ///
+ float ZoomFactor { get; set; }
+ ///
+ /// Matrix representing zoom applied to the surface.
+ ///
+ Matrix ZoomMatrix { get; }
+
void MakeUndoable(IMemento memento, bool allowMerge);
}
-}
\ No newline at end of file
+}