初めて使ってみたStructure、構造体、文字の描画の文字間隔の調整 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
前回の続き
取得した描画範囲を元にして描画するところまで進んだ
文字間(マージン)を1に指定した場合で描画
VWAを描画、今回の結果
下のリストボックスにある文字列は1文字ごと普通に描画した時の描画範囲情報
Top、Left、Bottom、Rightがって文字で説明するの難しいから図で↓
左上を基点(0)にして文字の上下左右の一番外側にある(色が付いている)
ピクセルの座標みたいなもの
TopCountとかCountが付いているのが
最初の1文字目「V」の横座標は左の空白を削るだけなので元の座標-Left、元の座標は0なので0-7=-7、指定マージンが1なので1足して6
2文字目からは
描画座標 = 前回の描画座標 + 一個前の文字の幅 + 一個前の文字の左空白 - 描画文字の左空白 + 指定マージン
「W」の描画座標 = -6 + 27 + 7 - 9 + 1=20
あいうえおをマージン指定0
「う」と「え」が離れている感じがする
「ソノ人」でマージン指定0
文字の形が四角形じゃないとマージン0にしても離れている感じがするのは
上下左右っていう単純な四角形で判定しているから
ソとノだとソの右上とノの左下がマージンの判定になっているけど
ノの左端に当たる左下のY座標と同じY座標にあるソはかなり離れている
ソの右端に当たる右上のY座標と同じY座標にあるノもかなり離れている
これを近づけるには
前の文字に近づけていって最初に重なる座標から計算かなあ
ってことで必要になりそうなライン上の
ピクセルの座標を持たせるように作りなおしたDrawRange描画範囲っていう構造体が↓
Public Structure DrawRange描画範囲
Public Property TopX As List(Of Integer)
Public Property TopY As Integer
Public Property BottomX As List(Of Integer)
Public Property BottomY As Integer
Public Property LeftX As Integer
Public Property LeftY As List(Of Integer)
Public Property RightX As Integer
Public Property RightY As List(Of Integer)
Public Overrides Function ToString() As String
Dim str As String
str = "TopY=" & TopY & " BottomY=" & BottomY & " LeftX=" & LeftX & " RightX=" & RightX &
" TopX_Count=" & TopX.Count & " BottomX_Count=" & BottomX.Count & " LeftY_Count=" & LeftY.Count & " RightY_Count=" & RightY.Count
Return str
End Function
End Structure
新しく使ってみたのが
Public Property TopX As List(Of Integer)
っていうところ
プロパティの変数にList(Of Integer)っていうリスト(コレクション?)型のinteger型を使ってみた
「V」の時だとTopXには図のTopCountのラインにある色のついたピクセルのX座標の入れ物になる
「V」を描画したときのDrawRange描画範囲の中の様子
TopXに8個の数字が入っている、これがTopのライン上にある色付きの
ピクセルのすべてのX座標で、Y座標はTopYになるので組み合わせれば一番上側の座標一覧ができる
最初できなかったのが
List(Of Integer)をNewして作るタイミング
実際に構造体を使うときにNewしたら使えるようになった
理解していないから使い方間違っていそうだけど期待通りに動いている
DrawRange描画範囲っていう構造体作ったけど構造体じゃなくてクラスで作ったほうがいいのかもとか
Public Propertyでいろいろ作ったけどDimでよかったんじゃないかとか
List(Of Integer)で作ったけど普通の配列でもよかったんじゃないかとか
Public Structure DrawRange描画範囲
Public Property TopX As List(Of Integer)
Public Property TopY As Integer
Public Property BottomX As List(Of Integer)
Public Property BottomY As Integer
Public Property LeftX As Integer
Public Property LeftY As List(Of Integer)
Public Property RightX As Integer
Public Property RightY As List(Of Integer)
Public Overrides Function ToString() As String
Dim str As String
str = "TopY=" & TopY & " BottomY=" & BottomY & " LeftX=" & LeftX & " RightX=" & RightX &
" TopX_Count=" & TopX.Count & " BottomX_Count=" & BottomX.Count & " LeftY_Count=" & LeftY.Count & " RightY_Count=" & RightY.Count
Return str
End Function
Public Function myString1() As String
Dim str As String = "TopY=" & TopY & " BottomY=" & BottomY & " LeftX=" & LeftX & " RightX=" & RightX & " Width=" & RightX - LeftX + 1 & " Height=" & BottomY - TopY + 1
Return str
End Function
Public Function myString2() As String
Return " TopX_Count=" & TopX.Count & " BottomX_Count=" & BottomX.Count & " LeftY_Count=" & LeftY.Count & " RightY_Count=" & RightY.Count
End Function
Public Function DrawWidth() As Integer
Return RightX - LeftX + 1
End Function
Public Function DrawHeight() As Integer
Return BottomY - TopY + 1
End Function
Public Function DrawSize() As Size
Return New Size(DrawWidth, DrawHeight)
End Function
End Structure
DrawRange描画範囲
文字の幅とかをFunctionを使って返すように作ったけど
Propertyで持たせたほうがいいのかなあ、違いがわからん
Private Function DrawRange描画範囲情報(bmp) As DrawRange描画範囲
Dim reInte As New DrawRange描画範囲
reInte.TopX = New List(Of Integer)
reInte.BottomX = New List(Of Integer)
reInte.LeftY = New List(Of Integer)
reInte.RightY = New List(Of Integer)
Dim w As Integer = bmp.Width
Dim h As Integer = bmp.Height
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim bmpData As BitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat)
Dim ptr As IntPtr = bmpData.Scan0
Dim data As Integer = bmpData.Stride * h - 1
Dim pixels(data) As Byte
Runtime.InteropServices.Marshal.Copy(ptr, pixels, 0, pixels.Length)
Dim pos As Integer = 0
Dim x, y As Integer
Dim isFind As Boolean = False
For y = 0 To h - 1
For x = 0 To w - 1
pos = y * bmpData.Stride + x * 4
If pixels(pos + 3) <> 0 Then
If isFind = False Then
isFind = True
reInte.TopY = y
End If
reInte.TopX.Add(x)
End If
Next
If isFind Then Exit For
Next
isFind = False
For y = h - 1 To 0 Step -1
For x = 0 To w - 1
pos = y * bmpData.Stride + x * 4
If pixels(pos + 3) <> 0 Then
If isFind = False Then
isFind = True
reInte.BottomY = y
End If
reInte.BottomX.Add(x)
End If
Next
If isFind Then Exit For
Next
isFind = False
For x = 0 To w - 1
For y = 0 To h - 1
pos = y * bmpData.Stride + x * 4
If pixels(pos + 3) <> 0 Then
If isFind = False Then
isFind = True
reInte.LeftX = x
End If
reInte.LeftY.Add(y)
End If
Next
If isFind Then Exit For
Next
isFind = False
For x = w - 1 To 0 Step -1
For y = 0 To h - 1
pos = y * bmpData.Stride + x * 4
If pixels(pos + 3) <> 0 Then
If isFind = False Then
isFind = True
reInte.RightX = x
End If
reInte.RightY.Add(y)
End If
Next
If isFind Then Exit For
Next
bmp.UnlockBits(bmpData)
Return reInte
End Function
文字画像を受け取ってDrawRange描画範囲に入れて返す関数の
DrawRange描画範囲情報
並べてみると名前が紛らわしいなw
前回はGetPixelを使っていたけど
BitmapDataやLockBitを使うものに変更したので速くなった
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Me.ListBox1.Items.Clear()
Me.ListBox1.Anchor = AnchorStyles.Top Or AnchorStyles.Left Or AnchorStyles.Right
Me.PictureBox1.Width = 256
Me.PictureBox1.Height = 178
Me.PictureBox1.Width = 400
Dim str As String = "Pixtack紫陽花"
str = "あいうえお"
str = "ノクターン"
str = "VWA"
Dim w As Integer = Me.PictureBox1.Width
Dim h As Integer = Me.PictureBox1.Height
Dim bmp As New Bitmap(w, h)
Dim g As Graphics = Graphics.FromImage(bmp)
Dim b As New SolidBrush(Color.Black)
Dim font As New Font("Meiryo UI", 30, FontStyle.Regular)
Dim rect As New Rectangle(New Point(0, 0), New Size(w, h))
Dim sf As New StringFormat()
Dim sizeCanvas As SizeF = g.MeasureString(str, font, New PointF(0, 0), sf)
Dim rectC As New RectangleF(New PointF(0, 0), sizeCanvas)
Dim drawRange As New List(Of DrawRange描画範囲)
For i As Integer = 0 To str.Length - 1
bmp = New Bitmap(w, h)
g = Graphics.FromImage(bmp)
g.DrawString(str.Chars(i), font, b, Rectangle.Round(rectC), sf)
drawRange.Add(DrawRange描画範囲情報(bmp))
Me.ListBox1.Items.Add(str.Chars(i) & " = " & drawRange.Item(i).myString1())
Next
For i = 0 To str.Length - 1
Me.ListBox1.Items.Add(str.Chars(i) & " = " & drawRange.Item(i).myString2())
Next
bmp = New Bitmap(w, h)
g = Graphics.FromImage(bmp)
Dim drawPoint As New PointF(0, 0)
Dim margin As Integer = 0
Dim stringWidth As Integer = drawRange(0).RightX - drawRange(0).LeftX + 1
drawPoint.X = -(drawRange(0).LeftX) + margin
g.DrawString(str.Chars(0), font, b, drawPoint, sf)
For i = 1 To str.Length - 1
drawPoint.X += drawRange(i - 1).DrawWidth + (drawRange(i - 1).LeftX) - (drawRange(i).LeftX) + margin
g.DrawString(str.Chars(i), font, b, drawPoint, sf)
Next
Dim rr As New Rectangle(rect.X, rect.Y, rect.Width - 1, rect.Height - 1)
g.DrawRectangle(Pens.Red, rr)
Me.PictureBox1.Image = bmp
g.Dispose()
b.Dispose()
sf.Dispose()
End Sub
Button3のクリックイベント
strに入れた文字列が描画される
マージン指定はmarginのところ、ってこういうのは変数じゃなくて定数にした方がいいな
参照したところ
バックアップファイル一式
ファイル名:文字の描画位置、範囲_20150405.7z
次回は2日後