Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0beef86c58 | |||
| 6a60458484 | |||
| 021dd288b6 | |||
| b0e80ce961 |
50
drawer.go
50
drawer.go
@@ -13,25 +13,31 @@ 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,
|
||||
color color.Color,
|
||||
col color.Color,
|
||||
offset image.Point,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
source := image.NewUniform(color)
|
||||
source := image.NewUniform(col)
|
||||
|
||||
drawer.For (func (
|
||||
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 (
|
||||
fixed.P (
|
||||
offset.X + position.X.Round(),
|
||||
offset.Y + position.Y.Round()),
|
||||
char)
|
||||
if !ok || unicode.IsSpace(char) || char == 0 {
|
||||
mask, maskPoint, _, ok := drawer.face.Glyph(dot, char)
|
||||
// tofu
|
||||
if !ok {
|
||||
drawer.drawTofu(char, destination, col, dot)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -50,3 +56,29 @@ func (drawer Drawer) Draw (
|
||||
})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
27
layout.go
27
layout.go
@@ -58,13 +58,16 @@ func DoWord (text []rune, face font.Face) (word WordLayout, remaining []rune) {
|
||||
|
||||
// consume and process the rune
|
||||
remaining = remaining[1:]
|
||||
_, advance, ok := face.GlyphBounds(char)
|
||||
if !ok { continue }
|
||||
word.Runes = append (word.Runes, RuneLayout {
|
||||
advance, ok := face.GlyphAdvance(char)
|
||||
if !ok {
|
||||
advance = tofuAdvance(face)
|
||||
}
|
||||
runeLayout := RuneLayout {
|
||||
X: x,
|
||||
Width: advance,
|
||||
Rune: char,
|
||||
})
|
||||
}
|
||||
word.Runes = append(word.Runes, runeLayout)
|
||||
|
||||
// advance
|
||||
if gettingSpace {
|
||||
@@ -209,3 +212,19 @@ func (line *LineLayout) justify () {
|
||||
x += spacePerWord + word.Width
|
||||
}
|
||||
}
|
||||
|
||||
func tofuAdvance (face font.Face) fixed.Int26_6 {
|
||||
if advance, ok := face.GlyphAdvance('M'); ok {
|
||||
return advance
|
||||
} else {
|
||||
return 16
|
||||
}
|
||||
}
|
||||
|
||||
func tofuBounds (face font.Face) (fixed.Rectangle26_6, fixed.Int26_6) {
|
||||
if bounds, advance, ok := face.GlyphBounds('M'); ok {
|
||||
return bounds, advance
|
||||
} else {
|
||||
return fixed.R(0, -16, 14, 0), 16
|
||||
}
|
||||
}
|
||||
|
||||
31
setter.go
31
setter.go
@@ -259,8 +259,18 @@ type RuneIterator func (
|
||||
)
|
||||
|
||||
// For calls the specified iterator for every rune in the typesetter. If the
|
||||
// iterator returns false, the loop will immediately stop.
|
||||
// iterator returns false, the loop will immediately stop. This method will
|
||||
// insert a fake null rune at the end.
|
||||
func (setter *TypeSetter) For (iterator RuneIterator) {
|
||||
setter.forInternal(iterator, true)
|
||||
}
|
||||
|
||||
// ForRunes is like For, but leaves out the fake null rune.
|
||||
func (setter *TypeSetter) ForRunes (iterator RuneIterator) {
|
||||
setter.forInternal(iterator, false)
|
||||
}
|
||||
|
||||
func (setter *TypeSetter) forInternal (iterator RuneIterator, fakeNull bool) {
|
||||
setter.needAlignedLayout()
|
||||
|
||||
index := 0
|
||||
@@ -271,7 +281,7 @@ func (setter *TypeSetter) For (iterator RuneIterator) {
|
||||
for _, word := range line.Words {
|
||||
for _, char := range word.Runes {
|
||||
lastCharRightBound = word.X + char.X + char.Width
|
||||
keepGoing := iterator (index, char.Rune, fixed.Point26_6 {
|
||||
keepGoing := iterator(index, char.Rune, fixed.Point26_6 {
|
||||
X: word.X + char.X,
|
||||
Y: line.Y,
|
||||
})
|
||||
@@ -280,21 +290,24 @@ func (setter *TypeSetter) For (iterator RuneIterator) {
|
||||
}}
|
||||
|
||||
if line.BreakAfter {
|
||||
keepGoing := iterator (index, '\n', fixed.Point26_6 {
|
||||
keepGoing := iterator(index, '\n', fixed.Point26_6 {
|
||||
X: lastCharRightBound,
|
||||
Y: line.Y,
|
||||
})
|
||||
if !keepGoing { return }
|
||||
index ++
|
||||
lastCharRightBound = fixed.Int26_6(0)
|
||||
}
|
||||
}
|
||||
|
||||
keepGoing := iterator (index, '\000', fixed.Point26_6 {
|
||||
X: lastCharRightBound,
|
||||
Y: lastLineY,
|
||||
})
|
||||
if !keepGoing { return }
|
||||
index ++
|
||||
if fakeNull {
|
||||
keepGoing := iterator (index, '\000', fixed.Point26_6 {
|
||||
X: lastCharRightBound,
|
||||
Y: lastLineY,
|
||||
})
|
||||
if !keepGoing { return }
|
||||
index ++
|
||||
}
|
||||
}
|
||||
|
||||
// AtPosition returns the index of the rune at the specified position.
|
||||
|
||||
Reference in New Issue
Block a user