【问题标题】:How to draw a signature and save it to disc as a Bitmap?如何绘制签名并将其作为位图保存到光盘?
【发布时间】:2019-07-17 17:22:24
【问题描述】:

我正在尝试执行签名捕获程序并将客户签名保存为PNGBMP 格式。我的 Picturebox 代码运行良好,结果看起来比使用 draw 更好。我无法保存图像。

Imports System.Drawing
Public Class Form1
    Dim color As System.Drawing.Pen = Pens.Black
    Dim bmp As Bitmap

    Private Sub form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        bmp = New Bitmap(PictureBox1.Width, PictureBox1.Height)
        PictureBox1.Image = bmp
    End Sub

    Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
        Static last As New Point
        If e.Button = Windows.Forms.MouseButtons.Left Then
            PictureBox1.CreateGraphics.DrawLine(color, last.X, last.Y, e.X, e.Y)
        End If
        last = e.Location
    End Sub

    Private Sub CmdClear_Click(sender As Object, e As EventArgs) Handles cmdClear.Click
        bmp = New Bitmap(PictureBox1.Width, PictureBox1.Height)
        PictureBox1.Image = bmp
    End Sub

    Private Sub CmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click
        If PictureBox1.Image IsNot Nothing Then
            bmp = PictureBox1.Image
            bmp.Save("c:\temp\test1.bmp")
        End If
    End Sub
End Class

我想看签名的图片。

