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