Sasha Koshka
34bf3038ac
This is the first step in transitioning the API over to the new design. The new tomo.Canvas interface gives drawing functions direct access to data buffers and eliminates overhead associated with calling functions for every pixel. The entire artist package will be remade around this.
117 lines
3.0 KiB
Go
117 lines
3.0 KiB
Go
package artist
|
|
|
|
import "image"
|
|
import "image/color"
|
|
import "git.tebibyte.media/sashakoshka/tomo"
|
|
|
|
// ShadingProfile contains shading information that can be used to draw chiseled
|
|
// objects.
|
|
type ShadingProfile struct {
|
|
Highlight Pattern
|
|
Shadow Pattern
|
|
Stroke Pattern
|
|
Fill Pattern
|
|
StrokeWeight int
|
|
ShadingWeight int
|
|
}
|
|
|
|
// Engraved reverses the shadown and highlight colors of the ShadingProfile to
|
|
// produce a new ShadingProfile with an engraved appearance.
|
|
func (profile ShadingProfile) Engraved () (reversed ShadingProfile) {
|
|
reversed = profile
|
|
reversed.Highlight = profile.Shadow
|
|
reversed.Shadow = profile.Highlight
|
|
return
|
|
}
|
|
|
|
// ChiseledRectangle draws a rectangle with a chiseled/embossed appearance,
|
|
// according to the ShadingProfile passed to it.
|
|
func ChiseledRectangle (
|
|
destination tomo.Canvas,
|
|
profile ShadingProfile,
|
|
bounds image.Rectangle,
|
|
) (
|
|
updatedRegion image.Rectangle,
|
|
) {
|
|
// FIXME: this breaks when the bounds are smaller than the border or
|
|
// shading weight
|
|
|
|
stroke := profile.Stroke
|
|
highlight := profile.Highlight
|
|
shadow := profile.Shadow
|
|
fill := profile.Fill
|
|
strokeWeight := profile.StrokeWeight
|
|
shadingWeight := profile.ShadingWeight
|
|
|
|
data, stride := destination.Buffer()
|
|
bounds = bounds.Canon()
|
|
updatedRegion = bounds
|
|
|
|
strokeWeightVector := image.Point { strokeWeight, strokeWeight }
|
|
shadingWeightVector := image.Point { shadingWeight, shadingWeight }
|
|
|
|
shadingBounds := bounds
|
|
shadingBounds.Min = shadingBounds.Min.Add(strokeWeightVector)
|
|
shadingBounds.Max = shadingBounds.Max.Sub(strokeWeightVector)
|
|
shadingBounds = shadingBounds.Canon()
|
|
|
|
fillBounds := shadingBounds
|
|
fillBounds.Min = fillBounds.Min.Add(shadingWeightVector)
|
|
fillBounds.Max = fillBounds.Max.Sub(shadingWeightVector)
|
|
fillBounds = fillBounds.Canon()
|
|
|
|
width := float64(bounds.Dx())
|
|
height := float64(bounds.Dy())
|
|
|
|
yy := 0
|
|
for y := bounds.Min.Y; y < bounds.Max.Y; y ++ {
|
|
xx := 0
|
|
for x := bounds.Min.X; x < bounds.Max.X; x ++ {
|
|
var pixel color.RGBA
|
|
point := image.Point { x, y }
|
|
switch {
|
|
case point.In(fillBounds):
|
|
pixel = fill.AtWhen (
|
|
xx - strokeWeight - shadingWeight,
|
|
yy - strokeWeight - shadingWeight,
|
|
fillBounds.Dx(), fillBounds.Dy())
|
|
|
|
case point.In(shadingBounds):
|
|
var highlighted bool
|
|
// FIXME: this doesn't work quite right, the
|
|
// slope of the line is somewhat off.
|
|
bottomCorner :=
|
|
float64(xx) < float64(yy) *
|
|
(width / height)
|
|
if bottomCorner {
|
|
highlighted =
|
|
float64(xx) <
|
|
height - float64(yy)
|
|
} else {
|
|
highlighted =
|
|
width - float64(xx) >
|
|
float64(yy)
|
|
}
|
|
|
|
shadingSource := shadow
|
|
if highlighted {
|
|
shadingSource = highlight
|
|
}
|
|
pixel = shadingSource.AtWhen (
|
|
xx - strokeWeight,
|
|
yy - strokeWeight,
|
|
shadingBounds.Dx(),
|
|
shadingBounds.Dy())
|
|
default:
|
|
pixel = stroke.AtWhen (
|
|
xx, yy, bounds.Dx(), bounds.Dy())
|
|
}
|
|
data[x + y * stride] = pixel
|
|
xx ++
|
|
}
|
|
yy ++
|
|
}
|
|
|
|
return
|
|
}
|