moved image loading and drawing into backend
This commit is contained in:
parent
7cef867541
commit
476dbac6e2
10 changed files with 406 additions and 320 deletions
|
@ -1,12 +1,36 @@
|
|||
package backendbase
|
||||
|
||||
import "image/color"
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
type Style struct {
|
||||
// Backend is used by the canvas to actually do the final
|
||||
// drawing. This enables the backend to be implemented by
|
||||
// various methods (OpenGL, but also other APIs or software)
|
||||
type Backend interface {
|
||||
ClearRect(x, y, w, h int)
|
||||
Clear(pts [4][2]float64)
|
||||
Fill(style *FillStyle, pts [][2]float64)
|
||||
LoadImage(img image.Image) (Image, error)
|
||||
DrawImage(dimg Image, sx, sy, sw, sh, dx, dy, dw, dh float64, alpha float64)
|
||||
}
|
||||
|
||||
// FillStyle is the color and other details on how to fill
|
||||
type FillStyle struct {
|
||||
Color color.RGBA
|
||||
GlobalAlpha float64
|
||||
Blur float64
|
||||
// radialGradient *RadialGradient
|
||||
// linearGradient *LinearGradient
|
||||
// image *Image
|
||||
}
|
||||
|
||||
type Image interface {
|
||||
Width() int
|
||||
Height() int
|
||||
Size() (w, h int)
|
||||
Delete()
|
||||
IsDeleted() bool
|
||||
Replace(src image.Image) error
|
||||
IsOpaque() bool
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func (b *GoGLBackend) Clear(pts [4][2]float64) {
|
|||
gl.Enable(gl.BLEND)
|
||||
}
|
||||
|
||||
func (b *GoGLBackend) Fill(style *backendbase.Style, pts [][2]float64) {
|
||||
func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
|
||||
if style.Blur > 0 {
|
||||
b.offscr1.alpha = true
|
||||
b.enableTextureRenderTarget(&b.offscr1)
|
||||
|
@ -71,7 +71,7 @@ func (b *GoGLBackend) Fill(style *backendbase.Style, pts [][2]float64) {
|
|||
gl.BindBuffer(gl.ARRAY_BUFFER, b.buf)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, len(b.ptsBuf)*4, unsafe.Pointer(&b.ptsBuf[0]), gl.STREAM_DRAW)
|
||||
|
||||
if style.GlobalAlpha >= 1 && style.Color.A >= 255 {
|
||||
if style.Color.A >= 255 {
|
||||
vertex := b.useShader(style)
|
||||
|
||||
gl.EnableVertexAttribArray(vertex)
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"image/color"
|
||||
|
||||
"github.com/go-gl/gl/v3.2-core/gl"
|
||||
"github.com/tfriedel6/canvas"
|
||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
||||
)
|
||||
|
||||
|
@ -46,7 +45,7 @@ type offscreenBuffer struct {
|
|||
alpha bool
|
||||
}
|
||||
|
||||
func New(x, y, w, h int) (canvas.Backend, error) {
|
||||
func New(x, y, w, h int) (backendbase.Backend, error) {
|
||||
err := gl.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -219,6 +218,23 @@ func glError() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Activate makes this GL backend active and sets the viewport. Only
|
||||
// needs to be called if any other GL code changes the viewport
|
||||
func (b *GoGLBackend) Activate() {
|
||||
// if b.offscreen {
|
||||
// gli.Viewport(0, 0, int32(cv.w), int32(cv.h))
|
||||
// cv.enableTextureRenderTarget(&cv.offscrBuf)
|
||||
// cv.offscrImg.w = cv.offscrBuf.w
|
||||
// cv.offscrImg.h = cv.offscrBuf.h
|
||||
// cv.offscrImg.tex = cv.offscrBuf.tex
|
||||
// } else {
|
||||
gl.Viewport(int32(b.x), int32(b.y), int32(b.w), int32(b.h))
|
||||
b.disableTextureRenderTarget()
|
||||
// }
|
||||
// b.applyScissor()
|
||||
gl.Clear(gl.STENCIL_BUFFER_BIT)
|
||||
}
|
||||
|
||||
type glColor struct {
|
||||
r, g, b, a float64
|
||||
}
|
||||
|
@ -232,7 +248,7 @@ func colorGoToGL(c color.RGBA) glColor {
|
|||
return glc
|
||||
}
|
||||
|
||||
func (b *GoGLBackend) useShader(style *backendbase.Style) (vertexLoc uint32) {
|
||||
func (b *GoGLBackend) useShader(style *backendbase.FillStyle) (vertexLoc uint32) {
|
||||
// if lg := style.LinearGradient; lg != nil {
|
||||
// lg.load()
|
||||
// gl.ActiveTexture(gl.TEXTURE0)
|
||||
|
@ -293,11 +309,11 @@ func (b *GoGLBackend) useShader(style *backendbase.Style) (vertexLoc uint32) {
|
|||
gl.Uniform2f(b.sr.CanvasSize, float32(b.fw), float32(b.fh))
|
||||
c := colorGoToGL(style.Color)
|
||||
gl.Uniform4f(b.sr.Color, float32(c.r), float32(c.g), float32(c.b), float32(c.a))
|
||||
gl.Uniform1f(b.sr.GlobalAlpha, float32(style.GlobalAlpha))
|
||||
gl.Uniform1f(b.sr.GlobalAlpha, 1)
|
||||
return b.sr.Vertex
|
||||
}
|
||||
|
||||
func (b *GoGLBackend) useAlphaShader(style *backendbase.Style, alphaTexSlot int32) (vertexLoc, alphaTexCoordLoc uint32) {
|
||||
func (b *GoGLBackend) useAlphaShader(style *backendbase.FillStyle, alphaTexSlot int32) (vertexLoc, alphaTexCoordLoc uint32) {
|
||||
// if lg := style.LinearGradient; lg != nil {
|
||||
// lg.load()
|
||||
// gl.ActiveTexture(gl.TEXTURE0)
|
||||
|
@ -362,7 +378,7 @@ func (b *GoGLBackend) useAlphaShader(style *backendbase.Style, alphaTexSlot int3
|
|||
c := colorGoToGL(style.Color)
|
||||
gl.Uniform4f(b.sar.Color, float32(c.r), float32(c.g), float32(c.b), float32(c.a))
|
||||
gl.Uniform1i(b.sar.AlphaTex, alphaTexSlot)
|
||||
gl.Uniform1f(b.sar.GlobalAlpha, float32(style.GlobalAlpha))
|
||||
gl.Uniform1f(b.sar.GlobalAlpha, 1)
|
||||
return b.sar.Vertex, b.sar.AlphaTexCoord
|
||||
}
|
||||
|
||||
|
|
250
backend/gogl/images.go
Normal file
250
backend/gogl/images.go
Normal file
|
@ -0,0 +1,250 @@
|
|||
package goglbackend
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-gl/gl/v3.2-core/gl"
|
||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
||||
)
|
||||
|
||||
// Image represents a loaded image that can be used in various drawing functions
|
||||
type Image struct {
|
||||
w, h int
|
||||
tex uint32
|
||||
deleted bool
|
||||
opaque bool
|
||||
}
|
||||
|
||||
func (b *GoGLBackend) LoadImage(src image.Image) (backendbase.Image, error) {
|
||||
var tex uint32
|
||||
gl.GenTextures(1, &tex)
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, tex)
|
||||
if src == nil {
|
||||
return &Image{tex: tex}, nil
|
||||
}
|
||||
|
||||
img, err := loadImage(src, tex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(img, func(img *Image) {
|
||||
if !img.deleted {
|
||||
b.glChan <- func() {
|
||||
gl.DeleteTextures(1, &img.tex)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func loadImage(src image.Image, tex uint32) (*Image, error) {
|
||||
var img *Image
|
||||
var err error
|
||||
switch v := src.(type) {
|
||||
case *image.RGBA:
|
||||
img, err = loadImageRGBA(v, tex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *image.Gray:
|
||||
img, err = loadImageGray(v, tex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case image.Image:
|
||||
img, err = loadImageConverted(v, tex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("Unsupported source type")
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func loadImageRGBA(src *image.RGBA, tex uint32) (*Image, error) {
|
||||
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy(), opaque: true}
|
||||
|
||||
checkOpaque:
|
||||
for y := 0; y < img.h; y++ {
|
||||
off := src.PixOffset(0, y) + 3
|
||||
for x := 0; x < img.w; x++ {
|
||||
if src.Pix[off] < 255 {
|
||||
img.opaque = false
|
||||
break checkOpaque
|
||||
}
|
||||
off += 4
|
||||
}
|
||||
}
|
||||
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if src.Stride == img.w*4 {
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(img.w), int32(img.h), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(&src.Pix[0]))
|
||||
} else {
|
||||
data := make([]uint8, 0, img.w*img.h*4)
|
||||
for y := 0; y < img.h; y++ {
|
||||
start := y * src.Stride
|
||||
end := start + img.w*4
|
||||
data = append(data, src.Pix[start:end]...)
|
||||
}
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(img.w), int32(img.h), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(&data[0]))
|
||||
}
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func loadImageGray(src *image.Gray, tex uint32) (*Image, error) {
|
||||
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy()}
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if src.Stride == img.w {
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, int32(img.w), int32(img.h), 0, gl.RED, gl.UNSIGNED_BYTE, gl.Ptr(&src.Pix[0]))
|
||||
} else {
|
||||
data := make([]uint8, 0, img.w*img.h)
|
||||
for y := 0; y < img.h; y++ {
|
||||
start := y * src.Stride
|
||||
end := start + img.w
|
||||
data = append(data, src.Pix[start:end]...)
|
||||
}
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, int32(img.w), int32(img.h), 0, gl.RED, gl.UNSIGNED_BYTE, gl.Ptr(&data[0]))
|
||||
}
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func loadImageConverted(src image.Image, tex uint32) (*Image, error) {
|
||||
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy(), opaque: true}
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]uint8, 0, img.w*img.h*4)
|
||||
for y := 0; y < img.h; y++ {
|
||||
for x := 0; x < img.w; x++ {
|
||||
ir, ig, ib, ia := src.At(x, y).RGBA()
|
||||
r, g, b, a := uint8(ir>>8), uint8(ig>>8), uint8(ib>>8), uint8(ia>>8)
|
||||
data = append(data, r, g, b, a)
|
||||
if a < 255 {
|
||||
img.opaque = false
|
||||
}
|
||||
}
|
||||
}
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(img.w), int32(img.h), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(&data[0]))
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// Width returns the width of the image
|
||||
func (img *Image) Width() int { return img.w }
|
||||
|
||||
// Height returns the height of the image
|
||||
func (img *Image) Height() int { return img.h }
|
||||
|
||||
// Size returns the width and height of the image
|
||||
func (img *Image) Size() (int, int) { return img.w, img.h }
|
||||
|
||||
// Delete deletes the image from memory. Any draw calls
|
||||
// with a deleted image will not do anything
|
||||
func (img *Image) Delete() {
|
||||
gl.DeleteTextures(1, &img.tex)
|
||||
img.deleted = true
|
||||
}
|
||||
|
||||
// IsDeleted returns true if the Delete function has been
|
||||
// called on this image
|
||||
func (img *Image) IsDeleted() bool { return img.deleted }
|
||||
|
||||
// Replace replaces the image with the new one
|
||||
func (img *Image) Replace(src image.Image) error {
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, img.tex)
|
||||
newImg, err := loadImage(src, img.tex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*img = *newImg
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsOpaque returns true if all pixels in the image
|
||||
// have a full alpha value
|
||||
func (img *Image) IsOpaque() bool { return img.opaque }
|
||||
|
||||
func (b *GoGLBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh, dx, dy, dw, dh float64, alpha float64) {
|
||||
img := dimg.(*Image)
|
||||
|
||||
sx /= float64(img.w)
|
||||
sy /= float64(img.h)
|
||||
sw /= float64(img.w)
|
||||
sh /= float64(img.h)
|
||||
|
||||
gl.StencilFunc(gl.EQUAL, 0, 0xFF)
|
||||
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, b.buf)
|
||||
data := [16]float32{
|
||||
float32(dx), float32(dy),
|
||||
float32(dx), float32(dy + dh),
|
||||
float32(dx + dw), float32(dy + dh),
|
||||
float32(dx + dw), float32(dy),
|
||||
float32(sx), float32(sy),
|
||||
float32(sx), float32(sy + sh),
|
||||
float32(sx + sw), float32(sy + sh),
|
||||
float32(sx + sw), float32(sy),
|
||||
}
|
||||
gl.BufferData(gl.ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl.STREAM_DRAW)
|
||||
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, img.tex)
|
||||
|
||||
gl.UseProgram(b.ir.ID)
|
||||
gl.Uniform1i(b.ir.Image, 0)
|
||||
gl.Uniform2f(b.ir.CanvasSize, float32(b.fw), float32(b.fh))
|
||||
gl.Uniform1f(b.ir.GlobalAlpha, float32(alpha))
|
||||
gl.VertexAttribPointer(b.ir.Vertex, 2, gl.FLOAT, false, 0, nil)
|
||||
gl.VertexAttribPointer(b.ir.TexCoord, 2, gl.FLOAT, false, 0, gl.PtrOffset(8*4))
|
||||
gl.EnableVertexAttribArray(b.ir.Vertex)
|
||||
gl.EnableVertexAttribArray(b.ir.TexCoord)
|
||||
gl.DrawArrays(gl.TRIANGLE_FAN, 0, 4)
|
||||
gl.DisableVertexAttribArray(b.ir.Vertex)
|
||||
gl.DisableVertexAttribArray(b.ir.TexCoord)
|
||||
|
||||
gl.StencilFunc(gl.ALWAYS, 0, 0xFF)
|
||||
}
|
95
canvas.go
95
canvas.go
|
@ -18,7 +18,7 @@ import (
|
|||
// Canvas represents an area on the viewport on which to draw
|
||||
// using a set of functions very similar to the HTML5 canvas
|
||||
type Canvas struct {
|
||||
b Backend
|
||||
b backendbase.Backend
|
||||
|
||||
x, y, w, h int
|
||||
fx, fy, fw, fh float64
|
||||
|
@ -34,16 +34,9 @@ type Canvas struct {
|
|||
offscrBuf offscreenBuffer
|
||||
offscrImg Image
|
||||
|
||||
shadowBuf [][2]float64
|
||||
}
|
||||
images map[interface{}]*Image
|
||||
|
||||
// Backend is used by the canvas to actually do the final
|
||||
// drawing. This enables the backend to be implemented by
|
||||
// various methods (OpenGL, but also other APIs or software)
|
||||
type Backend interface {
|
||||
ClearRect(x, y, w, h int)
|
||||
Clear(pts [4][2]float64)
|
||||
Fill(style *backendbase.Style, pts [][2]float64)
|
||||
shadowBuf [][2]float64
|
||||
}
|
||||
|
||||
type drawState struct {
|
||||
|
@ -149,11 +142,15 @@ var Performance = struct {
|
|||
// While all functions on the canvas use the top left point as
|
||||
// the origin, since GL uses the bottom left coordinate, the
|
||||
// coordinates given here also use the bottom left as origin
|
||||
func New(backend Backend, x, y, w, h int) *Canvas {
|
||||
func New(backend backendbase.Backend, x, y, w, h int) *Canvas {
|
||||
if gli == nil {
|
||||
panic("LoadGL must be called before a canvas can be created")
|
||||
}
|
||||
cv := &Canvas{b: backend, stateStack: make([]drawState, 0, 20)}
|
||||
cv := &Canvas{
|
||||
b: backend,
|
||||
stateStack: make([]drawState, 0, 20),
|
||||
images: make(map[interface{}]*Image),
|
||||
}
|
||||
cv.SetBounds(x, y, w, h)
|
||||
cv.state.lineWidth = 1
|
||||
cv.state.lineAlpha = 1
|
||||
|
@ -169,7 +166,7 @@ func New(backend Backend, x, y, w, h int) *Canvas {
|
|||
// does not render directly to the screen but renders to a
|
||||
// texture instead. If alpha is set to true, the offscreen
|
||||
// canvas will have an alpha channel
|
||||
func NewOffscreen(backend Backend, w, h int, alpha bool) *Canvas {
|
||||
func NewOffscreen(backend backendbase.Backend, w, h int, alpha bool) *Canvas {
|
||||
cv := New(backend, 0, 0, w, h)
|
||||
cv.offscreen = true
|
||||
cv.offscrBuf.alpha = alpha
|
||||
|
@ -217,9 +214,9 @@ func (cv *Canvas) Activate() {
|
|||
if cv.offscreen {
|
||||
gli.Viewport(0, 0, int32(cv.w), int32(cv.h))
|
||||
cv.enableTextureRenderTarget(&cv.offscrBuf)
|
||||
cv.offscrImg.w = cv.offscrBuf.w
|
||||
cv.offscrImg.h = cv.offscrBuf.h
|
||||
cv.offscrImg.tex = cv.offscrBuf.tex
|
||||
// cv.offscrImg.w = cv.offscrBuf.w
|
||||
// cv.offscrImg.h = cv.offscrBuf.h
|
||||
// cv.offscrImg.tex = cv.offscrBuf.tex
|
||||
} else {
|
||||
gli.Viewport(int32(cv.x), int32(cv.y), int32(cv.w), int32(cv.h))
|
||||
cv.disableTextureRenderTarget()
|
||||
|
@ -456,7 +453,7 @@ func glError() error {
|
|||
// the range 0-255, 3 or 4 float values for RGB(A) in the range 0-1, hex strings
|
||||
// in the format "#AABBCC", "#AABBCCDD", "#ABC", or "#ABCD"
|
||||
func (cv *Canvas) SetFillStyle(value ...interface{}) {
|
||||
cv.state.fill = parseStyle(value...)
|
||||
cv.state.fill = cv.parseStyle(value...)
|
||||
}
|
||||
|
||||
// SetStrokeStyle sets the color, gradient, or image for any line drawing calls.
|
||||
|
@ -464,10 +461,10 @@ func (cv *Canvas) SetFillStyle(value ...interface{}) {
|
|||
// RGB(A) in the range 0-255, 3 or 4 float values for RGB(A) in the range 0-1,
|
||||
// hex strings in the format "#AABBCC", "#AABBCCDD", "#ABC", or "#ABCD"
|
||||
func (cv *Canvas) SetStrokeStyle(value ...interface{}) {
|
||||
cv.state.stroke = parseStyle(value...)
|
||||
cv.state.stroke = cv.parseStyle(value...)
|
||||
}
|
||||
|
||||
func parseStyle(value ...interface{}) drawStyle {
|
||||
func (cv *Canvas) parseStyle(value ...interface{}) drawStyle {
|
||||
var style drawStyle
|
||||
if len(value) == 1 {
|
||||
switch v := value[0].(type) {
|
||||
|
@ -485,7 +482,7 @@ func parseStyle(value ...interface{}) drawStyle {
|
|||
} else if len(value) == 1 {
|
||||
switch v := value[0].(type) {
|
||||
case *Image, string:
|
||||
style.image = getImage(v)
|
||||
style.image = cv.getImage(v)
|
||||
}
|
||||
}
|
||||
return style
|
||||
|
@ -499,16 +496,16 @@ func (s *drawStyle) isOpaque() bool {
|
|||
return rg.opaque
|
||||
}
|
||||
if img := s.image; img != nil {
|
||||
return img.opaque
|
||||
return img.img.IsOpaque()
|
||||
}
|
||||
return s.color.A >= 255
|
||||
}
|
||||
|
||||
func (cv *Canvas) backendStyle(s *drawStyle, alpha float64) backendbase.Style {
|
||||
return backendbase.Style{
|
||||
Color: s.color,
|
||||
GlobalAlpha: cv.state.globalAlpha * alpha,
|
||||
}
|
||||
func (cv *Canvas) backendFillStyle(s *drawStyle, alpha float64) backendbase.FillStyle {
|
||||
col := s.color
|
||||
finalAlpha := (float64(s.color.A) / 255) * alpha * cv.state.globalAlpha
|
||||
col.A = uint8(finalAlpha * 255)
|
||||
return backendbase.FillStyle{Color: col}
|
||||
}
|
||||
|
||||
func (cv *Canvas) useShader(style *drawStyle) (vertexLoc uint32) {
|
||||
|
@ -551,16 +548,18 @@ func (cv *Canvas) useShader(style *drawStyle) (vertexLoc uint32) {
|
|||
gli.Uniform1f(rgr.globalAlpha, float32(cv.state.globalAlpha))
|
||||
return rgr.vertex
|
||||
}
|
||||
if img := style.image; img != nil {
|
||||
gli.UseProgram(ipr.id)
|
||||
gli.ActiveTexture(gl_TEXTURE0)
|
||||
gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
||||
gli.Uniform2f(ipr.canvasSize, float32(cv.fw), float32(cv.fh))
|
||||
gli.Uniform2f(ipr.imageSize, float32(img.w), float32(img.h))
|
||||
gli.Uniform1i(ipr.image, 0)
|
||||
gli.Uniform1f(ipr.globalAlpha, float32(cv.state.globalAlpha))
|
||||
return ipr.vertex
|
||||
}
|
||||
// if img := style.image; img != nil {
|
||||
// gli.UseProgram(ipr.id)
|
||||
// gli.ActiveTexture(gl_TEXTURE0)
|
||||
// gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
||||
// gli.Uniform2f(ipr.canvasSize, float32(cv.fw), float32(cv.fh))
|
||||
// inv := cv.state.transform.invert().f32()
|
||||
// gli.UniformMatrix3fv(ipr.invmat, 1, false, &inv[0])
|
||||
// gli.Uniform2f(ipr.imageSize, float32(img.w), float32(img.h))
|
||||
// gli.Uniform1i(ipr.image, 0)
|
||||
// gli.Uniform1f(ipr.globalAlpha, float32(cv.state.globalAlpha))
|
||||
// return ipr.vertex
|
||||
// }
|
||||
|
||||
gli.UseProgram(sr.id)
|
||||
gli.Uniform2f(sr.canvasSize, float32(cv.fw), float32(cv.fh))
|
||||
|
@ -612,17 +611,19 @@ func (cv *Canvas) useAlphaShader(style *drawStyle, alphaTexSlot int32) (vertexLo
|
|||
gli.Uniform1f(rgar.globalAlpha, float32(cv.state.globalAlpha))
|
||||
return rgar.vertex, rgar.alphaTexCoord
|
||||
}
|
||||
if img := style.image; img != nil {
|
||||
gli.UseProgram(ipar.id)
|
||||
gli.ActiveTexture(gl_TEXTURE0)
|
||||
gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
||||
gli.Uniform2f(ipar.canvasSize, float32(cv.fw), float32(cv.fh))
|
||||
gli.Uniform2f(ipar.imageSize, float32(img.w), float32(img.h))
|
||||
gli.Uniform1i(ipar.image, 0)
|
||||
gli.Uniform1i(ipar.alphaTex, alphaTexSlot)
|
||||
gli.Uniform1f(ipar.globalAlpha, float32(cv.state.globalAlpha))
|
||||
return ipar.vertex, ipar.alphaTexCoord
|
||||
}
|
||||
// if img := style.image; img != nil {
|
||||
// gli.UseProgram(ipar.id)
|
||||
// gli.ActiveTexture(gl_TEXTURE0)
|
||||
// gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
||||
// gli.Uniform2f(ipar.canvasSize, float32(cv.fw), float32(cv.fh))
|
||||
// inv := cv.state.transform.invert().f32()
|
||||
// gli.UniformMatrix3fv(ipar.invmat, 1, false, &inv[0])
|
||||
// gli.Uniform2f(ipar.imageSize, float32(img.w), float32(img.h))
|
||||
// gli.Uniform1i(ipar.image, 0)
|
||||
// gli.Uniform1i(ipar.alphaTex, alphaTexSlot)
|
||||
// gli.Uniform1f(ipar.globalAlpha, float32(cv.state.globalAlpha))
|
||||
// return ipar.vertex, ipar.alphaTexCoord
|
||||
// }
|
||||
|
||||
gli.UseProgram(sar.id)
|
||||
gli.Uniform2f(sar.canvasSize, float32(cv.fw), float32(cv.fh))
|
||||
|
|
|
@ -17,15 +17,13 @@ import (
|
|||
)
|
||||
|
||||
func run(t *testing.T, fn func(cv *canvas.Canvas)) {
|
||||
wnd, cv2, err := sdlcanvas.CreateWindow(100, 100, "test")
|
||||
wnd, cv, err := sdlcanvas.CreateWindow(100, 100, "test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to crete window: %v", err)
|
||||
return
|
||||
}
|
||||
defer wnd.Destroy()
|
||||
|
||||
cv := canvas.NewOffscreen(wnd.Backend, 100, 100, false)
|
||||
|
||||
gl.Disable(gl.MULTISAMPLE)
|
||||
|
||||
wnd.StartFrame()
|
||||
|
@ -34,9 +32,6 @@ func run(t *testing.T, fn func(cv *canvas.Canvas)) {
|
|||
fn(cv)
|
||||
img := cv.GetImageData(0, 0, 100, 100)
|
||||
|
||||
cv2.DrawImage(cv)
|
||||
img2 := cv2.GetImageData(0, 0, 100, 100)
|
||||
|
||||
caller, _, _, ok := runtime.Caller(1)
|
||||
if !ok {
|
||||
t.Fatal("Failed to get caller")
|
||||
|
@ -87,16 +82,9 @@ func run(t *testing.T, fn func(cv *canvas.Canvas)) {
|
|||
for y := 0; y < 100; y++ {
|
||||
for x := 0; x < 100; x++ {
|
||||
r1, g1, b1, a1 := img.At(x, y).RGBA()
|
||||
r2, g2, b2, a2 := img2.At(x, y).RGBA()
|
||||
r3, g3, b3, a3 := refImg.At(x, y).RGBA()
|
||||
if r1 != r3 || g1 != g3 || b1 != b3 || a1 != a3 {
|
||||
r2, g2, b2, a2 := refImg.At(x, y).RGBA()
|
||||
if r1 != r2 || g1 != g2 || b1 != b2 || a1 != a2 {
|
||||
writeImage(img, fmt.Sprintf("testdata/%s_fail.png", callerFuncName))
|
||||
t.Error("onscreen canvas failed")
|
||||
t.FailNow()
|
||||
}
|
||||
if r2 != r3 || g2 != g3 || b2 != b3 || a2 != a3 {
|
||||
writeImage(img2, fmt.Sprintf("testdata/%s_fail.png", callerFuncName))
|
||||
t.Error("offscreen canvas failed")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
|
286
images.go
286
images.go
|
@ -7,260 +7,108 @@ import (
|
|||
"image"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
||||
)
|
||||
|
||||
// Image represents a loaded image that can be used in various drawing functions
|
||||
type Image struct {
|
||||
w, h int
|
||||
tex uint32
|
||||
deleted bool
|
||||
opaque bool
|
||||
cv *Canvas
|
||||
img backendbase.Image
|
||||
}
|
||||
|
||||
var images = make(map[interface{}]*Image)
|
||||
|
||||
// LoadImage loads an image. The src parameter can be either an image from the
|
||||
// standard image package, a byte slice that will be loaded, or a file name
|
||||
// string. If you want the canvas package to load the image, make sure you
|
||||
// import the required format packages
|
||||
func LoadImage(src interface{}) (*Image, error) {
|
||||
if gli == nil {
|
||||
panic("LoadGL must be called before images can be loaded")
|
||||
}
|
||||
|
||||
var tex uint32
|
||||
gli.GenTextures(1, &tex)
|
||||
gli.ActiveTexture(gl_TEXTURE0)
|
||||
gli.BindTexture(gl_TEXTURE_2D, tex)
|
||||
if src == nil {
|
||||
return &Image{tex: tex}, nil
|
||||
}
|
||||
|
||||
img, err := loadImage(src, tex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(img, func(img *Image) {
|
||||
if !img.deleted {
|
||||
glChan <- func() {
|
||||
gli.DeleteTextures(1, &img.tex)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func loadImage(src interface{}, tex uint32) (*Image, error) {
|
||||
var img *Image
|
||||
var err error
|
||||
func (cv *Canvas) LoadImage(src interface{}) (*Image, error) {
|
||||
var srcImg image.Image
|
||||
switch v := src.(type) {
|
||||
case *image.RGBA:
|
||||
img, err = loadImageRGBA(v, tex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *image.Gray:
|
||||
img, err = loadImageGray(v, tex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case image.Image:
|
||||
img, err = loadImageConverted(v, tex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srcImg = v
|
||||
case string:
|
||||
data, err := ioutil.ReadFile(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srcImg, _, err := image.Decode(bytes.NewReader(data))
|
||||
srcImg, _, err = image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loadImage(srcImg, tex)
|
||||
case []byte:
|
||||
srcImg, _, err := image.Decode(bytes.NewReader(v))
|
||||
var err error
|
||||
srcImg, _, err = image.Decode(bytes.NewReader(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loadImage(srcImg, tex)
|
||||
case *Canvas:
|
||||
src = cv.GetImageData(0, 0, cv.Width(), cv.Height())
|
||||
default:
|
||||
return nil, errors.New("Unsupported source type")
|
||||
}
|
||||
return img, nil
|
||||
backendImg, err := cv.b.LoadImage(srcImg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Image{cv: cv, img: backendImg}, nil
|
||||
}
|
||||
|
||||
func getImage(src interface{}) *Image {
|
||||
if img, ok := images[src]; ok {
|
||||
func (cv *Canvas) getImage(src interface{}) *Image {
|
||||
if img, ok := cv.images[src]; ok {
|
||||
return img
|
||||
}
|
||||
switch v := src.(type) {
|
||||
case *Image:
|
||||
return v
|
||||
case image.Image:
|
||||
img, err := LoadImage(v)
|
||||
img, err := cv.LoadImage(v)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error loading image: %v\n", err)
|
||||
images[src] = nil
|
||||
cv.images[src] = nil
|
||||
return nil
|
||||
}
|
||||
images[v] = img
|
||||
cv.images[v] = img
|
||||
return img
|
||||
case string:
|
||||
img, err := LoadImage(v)
|
||||
img, err := cv.LoadImage(v)
|
||||
if err != nil {
|
||||
if strings.Contains(strings.ToLower(err.Error()), "format") {
|
||||
fmt.Fprintf(os.Stderr, "Error loading image %s: %v\nIt may be necessary to import the appropriate decoder, e.g.\nimport _ \"image/jpeg\"\n", v, err)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error loading image %s: %v\n", v, err)
|
||||
}
|
||||
images[src] = nil
|
||||
cv.images[src] = nil
|
||||
return nil
|
||||
}
|
||||
images[v] = img
|
||||
cv.images[v] = img
|
||||
return img
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Unknown image type: %T\n", src)
|
||||
images[src] = nil
|
||||
cv.images[src] = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadImageRGBA(src *image.RGBA, tex uint32) (*Image, error) {
|
||||
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy(), opaque: true}
|
||||
|
||||
checkOpaque:
|
||||
for y := 0; y < img.h; y++ {
|
||||
off := src.PixOffset(0, y) + 3
|
||||
for x := 0; x < img.w; x++ {
|
||||
if src.Pix[off] < 255 {
|
||||
img.opaque = false
|
||||
break checkOpaque
|
||||
}
|
||||
off += 4
|
||||
}
|
||||
}
|
||||
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR_MIPMAP_LINEAR)
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if src.Stride == img.w*4 {
|
||||
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGBA, int32(img.w), int32(img.h), 0, gl_RGBA, gl_UNSIGNED_BYTE, gli.Ptr(&src.Pix[0]))
|
||||
} else {
|
||||
data := make([]uint8, 0, img.w*img.h*4)
|
||||
for y := 0; y < img.h; y++ {
|
||||
start := y * src.Stride
|
||||
end := start + img.w*4
|
||||
data = append(data, src.Pix[start:end]...)
|
||||
}
|
||||
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGBA, int32(img.w), int32(img.h), 0, gl_RGBA, gl_UNSIGNED_BYTE, gli.Ptr(&data[0]))
|
||||
}
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gli.GenerateMipmap(gl_TEXTURE_2D)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func loadImageGray(src *image.Gray, tex uint32) (*Image, error) {
|
||||
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy()}
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR_MIPMAP_LINEAR)
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if src.Stride == img.w {
|
||||
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RED, int32(img.w), int32(img.h), 0, gl_RED, gl_UNSIGNED_BYTE, gli.Ptr(&src.Pix[0]))
|
||||
} else {
|
||||
data := make([]uint8, 0, img.w*img.h)
|
||||
for y := 0; y < img.h; y++ {
|
||||
start := y * src.Stride
|
||||
end := start + img.w
|
||||
data = append(data, src.Pix[start:end]...)
|
||||
}
|
||||
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RED, int32(img.w), int32(img.h), 0, gl_RED, gl_UNSIGNED_BYTE, gli.Ptr(&data[0]))
|
||||
}
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gli.GenerateMipmap(gl_TEXTURE_2D)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func loadImageConverted(src image.Image, tex uint32) (*Image, error) {
|
||||
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy(), opaque: true}
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR_MIPMAP_LINEAR)
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
|
||||
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]uint8, 0, img.w*img.h*4)
|
||||
for y := 0; y < img.h; y++ {
|
||||
for x := 0; x < img.w; x++ {
|
||||
ir, ig, ib, ia := src.At(x, y).RGBA()
|
||||
r, g, b, a := uint8(ir>>8), uint8(ig>>8), uint8(ib>>8), uint8(ia>>8)
|
||||
data = append(data, r, g, b, a)
|
||||
if a < 255 {
|
||||
img.opaque = false
|
||||
}
|
||||
}
|
||||
}
|
||||
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGBA, int32(img.w), int32(img.h), 0, gl_RGBA, gl_UNSIGNED_BYTE, gli.Ptr(&data[0]))
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gli.GenerateMipmap(gl_TEXTURE_2D)
|
||||
if err := glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// Width returns the width of the image
|
||||
func (img *Image) Width() int { return img.w }
|
||||
func (img *Image) Width() int { return img.img.Width() }
|
||||
|
||||
// Height returns the height of the image
|
||||
func (img *Image) Height() int { return img.h }
|
||||
func (img *Image) Height() int { return img.img.Height() }
|
||||
|
||||
// Size returns the width and height of the image
|
||||
func (img *Image) Size() (int, int) { return img.w, img.h }
|
||||
func (img *Image) Size() (int, int) { return img.img.Size() }
|
||||
|
||||
// Delete deletes the image from memory. Any draw calls with a deleted image
|
||||
// will not do anything
|
||||
func (img *Image) Delete() {
|
||||
gli.DeleteTextures(1, &img.tex)
|
||||
img.deleted = true
|
||||
}
|
||||
func (img *Image) Delete() { img.img.Delete() }
|
||||
|
||||
// Replace replaces the image with the new one
|
||||
func (img *Image) Replace(src interface{}) {
|
||||
gli.ActiveTexture(gl_TEXTURE0)
|
||||
gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
||||
newImg, err := loadImage(src, img.tex)
|
||||
func (img *Image) Replace(src interface{}) error {
|
||||
newImg, err := img.cv.LoadImage(src)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error replacing image: %v\n", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
*img = *newImg
|
||||
img.img = newImg.img
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawImage draws the given image to the given coordinates. The image
|
||||
|
@ -281,22 +129,22 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float64) {
|
|||
img = &cv2.offscrImg
|
||||
flip = true
|
||||
} else {
|
||||
img = getImage(image)
|
||||
img = cv.getImage(image)
|
||||
}
|
||||
|
||||
if img == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if img.deleted {
|
||||
if img.img.IsDeleted() {
|
||||
return
|
||||
}
|
||||
|
||||
cv.activate()
|
||||
|
||||
var sx, sy, sw, sh, dx, dy, dw, dh float64
|
||||
sw, sh = float64(img.w), float64(img.h)
|
||||
dw, dh = float64(img.w), float64(img.h)
|
||||
sw, sh = float64(img.Width()), float64(img.Height())
|
||||
dw, dh = float64(img.Width()), float64(img.Height())
|
||||
if len(coords) == 2 {
|
||||
dx, dy = coords[0], coords[1]
|
||||
} else if len(coords) == 4 {
|
||||
|
@ -314,55 +162,13 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float64) {
|
|||
dh = -dh
|
||||
}
|
||||
|
||||
sx /= float64(img.w)
|
||||
sy /= float64(img.h)
|
||||
sw /= float64(img.w)
|
||||
sh /= float64(img.h)
|
||||
var data [4][2]float64
|
||||
data[0] = cv.tf(vec{dx, dy})
|
||||
data[1] = cv.tf(vec{dx, dy + dh})
|
||||
data[2] = cv.tf(vec{dx + dw, dy + dh})
|
||||
data[3] = cv.tf(vec{dx + dw, dy})
|
||||
|
||||
p0 := cv.tf(vec{dx, dy})
|
||||
p1 := cv.tf(vec{dx, dy + dh})
|
||||
p2 := cv.tf(vec{dx + dw, dy + dh})
|
||||
p3 := cv.tf(vec{dx + dw, dy})
|
||||
cv.drawShadow2(data[:])
|
||||
|
||||
if cv.state.shadowColor.A != 0 {
|
||||
tris := [24]float32{
|
||||
0, 0,
|
||||
float32(cv.fw), 0,
|
||||
float32(cv.fw), float32(cv.fh),
|
||||
0, 0,
|
||||
float32(cv.fw), float32(cv.fh),
|
||||
0, float32(cv.fh),
|
||||
float32(p0[0]), float32(p0[1]),
|
||||
float32(p3[0]), float32(p3[1]),
|
||||
float32(p2[0]), float32(p2[1]),
|
||||
float32(p0[0]), float32(p0[1]),
|
||||
float32(p2[0]), float32(p2[1]),
|
||||
float32(p1[0]), float32(p1[1]),
|
||||
}
|
||||
cv.drawShadow(tris[:])
|
||||
}
|
||||
|
||||
gli.StencilFunc(gl_EQUAL, 0, 0xFF)
|
||||
|
||||
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
|
||||
data := [16]float32{float32(p0[0]), float32(p0[1]), float32(p1[0]), float32(p1[1]), float32(p2[0]), float32(p2[1]), float32(p3[0]), float32(p3[1]),
|
||||
float32(sx), float32(sy), float32(sx), float32(sy + sh), float32(sx + sw), float32(sy + sh), float32(sx + sw), float32(sy)}
|
||||
gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW)
|
||||
|
||||
gli.ActiveTexture(gl_TEXTURE0)
|
||||
gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
||||
|
||||
gli.UseProgram(ir.id)
|
||||
gli.Uniform1i(ir.image, 0)
|
||||
gli.Uniform2f(ir.canvasSize, float32(cv.fw), float32(cv.fh))
|
||||
gli.Uniform1f(ir.globalAlpha, float32(cv.state.globalAlpha))
|
||||
gli.VertexAttribPointer(ir.vertex, 2, gl_FLOAT, false, 0, 0)
|
||||
gli.VertexAttribPointer(ir.texCoord, 2, gl_FLOAT, false, 0, 8*4)
|
||||
gli.EnableVertexAttribArray(ir.vertex)
|
||||
gli.EnableVertexAttribArray(ir.texCoord)
|
||||
gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4)
|
||||
gli.DisableVertexAttribArray(ir.vertex)
|
||||
gli.DisableVertexAttribArray(ir.texCoord)
|
||||
|
||||
gli.StencilFunc(gl_ALWAYS, 0, 0xFF)
|
||||
cv.b.DrawImage(img.img, sx, sy, sw, sh, dx, dy, dw, dh, cv.state.globalAlpha)
|
||||
}
|
||||
|
|
6
paths.go
6
paths.go
|
@ -158,7 +158,7 @@ func (cv *Canvas) strokePath(path *Path2D) {
|
|||
|
||||
cv.drawShadow2(tris)
|
||||
|
||||
stl := cv.backendStyle(&cv.state.stroke, 1)
|
||||
stl := cv.backendFillStyle(&cv.state.stroke, 1)
|
||||
cv.b.Fill(&stl, tris)
|
||||
}
|
||||
|
||||
|
@ -352,7 +352,7 @@ func (cv *Canvas) FillPath(path *Path2D) {
|
|||
|
||||
cv.drawShadow2(tris)
|
||||
|
||||
stl := cv.backendStyle(&cv.state.fill, 1)
|
||||
stl := cv.backendFillStyle(&cv.state.fill, 1)
|
||||
cv.b.Fill(&stl, tris)
|
||||
}
|
||||
|
||||
|
@ -528,7 +528,7 @@ func (cv *Canvas) FillRect(x, y, w, h float64) {
|
|||
|
||||
cv.drawShadow2(data[:])
|
||||
|
||||
stl := cv.backendStyle(&cv.state.fill, 1)
|
||||
stl := cv.backendFillStyle(&cv.state.fill, 1)
|
||||
cv.b.Fill(&stl, data[:])
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/go-gl/gl/v3.2-core/gl"
|
||||
"github.com/tfriedel6/canvas"
|
||||
"github.com/tfriedel6/canvas/backend/backendbase"
|
||||
"github.com/tfriedel6/canvas/backend/gogl"
|
||||
"github.com/tfriedel6/canvas/glimpl/gogl"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
|
@ -23,7 +24,7 @@ type Window struct {
|
|||
Window *sdl.Window
|
||||
WindowID uint32
|
||||
GLContext sdl.GLContext
|
||||
Backend canvas.Backend
|
||||
Backend backendbase.Backend
|
||||
canvas *canvas.Canvas
|
||||
frameTimes [10]time.Time
|
||||
frameIndex int
|
||||
|
|
|
@ -28,7 +28,7 @@ func (cv *Canvas) drawShadow2(pts [][2]float64) {
|
|||
})
|
||||
}
|
||||
|
||||
style := backendbase.Style{Color: cv.state.shadowColor, GlobalAlpha: 1, Blur: cv.state.shadowBlur}
|
||||
style := backendbase.FillStyle{Color: cv.state.shadowColor, Blur: cv.state.shadowBlur}
|
||||
cv.b.Fill(&style, cv.shadowBuf)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue