C# 截图源码支持自动保存-东方仙盟

复制代码
/*
============================================
FairyAlliance Snap 软件说明
版本:NET Framework 4.5
兼容系统:Windows7 / Windows10 / Windows11
作者:东方仙盟
配置文件:FairyAllianceSnap.config.ini(放在exe同目录)
============================================
功能简介:
1. 托盘后台运行全局截图工具,支持全局快捷键,快捷键最多支持2个修饰键(Ctrl/Shift/Alt)+主键
2. 截图规则:
   默认保存目录:程序目录\FairyAllianceSnap\yyyy-MM(按月自动新建文件夹)
   可自定义保存目录,自定义目录优先级高于默认目录
   文件命名:snap_年月日时分秒_前台窗口标题.png,自动清理非法字符,支持外文系统不乱码
3. 标注工具(截图后弹出):
   矩形框、圆角矩形、椭圆、铅笔自由手绘、文字标注
   支持:线条粗细选择、拾色器、字体/字号选择、撤销上一步
   标注窗口两个保存按钮:
      ✅【对勾】:自动保存到配置目录,直接归档
      💾【保存】:弹出窗口,自选路径另存图片
4. 设置项【快捷键模式直接保存】:
   勾选后,按下快捷键截图会直接保存图片,**不会弹出标注窗口,不能绘制标注**
   不勾选:快捷键截图同样弹出标注界面,可编辑标注
   【托盘菜单手动截图】永远弹出标注界面,不受这个选项控制
5. 热键冲突检测,若快捷键被占用会弹出提示
6. 配置持久化:所有设置保存至 FairyAllianceSnap.config.ini,重启程序自动加载
============================================
*/

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace FairyAllianceSnap
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var app = new SnapApp();
            Application.Run();
        }
    }

    /// <summary>INI读写辅助类,Win32原生API</summary>
    public static class IniHelper
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);

        private const string IniFileName = "FairyAllianceSnap.config.ini";
        public static string IniPath => Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), IniFileName);
        private const string Section = "Setting";

        public static void Write(string key, string value)
        {
            WritePrivateProfileString(Section, key, value, IniPath);
        }
        public static string Read(string key, string defVal = "")
        {
            StringBuilder sb = new StringBuilder(256);
            GetPrivateProfileString(Section, key, defVal, sb, sb.Capacity, IniPath);
            return sb.ToString();
        }
    }

    public class SnapApp
    {
        private NotifyIcon _trayIcon;
        private ContextMenuStrip _trayMenu;
        private string _customSavePath = string.Empty;

        // 热键配置
        private Keys _hotKeyMain = Keys.F8;
        private HotkeyModifiers _hotKeyMod = HotkeyModifiers.None;
        private readonly int _hotKeyId = 9001;
        private bool _hotKeyRegistered = false;
        private MessageWindow _msgWindow;

        // 快捷键直接保存(跳过标注)
        private bool _hotkeyDirectSave = false;

        [Flags]
        public enum HotkeyModifiers
        {
            None = 0,
            Alt = 0x0001,
            Ctrl = 0x0002,
            Shift = 0x0004
        }

        #region Win32 API
        [DllImport("user32.dll")]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
        [DllImport("user32.dll")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count);
        [DllImport("user32.dll")]
        private static extern int GetWindowTextLength(IntPtr hWnd);
        #endregion

        public SnapApp()
        {
            // 启动加载INI配置
            LoadConfig();

            _trayIcon = new NotifyIcon();
            _trayIcon.Icon = SystemIcons.Application;
            _trayIcon.Text = "FairyAlliance Snap Tool";
            _trayIcon.Visible = true;

            _trayMenu = new ContextMenuStrip();
            _trayMenu.Items.Add("Start Capture / 开始截图", null, (s, e) => StartSelectCapture(false));
            _trayMenu.Items.Add("Settings / 设置", null, (s, e) => ShowSettingDialog());
            _trayMenu.Items.Add("Exit / 退出", null, (s, e) => ExitApp());
            _trayIcon.ContextMenuStrip = _trayMenu;

            _msgWindow = new MessageWindow();
            _msgWindow.HotKeyPressed += () => StartSelectCapture(_hotkeyDirectSave);
            ReRegisterHotkey();
        }

        /// <summary>读取ini配置</summary>
        private void LoadConfig()
        {
            _customSavePath = IniHelper.Read("CustomSavePath");
            string modStr = IniHelper.Read("HotkeyMod", "0");
            _hotKeyMod = (HotkeyModifiers)int.Parse(modStr);
            string mainKeyStr = IniHelper.Read("HotkeyMain", "F8");
            Enum.TryParse(mainKeyStr, out _hotKeyMain);
            string directSaveStr = IniHelper.Read("HotkeyDirectSave", "false");
            bool.TryParse(directSaveStr, out _hotkeyDirectSave);
        }

        /// <summary>保存配置写入ini</summary>
        private void SaveConfig()
        {
            IniHelper.Write("CustomSavePath", _customSavePath);
            IniHelper.Write("HotkeyMod", ((int)_hotKeyMod).ToString());
            IniHelper.Write("HotkeyMain", _hotKeyMain.ToString());
            IniHelper.Write("HotkeyDirectSave", _hotkeyDirectSave.ToString().ToLower());
        }

        private bool ReRegisterHotkey()
        {
            if (_hotKeyRegistered)
            {
                UnregisterHotKey(_msgWindow.Handle, _hotKeyId);
                _hotKeyRegistered = false;
            }
            uint mod = (uint)_hotKeyMod;
            uint key = (uint)_hotKeyMain;
            bool ok = RegisterHotKey(_msgWindow.Handle, _hotKeyId, mod, key);
            if (ok)
                _hotKeyRegistered = true;
            else
                MessageBox.Show("Hotkey register failed, key already occupied.\n热键注册失败,快捷键已被占用。");
            return ok;
        }

        /// <summary>获取前台窗口标题,清洗非法字符</summary>
        private string GetActiveWindowTitle()
        {
            IntPtr hwnd = GetForegroundWindow();
            int len = GetWindowTextLength(hwnd);
            if (len <= 0) return "NoTitle";
            var sb = new System.Text.StringBuilder(len + 1);
            GetWindowText(hwnd, sb, sb.Capacity);
            string title = sb.ToString();
            char[] invalid = Path.GetInvalidFileNameChars();
            foreach (var c in invalid)
                title = title.Replace(c, '_');
            return title;
        }

        /// <summary>获取最终保存目录</summary>
        private string GetFinalSaveDirectory()
        {
            if (!string.IsNullOrWhiteSpace(_customSavePath))
            {
                string dir = _customSavePath.Trim();
                if (!dir.EndsWith(Path.DirectorySeparatorChar.ToString()))
                    dir += Path.DirectorySeparatorChar;
                return dir;
            }
            string exeDir = Path.GetDirectoryName(Application.ExecutablePath);
            string baseFolder = Path.Combine(exeDir, "FairyAllianceSnap");
            string monthFolder = DateTime.Now.ToString("yyyy-MM");
            string fullDir = Path.Combine(baseFolder, monthFolder);
            Directory.CreateDirectory(fullDir);
            return fullDir + Path.DirectorySeparatorChar;
        }

        private string BuildFileName()
        {
            string title = GetActiveWindowTitle();
            string timeStr = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            string fileName = $"snap_{timeStr}_{title}.png";
            string saveDir = GetFinalSaveDirectory();
            Directory.CreateDirectory(saveDir);
            return Path.Combine(saveDir, fileName);
        }

        /// <param name="directSave">true=直接保存跳过标注;false=弹出标注窗口</param>
        private void StartSelectCapture(bool directSave)
        {
            var selectorForm = new SelectorForm();
            if (selectorForm.ShowDialog() == DialogResult.OK)
            {
                Rectangle rect = selectorForm.SelectedRect;
                using (Bitmap bmp = new Bitmap(rect.Width, rect.Height))
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    g.CopyFromScreen(rect.Left, rect.Top, 0, 0, rect.Size);
                }

                if (directSave)
                {
                    //快捷键直接保存,跳过标注
                    string fullFile = BuildFileName();
                    bmp.Save(fullFile, System.Drawing.Imaging.ImageFormat.Png);
                    bmp.Dispose();
                }
                else
                {
                    //弹出标注窗口
                    using (var annotateForm = new AnnotateForm(bmp))
                    {
                        var res = annotateForm.ShowDialog();
                        if (res == DialogResult.OK)
                        {
                            //对勾:自动归档保存
                            string fullFile = BuildFileName();
                            annotateForm.ResultBitmap.Save(fullFile, System.Drawing.Imaging.ImageFormat.Png);
                        }
                        else if (res == DialogResult.Yes)
                        {
                            //另存为:用户自选路径
                            using (SaveFileDialog sfd = new SaveFileDialog())
                            {
                                sfd.Filter = "PNG图片(*.png)|*.png";
                                sfd.FileName = Path.GetFileName(BuildFileName());
                                if (sfd.ShowDialog() == DialogResult.OK)
                                {
                                    annotateForm.ResultBitmap.Save(sfd.FileName, System.Drawing.Imaging.ImageFormat.Png);
                                }
                            }
                        }
                    }
                    bmp.Dispose();
                }
            }
        }

        private void ShowSettingDialog()
        {
            using (var frm = new Form())
            {
                frm.Text = "Settings - FairyAlliance Snap";
                frm.Size = new Size(600, 300);
                frm.StartPosition = FormStartPosition.CenterScreen;

                var lblPath = new Label
                {
                    Text = "Custom Save Path (Empty = use FairyAllianceSnap\\yyyy-MM):",
                    Location = new Point(20, 12),
                    AutoSize = true
                };
                var txtPath = new TextBox
                {
                    Text = _customSavePath,
                    Location = new Point(20, 35),
                    Width = 540
                };

                var lblHotkey = new Label
                {
                    Text = "Global Hotkey (Max 2 modifiers):",
                    Location = new Point(20, 70),
                    AutoSize = true
                };

                var chkCtrl = new CheckBox { Text = "Ctrl", Location = new Point(20, 95), Checked = (_hotKeyMod & HotkeyModifiers.Ctrl) != 0 };
                var chkShift = new CheckBox { Text = "Shift", Location = new Point(80, 95), Checked = (_hotKeyMod & HotkeyModifiers.Shift) != 0 };
                var chkAlt = new CheckBox { Text = "Alt", Location = new Point(140, 95), Checked = (_hotKeyMod & HotkeyModifiers.Alt) != 0 };

                //互斥:最多勾选2个修饰键
                Action checkMutual = () =>
                {
                    int count = 0;
                    if (chkCtrl.Checked) count++;
                    if (chkShift.Checked) count++;
                    if (chkAlt.Checked) count++;
                    if (count > 2)
                    {
                        MessageBox.Show("Cannot select more than 2 modifiers!\n最多只能选择2个修饰键");
                    }
                };
                chkCtrl.CheckedChanged += (s, e) => checkMutual();
                chkShift.CheckedChanged += (s, e) => checkMutual();
                chkAlt.CheckedChanged += (s, e) => checkMutual();

                var cboKey = new ComboBox { Location = new Point(200, 92), Width = 120, DropDownStyle = ComboBoxStyle.DropDownList };
                cboKey.Items.AddRange(new object[] { Keys.F7, Keys.F8, Keys.F9, Keys.F10, Keys.F11 });
                cboKey.SelectedItem = _hotKeyMain;

                //新增:快捷键直接保存选项 + 备注
                var chkHotDirectSave = new CheckBox
                {
                    Text = "快捷键模式直接保存",
                    Location = new Point(20, 130),
                    Checked = _hotkeyDirectSave
                };
                var lblTipDirect = new Label
                {
                    Text = "备注:开启后截图不会弹出标注窗口,无法进行标注绘制",
                    Location = new Point(40, 155),
                    ForeColor = Color.DarkRed,
                    AutoSize = true
                };

                var btnOk = new Button
                {
                    Text = "OK",
                    Location = new Point(20, 190),
                    Width = 100
                };
                btnOk.Click += (s, e) =>
                {
                    int cnt = 0;
                    HotkeyModifiers mod = HotkeyModifiers.None;
                    if (chkCtrl.Checked) { mod |= HotkeyModifiers.Ctrl; cnt++; }
                    if (chkShift.Checked) { mod |= HotkeyModifiers.Shift; cnt++; }
                    if (chkAlt.Checked) { mod |= HotkeyModifiers.Alt; cnt++; }
                    if (cnt > 2)
                    {
                        MessageBox.Show("Too many modifiers! Max 2.\n修饰键不能超过2个");
                        return;
                    }
                    _customSavePath = txtPath.Text.Trim();
                    _hotKeyMod = mod;
                    _hotKeyMain = (Keys)cboKey.SelectedItem;
                    _hotkeyDirectSave = chkHotDirectSave.Checked;

                    //保存写入ini
                    SaveConfig();
                    ReRegisterHotkey();
                    frm.DialogResult = DialogResult.OK;
                    frm.Close();
                };
                frm.Controls.AddRange(new Control[] { lblPath, txtPath, lblHotkey, chkCtrl, chkShift, chkAlt, cboKey, chkHotDirectSave, lblTipDirect, btnOk });
                frm.ShowDialog();
            }
        }

        private void ExitApp()
        {
            if (_hotKeyRegistered)
                UnregisterHotKey(_msgWindow.Handle, _hotKeyId);
            _trayIcon.Visible = false;
            Application.Exit();
        }
    }

    /// <summary>接收全局热键消息的隐藏窗口</summary>
    public class MessageWindow : Form
    {
        public Action HotKeyPressed;
        public MessageWindow()
        {
            this.ShowInTaskbar = false;
            this.WindowState = FormWindowState.Minimized;
            this.Visible = false;
        }
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x0312) // WM_HOTKEY
            {
                HotKeyPressed?.Invoke();
            }
            base.WndProc(ref m);
        }
    }

    /// <summary>全屏选区截图窗口</summary>
    public class SelectorForm : Form
    {
        public Rectangle SelectedRect { get; private set; }
        private Point _startPos;
        private bool _isDragging;

        public SelectorForm()
        {
            this.FormBorderStyle = FormBorderStyle.None;
            this.WindowState = FormWindowState.Maximized;
            this.TopMost = true;
            this.BackColor = Color.FromArgb(80, Color.Black);
            this.Opacity = 0.6;
            this.Cursor = Cursors.Cross;
            this.DoubleBuffered = true;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (e.Button == MouseButtons.Left)
            {
                _isDragging = true;
                _startPos = e.Location;
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (_isDragging)
            {
                SelectedRect = GetRect(_startPos, e.Location);
                this.Invalidate();
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (e.Button == MouseButtons.Left && _isDragging)
            {
                _isDragging = false;
                if (SelectedRect.Width > 5 && SelectedRect.Height > 5)
                    this.DialogResult = DialogResult.OK;
                this.Close();
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (_isDragging)
            {
                using (Pen pen = new Pen(Color.Red, 2))
                {
                    e.Graphics.DrawRectangle(pen, SelectedRect);
                }
            }
        }

        private Rectangle GetRect(Point p1, Point p2)
        {
            int x = Math.Min(p1.X, p2.X);
            int y = Math.Min(p1.Y, p2.Y);
            int w = Math.Abs(p2.X - p1.X);
            int h = Math.Abs(p2.Y - p1.Y);
            return new Rectangle(x, y, w, h);
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (keyData == Keys.Escape)
            {
                this.DialogResult = DialogResult.Cancel;
                this.Close();
                return true;
            }
            return base.ProcessCmdKey(ref msg, keyData);
        }
    }

    #region 标注画布窗口
    public enum AnnotateTool
    {
        Rect,
        RoundedRect,
        Ellipse,
        Pencil,
        Text
    }

    public class AnnotateForm : Form
    {
        public Bitmap ResultBitmap { get; private set; }
        private Bitmap _originBmp;
        private Bitmap _workBmp;
        private Graphics _g;

        private AnnotateTool _currentTool = AnnotateTool.Rect;
        private Color _drawColor = Color.Red;
        private int _lineWidth = 2;
        private Font _textFont = new Font("Microsoft Sans Serif", 12);

        private Point _startPt;
        private bool _isDrawing;
        private List<Point> _pencilPath = new List<Point>();
        private Bitmap _undoBackup;

        private PictureBox _picBox;

        public AnnotateForm(Bitmap srcBmp)
        {
            _originBmp = new Bitmap(srcBmp);
            _workBmp = new Bitmap(_originBmp);
            _g = Graphics.FromImage(_workBmp);
            _g.SmoothingMode = SmoothingMode.AntiAlias;
            _undoBackup = new Bitmap(_workBmp);

            this.Text = "Annotate - FairyAlliance Snap";
            this.Size = new Size(Math.Min(_workBmp.Width + 100, 1200), Math.Min(_workBmp.Height + 160, 800));
            this.StartPosition = FormStartPosition.CenterScreen;

            //顶部工具栏
            var panelTool = new Panel { Dock = DockStyle.Top, Height = 60 };
            var btnRect = new Button { Text = "矩形", Location = new Point(10, 10), Width = 70 };
            btnRect.Click += (s, e) => _currentTool = AnnotateTool.Rect;
            var btnRoundRect = new Button { Text = "圆角矩形", Location = new Point(85, 10), Width = 70 };
            btnRoundRect.Click += (s, e) => _currentTool = AnnotateTool.RoundedRect;
            var btnEllipse = new Button { Text = "椭圆", Location = new Point(160, 10), Width = 70 };
            btnEllipse.Click += (s, e) => _currentTool = AnnotateTool.Ellipse;
            var btnPencil = new Button { Text = "铅笔", Location = new Point(235, 10), Width = 70 };
            btnPencil.Click += (s, e) => _currentTool = AnnotateTool.Pencil;
            var btnText = new Button { Text = "文字", Location = new Point(310, 10), Width = 70 };
            btnText.Click += (s, e) => _currentTool = AnnotateTool.Text;

            var cboWidth = new ComboBox { Location = new Point(390, 10), Width = 70, DropDownStyle = ComboBoxStyle.DropDownList };
            cboWidth.Items.AddRange(new object[] { 1, 2, 3, 5 });
            cboWidth.SelectedItem = 2;
            cboWidth.SelectedIndexChanged += (s, e) => _lineWidth = (int)cboWidth.SelectedItem;

            var btnColor = new Button { Text = "颜色", Location = new Point(470, 10), Width = 70 };
            btnColor.Click += (s, e) =>
            {
                using (var cd = new ColorDialog())
                {
                    if (cd.ShowDialog() == DialogResult.OK)
                        _drawColor = cd.Color;
                }
            };

            var btnFont = new Button { Text = "字体", Location = new Point(545, 10), Width = 70 };
            btnFont.Click += (s, e) =>
            {
                using (var fd = new FontDialog())
                {
                    fd.Font = _textFont;
                    if (fd.ShowDialog() == DialogResult.OK)
                        _textFont = fd.Font;
                }
            };

            var btnUndo = new Button { Text = "撤销", Location = new Point(620, 10), Width = 70 };
            btnUndo.Click += (s, e) =>
            {
                var temp = new Bitmap(_undoBackup);
                _g.Dispose();
                _workBmp.Dispose();
                _workBmp = temp;
                _g = Graphics.FromImage(_workBmp);
                _g.SmoothingMode = SmoothingMode.AntiAlias;
                _picBox.Image = _workBmp;
            };

            //新增两个按钮:对勾自动保存、另存为
            var btnOkAutoSave = new Button { Text = "✓ 对勾", Location = new Point(695, 10), Width = 70 };
            btnOkAutoSave.Click += (s, e) =>
            {
                ResultBitmap = new Bitmap(_workBmp);
                this.DialogResult = DialogResult.OK;
                this.Close();
            };
            var btnSaveAs = new Button { Text = "💾保存", Location = new Point(770, 10), Width = 70 };
            btnSaveAs.Click += (s, e) =>
            {
                ResultBitmap = new Bitmap(_workBmp);
                this.DialogResult = DialogResult.Yes;
                this.Close();
            };

            var btnCancel = new Button { Text = "取消", Location = new Point(845, 10), Width = 70 };
            btnCancel.Click += (s, e) => this.DialogResult = DialogResult.Cancel;

            panelTool.Controls.AddRange(new Control[] { btnRect, btnRoundRect, btnEllipse, btnPencil, btnText, cboWidth, btnColor, btnFont, btnUndo, btnOkAutoSave, btnSaveAs, btnCancel });

            _picBox = new PictureBox
            {
                Dock = DockStyle.Fill,
                Image = _workBmp,
                SizeMode = PictureBoxSizeMode.AutoSize
            };
            _picBox.MouseDown += PicBox_MouseDown;
            _picBox.MouseMove += PicBox_MouseMove;
            _picBox.MouseUp += PicBox_MouseUp;

            this.Controls.Add(_picBox);
            this.Controls.Add(panelTool);
        }

        private void SaveUndo()
        {
            _undoBackup.Dispose();
            _undoBackup = new Bitmap(_workBmp);
        }

        //自制文字输入窗口,移除Microsoft.VisualBasic依赖
        private string ShowTextInputDialog(string title, string defaultText)
        {
            using (Form inputForm = new Form())
            {
                inputForm.Text = title;
                inputForm.Size = new Size(320, 140);
                inputForm.StartPosition = FormStartPosition.CenterParent;
                TextBox txtInput = new TextBox { Text = defaultText, Location = new Point(20, 20), Width = 260 };
                Button btnConfirm = new Button { Text = "确定", Location = new Point(20, 60), Width = 100 };
                btnConfirm.Click += (s, e) => inputForm.DialogResult = DialogResult.OK;
                inputForm.Controls.AddRange(new Control[] { txtInput, btnConfirm });
                if (inputForm.ShowDialog() == DialogResult.OK)
                    return txtInput.Text.Trim();
                return null;
            }
        }

        private void PicBox_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;
            _startPt = e.Location;
            _isDrawing = true;
            if (_currentTool == AnnotateTool.Pencil)
            {
                _pencilPath.Clear();
                _pencilPath.Add(e.Location);
            }
            if (_currentTool == AnnotateTool.Text)
            {
                SaveUndo();
                string input = ShowTextInputDialog("输入文字", "");
                if (!string.IsNullOrEmpty(input))
                {
                    using (var brush = new SolidBrush(_drawColor))
                    {
                        _g.DrawString(input, _textFont, brush, e.X, e.Y);
                    }
                    _picBox.Image = _workBmp;
                }
                _isDrawing = false;
            }
        }

        private void PicBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_isDrawing) return;
            if (_currentTool == AnnotateTool.Pencil)
            {
                _pencilPath.Add(e.Location);
                using (var pen = new Pen(_drawColor, _lineWidth))
                {
                    if (_pencilPath.Count >= 2)
                        _g.DrawLines(pen, _pencilPath.ToArray());
                }
                _picBox.Image = _workBmp;
            }
        }

        private void PicBox_MouseUp(object sender, MouseEventArgs e)
        {
            if (!_isDrawing) return;
            _isDrawing = false;
            SaveUndo();
            Rectangle rect = GetRect(_startPt, e.Location);
            using (var pen = new Pen(_drawColor, _lineWidth))
            {
                switch (_currentTool)
                {
                    case AnnotateTool.Rect:
                        _g.DrawRectangle(pen, rect);
                        break;
                    case AnnotateTool.RoundedRect:
                        using (GraphicsPath gp = new GraphicsPath())
                        {
                            int r = Math.Min(rect.Width, rect.Height) / 6;
                            gp.AddArc(rect.X, rect.Y, r, r, 180, 90);
                            gp.AddArc(rect.Right - r, rect.Y, r, r, 270, 90);
                            gp.AddArc(rect.Right - r, rect.Bottom - r, r, r, 0, 90);
                            gp.AddArc(rect.X, rect.Bottom - r, r, r, 90, 90);
                            gp.CloseAllFigures();
                            _g.DrawPath(pen, gp);
                        }
                        break;
                    case AnnotateTool.Ellipse:
                        _g.DrawEllipse(pen, rect);
                        break;
                }
            }
            _picBox.Image = _workBmp;
        }

        private Rectangle GetRect(Point p1, Point p2)
        {
            int x = Math.Min(p1.X, p2.X);
            int y = Math.Min(p1.Y, p2.Y);
            int w = Math.Abs(p2.X - p1.X);
            int h = Math.Abs(p2.Y - p1.Y);
            return new Rectangle(x, y, w, h);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _g?.Dispose();
                _workBmp?.Dispose();
                _originBmp?.Dispose();
                _undoBackup?.Dispose();
                _textFont?.Dispose();
            }
            base.Dispose(disposing);
        }
    }
    #endregion
}