【问题讨论】:

  • 您可以嵌入一个 InkCanvas 控件,它会自动处理所有绘图(使用 ElementHost

标签: .net vb.net winforms graphics gdi+


【解决方案1】:

切勿使用 [Control].CreateGraphics,除非您需要在特定环境中立即使用此对象。例如,在特定图形上下文中绘制时测量文本的大小。
当需要绘制持久化时,如在控件表面绘制,使用Paint事件的PaintEventArgs提供的Graphics对象(或类似事件,如ComboBox、ListBox的DrawItem事件, ListView 控件)。
您会在 SO(以及整个网络)上找到这种建议。

如何进行:

  1. 我们需要一个对象来存储定义手工曲线的鼠标/笔移动。
  2. 每次释放鼠标左键(或笔触丢失并重新获取)时,对象都需要存储新的曲线定义。
  3. 我们需要一个 Graphics 对象,它可以将鼠标/笔移动定义的点转换为Bezier curves(曲线组合在矢量图形中通常称为路径)。

这里,存储运动的对象是Dictionary(Of Integer, List(Of Point)),其中Key代表曲线,Value > 表示定义该曲线的点的集合。
每次按下鼠标左键时,都会创建一个新的Key,并与Key 关联一个新的List(Of Point)
当鼠标/笔移动时,新的点位置将添加到当前曲线的List(Of Point)

GraphicsPath 类可以使用GraphicsPath.AddCurve() 方法将List(Of Point) 集合转换为贝塞尔曲线的控制点。
此方法接受一个点数组和一个 Tension 值作为参数。 Tension 是一个介于 01 之间的值,它定义了连接点时应用于曲线的弯曲量。这里使用了0.5f 的值。

► 当我们需要在 Bitmap 上绘制图形以将结果保存到磁盘时,我们将相同的逻辑应用于从 Bitmap 对象派生的 Graphics 对象。
因此,只有一种方法用于在 Control 的表面和 Bitmap 对象上进行绘制。
此代码中的 DrawSignature(g As Graphics) 方法。

这就是它的工作原理:

重现上述过程的代码

Private signatureObject As New Dictionary(Of Integer, List(Of Point))
Private signaturePen As New Pen(Color.Black, 4)
Private currentCurvePoints As List(Of Point)
Private currentCurve As Integer = -1

Private Sub pBoxSignature_MouseDown(sender As Object, e As MouseEventArgs) Handles pBoxSignature.MouseDown
    currentCurvePoints = New List(Of Point)
    currentCurve += 1
    signatureObject.Add(currentCurve, currentCurvePoints)
End Sub

Private Sub pBoxSignature_MouseMove(sender As Object, e As MouseEventArgs) Handles pBoxSignature.MouseMove
    If e.Button <> MouseButtons.Left OrElse currentCurve < 0 Then Return
    signatureObject(currentCurve).Add(e.Location)
    pBoxSignature.Invalidate()
End Sub

Private Sub btnClearSignature_Click(sender As Object, e As EventArgs) Handles btnClearSignature.Click
    currentCurve = -1
    signatureObject.Clear()
    pBoxSignature.Invalidate()
End Sub

Private Sub btnSaveSignature_Click(sender As Object, e As EventArgs) Handles btnSaveSignature.Click
    Dim signatureFileName = txtSignatureFileName.Text.Trim()
    If String.IsNullOrEmpty(signatureFileName) Then Return
    If currentCurve < 0 OrElse signatureObject(currentCurve).Count = 0 Then Return

    Using imgSignature As Bitmap = New Bitmap(pBoxSignature.Width, pBoxSignature.Height, PixelFormat.Format32bppArgb), 
        g As Graphics = Graphics.FromImage(imgSignature)

        DrawSignature(g)
        Dim signaturePath As String = Path.Combine(Application.StartupPath, $"{signatureFileName}.png")
        imgSignature.Save(signaturePath, ImageFormat.Png)
        pBoxSavedSignature.Image = New Bitmap(imgSignature)
    End Using
End Sub

Private Sub pBoxSignature_Paint(sender As Object, e As PaintEventArgs) Handles pBoxSignature.Paint
    If currentCurve < 0 OrElse signatureObject(currentCurve).Count = 0 Then Return
    DrawSignature(e.Graphics)
End Sub

Private Sub DrawSignature(g As Graphics)
    g.CompositingMode = CompositingMode.SourceOver
    g.CompositingQuality = CompositingQuality.HighQuality
    g.SmoothingMode = SmoothingMode.AntiAlias

    For Each curve In signatureObject
        If curve.Value.Count < 2 Then Continue For
        Using gPath As New GraphicsPath()
            gPath.AddCurve(curve.Value.ToArray(), 0.5F)
            g.DrawPath(signaturePen, gPath)
        End Using
    Next
End Sub

【讨论】:

  • 感谢 Jimi,像魅力一样工作。
  • 很好的编码,请不要忘记添加以下内容: Imports System.Drawing Imports System.Drawing.Drawing2D Imports System.Drawing.Imaging Imports System.IO
【解决方案2】:

当要保存为 BMP 时,背景需要为白色,因为 BMP 不支持透明度。话虽如此,我在 DrawPath 调用上方添加了这两行。

Dim Brsh As SolidBrush = New SolidBrush(Color.White)
g.FillRectangle(Brsh, 0, 0, pBoxSignature.Width, pBoxSignature.Height)
g.DrawPath(signaturePen, gPath)

但是,现在我无法再拿起我的笔触,否则之前的所有笔触都会被擦除/清除。 (意思是名字和姓氏需要在没有拿起笔的情况下书写)对图像有更多了解的人可能会看到原因并指出解决方案的方向吗?

更新: 工作代码

代码如下:

Using imgSignature As Bitmap = New Bitmap(pBoxSignature.Width, pBoxSignature.Height, PixelFormat.Format32bppRgb)
    Using g As Graphics = Graphics.FromImage(imgSignature)
        g.FillRectangle(Brsh, 0, 0, pBoxSignature.Width, pBoxSignature.Height)
        Call DrawSignature(g)
    End Using
    imgSignature.Save(signaturePath, ImageFormat.Bmp)
    pBoxSavedSignature.Image = New Bitmap(imgSignature)
End Using

【讨论】:

    【解决方案3】:

    谢谢吉米。

    如果有人在 C# 中需要它:

    public partial class FSignature : Form
    {
        private Dictionary<int, List<Point>> signatureObject = new Dictionary<int, List<Point>>();
        private Pen signaturePen = new Pen(Color.Black, 4);
        private List<Point> currentCurvePoints;
        private int currentCurve = -1;
    
        public FSignature()
        {
            InitializeComponent();
        }
    
    
    
    
    
        private void pBoxSignature_MouseDown(object sender, MouseEventArgs e)
        {
            currentCurvePoints = new List<Point>();
            currentCurve += 1;
            signatureObject.Add(currentCurve, currentCurvePoints);
        }
    
        private void pBoxSignature_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left || currentCurve < 0)
                return;
            signatureObject[currentCurve].Add(e.Location);
            pBoxSignature.Invalidate();
        }
    
        private void btnClearSignature_Click(object sender, EventArgs e)
        {
            currentCurve = -1;
            signatureObject.Clear();
            pBoxSignature.Invalidate();
        }
    
        private void btnSaveSignature_Click(object sender, EventArgs e)
        {
            var signatureFileName = txtSignatureFileName.Text.Trim();
            if (string.IsNullOrEmpty(signatureFileName))
                return;
            if (currentCurve < 0 || signatureObject[currentCurve].Count == 0)
                return;
            using (Bitmap imgSignature = new Bitmap(pBoxSignature.Width, pBoxSignature.Height, PixelFormat.Format32bppArgb))
            {
                using (Graphics g = Graphics.FromImage(imgSignature))
                {
                    DrawSignature(g);
                    string signaturePath = Path.Combine(Application.StartupPath, $"{signatureFileName}.png");
               
                    imgSignature.Save(signaturePath, ImageFormat.Png);
                    pBoxSavedSignature.Image = new Bitmap(imgSignature);
                }
            }
        }
    
        private void pBoxSignature_Paint(object sender, PaintEventArgs e)
        {
            if (currentCurve < 0 || signatureObject[currentCurve].Count == 0)
                return;
            DrawSignature(e.Graphics);
        }
    
        private void DrawSignature(Graphics g)
        {
            g.CompositingMode = CompositingMode.SourceOver;
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            foreach (var curve in signatureObject)
            {
                if (curve.Value.Count < 2)
                    continue;
                using (GraphicsPath gPath = new GraphicsPath())
                {
                    gPath.AddCurve(curve.Value.ToArray(), 0.5F);
                    g.DrawPath(signaturePen, gPath);
                }
            }
        }
    
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多