diff --git a/ShareX.HelpersLib/Properties/Resources.Designer.cs b/ShareX.HelpersLib/Properties/Resources.Designer.cs index 5c7d84ebd..ffd0b36a6 100644 --- a/ShareX.HelpersLib/Properties/Resources.Designer.cs +++ b/ShareX.HelpersLib/Properties/Resources.Designer.cs @@ -3245,6 +3245,15 @@ namespace ShareX.HelpersLib.Properties { } } + /// + /// Looks up a localized string similar to Spotlight. + /// + internal static string ShapeType_ToolSpotlight { + get { + return ResourceManager.GetString("ShapeType_ToolSpotlight", resourceCulture); + } + } + /// /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// diff --git a/ShareX.HelpersLib/Properties/Resources.resx b/ShareX.HelpersLib/Properties/Resources.resx index 4e0f9d292..6d555f491 100644 --- a/ShareX.HelpersLib/Properties/Resources.resx +++ b/ShareX.HelpersLib/Properties/Resources.resx @@ -1323,4 +1323,7 @@ Would you like to download and install it? Analyze image + + Spotlight + \ No newline at end of file diff --git a/ShareX.ScreenCaptureLib/Enums.cs b/ShareX.ScreenCaptureLib/Enums.cs index dd0b2f0ce..3207bd65b 100644 --- a/ShareX.ScreenCaptureLib/Enums.cs +++ b/ShareX.ScreenCaptureLib/Enums.cs @@ -315,6 +315,7 @@ namespace ShareX.ScreenCaptureLib EffectBlur, EffectPixelate, EffectHighlight, + ToolSpotlight, ToolCrop, ToolCutOut } diff --git a/ShareX.ScreenCaptureLib/Helpers/ImageEditorButton.cs b/ShareX.ScreenCaptureLib/Helpers/ImageEditorButton.cs index dd4140d2c..420527927 100644 --- a/ShareX.ScreenCaptureLib/Helpers/ImageEditorButton.cs +++ b/ShareX.ScreenCaptureLib/Helpers/ImageEditorButton.cs @@ -33,8 +33,10 @@ namespace ShareX.ScreenCaptureLib { public string Text { get; set; } public Color ButtonColor { get; set; } - public int ButtonDepth { get; set; } = 3; - public Color ButtonDepthColor => ColorHelpers.DarkerColor(ButtonColor, 0.5f); + public Color IconColor { get; set; } + public int ButtonDepth { get; set; } = 5; + public Color ButtonDepthColor => ColorHelpers.DarkerColor(ButtonColor, 0.3f); + public Color IconDepthColor => ColorHelpers.DarkerColor(IconColor, 0.7f); public override void OnDraw(Graphics g) { @@ -66,12 +68,13 @@ namespace ShareX.ScreenCaptureLib g.SmoothingMode = SmoothingMode.None; - using (Font font = new Font("Arial", 18)) + using (Font font = new Font("Arial", 20)) using (StringFormat sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }) - using (SolidBrush textDepthBrush = new SolidBrush(ButtonDepthColor)) + using (SolidBrush textBrush = new SolidBrush(IconColor)) + using (SolidBrush textDepthBrush = new SolidBrush(IconDepthColor)) { - g.DrawString(Text, font, textDepthBrush, rect.LocationOffset(0, 4), sf); - g.DrawString(Text, font, Brushes.White, rect.LocationOffset(0, 2), sf); + g.DrawString(Text, font, textDepthBrush, rect.LocationOffset(0, -1), sf); + g.DrawString(Text, font, textBrush, rect.LocationOffset(0, 2), sf); } } } diff --git a/ShareX.ScreenCaptureLib/Helpers/ImageEditorHistory.cs b/ShareX.ScreenCaptureLib/Helpers/ImageEditorHistory.cs index 993d3bae7..ef35ed24e 100644 --- a/ShareX.ScreenCaptureLib/Helpers/ImageEditorHistory.cs +++ b/ShareX.ScreenCaptureLib/Helpers/ImageEditorHistory.cs @@ -77,7 +77,8 @@ namespace ShareX.ScreenCaptureLib public void CreateShapesMemento() { - if (!shapeManager.IsCurrentShapeTypeRegion && shapeManager.CurrentTool != ShapeType.ToolCrop && shapeManager.CurrentTool != ShapeType.ToolCutOut) + if (!shapeManager.IsCurrentShapeTypeRegion && shapeManager.CurrentTool != ShapeType.ToolCrop && + shapeManager.CurrentTool != ShapeType.ToolCutOut && shapeManager.CurrentTool != ShapeType.ToolSpotlight) { ImageEditorMemento memento = GetMementoFromShapes(); AddMemento(memento); diff --git a/ShareX.ScreenCaptureLib/Properties/Resources.Designer.cs b/ShareX.ScreenCaptureLib/Properties/Resources.Designer.cs index 9a9e8a477..daafe7385 100644 --- a/ShareX.ScreenCaptureLib/Properties/Resources.Designer.cs +++ b/ShareX.ScreenCaptureLib/Properties/Resources.Designer.cs @@ -666,6 +666,16 @@ namespace ShareX.ScreenCaptureLib.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap flashlight_shine { + get { + object obj = ResourceManager.GetObject("flashlight_shine", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/ShareX.ScreenCaptureLib/Properties/Resources.resx b/ShareX.ScreenCaptureLib/Properties/Resources.resx index 0cd3591be..08e9f7c64 100644 --- a/ShareX.ScreenCaptureLib/Properties/Resources.resx +++ b/ShareX.ScreenCaptureLib/Properties/Resources.resx @@ -857,4 +857,7 @@ Would you like to save the changes before closing the image editor? Bottom + + ..\Resources\flashlight-shine.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/ShareX.ScreenCaptureLib/Resources/flashlight-shine.png b/ShareX.ScreenCaptureLib/Resources/flashlight-shine.png new file mode 100644 index 000000000..7d0fc27d2 Binary files /dev/null and b/ShareX.ScreenCaptureLib/Resources/flashlight-shine.png differ diff --git a/ShareX.ScreenCaptureLib/Shapes/ShapeManager.cs b/ShareX.ScreenCaptureLib/Shapes/ShapeManager.cs index 38d29d0c8..f9e095755 100644 --- a/ShareX.ScreenCaptureLib/Shapes/ShapeManager.cs +++ b/ShareX.ScreenCaptureLib/Shapes/ShapeManager.cs @@ -1223,6 +1223,9 @@ namespace ShareX.ScreenCaptureLib case ShapeType.EffectHighlight: shape = new HighlightEffectShape(); break; + case ShapeType.ToolSpotlight: + shape = new SpotlightTool(); + break; case ShapeType.ToolCrop: shape = new CropTool(); break; @@ -1963,6 +1966,13 @@ namespace ShareX.ScreenCaptureLib } } + public void SpotlightArea(RectangleF rect, int dim, int blur) + { + history.CreateCanvasMemento(); + Bitmap bmp = Spotlight(rect.Round(), dim, blur); + UpdateCanvas(bmp); + } + public Bitmap CropImage(RectangleF rect, bool onlyIfSizeDifferent = false) { rect = CaptureHelpers.ScreenToClient(rect.Round()); @@ -1993,15 +2003,50 @@ namespace ShareX.ScreenCaptureLib if (isHorizontal && cropRect.Width > 0) { CollapseAllHorizontal(rect.X, rect.Width); - UpdateCanvas(ImageHelpers.CutOutBitmapMiddle(Form.Canvas, Orientation.Horizontal, cropRect.X, cropRect.Width, AnnotationOptions.CutOutEffectType, AnnotationOptions.CutOutEffectSize, AnnotationOptions.CutOutBackgroundColor)); + UpdateCanvas(ImageHelpers.CutOutBitmapMiddle(Form.Canvas, Orientation.Horizontal, cropRect.X, cropRect.Width, + AnnotationOptions.CutOutEffectType, AnnotationOptions.CutOutEffectSize, AnnotationOptions.CutOutBackgroundColor)); } else if (!isHorizontal && cropRect.Height > 0) { CollapseAllVertical(rect.Y, rect.Height); - UpdateCanvas(ImageHelpers.CutOutBitmapMiddle(Form.Canvas, Orientation.Vertical, cropRect.Y, cropRect.Height, AnnotationOptions.CutOutEffectType, AnnotationOptions.CutOutEffectSize, AnnotationOptions.CutOutBackgroundColor)); + UpdateCanvas(ImageHelpers.CutOutBitmapMiddle(Form.Canvas, Orientation.Vertical, cropRect.Y, cropRect.Height, + AnnotationOptions.CutOutEffectType, AnnotationOptions.CutOutEffectSize, AnnotationOptions.CutOutBackgroundColor)); } } + public Bitmap Spotlight(Rectangle rect, int dim, int blur) + { + Bitmap bmp = (Bitmap)Form.Canvas.Clone(); + + if (dim > 0) + { + float value = 1f - dim / 100f; + Bitmap bmpDimmed = ColorMatrixManager.Contrast(value).Apply(bmp); + bmp.Dispose(); + bmp = bmpDimmed; + } + + if (blur > 0) + { + ImageHelpers.BoxBlur(bmp, blur); + } + + using (Graphics g = Graphics.FromImage(bmp)) + { + Bitmap selection = CropImage(rect); + + Rectangle adjustedRect = CaptureHelpers.ScreenToClient(rect); + Point 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); + + g.DrawImage(selection, cropRect); + } + + return bmp; + } + public Color GetColor(Bitmap bmp, Point pos) { if (bmp != null) diff --git a/ShareX.ScreenCaptureLib/Shapes/ShapeManagerMenu.cs b/ShareX.ScreenCaptureLib/Shapes/ShapeManagerMenu.cs index e19d606dd..d7d5f7077 100644 --- a/ShareX.ScreenCaptureLib/Shapes/ShapeManagerMenu.cs +++ b/ShareX.ScreenCaptureLib/Shapes/ShapeManagerMenu.cs @@ -221,7 +221,7 @@ namespace ShareX.ScreenCaptureLib { tsMain.Items.Add(new ToolStripSeparator()); } - else if (shapeType == ShapeType.ToolCrop || shapeType == ShapeType.ToolCutOut) + else if (shapeType == ShapeType.ToolCrop || shapeType == ShapeType.ToolCutOut || shapeType == ShapeType.ToolSpotlight) { continue; } @@ -306,6 +306,9 @@ namespace ShareX.ScreenCaptureLib case ShapeType.EffectHighlight: img = Resources.highlighter_text; break; + case ShapeType.ToolSpotlight: + img = Resources.flashlight_shine; + break; case ShapeType.ToolCrop: img = Resources.image_crop; break; diff --git a/ShareX.ScreenCaptureLib/Shapes/Tool/CropTool.cs b/ShareX.ScreenCaptureLib/Shapes/Tool/CropTool.cs index b93aa2930..91085d611 100644 --- a/ShareX.ScreenCaptureLib/Shapes/Tool/CropTool.cs +++ b/ShareX.ScreenCaptureLib/Shapes/Tool/CropTool.cs @@ -36,7 +36,7 @@ namespace ShareX.ScreenCaptureLib public override bool LimitRectangleToInsideCanvas { get; } = true; private ImageEditorButton confirmButton, cancelButton; - private Size buttonSize = new Size(80, 40); + private Size buttonSize = new Size(50, 40); private int buttonOffset = 15; public override void OnUpdate() @@ -78,7 +78,8 @@ namespace ShareX.ScreenCaptureLib confirmButton = new ImageEditorButton() { Text = "\u2714", - ButtonColor = Color.ForestGreen, + ButtonColor = ShareXResources.Theme.LightBackgroundColor, + IconColor = Color.ForestGreen, Rectangle = new Rectangle(new Point(), buttonSize), Visible = true }; @@ -90,7 +91,8 @@ namespace ShareX.ScreenCaptureLib cancelButton = new ImageEditorButton() { Text = "\u2716", - ButtonColor = Color.FromArgb(227, 45, 45), + ButtonColor = ShareXResources.Theme.LightBackgroundColor, + IconColor = Color.FromArgb(227, 45, 45), Rectangle = new Rectangle(new Point(), buttonSize), Visible = true }; diff --git a/ShareX.ScreenCaptureLib/Shapes/Tool/CutOutTool.cs b/ShareX.ScreenCaptureLib/Shapes/Tool/CutOutTool.cs index a448d02f8..8d9a42c7b 100644 --- a/ShareX.ScreenCaptureLib/Shapes/Tool/CutOutTool.cs +++ b/ShareX.ScreenCaptureLib/Shapes/Tool/CutOutTool.cs @@ -69,7 +69,7 @@ namespace ShareX.ScreenCaptureLib } private ImageEditorButton confirmButton, cancelButton; - private Size buttonSize = new Size(80, 40); + private Size buttonSize = new Size(50, 40); private int buttonOffset = 15; public override void ShowNodes() @@ -149,7 +149,8 @@ namespace ShareX.ScreenCaptureLib confirmButton = new ImageEditorButton() { Text = "\u2714", - ButtonColor = Color.ForestGreen, + ButtonColor = ShareXResources.Theme.LightBackgroundColor, + IconColor = Color.ForestGreen, Rectangle = new Rectangle(new Point(), buttonSize), Visible = true }; @@ -161,7 +162,8 @@ namespace ShareX.ScreenCaptureLib cancelButton = new ImageEditorButton() { Text = "\u2716", - ButtonColor = Color.FromArgb(227, 45, 45), + ButtonColor = ShareXResources.Theme.LightBackgroundColor, + IconColor = Color.FromArgb(227, 45, 45), Rectangle = new Rectangle(new Point(), buttonSize), Visible = true }; diff --git a/ShareX.ScreenCaptureLib/Shapes/Tool/SpotlightTool.cs b/ShareX.ScreenCaptureLib/Shapes/Tool/SpotlightTool.cs new file mode 100644 index 000000000..422297872 --- /dev/null +++ b/ShareX.ScreenCaptureLib/Shapes/Tool/SpotlightTool.cs @@ -0,0 +1,148 @@ +#region License Information (GPL v3) + +/* + ShareX - A program that allows you to take screenshots and share any file type + Copyright (c) 2007-2025 ShareX Team + + 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 . +*/ + +#endregion License Information (GPL v3) + +using ShareX.HelpersLib; +using System.Drawing; +using System.Windows.Forms; + +namespace ShareX.ScreenCaptureLib +{ + public class SpotlightTool : BaseTool + { + public override ShapeType ShapeType { get; } = ShapeType.ToolSpotlight; + + public override bool LimitRectangleToInsideCanvas { get; } = true; + public int Dim { get; set; } = 30; + public int Blur { get; set; } = 10; + + private ImageEditorButton confirmButton, cancelButton; + private Size buttonSize = new Size(50, 40); + private int buttonOffset = 15; + + public override void OnUpdate() + { + base.OnUpdate(); + + if (confirmButton != null && cancelButton != null) + { + if (Rectangle.Bottom + buttonOffset + buttonSize.Height > Manager.Form.ClientArea.Bottom && + Rectangle.Width > (buttonSize.Width * 2) + (buttonOffset * 3) && + Rectangle.Height > buttonSize.Height + (buttonOffset * 2)) + { + confirmButton.Rectangle = new RectangleF(Rectangle.Right - (buttonOffset * 2) - (buttonSize.Width * 2), + Rectangle.Bottom - buttonOffset - buttonSize.Height, buttonSize.Width, buttonSize.Height); + cancelButton.Rectangle = new RectangleF(Rectangle.Right - buttonOffset - buttonSize.Width, + Rectangle.Bottom - buttonOffset - buttonSize.Height, buttonSize.Width, buttonSize.Height); + } + else + { + confirmButton.Rectangle = new RectangleF(Rectangle.Right - (buttonSize.Width * 2) - buttonOffset, + Rectangle.Bottom + buttonOffset, buttonSize.Width, buttonSize.Height); + cancelButton.Rectangle = new RectangleF(Rectangle.Right - buttonSize.Width, + Rectangle.Bottom + buttonOffset, buttonSize.Width, buttonSize.Height); + } + } + } + + public override void OnDraw(Graphics g) + { + if (IsValidShape) + { + Manager.DrawRegionArea(g, Rectangle, true, Manager.Options.ShowInfo); + g.DrawCross(Pens.Black, Rectangle.Center(), 10); + } + } + + public override void OnCreated() + { + confirmButton = new ImageEditorButton() + { + Text = "\u2714", + ButtonColor = ShareXResources.Theme.LightBackgroundColor, + IconColor = Color.ForestGreen, + Rectangle = new Rectangle(new Point(), buttonSize), + Visible = true + }; + confirmButton.MouseDown += ConfirmButton_MousePressed; + confirmButton.MouseEnter += () => Manager.Form.Cursor = Cursors.Hand; + confirmButton.MouseLeave += () => Manager.Form.SetDefaultCursor(); + Manager.DrawableObjects.Add(confirmButton); + + cancelButton = new ImageEditorButton() + { + Text = "\u2716", + ButtonColor = ShareXResources.Theme.LightBackgroundColor, + IconColor = Color.FromArgb(227, 45, 45), + Rectangle = new Rectangle(new Point(), buttonSize), + Visible = true + }; + cancelButton.MouseDown += CancelButton_MousePressed; + cancelButton.MouseEnter += () => Manager.Form.Cursor = Cursors.Hand; + cancelButton.MouseLeave += () => Manager.Form.SetDefaultCursor(); + Manager.DrawableObjects.Add(cancelButton); + } + + private void ConfirmButton_MousePressed(object sender, MouseEventArgs e) + { + Manager.SpotlightArea(Rectangle, Dim, Blur); + Remove(); + } + + private void CancelButton_MousePressed(object sender, MouseEventArgs e) + { + Remove(); + } + + public override void Remove() + { + base.Remove(); + + if (Options.SwitchToSelectionToolAfterDrawing) + { + Manager.CurrentTool = ShapeType.ToolSelect; + } + } + + public override void Dispose() + { + base.Dispose(); + + if ((confirmButton != null && confirmButton.IsCursorHover) || (cancelButton != null && cancelButton.IsCursorHover)) + { + Manager.Form.SetDefaultCursor(); + } + + if (confirmButton != null) + { + Manager.DrawableObjects.Remove(confirmButton); + } + + if (cancelButton != null) + { + Manager.DrawableObjects.Remove(cancelButton); + } + } + } +} \ No newline at end of file