人人皆为创造者,共创方能共成长

每个人都是使用者,也是创造者;是数字世界的消费者,更是价值的生产者与分享者。在智能时代的浪潮里,单打独斗的发展模式早已落幕,唯有开放连接、创意共创、利益共享,才能让个体价值汇聚成生态合力,让技术与创意双向奔赴,实现平台与伙伴的快速成长、共赢致远。

原创永久分成,共赴星辰大海

原创创意共创、永久收益分成,是东方仙盟始终坚守的核心理念。我们坚信,每一份原创智慧都值得被尊重与回馈,以永久分成锚定共创初心,让创意者长期享有价值红利,携手万千伙伴向着科技星辰大海笃定前行,拥抱硅基 生命与数字智能交融的未来,共筑跨越时代的数字文明共同体。

东方仙盟:拥抱知识开源,共筑数字新生态

在全球化与数字化浪潮中,东方仙盟始终秉持开放协作、知识共享的理念,积极拥抱开源技术与开放标准。我们相信,唯有打破技术壁垒、汇聚全球智慧,才能真正推动行业的可持续发展。

开源赋能中小商户:通过将前端异常检测、跨系统数据互联等核心能力开源化,东方仙盟为全球中小商户提供了低成本、高可靠的技术解决方案,让更多商家能够平等享受数字转型的红利。

