diff --git a/backend/backendbase/base.go b/backend/backendbase/base.go index 224bc5f..e96e589 100644 --- a/backend/backendbase/base.go +++ b/backend/backendbase/base.go @@ -13,6 +13,7 @@ type Backend interface { Size() (int, int) LoadImage(img image.Image) (Image, error) + LoadImagePattern(data ImagePatternData) ImagePattern LoadLinearGradient(data Gradient) LinearGradient LoadRadialGradient(data Gradient) RadialGradient @@ -43,7 +44,7 @@ type FillStyle struct { RadFrom float64 RadTo float64 } - Image Image + ImagePattern ImagePattern } type Gradient []GradientStop @@ -104,3 +105,12 @@ type Image interface { Delete() Replace(src image.Image) error } + +type ImagePatternData struct { + Image Image +} + +type ImagePattern interface { + Delete() + Replace(data ImagePatternData) +} diff --git a/backend/gogl/gogl.go b/backend/gogl/gogl.go index 894b1b6..53ea60d 100644 --- a/backend/gogl/gogl.go +++ b/backend/gogl/gogl.go @@ -441,8 +441,8 @@ func (b *GoGLBackend) useShader(style *backendbase.FillStyle) (vertexLoc uint32) gl.Uniform1f(b.rgr.GlobalAlpha, float32(style.Color.A)/255) return b.rgr.Vertex } - if img := style.Image; img != nil { - img := img.(*Image) + if ip := style.ImagePattern; ip != nil { + img := ip.(*ImagePattern).data.Image.(*Image) gl.UseProgram(b.ipr.ID) gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, img.tex) @@ -498,8 +498,8 @@ func (b *GoGLBackend) useAlphaShader(style *backendbase.FillStyle, alphaTexSlot gl.Uniform1f(b.rgar.GlobalAlpha, float32(style.Color.A)/255) return b.rgar.Vertex, b.rgar.AlphaTexCoord } - if img := style.Image; img != nil { - img := img.(*Image) + if ip := style.ImagePattern; ip != nil { + img := ip.(*ImagePattern).data.Image.(*Image) gl.UseProgram(b.ipar.ID) gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, img.tex) diff --git a/backend/gogl/images.go b/backend/gogl/images.go index e83846f..117874c 100644 --- a/backend/gogl/images.go +++ b/backend/gogl/images.go @@ -239,3 +239,18 @@ func (b *GoGLBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64, gl.StencilFunc(gl.ALWAYS, 0, 0xFF) } + +type ImagePattern struct { + b *GoGLBackend + data backendbase.ImagePatternData +} + +func (b *GoGLBackend) LoadImagePattern(data backendbase.ImagePatternData) backendbase.ImagePattern { + return &ImagePattern{ + b: b, + data: data, + } +} + +func (ip *ImagePattern) Delete() {} +func (ip *ImagePattern) Replace(data backendbase.ImagePatternData) { ip.data = data } diff --git a/backend/xmobile/images.go b/backend/xmobile/images.go index 3d63040..eebc4f4 100755 --- a/backend/xmobile/images.go +++ b/backend/xmobile/images.go @@ -241,3 +241,18 @@ func (b *XMobileBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float6 b.glctx.StencilFunc(gl.ALWAYS, 0, 0xFF) } + +type ImagePattern struct { + b *XMobileBackend + data backendbase.ImagePatternData +} + +func (b *XMobileBackend) LoadImagePattern(data backendbase.ImagePatternData) backendbase.ImagePattern { + return &ImagePattern{ + b: b, + data: data, + } +} + +func (ip *ImagePattern) Delete() {} +func (ip *ImagePattern) Replace(data backendbase.ImagePatternData) { ip.data = data } diff --git a/backend/xmobile/xmobile.go b/backend/xmobile/xmobile.go index b51288b..ea339db 100755 --- a/backend/xmobile/xmobile.go +++ b/backend/xmobile/xmobile.go @@ -434,8 +434,8 @@ func (b *XMobileBackend) useShader(style *backendbase.FillStyle) (vertexLoc gl.A b.glctx.Uniform1f(b.rgr.GlobalAlpha, float32(style.Color.A)/255) return b.rgr.Vertex } - if img := style.Image; img != nil { - img := img.(*Image) + if ip := style.ImagePattern; ip != nil { + img := ip.(*ImagePattern).data.Image.(*Image) b.glctx.UseProgram(b.ipr.ID) b.glctx.ActiveTexture(gl.TEXTURE0) b.glctx.BindTexture(gl.TEXTURE_2D, img.tex) @@ -491,8 +491,8 @@ func (b *XMobileBackend) useAlphaShader(style *backendbase.FillStyle, alphaTexSl b.glctx.Uniform1f(b.rgar.GlobalAlpha, float32(style.Color.A)/255) return b.rgar.Vertex, b.rgar.AlphaTexCoord } - if img := style.Image; img != nil { - img := img.(*Image) + if ip := style.ImagePattern; ip != nil { + img := ip.(*ImagePattern).data.Image.(*Image) b.glctx.UseProgram(b.ipar.ID) b.glctx.ActiveTexture(gl.TEXTURE0) b.glctx.BindTexture(gl.TEXTURE_2D, img.tex) diff --git a/canvas.go b/canvas.go index c08a2c9..9532949 100644 --- a/canvas.go +++ b/canvas.go @@ -74,7 +74,7 @@ type drawStyle struct { color color.RGBA radialGradient *RadialGradient linearGradient *LinearGradient - image *Image + imagePattern *ImagePattern } type lineJoin uint8 @@ -202,6 +202,8 @@ func (cv *Canvas) SetStrokeStyle(value ...interface{}) { cv.state.stroke = cv.parseStyle(value...) } +var imagePatterns = make(map[interface{}]*ImagePattern) + func (cv *Canvas) parseStyle(value ...interface{}) drawStyle { var style drawStyle if len(value) == 1 { @@ -220,7 +222,12 @@ func (cv *Canvas) parseStyle(value ...interface{}) drawStyle { } else if len(value) == 1 { switch v := value[0].(type) { case *Image, string: - style.image = cv.getImage(v) + if _, ok := imagePatterns[v]; !ok { + imagePatterns[v] = cv.CreatePattern(v, "") + } + style.imagePattern = imagePatterns[v] + case *ImagePattern: + style.imagePattern = v } } return style @@ -249,8 +256,12 @@ func (cv *Canvas) backendFillStyle(s *drawStyle, alpha float64) backendbase.Fill stl.Gradient.RadFrom = rg.radFrom stl.Gradient.RadTo = rg.radTo stl.RadialGradient = rg.grad - } else if img := s.image; img != nil { - stl.Image = img.img + } else if ip := s.imagePattern; ip != nil { + if ip.ip == nil { + stl.Color = color.RGBA{} + } else { + stl.ImagePattern = ip.ip + } } else { alpha *= float64(s.color.A) / 255 } diff --git a/images.go b/images.go index 0d46156..7dd558e 100644 --- a/images.go +++ b/images.go @@ -183,3 +183,30 @@ func (cv *Canvas) GetImageData(x, y, w, h int) *image.RGBA { func (cv *Canvas) PutImageData(img *image.RGBA, x, y int) { cv.b.PutImageData(img, x, y) } + +// ImagePattern is an image pattern that can be used for any +// fill call +type ImagePattern struct { + cv *Canvas + img *Image + ip backendbase.ImagePattern +} + +func (ip *ImagePattern) data() backendbase.ImagePatternData { + return backendbase.ImagePatternData{ + Image: ip.img.img, + } +} + +// CreatePattern creates a new image pattern with the specified +// image and repetition +func (cv *Canvas) CreatePattern(src interface{}, repetition string) *ImagePattern { + ip := &ImagePattern{ + cv: cv, + img: cv.getImage(src), + } + if ip.img != nil { + ip.ip = cv.b.LoadImagePattern(ip.data()) + } + return ip +}