ShareX/ShareX.ScreenCaptureLib/Shapes/ShapeManager.cs

2338 lines
72 KiB
C#
Raw Normal View History

2013-11-03 23:53:49 +13:00
#region License Information (GPL v3)
/*
ShareX - A program that allows you to take screenshots and share any file type
2023-01-10 09:31:02 +13:00
Copyright (c) 2007-2023 ShareX Team
2013-11-03 23:53:49 +13:00
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Optionally you can also view the license at <http://www.gnu.org/licenses/>.
*/
#endregion License Information (GPL v3)
2014-12-11 09:25:20 +13:00
using ShareX.HelpersLib;
using ShareX.ImageEffectsLib;
using System;
2013-11-03 23:53:49 +13:00
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
2013-11-03 23:53:49 +13:00
using System.Linq;
using System.Threading;
2013-11-03 23:53:49 +13:00
using System.Windows.Forms;
2014-12-11 09:25:20 +13:00
namespace ShareX.ScreenCaptureLib
2013-11-03 23:53:49 +13:00
{
internal partial class ShapeManager : IDisposable
2013-11-03 23:53:49 +13:00
{
public List<BaseShape> Shapes { get; private set; } = new List<BaseShape>();
2013-11-03 23:53:49 +13:00
private BaseShape currentShape;
public BaseShape CurrentShape
{
get
{
return currentShape;
}
private set
{
currentShape = value;
if (currentShape != null)
{
currentShape.OnConfigSave();
}
OnCurrentShapeChanged(currentShape);
}
}
2017-11-07 05:01:02 +13:00
private ShapeType currentTool;
2017-11-07 05:01:02 +13:00
public ShapeType CurrentTool
{
get
{
2017-11-07 05:01:02 +13:00
return currentTool;
}
set
{
if (currentTool == value) return;
2017-11-07 05:01:02 +13:00
currentTool = value;
if (Form.IsAnnotationMode)
{
2016-06-12 23:37:03 +12:00
if (IsCurrentShapeTypeRegion)
{
2017-11-07 05:01:02 +13:00
Options.LastRegionTool = CurrentTool;
2016-06-12 23:37:03 +12:00
}
else if (Form.IsEditorMode)
{
2017-11-07 05:01:02 +13:00
Options.LastEditorTool = CurrentTool;
}
2016-06-12 23:37:03 +12:00
else
{
2017-11-07 05:01:02 +13:00
Options.LastAnnotationTool = CurrentTool;
2016-06-12 23:37:03 +12:00
}
ClearTools();
}
if (CurrentShape != null)
{
// do not keep selection if select tool does not handle it
if (currentTool == ShapeType.ToolSelect)
{
if (!CurrentShape.IsHandledBySelectTool)
{
DeselectCurrentShape();
}
}
// do not keep selection if we switch away from a tool and the selected shape does not match the new type
else if (CurrentShape.ShapeType != currentTool)
{
DeselectCurrentShape();
}
}
2017-11-07 05:01:02 +13:00
OnCurrentShapeTypeChanged(currentTool);
}
}
public ShapeType CurrentShapeTool
{
get
{
ShapeType tool = CurrentTool;
if (tool == ShapeType.ToolSelect)
{
BaseShape shape = CurrentShape;
if (shape != null)
{
tool = shape.ShapeType;
}
}
return tool;
}
}
public RectangleF CurrentRectangle
2013-11-03 23:53:49 +13:00
{
get
{
if (CurrentShape != null)
{
return CurrentShape.Rectangle;
}
return RectangleF.Empty;
}
}
public PointF CurrentDPI = new PointF(96f, 96f);
public bool IsCurrentShapeValid => CurrentShape != null && CurrentShape.IsValidShape;
public BaseShape[] Regions => Shapes.OfType<BaseRegionShape>().ToArray();
2013-11-03 23:53:49 +13:00
public BaseShape[] ValidRegions => Regions.Where(x => x.IsValidShape).ToArray();
2016-05-03 22:06:58 +12:00
public BaseDrawingShape[] DrawingShapes => Shapes.OfType<BaseDrawingShape>().ToArray();
2016-05-11 10:17:51 +12:00
public BaseEffectShape[] EffectShapes => Shapes.OfType<BaseEffectShape>().ToArray();
2013-11-03 23:53:49 +13:00
public BaseTool[] ToolShapes => Shapes.OfType<BaseTool>().ToArray();
2017-04-03 19:55:54 +12:00
private BaseShape currentHoverShape;
2013-11-03 23:53:49 +13:00
2017-04-03 19:55:54 +12:00
public BaseShape CurrentHoverShape
{
get
{
2017-04-03 19:55:54 +12:00
return currentHoverShape;
}
private set
{
2017-04-08 00:09:18 +12:00
if (currentHoverShape != null)
2017-04-03 19:55:54 +12:00
{
2017-04-08 00:09:18 +12:00
if (PreviousHoverRectangle == Rectangle.Empty || PreviousHoverRectangle != currentHoverShape.Rectangle)
{
PreviousHoverRectangle = currentHoverShape.Rectangle;
}
}
else
{
PreviousHoverRectangle = Rectangle.Empty;
2017-04-03 19:55:54 +12:00
}
currentHoverShape = value;
}
}
public RectangleF PreviousHoverRectangle { get; private set; }
2017-04-03 19:55:54 +12:00
public bool IsCurrentHoverShapeValid => CurrentHoverShape != null && CurrentHoverShape.IsValidShape;
2017-11-07 05:01:02 +13:00
public bool IsCurrentShapeTypeRegion => IsShapeTypeRegion(CurrentTool);
2023-12-27 20:06:42 +13:00
public int StartingStepNumber { get; set; } = 1;
2017-04-03 19:55:54 +12:00
2016-08-06 01:35:44 +12:00
public bool IsCreating { get; set; }
private bool isMoving;
public bool IsMoving
{
get
{
return isMoving;
}
set
{
if (isMoving != value)
{
isMoving = value;
if (isMoving)
{
Form.SetHandCursor(true);
}
else
{
Form.SetDefaultCursor();
}
}
}
}
private bool isPanning;
public bool IsPanning
{
get
{
return isPanning;
}
set
{
if (isPanning != value)
{
isPanning = value;
if (isPanning)
{
Form.SetHandCursor(true);
}
else
{
Form.SetDefaultCursor();
}
}
}
}
public bool IsResizing { get; set; }
// Is holding Ctrl?
public bool IsCtrlModifier { get; private set; }
public bool IsCornerMoving { get; private set; }
// Is holding Shift?
public bool IsProportionalResizing { get; private set; }
// Is holding Alt?
public bool IsSnapResizing { get; private set; }
public bool IsRenderingOutput { get; private set; }
public PointF RenderOffset { get; private set; }
2021-10-23 11:44:29 +13:00
public bool IsImageModified { get; internal set; }
2017-10-24 10:40:37 +13:00
public InputManager InputManager { get; private set; } = new InputManager();
public List<SimpleWindowInfo> Windows { get; set; }
2013-11-03 23:53:49 +13:00
public bool WindowCaptureMode { get; set; }
2014-07-10 09:44:08 +12:00
public bool IncludeControls { get; set; }
2013-11-03 23:53:49 +13:00
2017-11-07 05:01:02 +13:00
public RegionCaptureOptions Options { get; private set; }
2017-11-07 05:01:02 +13:00
public AnnotationOptions AnnotationOptions => Options.AnnotationOptions;
2016-05-21 05:34:45 +12:00
2018-04-08 21:12:52 +12:00
internal List<ImageEditorControl> DrawableObjects { get; private set; }
internal ResizeNode[] ResizeNodes { get; private set; }
private bool nodesVisible;
public bool NodesVisible
{
get
{
return nodesVisible;
}
set
{
nodesVisible = value;
if (!nodesVisible)
{
2016-08-17 20:35:06 +12:00
foreach (ResizeNode node in ResizeNodes)
{
node.Visible = false;
}
}
else
{
BaseShape shape = CurrentShape;
if (shape != null)
{
2016-08-06 01:35:44 +12:00
shape.OnNodePositionUpdate();
shape.OnNodeVisible();
}
}
}
}
public bool IsCursorOnObject => DrawableObjects.Any(x => x.HandleMouseInput && x.IsCursorHover);
public event Action<BaseShape> CurrentShapeChanged;
public event Action<ShapeType> CurrentShapeTypeChanged;
2016-11-19 13:03:44 +13:00
public event Action<BaseShape> ShapeCreated;
2021-10-23 11:44:29 +13:00
public event Action ImageModified;
internal RegionCaptureForm Form { get; private set; }
2023-12-27 17:57:06 +13:00
private readonly ImageEditorHistory history;
private bool isLeftPressed, isRightPressed, isUpPressed, isDownPressed;
2018-04-04 08:46:38 +12:00
private ScrollbarManager scrollbarManager;
2013-11-03 23:53:49 +13:00
2016-09-05 04:22:27 +12:00
public ShapeManager(RegionCaptureForm form)
2013-11-03 23:53:49 +13:00
{
Form = form;
2017-11-07 05:01:02 +13:00
Options = form.Options;
2013-11-03 23:53:49 +13:00
2018-04-08 21:12:52 +12:00
DrawableObjects = new List<ImageEditorControl>();
2017-11-16 22:21:05 +13:00
ResizeNodes = new ResizeNode[9];
2017-11-16 22:21:05 +13:00
for (int i = 0; i < ResizeNodes.Length; i++)
{
2016-08-17 20:35:06 +12:00
ResizeNode node = new ResizeNode();
2017-10-30 21:20:03 +13:00
node.SetCustomNode(form.CustomNodeImage);
DrawableObjects.Add(node);
2017-11-16 22:21:05 +13:00
ResizeNodes[i] = node;
}
2016-08-17 20:35:06 +12:00
ResizeNodes[(int)NodePosition.BottomRight].Order = 10;
2013-11-03 23:53:49 +13:00
2016-10-01 03:48:31 +13:00
form.Shown += form_Shown;
form.LostFocus += form_LostFocus;
form.MouseDown += form_MouseDown;
form.MouseUp += form_MouseUp;
form.MouseDoubleClick += form_MouseDoubleClick;
form.MouseWheel += form_MouseWheel;
form.KeyDown += form_KeyDown;
form.KeyUp += form_KeyUp;
CurrentShape = null;
if (form.Mode == RegionCaptureMode.Annotation)
{
2017-11-07 05:01:02 +13:00
CurrentTool = Options.LastRegionTool;
}
else if (form.IsEditorMode)
{
2017-11-07 05:01:02 +13:00
CurrentTool = Options.LastEditorTool;
}
else
{
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.RegionRectangle;
}
2018-04-04 08:46:38 +12:00
if (form.IsEditorMode)
{
scrollbarManager = new ScrollbarManager(form, this);
}
foreach (ImageEditorControl control in DrawableObjects)
{
control.MouseDown += (sender, e) => Form.SetHandCursor(true);
control.MouseUp += (sender, e) =>
{
if (control.IsCursorHover)
{
Form.SetHandCursor(false);
}
else
{
Form.SetDefaultCursor();
}
};
control.MouseEnter += () => Form.SetHandCursor(false);
control.MouseLeave += () => Form.SetDefaultCursor();
}
history = new ImageEditorHistory(this);
}
2021-10-23 11:44:29 +13:00
private void OnCurrentShapeChanged(BaseShape shape)
{
CurrentShapeChanged?.Invoke(shape);
}
private void OnCurrentShapeTypeChanged(ShapeType shapeType)
{
CurrentShapeTypeChanged?.Invoke(shapeType);
}
private void OnShapeCreated(BaseShape shape)
{
ShapeCreated?.Invoke(shape);
}
internal void OnImageModified()
2021-10-23 11:44:29 +13:00
{
OrderStepShapes();
2021-10-23 11:44:29 +13:00
IsImageModified = true;
ImageModified?.Invoke();
}
2016-10-01 03:48:31 +13:00
private void form_Shown(object sender, EventArgs e)
{
if (Form.IsAnnotationMode)
{
2016-11-01 00:30:05 +13:00
CreateToolbar();
}
2016-10-01 03:48:31 +13:00
}
private void form_LostFocus(object sender, EventArgs e)
{
ResetModifiers();
}
private void form_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (!IsCreating)
{
StartRegionSelection();
}
}
2017-10-17 12:53:54 +13:00
else if (e.Button == MouseButtons.Middle)
{
if (Form.IsEditorMode)
2017-10-17 12:53:54 +13:00
{
StartPanning();
}
}
}
private void form_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (IsMoving || IsCreating)
{
EndRegionSelection();
}
}
else if (e.Button == MouseButtons.Right)
{
if (IsCreating)
{
DeleteCurrentShape();
EndRegionSelection();
}
else if (Form.IsAnnotationMode)
{
2017-11-07 05:01:02 +13:00
RunAction(Options.RegionCaptureActionRightClick);
}
else if (IsShapeIntersect())
{
DeleteIntersectShape();
}
else
{
Form.CloseWindow();
}
}
2016-05-20 03:32:25 +12:00
else if (e.Button == MouseButtons.Middle)
{
if (Form.IsEditorMode)
2017-10-17 12:53:54 +13:00
{
EndPanning();
}
else
{
2017-11-07 05:01:02 +13:00
RunAction(Options.RegionCaptureActionMiddleClick);
2017-10-17 12:53:54 +13:00
}
2016-05-20 03:32:25 +12:00
}
2016-06-12 23:37:03 +12:00
else if (e.Button == MouseButtons.XButton1)
{
2017-11-07 05:01:02 +13:00
RunAction(Options.RegionCaptureActionX1Click);
}
else if (e.Button == MouseButtons.XButton2)
{
2017-11-07 05:01:02 +13:00
RunAction(Options.RegionCaptureActionX2Click);
2016-06-12 23:37:03 +12:00
}
2013-11-03 23:53:49 +13:00
}
private void form_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (IsCurrentShapeTypeRegion && ValidRegions.Length > 0)
{
Form.UpdateRegionPath();
Form.CloseWindow(RegionResult.Region);
}
2016-06-11 01:54:54 +12:00
else if (CurrentShape != null && !IsCreating)
{
CurrentShape.OnDoubleClicked();
}
}
}
private void form_MouseWheel(object sender, MouseEventArgs e)
{
2022-02-15 21:01:29 +13:00
if (Control.ModifierKeys == Keys.None)
{
2022-02-15 21:01:29 +13:00
if (e.Delta > 0)
{
2022-02-15 21:01:29 +13:00
if (Options.ShowMagnifier)
{
Options.MagnifierPixelCount = Math.Min(Options.MagnifierPixelCount + 2, RegionCaptureOptions.MagnifierPixelCountMaximum);
}
else
{
Options.ShowMagnifier = true;
}
}
2022-02-15 21:01:29 +13:00
else if (e.Delta < 0)
{
2022-02-15 21:01:29 +13:00
int magnifierPixelCount = Options.MagnifierPixelCount - 2;
if (magnifierPixelCount < RegionCaptureOptions.MagnifierPixelCountMinimum)
{
magnifierPixelCount = RegionCaptureOptions.MagnifierPixelCountMinimum;
Options.ShowMagnifier = false;
}
Options.MagnifierPixelCount = magnifierPixelCount;
}
2022-02-15 21:01:29 +13:00
if (Form.IsAnnotationMode)
{
2022-02-15 21:01:29 +13:00
tsmiShowMagnifier.Checked = Options.ShowMagnifier;
tslnudMagnifierPixelCount.Content.Value = Options.MagnifierPixelCount;
}
}
}
2019-04-05 05:32:58 +13:00
public bool HandleEscape()
{
2019-04-05 06:33:35 +13:00
// the escape key handling has 3 stages:
// 1. initiate exit if region selection is active
// 2. if a shape is selected, unselect it
// 3. switch to the select tool if a any other tool is active
switch (CurrentTool)
{
case ShapeType.RegionRectangle:
case ShapeType.RegionEllipse:
case ShapeType.RegionFreehand:
return false;
}
if (CurrentShape != null)
{
ClearTools();
DeselectCurrentShape();
}
2019-04-05 05:32:58 +13:00
if (CurrentTool != ShapeType.ToolSelect)
{
CurrentTool = ShapeType.ToolSelect;
return true;
}
2019-04-05 06:33:35 +13:00
2019-04-05 05:32:58 +13:00
return false;
}
private void form_KeyDown(object sender, KeyEventArgs e)
2013-11-03 23:53:49 +13:00
{
2015-08-05 00:40:25 +12:00
switch (e.KeyCode)
2013-11-03 23:53:49 +13:00
{
case Keys.ControlKey:
if (!IsCtrlModifier && !IsCornerMoving)
{
if (IsCreating || IsResizing)
{
IsCornerMoving = true;
}
else
{
IsCtrlModifier = true;
}
}
break;
case Keys.ShiftKey:
IsProportionalResizing = true;
break;
case Keys.Menu:
IsSnapResizing = true;
break;
case Keys.Left:
isLeftPressed = true;
break;
case Keys.Right:
isRightPressed = true;
break;
case Keys.Up:
isUpPressed = true;
break;
case Keys.Down:
isDownPressed = true;
break;
}
2016-08-16 08:48:27 +12:00
switch (e.KeyData)
{
case Keys.Insert:
if (IsCreating)
{
EndRegionSelection();
}
else
{
StartRegionSelection();
}
break;
case Keys.Delete:
DeleteCurrentShape();
if (IsCreating)
{
EndRegionSelection();
}
break;
2016-12-15 05:38:07 +13:00
case Keys.Shift | Keys.Delete:
2023-12-27 21:42:38 +13:00
DeleteAllShapes(true);
break;
2016-08-16 08:48:27 +12:00
}
if (!IsCreating)
{
if (Form.Mode == RegionCaptureMode.Annotation)
{
switch (e.KeyData)
{
case Keys.Tab:
SwapShapeType();
break;
case Keys.NumPad0:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.RegionRectangle;
break;
}
}
if (Form.IsAnnotationMode)
{
switch (e.KeyData)
{
2022-08-20 05:10:13 +12:00
case Keys.M:
CurrentTool = ShapeType.ToolSelect;
break;
case Keys.R:
case Keys.NumPad1:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.DrawingRectangle;
break;
case Keys.E:
case Keys.NumPad2:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.DrawingEllipse;
break;
case Keys.F:
case Keys.NumPad3:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.DrawingFreehand;
break;
case Keys.L:
case Keys.NumPad4:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.DrawingLine;
break;
case Keys.A:
case Keys.NumPad5:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.DrawingArrow;
break;
2018-03-26 08:23:17 +13:00
case Keys.O:
case Keys.NumPad6:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.DrawingTextOutline;
break;
case Keys.T:
CurrentTool = ShapeType.DrawingTextBackground;
break;
case Keys.S:
CurrentTool = ShapeType.DrawingSpeechBalloon;
break;
2018-03-26 08:23:17 +13:00
case Keys.I:
case Keys.NumPad7:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.DrawingStep;
break;
2018-03-26 08:23:17 +13:00
case Keys.B:
case Keys.NumPad8:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.EffectBlur;
break;
2018-03-26 08:23:17 +13:00
case Keys.P:
case Keys.NumPad9:
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.EffectPixelate;
break;
case Keys.H:
CurrentTool = ShapeType.EffectHighlight;
break;
case Keys.Control | Keys.D:
2020-09-16 06:44:39 +12:00
DuplicateCurrrentShape(true);
break;
case Keys.Control | Keys.V:
PasteFromClipboard(true);
break;
case Keys.Control | Keys.Z:
history.Undo();
break;
case Keys.Control | Keys.Y:
history.Redo();
break;
case Keys.Home:
MoveCurrentShapeTop();
break;
case Keys.End:
MoveCurrentShapeBottom();
break;
case Keys.PageUp:
MoveCurrentShapeUp();
break;
case Keys.PageDown:
MoveCurrentShapeDown();
break;
}
}
if (Form.IsEditorMode)
{
switch (e.KeyData)
{
case Keys.C:
CurrentTool = ShapeType.ToolCrop;
break;
2022-08-19 05:41:10 +12:00
case Keys.X:
CurrentTool = ShapeType.ToolCutOut;
break;
2018-03-27 02:18:35 +13:00
case Keys.Control | Keys.S:
Form.OnSaveImageRequested();
break;
case Keys.Control | Keys.Shift | Keys.S:
Form.OnSaveImageAsRequested();
break;
case Keys.Control | Keys.Shift | Keys.C:
Form.OnCopyImageRequested();
break;
case Keys.Control | Keys.U:
Form.OnUploadImageRequested();
break;
case Keys.Control | Keys.P:
Form.OnPrintImageRequested();
break;
}
}
}
int speed;
if (e.Shift)
{
speed = RegionCaptureOptions.MoveSpeedMaximum;
}
else
{
speed = RegionCaptureOptions.MoveSpeedMinimum;
}
int x = 0;
if (isLeftPressed)
{
x -= speed;
}
if (isRightPressed)
{
x += speed;
}
int y = 0;
if (isUpPressed)
{
y -= speed;
}
if (isDownPressed)
{
y += speed;
}
if (x != 0 || y != 0)
{
BaseShape shape = CurrentShape;
if (shape == null || IsCreating)
{
Cursor.Position = Cursor.Position.Add(x, y);
}
else if (e.Control)
{
shape.OnResizing();
shape.Resize(x, y, true);
}
else if (e.Alt)
{
shape.OnResizing();
shape.Resize(x, y, false);
}
else
{
shape.OnMoving();
shape.Move(x, y);
}
}
}
private void form_KeyUp(object sender, KeyEventArgs e)
2013-11-03 23:53:49 +13:00
{
bool wasMoving = isLeftPressed || isRightPressed || isUpPressed || isDownPressed;
switch (e.KeyCode)
2013-11-03 23:53:49 +13:00
{
case Keys.ControlKey:
IsCtrlModifier = false;
IsCornerMoving = false;
break;
2016-06-13 04:32:33 +12:00
case Keys.ShiftKey:
IsProportionalResizing = false;
break;
case Keys.Menu:
IsSnapResizing = false;
break;
case Keys.Left:
isLeftPressed = false;
break;
case Keys.Right:
isRightPressed = false;
break;
case Keys.Up:
isUpPressed = false;
break;
case Keys.Down:
isDownPressed = false;
break;
2013-11-03 23:53:49 +13:00
}
if (!IsCreating && !IsMoving && wasMoving)
{
bool isMoving = isLeftPressed || isRightPressed || isUpPressed || isDownPressed;
if (!isMoving)
{
ShapeMoved();
}
}
}
private void ShapeMoved()
{
if (!IsCreating)
{
BaseShape shape = CurrentShape;
if (shape != null)
{
shape.OnMoved();
}
}
}
private void RunAction(RegionCaptureAction action)
{
switch (action)
{
case RegionCaptureAction.CancelCapture:
if (Form.IsEditorMode)
{
if (Form.ShowExitConfirmation())
{
Form.CloseWindow(RegionResult.AnnotateContinueTask);
}
}
else
{
Form.CloseWindow();
}
break;
case RegionCaptureAction.RemoveShapeCancelCapture:
if (IsShapeIntersect())
{
DeleteIntersectShape();
}
else if (Form.IsEditorMode)
{
if (Form.ShowExitConfirmation())
{
Form.CloseWindow(RegionResult.AnnotateContinueTask);
}
}
else
{
Form.CloseWindow();
}
break;
case RegionCaptureAction.RemoveShape:
DeleteIntersectShape();
break;
case RegionCaptureAction.SwapToolType:
SwapShapeType();
break;
case RegionCaptureAction.CaptureFullscreen:
Form.CloseWindow(RegionResult.Fullscreen);
break;
case RegionCaptureAction.CaptureActiveMonitor:
Form.CloseWindow(RegionResult.ActiveMonitor);
break;
case RegionCaptureAction.CaptureLastRegion:
if (RegionCaptureForm.LastRegionFillPath != null)
{
Form.CloseWindow(RegionResult.LastRegion);
}
break;
}
2013-11-03 23:53:49 +13:00
}
public void Update()
{
BaseShape shape = CurrentShape;
if (shape != null)
2013-11-03 23:53:49 +13:00
{
shape.OnUpdate();
2013-11-03 23:53:49 +13:00
}
2017-04-08 00:09:18 +12:00
UpdateCurrentHoverShape();
2013-11-03 23:53:49 +13:00
UpdateNodes();
2018-04-04 08:46:38 +12:00
if (scrollbarManager != null)
{
scrollbarManager.Update();
}
2013-11-03 23:53:49 +13:00
}
public void StartRegionSelection()
{
if (IsCursorOnObject)
{
return;
}
InputManager.Update(Form); // If it's a touch event we don't have the correct point yet, so refresh it now
BaseShape shape = GetIntersectShape();
if (shape != null && shape.IsSelectable) // Select shape
{
DeselectCurrentShape();
2023-12-27 20:06:42 +13:00
2023-12-27 17:57:06 +13:00
if (!IsMoving)
{
history.CreateShapesMemento();
}
2023-12-27 20:06:42 +13:00
IsMoving = true;
shape.OnMoving();
CurrentShape = shape;
SelectCurrentShape();
}
else if (shape == null && CurrentTool == ShapeType.ToolSelect)
{
ClearTools();
DeselectCurrentShape();
}
2019-04-05 05:32:58 +13:00
else if (!IsCreating && CurrentTool != ShapeType.ToolSelect) // Create new shape
2013-11-03 23:53:49 +13:00
{
ClearTools();
2016-08-16 08:48:27 +12:00
DeselectCurrentShape();
2023-12-27 20:06:42 +13:00
shape = AddShape();
2016-08-24 01:03:48 +12:00
shape.OnCreating();
2013-11-03 23:53:49 +13:00
}
}
public void EndRegionSelection()
2013-11-03 23:53:49 +13:00
{
bool wasCreating = IsCreating;
bool wasMoving = IsMoving;
IsCreating = false;
IsMoving = false;
2013-11-03 23:53:49 +13:00
BaseShape shape = CurrentShape;
if (shape != null)
{
if (!shape.IsValidShape)
2013-11-03 23:53:49 +13:00
{
shape.Rectangle = Rectangle.Empty;
2017-04-08 00:09:18 +12:00
UpdateCurrentHoverShape();
if (IsCurrentHoverShapeValid)
{
shape.Rectangle = CurrentHoverShape.Rectangle;
}
else
{
DeleteCurrentShape();
shape = null;
}
2013-11-03 23:53:49 +13:00
}
if (shape != null)
2013-11-03 23:53:49 +13:00
{
2017-11-07 05:01:02 +13:00
if (Options.QuickCrop && IsCurrentShapeTypeRegion)
{
Form.UpdateRegionPath();
Form.CloseWindow(RegionResult.Region);
}
else
{
if (wasCreating)
{
shape.OnCreated();
2016-11-19 13:03:44 +13:00
OnShapeCreated(shape);
SelectCurrentShape();
if (Options.SwitchToSelectionToolAfterDrawing && shape.IsHandledBySelectTool)
{
CurrentTool = ShapeType.ToolSelect;
}
}
else if (wasMoving)
{
shape.OnMoved();
SelectCurrentShape();
}
}
2013-11-03 23:53:49 +13:00
}
}
}
2017-10-17 12:53:54 +13:00
private void StartPanning()
{
IsPanning = true;
Options.ShowEditorPanTip = false;
2017-10-17 12:53:54 +13:00
}
private void EndPanning()
{
IsPanning = false;
}
2017-10-18 03:44:46 +13:00
internal void UpdateObjects(ImageEditorControl[] objects, PointF mousePosition)
{
if (objects.All(x => !x.IsDragging))
{
for (int i = 0; i < objects.Length; i++)
{
2018-04-08 21:12:52 +12:00
ImageEditorControl obj = objects[i];
if (!IsCtrlModifier && obj.Visible)
{
obj.IsCursorHover = obj.Rectangle.Contains(mousePosition);
if (obj.IsCursorHover)
{
if (InputManager.IsMousePressed(MouseButtons.Left))
{
obj.OnMouseDown(mousePosition.Round());
}
for (int j = i + 1; j < objects.Length; j++)
{
objects[j].IsCursorHover = false;
}
break;
}
}
2017-12-28 09:07:54 +13:00
else
{
obj.IsCursorHover = false;
}
}
}
else
{
if (InputManager.IsMouseReleased(MouseButtons.Left))
{
2018-04-08 21:12:52 +12:00
foreach (ImageEditorControl obj in objects)
{
if (obj.IsDragging)
{
obj.OnMouseUp(mousePosition.Round());
}
}
}
}
}
internal void UpdateObjects()
{
ImageEditorControl[] scrollbars = DrawableObjects.Where(x => x is ImageEditorScrollbar).ToArray();
ImageEditorControl[] shapes = DrawableObjects.Except(scrollbars).OrderByDescending(x => x.Order).ToArray();
UpdateObjects(shapes, Form.ScaledClientMousePosition);
UpdateObjects(scrollbars, InputManager.ClientMousePosition);
}
internal void DrawObjects(Graphics g)
{
if (!IsCtrlModifier)
{
foreach (ImageEditorControl obj in DrawableObjects)
{
if (obj.Visible)
{
obj.OnDraw(g);
}
}
}
}
private BaseShape AddShape()
{
BaseShape shape = CreateShape();
AddShape(shape);
return shape;
}
private void AddShape(BaseShape shape)
{
2023-12-27 21:42:38 +13:00
history.CreateShapesMemento();
Shapes.Add(shape);
CurrentShape = shape;
}
private BaseShape CreateShape()
{
2017-11-07 05:01:02 +13:00
return CreateShape(CurrentTool);
}
private BaseShape CreateShape(ShapeType shapeType)
{
BaseShape shape;
2015-07-17 07:51:19 +12:00
switch (shapeType)
2015-07-17 07:51:19 +12:00
{
default:
case ShapeType.RegionRectangle:
shape = new RectangleRegionShape();
break;
case ShapeType.RegionEllipse:
shape = new EllipseRegionShape();
break;
case ShapeType.RegionFreehand:
shape = new FreehandRegionShape();
break;
case ShapeType.DrawingRectangle:
2016-05-06 05:03:57 +12:00
shape = new RectangleDrawingShape();
break;
2016-05-06 05:42:56 +12:00
case ShapeType.DrawingEllipse:
shape = new EllipseDrawingShape();
break;
2016-08-15 11:11:21 +12:00
case ShapeType.DrawingFreehand:
shape = new FreehandDrawingShape();
break;
2022-10-01 20:07:12 +13:00
case ShapeType.DrawingFreehandArrow:
shape = new FreehandArrowDrawingShape();
break;
2016-05-06 05:03:57 +12:00
case ShapeType.DrawingLine:
shape = new LineDrawingShape();
break;
2016-05-07 02:38:49 +12:00
case ShapeType.DrawingArrow:
shape = new ArrowDrawingShape();
break;
2017-02-16 13:49:13 +13:00
case ShapeType.DrawingTextOutline:
shape = new TextOutlineDrawingShape();
break;
case ShapeType.DrawingTextBackground:
shape = new TextDrawingShape();
break;
case ShapeType.DrawingSpeechBalloon:
shape = new SpeechBalloonDrawingShape();
break;
2016-05-23 07:19:25 +12:00
case ShapeType.DrawingStep:
shape = new StepDrawingShape();
break;
case ShapeType.DrawingMagnify:
shape = new MagnifyDrawingShape();
break;
case ShapeType.DrawingImage:
shape = new ImageFileDrawingShape();
break;
case ShapeType.DrawingImageScreen:
shape = new ImageScreenDrawingShape();
break;
2018-02-06 21:25:15 +13:00
case ShapeType.DrawingSticker:
shape = new StickerDrawingShape();
break;
case ShapeType.DrawingCursor:
shape = new CursorDrawingShape();
break;
2020-03-12 01:19:04 +13:00
case ShapeType.DrawingSmartEraser:
shape = new SmartEraserDrawingShape();
break;
2016-09-02 18:16:52 +12:00
case ShapeType.EffectBlur:
2016-05-11 10:17:51 +12:00
shape = new BlurEffectShape();
break;
2016-09-02 18:16:52 +12:00
case ShapeType.EffectPixelate:
2016-05-11 10:17:51 +12:00
shape = new PixelateEffectShape();
2016-05-07 05:04:24 +12:00
break;
2016-09-02 18:16:52 +12:00
case ShapeType.EffectHighlight:
2016-05-11 10:17:51 +12:00
shape = new HighlightEffectShape();
2016-05-07 12:11:31 +12:00
break;
case ShapeType.ToolCrop:
shape = new CropTool();
break;
2022-08-16 22:11:13 +12:00
case ShapeType.ToolCutOut:
shape = new CutOutTool();
2022-08-16 10:22:54 +12:00
break;
2015-07-17 07:51:19 +12:00
}
shape.Manager = this;
shape.OnConfigLoad();
2016-05-06 05:03:57 +12:00
return shape;
}
private void UpdateCurrentShape()
{
BaseShape shape = CurrentShape;
if (shape != null)
{
shape.OnConfigLoad();
2017-11-15 12:37:17 +13:00
shape.OnMoved();
}
2018-05-01 01:41:39 +12:00
Form.Resume();
}
2016-06-12 23:37:03 +12:00
private void SwapShapeType()
{
if (Form.Mode == RegionCaptureMode.Annotation)
2016-06-12 23:37:03 +12:00
{
if (IsCurrentShapeTypeRegion)
{
2017-11-07 05:01:02 +13:00
CurrentTool = Options.LastAnnotationTool;
}
else
{
2017-11-07 05:01:02 +13:00
CurrentTool = Options.LastRegionTool;
}
2016-06-12 23:37:03 +12:00
}
}
public PointF SnapPosition(PointF posOnClick, PointF posCurrent)
{
SizeF currentSize = CaptureHelpers.CreateRectangle(posOnClick, posCurrent).Size;
2016-06-25 19:44:39 +12:00
Vector2 vector = new Vector2(currentSize.Width, currentSize.Height);
2017-11-07 05:01:02 +13:00
SnapSize snapSize = (from size in Options.SnapSizes
2016-06-25 19:44:39 +12:00
let distance = MathHelpers.Distance(vector, new Vector2(size.Width, size.Height))
where distance > 0 && distance < RegionCaptureOptions.SnapDistance
2016-06-25 19:44:39 +12:00
orderby distance
select size).FirstOrDefault();
if (snapSize != null)
{
PointF posNew = CaptureHelpers.CalculateNewPosition(posOnClick, posCurrent, snapSize);
RectangleF newRect = CaptureHelpers.CreateRectangle(posOnClick, posNew);
if (Form.ClientArea.Contains(newRect.Round()))
2016-06-25 19:44:39 +12:00
{
return posNew;
}
}
return posCurrent;
}
2017-04-08 00:09:18 +12:00
private void UpdateCurrentHoverShape()
{
2017-04-08 00:09:18 +12:00
CurrentHoverShape = CheckHover();
}
2017-04-08 00:09:18 +12:00
private BaseShape CheckHover()
{
if (!IsCursorOnObject && !IsCreating && !IsMoving && !IsResizing)
{
BaseShape shape = GetIntersectShape();
if (shape != null && shape.IsValidShape)
{
2017-04-08 00:09:18 +12:00
return shape;
}
else
{
2017-11-07 05:01:02 +13:00
switch (CurrentTool)
{
2016-08-03 21:31:15 +12:00
case ShapeType.RegionFreehand:
2016-08-15 11:11:21 +12:00
case ShapeType.DrawingFreehand:
2022-10-01 20:07:12 +13:00
case ShapeType.DrawingFreehandArrow:
case ShapeType.DrawingLine:
case ShapeType.DrawingArrow:
2017-02-16 13:49:13 +13:00
case ShapeType.DrawingTextOutline:
case ShapeType.DrawingTextBackground:
2016-08-25 11:01:07 +12:00
case ShapeType.DrawingSpeechBalloon:
case ShapeType.DrawingStep:
case ShapeType.DrawingImage:
2018-02-22 12:44:25 +13:00
case ShapeType.DrawingSticker:
case ShapeType.DrawingCursor:
case ShapeType.ToolSelect:
2017-04-08 00:09:18 +12:00
return null;
}
2017-11-07 05:01:02 +13:00
if (Options.IsFixedSize && IsCurrentShapeTypeRegion)
{
PointF location = Form.ScaledClientMousePosition;
2019-07-22 12:17:18 +12:00
BaseShape rectangleRegionShape = CreateShape(ShapeType.RegionRectangle);
rectangleRegionShape.Rectangle = new RectangleF(new PointF(location.X - (Options.FixedSize.Width / 2),
2019-07-22 12:17:18 +12:00
location.Y - (Options.FixedSize.Height / 2)), Options.FixedSize);
return rectangleRegionShape;
}
else
{
SimpleWindowInfo window = FindSelectedWindow();
if (window != null && !window.Rectangle.IsEmpty)
{
2022-02-24 20:24:16 +13:00
Rectangle hoverArea = Form.RectangleToClient(window.Rectangle);
2019-07-22 12:17:18 +12:00
BaseShape rectangleRegionShape = CreateShape(ShapeType.RegionRectangle);
rectangleRegionShape.Rectangle = Rectangle.Intersect(Form.ClientArea, hoverArea);
return rectangleRegionShape;
}
}
}
}
2017-04-08 00:09:18 +12:00
return null;
}
public SimpleWindowInfo FindSelectedWindow()
{
if (Windows != null)
{
return Windows.FirstOrDefault(x => x.Rectangle.Contains(InputManager.MousePosition));
}
return null;
}
2016-05-29 22:10:48 +12:00
public WindowInfo FindSelectedWindowInfo(Point position)
{
if (Windows != null)
{
2016-05-29 22:10:48 +12:00
SimpleWindowInfo windowInfo = Windows.FirstOrDefault(x => x.IsWindow && x.Rectangle.Contains(position));
if (windowInfo != null)
{
return windowInfo.WindowInfo;
}
}
return null;
}
2020-03-21 14:41:34 +13:00
public Bitmap RenderOutputImage(Bitmap bmp)
{
2020-03-21 14:41:34 +13:00
return RenderOutputImage(bmp, Point.Empty);
}
public Bitmap RenderOutputImage(Bitmap bmp, PointF offset)
{
2020-03-21 14:41:34 +13:00
Bitmap bmpOutput = (Bitmap)bmp.Clone();
bmpOutput.SetResolution(CurrentDPI.X, CurrentDPI.Y);
if (DrawingShapes.Length > 0 || EffectShapes.Length > 0)
{
IsRenderingOutput = true;
RenderOffset = offset;
MoveAll(-offset.X, -offset.Y);
2020-03-21 14:41:34 +13:00
using (Graphics g = Graphics.FromImage(bmpOutput))
{
foreach (BaseEffectShape shape in EffectShapes)
{
if (shape != null)
{
2020-03-21 14:41:34 +13:00
shape.OnDrawFinal(g, bmpOutput);
}
}
foreach (BaseDrawingShape shape in DrawingShapes)
{
if (shape != null)
{
shape.OnDraw(g);
}
}
}
MoveAll(offset);
RenderOffset = Point.Empty;
IsRenderingOutput = false;
}
2020-03-21 14:41:34 +13:00
return bmpOutput;
}
private void SelectShape(BaseShape shape)
2013-11-03 23:53:49 +13:00
{
if (shape != null)
2013-11-03 23:53:49 +13:00
{
shape.ShowNodes();
if (Options.SwitchToDrawingToolAfterSelection)
{
CurrentTool = shape.ShapeType;
}
2013-11-03 23:53:49 +13:00
}
}
private void SelectCurrentShape()
{
SelectShape(CurrentShape);
}
private void SelectIntersectShape()
{
BaseShape shape = GetIntersectShape();
if (shape != null)
{
CurrentShape = shape;
SelectShape(shape);
}
}
2016-08-16 08:48:27 +12:00
private void DeselectShape(BaseShape shape)
2013-11-03 23:53:49 +13:00
{
2016-08-16 08:48:27 +12:00
if (shape == CurrentShape)
{
CurrentShape = null;
NodesVisible = false;
}
2013-11-03 23:53:49 +13:00
}
2016-08-16 08:48:27 +12:00
private void DeselectCurrentShape()
2016-05-05 02:01:03 +12:00
{
2016-08-16 08:48:27 +12:00
DeselectShape(CurrentShape);
}
2016-05-05 02:01:03 +12:00
public void DeleteShape(BaseShape shape)
2016-08-16 08:48:27 +12:00
{
2016-05-05 02:01:03 +12:00
if (shape != null)
{
2023-12-27 21:42:38 +13:00
history.CreateShapesMemento();
shape.Dispose();
2016-05-05 02:01:03 +12:00
Shapes.Remove(shape);
2016-08-16 08:48:27 +12:00
DeselectShape(shape);
if (shape.ShapeCategory == ShapeCategory.Drawing || shape.ShapeCategory == ShapeCategory.Effect)
{
2021-10-23 11:44:29 +13:00
OnImageModified();
}
2016-10-03 02:47:26 +13:00
UpdateMenu();
2016-05-05 02:01:03 +12:00
}
}
2016-08-16 08:48:27 +12:00
private void DeleteCurrentShape()
2013-11-03 23:53:49 +13:00
{
2016-08-16 08:48:27 +12:00
DeleteShape(CurrentShape);
}
2016-08-16 08:48:27 +12:00
private void DeleteIntersectShape()
{
DeleteShape(GetIntersectShape());
2013-11-03 23:53:49 +13:00
}
2023-12-27 21:42:38 +13:00
private void DeleteAllShapes(bool takeSnapshot = false)
{
if (Shapes.Count > 0)
{
2023-12-27 21:42:38 +13:00
if (takeSnapshot)
{
history.CreateShapesMemento();
}
foreach (BaseShape shape in Shapes)
{
shape.Dispose();
}
Shapes.Clear();
DeselectCurrentShape();
2021-10-23 11:44:29 +13:00
OnImageModified();
}
}
private void ResetModifiers()
{
IsCtrlModifier = IsCornerMoving = IsProportionalResizing = IsSnapResizing = false;
}
private void ClearTools()
{
foreach (BaseTool tool in ToolShapes)
{
tool.Dispose();
Shapes.Remove(tool);
}
}
public BaseShape GetIntersectShape()
{
return GetIntersectShape(Form.ScaledClientMousePosition);
}
public BaseShape GetIntersectShape(PointF position)
2013-11-03 23:53:49 +13:00
{
if (!IsCtrlModifier)
2013-11-03 23:53:49 +13:00
{
for (int i = Shapes.Count - 1; i >= 0; i--)
2013-11-03 23:53:49 +13:00
{
BaseShape shape = Shapes[i];
if (shape.IsSelectable && shape.Intersects(position))
{
return shape;
}
2013-11-03 23:53:49 +13:00
}
}
return null;
2013-11-03 23:53:49 +13:00
}
public bool IsShapeIntersect()
2013-11-03 23:53:49 +13:00
{
return GetIntersectShape() != null;
2013-11-03 23:53:49 +13:00
}
2023-12-27 18:26:53 +13:00
public void RestoreState(ImageEditorMemento memento)
{
2023-12-27 18:26:53 +13:00
if (memento != null)
{
if (memento.Canvas != null)
{
UpdateCanvas(memento.Canvas);
}
2023-12-27 17:57:06 +13:00
2023-12-27 18:26:53 +13:00
Shapes = memento.Shapes;
2023-12-27 18:56:07 +13:00
foreach (BaseEffectShape effect in EffectShapes)
{
effect.OnMoved();
}
2023-12-27 20:06:42 +13:00
ClearTools();
DeselectCurrentShape();
2023-12-27 18:56:07 +13:00
OnImageModified();
2023-12-27 18:26:53 +13:00
UpdateMenu();
}
2016-08-16 08:48:27 +12:00
}
public void MoveShapeBottom(BaseShape shape)
{
if (shape != null)
{
for (int i = 0; i < Shapes.Count; i++)
{
if (Shapes[i] == shape)
{
Shapes.Move(i, 0);
return;
}
}
}
}
public void MoveCurrentShapeBottom()
{
MoveShapeBottom(CurrentShape);
}
public void MoveShapeTop(BaseShape shape)
{
if (shape != null)
{
for (int i = 0; i < Shapes.Count; i++)
{
if (Shapes[i] == shape)
{
Shapes.Move(i, Shapes.Count - 1);
return;
}
}
}
}
public void MoveCurrentShapeTop()
{
MoveShapeTop(CurrentShape);
}
public void MoveShapeDown(BaseShape shape)
{
if (shape != null)
{
for (int i = 1; i < Shapes.Count; i++)
{
if (Shapes[i] == shape)
{
Shapes.Move(i, --i);
return;
}
}
}
}
public void MoveCurrentShapeDown()
{
MoveShapeDown(CurrentShape);
}
public void MoveShapeUp(BaseShape shape)
{
if (shape != null)
{
for (int i = 0; i < Shapes.Count - 1; i++)
{
if (Shapes[i] == shape)
{
Shapes.Move(i, ++i);
return;
}
}
}
}
public void MoveCurrentShapeUp()
{
MoveShapeUp(CurrentShape);
}
public void MoveAll(float x, float y)
2017-10-18 03:44:46 +13:00
{
if (x != 0 || y != 0)
2017-10-18 03:44:46 +13:00
{
foreach (BaseShape shape in Shapes)
{
shape.Move(x, y);
}
2017-10-18 03:44:46 +13:00
}
}
public void MoveAll(PointF offset)
2017-10-18 03:44:46 +13:00
{
MoveAll(offset.X, offset.Y);
}
2022-08-16 10:22:54 +12:00
public void CollapseAllHorizontal(float x, float width)
{
float x2 = x + width;
2023-12-27 21:42:38 +13:00
if (width <= 0) return;
List<BaseShape> toDelete = new List<BaseShape>();
foreach (BaseShape shape in Shapes)
{
RectangleF sr = shape.Rectangle;
2023-12-27 21:42:38 +13:00
if (sr.Left < x)
{
if (sr.Right <= x)
{
// case 1: entirely before the cut, no action needed
}
else if (sr.Right < x2)
{
// case 2: end reaches into the cut, shorten shape to end at x
shape.Rectangle = new RectangleF(sr.X, sr.Y, x - sr.X, sr.Height);
}
else
{
// case 3: end reaches over the cut, shorten shape by width, keeping left
shape.Rectangle = new RectangleF(sr.X, sr.Y, sr.Width - width, sr.Height);
}
}
else if (sr.Left < x2)
{
if (sr.Right <= x2)
{
// case 4: entirely inside the cut, delete the shape
toDelete.Add(shape);
}
else
{
// case 5: beginning reaches into the cut, shorten shape by difference between shape left and x2
shape.Rectangle = new RectangleF(x, sr.Y, sr.Right - x2, sr.Height);
}
}
else
{
// case 6: entirely after the cut, offset shape by width
shape.Rectangle = new RectangleF(sr.X - width, sr.Y, sr.Width, sr.Height);
}
}
foreach (BaseShape shape in toDelete)
{
2022-08-17 18:49:45 +12:00
DeleteShape(shape);
}
2022-08-16 10:22:54 +12:00
}
public void CollapseAllVertical(float y, float height)
{
float y2 = y + height;
2023-12-27 21:42:38 +13:00
if (height <= 0) return;
List<BaseShape> toDelete = new List<BaseShape>();
foreach (BaseShape shape in Shapes)
{
RectangleF sr = shape.Rectangle;
2023-12-27 21:42:38 +13:00
if (sr.Top < y)
{
if (sr.Bottom <= y)
{
// case 1: entirely before the cut, no action needed
}
else if (sr.Bottom < y2)
{
// case 2: end reaches into the cut, shorten shape to end at x
shape.Rectangle = new RectangleF(sr.X, sr.Y, sr.Width, y - sr.Y);
}
else
{
// case 3: end reaches over the cut, shorten shape by width, keeping left
shape.Rectangle = new RectangleF(sr.X, sr.Y, sr.Width, sr.Height - height);
}
}
else if (sr.Top < y2)
{
if (sr.Bottom <= y2)
{
// case 4: entirely inside the cut, delete the shape
toDelete.Add(shape);
}
else
{
// case 5: beginning reaches into the cut, shorten shape by difference between shape left and x2
shape.Rectangle = new RectangleF(sr.X, y, sr.Width, sr.Bottom - y2);
}
}
else
{
// case 6: entirely after the cut, offset shape by width
shape.Rectangle = new RectangleF(sr.X, sr.Y - height, sr.Width, sr.Height);
}
}
foreach (BaseShape shape in toDelete)
{
2022-08-17 18:49:45 +12:00
DeleteShape(shape);
}
2022-08-16 10:22:54 +12:00
}
2017-10-18 07:34:06 +13:00
public void RemoveOutsideShapes()
{
foreach (BaseShape shape in Shapes.ToArray())
{
if (!Form.CanvasRectangle.IntersectsWith(shape.Rectangle))
2017-10-18 07:34:06 +13:00
{
shape.Remove();
}
}
}
private bool IsShapeTypeRegion(ShapeType shapeType)
{
switch (shapeType)
{
case ShapeType.RegionRectangle:
case ShapeType.RegionEllipse:
case ShapeType.RegionFreehand:
return true;
}
return false;
}
private void UpdateNodes()
{
BaseShape shape = CurrentShape;
if (shape != null && NodesVisible)
{
if (InputManager.IsMouseDown(MouseButtons.Left))
{
2016-08-06 01:35:44 +12:00
shape.OnNodeUpdate();
}
else if (IsResizing)
{
IsResizing = false;
shape.OnResized();
}
2016-08-06 01:35:44 +12:00
shape.OnNodePositionUpdate();
}
}
2016-05-23 08:43:19 +12:00
public void OrderStepShapes()
{
int i = StartingStepNumber;
2016-05-23 08:43:19 +12:00
foreach (StepDrawingShape shape in Shapes.OfType<StepDrawingShape>())
{
shape.Number = i++;
}
}
2020-09-16 06:44:39 +12:00
private void DuplicateCurrrentShape(bool insertMousePosition)
{
BaseShape shape = CurrentShape;
if (shape != null && shape.IsHandledBySelectTool)
{
BaseShape shapeCopy = shape.Duplicate();
2023-12-27 18:56:07 +13:00
if (shapeCopy != null)
{
2020-09-16 06:44:39 +12:00
if (insertMousePosition)
{
shapeCopy.MoveAbsolute(Form.ScaledClientMousePosition, true);
2020-09-16 06:44:39 +12:00
}
else
{
2020-09-16 07:12:57 +12:00
shapeCopy.Move(10, 10);
2020-09-16 06:44:39 +12:00
}
2020-09-16 04:42:58 +12:00
shapeCopy.OnMoved();
AddShape(shapeCopy);
SelectCurrentShape();
}
}
}
private void PasteFromClipboard(bool insertMousePosition)
{
if (ClipboardHelpers.ContainsImage())
{
2020-03-24 08:33:26 +13:00
Bitmap bmp = ClipboardHelpers.GetImage();
InsertImage(bmp);
}
else if (ClipboardHelpers.ContainsFileDropList())
{
string[] files = ClipboardHelpers.GetFileDropList();
if (files != null)
{
string imageFilePath = files.FirstOrDefault(x => FileHelpers.IsImageFile(x));
if (!string.IsNullOrEmpty(imageFilePath))
{
Bitmap bmp = ImageHelpers.LoadImage(imageFilePath);
InsertImage(bmp);
}
}
}
else if (ClipboardHelpers.ContainsText())
{
string text = ClipboardHelpers.GetText();
if (!string.IsNullOrEmpty(text))
{
PointF pos;
if (insertMousePosition)
{
pos = Form.ScaledClientMousePosition;
}
else
{
pos = Form.ClientArea.Center();
}
2017-11-07 05:01:02 +13:00
CurrentTool = ShapeType.DrawingTextBackground;
2017-02-16 13:49:13 +13:00
TextDrawingShape shape = (TextDrawingShape)CreateShape(ShapeType.DrawingTextBackground);
shape.Rectangle = new RectangleF(pos.X, pos.Y, 1, 1);
shape.Text = text.Trim();
shape.OnCreated();
AddShape(shape);
SelectCurrentShape();
}
}
}
public void AddCursor(Bitmap bmpCursor, Point position)
{
CursorDrawingShape shape = (CursorDrawingShape)CreateShape(ShapeType.DrawingCursor);
shape.UpdateCursor(bmpCursor, position);
Shapes.Add(shape);
}
public void DrawRegionArea(Graphics g, RectangleF rect, bool isAnimated, bool showAreaInfo = false)
{
Form.DrawRegionArea(g, rect, isAnimated);
2017-12-31 11:57:13 +13:00
if (showAreaInfo)
{
string areaText = Form.GetAreaText(rect);
Form.DrawAreaText(g, areaText, rect);
}
}
2020-03-21 08:02:26 +13:00
public void UpdateCanvas(Bitmap canvas, bool centerCanvas = true)
{
2020-03-21 08:02:26 +13:00
Form.InitBackground(canvas, centerCanvas);
foreach (BaseEffectShape effect in EffectShapes)
{
effect.OnMoved();
}
2021-10-23 11:44:29 +13:00
OnImageModified();
}
public void CropArea(RectangleF rect)
{
2020-03-21 08:02:26 +13:00
Bitmap bmp = CropImage(rect, true);
2020-03-21 08:02:26 +13:00
if (bmp != null)
{
history.CreateCanvasMemento();
MoveAll(Form.CanvasRectangle.X - rect.X, Form.CanvasRectangle.Y - rect.Y);
2020-03-21 08:02:26 +13:00
UpdateCanvas(bmp);
}
}
public Bitmap CropImage(RectangleF rect, bool onlyIfSizeDifferent = false)
{
rect = CaptureHelpers.ScreenToClient(rect.Round());
PointF offset = CaptureHelpers.ScreenToClient(Form.CanvasRectangle.Location.Round());
2017-05-24 02:28:56 +12:00
rect.X -= offset.X;
rect.Y -= offset.Y;
Rectangle cropRect = Rectangle.Intersect(new Rectangle(0, 0, Form.Canvas.Width, Form.Canvas.Height), rect.Round());
if (cropRect.IsValid() && (!onlyIfSizeDifferent || cropRect.Size != Form.Canvas.Size))
{
return ImageHelpers.CropBitmap(Form.Canvas, cropRect);
}
return null;
}
2022-08-16 22:11:13 +12:00
public void CutOut(RectangleF rect)
2022-08-16 10:22:54 +12:00
{
2023-12-27 17:57:06 +13:00
history.CreateCanvasMemento();
2022-08-16 10:22:54 +12:00
2023-12-27 17:57:06 +13:00
bool isHorizontal = rect.Width > rect.Height;
RectangleF adjustedRect = CaptureHelpers.ScreenToClient(rect.Round());
2022-08-16 10:22:54 +12:00
PointF offset = CaptureHelpers.ScreenToClient(Form.CanvasRectangle.Location.Round());
adjustedRect.X -= offset.X;
adjustedRect.Y -= offset.Y;
Rectangle cropRect = Rectangle.Intersect(new Rectangle(0, 0, Form.Canvas.Width, Form.Canvas.Height), adjustedRect.Round());
2022-08-16 10:22:54 +12:00
if (isHorizontal && cropRect.Width > 0)
{
CollapseAllHorizontal(rect.X, rect.Width);
2022-08-18 07:46:45 +12:00
UpdateCanvas(ImageHelpers.CutOutBitmapMiddle(Form.Canvas, Orientation.Horizontal, cropRect.X, cropRect.Width, AnnotationOptions.CutOutEffectType, AnnotationOptions.CutOutEffectSize));
2022-08-16 10:22:54 +12:00
}
else if (!isHorizontal && cropRect.Height > 0)
{
CollapseAllVertical(rect.Y, rect.Height);
2022-08-18 07:46:45 +12:00
UpdateCanvas(ImageHelpers.CutOutBitmapMiddle(Form.Canvas, Orientation.Vertical, cropRect.Y, cropRect.Height, AnnotationOptions.CutOutEffectType, AnnotationOptions.CutOutEffectSize));
2022-08-16 10:22:54 +12:00
}
}
public Color GetColor(Bitmap bmp, Point pos)
2020-03-12 01:19:04 +13:00
{
if (bmp != null)
2020-03-12 01:19:04 +13:00
{
Point position = CaptureHelpers.ScreenToClient(pos);
Point offset = CaptureHelpers.ScreenToClient(Form.CanvasRectangle.Location.Round());
2020-03-12 01:19:04 +13:00
position.X -= offset.X;
position.Y -= offset.Y;
if (position.X.IsBetween(0, bmp.Width - 1) && position.Y.IsBetween(0, bmp.Height - 1))
2020-03-12 01:19:04 +13:00
{
return bmp.GetPixel(position.X, position.Y);
2020-03-12 01:19:04 +13:00
}
}
return Color.Empty;
}
public Color GetCurrentColor(Bitmap bmp)
{
return GetColor(bmp, Form.ScaledClientMousePosition.Round());
}
2020-03-12 01:19:04 +13:00
public Color GetCurrentColor()
{
return GetCurrentColor(Form.Canvas);
2020-03-12 01:19:04 +13:00
}
2017-12-27 02:40:36 +13:00
public void NewImage()
{
Form.Pause();
2020-03-21 08:02:26 +13:00
Bitmap bmp = NewImageForm.CreateNewImage(Options, Form);
Form.Resume();
2020-03-21 08:02:26 +13:00
if (bmp != null)
{
Form.ImageFilePath = "";
history.CreateCanvasMemento();
2023-12-27 21:42:38 +13:00
DeleteAllShapes();
UpdateMenu();
2020-03-21 08:02:26 +13:00
UpdateCanvas(bmp);
}
}
private void OpenImageFile()
{
Form.Pause();
2017-12-27 02:54:10 +13:00
string filePath = ImageHelpers.OpenImageFileDialog(Form);
Form.Resume();
LoadImageFile(filePath);
}
private void LoadImageFile(string filePath)
{
if (!string.IsNullOrEmpty(filePath))
{
Bitmap bmp = ImageHelpers.LoadImage(filePath);
2020-03-21 08:02:26 +13:00
if (bmp != null)
{
Form.ImageFilePath = filePath;
history.CreateCanvasMemento();
2023-12-27 21:42:38 +13:00
DeleteAllShapes();
UpdateMenu();
2020-03-21 08:02:26 +13:00
UpdateCanvas(bmp);
}
}
}
private void InsertImageFile()
{
Form.Pause();
2017-12-27 02:54:10 +13:00
string filePath = ImageHelpers.OpenImageFileDialog(Form);
Form.Resume();
if (!string.IsNullOrEmpty(filePath))
{
Bitmap bmp = ImageHelpers.LoadImage(filePath);
InsertImage(bmp);
}
}
private void InsertImageFromScreen()
{
2020-03-21 14:41:34 +13:00
Bitmap bmp;
try
{
Form.Pause();
Form.Hide();
menuForm.Hide();
Thread.Sleep(250);
2020-03-21 14:41:34 +13:00
bmp = RegionCaptureTasks.GetRegionImage(Options);
}
finally
{
Form.Show();
menuForm.Show();
Form.Resume();
}
2020-03-21 14:41:34 +13:00
InsertImage(bmp);
}
private void InsertImage(Image img)
{
if (img != null)
{
PointF pos;
bool centerImage;
using (ImageInsertForm imageInsertForm = new ImageInsertForm())
{
imageInsertForm.ShowDialog(Form);
switch (imageInsertForm.ImageInsertMethod)
{
default:
img.Dispose();
return;
case ImageInsertMethod.Center:
pos = Form.ClientArea.Center();
centerImage = true;
break;
case ImageInsertMethod.CanvasExpandDown:
pos = new PointF(Form.CanvasRectangle.X, Form.CanvasRectangle.Bottom);
centerImage = false;
ChangeCanvasSize(new Padding(0, 0, (int)Math.Round(Math.Max(0, img.Width - Form.CanvasRectangle.Width)), img.Height), Options.EditorCanvasColor);
break;
case ImageInsertMethod.CanvasExpandRight:
pos = new PointF(Form.CanvasRectangle.Right, Form.CanvasRectangle.Y);
centerImage = false;
ChangeCanvasSize(new Padding(0, 0, img.Width, (int)Math.Round(Math.Max(0, img.Height - Form.CanvasRectangle.Height))), Options.EditorCanvasColor);
break;
}
}
CurrentTool = ShapeType.DrawingImage;
ImageDrawingShape shape = (ImageDrawingShape)CreateShape(ShapeType.DrawingImage);
shape.Rectangle = new RectangleF(pos.X, pos.Y, 1, 1);
shape.SetImage(img, centerImage);
shape.OnCreated();
AddShape(shape);
SelectCurrentShape();
}
}
2017-10-22 11:33:00 +13:00
private void ChangeImageSize()
{
Form.Pause();
Size oldSize = Form.Canvas.Size;
2017-10-22 11:33:00 +13:00
using (ImageSizeForm imageSizeForm = new ImageSizeForm(oldSize, Options.ImageEditorResizeInterpolationMode))
2017-10-22 12:26:47 +13:00
{
if (imageSizeForm.ShowDialog(Form) == DialogResult.OK)
2017-10-22 11:33:00 +13:00
{
Size size = imageSizeForm.ImageSize;
Options.ImageEditorResizeInterpolationMode = imageSizeForm.InterpolationMode;
2017-10-22 12:26:47 +13:00
if (size != oldSize)
{
history.CreateCanvasMemento();
2023-12-27 17:57:06 +13:00
InterpolationMode interpolationMode = ImageHelpers.GetInterpolationMode(Options.ImageEditorResizeInterpolationMode);
Bitmap bmp = ImageHelpers.ResizeImage(Form.Canvas, size, interpolationMode);
2017-10-22 12:26:47 +13:00
2020-03-21 08:02:26 +13:00
if (bmp != null)
2017-10-22 12:26:47 +13:00
{
2020-03-21 08:02:26 +13:00
UpdateCanvas(bmp);
2017-10-22 12:26:47 +13:00
}
}
2017-10-22 11:33:00 +13:00
}
}
Form.Resume();
2017-10-22 11:33:00 +13:00
}
private void ChangeCanvasSize()
{
Form.Pause();
using (CanvasSizeForm canvasSizeForm = new CanvasSizeForm(Padding.Empty, Options.EditorCanvasColor))
{
if (canvasSizeForm.ShowDialog(Form) == DialogResult.OK)
2017-10-22 09:43:39 +13:00
{
Padding canvas = canvasSizeForm.Canvas;
2022-01-25 04:22:54 +13:00
Options.EditorCanvasColor = canvasSizeForm.CanvasColor;
Bitmap bmp = ImageHelpers.AddCanvas(Form.Canvas, canvas, Options.EditorCanvasColor);
2020-03-21 08:02:26 +13:00
if (bmp != null)
2017-10-22 09:43:39 +13:00
{
history.CreateCanvasMemento();
2023-12-27 17:57:06 +13:00
2017-10-28 13:26:29 +13:00
MoveAll(canvas.Left, canvas.Top);
2020-03-21 08:02:26 +13:00
UpdateCanvas(bmp);
2017-10-22 09:43:39 +13:00
}
}
}
Form.Resume();
}
public void AutoResizeCanvas()
{
RectangleF canvas = Form.CanvasRectangle;
RectangleF combinedImageRectangle = Shapes.OfType<ImageDrawingShape>().Select(x => x.Rectangle).Combine();
if (!canvas.Contains(combinedImageRectangle))
{
Padding margin = new Padding((int)Math.Round(Math.Max(0, canvas.X - combinedImageRectangle.X)), (int)Math.Round(Math.Max(0, canvas.Y - combinedImageRectangle.Y)),
(int)Math.Round(Math.Max(0, combinedImageRectangle.Right - canvas.Right)), (int)Math.Round(Math.Max(0, combinedImageRectangle.Bottom - canvas.Bottom)));
ChangeCanvasSize(margin, Options.EditorCanvasColor);
}
}
private void ChangeCanvasSize(Padding margin, Color canvasColor)
{
2020-03-21 14:41:34 +13:00
Bitmap bmp = ImageHelpers.AddCanvas(Form.Canvas, margin, canvasColor);
2020-03-21 08:02:26 +13:00
if (bmp != null)
{
history.CreateCanvasMemento();
2023-12-27 17:57:06 +13:00
Form.CanvasRectangle = Form.CanvasRectangle.LocationOffset(-margin.Left, -margin.Top);
2020-03-21 08:02:26 +13:00
UpdateCanvas(bmp, false);
}
}
private void AddCropTool()
{
CurrentTool = ShapeType.ToolCrop;
CropTool tool = (CropTool)CreateShape(ShapeType.ToolCrop);
tool.Rectangle = Form.CanvasRectangle;
tool.OnCreated();
AddShape(tool);
SelectCurrentShape();
}
private void AutoCropImage()
{
Rectangle source = new Rectangle(0, 0, Form.Canvas.Width, Form.Canvas.Height);
RectangleF crop;
2020-08-24 03:30:12 +12:00
using (Bitmap resultImage = Form.GetResultImage())
{
crop = ImageHelpers.FindAutoCropRectangle(resultImage);
}
if (source != crop && crop.X >= 0 && crop.Y >= 0 && crop.Width > 0 && crop.Height > 0)
{
CurrentTool = ShapeType.ToolCrop;
CropTool tool = (CropTool)CreateShape(ShapeType.ToolCrop);
2020-08-24 03:30:12 +12:00
tool.Rectangle = crop.LocationOffset(Form.CanvasRectangle.Location);
tool.OnCreated();
AddShape(tool);
SelectCurrentShape();
}
}
private void RotateImage(RotateFlipType type)
{
history.CreateCanvasMemento();
2023-12-27 17:57:06 +13:00
2020-03-21 08:02:26 +13:00
Bitmap bmp = (Bitmap)Form.Canvas.Clone();
bmp.RotateFlip(type);
UpdateCanvas(bmp);
}
private void AddImageEffects()
{
Form.Pause();
2018-01-21 23:12:32 +13:00
using (ImageEffectsForm imageEffectsForm = new ImageEffectsForm(Form.Canvas, Options.ImageEffectPresets, Options.SelectedImageEffectPreset))
{
imageEffectsForm.EditorMode();
2018-01-21 23:12:32 +13:00
bool applyEffect = imageEffectsForm.ShowDialog(Form) == DialogResult.OK;
Options.SelectedImageEffectPreset = imageEffectsForm.SelectedPresetIndex;
if (applyEffect)
{
2018-01-21 23:12:32 +13:00
ImageEffectPreset preset = imageEffectsForm.Presets.ReturnIfValidIndex(Options.SelectedImageEffectPreset);
if (preset != null)
{
2020-03-21 08:02:26 +13:00
Bitmap bmp = preset.ApplyEffects(Form.Canvas);
2020-03-21 08:02:26 +13:00
if (bmp != null)
{
history.CreateCanvasMemento();
2023-12-27 17:57:06 +13:00
2020-03-21 08:02:26 +13:00
UpdateCanvas(bmp);
}
}
}
}
Form.Resume();
}
private bool PickColor(Color currentColor, out Color newColor)
{
Func<PointInfo> openScreenColorPicker = null;
if (Form.IsFullscreen)
{
openScreenColorPicker = () =>
{
2020-03-21 08:02:26 +13:00
using (Bitmap canvas = Form.Canvas.CloneSafe())
{
return RegionCaptureTasks.GetPointInfo(Options, canvas);
}
};
}
else
{
openScreenColorPicker = () => RegionCaptureTasks.GetPointInfo(Options);
}
return ColorPickerForm.PickColor(currentColor, out newColor, Form, openScreenColorPicker, Options.ColorPickerOptions);
}
public void Dispose()
{
DeleteAllShapes();
2023-12-27 17:57:06 +13:00
history.Dispose();
}
2013-11-03 23:53:49 +13:00
}
}