c#给图片添加边框

发表博文的过程中,很多博客平台背景是纯白色的,而需要发表的截图也是纯白色,导致看不到边界,体验感不好。

此时可以借助工具将图片处理后再发表到博客,增强体验感。

 

原始图片:

 

处理后:

 

程序截图:

 

程序下载地址:https://pan.quark.cn/s/5208302ebc74

 

程序源码Form1.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ImageShadowTool
{
    public partial class MainForm : Form
    {
        private Panel topPanel;
        private Panel centerPanel;
        private Panel bottomPanel;
        private Button btnSelectFiles;
        private Button btnSelectFolder;
        private Button btnProcess;
        private Button btnClear;
        private ListBox listBoxFiles;
        private ProgressBar progressBar;
        private Label lblProgress;
        private NumericUpDown numShadowSize;
        private NumericUpDown numShadowOpacity;
        private Label lblShadowSize;
        private Label lblShadowOpacity;
        private Label lblStatus;
        private Label lblTitle;
        private Label lblFileCount;
        private List selectedFiles = new List();

        public MainForm()
        {
            InitializeComponent();
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();

            // 设置窗体属性
            this.Text = "图片阴影效果工具";
            this.Size = new Size(700, 600);
            this.StartPosition = FormStartPosition.CenterScreen;
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.BackColor = Color.FromArgb(245, 245, 245);
            this.Icon = CreateIcon();

            // 顶部面板
            topPanel = new Panel();
            topPanel.Dock = DockStyle.Top;
            topPanel.Height = 120;
            topPanel.BackColor = Color.FromArgb(52, 152, 219);
            topPanel.Paint += TopPanel_Paint;
            this.Controls.Add(topPanel);

            // 标题
            lblTitle = new Label();
            lblTitle.Text = "图片阴影效果工具";
            lblTitle.Font = new Font("微软雅黑", 16, FontStyle.Bold);
            lblTitle.ForeColor = Color.White;
            lblTitle.Location = new Point(30, 25);
            lblTitle.Size = new Size(200, 30);
            topPanel.Controls.Add(lblTitle);

            // 文件计数标签
            lblFileCount = new Label();
            lblFileCount.Text = "未选择文件";
            lblFileCount.Font = new Font("微软雅黑", 10);
            lblFileCount.ForeColor = Color.White;
            lblFileCount.Location = new Point(30, 65);
            lblFileCount.Size = new Size(200, 20);
            topPanel.Controls.Add(lblFileCount);

            // 阴影大小设置
            lblShadowSize = new Label();
            lblShadowSize.Text = "阴影大小(像素):";
            lblShadowSize.Font = new Font("微软雅黑", 9);
            lblShadowSize.ForeColor = Color.White;
            lblShadowSize.Location = new Point(400, 25);
            lblShadowSize.Size = new Size(100, 20);
            topPanel.Controls.Add(lblShadowSize);

            numShadowSize = new NumericUpDown();
            numShadowSize.Location = new Point(400, 50);
            numShadowSize.Size = new Size(80, 25);
            numShadowSize.Minimum = 5;
            numShadowSize.Maximum = 50;
            numShadowSize.Value = 5;
            numShadowSize.Font = new Font("微软雅黑", 9);
            topPanel.Controls.Add(numShadowSize);

            // 阴影透明度设置
            lblShadowOpacity = new Label();
            lblShadowOpacity.Text = "阴影透明度(%):";
            lblShadowOpacity.Font = new Font("微软雅黑", 9);
            lblShadowOpacity.ForeColor = Color.White;
            lblShadowOpacity.Location = new Point(520, 25);
            lblShadowOpacity.Size = new Size(100, 20);
            topPanel.Controls.Add(lblShadowOpacity);

            NumericUpDown numShadowOpacity = new NumericUpDown();
            numShadowOpacity.Location = new Point(520, 50);
            numShadowOpacity.Size = new Size(80, 25);
            numShadowOpacity.Minimum = 10;
            numShadowOpacity.Maximum = 100;
            numShadowOpacity.Value = 50;
            numShadowOpacity.Font = new Font("微软雅黑", 9);
            this.numShadowOpacity = numShadowOpacity;
            topPanel.Controls.Add(numShadowOpacity);

            // 中间区域 - 直接在主窗体上添加控件
            // 选择文件按钮
            btnSelectFiles = CreateStyledButton("选择图片文件", Color.FromArgb(46, 204, 113));
            btnSelectFiles.Location = new Point(20, 140);
            btnSelectFiles.Click += BtnSelectFiles_Click;
            this.Controls.Add(btnSelectFiles);

            // 选择文件夹按钮
            btnSelectFolder = CreateStyledButton("选择文件夹", Color.FromArgb(155, 89, 182));
            btnSelectFolder.Location = new Point(160, 140);
            btnSelectFolder.Click += BtnSelectFolder_Click;
            this.Controls.Add(btnSelectFolder);

            // 清空按钮
            btnClear = CreateStyledButton("清空列表", Color.FromArgb(231, 76, 60));
            btnClear.Location = new Point(300, 140);
            btnClear.Click += BtnClear_Click;
            this.Controls.Add(btnClear);

            // 处理按钮
            btnProcess = CreateStyledButton("开始处理", Color.FromArgb(230, 126, 34));
            btnProcess.Location = new Point(440, 140);
            btnProcess.Size = new Size(120, 35);
            btnProcess.Enabled = false;
            btnProcess.Click += BtnProcess_Click;
            this.Controls.Add(btnProcess);

            // 文件列表 - 增大字体
            listBoxFiles = new ListBox();
            listBoxFiles.Location = new Point(20, 190);
            listBoxFiles.Size = new Size(640, 300);
            listBoxFiles.Font = new Font("微软雅黑", 12); // 从10增大到12
            listBoxFiles.BorderStyle = BorderStyle.FixedSingle;
            listBoxFiles.BackColor = Color.FromArgb(250, 250, 250);
            listBoxFiles.SelectionMode = SelectionMode.One;
            listBoxFiles.ItemHeight = 30; // 从25增大到30
            listBoxFiles.DrawMode = DrawMode.OwnerDrawFixed;
            listBoxFiles.DrawItem += ListBoxFiles_DrawItem;
            this.Controls.Add(listBoxFiles);

            // 底部面板
            bottomPanel = new Panel();
            bottomPanel.Dock = DockStyle.Bottom;
            bottomPanel.Height = 80;
            bottomPanel.BackColor = Color.FromArgb(236, 240, 241);
            bottomPanel.Paint += BottomPanel_Paint;
            this.Controls.Add(bottomPanel);

            // 进度条
            progressBar = new ProgressBar();
            progressBar.Location = new Point(20, 15);
            progressBar.Size = new Size(640, 20);
            progressBar.Style = ProgressBarStyle.Continuous;
            progressBar.ForeColor = Color.FromArgb(52, 152, 219);
            bottomPanel.Controls.Add(progressBar);

            // 进度标签
            lblProgress = new Label();
            lblProgress.Text = "准备就绪";
            lblProgress.Font = new Font("微软雅黑", 9);
            lblProgress.Location = new Point(20, 45);
            lblProgress.Size = new Size(300, 20);
            lblProgress.ForeColor = Color.FromArgb(52, 73, 94);
            bottomPanel.Controls.Add(lblProgress);

            // 状态标签
            lblStatus = new Label();
            lblStatus.Text = "请选择要处理的图片文件或文件夹";
            lblStatus.Font = new Font("微软雅黑", 9);
            lblStatus.Location = new Point(350, 45);
            lblStatus.Size = new Size(310, 20);
            lblStatus.ForeColor = Color.FromArgb(52, 152, 219);
            lblStatus.TextAlign = ContentAlignment.MiddleRight;
            bottomPanel.Controls.Add(lblStatus);

            this.ResumeLayout(false);
        }

        private Button CreateStyledButton(string text, Color backColor)
        {
            Button btn = new Button();
            btn.Text = text;
            btn.Size = new Size(120, 35);
            btn.BackColor = backColor;
            btn.ForeColor = Color.White;
            btn.FlatStyle = FlatStyle.Flat;
            btn.FlatAppearance.BorderSize = 0;
            btn.Font = new Font("微软雅黑", 9, FontStyle.Bold);
            btn.Cursor = Cursors.Hand;

            // 鼠标悬停效果
            btn.MouseEnter += (s, e) => {
                btn.BackColor = ControlPaint.Light(backColor, 0.2f);
            };
            btn.MouseLeave += (s, e) => {
                btn.BackColor = backColor;
            };

            return btn;
        }

        private Icon CreateIcon()
        {
            // 创建一个简单的图标
            Bitmap bmp = new Bitmap(16, 16);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.FillRectangle(new SolidBrush(Color.FromArgb(52, 152, 219)), 0, 0, 16, 16);
                g.FillRectangle(new SolidBrush(Color.White), 2, 2, 12, 12);
            }
            return Icon.FromHandle(bmp.GetHicon());
        }

        private void ListBoxFiles_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (e.Index < 0) return;

            e.DrawBackground();

            using (Brush brush = new SolidBrush(e.ForeColor))
            {
                string text = listBoxFiles.Items[e.Index].ToString();
                e.Graphics.DrawString(text, e.Font, brush, e.Bounds.Left + 5, e.Bounds.Top + 7); // 调整垂直位置
            }

            e.DrawFocusRectangle();
        }

        private void TopPanel_Paint(object sender, PaintEventArgs e)
        {
            // 添加渐变背景
            using (LinearGradientBrush brush = new LinearGradientBrush(
                topPanel.ClientRectangle,
                Color.FromArgb(52, 152, 219),
                Color.FromArgb(41, 128, 185),
                LinearGradientMode.Vertical))
            {
                e.Graphics.FillRectangle(brush, topPanel.ClientRectangle);
            }
        }

        private void BottomPanel_Paint(object sender, PaintEventArgs e)
        {
            // 添加顶部分隔线
            using (Pen pen = new Pen(Color.FromArgb(189, 195, 199), 1))
            {
                e.Graphics.DrawLine(pen, 0, 0, bottomPanel.Width, 0);
            }
        }

        private void BtnSelectFiles_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff|所有文件|*.*";
                openFileDialog.Multiselect = true;
                openFileDialog.Title = "选择图片文件";

                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    selectedFiles.AddRange(openFileDialog.FileNames.Where(f => !selectedFiles.Contains(f)));
                    UpdateFileList();
                }
            }
        }

        private void BtnSelectFolder_Click(object sender, EventArgs e)
        {
            using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
            {
                folderBrowserDialog.Description = "选择包含图片的文件夹";

                if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
                {
                    // 获取文件夹中的所有图片文件
                    string[] extensions = { "*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff" };

                    foreach (string extension in extensions)
                    {
                        var files = Directory.GetFiles(folderBrowserDialog.SelectedPath, extension, SearchOption.AllDirectories);
                        selectedFiles.AddRange(files.Where(f => !selectedFiles.Contains(f)));
                    }

                    UpdateFileList();
                }
            }
        }

        private void BtnClear_Click(object sender, EventArgs e)
        {
            selectedFiles.Clear();
            UpdateFileList();
        }

        private void UpdateFileList()
        {
            listBoxFiles.Items.Clear();

            foreach (string file in selectedFiles)
            {
                string displayText = $"📷 {Path.GetFileName(file)}";
                listBoxFiles.Items.Add(displayText);
            }

            btnProcess.Enabled = selectedFiles.Count > 0;
            lblFileCount.Text = selectedFiles.Count > 0 ? $"已选择 {selectedFiles.Count} 个文件" : "未选择文件";
            lblStatus.Text = selectedFiles.Count > 0 ? "准备处理" : "请选择要处理的图片文件或文件夹";
        }

        private async void BtnProcess_Click(object sender, EventArgs e)
        {
            if (selectedFiles.Count == 0)
            {
                MessageBox.Show("请先选择要处理的图片文件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // 禁用按钮防止重复点击
            SetButtonsEnabled(false);

            try
            {
                await ProcessImagesAsync();
                MessageBox.Show("处理完成!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"处理过程中发生错误:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                // 重新启用按钮
                SetButtonsEnabled(true);
                progressBar.Value = 0;
                lblProgress.Text = "处理完成";
            }
        }

        private void SetButtonsEnabled(bool enabled)
        {
            btnProcess.Enabled = enabled && selectedFiles.Count > 0;
            btnSelectFiles.Enabled = enabled;
            btnSelectFolder.Enabled = enabled;
            btnClear.Enabled = enabled;
        }

        private async Task ProcessImagesAsync()
        {
            progressBar.Maximum = selectedFiles.Count;
            progressBar.Value = 0;

            // 创建输出目录
            string outputDir = CreateOutputDirectory();
            lblStatus.Text = $"输出: {Path.GetFileName(outputDir)}";

            int shadowSize = (int)numShadowSize.Value;
            float shadowOpacity = (float)numShadowOpacity.Value / 100f;

            for (int i = 0; i < selectedFiles.Count; i++)
            {
                string inputFile = selectedFiles[i];

                try
                {
                    lblProgress.Text = $"正在处理: {Path.GetFileName(inputFile)} ({i + 1}/{selectedFiles.Count})";
                    Application.DoEvents();

                    await Task.Run(() => AddShadowToImage(inputFile, outputDir, shadowSize, shadowOpacity));

                    progressBar.Value = i + 1;
                }
                catch (Exception ex)
                {
                    // 记录错误但继续处理其他文件
                    lblProgress.Text = $"处理 {Path.GetFileName(inputFile)} 时出错: {ex.Message}";
                    await Task.Delay(1000); // 显示错误信息1秒
                }
            }
        }

        private string CreateOutputDirectory()
        {
            // 获取第一个文件的目录作为基准
            string baseDir = Path.GetDirectoryName(selectedFiles[0]);

            // 创建时间戳
            long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();

            // 创建输出目录名
            string outputDirName = $"output_{timestamp}";
            string outputDir = Path.Combine(baseDir, outputDirName);

            // 创建目录
            Directory.CreateDirectory(outputDir);

            return outputDir;
        }

        private void AddShadowToImage(string inputPath, string outputDir, int shadowSize, float shadowOpacity)
        {
            using (Image originalImage = Image.FromFile(inputPath))
            {
                // 计算新图片尺寸(为四周阴影留出空间)
                int margin = shadowSize + 8; // 增加更多边距,为四周阴影留出空间
                int newWidth = originalImage.Width + (margin * 2);
                int newHeight = originalImage.Height + (margin * 2);

                // 创建新图片 - 使用透明背景
                using (Bitmap newImage = new Bitmap(newWidth, newHeight))
                {
                    using (Graphics graphics = Graphics.FromImage(newImage))
                    {
                        // 设置高质量渲染
                        graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        graphics.SmoothingMode = SmoothingMode.AntiAlias;
                        graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

                        // 使用透明背景
                        graphics.Clear(Color.Transparent);

                        // 创建四周阴影效果
                        CreateShadowEffect(graphics, margin, margin, originalImage.Width, originalImage.Height, shadowSize, shadowOpacity);

                        // 在中心绘制原始图片
                        graphics.DrawImage(originalImage, margin, margin, originalImage.Width, originalImage.Height);
                    }

                    // 保存到输出目录
                    string fileName = Path.GetFileNameWithoutExtension(inputPath) + "_shadow" + Path.GetExtension(inputPath);
                    string outputPath = Path.Combine(outputDir, fileName);

                    // 保存为PNG以支持透明度
                    newImage.Save(outputPath, ImageFormat.Png);
                }
            }
        }

        private void CreateShadowEffect(Graphics graphics, int x, int y, int width, int height, int shadowSize, float opacity)
        {
            // 创建四周阴影效果
            int shadowOffset = shadowSize / 3; // 减小偏移量,让阴影更均匀

            // 创建阴影的矩形,四周都有阴影
            Rectangle shadowRect = new Rectangle(x - shadowOffset, y - shadowOffset,
                                               width + shadowOffset * 2, height + shadowOffset * 2);

            // 绘制多层渐变阴影,从外到内逐渐变浅
            for (int i = shadowSize; i > 0; i--)
            {
                // 计算当前层的透明度
                float layerOpacity = opacity * (1.0f - (float)i / shadowSize) * 0.6f;
                int alpha = (int)(255 * layerOpacity);

                // 使用深灰色阴影
                Color shadowColor = Color.FromArgb(alpha, 80, 80, 80);

                using (Brush shadowBrush = new SolidBrush(shadowColor))
                {
                    // 计算当前层的位置
                    int currentOffset = i;
                    Rectangle currentShadowRect = new Rectangle(
                        x - currentOffset,
                        y - currentOffset,
                        width + currentOffset * 2,
                        height + currentOffset * 2
                    );

                    // 绘制阴影层 - 使用路径来创建圆角效果
                    using (GraphicsPath shadowPath = CreateRoundedRectanglePath(currentShadowRect, 2))
                    {
                        graphics.FillPath(shadowBrush, shadowPath);
                    }
                }
            }
        }

        private GraphicsPath CreateRoundedRectanglePath(Rectangle rect, int radius)
        {
            GraphicsPath path = new GraphicsPath();

            if (radius <= 0)
            {
                path.AddRectangle(rect);
                return path;
            }

            // 确保圆角不超过矩形的一半
            radius = Math.Min(radius, Math.Min(rect.Width, rect.Height) / 2);

            // 创建圆角矩形路径
            path.AddArc(rect.X, rect.Y, radius * 2, radius * 2, 180, 90); // 左上角
            path.AddArc(rect.Right - radius * 2, rect.Y, radius * 2, radius * 2, 270, 90); // 右上角
            path.AddArc(rect.Right - radius * 2, rect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90); // 右下角
            path.AddArc(rect.X, rect.Bottom - radius * 2, radius * 2, radius * 2, 90, 90); // 左下角
            path.CloseAllFigures();

            return path;
        }
    }

    // Program.cs
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
}

 

posted @ 2025-07-18 00:35  sherlock-merlin  阅读(19)  评论(1)    收藏  举报