Initial commit
This commit is contained in:
89
patterns/border.go
Normal file
89
patterns/border.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package patterns
|
||||
|
||||
import "art"
|
||||
import "image"
|
||||
|
||||
// Border is a pattern that behaves similarly to border-image in CSS. It divides
|
||||
// a source canvas into nine sections...
|
||||
//
|
||||
// Inset[1]
|
||||
// ┌──┴──┐
|
||||
// ┌─┌─────┬─────┬─────┐
|
||||
// Inset[0]─┤ │ 0 │ 1 │ 2 │
|
||||
// └─├─────┼─────┼─────┤
|
||||
// │ 3 │ 4 │ 5 │
|
||||
// ├─────┼─────┼─────┤─┐
|
||||
// │ 6 │ 7 │ 8 │ ├─Inset[2]
|
||||
// └─────┴─────┴─────┘─┘
|
||||
// └──┬──┘
|
||||
// Inset[3]
|
||||
//
|
||||
// ... Where the bounds of section 4 are defined as the application of the
|
||||
// pattern's inset to the canvas's bounds. The bounds of the other eight
|
||||
// sections are automatically sized around it.
|
||||
//
|
||||
// When drawn to a destination canvas, the bounds of sections 1, 3, 4, 5, and 7
|
||||
// are expanded or contracted to fit the given drawing bounds. All sections are
|
||||
// rendered as if they are Texture patterns, meaning these flexible sections
|
||||
// will repeat to fill in any empty space.
|
||||
//
|
||||
// This pattern can be used to make a static image texture into something that
|
||||
// responds well to being resized.
|
||||
type Border struct {
|
||||
art.Canvas
|
||||
art.Inset
|
||||
}
|
||||
|
||||
// Draw draws the border pattern onto the destination canvas within the given
|
||||
// bounds.
|
||||
func (pattern Border) Draw (destination art.Canvas, bounds image.Rectangle) {
|
||||
drawBounds := bounds.Canon().Intersect(destination.Bounds())
|
||||
if drawBounds.Empty() { return }
|
||||
|
||||
srcSections := nonasect(pattern.Bounds(), pattern.Inset)
|
||||
srcTextures := [9]Texture { }
|
||||
for index, section := range srcSections {
|
||||
srcTextures[index].Canvas = art.Cut(pattern, section)
|
||||
}
|
||||
|
||||
dstSections := nonasect(bounds, pattern.Inset)
|
||||
for index, section := range dstSections {
|
||||
srcTextures[index].Draw(destination, section)
|
||||
}
|
||||
}
|
||||
|
||||
func nonasect (bounds image.Rectangle, inset art.Inset) [9]image.Rectangle {
|
||||
center := inset.Apply(bounds)
|
||||
return [9]image.Rectangle {
|
||||
// top
|
||||
image.Rectangle {
|
||||
bounds.Min,
|
||||
center.Min },
|
||||
image.Rect (
|
||||
center.Min.X, bounds.Min.Y,
|
||||
center.Max.X, center.Min.Y),
|
||||
image.Rect (
|
||||
center.Max.X, bounds.Min.Y,
|
||||
bounds.Max.X, center.Min.Y),
|
||||
|
||||
// center
|
||||
image.Rect (
|
||||
bounds.Min.X, center.Min.Y,
|
||||
center.Min.X, center.Max.Y),
|
||||
center,
|
||||
image.Rect (
|
||||
center.Max.X, center.Min.Y,
|
||||
bounds.Max.X, center.Max.Y),
|
||||
|
||||
// bottom
|
||||
image.Rect (
|
||||
bounds.Min.X, center.Max.Y,
|
||||
center.Min.X, bounds.Max.Y),
|
||||
image.Rect (
|
||||
center.Min.X, center.Max.Y,
|
||||
center.Max.X, bounds.Max.Y),
|
||||
image.Rect (
|
||||
center.Max.X, center.Max.Y,
|
||||
bounds.Max.X, bounds.Max.Y),
|
||||
}
|
||||
}
|
||||
3
patterns/doc.go
Normal file
3
patterns/doc.go
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package patterns provides a basic set of types that satisfy the Pattern
|
||||
// interface.
|
||||
package patterns
|
||||
77
patterns/texture.go
Normal file
77
patterns/texture.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package patterns
|
||||
|
||||
import "art"
|
||||
import "image"
|
||||
|
||||
// Texture is a pattern that tiles the content of a canvas both horizontally and
|
||||
// vertically.
|
||||
type Texture struct {
|
||||
art.Canvas
|
||||
}
|
||||
|
||||
// Draw tiles the pattern's canvas within the given bounds. The minimum
|
||||
// point of the pattern's canvas will be lined up with the minimum point of the
|
||||
// bounding rectangle.
|
||||
func (pattern Texture) Draw (destination art.Canvas, bounds image.Rectangle) {
|
||||
dstBounds := bounds.Canon().Intersect(destination.Bounds())
|
||||
if dstBounds.Empty() { return }
|
||||
|
||||
dstData, dstStride := destination.Buffer()
|
||||
srcData, srcStride := pattern.Buffer()
|
||||
srcBounds := pattern.Bounds()
|
||||
|
||||
// offset is a vector that is added to points in destination space to
|
||||
// convert them to points in source space
|
||||
offset := srcBounds.Min.Sub(bounds.Min)
|
||||
|
||||
// calculate the starting position in source space
|
||||
srcPoint := dstBounds.Min.Add(offset)
|
||||
srcPoint.X = wrap(srcPoint.X, srcBounds.Min.X, srcBounds.Max.X)
|
||||
srcPoint.Y = wrap(srcPoint.Y, srcBounds.Min.Y, srcBounds.Max.Y)
|
||||
srcStartPoint := srcPoint
|
||||
|
||||
// for each row
|
||||
dstPoint := image.Point { }
|
||||
for dstPoint.Y = dstBounds.Min.Y; dstPoint.Y < dstBounds.Max.Y; dstPoint.Y ++ {
|
||||
srcPoint.X = srcStartPoint.X
|
||||
dstPoint.X = dstBounds.Min.X
|
||||
dstYComponent := dstPoint.Y * dstStride
|
||||
srcYComponent := srcPoint.Y * srcStride
|
||||
|
||||
// for each pixel in the row
|
||||
for {
|
||||
// draw pixel
|
||||
dstIndex := dstYComponent + dstPoint.X
|
||||
srcIndex := srcYComponent + srcPoint.X
|
||||
dstData[dstIndex] = srcData[srcIndex]
|
||||
|
||||
// increment X in source space. wrap to start if out of
|
||||
// bounds.
|
||||
srcPoint.X ++
|
||||
if srcPoint.X >= srcBounds.Max.X {
|
||||
srcPoint.X = srcBounds.Min.X
|
||||
}
|
||||
|
||||
// increment X in destination space. stop drawing this
|
||||
// row if out of bounds.
|
||||
dstPoint.X ++
|
||||
if dstPoint.X >= dstBounds.Max.X {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// increment row in source space. wrap to start if out of
|
||||
// bounds.
|
||||
srcPoint.Y ++
|
||||
if srcPoint.Y >= srcBounds.Max.Y {
|
||||
srcPoint.Y = srcBounds.Min.Y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func wrap (value, min, max int) int {
|
||||
difference := max - min
|
||||
value = (value - min) % difference
|
||||
if value < 0 { value += difference }
|
||||
return value + min
|
||||
}
|
||||
20
patterns/uniform.go
Normal file
20
patterns/uniform.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package patterns
|
||||
|
||||
import "art"
|
||||
import "image"
|
||||
import "art/shapes"
|
||||
import "art/artutil"
|
||||
import "image/color"
|
||||
|
||||
// Uniform is a pattern that draws a solid color.
|
||||
type Uniform color.RGBA
|
||||
|
||||
// Draw fills the bounding rectangle with the pattern's color.
|
||||
func (pattern Uniform) Draw (destination art.Canvas, bounds image.Rectangle) {
|
||||
shapes.FillColorRectangle(destination, color.RGBA(pattern), bounds)
|
||||
}
|
||||
|
||||
// Uhex creates a new Uniform pattern from an RGBA integer value.
|
||||
func Uhex (color uint32) (uniform Uniform) {
|
||||
return Uniform(artutil.Hex(color))
|
||||
}
|
||||
Reference in New Issue
Block a user