mirror of
https://github.com/greenshot/greenshot
synced 2025-08-21 05:53:27 -07:00
Merge pull request #201 from KillyMXI/feature/zoom
* Zoom feature for the editor
- better interface for coordinate translation;
- correct context menu location;
- speech bubble tail can be dragged over the whole image.
* Image size in OneNoteExporter
There is now a distinction between Surface size and Surface.Image size.
* Changed shortcuts - Ctrl+0, Ctrl+9
* GetOptimalWindowSize - bound by available screen space
Limiting by working area size was not good enough - having whole chrome on screen is important.
* More fixes for Surface.Image size
- crop works properly;
- Freehand drawings are selectable everywhere at all zoom levels.
Known issue: freehand selection outline is not drawn
* Rework for paste from clipboard
- location is determined by the same code for images and text;
- use visible area to determine the location.
* Fix NPE
* Width and Height shouldn't be exposed through ISurface anymore
* Fix GetAvailableScreenSpace on secondary screens
Co-Authored-By: jklingen <jklingen@users.noreply.github.com>
* Fix rendering quality at small zoom levels when redrawing small parts
* Robust clip region resize
* Keep center of visible area static on zoom when possible
* Unneeded using
after 169dbdc
* Fix for Best Fit on images bigger than 4 times the available space
* Fix weird scroll position when going from no scroll zoom value
Also prefer top left corner in that situation - it is less disorienting when working with screenshots.
* Use Fractions to represent zoom factor
The goal is to be able to get as close as possible to perfect 66.(6)% (2/3) zoom factor, and also remove types mismatch between the editor form and the surface.
* Fix for pixel jerk
* Naming consistency - not a percentage value anymore
* TextContainer's TextBox on zoom - fix for position, update font size
* Smarter zoom - keep selected elements in sight
* Fix: prevent image blurring at 100% zoom
Even at 100% GDI+ manages to do some smoothing.
Also minor optimization - not messing with Graphics state when not needed.
* No ScaleTransform is needed at 100% zoom
* Fix: zoom-in when selection is out of sight
Co-authored-by: jklingen <jklingen@users.noreply.github.com>
This commit is contained in:
commit
10f227da51
22 changed files with 1059 additions and 138 deletions
|
@ -116,6 +116,18 @@ namespace Greenshot.Drawing.Adorners
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the bounds of the Adorner as displayed on the parent Surface
|
||||
/// </summary>
|
||||
protected virtual Rectangle BoundsOnSurface
|
||||
{
|
||||
get
|
||||
{
|
||||
Point displayLocation = Owner.Parent.ToSurfaceCoordinates(Location);
|
||||
return new Rectangle(displayLocation.X - _size.Width / 2, displayLocation.Y - _size.Height / 2, _size.Width, _size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The adorner is active if the edit status is not idle or undrawn
|
||||
/// </summary>
|
||||
|
|
|
@ -142,7 +142,7 @@ namespace Greenshot.Drawing.Adorners
|
|||
{
|
||||
Graphics targetGraphics = paintEventArgs.Graphics;
|
||||
|
||||
var bounds = Bounds;
|
||||
var bounds = BoundsOnSurface;
|
||||
GraphicsState state = targetGraphics.Save();
|
||||
|
||||
targetGraphics.SmoothingMode = SmoothingMode.None;
|
||||
|
|
|
@ -169,7 +169,7 @@ namespace Greenshot.Drawing.Adorners
|
|||
{
|
||||
Graphics targetGraphics = paintEventArgs.Graphics;
|
||||
|
||||
var bounds = Bounds;
|
||||
var bounds = BoundsOnSurface;
|
||||
GraphicsState state = targetGraphics.Save();
|
||||
|
||||
targetGraphics.SmoothingMode = SmoothingMode.None;
|
||||
|
|
|
@ -61,26 +61,26 @@ namespace Greenshot.Drawing.Adorners
|
|||
|
||||
Owner.Invalidate();
|
||||
Point newGripperLocation = new Point(mouseEventArgs.X, mouseEventArgs.Y);
|
||||
Rectangle surfaceBounds = new Rectangle(0, 0, Owner.Parent.Width, Owner.Parent.Height);
|
||||
Rectangle imageBounds = new Rectangle(0, 0, Owner.Parent.Image.Width, Owner.Parent.Image.Height);
|
||||
// Check if gripper inside the parent (surface), if not we need to move it inside
|
||||
// This was made for BUG-1682
|
||||
if (!surfaceBounds.Contains(newGripperLocation))
|
||||
if (!imageBounds.Contains(newGripperLocation))
|
||||
{
|
||||
if (newGripperLocation.X > surfaceBounds.Right)
|
||||
if (newGripperLocation.X > imageBounds.Right)
|
||||
{
|
||||
newGripperLocation.X = surfaceBounds.Right - 5;
|
||||
newGripperLocation.X = imageBounds.Right - 5;
|
||||
}
|
||||
if (newGripperLocation.X < surfaceBounds.Left)
|
||||
if (newGripperLocation.X < imageBounds.Left)
|
||||
{
|
||||
newGripperLocation.X = surfaceBounds.Left;
|
||||
newGripperLocation.X = imageBounds.Left;
|
||||
}
|
||||
if (newGripperLocation.Y > surfaceBounds.Bottom)
|
||||
if (newGripperLocation.Y > imageBounds.Bottom)
|
||||
{
|
||||
newGripperLocation.Y = surfaceBounds.Bottom - 5;
|
||||
newGripperLocation.Y = imageBounds.Bottom - 5;
|
||||
}
|
||||
if (newGripperLocation.Y < surfaceBounds.Top)
|
||||
if (newGripperLocation.Y < imageBounds.Top)
|
||||
{
|
||||
newGripperLocation.Y = surfaceBounds.Top;
|
||||
newGripperLocation.Y = imageBounds.Top;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ namespace Greenshot.Drawing.Adorners
|
|||
{
|
||||
Graphics targetGraphics = paintEventArgs.Graphics;
|
||||
|
||||
var bounds = Bounds;
|
||||
var bounds = BoundsOnSurface;
|
||||
targetGraphics.FillRectangle(Brushes.Green, bounds.X, bounds.Y, bounds.Width, bounds.Height);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
/// </summary>
|
||||
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 Rectangle.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -33,7 +33,6 @@ using System.ComponentModel;
|
|||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Windows.Forms;
|
||||
using GreenshotPlugin.IniFile;
|
||||
using GreenshotPlugin.Interfaces;
|
||||
using GreenshotPlugin.Interfaces.Drawing.Adorners;
|
||||
|
@ -298,34 +297,7 @@ namespace Greenshot.Drawing
|
|||
|
||||
public virtual void Invalidate() {
|
||||
if (Status != EditStatus.UNDRAWN) {
|
||||
_parent?.Invalidate(DrawingBounds);
|
||||
}
|
||||
}
|
||||
|
||||
public void AlignToParent(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) {
|
||||
if (_parent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
|
||||
if (horizontalAlignment == HorizontalAlignment.Left) {
|
||||
Left = lineThickness/2;
|
||||
}
|
||||
if (horizontalAlignment == HorizontalAlignment.Right) {
|
||||
Left = _parent.Width - Width - lineThickness/2;
|
||||
}
|
||||
if (horizontalAlignment == HorizontalAlignment.Center) {
|
||||
Left = (_parent.Width / 2) - (Width / 2) - lineThickness/2;
|
||||
}
|
||||
|
||||
if (verticalAlignment == VerticalAlignment.TOP) {
|
||||
Top = lineThickness/2;
|
||||
}
|
||||
if (verticalAlignment == VerticalAlignment.BOTTOM) {
|
||||
Top = _parent.Height - Height - lineThickness/2;
|
||||
}
|
||||
if (verticalAlignment == VerticalAlignment.CENTER) {
|
||||
Top = (_parent.Height / 2) - (Height / 2) - lineThickness/2;
|
||||
_parent?.InvalidateElements(DrawingBounds);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -235,6 +235,30 @@ namespace Greenshot.Drawing {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A rectangle containing DrawingBounds of all drawableContainers in this list,
|
||||
/// or empty rectangle if nothing is there.
|
||||
/// </summary>
|
||||
public Rectangle DrawingBounds
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Count == 0)
|
||||
{
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = this[0].DrawingBounds;
|
||||
for (int i = 1; i < Count; i++)
|
||||
{
|
||||
result = Rectangle.Union(result, this[i].DrawingBounds);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers all elements in the list ot be redrawn.
|
||||
/// </summary>
|
||||
|
@ -286,7 +310,7 @@ namespace Greenshot.Drawing {
|
|||
{
|
||||
region = Rectangle.Union(region, dc.DrawingBounds);
|
||||
}
|
||||
Parent.Invalidate(region);
|
||||
Parent.InvalidateElements(region);
|
||||
}
|
||||
/// <summary>
|
||||
/// Indicates whether the given list of elements can be pulled up,
|
||||
|
@ -523,9 +547,9 @@ namespace Greenshot.Drawing {
|
|||
}
|
||||
}
|
||||
|
||||
public virtual void ShowContextMenu(MouseEventArgs e, ISurface surface)
|
||||
public virtual void ShowContextMenu(MouseEventArgs e, ISurface iSurface)
|
||||
{
|
||||
if (!(surface is Surface))
|
||||
if (!(iSurface is Surface surface))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -542,8 +566,7 @@ namespace Greenshot.Drawing {
|
|||
ContextMenuStrip menu = new ContextMenuStrip();
|
||||
AddContextMenuItems(menu, surface);
|
||||
if (menu.Items.Count > 0) {
|
||||
// TODO: cast should be somehow avoided
|
||||
menu.Show((Surface)surface, e.Location);
|
||||
menu.Show(surface, surface.ToSurfaceCoordinates(e.Location));
|
||||
while (true) {
|
||||
if (menu.Visible) {
|
||||
Application.DoEvents();
|
||||
|
|
|
@ -47,8 +47,8 @@ namespace Greenshot.Drawing {
|
|||
/// Constructor
|
||||
/// </summary>
|
||||
public FreehandContainer(Surface parent) : base(parent) {
|
||||
Width = parent.Width;
|
||||
Height = parent.Height;
|
||||
Width = parent.Image.Width;
|
||||
Height = parent.Image.Height;
|
||||
Top = 0;
|
||||
Left = 0;
|
||||
}
|
||||
|
@ -236,7 +236,14 @@ namespace Greenshot.Drawing {
|
|||
int safetymargin = 10;
|
||||
return new Rectangle(myBounds.Left + Left - (safetymargin+lineThickness), myBounds.Top + Top - (safetymargin+lineThickness), myBounds.Width + 2*(lineThickness+safetymargin), myBounds.Height + 2*(lineThickness+safetymargin));
|
||||
}
|
||||
return new Rectangle(0, 0, _parent?.Width??0, _parent?.Height?? 0);
|
||||
if (_parent?.Image is Image image)
|
||||
{
|
||||
return new Rectangle(0, 0, image.Width, image.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
|
@ -298,10 +298,39 @@ namespace Greenshot.Drawing
|
|||
set
|
||||
{
|
||||
_image = value;
|
||||
Size = _image.Size;
|
||||
UpdateSize();
|
||||
}
|
||||
}
|
||||
|
||||
[NonSerialized]
|
||||
private Matrix _zoomMatrix = new Matrix(1, 0, 0, 1, 0, 0);
|
||||
[NonSerialized]
|
||||
private Matrix _inverseZoomMatrix = new Matrix(1, 0, 0, 1, 0, 0);
|
||||
[NonSerialized]
|
||||
private Fraction _zoomFactor = Fraction.Identity;
|
||||
public Fraction ZoomFactor
|
||||
{
|
||||
get => _zoomFactor;
|
||||
set
|
||||
{
|
||||
_zoomFactor = value;
|
||||
var inverse = _zoomFactor.Inverse();
|
||||
_zoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0);
|
||||
_inverseZoomMatrix = new Matrix(inverse, 0, 0, inverse, 0, 0);
|
||||
UpdateSize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the surface size as zoomed image size.
|
||||
/// </summary>
|
||||
private void UpdateSize()
|
||||
{
|
||||
var size = _image.Size;
|
||||
Size = new Size((int)(size.Width * _zoomFactor), (int)(size.Height * _zoomFactor));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 +476,6 @@ namespace Greenshot.Drawing
|
|||
|
||||
// Set new values
|
||||
Image = newImage;
|
||||
Size = newImage.Size;
|
||||
|
||||
_modified = true;
|
||||
}
|
||||
|
@ -779,9 +807,9 @@ namespace Greenshot.Drawing
|
|||
return cursorContainer;
|
||||
}
|
||||
|
||||
public ITextContainer AddTextContainer(string text, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, Color fillColor)
|
||||
public ITextContainer AddTextContainer(string text, int x, int y, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, Color fillColor)
|
||||
{
|
||||
TextContainer textContainer = new TextContainer(this) {Text = text};
|
||||
TextContainer textContainer = new TextContainer(this) {Text = text, Left = x, Top = y};
|
||||
textContainer.SetFieldValue(FieldType.FONT_FAMILY, family.Name);
|
||||
textContainer.SetFieldValue(FieldType.FONT_BOLD, bold);
|
||||
textContainer.SetFieldValue(FieldType.FONT_ITALIC, italic);
|
||||
|
@ -792,8 +820,6 @@ namespace Greenshot.Drawing
|
|||
textContainer.SetFieldValue(FieldType.SHADOW, shadow);
|
||||
// Make sure the Text fits
|
||||
textContainer.FitToText();
|
||||
// Align to Surface
|
||||
textContainer.AlignToParent(horizontalAlignment, verticalAlignment);
|
||||
|
||||
//AggregatedProperties.UpdateElement(textContainer);
|
||||
AddElement(textContainer);
|
||||
|
@ -967,13 +993,13 @@ namespace Greenshot.Drawing
|
|||
{
|
||||
cropRectangle = new Rectangle(cropRectangle.Left, 0, cropRectangle.Width, cropRectangle.Height + cropRectangle.Top);
|
||||
}
|
||||
if (cropRectangle.Left + cropRectangle.Width > Width)
|
||||
if (cropRectangle.Left + cropRectangle.Width > Image.Width)
|
||||
{
|
||||
cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Width - cropRectangle.Left, cropRectangle.Height);
|
||||
cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Image.Width - cropRectangle.Left, cropRectangle.Height);
|
||||
}
|
||||
if (cropRectangle.Top + cropRectangle.Height > Height)
|
||||
if (cropRectangle.Top + cropRectangle.Height > Image.Height)
|
||||
{
|
||||
cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Height - cropRectangle.Top);
|
||||
cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Image.Height - cropRectangle.Top);
|
||||
}
|
||||
if (cropRectangle.Height > 0 && cropRectangle.Width > 0)
|
||||
{
|
||||
|
@ -1086,6 +1112,12 @@ namespace Greenshot.Drawing
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translate mouse coordinates as if they were applied directly to unscaled image.
|
||||
/// </summary>
|
||||
private MouseEventArgs InverseZoomMouseCoordinates(MouseEventArgs e)
|
||||
=> new MouseEventArgs(e.Button, e.Clicks, (int)(e.X / _zoomFactor), (int)(e.Y / _zoomFactor), e.Delta);
|
||||
|
||||
/// <summary>
|
||||
/// This event handler is called when someone presses the mouse on a surface.
|
||||
/// </summary>
|
||||
|
@ -1093,6 +1125,7 @@ namespace Greenshot.Drawing
|
|||
/// <param name="e"></param>
|
||||
private void SurfaceMouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
e = InverseZoomMouseCoordinates(e);
|
||||
|
||||
// Handle Adorners
|
||||
var adorner = FindActiveAdorner(e);
|
||||
|
@ -1187,6 +1220,7 @@ namespace Greenshot.Drawing
|
|||
/// <param name="e"></param>
|
||||
private void SurfaceMouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
e = InverseZoomMouseCoordinates(e);
|
||||
|
||||
// Handle Adorners
|
||||
var adorner = FindActiveAdorner(e);
|
||||
|
@ -1276,6 +1310,8 @@ namespace Greenshot.Drawing
|
|||
/// <param name="e"></param>
|
||||
private void SurfaceMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
e = InverseZoomMouseCoordinates(e);
|
||||
|
||||
// Handle Adorners
|
||||
var adorner = FindActiveAdorner(e);
|
||||
if (adorner != null)
|
||||
|
@ -1371,6 +1407,25 @@ namespace Greenshot.Drawing
|
|||
return GetImage(RenderMode.EXPORT);
|
||||
}
|
||||
|
||||
private static Rectangle ZoomClipRectangle(Rectangle rc, double scale, int inflateAmount = 0)
|
||||
{
|
||||
rc = new Rectangle(
|
||||
(int)(rc.X * scale),
|
||||
(int)(rc.Y * scale),
|
||||
(int)(rc.Width * scale) + 1,
|
||||
(int)(rc.Height * scale) + 1
|
||||
);
|
||||
if (scale > 1)
|
||||
{
|
||||
inflateAmount = (int)(inflateAmount * scale);
|
||||
}
|
||||
rc.Inflate(inflateAmount, inflateAmount);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void InvalidateElements(Rectangle rc)
|
||||
=> Invalidate(ZoomClipRectangle(rc, _zoomFactor, 1));
|
||||
|
||||
/// <summary>
|
||||
/// This is the event handler for the Paint Event, try to draw as little as possible!
|
||||
/// </summary>
|
||||
|
@ -1379,14 +1434,34 @@ 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;
|
||||
}
|
||||
|
||||
if (_elements.HasIntersectingFilters(clipRectangle))
|
||||
// Correction to prevent rounding errors at certain zoom levels.
|
||||
// When zooming to N/M, clip rectangle top and left coordinates should be multiples of N.
|
||||
if (_zoomFactor.Numerator > 1 && _zoomFactor.Denominator > 1)
|
||||
{
|
||||
int horizontalCorrection = targetClipRectangle.Left % (int)_zoomFactor.Numerator;
|
||||
int verticalCorrection = targetClipRectangle.Top % (int)_zoomFactor.Numerator;
|
||||
if (horizontalCorrection != 0)
|
||||
{
|
||||
targetClipRectangle.X -= horizontalCorrection;
|
||||
targetClipRectangle.Width += horizontalCorrection;
|
||||
}
|
||||
if (verticalCorrection != 0)
|
||||
{
|
||||
targetClipRectangle.Y -= verticalCorrection;
|
||||
targetClipRectangle.Height += verticalCorrection;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2);
|
||||
|
||||
if (_elements.HasIntersectingFilters(imageClipRectangle) || _zoomFactor > Fraction.Identity)
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
|
@ -1409,18 +1484,44 @@ 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(Rectangle.Round(targetGraphics.ClipBounds), _zoomFactor.Inverse(), 2));
|
||||
_elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle);
|
||||
}
|
||||
if (_zoomFactor == Fraction.Identity)
|
||||
{
|
||||
targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor);
|
||||
if (_zoomFactor > Fraction.Identity)
|
||||
{
|
||||
DrawSharpImage(targetGraphics, _buffer, imageClipRectangle);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawSmoothImage(targetGraphics, _buffer, imageClipRectangle);
|
||||
}
|
||||
targetGraphics.ResetTransform();
|
||||
}
|
||||
targetGraphics.DrawImage(_buffer, clipRectangle, clipRectangle, GraphicsUnit.Pixel);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawBackground(targetGraphics, clipRectangle);
|
||||
targetGraphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel);
|
||||
_elements.Draw(targetGraphics, null, RenderMode.EDIT, clipRectangle);
|
||||
DrawBackground(targetGraphics, targetClipRectangle);
|
||||
if (_zoomFactor == Fraction.Identity)
|
||||
{
|
||||
targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel);
|
||||
_elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor);
|
||||
DrawSmoothImage(targetGraphics, Image, imageClipRectangle);
|
||||
_elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle);
|
||||
targetGraphics.ResetTransform();
|
||||
}
|
||||
}
|
||||
|
||||
// No clipping for the adorners
|
||||
|
@ -1435,6 +1536,32 @@ namespace Greenshot.Drawing
|
|||
}
|
||||
}
|
||||
|
||||
private void DrawSmoothImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle)
|
||||
{
|
||||
var state = targetGraphics.Save();
|
||||
targetGraphics.SmoothingMode = SmoothingMode.HighQuality;
|
||||
targetGraphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
|
||||
targetGraphics.CompositingQuality = CompositingQuality.HighQuality;
|
||||
targetGraphics.PixelOffsetMode = PixelOffsetMode.None;
|
||||
|
||||
targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel);
|
||||
|
||||
targetGraphics.Restore(state);
|
||||
}
|
||||
|
||||
private void DrawSharpImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle)
|
||||
{
|
||||
var state = targetGraphics.Save();
|
||||
targetGraphics.SmoothingMode = SmoothingMode.None;
|
||||
targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||
targetGraphics.CompositingQuality = CompositingQuality.HighQuality;
|
||||
targetGraphics.PixelOffsetMode = PixelOffsetMode.None;
|
||||
|
||||
targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel);
|
||||
|
||||
targetGraphics.Restore(state);
|
||||
}
|
||||
|
||||
private void DrawBackground(Graphics targetGraphics, Rectangle clipRectangle)
|
||||
{
|
||||
// check if we need to draw the checkerboard
|
||||
|
@ -1742,43 +1869,79 @@ namespace Greenshot.Drawing
|
|||
}
|
||||
else if (ClipboardHelper.ContainsImage(clipboard))
|
||||
{
|
||||
int x = 10;
|
||||
int y = 10;
|
||||
|
||||
// FEATURE-995: Added a check for the current mouse cursor location, to paste the image on that location.
|
||||
var mousePositionOnControl = PointToClient(MousePosition);
|
||||
if (ClientRectangle.Contains(mousePositionOnControl))
|
||||
{
|
||||
x = mousePositionOnControl.X;
|
||||
y = mousePositionOnControl.Y;
|
||||
}
|
||||
Point pasteLocation = GetPasteLocation(0.1f, 0.1f);
|
||||
|
||||
foreach (Image clipboardImage in ClipboardHelper.GetImages(clipboard))
|
||||
{
|
||||
if (clipboardImage != null)
|
||||
{
|
||||
DeselectAllElements();
|
||||
IImageContainer container = AddImageContainer(clipboardImage as Bitmap, x, y);
|
||||
IImageContainer container = AddImageContainer(clipboardImage as Bitmap, pasteLocation.X, pasteLocation.Y);
|
||||
SelectElement(container);
|
||||
clipboardImage.Dispose();
|
||||
x += 10;
|
||||
y += 10;
|
||||
pasteLocation.X += 10;
|
||||
pasteLocation.Y += 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ClipboardHelper.ContainsText(clipboard))
|
||||
{
|
||||
Point pasteLocation = GetPasteLocation(0.4f, 0.4f);
|
||||
|
||||
string text = ClipboardHelper.GetText(clipboard);
|
||||
if (text != null)
|
||||
{
|
||||
DeselectAllElements();
|
||||
ITextContainer textContainer = AddTextContainer(text, HorizontalAlignment.Center, VerticalAlignment.CENTER,
|
||||
ITextContainer textContainer = AddTextContainer(text, pasteLocation.X, pasteLocation.Y,
|
||||
FontFamily.GenericSansSerif, 12f, false, false, false, 2, Color.Black, Color.Transparent);
|
||||
SelectElement(textContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a location to paste elements.
|
||||
/// If mouse is over the surface - use it's position, otherwise use the visible area.
|
||||
/// Return a point in image coordinate space.
|
||||
/// </summary>
|
||||
/// <param name="horizontalRatio">0.0f for the left edge of visible area, 1.0f for the right edge of visible area.</param>
|
||||
/// <param name="verticalRatio">0.0f for the top edge of visible area, 1.0f for the bottom edge of visible area.</param>
|
||||
private Point GetPasteLocation(float horizontalRatio = 0.5f, float verticalRatio = 0.5f)
|
||||
{
|
||||
var point = PointToClient(MousePosition);
|
||||
var rc = GetVisibleRectangle();
|
||||
if (!rc.Contains(point))
|
||||
{
|
||||
point = new Point(
|
||||
rc.Left + (int)(rc.Width * horizontalRatio),
|
||||
rc.Top + (int)(rc.Height * verticalRatio)
|
||||
);
|
||||
}
|
||||
return ToImageCoordinates(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the rectangle bounding the part of this Surface currently visible in the editor (in surface coordinate space).
|
||||
/// </summary>
|
||||
public Rectangle GetVisibleRectangle()
|
||||
{
|
||||
var bounds = Bounds;
|
||||
var clientArea = Parent.ClientRectangle;
|
||||
return new Rectangle(
|
||||
Math.Max(0, -bounds.Left),
|
||||
Math.Max(0, -bounds.Top),
|
||||
clientArea.Width,
|
||||
clientArea.Height
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the rectangle bounding all selected elements (in surface coordinates space),
|
||||
/// or empty rectangle if nothing is selcted.
|
||||
/// </summary>
|
||||
public Rectangle GetSelectionRectangle()
|
||||
=> ToSurfaceCoordinates(selectedElements.DrawingBounds);
|
||||
|
||||
/// <summary>
|
||||
/// Duplicate all the selecteded elements
|
||||
/// </summary>
|
||||
|
@ -2034,5 +2197,57 @@ namespace Greenshot.Drawing
|
|||
{
|
||||
return _elements.Contains(container);
|
||||
}
|
||||
|
||||
public Point ToSurfaceCoordinates(Point point)
|
||||
{
|
||||
Point[] points = { point };
|
||||
_zoomMatrix.TransformPoints(points);
|
||||
return points[0];
|
||||
}
|
||||
|
||||
public Rectangle ToSurfaceCoordinates(Rectangle rc)
|
||||
{
|
||||
if (_zoomMatrix.IsIdentity)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
Point[] points = { rc.Location, rc.Location + rc.Size };
|
||||
_zoomMatrix.TransformPoints(points);
|
||||
return new Rectangle(
|
||||
points[0].X,
|
||||
points[0].Y,
|
||||
points[1].X - points[0].X,
|
||||
points[1].Y - points[0].Y
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public Point ToImageCoordinates(Point point)
|
||||
{
|
||||
Point[] points = { point };
|
||||
_inverseZoomMatrix.TransformPoints(points);
|
||||
return points[0];
|
||||
}
|
||||
|
||||
public Rectangle ToImageCoordinates(Rectangle rc)
|
||||
{
|
||||
if (_inverseZoomMatrix.IsIdentity)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
Point[] points = { rc.Location, rc.Location + rc.Size };
|
||||
_inverseZoomMatrix.TransformPoints(points);
|
||||
return new Rectangle(
|
||||
points[0].X,
|
||||
points[0].Y,
|
||||
points[1].X - points[0].X,
|
||||
points[1].Y - points[0].Y
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
using Greenshot.Drawing.Fields;
|
||||
using Greenshot.Helpers;
|
||||
using Greenshot.Memento;
|
||||
using GreenshotPlugin.Core;
|
||||
using GreenshotPlugin.Interfaces.Drawing;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
@ -155,6 +156,24 @@ namespace Greenshot.Drawing
|
|||
FieldChanged += TextContainer_FieldChanged;
|
||||
}
|
||||
|
||||
protected override void SwitchParent(Surface newParent)
|
||||
{
|
||||
_parent.SizeChanged -= Parent_SizeChanged;
|
||||
base.SwitchParent(newParent);
|
||||
_parent.SizeChanged += Parent_SizeChanged;
|
||||
}
|
||||
|
||||
private void Parent_SizeChanged(object sender, EventArgs e)
|
||||
{
|
||||
UpdateTextBoxPosition();
|
||||
UpdateTextBoxFont();
|
||||
}
|
||||
|
||||
public override void ApplyBounds(RectangleF newBounds)
|
||||
{
|
||||
base.ApplyBounds(newBounds);
|
||||
UpdateTextBoxPosition();
|
||||
}
|
||||
|
||||
public override void Invalidate()
|
||||
{
|
||||
|
@ -255,7 +274,8 @@ namespace Greenshot.Drawing
|
|||
AcceptsTab = true,
|
||||
AcceptsReturn = true,
|
||||
BorderStyle = BorderStyle.None,
|
||||
Visible = false
|
||||
Visible = false,
|
||||
Font = new Font(FontFamily.GenericSansSerif, 1) // just need something non-default here
|
||||
};
|
||||
|
||||
_textBox.DataBindings.Add("Text", this, "Text", false, DataSourceUpdateMode.OnPropertyChanged);
|
||||
|
@ -388,7 +408,6 @@ namespace Greenshot.Drawing
|
|||
var newFont = CreateFont(fontFamily, fontBold, fontItalic, fontSize);
|
||||
_font?.Dispose();
|
||||
_font = newFont;
|
||||
_textBox.Font = _font;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -400,7 +419,6 @@ namespace Greenshot.Drawing
|
|||
var newFont = CreateFont(fontFamily, fontBold, fontItalic, fontSize);
|
||||
_font?.Dispose();
|
||||
_font = newFont;
|
||||
_textBox.Font = _font;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -413,6 +431,8 @@ namespace Greenshot.Drawing
|
|||
}
|
||||
}
|
||||
|
||||
UpdateTextBoxFont();
|
||||
|
||||
UpdateAlignment();
|
||||
}
|
||||
|
||||
|
@ -423,12 +443,34 @@ namespace Greenshot.Drawing
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will create the textbox exactly to the inner size of the element
|
||||
/// Set TextBox font according to the TextContainer font and the parent zoom factor.
|
||||
/// </summary>
|
||||
private void UpdateTextBoxFont()
|
||||
{
|
||||
if (_textBox == null || _font == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var textBoxFontScale = _parent?.ZoomFactor ?? Fraction.Identity;
|
||||
|
||||
var newFont = new Font(
|
||||
_font.FontFamily,
|
||||
_font.Size * textBoxFontScale,
|
||||
_font.Style,
|
||||
GraphicsUnit.Pixel
|
||||
);
|
||||
_textBox.Font.Dispose();
|
||||
_textBox.Font = newFont;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will align the textbox exactly to the inner size of the element
|
||||
/// is a bit of a hack, but for now it seems to work...
|
||||
/// </summary>
|
||||
private void UpdateTextBoxPosition()
|
||||
{
|
||||
if (_textBox == null)
|
||||
if (_textBox == null || Parent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -442,22 +484,20 @@ namespace Greenshot.Drawing
|
|||
correction = -1;
|
||||
}
|
||||
Rectangle absRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
|
||||
_textBox.Left = absRectangle.Left + lineWidth;
|
||||
_textBox.Top = absRectangle.Top + lineWidth;
|
||||
Rectangle displayRectangle = Parent.ToSurfaceCoordinates(absRectangle);
|
||||
_textBox.Left = displayRectangle.X + lineWidth;
|
||||
_textBox.Top = displayRectangle.Y + lineWidth;
|
||||
if (lineThickness <= 1)
|
||||
{
|
||||
lineWidth = 0;
|
||||
}
|
||||
_textBox.Width = absRectangle.Width - 2 * lineWidth + correction;
|
||||
_textBox.Height = absRectangle.Height - 2 * lineWidth + correction;
|
||||
}
|
||||
|
||||
public override void ApplyBounds(RectangleF newBounds)
|
||||
{
|
||||
base.ApplyBounds(newBounds);
|
||||
UpdateTextBoxPosition();
|
||||
_textBox.Width = displayRectangle.Width - 2 * lineWidth + correction;
|
||||
_textBox.Height = displayRectangle.Height - 2 * lineWidth + correction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set TextBox text align and fore color according to field values.
|
||||
/// </summary>
|
||||
private void UpdateTextBoxFormat()
|
||||
{
|
||||
if (_textBox == null)
|
||||
|
|
212
Greenshot/Forms/ImageEditorForm.Designer.cs
generated
212
Greenshot/Forms/ImageEditorForm.Designer.cs
generated
|
@ -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+9";
|
||||
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 = "1/4";
|
||||
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 = "1/2";
|
||||
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 = "2/3";
|
||||
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 = "3/4";
|
||||
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+0";
|
||||
this.zoomActualSizeMenuItem.Size = new System.Drawing.Size(209, 22);
|
||||
this.zoomActualSizeMenuItem.Tag = "1/1";
|
||||
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 = "2/1";
|
||||
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 = "3/1";
|
||||
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 = "4/1";
|
||||
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 = "6/1";
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,11 @@ namespace Greenshot {
|
|||
// whether part of the editor controls are disabled depending on selected item(s)
|
||||
private bool _controlsDisabledDueToConfirmable;
|
||||
|
||||
/// <summary>
|
||||
/// All provided zoom values (in percents) in ascending order.
|
||||
/// </summary>
|
||||
private readonly Fraction[] ZOOM_VALUES = new Fraction[] { (1, 4), (1, 2), (2, 3), (3, 4), (1 ,1), (2, 1), (3, 1), (4, 1), (6, 1) };
|
||||
|
||||
/// <summary>
|
||||
/// An Implementation for the IImageEditor, this way Plugins have access to the HWND handles wich can be used with Win32 API calls.
|
||||
/// </summary>
|
||||
|
@ -420,20 +425,14 @@ namespace Greenshot {
|
|||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
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 +859,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.NumPad0: // Ctrl + Num0
|
||||
case Keys.D0: // Ctrl + 0
|
||||
ZoomSetValueMenuItemClick(zoomActualSizeMenuItem, e);
|
||||
break;
|
||||
case Keys.NumPad9: // Ctrl + Num9
|
||||
case Keys.D9: // Ctrl + 9
|
||||
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 +1462,178 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute a size as a sum of surface size and chrome.
|
||||
/// Upper bound is working area of the screen. Lower bound is fixed value.
|
||||
/// </summary>
|
||||
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 maxWindowSize = GetAvailableScreenSpace();
|
||||
newWidth = Math.Min(newWidth, maxWindowSize.Width);
|
||||
newHeight = Math.Min(newHeight, maxWindowSize.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;
|
||||
|
||||
/// <summary>
|
||||
/// Compute a size that the form can take without getting out of working area of the screen.
|
||||
/// </summary>
|
||||
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.Right - Left, workingArea.Bottom - Top);
|
||||
} else {
|
||||
return workingArea.Size;
|
||||
}
|
||||
}
|
||||
|
||||
private void ZoomInMenuItemClick(object sender, EventArgs e) {
|
||||
var zoomValue = Surface.ZoomFactor;
|
||||
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 zoomValue = Surface.ZoomFactor;
|
||||
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;
|
||||
var nextValue = Fraction.Parse((string)senderMenuItem.Tag);
|
||||
|
||||
ZoomSetValue(nextValue);
|
||||
}
|
||||
|
||||
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(Fraction scale, int source, int boundary)
|
||||
=> (int)(source * scale) <= boundary;
|
||||
|
||||
var nextIndex = Array.FindLastIndex(
|
||||
ZOOM_VALUES,
|
||||
zoom => isFit(zoom, imageSize.Width, maxImageSize.Width)
|
||||
&& isFit(zoom, imageSize.Height, maxImageSize.Height)
|
||||
);
|
||||
var nextValue = nextIndex < 0 ? ZOOM_VALUES[0] : ZOOM_VALUES[nextIndex];
|
||||
|
||||
ZoomSetValue(nextValue);
|
||||
}
|
||||
|
||||
private void ZoomSetValue(Fraction value) {
|
||||
var surface = Surface as Surface;
|
||||
var panel = surface?.Parent as Panel;
|
||||
if (panel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (value == Surface.ZoomFactor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Store scroll position
|
||||
var rc = surface.GetVisibleRectangle(); // use visible rc by default
|
||||
var size = surface.Size;
|
||||
if (value > Surface.ZoomFactor) // being smart on zoom-in
|
||||
{
|
||||
var selection = surface.GetSelectionRectangle();
|
||||
selection.Intersect(rc);
|
||||
if (selection != Rectangle.Empty)
|
||||
{
|
||||
rc = selection; // zoom to visible part of selection
|
||||
}
|
||||
else
|
||||
{
|
||||
// if image fits completely to currently visible rc and there are no things to focus on
|
||||
// - prefer top left corner to zoom-in as less disorienting for screenshots
|
||||
if (size.Width < rc.Width)
|
||||
{
|
||||
rc.Width = 0;
|
||||
}
|
||||
if (size.Height < rc.Height)
|
||||
{
|
||||
rc.Height = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
var horizontalCenter = 1.0 * (rc.Left + rc.Width / 2) / size.Width;
|
||||
var verticalCenter = 1.0 * (rc.Top + rc.Height / 2) / size.Height;
|
||||
|
||||
// Set the new zoom value
|
||||
Surface.ZoomFactor = value;
|
||||
Size = GetOptimalWindowSize();
|
||||
AlignCanvasPositionAfterResize();
|
||||
|
||||
// Update zoom controls
|
||||
zoomStatusDropDownBtn.Text = ((int)(100 * (double)value)).ToString() + "%";
|
||||
var valueString = value.ToString();
|
||||
foreach (var item in zoomMenuStrip.Items) {
|
||||
if (item is ToolStripMenuItem menuItem) {
|
||||
menuItem.Checked = menuItem.Tag as string == valueString;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore scroll position
|
||||
rc = surface.GetVisibleRectangle();
|
||||
size = surface.Size;
|
||||
panel.AutoScrollPosition = new Point(
|
||||
(int)(horizontalCenter * size.Width) - rc.Width / 2,
|
||||
(int)(verticalCenter * size.Height) - rc.Height / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -937,6 +937,84 @@
|
|||
BIhPAPEZIL7CAAQ6fptA+D+IJskFIM2cgtoMKm6rQfg/iEY3oB9oC8jWozBbgXquAvFykGYQkDJuZlBy
|
||||
WQvC/0E0QRfANIJoLmF9BnXPHTD8H8QmyQD9wBMMSPg/iE20ATK6uQwWUTeR8X8Qn2gDHJOfM6Dh/yA+
|
||||
igHkZijqZSZyXQAA4IG1TpHFZ2gAAAAASUVORK5CYII=
|
||||
</value>
|
||||
</data>
|
||||
<data name="zoomInMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
|
||||
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJKSURBVDhPY/j//z+DX8FVDBxZ/ZAhqeWVRkrbm7lA
|
||||
fB+If0HpuSBxkDxIHU4DIqruB0bXPrzftODpia0n3+668/zLiaNXPh3oXf7yFEgcJA9SBzbALeMsCvbI
|
||||
Oq/hk3fx/rSND3ccufH60MuPP259+vb7+etPP28/evPt7PztT3b5AuVB6sAGWMUcQMdz83svnth96cWB
|
||||
q48/ngBpBor9B9FP3n47d+3JpxMlEy6dAqkDG6Djtwkd35+77e72M/feXbj78suDd19+fQCK/QfRID5I
|
||||
fOme+3tA6sAGqLitRse/dp5/fgCkGMj+j44/fvv97PC118eA7F9gA5Rc1qLj+wu239sNsgmk+Ofvv5+B
|
||||
Yv9BNDgsPv68tWrfw0MgdWAD1D13oOO52W3nz+6/+urw/ZdfT4M0AcXgYQAMyHPF3RcvgdSBDXBwcGCw
|
||||
s7NjsLW1ZbCxsWEwd8q0Mgo+/GLe5gdnbz77fBoU+mCbgfSz998vbtj/6pRxyMn7egHHILGAbIC1tbU8
|
||||
0JDijsl7/ltFXnjVOOP5pZNXvpx+/u7H9VNXv5zpmPvymk3crfvmkdcDDYPPQNIBzACgRi03N/eaHTv2
|
||||
/BcVFWtWs2qxc0x+PheI7wPxLyg91z7xiYZF1E0GFAOAtlsEBga3HTly5r+iolIPCwuLqpJxCYNTyisM
|
||||
DDSAAcUAoO3eCQkpEy5evPtfT89gGlCzMRAzEG2Aubl5ya1br/7b2zvPY2VldQFpJskAPT09LRERkRag
|
||||
5hiYZhAWlrEhzgDy8X8GAJItIDq7n94UAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="zoomOutMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
|
||||
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIySURBVDhPY/j//z+DtrY2g559IYNfwVU4jqx+yJDU
|
||||
8kojpe3NXCC+D8S/oPRckDhIHqQObACyRhiOqLofGF378H7Tgqcntp58u+vO8y8njl75dKB3+ctTIHGQ
|
||||
PEgd2AC3jLMo2CPrvIZP3sX70zY+3HHkxutDj958O/vk7bdzIAxiz9/+ZJcvUB6kDmyAVcwBdDw3v/fi
|
||||
id2XXhy4+vjjCZhmGL725NOJkgmXToHUgQ3Q8duEju/P3XZ3+5l77y7cffnlAToGiS/dc38PSB3YABW3
|
||||
1ej4187zzw+AFAPZ/9Hxx2+/nx2+9voYkP0LbICSy1p0fH/B9nu7QTaBFH/69vs5Mn798eetVfseHgKp
|
||||
Axug7rkDHc/Nbjt/dv/VV4fvv/x6Gj0MgAF5rrj74iWQOrABDg4ODHZ2dgy2trYMNjY2DOZOmVZGwYdf
|
||||
zNv84OzNZ59RDHj2/vvFDftfnTIOOXlfL+AYJBaQDbC2tpYHGlLcMXnPf6vIC68aZzy/dPLKl9PP3/24
|
||||
furqlzMdc19es4m7dd888nqgYfAZSDqAGQDUqOXm5l6zY8ee/6KiYs1qVi12jsnP5wLxfSD+BaXn2ic+
|
||||
0bCIusmAYgDQdovAwOC2I0fO/FdUVOphYWFRVTIuYXBKeYWBgQYwoBgAtN07ISFlwsWLd//r6RlMA2o2
|
||||
BmIGog0wNzcvuXXr1X97e+d5rKysLiDNJBmgp6enJSIi0gLUHAPTDMLCMjbEGUA+/s8AAJUZIgOF4ptY
|
||||
AAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="zoomBestFitMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
|
||||
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJISURBVDhPnZLLaxNRFIfvIvkLTF0J0uAjNDKoWaTk
|
||||
bS1RUYshLowWqZqFD/BBs/KxUEMoaKEgiJuRbqRQ6UaojTqUYGpJYmpMSFuMxklK2mmmmba2kzSZINd7
|
||||
pk1p4qa6+Djn3vs73x24gzDGSKvVIsp6B3XcntzEdS+LLnt5jdtXoAksQdqoNOzDOeRkwdbBGufuso4L
|
||||
D7Lso/7Z0HBYeP+DE0OfkiuB3oF8BPbhHHKywH51oo7j12OaUzfj7ADDhTJ8MbNclOZWSlUOgH5wdD50
|
||||
mpxDThYYOgON0Ld646F0XsyQHgOFpYpcgZywNuPpS0QgJwsOdLxphKXfpkdAQHq8KErL0EOFNfSvGJaB
|
||||
nCzYY3/diPQuxgUgmBfWMHx6Tih9gQrrX6XqXHBqYRxyskDdPtQI2z/y8wMISI8r1d+rMAwV1iAYHM1+
|
||||
hJws2H/C3wh9wxebyC4UZ0iPAV4oyxVYKkpc95N4AnKywGazIYvFgsxmMzKZTEjfds1w2BmcH2JmvxdW
|
||||
K5svAIjlKj8cLCR1Z8MsdWZ8/RW2CoxG424i6e55xmCD6yv/8AWXCCfFz9xieToyKUZ76PyU6WKK1bum
|
||||
HYec0fX/oCYggy12+7H7fj+Dm5p2Pt5n8FqOXOFoAkuQNiptvZTTtJ7/huoE5PZWh8PpGxuL4uZm9VOF
|
||||
QrFXrfOgNjf/F0SA6gTk9pNdXe6+eDyNKergczKsI6BtC/R6vSeV4rHVevSlUqlsh+F/ElAU1aJSqbxk
|
||||
uLM2DOzYZdqe4P/B6A86Ah9NBTgWLgAAAABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="zoomActualSizeMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
|
||||
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJXSURBVDhPnZLda1JhHMefC/0Lcl0FMelFZhwqLxy+
|
||||
t4YV1UjsImvFKi+qQS9MuujlohIZ1GAQjG7O2k0ERdALpW3SZNpQ0zllall6NKwzj1OWczId8fT8zuaY
|
||||
drO6+PBwvs/393kezjkIY4ykUimitNdQ19XoGqabGXTOyknMtjmawBBqqysNOexDjxesH6xz4gZjOHU7
|
||||
w9wd+eF96yuMfmPL3o8zJdfA05wfctiHHi/QXwg2cPBSSHLkcpgZepVxeD7nJ+YXaz9LlWU2X6p+/T5X
|
||||
CT62Z0ePkn3o8QJFt6sZ+spA2DsWmXVlC5VMrzWESYZBQp6nYtmS1zIY8UOPF+zqet0MQ79L2kEQSBWn
|
||||
i+XaPMlwMldOQwY8cTJO6PGCbfrnzdTeh1i+CMDJJFu7AeCO5SehxwvEnS+aYUbsqTEY5n4tJarLvxdI
|
||||
hmGF9wCCZx8yE9DjBTsPOZqhe22h4HiUc8+VqtnT1/2YZBhWuAV5kVN998MR6PECnU6HNBoNUqvVSKVS
|
||||
IXnHRcVeo3t2+E06mOYW4zBUp7BQTb0c5/yy4z6GOja58hXWC5RK5VYi6et/6MQK0zR35xEb8c2UP7HF
|
||||
pbg/Wg7007mY6kyCkZvihj3GwMp/UBeQwTa9/sAth8OJW1o239uhsGr2nWdpAkOora609mxW0n7yC2oQ
|
||||
kNPbDQajzeMJ4NZW8QOBQLBdLLOgDjP3F0SAGgTk9MM9PebBcDiJKWr3EBmWEdCGBXK53JJIcFir3T8s
|
||||
FAo7YfifBBRFtYlEIisZ7q4PA5u2qDYm+H8w+gNv9h/ta4LougAAAABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="zoomStatusDropDownBtn.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHYSURBVDhPY8AH4ltfa6S0vZ6b3Pr6PhD/AtEgPkgcqgQ3
|
||||
iKi6Hxhd8/B+0/ynJ7acfLvrzrPPJ45c+Xigd9nLUyBxkDxUKSZwyzmj4Z176f7UjQ92HL7++tCjN9/O
|
||||
Pn7z7RwIg9jztz3e5QOUB6mDakEFVjH75xb0Xjyx++KLA1cefToO0wzD1558OlE84eJpkDqoFlSg7bvp
|
||||
/pytd3aADMCFl+6+vwekDqoFFSi7rf614/xzuGJ0Fzx+++3soauvjoHUQbWgAiXnNffn77i3G6wZqBib
|
||||
ASv3PTgEUgfVggrUPLfPzWo/d3bv5VeH77/4fBrdgEevv54r7rpwCaQOqgUVWDpmWhkGHXoxf/P9szef
|
||||
fkIx4Nn7b5fW73t5yjj45H2doKOYsWBpaSlvbW1d3DF5z3/LyAuvGqY/vXTiyqfTz999v37qyucz7fNe
|
||||
XrOOvXXfNOIaZjqwsbHRcnV1r9mxY89/UVHRZlXLZjuH5GdzHZOf3XdMevYLRIP49vFPMW0G2moRGBjc
|
||||
duTImf8KCko9bGxsqlApwgBos098fPKEixfv/tfT05/KyspqDJUiDpiZmZXcuvXqv52d8zxmZmYXqDDx
|
||||
wMDAQEtYWLgFaHMMVIhegIEBADK7VwLsrsplAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="btnStepLabel01.Image" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
|
@ -1026,4 +1104,7 @@
|
|||
<metadata name="fileSavedStatusContextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="zoomMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>782, 17</value>
|
||||
</metadata>
|
||||
</root>
|
BIN
Greenshot/icons/fugue/magnifier-zoom-actual.png
Normal file
BIN
Greenshot/icons/fugue/magnifier-zoom-actual.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 742 B |
BIN
Greenshot/icons/fugue/magnifier-zoom-fit.png
Normal file
BIN
Greenshot/icons/fugue/magnifier-zoom-fit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 726 B |
BIN
Greenshot/icons/fugue/magnifier-zoom-in.png
Normal file
BIN
Greenshot/icons/fugue/magnifier-zoom-in.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 733 B |
BIN
Greenshot/icons/fugue/magnifier-zoom-out.png
Normal file
BIN
Greenshot/icons/fugue/magnifier-zoom-out.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 707 B |
BIN
Greenshot/icons/fugue/magnifier-zoom.png
Normal file
BIN
Greenshot/icons/fugue/magnifier-zoom.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 676 B |
|
@ -95,7 +95,7 @@ namespace GreenshotOfficePlugin.OfficeExport
|
|||
var pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
|
||||
ImageOutput.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings);
|
||||
var base64String = Convert.ToBase64String(pngStream.GetBuffer());
|
||||
var imageXmlStr = string.Format(XmlImageContent, base64String, surfaceToUpload.Width, surfaceToUpload.Height);
|
||||
var imageXmlStr = string.Format(XmlImageContent, base64String, surfaceToUpload.Image.Width, surfaceToUpload.Image.Height);
|
||||
var pageChangesXml = string.Format(XmlOutline, imageXmlStr, page.Id, OnenoteNamespace2010, page.Name);
|
||||
LOG.InfoFormat("Sending XML: {0}", pageChangesXml);
|
||||
oneNoteApplication.ComObject.UpdatePageContent(pageChangesXml, DateTime.MinValue, XMLSchema.xs2010, false);
|
||||
|
|
152
GreenshotPlugin/Core/Fraction.cs
Normal file
152
GreenshotPlugin/Core/Fraction.cs
Normal file
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GreenshotPlugin.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic Fraction (Rational) numbers with features only needed to represent scale factors.
|
||||
/// </summary>
|
||||
public readonly struct Fraction : IEquatable<Fraction>, IComparable<Fraction>
|
||||
{
|
||||
public static Fraction Identity { get; } = new Fraction(1, 1);
|
||||
|
||||
public uint Numerator { get; }
|
||||
public uint Denominator { get; }
|
||||
|
||||
public Fraction(uint numerator, uint denominator)
|
||||
{
|
||||
if (denominator == 0)
|
||||
{
|
||||
throw new ArgumentException("Can't divide by zero.", nameof(denominator));
|
||||
}
|
||||
if (numerator == 0)
|
||||
{
|
||||
throw new ArgumentException("Zero is not supported by this implementation.", nameof(numerator));
|
||||
}
|
||||
var gcd = GreatestCommonDivisor(numerator, denominator);
|
||||
Numerator = numerator / gcd;
|
||||
Denominator = denominator / gcd;
|
||||
}
|
||||
|
||||
public Fraction Inverse()
|
||||
=> new Fraction(Denominator, Numerator);
|
||||
|
||||
#region Parse
|
||||
|
||||
private static readonly Regex PARSE_REGEX = new Regex(@"^([1-9][0-9]*)\/([1-9][0-9]*)$", RegexOptions.Compiled);
|
||||
public static bool TryParse(string str, out Fraction result)
|
||||
{
|
||||
var match = PARSE_REGEX.Match(str);
|
||||
if (!match.Success)
|
||||
{
|
||||
result = Identity;
|
||||
return false;
|
||||
}
|
||||
var numerator = uint.Parse(match.Groups[1].Value);
|
||||
var denominator = uint.Parse(match.Groups[2].Value);
|
||||
result = new Fraction(numerator, denominator);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Fraction Parse(string str)
|
||||
=> TryParse(str, out var result)
|
||||
? result
|
||||
: throw new ArgumentException($"Could not parse the input \"{str}\".", nameof(str));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides, interface implementations
|
||||
|
||||
public override string ToString()
|
||||
=> $"{Numerator}/{Denominator}";
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is Fraction fraction && Equals(fraction);
|
||||
|
||||
public bool Equals(Fraction other)
|
||||
=> Numerator == other.Numerator && Denominator == other.Denominator;
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = -1534900553;
|
||||
hashCode = hashCode * -1521134295 + Numerator.GetHashCode();
|
||||
hashCode = hashCode * -1521134295 + Denominator.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(Fraction other)
|
||||
=> (int)(Numerator * other.Denominator) - (int)(other.Numerator * Denominator);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equality operators
|
||||
|
||||
public static bool operator ==(Fraction left, Fraction right)
|
||||
=> left.Equals(right);
|
||||
|
||||
public static bool operator !=(Fraction left, Fraction right)
|
||||
=> !(left == right);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparison operators
|
||||
|
||||
public static bool operator <(Fraction left, Fraction right)
|
||||
=> left.CompareTo(right) < 0;
|
||||
|
||||
public static bool operator <=(Fraction left, Fraction right)
|
||||
=> left.CompareTo(right) <= 0;
|
||||
|
||||
public static bool operator >(Fraction left, Fraction right)
|
||||
=> left.CompareTo(right) > 0;
|
||||
|
||||
public static bool operator >=(Fraction left, Fraction right)
|
||||
=> left.CompareTo(right) >= 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scale operators
|
||||
|
||||
public static Fraction operator *(Fraction left, Fraction right)
|
||||
=> new Fraction(left.Numerator * right.Numerator, left.Denominator * right.Denominator);
|
||||
|
||||
public static Fraction operator *(Fraction left, uint right)
|
||||
=> new Fraction(left.Numerator * right, left.Denominator);
|
||||
|
||||
public static Fraction operator *(uint left, Fraction right)
|
||||
=> new Fraction(left * right.Numerator, right.Denominator);
|
||||
|
||||
public static Fraction operator /(Fraction left, Fraction right)
|
||||
=> new Fraction(left.Numerator * right.Denominator, left.Denominator * right.Numerator);
|
||||
|
||||
public static Fraction operator /(Fraction left, uint right)
|
||||
=> new Fraction(left.Numerator, left.Denominator * right);
|
||||
|
||||
public static Fraction operator /(uint left, Fraction right)
|
||||
=> new Fraction(left * right.Denominator, right.Numerator);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Type conversion operators
|
||||
|
||||
public static implicit operator double(Fraction fraction)
|
||||
=> 1.0 * fraction.Numerator / fraction.Denominator;
|
||||
|
||||
public static implicit operator float(Fraction fraction)
|
||||
=> 1.0f * fraction.Numerator / fraction.Denominator;
|
||||
|
||||
public static implicit operator Fraction(uint number)
|
||||
=> new Fraction(number, 1u);
|
||||
|
||||
public static implicit operator Fraction((uint numerator, uint demoninator) tuple)
|
||||
=> new Fraction(tuple.numerator, tuple.demoninator);
|
||||
|
||||
#endregion
|
||||
|
||||
private static uint GreatestCommonDivisor(uint a, uint b)
|
||||
=> (b != 0) ? GreatestCommonDivisor(b, a % b) : a;
|
||||
}
|
||||
}
|
|
@ -102,7 +102,6 @@ namespace GreenshotPlugin.Interfaces.Drawing
|
|||
get;
|
||||
set;
|
||||
}
|
||||
void AlignToParent(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment);
|
||||
void Invalidate();
|
||||
bool ClickableAt(int x, int y);
|
||||
void MoveBy(int x, int y);
|
||||
|
@ -146,6 +145,10 @@ namespace GreenshotPlugin.Interfaces.Drawing
|
|||
get;
|
||||
set;
|
||||
}
|
||||
Rectangle DrawingBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
void MakeBoundsChangeUndoable(bool allowMerge);
|
||||
void Transform(Matrix matrix);
|
||||
void MoveBy(int dx, int dy);
|
||||
|
|
|
@ -23,6 +23,7 @@ using System;
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using GreenshotPlugin.Core;
|
||||
using GreenshotPlugin.Effects;
|
||||
using GreenshotPlugin.Interfaces.Drawing;
|
||||
|
||||
|
@ -84,8 +85,8 @@ namespace GreenshotPlugin.Interfaces
|
|||
/// The TextContainer will be "re"sized to the text size.
|
||||
/// </summary>
|
||||
/// <param name="text">String to show</param>
|
||||
/// <param name="horizontalAlignment">Left, Center, Right</param>
|
||||
/// <param name="verticalAlignment">TOP, CENTER, BOTTOM</param>
|
||||
/// <param name="x">Where to put the container, X coordinate in the Image coordinate space</param>
|
||||
/// <param name="y">Where to put the container, Y coordinate in the Image coordinate space</param>
|
||||
/// <param name="family">FontFamily</param>
|
||||
/// <param name="size">Font Size in float</param>
|
||||
/// <param name="italic">bool true if italic</param>
|
||||
|
@ -94,7 +95,7 @@ namespace GreenshotPlugin.Interfaces
|
|||
/// <param name="borderSize">size of border (0 for none)</param>
|
||||
/// <param name="color">Color of string</param>
|
||||
/// <param name="fillColor">Color of background (e.g. Color.Transparent)</param>
|
||||
ITextContainer AddTextContainer(string text, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, Color fillColor);
|
||||
ITextContainer AddTextContainer(string text, int x, int y, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, Color fillColor);
|
||||
|
||||
IImageContainer AddImageContainer(Image image, int x, int y);
|
||||
ICursorContainer AddCursorContainer(Cursor cursor, int x, int y);
|
||||
|
@ -147,8 +148,13 @@ namespace GreenshotPlugin.Interfaces
|
|||
/// <param name="container"></param>
|
||||
/// <returns>This returns false if the container is deleted but still in the undo stack</returns>
|
||||
bool IsOnSurface(IDrawableContainer container);
|
||||
void Invalidate(Rectangle rectangleToInvalidate);
|
||||
void Invalidate();
|
||||
/// <summary>
|
||||
/// Invalidates the specified region of the Surface.
|
||||
/// Takes care of the Surface zoom level, accepts rectangle in the coordinate space of the Image.
|
||||
/// </summary>
|
||||
/// <param name="rectangleToInvalidate">Bounding rectangle for updated elements, in the coordinate space of the Image.</param>
|
||||
void InvalidateElements(Rectangle rectangleToInvalidate);
|
||||
bool Modified
|
||||
{
|
||||
get;
|
||||
|
@ -186,9 +192,32 @@ namespace GreenshotPlugin.Interfaces
|
|||
get;
|
||||
set;
|
||||
}
|
||||
int Width { get; }
|
||||
int Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Zoom value applied to the surface.
|
||||
/// </summary>
|
||||
Fraction ZoomFactor { get; set; }
|
||||
/// <summary>
|
||||
/// Translate a point from image coorditate space to surface coordinate space.
|
||||
/// </summary>
|
||||
/// <param name="point">A point in the coordinate space of the image.</param>
|
||||
Point ToSurfaceCoordinates(Point point);
|
||||
/// <summary>
|
||||
/// Translate a rectangle from image coorditate space to surface coordinate space.
|
||||
/// </summary>
|
||||
/// <param name="rc">A rectangle in the coordinate space of the image.</param>
|
||||
Rectangle ToSurfaceCoordinates(Rectangle rc);
|
||||
/// <summary>
|
||||
/// Translate a point from surface coorditate space to image coordinate space.
|
||||
/// </summary>
|
||||
/// <param name="point">A point in the coordinate space of the surface.</param>
|
||||
Point ToImageCoordinates(Point point);
|
||||
/// <summary>
|
||||
/// Translate a rectangle from surface coorditate space to image coordinate space.
|
||||
/// </summary>
|
||||
/// <param name="rc">A rectangle in the coordinate space of the surface.</param>
|
||||
Rectangle ToImageCoordinates(Rectangle rc);
|
||||
|
||||
void MakeUndoable(IMemento memento, bool allowMerge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue