85 lines
2.0 KiB
Go
85 lines
2.0 KiB
Go
|
package typeset
|
||
|
|
||
|
import "image"
|
||
|
import "unicode"
|
||
|
import "image/draw"
|
||
|
import "image/color"
|
||
|
import "golang.org/x/image/math/fixed"
|
||
|
|
||
|
// Drawer is an extended TypeSetter that is able to draw text. Much like
|
||
|
// TypeSetter, It has no constructor and its zero value can be used safely.
|
||
|
type Drawer struct { TypeSetter }
|
||
|
|
||
|
// Draw draws the drawer's text onto the specified canvas at the given offset.
|
||
|
func (drawer Drawer) Draw (
|
||
|
destination draw.Image,
|
||
|
col color.Color,
|
||
|
offset image.Point,
|
||
|
) (
|
||
|
updatedRegion image.Rectangle,
|
||
|
) {
|
||
|
source := image.NewUniform(col)
|
||
|
|
||
|
drawer.ForRunes (func (
|
||
|
index int,
|
||
|
char rune,
|
||
|
position fixed.Point26_6,
|
||
|
) bool {
|
||
|
// leave empty space for space characters
|
||
|
if unicode.IsSpace(char) {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
dot := fixed.P (
|
||
|
offset.X + position.X.Round(),
|
||
|
offset.Y + position.Y.Round())
|
||
|
destinationRectangle,
|
||
|
mask, maskPoint, _, ok := drawer.face.Glyph(dot, char)
|
||
|
// tofu
|
||
|
if !ok {
|
||
|
drawer.drawTofu(char, destination, col, dot)
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// FIXME:? clip destination rectangle if we are on the cusp of
|
||
|
// the maximum height.
|
||
|
|
||
|
draw.DrawMask (
|
||
|
destination,
|
||
|
destinationRectangle,
|
||
|
source, image.Point { },
|
||
|
mask, maskPoint,
|
||
|
draw.Over)
|
||
|
|
||
|
updatedRegion = updatedRegion.Union(destinationRectangle)
|
||
|
return true
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (drawer Drawer) drawTofu (
|
||
|
char rune,
|
||
|
destination draw.Image,
|
||
|
col color.Color,
|
||
|
position fixed.Point26_6,
|
||
|
) {
|
||
|
bounds, _ := tofuBounds(drawer.face)
|
||
|
rectBounds := image.Rect (
|
||
|
bounds.Min.X.Round(),
|
||
|
bounds.Min.Y.Round(),
|
||
|
bounds.Max.X.Round(),
|
||
|
bounds.Max.Y.Round()).Add(image.Pt(
|
||
|
position.X.Round(),
|
||
|
position.Y.Round()))
|
||
|
for x := rectBounds.Min.X; x < rectBounds.Max.X; x ++ {
|
||
|
destination.Set(x, rectBounds.Min.Y, col)
|
||
|
}
|
||
|
for y := rectBounds.Min.Y; y < rectBounds.Max.Y; y ++ {
|
||
|
destination.Set(rectBounds.Min.X, y, col)
|
||
|
destination.Set(rectBounds.Max.X - 1, y, col)
|
||
|
}
|
||
|
for x := rectBounds.Min.X; x < rectBounds.Max.X; x ++ {
|
||
|
destination.Set(x, rectBounds.Max.Y - 1, col)
|
||
|
}
|
||
|
}
|