2023-01-08 23:03:19 -07:00
|
|
|
package artist
|
|
|
|
|
|
|
|
import "image"
|
2023-02-01 23:48:16 -07:00
|
|
|
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
2023-01-08 23:03:19 -07:00
|
|
|
|
2023-01-13 23:54:57 -07:00
|
|
|
// Paste transfers one canvas onto another, offset by the specified point.
|
2023-01-08 23:03:19 -07:00
|
|
|
func Paste (
|
2023-02-01 23:48:16 -07:00
|
|
|
destination canvas.Canvas,
|
|
|
|
source canvas.Canvas,
|
2023-01-08 23:03:19 -07:00
|
|
|
offset image.Point,
|
|
|
|
) (
|
|
|
|
updatedRegion image.Rectangle,
|
|
|
|
) {
|
2023-01-13 23:54:57 -07:00
|
|
|
dstData, dstStride := destination.Buffer()
|
|
|
|
srcData, srcStride := source.Buffer()
|
|
|
|
|
|
|
|
sourceBounds :=
|
|
|
|
source.Bounds().Canon().
|
|
|
|
Intersect(destination.Bounds().Sub(offset))
|
|
|
|
if sourceBounds.Empty() { return }
|
|
|
|
|
2023-01-08 23:03:19 -07:00
|
|
|
updatedRegion = sourceBounds.Add(offset)
|
|
|
|
for y := sourceBounds.Min.Y; y < sourceBounds.Max.Y; y ++ {
|
|
|
|
for x := sourceBounds.Min.X; x < sourceBounds.Max.X; x ++ {
|
2023-01-13 23:54:57 -07:00
|
|
|
dstData[x + offset.X + (y + offset.Y) * dstStride] =
|
|
|
|
srcData[x + y * srcStride]
|
2023-01-08 23:03:19 -07:00
|
|
|
}}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-01-14 10:41:51 -07:00
|
|
|
// FillRectangle draws a filled rectangle with the specified pattern.
|
2023-01-13 23:54:57 -07:00
|
|
|
func FillRectangle (
|
2023-02-01 23:48:16 -07:00
|
|
|
destination canvas.Canvas,
|
2023-01-13 23:54:57 -07:00
|
|
|
source Pattern,
|
2023-01-08 23:03:19 -07:00
|
|
|
bounds image.Rectangle,
|
|
|
|
) (
|
|
|
|
updatedRegion image.Rectangle,
|
2023-02-14 13:47:41 -07:00
|
|
|
) {
|
|
|
|
return FillRectangleClip(destination, source, bounds, bounds)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FillRectangleClip is similar to FillRectangle, but it clips the pattern to
|
|
|
|
// a specified rectangle mask. That is—the pattern will be queried as if it
|
|
|
|
// were drawn without the mask, but only the area specified by the intersection
|
|
|
|
// of bounds and mask will be drawn to.
|
|
|
|
func FillRectangleClip (
|
|
|
|
destination canvas.Canvas,
|
|
|
|
source Pattern,
|
|
|
|
bounds image.Rectangle,
|
|
|
|
mask image.Rectangle,
|
|
|
|
) (
|
|
|
|
updatedRegion image.Rectangle,
|
2023-01-08 23:03:19 -07:00
|
|
|
) {
|
2023-01-13 23:54:57 -07:00
|
|
|
data, stride := destination.Buffer()
|
2023-01-21 13:19:34 -07:00
|
|
|
realBounds := bounds
|
2023-02-14 13:47:41 -07:00
|
|
|
bounds =
|
|
|
|
bounds.Canon().
|
|
|
|
Intersect(destination.Bounds()).
|
|
|
|
Intersect(mask)
|
2023-01-13 23:54:57 -07:00
|
|
|
if bounds.Empty() { return }
|
2023-01-08 23:03:19 -07:00
|
|
|
updatedRegion = bounds
|
|
|
|
|
2023-01-21 13:19:34 -07:00
|
|
|
realWidth, realHeight := realBounds.Dx(), realBounds.Dy()
|
|
|
|
patternOffset := realBounds.Min.Sub(bounds.Min)
|
|
|
|
|
2023-01-13 23:54:57 -07:00
|
|
|
width, height := bounds.Dx(), bounds.Dy()
|
|
|
|
for y := 0; y < height; y ++ {
|
|
|
|
for x := 0; x < width; x ++ {
|
|
|
|
data[x + bounds.Min.X + (y + bounds.Min.Y) * stride] =
|
2023-01-21 13:19:34 -07:00
|
|
|
source.AtWhen (
|
|
|
|
x - patternOffset.X, y - patternOffset.Y,
|
|
|
|
realWidth, realHeight)
|
2023-01-08 23:03:19 -07:00
|
|
|
}}
|
|
|
|
return
|
|
|
|
}
|
2023-01-14 11:40:05 -07:00
|
|
|
|
|
|
|
// StrokeRectangle draws the outline of a rectangle with the specified line
|
|
|
|
// weight and pattern.
|
|
|
|
func StrokeRectangle (
|
2023-02-01 23:48:16 -07:00
|
|
|
destination canvas.Canvas,
|
2023-01-14 11:40:05 -07:00
|
|
|
source Pattern,
|
|
|
|
weight int,
|
|
|
|
bounds image.Rectangle,
|
|
|
|
) {
|
|
|
|
bounds = bounds.Canon()
|
|
|
|
insetBounds := bounds.Inset(weight)
|
|
|
|
if insetBounds.Empty() {
|
|
|
|
FillRectangle(destination, source, bounds)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// top
|
|
|
|
FillRectangle (destination, source, image.Rect (
|
|
|
|
bounds.Min.X, bounds.Min.Y,
|
2023-01-14 11:59:07 -07:00
|
|
|
bounds.Max.X, insetBounds.Min.Y))
|
2023-01-14 11:40:05 -07:00
|
|
|
|
|
|
|
// bottom
|
|
|
|
FillRectangle (destination, source, image.Rect (
|
|
|
|
bounds.Min.X, insetBounds.Max.Y,
|
2023-01-14 11:59:07 -07:00
|
|
|
bounds.Max.X, bounds.Max.Y))
|
2023-01-14 11:40:05 -07:00
|
|
|
|
|
|
|
// left
|
|
|
|
FillRectangle (destination, source, image.Rect (
|
|
|
|
bounds.Min.X, insetBounds.Min.Y,
|
|
|
|
insetBounds.Min.X, insetBounds.Max.Y))
|
|
|
|
|
|
|
|
// right
|
|
|
|
FillRectangle (destination, source, image.Rect (
|
|
|
|
insetBounds.Max.X, insetBounds.Min.Y,
|
|
|
|
bounds.Max.X, insetBounds.Max.Y))
|
|
|
|
}
|