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






4.85/5 (11投票s)
向图像添加带轮廓的文本,旋转它,并使用鼠标移动它。
引言
这是一个简单的演示,展示了如何将带或不带轮廓的旋转文本绘制到图像上,并使用鼠标移动它。
背景
这是一个启动程序,可以轻松地扩展出许多选项,例如多个对象、保存选项、文本更改、颜色选择……具体取决于你想用它做什么,但我想保持这个示例的简单性。我正在开发一个照片组织程序,并且希望将注释作为程序的一部分。我找不到一个简单的入门解释,所以这里有一个。
关注点
绘制文本
我使用了 GraphicsPath.AddString
和 Graphics.DrawPath
方法,而不是 Graphics.Drawstring
,因为我也想能够勾勒文本轮廓。DrawString
仅填充文本,但 GraphicsPath
可以被填充或仅绘制为轮廓。为了旋转文本,使用了 Matrix
结构的 RotateAt
。
- 从图像的副本创建一个
Graphics
对象(我们称之为画布)。 - 将
SmoothingMode
设置为AntiAlias
,这样文本就不会是块状的。 - 为文本创建一个
GraphicsPath
。 - 在文本的中心点,按给定的值旋转一个
Matrix
。 - 使用
TransformPoints
获取文本边界的旋转角点。 - 在画布上绘制填充文本的路径。
- 在画布上绘制文本路径的轮廓。
- 现在,你有一个带有注释的图像,可以按照你希望的方式和位置绘制或保存。
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
移动文本
为了能够移动文本,它必须与图像分层隔离。每次绘制文本时,使用原始图像的干净副本,并在新位置绘制文本。使用 MouseUp
、MouseDown
和 MouseMove
事件,以及布尔变量 MouseOver
和 MouseMoving
,以及 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 月 - 在修复确定鼠标是否悬停在文本上的错误时,我发现了一种更简单的检查鼠标是否在文本区域内的方法。