共建行业标准:我们积极参与国际技术社区,与全球开发者、合作伙伴共同制定开放协议 与技术规范,推动跨境零售、文旅、餐饮等多业态的系统互联互通,构建更加公平、高效的数字生态。

知识普惠,共促发展:通过开源社区 、技术文档与培训体系,东方仙盟致力于将前沿技术转化为可落地的行业实践,赋能全球合作伙伴,共同培育创新人才,推动数字经济 的普惠式增长

阿雪技术观

在科技发展浪潮中,我们不妨积极投身技术共享。不满足于做受益者,更要主动担当贡献者 。无论是分享代码、撰写技术博客,还是参与开源项目 维护改进,每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地,我们携手在此探索硅基 生命,为科技进步添砖加瓦。

Hey folks, in this wild tech - driven world, why not dive headfirst into the whole tech - sharing scene? Don't just be the one reaping all the benefits; step up and be a contributor too. Whether you're tossing out your code snippets , hammering out some tech blogs, or getting your hands dirty with maintaining and sprucing up open - source projects, every little thing you do might just end up being a massive force that pushes tech forward. And guess what? The Eastern FairyAlliance is this awesome place where we all come together. We're gonna team up and explore the whole silicon - based life thing, and in the process, we'll be fueling the growth of technology

相关推荐
UIU11413 小时前
-7 / 2 在 C 里是 -3,在 Python 里是 -4,谁算错了?
c++·学习·c#
wflynn15 小时前
GitHub 今日推荐|REDox:64 位 token 表示结构化数据,内存占用降 70% 支持多格式互转
开源·c#·github
驰骋工作流16 小时前
C#/.NET 开源BPM工作流引擎6大常用对比选型分析驰骋BPM驰骋低代码低代码工作流引擎
开源·c#·.net
en.en..16 小时前
C语言 fp与fd 区别详解
c#
淡海水1 天前
02-02-原理篇-分代GC
算法·unity·c#·游戏引擎·.net·gc
BD_Marathon1 天前
多轮对话聊天机器人
开发语言·机器人·c#
未来之窗软件服务1 天前
[english][casierdesk]酒店收银系统,未来之窗酒店PMS管理-东方仙盟
酒店管理系统·仙盟创梦ide·东方仙盟
czhc11400756631 天前
设备标定页防呆为何失效:草稿与真值的错位
c#
周杰伦fans1 天前
轻量模型高并发推理优化技巧
开发语言·人工智能·c#