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