Added "Spotlight" tool to image editor

This commit is contained in:
Jaex 2025-09-28 12:41:35 +03:00
parent e26232d068
commit 874c7eee7f
13 changed files with 246 additions and 16 deletions

View File

@ -3245,6 +3245,15 @@ namespace ShareX.HelpersLib.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Spotlight.
/// </summary>
internal static string ShapeType_ToolSpotlight {
get {
return ResourceManager.GetString("ShapeType_ToolSpotlight", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>

View File

@ -1323,4 +1323,7 @@ Would you like to download and install it?</value>
<data name="HotkeyType_AnalyzeImage" xml:space="preserve">
<value>Analyze image</value>
</data>
<data name="ShapeType_ToolSpotlight" xml:space="preserve">
<value>Spotlight</value>
</data>
</root>

View File

@ -315,6 +315,7 @@ namespace ShareX.ScreenCaptureLib
EffectBlur,
EffectPixelate,
EffectHighlight,
ToolSpotlight,
ToolCrop,
ToolCutOut
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -666,6 +666,16 @@ namespace ShareX.ScreenCaptureLib.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap flashlight_shine {
get {
object obj = ResourceManager.GetObject("flashlight_shine", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View File

@ -857,4 +857,7 @@ Would you like to save the changes before closing the image editor?</value>
<data name="RulerBottom" xml:space="preserve">
<value>Bottom</value>
</data>
<data name="flashlight_shine" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\flashlight-shine.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

View File

@ -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)

View File

@ -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;

View File

@ -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
};

View File

@ -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
};

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}
}
}
}