Update shaper_usage.md with correct glyph drawing approach

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Greg Pomerantz 2026-05-09 11:15:57 -04:00
parent fcd2fea870
commit 0f62ef32fa

View File

@ -159,34 +159,52 @@ func charWidths(gtx layout.Context, shp *text.Shaper, str string, size unit.Sp)
## 5. Rendering Glyphs Directly
Instead of using `material.Label()` (which shapes text again), draw glyphs directly:
Gio's textView draws glyphs using `shaper.Shape()` (for vector glyphs) and `shaper.Bitmaps()` (for bitmap glyphs like emoji). This is the correct approach to avoid double-shaping.
### 5.1 Drawing a Single Glyph
### 5.1 Drawing Text Using Gio's textView Approach
```go
// After layout, iterate through glyphs
for {
g, ok := shp.NextGlyph()
if !ok {
break
func drawText(gtx layout.Context, shp *text.Shaper, str string, size unit.Sp, x, y unit.Dp, col color.NRGBA) {
// Layout text
shp.LayoutString(text.Parameters{PxPerEm: fixed.I(gtx.Sp(size))}, str)
// Draw glyphs using the same approach as Gio's textView
var glyphs [32]text.Glyph
line := glyphs[:0]
for g, ok := shp.NextGlyph(); ok; g, ok = shp.NextGlyph() {
line = append(line, g)
if g.Flags&text.FlagLineBreak != 0 || cap(line)-len(line) == 0 {
drawLine(gtx, shp, line, x, y, col)
line = line[:0]
}
}
// Draw glyph at position (g.X, g.Y)
// Use shp.DrawGlyph() or manual drawing
if len(line) > 0 {
drawLine(gtx, shp, line, x, y, col)
}
}
func drawLine(gtx layout.Context, shp *text.Shaper, line []text.Glyph, x, y unit.Dp, col color.NRGBA) {
// Apply offset transform
off := f32.Point{X: float32(x), Y: float32(y)}
t := op.Affine(f32.Affine2D{}.Offset(off)).Push(gtx.Ops)
// Draw vector glyphs
path := shp.Shape(line)
outline := clip.Outline{Path: path}.Op().Push(gtx.Ops)
paint.ColorOp{Color: col}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops)
outline.Pop()
// Draw bitmap glyphs (emoji, etc.)
if call := shp.Bitmaps(line); call != (op.CallOp{}) {
call.Add(gtx.Ops)
}
t.Pop()
}
```
### 5.2 Using Shaper's Drawing Functions
The shaper provides drawing functions that use the already-computed glyph data:
```go
// Draw all laid-out glyphs
shp.Draw(gtx, ops, color.NRGBA{R: 0, G: 0, B: 0, A: 255})
```
**Important**: Call `shp.Draw()` AFTER calling `LayoutString()` or `Layout()`. The drawing functions use the glyph data computed during the layout phase.
### 5.3 Avoiding Double-Shaping
### 5.2 Avoiding Double-Shaping
**WRONG** (shapes text twice):
@ -198,14 +216,19 @@ widths := charWidths(gtx, shp, text, size)
material.Label(th, size, text).Layout(gtx)
```
**CORRECT** (shapes text once):
**CORRECT** (shapes text once for rendering):
```go
// Single pass: layout and draw
shp.LayoutString(text.Parameters{PxPerEm: fixed.I(gtx.Sp(size))}, text)
shp.Draw(gtx, ops, color.NRGBA{R: 0, G: 0, B: 0, A: 255})
// Single pass: layout and draw using shaper.Shape()
drawText(gtx, shp, text, size, x, y, col)
```
**NOTE**: For truncation, we may need to layout twice:
1. First layout to measure widths and find truncation point
2. Second layout to draw the truncated text
This is acceptable because truncation is only needed when text doesn't fit, and the string is short (filename in StatusBar).
## 6. Comparison with Gio's textView
Gio's textView (`widget/text.go`) demonstrates the correct approach: