Q: can text be clipped
glycerine opened this issue · 5 comments
I'm just curious if (and how if so) it is possible in Oak to clip text to be inside a given rectangle bounding-box. Here's an example of what that looks like in Gio if its not clear what I mean:
https://github.com/glycerine/hello_gio/blob/master/screenshot.png
Yes-- it isn't built in (yet) but it's not too bad to make a renderable type that cuts off other renderables within it:
package main
import (
"image"
"image/color"
"image/draw"
"github.com/oakmound/oak/v2"
"github.com/oakmound/oak/v2/render"
"github.com/oakmound/oak/v2/scene"
)
func main() {
var loop = true
oak.AddScene("bounding-box-example", scene.Scene{
Start: func(string, interface{}) {
testBB := NewBoundingBox(130, 15, []render.Renderable{
render.NewColorBox(130, 15, color.RGBA{50, 0, 0, 50}),
render.NewStrText("a very long text string- a very long text string", 0, 10),
})
testBB.SetPos(100, 100)
render.Draw(testBB, 0, 0)
},
Loop: scene.BooleanLoop(&loop),
End: scene.GoTo("bounding-box-example"),
})
oak.Init("bounding-box-example")
}
type BoundingBox struct {
render.LayeredPoint
rgba *image.RGBA
toBound []render.Renderable
}
func NewBoundingBox(w, h int, toBound []render.Renderable) *BoundingBox {
return &BoundingBox{
LayeredPoint: render.NewLayeredPoint(0, 0, 0),
rgba: image.NewRGBA(image.Rect(0, 0, w, h)),
toBound: toBound,
}
}
func (bb *BoundingBox) Draw(buff draw.Image) {
bb.DrawOffset(buff, 0, 0)
}
func (bb *BoundingBox) DrawOffset(buff draw.Image, xOff, yOff float64) {
for _, bound := range bb.toBound {
bound.Draw(bb.rgba)
}
render.ShinyDraw(buff, bb.rgba, int(bb.X()+xOff), int(bb.Y()+yOff))
}
func (bb *BoundingBox) GetDims() (int, int) {
rect := bb.rgba.Rect
return rect.Max.X, rect.Max.Y
}
Another approach that treats transparency differently and has fewer pixel-drawing iterations, in exchange for some additional complexity:
type BoundingBox struct {
render.LayeredPoint
width, height int
toBound []render.Renderable
}
func NewBoundingBox(w, h int, toBound []render.Renderable) *BoundingBox {
return &BoundingBox{
LayeredPoint: render.NewLayeredPoint(0, 0, 0),
width: w,
height: h,
toBound: toBound,
}
}
type boundImage struct {
x, y int
width, height int
draw.Image
}
func (b boundImage) Set(x, y int, c color.Color) {
if x > b.width+b.x || y > b.height+b.x {
return
}
if x < b.x || y < b.y {
return
}
b.Image.Set(x, y, c)
}
func (bb *BoundingBox) Draw(buff draw.Image) {
bb.DrawOffset(buff, 0, 0)
}
func (bb *BoundingBox) DrawOffset(buff draw.Image, xOff, yOff float64) {
wrapperImg := boundImage{x: int(bb.X()), y: int(bb.Y()), width: bb.width, height: bb.height, Image: buff}
for _, bound := range bb.toBound {
bound.DrawOffset(wrapperImg, bb.X(), bb.Y())
}
}
func (bb *BoundingBox) GetDims() (int, int) {
return bb.width, bb.height
}
awesome. thank you so much!
By the way I'm using the first less efficient version because the 2nd one is buggy (most text isn't shown) and I haven't had time to debug it yet.
Ah. I see it.
This:
func (b boundImage) Set(x, y int, c color.Color) {
if x > b.width+b.x || y > b.height+b.x {
should be
func (b boundImage) Set(x, y int, c color.Color) {
if x > b.width+b.x || y > b.height+b.y { // change b.x to b.y in the 2nd instance