diff --git a/artist/pattern.go b/artist/pattern.go new file mode 100644 index 0000000..ef87caa --- /dev/null +++ b/artist/pattern.go @@ -0,0 +1,12 @@ +package artist + +import "image/color" + +// Pattern is capable of generating a pattern pixel by pixel. +type Pattern interface { + // AtWhen returns the color of the pixel located at (x, y) relative to + // the origin point of the pattern (0, 0), when the pattern has the + // specified width and height. Patterns may ignore the width and height + // parameters, but it may be useful for some patterns such as gradients. + AtWhen (x, y, width, height int) (color.RGBA) +} diff --git a/artist/rectangle.go b/artist/rectangle.go index 3b613be..4e15469 100644 --- a/artist/rectangle.go +++ b/artist/rectangle.go @@ -29,6 +29,7 @@ func Paste ( return } +// FillRectangle draws a filled rectangle with the specified pattern. func FillRectangle ( destination tomo.Canvas, source Pattern, diff --git a/artist/text.go b/artist/text.go index b1c1e61..823db44 100644 --- a/artist/text.go +++ b/artist/text.go @@ -3,7 +3,7 @@ package artist // import "fmt" import "image" import "unicode" -// import "image/draw" +import "image/draw" import "golang.org/x/image/font" import "golang.org/x/image/math/fixed" import "git.tebibyte.media/sashakoshka/tomo" @@ -100,30 +100,36 @@ func (drawer *TextDrawer) Draw ( ) ( updatedRegion image.Rectangle, ) { + wrappedSource := WrappedPattern { + Pattern: source, + Width: 0, + Height: 0, // TODO: choose a better width and height + } + if !drawer.layoutClean { drawer.recalculate() } // TODO: reimplement a version of draw mask that takes in a pattern - // for _, word := range drawer.layout { - // for _, character := range word.text { - // destinationRectangle, - // mask, maskPoint, _, ok := drawer.face.Glyph ( - // fixed.P ( - // offset.X + word.position.X + character.x, - // offset.Y + word.position.Y), - // character.character) - // if !ok { continue } + for _, word := range drawer.layout { + for _, character := range word.text { + destinationRectangle, + mask, maskPoint, _, ok := drawer.face.Glyph ( + fixed.P ( + offset.X + word.position.X + character.x, + offset.Y + word.position.Y), + character.character) + if !ok { continue } // 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) + draw.DrawMask ( + destination, + destinationRectangle, + wrappedSource, image.Point { }, + mask, maskPoint, + draw.Over) - // updatedRegion = updatedRegion.Union(destinationRectangle) - // }} + updatedRegion = updatedRegion.Union(destinationRectangle) + }} return } diff --git a/artist/artist.go b/artist/texture.go similarity index 72% rename from artist/artist.go rename to artist/texture.go index d2f4bd4..f2d0ae2 100644 --- a/artist/artist.go +++ b/artist/texture.go @@ -3,15 +3,6 @@ package artist import "image" import "image/color" -// Pattern is capable of generating a pattern pixel by pixel. -type Pattern interface { - // AtWhen returns the color of the pixel located at (x, y) relative to - // the origin point of the pattern (0, 0), when the pattern has the - // specified width and height. Patterns may ignore the width and height - // parameters, but it may be useful for some patterns such as gradients. - AtWhen (x, y, width, height int) (color.RGBA) -} - // Texture is a struct that allows an image to be converted into a tiling // texture pattern. type Texture struct { diff --git a/artist/uniform.go b/artist/uniform.go index e50923e..4fa3013 100644 --- a/artist/uniform.go +++ b/artist/uniform.go @@ -4,61 +4,52 @@ import "image" import "image/color" // Uniform is an infinite-sized pattern of uniform color. It implements the -// color.Color, color.Model, and image.Image interfaces. -type Uniform struct { - C color.RGBA -} +// Pattern, color.Color, color.Model, and image.Image interfaces. +type Uniform color.RGBA // NewUniform returns a new Uniform image of the given color. -func NewUniform (c color.Color) (uniform *Uniform) { - uniform = &Uniform { } +func NewUniform (c color.Color) (uniform Uniform) { r, g, b, a := c.RGBA() - uniform.C.R = uint8(r >> 8) - uniform.C.G = uint8(g >> 8) - uniform.C.B = uint8(b >> 8) - uniform.C.A = uint8(a >> 8) + uniform.R = uint8(r >> 8) + uniform.G = uint8(g >> 8) + uniform.B = uint8(b >> 8) + uniform.A = uint8(a >> 8) return } -func (uniform *Uniform) RGBA () (r, g, b, a uint32) { - r = uint32(uniform.C.R) << 8 | uint32(uniform.C.R) - g = uint32(uniform.C.G) << 8 | uint32(uniform.C.G) - b = uint32(uniform.C.B) << 8 | uint32(uniform.C.B) - a = uint32(uniform.C.A) << 8 | uint32(uniform.C.A) - return -} - -func (uniform *Uniform) ColorModel () (model color.Model) { +// ColorModel satisfies the image.Image interface. +func (uniform Uniform) ColorModel () (model color.Model) { return uniform } -func (uniform *Uniform) Convert (in color.Color) (c color.Color) { - return uniform.C +// Convert satisfies the color.Model interface. +func (uniform Uniform) Convert (in color.Color) (c color.Color) { + return color.RGBA(uniform) } -func (uniform *Uniform) Bounds () (rectangle image.Rectangle) { +// Bounds satisfies the image.Image interface. +func (uniform Uniform) Bounds () (rectangle image.Rectangle) { rectangle.Min = image.Point { -1e9, -1e9 } rectangle.Max = image.Point { 1e9, 1e9 } return } -func (uniform *Uniform) At (x, y int) (c color.Color) { - return uniform.C +// At satisfies the image.Image interface. +func (uniform Uniform) At (x, y int) (c color.Color) { + return color.RGBA(uniform) } -func (uniform *Uniform) AtWhen (x, y, width, height int) (c color.RGBA) { - return uniform.C +// AtWhen satisfies the Pattern interface. +func (uniform Uniform) AtWhen (x, y, width, height int) (c color.RGBA) { + return color.RGBA(uniform) } -func (uniform *Uniform) RGBA64At (x, y int) (c color.RGBA64) { - r := uint16(uniform.C.R) << 8 | uint16(uniform.C.R) - g := uint16(uniform.C.G) << 8 | uint16(uniform.C.G) - b := uint16(uniform.C.B) << 8 | uint16(uniform.C.B) - a := uint16(uniform.C.A) << 8 | uint16(uniform.C.A) - return color.RGBA64 { R: r, G: g, B: b, A: a } +// RGBA satisfies the color.Color interface. +func (uniform Uniform) RGBA () (r, g, b, a uint32) { + return color.RGBA(uniform).RGBA() } // Opaque scans the entire image and reports whether it is fully opaque. -func (uniform *Uniform) Opaque () (opaque bool) { - return uniform.C.A == 0xFF +func (uniform Uniform) Opaque () (opaque bool) { + return uniform.A == 0xFF } diff --git a/artist/wrap.go b/artist/wrap.go index 530e53f..5cf0520 100644 --- a/artist/wrap.go +++ b/artist/wrap.go @@ -1 +1,27 @@ package artist + +import "image" +import "image/color" + +// WrappedPattern is a pattern that is able to behave like an image.Image. +type WrappedPattern struct { + Pattern + Width, Height int +} + +// At satisfies the image.Image interface. +func (pattern WrappedPattern) At (x, y int) (c color.Color) { + return pattern.Pattern.AtWhen(x, y, pattern.Width, pattern.Height) +} + +// Bounds satisfies the image.Image interface. +func (pattern WrappedPattern) Bounds () (rectangle image.Rectangle) { + rectangle.Min = image.Point { -1e9, -1e9 } + rectangle.Max = image.Point { 1e9, 1e9 } + return +} + +// ColorModel satisfies the image.Image interface. +func (pattern WrappedPattern) ColorModel () (model color.Model) { + return color.RGBAModel +}