65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的图像注释 - 如何动态向图像添加旋转文本

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (11投票s)

2008年7月2日

CPOL

3分钟阅读

viewsIcon

70915

downloadIcon

1885

向图像添加带轮廓的文本,旋转它,并使用鼠标移动它。

引言

这是一个简单的演示,展示了如何将带或不带轮廓的旋转文本绘制到图像上,并使用鼠标移动它。

背景

这是一个启动程序,可以轻松地扩展出许多选项,例如多个对象、保存选项、文本更改、颜色选择……具体取决于你想用它做什么,但我想保持这个示例的简单性。我正在开发一个照片组织程序,并且希望将注释作为程序的一部分。我找不到一个简单的入门解释,所以这里有一个。

关注点

绘制文本

我使用了 GraphicsPath.AddStringGraphics.DrawPath 方法,而不是 Graphics.Drawstring,因为我也想能够勾勒文本轮廓。DrawString 仅填充文本,但 GraphicsPath 可以被填充或仅绘制为轮廓。为了旋转文本,使用了 Matrix 结构的 RotateAt

  1. 从图像的副本创建一个 Graphics 对象(我们称之为画布)。
  2. SmoothingMode 设置为 AntiAlias,这样文本就不会是块状的。
  3. 为文本创建一个 GraphicsPath
  4. 在文本的中心点,按给定的值旋转一个 Matrix
  5. 使用 TransformPoints 获取文本边界的旋转角点。
  6. 在画布上绘制填充文本的路径。
  7. 在画布上绘制文本路径的轮廓。
  8. 现在,你有一个带有注释的图像,可以按照你希望的方式和位置绘制或保存。
Private Sub Form1_Paint(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.PaintEventArgs) _
    Handles MyBase.Paint

    Dim tbm As Bitmap = CType(bm.Clone, Bitmap)
    Dim g As Graphics = Graphics.FromImage(tbm)
    Dim mx As Matrix = New Matrix
    Dim br As SolidBrush = New SolidBrush(Color.FromArgb(tbarTrans.Value, _
                                          Color.LightCoral))

    'Set the Points for the Text region        
    SetptsText()

    'Smooth the Text
    g.SmoothingMode = SmoothingMode.AntiAlias

    'Make the GraphicsPath for the Text
    Dim emsize As Single = Me.CreateGraphics.DpiY * pic_font.SizeInPoints / 72
    gpathText.AddString(strText, pic_font.FontFamily, CInt(pic_font.Style), _
        emsize, New RectangleF(ptText.X, ptText.Y, szText.Width, szText.Height), _
        StringFormat.GenericDefault)

    'Draw a copy of the image to the Graphics Object canvas
    g.DrawImage(CType(bm.Clone, Bitmap), 0, 0)

    'Rotate the Matrix at the center point
    mx.RotateAt(tbarRotate.Value, _
        New Point(ptText.X + (szText.Width / 2), _
        ptText.Y + (szText.Height / 2)))

    'Get the points for the rotated text bounds
    mx.TransformPoints(ptsText)

    'Transform the Graphics Object with the Matrix
    g.Transform = mx

    'Draw the Rotated Text
    g.FillPath(br, pathText)
    If chkAddOutline.Checked Then
        Using pn As Pen = New Pen(Color.FromArgb(tbarTrans.Value, Color.White), 1)
            g.DrawPath(pn, pathText)
        End Using
    End If

    'Draw the box if the mouse is over the Text
    If MouseOver Then
        g.ResetTransform()
        g.DrawPolygon(ptsTextPen, ptsText)
    End If

    'Draw the whole thing to the form
    e.Graphics.DrawImage(tbm, 10, 10)

    tbm.Dispose()
    g.Dispose()
    mx.Dispose()
    br.Dispose()
    pathText.Dispose()

End Sub

跟踪文本位置

为了知道鼠标是否悬停在文本上,我可以获取旋转文本的边界,并在鼠标事件中使用 Rectangle.Contains(pt.x,pt.y),但是当文本旋转时,这会在你实际上不在文本上方时创建命中。要检查一个点是否在文本本身之上,将文本的矩形边界的角作为点数组,并使用旋转的 Matrix 来转换这些点。从由旋转点创建的 GraphicsPath 创建一个 Region,并使用 IsVisible 方法检查鼠标位置是否在 Region 内。

Public Function IsMouseOverText(ByVal X As Integer, ByVal Y As Integer) As Boolean
 'Make a Graphics Path from the rotated ptsText. 
 Using gp As New GraphicsPath()
     gp.AddPolygon(ptsText)

     'Convert to Region.
     Using TextRegion As New Region(gp)
         'Is the point inside the region.
         Return TextRegion.IsVisible(X, Y)
     End Using

 End Using
End Function

移动文本

为了能够移动文本,它必须与图像分层隔离。每次绘制文本时,使用原始图像的干净副本,并在新位置绘制文本。使用 MouseUpMouseDownMouseMove 事件,以及布尔变量 MouseOverMouseMoving,以及 MouseOffset 点变量。

MouseDown 事件中,标记 MouseMoving 变量,并将 MouseOffset 点设置为当前光标位置与文本左上角之间的差。

Private Sub Form1_MouseDown(ByVal sender As Object, _
            ByVal e As System.Windows.Forms.MouseEventArgs) _
            Handles Me.MouseDown

    'Check if the pointer is over the Text
    If IsMouseOverText(e.X - 10, e.Y - 10) Then
        MouseMoving = True
        'Determine the upper left corner point 
        'from where the mouse was clicked
        MovingOffset.X = e.X - ptText.X
        MovingOffset.Y = e.Y - ptText.Y
    Else
        MouseMoving = False
    End If

End Sub

如果鼠标正在移动,但按钮未按下,则设置 MouseOver 标志,以便根据鼠标是否悬停在文本上来绘制或不绘制选择框。如果按下按钮,则将文本的左上角设置为新位置减去 MouseOffset

Private Sub Form1_MouseMove(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove

    'Check if the pointer is over the Text
    If IsMouseOverText(e.X - 10, e.Y - 10) Then
        If Not MouseOver Then
            MouseOver = True
            Me.Refresh()
        End If
    Else
        If MouseOver Then
            MouseOver = False
            Me.Refresh()
        End If
    End If

    If e.Button = Windows.Forms.MouseButtons.Left And MouseMoving Then
        ptText.X = CInt(e.X - MovingOffset.X)
        ptText.Y = CInt(e.Y - MovingOffset.Y)
        Me.Refresh()
    End If
End Sub

如果释放鼠标按钮,则将 MouseMoving 设置为 False

Private Sub Form1_MouseUp(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
    MouseMoving = False
    Me.Refresh()
End Sub

尽情享用!

历史

  • 1.0 版本 - 2008 年 6 月 - 首次试用。
  • 2.0 版本 - 2008 年 7 月 - 在修复确定鼠标是否悬停在文本上的错误时,我发现了一种更简单的检查鼠标是否在文本区域内的方法。
© . All rights reserved.