当前位置:

首页 > 编程开发 > C#使用System.Drawing实现位图取色与绘图的方法

C#使用System.Drawing实现位图取色与绘图的方法

本文目录

    在C#里,想用System.Drawing处理位图,做取色、绘图这类操作,其实比想象中要简单不少。直接上几句核心代码,就能把屏幕颜色抓下来,或者往图片上画个圈。下面就把关键方法拆开聊聊,顺便附上一个完整的交互示例,方便你直接上手跑。 1. 提取鼠标指定点的颜色值 要获取屏幕上任意一点的颜色,最直接的

    在C#里,想用System.Drawing处理位图,做取色、绘图这类操作,其实比想象中要简单不少。直接上几句核心代码,就能把屏幕颜色抓下来,或者往图片上画个圈。下面就把关键方法拆开聊聊,顺便附上一个完整的交互示例,方便你直接上手跑。

    C#使用System.Drawing实现位图取色与绘图的方法

    1. 提取鼠标指定点的颜色值

    要获取屏幕上任意一点的颜色,最直接的路子是截取屏幕,然后从截下来的位图中读像素。下面给两种方法:一种靠Graphics.CopyFromScreen,更通用也更安全;另一种是调用Windows API,速度更快但需要引入外部库。实际开发中推荐第一种。

    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    public class ColorPicker
    {
        // 方法一:通过截屏获取指定屏幕坐标的颜色(推荐,更通用)
        public static Color GetColorAtScreenPoint(Point screenPoint)
        {
            // 创建一个1x1像素的位图
            using (Bitmap bitmap = new Bitmap(1, 1))
            {
                // 使用Graphics对象从屏幕指定位置拷贝1个像素 using (Graphics g = Graphics.FromImage(bitmap))
                {
                    g.CopyFromScreen(screenPoint, Point.Empty, new Size(1, 1));
                }
                // 返回该像素的颜色
                return bitmap.GetPixel(0, 0);
            }
        }
        // 方法二:使用Windows API (GetPixel) 直接获取(需要引用user32.dll和gdi32.dll)
        [DllImport("user32.dll")]
        private static extern IntPtr GetDC(IntPtr hwnd);
        [DllImport("user32.dll")]
        private static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
        [DllImport("gdi32.dll")]
        private static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);
        public static Color GetColorAtScreenPoint_API(Point screenPoint)
        {
            IntPtr hdc = GetDC(IntPtr.Zero);
            uint pixel = GetPixel(hdc, screenPoint.X, screenPoint.Y);
            ReleaseDC(IntPtr.Zero, hdc);
            // 将GetPixel返回的COLORREF转换为Color对象 byte red = (byte)(pixel & 0x000000FF);
            byte green = (byte)((pixel & 0x0000FF00) >> 8);
            byte blue = (byte)((pixel & 0x00FF0000) >> 16);
            return Color.FromArgb(red, green, blue);
        }
        // 示例:在定时器或循环中实时获取鼠标位置颜色 public static void StartRealTimeColorPick(Label colorLabel)
        {
            Timer timer = new Timer();
            timer.Interval = 50; // 每50毫秒更新一次
            timer.Tick += (sender, e) =>
            {
                Point mousePos = Control.MousePosition;
                Color color = GetColorAtScreenPoint(mousePos);
                colorLabel.Text = $"坐标:({mousePos.X},{mousePos.Y}), RGB:({color.R},{color.G},{color.B})";
                colorLabel.BackColor = color;
            };
            timer.Start();
        }
    }

    方法一的核心逻辑就四步:建一个1x1的Bitmap,拿Graphics对象从屏幕拷像素,然后调用GetPixel读出颜色。方法二走的是GetDC+GetPixel的API路线,虽然不需要生成临时位图,但得手动释放设备上下文,稍不注意容易内存泄漏。所以日常用的话,还是推荐方法一。

    2. 在位图上画圆

    画图这事,离不开Graphics对象。只要从位图创建出Graphics,就能在上面画各种形状。下面两个方法,一个是在现有位图上画圆,另一个是新建一张位图然后画圆——想怎么用都行。

    using System.Drawing;
    using System.Drawing.Drawing2D;
    
    public class BitmapDrawer
    {
        // 方法:在现有位图上绘制一个圆
        public static Bitmap DrawCircleOnBitmap(Bitmap originalBitmap, Point center, int radius, Color circleColor, int penWidth = 2)
        {
            // 创建原图的副本,避免修改原图 Bitmap resultBitmap = new Bitmap(originalBitmap);
            using (Graphics g = Graphics.FromImage(resultBitmap))
            {
                // 设置绘图质量
                g.SmoothingMode = SmoothingMode.AntiAlias;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    
                // 创建画笔
                using (Pen pen = new Pen(circleColor, penWidth))
                {
                    // 计算圆的左上角坐标和直径 Rectangle rect = new Rectangle(center.X - radius, center.Y - radius, radius * 2, radius * 2);
                    // 绘制圆 g.DrawEllipse(pen, rect);
                    // 如果需要填充圆,使用 Brush
                    // using (SolidBrush brush = new SolidBrush(Color.FromArgb(50, circleColor)))
                    // {
                    //     g.FillEllipse(brush, rect);
                    // }
                }
            }
            return resultBitmap;
        }
    
        // 方法:创建一个新的位图并在上面画圆
        public static Bitmap CreateBitmapWithCircle(int width, int height, Point center, int radius, Color backgroundColor, Color circleColor)
        {
            Bitmap bitmap = new Bitmap(width, height);
            using (Graphics g = Graphics.FromImage(bitmap))
            {
                // 填充背景色 using (SolidBrush backgroundBrush = new SolidBrush(backgroundColor))
                {
                    g.FillRectangle(backgroundBrush, 0, 0, width, height);
                }
                // 画圆
                using (Pen pen = new Pen(circleColor, 3))
                {
                    Rectangle rect = new Rectangle(center.X - radius, center.Y - radius, radius * 2, radius * 2);
                    g.DrawEllipse(pen, rect);
                }
            }
            return bitmap;
        }
    }
    

    注意这里用DrawEllipse画圆,其实是画一个外接矩形为正方形的椭圆。圆的半径就是正方形边长的一半。另外,抗锯齿开关SmoothingMode.AntiAlias一定要打开,否则画出来的圆边缘会有锯齿,看着就不专业了。

    3. 结合鼠标交互:点击取色并画圆

    把上面两个功能串起来,就能做出一个完整的交互工具:鼠标点击图片上的某个点,立即获取该点的颜色,同时在点击位置画一个圆作为标记。下面这个Windows Forms示例把这些都整合在一起了,代码里注释很详细,可以直接拿来用。

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace BitmapSimpleDemo
    {
        public partial class MainForm : Form
        {
            private Bitmap originalBitmap; // 原始位图 private Bitmap displayedBitmap; // 当前显示的位图(可能已画圆)
            private Point lastClickPoint;
            private int circleRadius = 30;
    
            public MainForm()
            {
                InitializeComponent();
                // 加载一张示例图片到PictureBox
                originalBitmap = new Bitmap(@"你的图片路径.jpg");
                pictureBox1.Image = originalBitmap;
                displayedBitmap = (Bitmap)originalBitmap.Clone();
            }
    
            // PictureBox的鼠标点击事件 private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
            {
                if (originalBitmap == null) return;
    
                lastClickPoint = e.Location;
    
                // 1. 获取点击位置的颜色
                Color clickedColor = originalBitmap.GetPixel(e.X, e.Y);
                lblColorInfo.Text = $"点击点颜色 - R:{clickedColor.R} G:{clickedColor.G} B:{clickedColor.B}";
                pnlColorPreview.BackColor = clickedColor;
    
                // 2. 在 displayedBitmap 上以点击点为中心画圆
                // 先重置为原图
                displayedBitmap?.Dispose();
                displayedBitmap = (Bitmap)originalBitmap.Clone();
    
                using (Graphics g = Graphics.FromImage(displayedBitmap))
                {
                    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                    // 使用与取色形成对比的颜色画圆,例如取反色 Color drawColor = Color.FromArgb(255 - clickedColor.R, 255 - clickedColor.G, 255 - clickedColor.B);
                    using (Pen pen = new Pen(drawColor, 3))
                    {
                        Rectangle rect = new Rectangle(e.X - circleRadius, e.Y - circleRadius, circleRadius * 2, circleRadius * 2);
                        g.DrawEllipse(pen, rect);
                    }
                }
    
                // 3. 更新PictureBox显示 pictureBox1.Image = displayedBitmap;
            }
    
            // 按钮:保存画了圆的图片 private void btnSave_Click(object sender, EventArgs e)
            {
                if (displayedBitmap != null)
                {
                    SaveFileDialog sfd = new SaveFileDialog();
                    sfd.Filter = "PNG Image|*.png|JPEG Image|*.jpg";
                    if (sfd.ShowDialog() == DialogResult.OK)
                    {
                        displayedBitmap.Save(sfd.FileName);
                    }
                }
            }
    
            // 按钮:重置图片,清除所有圆圈 private void btnReset_Click(object sender, EventArgs e)
            {
                displayedBitmap?.Dispose();
                displayedBitmap = (Bitmap)originalBitmap.Clone();
                pictureBox1.Image = displayedBitmap;
                lblColorInfo.Text = "颜色信息";
                pnlColorPreview.BackColor = SystemColors.Control;
            }
    
            // 窗体关闭时释放资源
            private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
            {
                originalBitmap?.Dispose();
                displayedBitmap?.Dispose();
            }
        }
    }
    

    这个示例里有一个细节值得留意:每次点击画圆之前,都会先把displayedBitmap重置为原图的副本,这样就不会出现多个圆圈叠加在一起的情况。如果你想要保留历史标记,那就不需要重置,直接在displayedBitmap上继续画就行。另外,画圆的颜色用了取反色,这样不管点击到哪种颜色,都能保证画出来的圈清晰可见。

    关键操作总结:

    任务核心类/方法说明
    获取屏幕坐标颜色Graphics.CopyFromScreen通过截取1像素屏幕区域实现,无需API声明,更安全 。
    获取位图像素颜色Bitmap.GetPixel(x, y)直接从Bitmap对象中读取指定坐标的颜色。
    在位图上绘图Graphics.FromImage(bitmap)从位图创建Graphics画布,然后使用DrawEllipse画圆 。
    鼠标交互取色PictureBox.MouseClick事件 + e.Location在控件内获取鼠标相对坐标,再对应到位图像素。
    资源管理using语句或手动Dispose()Bitmap和Graphics是非托管资源,使用后必须释放。

    最后再啰嗦一句:Bitmap和Graphics都是非托管资源,用完一定要释放,要么用using包裹,要么在Dispose里清理。这个习惯养成了,后面写大项目才不会出现莫名其妙的内存暴涨。

    本文内容来源于网友投稿,如有侵权请联系删除。
    作者最新文章
    编程开发
    相关文章 更多
    PHP递归性能优化技巧与迭代替代方案
    PHP递归性能优化技巧与迭代替代方案

    解析PHP递归函数在树形数据处理中的性能瓶颈,提供预加载数据消除I/O、使用显式栈替代深层递归的实战方案,帮助开发者在代码可读性与执行效率间做出合理取舍。

    Java测试中怎么使用Mockito模拟依赖对象
    Java测试中怎么使用Mockito模拟依赖对象

    详细讲解在Java单元测试中如何使用Mockito模拟依赖对象,包括引入依赖、创建Mock、打桩返回值、行为验证以及Mock与Spy的核心差异和常见陷阱排查。

    链表删除节点的时间复杂度是多少及其详细分析
    链表删除节点的时间复杂度是多少及其详细分析

    详细分析链表删除节点的时间复杂度,深入探讨单链表与双向链表在不同已知前提下的查找与删除开销,并结合完整代码与清晰图解进行对比总结。

    codex如何配置模型参数及文件设置教程
    codex如何配置模型参数及文件设置教程

    想知道如何让AI写出的代码更贴合你的习惯?本文手把手教你在VS Code中调整Codex相关模型参数,通过修改配置文件优化温度值和令牌限制,解决代码建议不准确或响应慢的问题。

    Claude Code AI编程工具实力揭秘与编程助手实测
    Claude Code AI编程工具实力揭秘与编程助手实测

    通过实测展示Claude Code在终端中如何理解自然语言指令、自动修改代码文件并处理复杂编程任务,帮助开发者评估其实际辅助能力。

    winforms教程自学入门与基础开发步骤详解
    winforms教程自学入门与基础开发步骤详解

    本教程详细讲解如何使用Visual Studio创建WinForms项目,通过添加按钮和标签控件并编写点击事件代码,实现一个基础的计数器功能,适合C#初学者快速上手Windows窗体应用开发。

    Cursor自动补全设置教程教你快速开启代码补全功能
    Cursor自动补全设置教程教你快速开启代码补全功能

    详解Cursor编辑器中自动补全功能的开启与优化设置,涵盖Tab触发机制、上下文窗口调整及模型切换,帮助开发者解决补全延迟、干扰大等问题,提升编码流畅度。

    pandas的数据格式怎么转换和设置方法教程
    pandas的数据格式怎么转换和设置方法教程

    详解Pandas中数据格式转换的核心方法,包括astype强制转换、to_numeric容错处理及日期解析技巧,解决常见类型错误并提升数据处理效率。

    VS Code中文设置方法 简体语言包安装与切换教程
    VS Code中文设置方法 简体语言包安装与切换教程

    详细介绍在Visual Studio Code中安装Chinese (Simplified)语言包的方法,包括通过扩展市场搜索、安装及自动重启切换至简体中文界面的完整步骤,帮助开发者快速将编辑器本地化。

    cursor安装过程无法更改安装位置的解决方法
    cursor安装过程无法更改安装位置的解决方法

    针对Cursor安装包默认锁定C盘且无路径选择界面的问题,提供通过手动移动文件并创建目录联结(Symbolic Link)的解决方案,实现将软件安装在其他磁盘分区。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    WINDOWS 更多
    3dmax(3ds max)
    3dmax(3ds max)
    Windows

    Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。