@@ -4,14 +4,22 @@ function loadchar(face::FTFont, c::Char)
4
4
check_error (err, " Could not load char to render." )
5
5
end
6
6
7
- function renderface (face:: FTFont , c:: Char , pixelsize:: Integer )
7
+ function loadglyph (face:: FTFont , c:: Char , pixelsize:: Integer )
8
8
set_pixelsize (face, pixelsize)
9
9
loadchar (face, c)
10
10
glyph = unsafe_load (face. glyph)
11
11
@assert glyph. format == FreeType. FT_GLYPH_FORMAT_BITMAP
12
- return glyphbitmap (glyph. bitmap), FontExtent (glyph. metrics)
12
+ glyph
13
+ end
14
+
15
+ function renderface (face:: FTFont , c:: Char , pixelsize:: Integer )
16
+ glyph = loadglyph (face, c, pixelsize)
17
+ glyphbitmap (glyph. bitmap), FontExtent (glyph. metrics)
13
18
end
14
19
20
+ extents (face:: FTFont , c:: Char , pixelsize:: Integer ) =
21
+ FontExtent (loadglyph (face, c, pixelsize). metrics)
22
+
15
23
function glyphbitmap (bitmap:: FreeType.FT_Bitmap )
16
24
@assert bitmap. pixel_mode == FreeType. FT_PIXEL_MODE_GRAY
17
25
bmp = Matrix {UInt8} (undef, bitmap. width, bitmap. rows)
@@ -24,7 +32,7 @@ function glyphbitmap(bitmap::FreeType.FT_Bitmap)
24
32
bmp[:, r] = src
25
33
row += bitmap. pitch
26
34
end
27
- return bmp
35
+ bmp
28
36
end
29
37
30
38
one_or_typemax (:: Type{T} ) where {T<: Union{Real,Colorant} } = T<: Integer ? typemax (T) : oneunit (T)
@@ -34,107 +42,163 @@ one_or_typemax(::Type{T}) where {T<:Union{Real,Colorant}} = T<:Integer ? typemax
34
42
fcolor=one_or_typemax(T), bcolor=zero(T), halign=:hleft, valign=:vbaseline) -> Matrix
35
43
36
44
Render `str` into `img` using the font `face` of size `pixelsize` at coordinates `y0,x0`.
45
+ Uses the conventions of freetype.org/freetype2/docs/glyphs/glyphs-3.html
37
46
38
47
# Arguments
39
48
* `y0,x0`: origin is in upper left with positive `y` going down
40
49
* `fcolor`: foreground color; AbstractVector{T}, typemax(T) for T<:Integer, otherwise one(T)
41
50
* `gcolor`: background color; AbstractVector{T}, typemax(T) for T<:Integer, otherwise one(T)
42
51
* `bcolor`: canvas background color; set to `nothing` for transparent
43
52
* `halign`: :hleft, :hcenter, or :hright
44
- * `valign`: :vtop, :vcenter, :vbaseline, or :vbottom
53
+ * `valign`: :vtop, :vcenter, :vbaseline, or :vbttom
54
+ * `bbox_glyph`: glyph bounding box color (debugging)
55
+ * `bbox`: bounding box color (debugging)
56
+ * `gstr`: background string or array of chars (for background sizing)
57
+ * `incx`: extra x spacing
45
58
"""
46
59
function renderstring! (
47
- img:: AbstractMatrix{T} , str:: Union{AbstractVector{Char},String} , face:: FTFont , pixelsize:: Union{Int, Tuple{Int, Int}} , y0, x0;
60
+ img:: AbstractMatrix{T} , fstr:: Union{AbstractVector{Char},String} ,
61
+ face:: FTFont , pixelsize:: Union{Int, Tuple{Int, Int}} , y0, x0;
48
62
fcolor:: Union{AbstractVector{T},T} = one_or_typemax (T),
49
63
gcolor:: Union{AbstractVector{T},T,Nothing} = nothing ,
50
64
bcolor:: Union{T,Nothing} = zero (T),
51
- halign:: Symbol = :hleft , valign:: Symbol = :vbaseline
65
+ halign:: Symbol = :hleft ,
66
+ valign:: Symbol = :vbaseline ,
67
+ bbox_glyph:: Union{T,Nothing} = nothing ,
68
+ bbox:: Union{T,Nothing} = nothing ,
69
+ gstr:: Union{AbstractVector{Char},String,Nothing} = nothing ,
70
+ off_bg:: Int = 0 ,
71
+ incx:: Int = 0 ,
52
72
) where T<: Union{Real,Colorant}
53
73
54
74
if pixelsize isa Tuple
55
75
@warn " using tuple for pixelsize is deprecated, please use one integer"
56
76
pixelsize = pixelsize[1 ]
57
77
end
58
-
59
- str = str isa AbstractVector ? String (str) : str
60
-
61
78
set_pixelsize (face, pixelsize)
62
79
63
- bitmaps = Vector {Matrix{UInt8}} (undef, lastindex (str) )
64
- metrics = Vector {FontExtent{Int}} (undef, lastindex (str))
65
- # ymin and ymax w.r.t the baseline
66
- ymin = ymax = sumadvancex = 0
80
+ fstr = fstr isa AbstractVector ? fstr : collect (fstr )
81
+ if gstr != = nothing
82
+ gstr = gstr isa AbstractVector ? gstr : collect (gstr)
83
+ end
67
84
68
- for (istr, char) = enumerate (str)
69
- bitmap, metric_float = renderface (face, char, pixelsize)
70
- metric = round .(Int, metric_float)
71
- bitmaps[istr] = bitmap
72
- metrics[istr] = metric
85
+ len = length (fstr)
86
+ bitmaps = Vector {Matrix{UInt8}} (undef, len)
87
+ metrics = Vector {FontExtent{Int}} (undef, len)
73
88
74
- # scale of glyph (size of bitmap)
75
- w, h = metric. scale
76
- # offset between glyph origin and bitmap top left corner
77
- bx, by = metric. horizontal_bearing
78
- ymin = min (ymin, by - h)
79
- ymax = max (ymax, by)
80
- sumadvancex += metric. advance[1 ]
89
+ y_min = y_max = sum_advance_x = 0 # y_min and y_max are w.r.t the baseline
90
+ for (i, char) in enumerate (fstr)
91
+ bitmap, metricf = renderface (face, char, pixelsize)
92
+ metric = round .(Int, metricf)
93
+ bitmaps[i] = bitmap
94
+ metrics[i] = metric
95
+
96
+ y_min = min (y_min, bottominkbound (metric))
97
+ y_max = max (y_max, topinkbound (metric))
98
+ sum_advance_x += hadvance (metric)
81
99
end
82
100
83
- px = x0 - (halign == :hright ? sumadvancex : halign == :hcenter ? sumadvancex >> 1 : 0 )
101
+ bitmap_max = bitmaps |> first |> eltype |> typemax
102
+ imgh, imgw = size (img)
103
+
104
+ # initial pen position
105
+ px = x0 - (halign == :hright ? sum_advance_x : halign == :hcenter ? sum_advance_x >> 1 : 0 )
84
106
py = y0 + (
85
- valign == :vtop ? ymax : valign == :vbottom ? ymin :
86
- valign == :vcenter ? (ymax - ymin ) >> 1 + ymin : 0
107
+ valign == :vtop ? y_max : valign == :vbottom ? y_min :
108
+ valign == :vcenter ? (y_max - y_min ) >> 1 + y_min : 0
87
109
)
88
110
89
- bitmapmax = typemax (eltype (bitmaps[1 ]))
90
-
91
- imgh, imgw = size (img)
92
111
if bcolor != = nothing
93
112
img[
94
- clamp (py- ymax + 1 , 1 , imgh) : clamp (py- ymin - 1 , 1 , imgh),
95
- clamp (px- 1 , 1 , imgw) : clamp (px+ sumadvancex - 1 , 1 , imgw)
113
+ clamp (py - y_max , 1 , imgh) : clamp (py - y_min , 1 , imgh),
114
+ clamp (px, 1 , imgw) : clamp (px + sum_advance_x , 1 , imgw)
96
115
] .= bcolor
97
116
end
98
117
99
118
local prev_char:: Char
100
- for (istr, char) = enumerate (str)
101
- w, h = metrics[istr]. scale
102
- bx, by = metrics[istr]. horizontal_bearing
119
+ for (i, char) in enumerate (fstr)
120
+ bitmap = bitmaps[i]
121
+ metric = metrics[i]
122
+ bx, by = metric. horizontal_bearing
123
+ ax, ay = metric. advance
124
+ sx, sy = metric. scale
103
125
104
- if istr == 1
126
+ if i == 1
105
127
prev_char = char
106
128
else
107
- kx, ky = map (x-> round (Int, x), kerning (prev_char, char, face))
129
+ kx, _ = map (x-> round (Int, x), kerning (prev_char, char, face))
108
130
px += kx
109
131
end
110
132
111
- fcol = fcolor isa AbstractVector ? fcolor[istr] : fcolor
112
- gcol = gcolor isa AbstractVector ? gcolor[istr] : gcolor
133
+ # glyph origin
134
+ oy = py - by
135
+ ox = px + bx
136
+
137
+ fcol = fcolor isa AbstractVector ? fcolor[i] : fcolor
138
+ gcol = gcolor isa AbstractVector ? gcolor[i] : gcolor
113
139
114
140
# trim parts of glyph images that are outside the destination
115
- cliprowlo, cliprowhi = max (0 , - (py - by)), max (0 , py - by + h - imgh)
116
- clipcollo, clipcolhi = max (0 , - bx - px ), max (0 , px + bx + w - imgw)
141
+ row_lo, row_hi = 1 + max (0 , - oy), sy - max (0 , oy + sy - imgh)
142
+ col_lo, col_hi = 1 + max (0 , - ox ), sx - max (0 , ox + sx - imgw)
117
143
118
144
if gcol === nothing
119
- for row = 1 + cliprowlo : h - cliprowhi, col = 1 + clipcollo : w - clipcolhi
120
- bitmaps[istr][col,row] == 0 && continue
121
- c1 = bitmaps[istr][col,row] / bitmapmax * fcol
122
- img[row + py - by, col + px + bx ] = T <: Integer ? round (T, c1 ) : T (c1 )
145
+ for r in row_lo : row_hi, c in col_lo : col_hi
146
+ (bm = bitmap[c, r]) == 0 && continue
147
+ color = bm / bitmap_max * fcol
148
+ img[oy + r, ox + c ] = T <: Integer ? round (T, color ) : T (color )
123
149
end
124
150
else
125
- img[
126
- clamp (py- ymax+ 1 , 1 , imgh) : clamp (py- ymin- 1 , 1 , imgh),
127
- clamp (px- 1 , 1 , imgw) : clamp (px+ sumadvancex- 1 , 1 , imgw)
128
- ] .= gcol
129
- for row = 1 + cliprowlo : h- cliprowhi, col = 1 + clipcollo : w- clipcolhi
130
- bitmaps[istr][col, row] == 0 && continue
131
- w1 = bitmaps[istr][col, row] / bitmapmax
132
- c1 = w1 * fcol
133
- c0 = (1.0 - w1) * gcol
134
- img[row + py - by, col + px + bx] = T <: Integer ? round (T, c1 + c0) : T (c1 + c0)
151
+ if gstr != = nothing
152
+ gmetric = round .(Int, extents (face, gstr[i], pixelsize))
153
+ y_min = bottominkbound (gmetric)
154
+ y_max = topinkbound (gmetric)
155
+ end
156
+
157
+ # fill background
158
+ by1, by2 = py - y_max, py - y_min
159
+ bx1, bx2 = px, px + ax
160
+ r1, r2 = clamp (by1, 1 , imgh), clamp (by2, 1 , imgh)
161
+ c1, c2 = clamp (bx1, 1 , imgw), clamp (bx2, 1 , imgw)
162
+ for r in r1 + off_bg: r2 - off_bg, c in c1 + off_bg: c2 - off_bg
163
+ img[r, c] = gcol
164
+ end
165
+
166
+ # render character by drawing the corresponding glyph
167
+ for r in row_lo: row_hi, c in col_lo: col_hi
168
+ (bm = bitmap[c, r]) == 0 && continue
169
+ w1 = bm / bitmap_max
170
+ color0 = w1 * fcol
171
+ color1 = (1.0 - w1) * gcol
172
+ img[oy + r, ox + c] = T <: Integer ? round (T, color0 + color1) : T (color0 + color1)
173
+ end
174
+
175
+ # draw background bounding box
176
+ if bbox != = nothing && r2 > r1 && c2 > c1
177
+ img[r1, c1: c2] .= bbox
178
+ img[r2, c1: c2] .= bbox
179
+ img[r1: r2, c1] .= bbox
180
+ img[r1: r2, c2] .= bbox
181
+ end
182
+ end
183
+
184
+ # draw glyph bounding box
185
+ if bbox_glyph != = nothing
186
+ rect = boundingbox (metric)
187
+ (mc, mr), (Mc, Mr) = extrema (rect)
188
+ r1, r2 = clamp (py + mr, 1 , imgh), clamp (py + Mr, 1 , imgh)
189
+ c1, c2 = clamp (px + mc, 1 , imgw), clamp (px + Mc, 1 , imgw)
190
+ # ^^^ should be equivalent to vvv
191
+ # r1, r2 = clamp(oy + row_lo, 1, imgh), clamp(oy + row_hi, 1, imgh)
192
+ # c1, c2 = clamp(ox + col_lo, 1, imgw), clamp(ox + col_hi, 1, imgw)
193
+ if r2 > r1 && c2 > c1
194
+ img[r1, c1: c2] .= bbox_glyph
195
+ img[r2, c1: c2] .= bbox_glyph
196
+ img[r1: r2, c1] .= bbox_glyph
197
+ img[r1: r2, c2] .= bbox_glyph
135
198
end
136
199
end
137
- px += metrics[istr]. advance[1 ]
200
+
201
+ px += ax + incx
138
202
end
139
203
return img
140
204
end
0 commit comments