diff --git a/src/Greenshot.Editor/Drawing/CropContainer.cs b/src/Greenshot.Editor/Drawing/CropContainer.cs
index 2324331f0..2a4797760 100644
--- a/src/Greenshot.Editor/Drawing/CropContainer.cs
+++ b/src/Greenshot.Editor/Drawing/CropContainer.cs
@@ -19,10 +19,12 @@
* along with this program. If not, see .
*/
+
using System.Drawing;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
+using Greenshot.Editor.Drawing.Adorners;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Helpers;
@@ -33,6 +35,11 @@ namespace Greenshot.Editor.Drawing
///
public class CropContainer : DrawableContainer
{
+ //awailable Styles
+ public static readonly string DefaultCropStyle = nameof(DefaultCropStyle);
+ public static readonly string VerticalCropOutStyle = nameof(VerticalCropOutStyle);
+ public static readonly string HorizontalCropOutStyle = nameof(HorizontalCropOutStyle);
+
public CropContainer(ISurface parent) : base(parent)
{
Init();
@@ -45,13 +52,85 @@ namespace Greenshot.Editor.Drawing
}
private void Init()
+ {
+ switch (GetFieldValueAsString(FieldType.CROPSTYLE))
+ {
+ case string s when s.Equals(HorizontalCropOutStyle):
+ {
+ InitHorizontalCropOutStyle();
+ break;
+ }
+ case string s when s.Equals(VerticalCropOutStyle):
+ {
+ InitVerticalCropOutStyle();
+ break;
+ }
+ default:
+ {
+ InitCropStyle();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// rotate through all awailable Styles
+ ///
+ ///
+ ///
+ public static string GetNextStyle(string style)
+ {
+ return style switch
+ {
+ var s when s.Equals(HorizontalCropOutStyle) => VerticalCropOutStyle,
+ var s when s.Equals(VerticalCropOutStyle) => DefaultCropStyle,
+ _ => HorizontalCropOutStyle,
+ };
+ }
+
+ private void InitCropStyle()
{
CreateDefaultAdorners();
}
+ private void InitHorizontalCropOutStyle()
+ {
+ var defaultHeight = 25;
+
+ if (_parent?.Image is { } image)
+ {
+ Size = new Size(image.Width, defaultHeight);
+ }
+ CreateTopBottomAdorners();
+ }
+
+ private void InitVerticalCropOutStyle()
+ {
+ var defaultWidth = 25;
+
+ if (_parent?.Image is { } image)
+ {
+ Size = new Size(defaultWidth, image.Height);
+ }
+
+ CreateLeftRightAdorners();
+ }
+
+ private void CreateTopBottomAdorners()
+ {
+ Adorners.Add(new ResizeAdorner(this, Positions.TopCenter));
+ Adorners.Add(new ResizeAdorner(this, Positions.BottomCenter));
+ }
+ private void CreateLeftRightAdorners()
+ {
+ Adorners.Add(new ResizeAdorner(this, Positions.MiddleLeft));
+ Adorners.Add(new ResizeAdorner(this, Positions.MiddleRight));
+ }
+
protected override void InitializeFields()
{
AddField(GetType(), FieldType.FLAGS, FieldFlag.CONFIRMABLE);
+ AddField(GetType(), FieldType.CROPSTYLE, DefaultCropStyle);
}
public override void Invalidate()
@@ -83,6 +162,7 @@ namespace Greenshot.Editor.Drawing
return;
}
+
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);
@@ -90,20 +170,100 @@ namespace Greenshot.Editor.Drawing
DrawSelectionBorder(g, selectionRect);
- // 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, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height));
- // bottom
- g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height)));
+ switch (GetFieldValueAsString(FieldType.CROPSTYLE))
+ {
+ case var s when s.Equals(HorizontalCropOutStyle):
+ case var t when t.Equals(VerticalCropOutStyle):
+ {
+ //draw inside
+ g.FillRectangle(cropBrush, cropRectangle);
+ break;
+ }
+ default:
+ {
+ //draw outside
+ // 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, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height));
+ // bottom
+ g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height)));
+ break;
+ }
+ }
+
+
}
///
/// No context menu for the CropContainer
///
public override bool HasContextMenu => false;
+
+ public override bool HandleMouseDown(int x, int y)
+ {
+ return GetFieldValueAsString(FieldType.CROPSTYLE) switch
+ {
+ //force horizontal crop to left edge
+ var s when s.Equals(HorizontalCropOutStyle) => base.HandleMouseDown(0, y),
+ //force vertical crop to top edge
+ var s when s.Equals(VerticalCropOutStyle) => base.HandleMouseDown(x, 0),
+ _ => base.HandleMouseDown(x, y),
+ };
+ }
+
+ public override bool HandleMouseMove(int x, int y)
+ {
+ Invalidate();
+
+ switch (GetFieldValueAsString(FieldType.CROPSTYLE))
+ {
+ case var s when s.Equals(HorizontalCropOutStyle):
+ {
+ //stick on left and right
+ //allow only horizontal changes
+ if (_parent?.Image is { } image)
+ {
+ _boundsAfterResize.X = 0;
+ _boundsAfterResize.Y = _boundsBeforeResize.Top;
+ _boundsAfterResize.Width = image.Width;
+ _boundsAfterResize.Height = y - _boundsAfterResize.Top;
+ }
+ break;
+ }
+ case var s when s.Equals(VerticalCropOutStyle):
+ {
+ //stick on top and bottom
+ //allow only vertical changes
+ if (_parent?.Image is { } image)
+ {
+ _boundsAfterResize.X = _boundsBeforeResize.Left;
+ _boundsAfterResize.Y = 0;
+ _boundsAfterResize.Width = x - _boundsAfterResize.Left;
+ _boundsAfterResize.Height = image.Height;
+ }
+ break;
+ }
+ default:
+ {
+ // reset "workbench" rectangle to current bounds
+ _boundsAfterResize.X = _boundsBeforeResize.Left;
+ _boundsAfterResize.Y = _boundsBeforeResize.Top;
+ _boundsAfterResize.Width = x - _boundsAfterResize.Left;
+ _boundsAfterResize.Height = y - _boundsAfterResize.Top;
+ break;
+ }
+
+ }
+ ScaleHelper.Scale(_boundsBeforeResize, x, y, ref _boundsAfterResize, GetAngleRoundProcessor());
+
+ // apply scaled bounds to this DrawableContainer
+ ApplyBounds(_boundsAfterResize);
+
+ Invalidate();
+ return true;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Greenshot.Editor/Drawing/Fields/FieldType.cs b/src/Greenshot.Editor/Drawing/Fields/FieldType.cs
index c3b732f4b..610cbb7ca 100644
--- a/src/Greenshot.Editor/Drawing/Fields/FieldType.cs
+++ b/src/Greenshot.Editor/Drawing/Fields/FieldType.cs
@@ -31,31 +31,33 @@ namespace Greenshot.Editor.Drawing.Fields
[Serializable]
public class FieldType : IFieldType
{
- public static readonly IFieldType ARROWHEADS = new FieldType("ARROWHEADS");
- public static readonly IFieldType BLUR_RADIUS = new FieldType("BLUR_RADIUS");
- public static readonly IFieldType BRIGHTNESS = new FieldType("BRIGHTNESS");
- public static readonly IFieldType FILL_COLOR = new FieldType("FILL_COLOR");
- public static readonly IFieldType FONT_BOLD = new FieldType("FONT_BOLD");
- public static readonly IFieldType FONT_FAMILY = new FieldType("FONT_FAMILY");
- public static readonly IFieldType FONT_ITALIC = new FieldType("FONT_ITALIC");
- public static readonly IFieldType FONT_SIZE = new FieldType("FONT_SIZE");
- public static readonly IFieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType("TEXT_HORIZONTAL_ALIGNMENT");
- public static readonly IFieldType TEXT_VERTICAL_ALIGNMENT = new FieldType("TEXT_VERTICAL_ALIGNMENT");
- public static readonly IFieldType HIGHLIGHT_COLOR = new FieldType("HIGHLIGHT_COLOR");
- public static readonly IFieldType LINE_COLOR = new FieldType("LINE_COLOR");
- public static readonly IFieldType LINE_THICKNESS = new FieldType("LINE_THICKNESS");
- public static readonly IFieldType MAGNIFICATION_FACTOR = new FieldType("MAGNIFICATION_FACTOR");
- public static readonly IFieldType PIXEL_SIZE = new FieldType("PIXEL_SIZE");
- public static readonly IFieldType PREVIEW_QUALITY = new FieldType("PREVIEW_QUALITY");
- public static readonly IFieldType SHADOW = new FieldType("SHADOW");
- public static readonly IFieldType PREPARED_FILTER_OBFUSCATE = new FieldType("PREPARED_FILTER_OBFUSCATE");
- public static readonly IFieldType PREPARED_FILTER_HIGHLIGHT = new FieldType("PREPARED_FILTER_HIGHLIGHT");
- public static readonly IFieldType FLAGS = new FieldType("FLAGS");
+ public static readonly IFieldType ARROWHEADS = new FieldType(nameof(ARROWHEADS));
+ public static readonly IFieldType BLUR_RADIUS = new FieldType(nameof(BLUR_RADIUS));
+ public static readonly IFieldType BRIGHTNESS = new FieldType(nameof(BRIGHTNESS));
+ public static readonly IFieldType FILL_COLOR = new FieldType(nameof(FILL_COLOR));
+ public static readonly IFieldType FONT_BOLD = new FieldType(nameof(FONT_BOLD));
+ public static readonly IFieldType FONT_FAMILY = new FieldType(nameof(FONT_FAMILY));
+ public static readonly IFieldType FONT_ITALIC = new FieldType(nameof(FONT_ITALIC));
+ public static readonly IFieldType FONT_SIZE = new FieldType(nameof(FONT_SIZE));
+ public static readonly IFieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType(nameof(TEXT_HORIZONTAL_ALIGNMENT));
+ public static readonly IFieldType TEXT_VERTICAL_ALIGNMENT = new FieldType(nameof(TEXT_VERTICAL_ALIGNMENT));
+ public static readonly IFieldType HIGHLIGHT_COLOR = new FieldType(nameof(HIGHLIGHT_COLOR));
+ public static readonly IFieldType LINE_COLOR = new FieldType(nameof(LINE_COLOR));
+ public static readonly IFieldType LINE_THICKNESS = new FieldType(nameof(LINE_THICKNESS));
+ public static readonly IFieldType MAGNIFICATION_FACTOR = new FieldType(nameof(MAGNIFICATION_FACTOR));
+ public static readonly IFieldType PIXEL_SIZE = new FieldType(nameof(PIXEL_SIZE));
+ public static readonly IFieldType PREVIEW_QUALITY = new FieldType(nameof(PREVIEW_QUALITY));
+ public static readonly IFieldType SHADOW = new FieldType(nameof(SHADOW));
+ public static readonly IFieldType PREPARED_FILTER_OBFUSCATE = new FieldType(nameof(PREPARED_FILTER_OBFUSCATE));
+ public static readonly IFieldType PREPARED_FILTER_HIGHLIGHT = new FieldType(nameof(PREPARED_FILTER_HIGHLIGHT));
+ public static readonly IFieldType FLAGS = new FieldType(nameof(FLAGS));
+ public static readonly IFieldType CROPSTYLE = new FieldType(nameof(CROPSTYLE));
+
public static IFieldType[] Values =
{
ARROWHEADS, BLUR_RADIUS, BRIGHTNESS, FILL_COLOR, FONT_BOLD, FONT_FAMILY, FONT_ITALIC, FONT_SIZE, TEXT_HORIZONTAL_ALIGNMENT, TEXT_VERTICAL_ALIGNMENT, HIGHLIGHT_COLOR,
- LINE_COLOR, LINE_THICKNESS, MAGNIFICATION_FACTOR, PIXEL_SIZE, PREVIEW_QUALITY, SHADOW, PREPARED_FILTER_OBFUSCATE, PREPARED_FILTER_HIGHLIGHT, FLAGS
+ LINE_COLOR, LINE_THICKNESS, MAGNIFICATION_FACTOR, PIXEL_SIZE, PREVIEW_QUALITY, SHADOW, PREPARED_FILTER_OBFUSCATE, PREPARED_FILTER_HIGHLIGHT, FLAGS, CROPSTYLE
};
public string Name { get; set; }
diff --git a/src/Greenshot.Editor/Drawing/Surface.cs b/src/Greenshot.Editor/Drawing/Surface.cs
index 7c27c8ce1..2979026ab 100644
--- a/src/Greenshot.Editor/Drawing/Surface.cs
+++ b/src/Greenshot.Editor/Drawing/Surface.cs
@@ -1184,7 +1184,7 @@ namespace Greenshot.Editor.Drawing
///
/// Crop the surface
///
- ///
+ /// rectangle that remains
///
public bool ApplyCrop(Rectangle cropRectangle)
{
@@ -1226,6 +1226,112 @@ namespace Greenshot.Editor.Drawing
return false;
}
+ ///
+ /// Crop out the surface
+ /// Splits the image in 3 parts(top, middle, bottom). Crop out the middle and joins top and bottom.
+ ///
+ /// rectangle of the middle part
+ ///
+ public bool ApplyHorizontalCrop(Rectangle cropRectangle)
+ {
+ if (IsCropPossible(ref cropRectangle))
+ {
+ Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size);
+ Bitmap tmpNewimage, tmpImageTop, tmpImageBottom;
+ // Make sure we have information, this this fails
+ try
+ {
+ tmpNewimage = new Bitmap(Image.Size.Width, Image.Size.Height - cropRectangle.Height);
+ tmpImageTop = ImageHelper.CloneArea(Image, new Rectangle(0, 0, Image.Size.Width, cropRectangle.Top), PixelFormat.DontCare);
+ tmpImageBottom = ImageHelper.CloneArea(Image, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, Image.Size.Width, Image.Size.Height - cropRectangle.Top - cropRectangle.Height), PixelFormat.DontCare);
+ }
+ catch (Exception ex)
+ {
+ ex.Data.Add("CropRectangle", cropRectangle);
+ ex.Data.Add("Width", Image.Width);
+ ex.Data.Add("Height", Image.Height);
+ ex.Data.Add("Pixelformat", Image.PixelFormat);
+ throw;
+ }
+ using Graphics g = Graphics.FromImage(tmpNewimage);
+ g.DrawImage(tmpImageTop, new Point(0, 0));
+ g.DrawImage(tmpImageBottom, new Point(0, tmpImageTop.Height));
+
+
+ Matrix matrix = new Matrix();
+ matrix.Translate(0, -(cropRectangle.Top + cropRectangle.Height), MatrixOrder.Append);
+ // Make undoable
+ MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false);
+
+ // Do not dispose otherwise we can't undo the image!
+ SetImage(tmpNewimage, false);
+
+ _elements.Transform(matrix);
+ if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, tmpNewimage.Size)))
+ {
+ _surfaceSizeChanged(this, null);
+ }
+
+ Invalidate();
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Crop out the surface
+ /// Splits the image in 3 parts(left, middle, right). Crop out the middle and joins top and bottom.
+ ///
+ /// rectangle of the middle part
+ ///
+ public bool ApplyVerticalCrop(Rectangle cropRectangle)
+ {
+ if (IsCropPossible(ref cropRectangle))
+ {
+ Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size);
+ Bitmap tmpNewimage, tmpImageLeft, tmpImageRight;
+ // Make sure we have information, this this fails
+ try
+ {
+ tmpNewimage = new Bitmap(Image.Size.Width - cropRectangle.Width, Image.Size.Height);
+
+ tmpImageLeft = ImageHelper.CloneArea(Image, new Rectangle(0, 0, cropRectangle.Left, Image.Size.Height), PixelFormat.DontCare);
+ tmpImageRight = ImageHelper.CloneArea(Image, new Rectangle(cropRectangle.Left + cropRectangle.Width, 0, Image.Size.Width - cropRectangle.Width - cropRectangle.Left, Image.Size.Height), PixelFormat.DontCare);
+ }
+ catch (Exception ex)
+ {
+ ex.Data.Add("CropRectangle", cropRectangle);
+ ex.Data.Add("Width", Image.Width);
+ ex.Data.Add("Height", Image.Height);
+ ex.Data.Add("Pixelformat", Image.PixelFormat);
+ throw;
+ }
+ using Graphics g = Graphics.FromImage(tmpNewimage);
+ g.DrawImage(tmpImageLeft, new Point(0, 0));
+ g.DrawImage(tmpImageRight, new Point(tmpImageLeft.Width, 0));
+
+ Matrix matrix = new Matrix();
+ matrix.Translate(- cropRectangle.Left - cropRectangle.Width, 0, MatrixOrder.Append);
+ // Make undoable
+ MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false);
+
+ // Do not dispose otherwise we can't undo the image!
+ SetImage(tmpNewimage, false);
+
+ _elements.Transform(matrix);
+ if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, tmpNewimage.Size)))
+ {
+ _surfaceSizeChanged(this, null);
+ }
+
+ Invalidate();
+ return true;
+ }
+
+ return false;
+ }
+
///
/// The background here is the captured image.
/// This is called from the SurfaceBackgroundChangeMemento.
@@ -1961,7 +2067,31 @@ namespace Greenshot.Editor.Drawing
RemoveElement(_cropContainer, false);
if (confirm)
{
- ApplyCrop(_cropContainer.Bounds);
+ if (dc is CropContainer e)
+ {
+ switch (e.GetFieldValueAsString(FieldType.CROPSTYLE))
+ {
+ case var s when s.Equals(CropContainer.HorizontalCropOutStyle):
+ {
+ ApplyHorizontalCrop(_cropContainer.Bounds);
+ break;
+ }
+ case var s when s.Equals(CropContainer.VerticalCropOutStyle):
+ {
+ ApplyVerticalCrop(_cropContainer.Bounds);
+ break;
+ }
+ default:
+ {
+ ApplyCrop(_cropContainer.Bounds);
+ break;
+ }
+ }
+ }
+ else
+ {
+ ApplyCrop(_cropContainer.Bounds);
+ }
}
_cropContainer.Dispose();
@@ -1971,6 +2101,15 @@ namespace Greenshot.Editor.Drawing
}
}
+ public void RemoveCropContainer()
+ {
+ if (_cropContainer != null)
+ {
+ RemoveElement(_cropContainer, false);
+ _cropContainer.Dispose();
+ _cropContainer = null;
+ }
+ }
///
/// Paste all the elements that are on the clipboard
///
diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.cs
index 8ab283d0b..277f04252 100644
--- a/src/Greenshot.Editor/Forms/ImageEditorForm.cs
+++ b/src/Greenshot.Editor/Forms/ImageEditorForm.cs
@@ -727,7 +727,20 @@ namespace Greenshot.Editor.Forms
private void BtnCropClick(object sender, EventArgs e)
{
- _surface.DrawingMode = DrawingModes.Crop;
+ if (_surface.DrawingMode == DrawingModes.Crop)
+ {
+ //intercept repeated click event
+ //rotate through crop styles
+ _surface.FieldAggregator.GetField(FieldType.CROPSTYLE).Value = CropContainer.GetNextStyle((string)_surface.FieldAggregator.GetField(FieldType.CROPSTYLE).Value);
+ _surface.RemoveCropContainer();
+
+ //reinitialize crop modus
+ _surface.DrawingMode = DrawingModes.Crop;
+ }
+ else
+ {
+ _surface.DrawingMode = DrawingModes.Crop;
+ }
RefreshFieldControls();
}
@@ -1379,6 +1392,11 @@ namespace Greenshot.Editor.Forms
ToolStripItemEndisabler.Enable(helpToolStripMenuItem);
ToolStripItemEndisabler.Enable(aboutToolStripMenuItem);
ToolStripItemEndisabler.Enable(preferencesToolStripMenuItem);
+ if (_surface.DrawingMode == DrawingModes.Crop)
+ {
+ //While cropping, enable the button to change crop style
+ btnCrop.Enabled = true;
+ }
_controlsDisabledDueToConfirmable = true;
}
}