* Render a character to the atlas * 将字符渲染到图集
(charCode: number)
| 275 | * 将字符渲染到图集 |
| 276 | */ |
| 277 | private renderCharacter(charCode: number): boolean { |
| 278 | const ctx = this._ctx; |
| 279 | const char = String.fromCharCode(charCode); |
| 280 | |
| 281 | // Measure character |
| 282 | ctx.font = `${this._fontSize}px "${this._fontFamily}"`; |
| 283 | const metrics = ctx.measureText(char); |
| 284 | const charWidth = Math.ceil(metrics.width); |
| 285 | const charHeight = Math.ceil(this._lineHeight); |
| 286 | |
| 287 | // Check if we need a new row |
| 288 | if (this._cursorX + charWidth + this._padding * 2 > this._atlasWidth) { |
| 289 | this._cursorX = 0; |
| 290 | this._cursorY += this._rowHeight + this._padding; |
| 291 | this._rowHeight = 0; |
| 292 | } |
| 293 | |
| 294 | // Check if we're out of space |
| 295 | if (this._cursorY + charHeight + this._padding * 2 > this._atlasHeight) { |
| 296 | console.warn(`[DynamicFont] Atlas full, cannot add character: ${char}`); |
| 297 | return false; |
| 298 | } |
| 299 | |
| 300 | const x = this._cursorX + this._padding; |
| 301 | const y = this._cursorY + this._padding; |
| 302 | |
| 303 | // Render character |
| 304 | ctx.fillStyle = 'white'; |
| 305 | ctx.fillText(char, x, y); |
| 306 | |
| 307 | // Create glyph info |
| 308 | const glyph: IDynamicGlyph = { |
| 309 | charCode, |
| 310 | x, |
| 311 | y, |
| 312 | width: charWidth, |
| 313 | height: charHeight, |
| 314 | advance: charWidth, |
| 315 | baseline: this._ascent |
| 316 | }; |
| 317 | |
| 318 | this._glyphs.set(charCode, glyph); |
| 319 | |
| 320 | // Update cursor |
| 321 | this._cursorX += charWidth + this._padding * 2; |
| 322 | this._rowHeight = Math.max(this._rowHeight, charHeight); |
| 323 | |
| 324 | // Mark dirty region |
| 325 | this.markDirty(x, y, charWidth, charHeight); |
| 326 | |
| 327 | return true; |
| 328 | } |
| 329 | |
| 330 | /** |
| 331 | * Mark a region as dirty |
no test coverage detected