diff --git a/draw.go b/draw.go new file mode 100644 index 0000000..1fc6af7 --- /dev/null +++ b/draw.go @@ -0,0 +1,103 @@ +package typeset + +import "image" +import "unicode" +import "image/draw" +import "image/color" +import "golang.org/x/image/font" +import "golang.org/x/image/math/fixed" + +// Draw draws the contents of a TypeSetter to an image at the given offset. It +// returns a rectangle containing all pixels in the image that were updated. +func Draw (destination draw.Image, setter *TypeSetter, offset fixed.Point26_6, col color.Color) image.Rectangle { + source := image.NewUniform(col) + face := setter.Face() + var updatedRegion image.Rectangle + bounds := destination.Bounds() + + setter.Runes()(func (position fixed.Point26_6, run rune) bool { + // leave empty space for space characters + if unicode.IsSpace(run) { + return true + } + + dot := offset.Add(position) + destinationRectangle, mask, maskPoint, _, ok := face.Glyph(dot, run) + + if ok { + // don't bother drawing runes that are out of bounds + if destinationRectangle.Min.Y > bounds.Max.Y { return false } + if destinationRectangle.Intersect(bounds).Empty() { return true } + + // draw rune + draw.DrawMask ( + destination, + destinationRectangle, + source, image.Point { }, + mask, maskPoint, + draw.Over) + } else { + // draw tofu + drawTofu(run, destination, dot, face, col) + } + + + updatedRegion = updatedRegion.Union(destinationRectangle) + return true + }) + + return updatedRegion +} + +// DrawBounds draws the LayoutBounds, MinimumSize, and LayoutBoundsSpace of a +// TypeSetter to the given image. +func DrawBounds (destination draw.Image, setter *TypeSetter, offset fixed.Point26_6) { + blue := color.RGBA { B: 255, A: 255 } + red := color.RGBA { R: 255, A: 255 } + green := color.RGBA { G: 255, A: 255 } + + layoutBoundsSpace := setter.LayoutBoundsSpace() + layoutBounds := setter.LayoutBounds() + + minimum := setter.MinimumSize() + minimumRect := roundRect(fixed.Rectangle26_6 { Max: minimum }.Add(offset).Add(layoutBounds.Min)) + drawRectangleOutline(destination, minimumRect, green) + + drawRectangleOutline(destination, roundRect(layoutBoundsSpace.Add(offset)), blue) + drawRectangleOutline(destination, roundRect(layoutBounds.Add(offset)), red) +} + +func drawTofu ( + char rune, + destination draw.Image, + position fixed.Point26_6, + face font.Face, + col color.Color, +) { + bounds, _ := tofuBounds(face) + rectBounds := roundRect(bounds).Add(image.Pt ( + position.X.Round(), + position.Y.Round())) + drawRectangleOutline(destination, rectBounds, col) +} + +func drawRectangleOutline (destination draw.Image, bounds image.Rectangle, col color.Color) { + for x := bounds.Min.X; x < bounds.Max.X; x ++ { + destination.Set(x, bounds.Min.Y, col) + } + for y := bounds.Min.Y; y < bounds.Max.Y; y ++ { + destination.Set(bounds.Min.X, y, col) + destination.Set(bounds.Max.X - 1, y, col) + } + for x := bounds.Min.X; x < bounds.Max.X; x ++ { + destination.Set(x, bounds.Max.Y - 1, col) + } +} + +func roundRect (rectangle fixed.Rectangle26_6) image.Rectangle { + return image.Rect ( + rectangle.Min.X.Round(), + rectangle.Min.Y.Round(), + rectangle.Max.X.Round(), + rectangle.Max.Y.Round()) +}