github.com/kintar/etxt@v0.0.9/renderer_draw.go (about) 1 package etxt 2 3 import "strconv" 4 5 import "golang.org/x/image/math/fixed" 6 7 import "github.com/kintar/etxt/emask" 8 9 // Drawing functions for the Renderer type. 10 11 // Draws the given text with the current configuration (font, size, color, 12 // target, etc). The position at which the text will be drawn depends on 13 // the given pixel coordinates and the renderer's align (see 14 // [Renderer.SetAlign]() rules). 15 // 16 // The returned value should be ignored except on advanced use-cases 17 // (refer to [Renderer.Traverse]() documentation). 18 // 19 // Missing glyphs in the current font will cause the renderer to panic. 20 // See [GetMissingRunes]() if you need to make your system more robust. 21 // 22 // Line breaks encoded as \n will be handled automatically. 23 func (self *Renderer) Draw(text string, x, y int) fixed.Point26_6 { 24 fx, fy := fixed.Int26_6(x<<6), fixed.Int26_6(y<<6) 25 return self.DrawFract(text, fx, fy) 26 } 27 28 // Exactly the same as [Renderer.Draw](), but accepting [fractional pixel] coordinates. 29 // 30 // Notice that passing a fractional coordinate won't make the draw operation 31 // be fractionally aligned by itself, that still depends on the renderer's 32 // [QuantizationMode]. 33 // 34 // [fractional pixel]: https://github.com/kintar/etxt/blob/main/docs/fixed-26-6.md 35 func (self *Renderer) DrawFract(text string, x, y fixed.Int26_6) fixed.Point26_6 { 36 // safety checks 37 if self.target == nil { 38 panic("draw called while target == nil (tip: renderer.SetTarget())") 39 } 40 if self.font == nil { 41 panic("draw called while font == nil (tip: renderer.SetFont())") 42 } 43 if text == "" { 44 return fixed.Point26_6{X: x, Y: y} 45 } 46 47 // traverse text and draw each glyph 48 return self.Traverse(text, fixed.Point26_6{X: x, Y: y}, 49 func(currentDot fixed.Point26_6, codePoint rune, glyphIndex GlyphIndex) { 50 if codePoint == '\n' { 51 return 52 } 53 mask := self.LoadGlyphMask(glyphIndex, currentDot) 54 self.DefaultDrawFunc(currentDot, mask, glyphIndex) 55 }) 56 } 57 58 // Low-level function typically used with [Renderer.Traverse]*() functions when 59 // drawing glyph masks manually. 60 // 61 // LoadGlyphMask loads the mask for the given glyph at the given fractional 62 // pixel position. The renderer's cache handler, font, size, rasterizer and 63 // mask format are all taken into account. 64 func (self *Renderer) LoadGlyphMask(index GlyphIndex, dot fixed.Point26_6) GlyphMask { 65 // if the mask is available in the cache, that's all 66 if self.cacheHandler != nil { 67 glyphMask, found := self.cacheHandler.GetMask(index) 68 if found { 69 return glyphMask 70 } 71 } 72 73 // glyph mask not cached, let's rasterize on our own 74 segments, err := self.font.LoadGlyph(&self.buffer, index, self.sizePx, nil) 75 if err != nil { 76 // if you need to deal with missing glyphs, you should do so before 77 // reaching this point with functions like GetMissingRunes() and 78 // replacing the relevant runes or glyphs 79 panic("font.LoadGlyph(index = " + strconv.Itoa(int(index)) + ") error: " + err.Error()) 80 } 81 82 // rasterize the glyph mask 83 alphaMask, err := emask.Rasterize(segments, self.rasterizer, dot) 84 if err != nil { 85 panic("RasterizeGlyphMask failed: " + err.Error()) 86 } 87 88 // pass to cache and return 89 glyphMask := convertAlphaImageToGlyphMask(alphaMask) 90 if self.cacheHandler != nil { 91 self.cacheHandler.PassMask(index, glyphMask) 92 } 93 return glyphMask 94 }