Pattern, solid(color) and surface(image)

This commit is contained in:
wsw 2016-11-20 20:31:05 +08:00
parent b28a9453d4
commit db5b86472e
3 changed files with 189 additions and 28 deletions

View File

@ -45,25 +45,27 @@ const (
) )
type Context struct { type Context struct {
width int width int
height int height int
im *image.RGBA im *image.RGBA
mask *image.Alpha mask *image.Alpha
color color.Color color color.Color
strokePath raster.Path fillPattern Pattern
fillPath raster.Path strokePattern Pattern
start Point strokePath raster.Path
current Point fillPath raster.Path
hasCurrent bool start Point
dashes []float64 current Point
lineWidth float64 hasCurrent bool
lineCap LineCap dashes []float64
lineJoin LineJoin lineWidth float64
fillRule FillRule lineCap LineCap
fontFace font.Face lineJoin LineJoin
fontHeight float64 fillRule FillRule
matrix Matrix fontFace font.Face
stack []*Context fontHeight float64
matrix Matrix
stack []*Context
} }
// NewContext creates a new image.RGBA with the specified width and height // NewContext creates a new image.RGBA with the specified width and height
@ -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)
} }

20
examples/fill-pattern.go Normal file
View 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")
}

121
pattern.go Normal file
View 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}
}