Merge pull request #12 from wsw0108/pattern-gradient
Implement pattern and gradient
This commit is contained in:
commit
bb1054e936
38
context.go
38
context.go
@ -50,6 +50,8 @@ type Context struct {
|
|||||||
im *image.RGBA
|
im *image.RGBA
|
||||||
mask *image.Alpha
|
mask *image.Alpha
|
||||||
color color.Color
|
color color.Color
|
||||||
|
fillPattern Pattern
|
||||||
|
strokePattern Pattern
|
||||||
strokePath raster.Path
|
strokePath raster.Path
|
||||||
fillPath raster.Path
|
fillPath raster.Path
|
||||||
start Point
|
start Point
|
||||||
@ -172,9 +174,25 @@ func (dc *Context) SetFillRuleEvenOdd() {
|
|||||||
|
|
||||||
// Color Setters
|
// Color Setters
|
||||||
|
|
||||||
|
func (dc *Context) setFillAndStrokeColor(c color.Color) {
|
||||||
|
dc.color = c
|
||||||
|
dc.fillPattern = NewSolidPattern(c)
|
||||||
|
dc.strokePattern = NewSolidPattern(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFillStyle sets current fill style
|
||||||
|
func (dc *Context) SetFillStyle(pattern Pattern) {
|
||||||
|
dc.fillPattern = pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStrokeStyle sets current stroke style
|
||||||
|
func (dc *Context) SetStrokeStyle(pattern Pattern) {
|
||||||
|
dc.strokePattern = pattern
|
||||||
|
}
|
||||||
|
|
||||||
// SetColor sets the current color.
|
// SetColor sets the current color.
|
||||||
func (dc *Context) SetColor(c color.Color) {
|
func (dc *Context) SetColor(c color.Color) {
|
||||||
dc.color = c
|
dc.setFillAndStrokeColor(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetHexColor sets the current color using a hex string. The leading pound
|
// SetHexColor sets the current color using a hex string. The leading pound
|
||||||
@ -189,6 +207,7 @@ func (dc *Context) SetHexColor(x string) {
|
|||||||
// 255, inclusive.
|
// 255, inclusive.
|
||||||
func (dc *Context) SetRGBA255(r, g, b, a int) {
|
func (dc *Context) SetRGBA255(r, g, b, a int) {
|
||||||
dc.color = color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
|
dc.color = color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
|
||||||
|
dc.setFillAndStrokeColor(dc.color)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRGB255 sets the current color. r, g, b values should be between 0 and 255,
|
// SetRGB255 sets the current color. r, g, b values should be between 0 and 255,
|
||||||
@ -206,6 +225,7 @@ func (dc *Context) SetRGBA(r, g, b, a float64) {
|
|||||||
uint8(b * 255),
|
uint8(b * 255),
|
||||||
uint8(a * 255),
|
uint8(a * 255),
|
||||||
}
|
}
|
||||||
|
dc.setFillAndStrokeColor(dc.color)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRGB sets the current color. r, g, b values should be between 0 and 1,
|
// SetRGB sets the current color. r, g, b values should be between 0 and 1,
|
||||||
@ -372,13 +392,13 @@ func (dc *Context) fill(painter raster.Painter) {
|
|||||||
// operation.
|
// operation.
|
||||||
func (dc *Context) StrokePreserve() {
|
func (dc *Context) StrokePreserve() {
|
||||||
if dc.mask == nil {
|
if dc.mask == nil {
|
||||||
painter := raster.NewRGBAPainter(dc.im)
|
painter := newPatternPainter(dc.im)
|
||||||
painter.SetColor(dc.color)
|
painter.setPattern(dc.strokePattern)
|
||||||
dc.stroke(painter)
|
dc.stroke(painter)
|
||||||
} else {
|
} else {
|
||||||
im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
|
im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
|
||||||
painter := raster.NewRGBAPainter(im)
|
painter := newPatternPainter(im)
|
||||||
painter.SetColor(dc.color)
|
painter.setPattern(dc.strokePattern)
|
||||||
dc.stroke(painter)
|
dc.stroke(painter)
|
||||||
draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
|
draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
|
||||||
}
|
}
|
||||||
@ -396,13 +416,13 @@ func (dc *Context) Stroke() {
|
|||||||
// are implicity closed. The path is preserved after this operation.
|
// are implicity closed. The path is preserved after this operation.
|
||||||
func (dc *Context) FillPreserve() {
|
func (dc *Context) FillPreserve() {
|
||||||
if dc.mask == nil {
|
if dc.mask == nil {
|
||||||
painter := raster.NewRGBAPainter(dc.im)
|
painter := newPatternPainter(dc.im)
|
||||||
painter.SetColor(dc.color)
|
painter.setPattern(dc.fillPattern)
|
||||||
dc.fill(painter)
|
dc.fill(painter)
|
||||||
} else {
|
} else {
|
||||||
im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
|
im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height))
|
||||||
painter := raster.NewRGBAPainter(im)
|
painter := newPatternPainter(im)
|
||||||
painter.SetColor(dc.color)
|
painter.setPattern(dc.fillPattern)
|
||||||
dc.fill(painter)
|
dc.fill(painter)
|
||||||
draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
|
draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over)
|
||||||
}
|
}
|
||||||
|
39
examples/gradient-linear.go
Normal file
39
examples/gradient-linear.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/fogleman/gg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
dc := gg.NewContext(500, 400)
|
||||||
|
|
||||||
|
grad := gg.NewLinearGradient(20, 320, 400, 20)
|
||||||
|
grad.AddColorStop(0, color.RGBA{0, 255, 0, 255})
|
||||||
|
grad.AddColorStop(1, color.RGBA{0, 0, 255, 255})
|
||||||
|
grad.AddColorStop(0.5, color.RGBA{255, 0, 0, 255})
|
||||||
|
|
||||||
|
dc.SetColor(color.White)
|
||||||
|
dc.DrawRectangle(20, 20, 400-20, 300)
|
||||||
|
dc.Stroke()
|
||||||
|
|
||||||
|
dc.SetStrokeStyle(grad)
|
||||||
|
dc.SetLineWidth(4)
|
||||||
|
dc.MoveTo(10, 10)
|
||||||
|
dc.LineTo(410, 10)
|
||||||
|
dc.LineTo(410, 100)
|
||||||
|
dc.LineTo(10, 100)
|
||||||
|
dc.ClosePath()
|
||||||
|
dc.Stroke()
|
||||||
|
|
||||||
|
dc.SetFillStyle(grad)
|
||||||
|
dc.MoveTo(10, 120)
|
||||||
|
dc.LineTo(410, 120)
|
||||||
|
dc.LineTo(410, 300)
|
||||||
|
dc.LineTo(10, 300)
|
||||||
|
dc.ClosePath()
|
||||||
|
dc.Fill()
|
||||||
|
|
||||||
|
dc.SavePNG("out.png")
|
||||||
|
}
|
27
examples/gradient-radial.go
Normal file
27
examples/gradient-radial.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/fogleman/gg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
dc := gg.NewContext(400, 200)
|
||||||
|
|
||||||
|
grad := gg.NewRadialGradient(100, 100, 10, 100, 120, 80)
|
||||||
|
grad.AddColorStop(0, color.RGBA{0, 255, 0, 255})
|
||||||
|
grad.AddColorStop(1, color.RGBA{0, 0, 255, 255})
|
||||||
|
|
||||||
|
dc.SetFillStyle(grad)
|
||||||
|
dc.DrawRectangle(0, 0, 200, 200)
|
||||||
|
dc.Fill()
|
||||||
|
|
||||||
|
dc.SetColor(color.White)
|
||||||
|
dc.DrawCircle(100, 100, 10)
|
||||||
|
dc.Stroke()
|
||||||
|
dc.DrawCircle(100, 120, 80)
|
||||||
|
dc.Stroke()
|
||||||
|
|
||||||
|
dc.SavePNG("out.png")
|
||||||
|
}
|
20
examples/pattern-fill.go
Normal file
20
examples/pattern-fill.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/fogleman/gg"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
im, err := gg.LoadPNG("examples/lenna.png")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pattern := gg.NewSurfacePattern(im, gg.RepeatBoth)
|
||||||
|
dc := gg.NewContext(600, 600)
|
||||||
|
dc.MoveTo(20, 20)
|
||||||
|
dc.LineTo(590, 20)
|
||||||
|
dc.LineTo(590, 590)
|
||||||
|
dc.LineTo(20, 590)
|
||||||
|
dc.ClosePath()
|
||||||
|
dc.SetFillStyle(pattern)
|
||||||
|
dc.Fill()
|
||||||
|
dc.SavePNG("out.png")
|
||||||
|
}
|
202
gradient.go
Normal file
202
gradient.go
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
package gg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stop struct {
|
||||||
|
pos float64
|
||||||
|
color color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
type stops []stop
|
||||||
|
|
||||||
|
// Len satisfies the Sort interface.
|
||||||
|
func (s stops) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less satisfies the Sort interface.
|
||||||
|
func (s stops) Less(i, j int) bool {
|
||||||
|
return s[i].pos < s[j].pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap satisfies the Sort interface.
|
||||||
|
func (s stops) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
type Gradient interface {
|
||||||
|
Pattern
|
||||||
|
AddColorStop(offset float64, color color.Color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linear Gradient
|
||||||
|
type linearGradient struct {
|
||||||
|
x0, y0, x1, y1 float64
|
||||||
|
stops stops
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *linearGradient) ColorAt(x, y int) color.Color {
|
||||||
|
if len(g.stops) == 0 {
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
fx, fy := float64(x), float64(y)
|
||||||
|
x0, y0, x1, y1 := g.x0, g.y0, g.x1, g.y1
|
||||||
|
dx, dy := x1-x0, y1-y0
|
||||||
|
|
||||||
|
// Horizontal
|
||||||
|
if dy == 0 && dx != 0 {
|
||||||
|
return getColor((fx-x0)/dx, g.stops)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical
|
||||||
|
if dx == 0 && dy != 0 {
|
||||||
|
return getColor((fy-y0)/dy, g.stops)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dot product
|
||||||
|
s0 := dx*(fx-x0) + dy*(fy-y0)
|
||||||
|
if s0 < 0 {
|
||||||
|
return g.stops[0].color
|
||||||
|
}
|
||||||
|
// Calculate distance to (x0,y0) alone (x0,y0)->(x1,y1)
|
||||||
|
mag := math.Hypot(dx, dy)
|
||||||
|
u := ((fx-x0)*-dy + (fy-y0)*dx) / (mag * mag)
|
||||||
|
x2, y2 := x0+u*-dy, y0+u*dx
|
||||||
|
d := math.Hypot(fx-x2, fy-y2) / mag
|
||||||
|
return getColor(d, g.stops)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *linearGradient) AddColorStop(offset float64, color color.Color) {
|
||||||
|
g.stops = append(g.stops, stop{pos: offset, color: color})
|
||||||
|
sort.Sort(g.stops)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinearGradient(x0, y0, x1, y1 float64) Gradient {
|
||||||
|
g := &linearGradient{
|
||||||
|
x0: x0, y0: y0,
|
||||||
|
x1: x1, y1: y1,
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// Radial Gradient
|
||||||
|
type circle struct {
|
||||||
|
x, y, r float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type radialGradient struct {
|
||||||
|
c0, c1, cd circle
|
||||||
|
a, inva float64
|
||||||
|
mindr float64
|
||||||
|
stops stops
|
||||||
|
}
|
||||||
|
|
||||||
|
func dot3(x0, y0, z0, x1, y1, z1 float64) float64 {
|
||||||
|
return x0*x1 + y0*y1 + z0*z1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *radialGradient) ColorAt(x, y int) color.Color {
|
||||||
|
if len(g.stops) == 0 {
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy from pixman's pixman-radial-gradient.c
|
||||||
|
|
||||||
|
dx, dy := float64(x)+0.5-g.c0.x, float64(y)+0.5-g.c0.y
|
||||||
|
b := dot3(dx, dy, g.c0.r, g.cd.x, g.cd.y, g.cd.r)
|
||||||
|
c := dot3(dx, dy, -g.c0.r, dx, dy, g.c0.r)
|
||||||
|
|
||||||
|
if g.a == 0 {
|
||||||
|
if b == 0 {
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
t := 0.5 * c / b
|
||||||
|
if t*g.cd.r >= g.mindr {
|
||||||
|
return getColor(t, g.stops)
|
||||||
|
}
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
discr := dot3(b, g.a, 0, b, -c, 0)
|
||||||
|
if discr >= 0 {
|
||||||
|
sqrtdiscr := math.Sqrt(discr)
|
||||||
|
t0 := (b + sqrtdiscr) * g.inva
|
||||||
|
t1 := (b - sqrtdiscr) * g.inva
|
||||||
|
|
||||||
|
if t0*g.cd.r >= g.mindr {
|
||||||
|
return getColor(t0, g.stops)
|
||||||
|
} else if t1*g.cd.r >= g.mindr {
|
||||||
|
return getColor(t1, g.stops)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *radialGradient) AddColorStop(offset float64, color color.Color) {
|
||||||
|
g.stops = append(g.stops, stop{pos: offset, color: color})
|
||||||
|
sort.Sort(g.stops)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) Gradient {
|
||||||
|
c0 := circle{x0, y0, r0}
|
||||||
|
c1 := circle{x1, y1, r1}
|
||||||
|
cd := circle{x1 - x0, y1 - y0, r1 - r0}
|
||||||
|
a := dot3(cd.x, cd.y, -cd.r, cd.x, cd.y, cd.r)
|
||||||
|
var inva float64
|
||||||
|
if a != 0 {
|
||||||
|
inva = 1.0 / a
|
||||||
|
}
|
||||||
|
mindr := -c0.r
|
||||||
|
g := &radialGradient{
|
||||||
|
c0: c0,
|
||||||
|
c1: c1,
|
||||||
|
cd: cd,
|
||||||
|
a: a,
|
||||||
|
inva: inva,
|
||||||
|
mindr: mindr,
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func getColor(pos float64, stops stops) color.Color {
|
||||||
|
if pos <= 0.0 || len(stops) == 1 {
|
||||||
|
return stops[0].color
|
||||||
|
}
|
||||||
|
|
||||||
|
last := stops[len(stops)-1]
|
||||||
|
|
||||||
|
if pos >= last.pos {
|
||||||
|
return last.color
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, stop := range stops[1:] {
|
||||||
|
if pos < stop.pos {
|
||||||
|
pos = (pos - stops[i].pos) / (stop.pos - stops[i].pos)
|
||||||
|
return colorLerp(stops[i].color, stop.color, pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return last.color
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorLerp(c0, c1 color.Color, t float64) color.Color {
|
||||||
|
r0, g0, b0, a0 := c0.RGBA()
|
||||||
|
r1, g1, b1, a1 := c1.RGBA()
|
||||||
|
|
||||||
|
return color.NRGBA{
|
||||||
|
lerp(r0, r1, t),
|
||||||
|
lerp(g0, g1, t),
|
||||||
|
lerp(b0, b1, t),
|
||||||
|
lerp(a0, a1, t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lerp(a, b uint32, t float64) uint8 {
|
||||||
|
return uint8(int32(float64(a)*(1.0-t)+float64(b)*t) >> 8)
|
||||||
|
}
|
121
pattern.go
Normal file
121
pattern.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package gg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/golang/freetype/raster"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepeatOp int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RepeatBoth RepeatOp = iota
|
||||||
|
RepeatX
|
||||||
|
RepeatY
|
||||||
|
RepeatNone
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pattern interface {
|
||||||
|
ColorAt(x, y int) color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solid Pattern
|
||||||
|
type solidPattern struct {
|
||||||
|
color color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *solidPattern) ColorAt(x, y int) color.Color {
|
||||||
|
return p.color
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSolidPattern(color color.Color) Pattern {
|
||||||
|
return &solidPattern{color: color}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surface Pattern
|
||||||
|
type surfacePattern struct {
|
||||||
|
im image.Image
|
||||||
|
op RepeatOp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *surfacePattern) ColorAt(x, y int) color.Color {
|
||||||
|
b := p.im.Bounds()
|
||||||
|
switch p.op {
|
||||||
|
case RepeatX:
|
||||||
|
if y >= b.Dy() {
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
case RepeatY:
|
||||||
|
if x >= b.Dx() {
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
case RepeatNone:
|
||||||
|
if x >= b.Dx() || y >= b.Dy() {
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x = x%b.Dx() + b.Min.X
|
||||||
|
y = y%b.Dy() + b.Min.Y
|
||||||
|
return p.im.At(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSurfacePattern(im image.Image, op RepeatOp) Pattern {
|
||||||
|
return &surfacePattern{im: im, op: op}
|
||||||
|
}
|
||||||
|
|
||||||
|
type patternPainter struct {
|
||||||
|
im *image.RGBA
|
||||||
|
p Pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paint satisfies the Painter interface.
|
||||||
|
func (r *patternPainter) Paint(ss []raster.Span, done bool) {
|
||||||
|
b := r.im.Bounds()
|
||||||
|
for _, s := range ss {
|
||||||
|
if s.Y < b.Min.Y {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.Y >= b.Max.Y {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.X0 < b.Min.X {
|
||||||
|
s.X0 = b.Min.X
|
||||||
|
}
|
||||||
|
if s.X1 > b.Max.X {
|
||||||
|
s.X1 = b.Max.X
|
||||||
|
}
|
||||||
|
if s.X0 >= s.X1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ma := s.Alpha
|
||||||
|
const m = 1<<16 - 1
|
||||||
|
y := s.Y - r.im.Rect.Min.Y
|
||||||
|
x0 := s.X0 - r.im.Rect.Min.X
|
||||||
|
// x1 := x0 + s.X1 - s.X0
|
||||||
|
// RGBAPainter.Paint() in $GOPATH/src/github.com/golang/freetype/raster/paint.go
|
||||||
|
i0 := (s.Y-r.im.Rect.Min.Y)*r.im.Stride + (s.X0-r.im.Rect.Min.X)*4
|
||||||
|
i1 := i0 + (s.X1-s.X0)*4
|
||||||
|
for i, x := i0, x0; i < i1; i, x = i+4, x+1 {
|
||||||
|
c := r.p.ColorAt(x, y)
|
||||||
|
cr, cg, cb, ca := c.RGBA()
|
||||||
|
dr := uint32(r.im.Pix[i+0])
|
||||||
|
dg := uint32(r.im.Pix[i+1])
|
||||||
|
db := uint32(r.im.Pix[i+2])
|
||||||
|
da := uint32(r.im.Pix[i+3])
|
||||||
|
a := (m - (ca * ma / m)) * 0x101
|
||||||
|
r.im.Pix[i+0] = uint8((dr*a + cr*ma) / m >> 8)
|
||||||
|
r.im.Pix[i+1] = uint8((dg*a + cg*ma) / m >> 8)
|
||||||
|
r.im.Pix[i+2] = uint8((db*a + cb*ma) / m >> 8)
|
||||||
|
r.im.Pix[i+3] = uint8((da*a + ca*ma) / m >> 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *patternPainter) setPattern(pattern Pattern) {
|
||||||
|
r.p = pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPatternPainter(im *image.RGBA) *patternPainter {
|
||||||
|
return &patternPainter{im: im}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user