comparison samples/freetype.tp @ 324:615f23450f8f

Freetype sample: Build texture of glyphs in a more intelligent way
author Michael Pavone <pavone@retrodev.com>
date Mon, 23 Mar 2015 21:21:43 -0700
parents 3edd0169311a
children 50760ba52b11
comparison
equal deleted inserted replaced
323:eb5f1fca9b78 324:615f23450f8f
13 ] from: (sdl textureAccess) 13 ] from: (sdl textureAccess)
14 14
15 import: [ 15 import: [
16 bgra8888 16 bgra8888
17 ] from: (sdl pixelFormats) 17 ] from: (sdl pixelFormats)
18
19 import: [
20 render
21 linearDesign
22 ] from: (freetype loadFlags)
23
24 makeAtlas <- :renderer face size dpi color {
25 face setCharSize: size res: dpi
26 slot <- face glyphSlot
27
28 glyphs <- #[]
29 //TODO: Use a bytearray once that has an append method
30 pixels <- #[]
31 foreach: (face charmap) :char glyphIndex {
32 face loadGlyph: glyphIndex flags: (render or linearDesign)
33 pixelStart <- pixels length
34 _width <- slot bitmapWidth
35 _height <- slot bitmapRows
36 pitch <- slot bitmapPitch
37 buffer <- slot bitmapData
38
39 y <- 0
40 while: { y < _height } do: {
41 x <- 0
42 idx <- y * pitch
43 while: { x < _width } do: {
44 pixels append: (buffer get: idx)
45
46 x <- x + 1
47 idx <- idx + 1
48 }
49 y <- y + 1
50 }
51
52 glyphs append: #{
53 width <- _width
54 height <- _height
55 pixelOffset <- pixelStart
56 hAdvance <- slot linearHoriAdvance
57 vAdvance <- slot linearVertAdvance
58 leftOffset <- (slot bitmapLeft)
59 topOffset <- (slot bitmapTop)
60 charCode <- char
61
62 atlasX <- -1
63 atlasY <- -1
64
65 <= <- :other {
66 if: height > (other height) {
67 true
68 } else: {
69 if: height < (other height) {
70 false
71 } else: {
72 width >= (other width)
73 }
74 }
75 }
76 }
77 }
78 glyphs sort
79 maxDim <- 2048
80 aWidth <- 128
81 minSize <- maxDim * maxDim
82 minSizeWidth <- -1
83 aHeight <- maxDim
84
85 while: { aWidth <= maxDim } do: {
86 print: "Checking width: " . aWidth . "\n"
87 curX <- 0
88 curY <- 0
89 minHeight <- 0
90 avail <- glyphs foldr: [] with: :acc val {
91 val | acc
92 }
93 while: { (not: (avail empty?)) && curY <= maxDim } do: {
94 curGlyph <- avail value
95 if: curX + (curGlyph width) < aWidth {
96 if: curY + (curGlyph height) < maxDim {
97 curX <- curX + (curGlyph width)
98 avail <- avail tail
99 if: (curGlyph height) > minHeight {
100 minHeight <- curGlyph height
101 }
102 } else: {
103 curY <- maxDim + 1
104 }
105 } else: {
106 skinny <- option none
107 if: aWidth > curX {
108 availPixels <- aWidth - curX
109
110 skinny <- avail find: :val {
111 (val width) <= availPixels
112 }
113 }
114
115 skinny value: :curGlyph {
116 curX <- curX + (curGlyph width)
117 if: (curGlyph height) > minHeight {
118 minHeight <- curGlyph height
119 }
120 avail <- avail filter: :glyph {
121 (glyph charCode) != (curGlyph charCode)
122 }
123 } none: {
124 curY <- curY + minHeight
125 minHeight <- 0
126 curX <- 0
127 }
128 }
129 }
130 if: (avail empty?) {
131 aHeight <- curY + minHeight
132 p2Height <- 1
133 while: { p2Height < aHeight } do: {
134 p2Height <- lshift: p2Height by: 1
135 }
136
137 size <- aWidth * p2Height
138 if: size < minSize {
139 minSize <- size
140 minSizeWidth <- aWidth
141 }
142 }
143 aWidth <- aWidth * 2
144 }
145 if: minSizeWidth > -1 {
146 print: "Best width: " . minSizeWidth . ", height: " . (minSize / minSizeWidth) . "\n"
147 aWidth <- minSizeWidth
148 aHeight <- minSize / minSizeWidth
149
150 (renderer createTexture: bgra8888 access: streaming width: aWidth height: aHeight) value: :drawTex {
151 drawTex blendMode!: ((sdl blendModes) blend)
152 //TODO: Use "update" with a static texture
153 drawTex lockRect: (sdl rect: 0 0 size: aWidth aHeight) with: :bytearr pitch {
154 n <- aHeight * pitch
155 i <- 0
156 while: { i < n } do: {
157 bytearr set: i 0u8
158 i <- i + 1
159 }
160 curX <- 0
161 curY <- 0
162 minHeight <- 0
163 avail <- glyphs foldr: [] with: :acc val {
164 val | acc
165 }
166 while: { not: (avail empty?) } do: {
167 curGlyph <- avail value
168 if: curX + (curGlyph width) < aWidth {
169 curGlyph atlasX!: curX
170 curGlyph atlasY!: curY
171 y <- 0
172 dstY <- curY
173 idx <- curGlyph pixelOffset
174 while: { y < (curGlyph height) } do: {
175 dstIdx <- dstY * pitch + curX * 4
176 x <- 0
177 while: { x < (curGlyph width) } do: {
178 //FIXME: This will probably only work on little endian machines
179 bytearr set: dstIdx (pixels get: idx)
180 dstIdx <- dstIdx + 1
181 bytearr set: dstIdx (color r)
182 dstIdx <- dstIdx + 1
183 bytearr set: dstIdx (color g)
184 dstIdx <- dstIdx + 1
185 bytearr set: dstIdx (color b)
186 dstIdx <- dstIdx + 1
187
188 idx <- idx + 1
189 x <- x + 1
190 }
191 y <- y + 1
192 dstY <- dstY + 1
193 }
194
195 curX <- curX + (curGlyph width)
196 avail <- avail tail
197 if: (curGlyph height) > minHeight {
198 minHeight <- curGlyph height
199 }
200 } else: {
201 skinny <- option none
202 if: aWidth > curX {
203 availPixels <- aWidth - curX
204 skinny <- avail find: :val {
205 (val width) <= availPixels
206 }
207 }
208
209 skinny value: :curGlyph {
210 curX <- curX + (curGlyph width)
211 if: (curGlyph height) > minHeight {
212 minHeight <- curGlyph height
213 }
214 avail <- avail filter: :glyph {
215 (glyph charCode) != (curGlyph charCode)
216 }
217 } none: {
218 curY <- curY + minHeight
219 minHeight <- 0
220 curX <- 0
221 }
222 }
223 }
224 }
225 glyphDict <- dict hash
226 foreach: glyphs :idx glyph {
227 glyphDict set: (glyph charCode) glyph
228 }
229 option value: #{
230 texture <- drawTex
231 width <- aWidth
232 height <- aHeight
233 glyphs <- glyphDict
234 }
235 } none: {
236 print: "Failed to create texture for atlas"
237 option none
238 }
239 } else: {
240 print: "Font is too big for a 2048x2048 texture!"
241 option none
242 }
243 }
18 244
19 hex2 <- :num { 245 hex2 <- :num {
20 val <- hex: num 246 val <- hex: num
21 if: (val length) < 2 { 247 if: (val length) < 2 {
22 val <- "0" . val 248 val <- "0" . val
29 dpi <- 96 255 dpi <- 96
30 arg <- 1 256 arg <- 1
31 expectVal <- false 257 expectVal <- false
32 optName <- "" 258 optName <- ""
33 path <- "" 259 path <- ""
260 windowWidth <- 512
261 windowHeight <- 512
34 while: { arg < (args length) } do: { 262 while: { arg < (args length) } do: {
35 curArg <- args get: arg 263 curArg <- args get: arg
36 if: expectVal { 264 if: expectVal {
37 if: optName = "--dpi" || optName = "-d" { 265 if: optName = "--dpi" || optName = "-d" {
38 dpi <- curArg int32 266 dpi <- curArg int32
39 } else: { 267 } else: {
40 print: "Unrecognized option: " . optName . "\n" 268 if: optName = "--height" {
269 windowHeight <- curArg int32
270 } else: {
271 if: optName = "--width" {
272 windowWidth <- curArg int32
273 } else: {
274 print: "Unrecognized option: " . optName . "\n"
275 }
276 }
41 } 277 }
42 expectVal <- false 278 expectVal <- false
43 } else: { 279 } else: {
44 if: (curArg startsWith?: "-") { 280 if: (curArg startsWith?: "-") {
45 expectVal <- true 281 expectVal <- true
59 print: "Char: " . char . ", glyph index: " . glyph . "\n" 295 print: "Char: " . char . ", glyph index: " . glyph . "\n"
60 charCodes append: char 296 charCodes append: char
61 } 297 }
62 298
63 if: (sdl init: (video or timer)) = 0 { 299 if: (sdl init: (video or timer)) = 0 {
64 (sdl createWindow: "Freetype Test" pos: 0 0 size: 512 512 flags: 0u32) value: :window { 300 (sdl createWindow: "Freetype Test" pos: 0 0 size: windowWidth windowHeight flags: 0u32) value: :window {
65 (window createRenderer: -1 flags: ((window renderOpts) accelerated)) value: :renderer { 301 (window createRenderer: -1 flags: ((window renderOpts) accelerated)) value: :renderer {
66 renderer drawColor!: (sdl r: 255u8 g: 255u8 b: 255u8) 302 renderer drawColor!: (sdl r: 255u8 g: 255u8 b: 255u8)
67 (renderer createTexture: bgra8888 access: streaming width: 512 height: 512) value: :drawTex { 303
68 drawTex blendMode!: ((sdl blendModes) blend) 304 (makeAtlas: renderer face 12.0 dpi (sdl r: 0u8 g: 0u8 b: 0u8)) value: :atlas {
69 drawTex lockRect: (sdl rect: 0 0 size: 512 512) with: :bytearr pitch {
70 i <- 0
71 n <- charCodes length
72 maxHeight <- 0
73 startY <- 0
74 startX <- 0
75 slot <- face glyphSlot
76 face setCharSize: 12.0 res: dpi
77 while: { i < n && startY < 512 } do: {
78 charCode <- charCodes get: i
79 glyphIndex <- charMap get: charCode else: { 0 }
80 rescode <- face loadGlyph: glyphIndex flags: ((freetype loadFlags) render)
81 if: rescode = 0 {
82 height <- slot bitmapRows
83 width <- slot bitmapWidth
84 if: startX + width > 512 {
85 startY <- startY + maxHeight
86 startX <- 0
87 maxHeight <- 0
88 }
89
90 if: height > maxHeight {
91 maxHeight <- height
92 }
93
94 if: height + startY > 512 {
95 startY <- 512
96 } else: {
97 print: "Rendering glyph " . glyphIndex . " to " . startX . ", " . startY . " (" . (startY * pitch + 4 * startX) . ")\n"
98 print: "Width: " . width . ", Height: " . height . "\n"
99 destY <- startY
100 srcY <- 0
101 srcPitch <- slot bitmapPitch
102 srcBitmap <- slot bitmapData
103 while: { srcY < height } do: {
104 line <- ""
105 destIndex <- destY * pitch + startX * 4
106 srcIndex <- srcY * srcPitch
107 destX <- startX
108 srcX <- 0
109 while: { srcX < width } do: {
110 srcPixel <- srcBitmap get: srcIndex
111 bytearr set: destIndex srcPixel
112 destIndex <- destIndex + 1
113 bytearr set: destIndex 0u8
114 destIndex <- destIndex + 1
115 bytearr set: destIndex 0u8
116 destIndex <- destIndex + 1
117 bytearr set: destIndex 0u8
118 line <- line . " " . (hex2: srcPixel)
119
120 destX <- destX + 1
121 srcX <- srcX + 1
122 destIndex <- destIndex + 1
123 srcIndex <- srcIndex + 1
124 }
125 print: line . "\n"
126 destY <- destY + 1
127 srcY <- srcY + 1
128 }
129 }
130 startX <- startX + width
131
132 i <- i + 1
133 } else: {
134 print: "Got error " . rescode . " when loading glyph " . glyphIndex . "\n"
135 }
136 }
137 }
138 continue? <- true 305 continue? <- true
139 while: { continue? } do: { 306 while: { continue? } do: {
140 renderer clear 307 renderer clear
141 drawTex copy 308 y <- 0
309 x <- 0
310 while: { y < (atlas height) } do: {
311 copyWidth <- if: (atlas width) < windowWidth { atlas width } else: { windowWidth }
312 copyHeight <- if: (atlas height) < windowHeight { atlas height } else: { windowHeight }
313 (atlas texture) copyRect: (sdl rect: 0 y size: copyWidth copyHeight) To: (sdl rect: x 0 size: copyWidth copyHeight)
314 y <- y + windowHeight
315 x <- x + copyWidth
316 }
142 renderer present 317 renderer present
143 event <- option none 318 event <- option none
144 while: { 319 while: {
145 event <- sdl pollEvent 320 event <- sdl pollEvent
146 event value? 321 event value?
151 } 326 }
152 } none: {} 327 } none: {}
153 } 328 }
154 } 329 }
155 } none: { 330 } none: {
156 print: "Failed to create texture\n" 331 retcode <- -1
157 retcode <- 1
158 } 332 }
159 } none: { 333 } none: {
160 print: "Failed to create renderer\n" 334 print: "Failed to create renderer\n"
161 retcode <- 1 335 retcode <- 1
162 } 336 }