mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
driver: Add Region and sourceRegion parameter at Draw
This is a preparation to remove source-region information from vertices. Updates #1210
This commit is contained in:
parent
dcba380827
commit
71c9e7ac40
11
image.go
11
image.go
@ -333,7 +333,14 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
|||||||
is := make([]uint16, len(indices))
|
is := make([]uint16, len(indices))
|
||||||
copy(is, indices)
|
copy(is, indices)
|
||||||
|
|
||||||
i.buffered.DrawTriangles(img.buffered, vs, is, options.ColorM.impl, mode, filter, driver.Address(options.Address), nil, nil)
|
sr := driver.Region{
|
||||||
|
X: float32(b.Min.X),
|
||||||
|
Y: float32(b.Min.Y),
|
||||||
|
Width: float32(b.Dx()),
|
||||||
|
Height: float32(b.Dy()),
|
||||||
|
}
|
||||||
|
|
||||||
|
i.buffered.DrawTriangles(img.buffered, vs, is, options.ColorM.impl, mode, filter, driver.Address(options.Address), sr, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DrawTrianglesWithShaderOptions struct {
|
type DrawTrianglesWithShaderOptions struct {
|
||||||
@ -402,7 +409,7 @@ func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, sha
|
|||||||
is := make([]uint16, len(indices))
|
is := make([]uint16, len(indices))
|
||||||
copy(is, indices)
|
copy(is, indices)
|
||||||
|
|
||||||
i.buffered.DrawTriangles(nil, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, shader.shader, us)
|
i.buffered.DrawTriangles(nil, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, shader.shader, us)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubImage returns an image representing the portion of the image p visible through r.
|
// SubImage returns an image representing the portion of the image p visible through r.
|
||||||
|
@ -266,7 +266,7 @@ func (i *Image) drawImage(src *Image, bounds image.Rectangle, g mipmap.GeoM, col
|
|||||||
// DrawTriangles draws the src image with the given vertices.
|
// DrawTriangles draws the src image with the given vertices.
|
||||||
//
|
//
|
||||||
// Copying vertices and indices is the caller's responsibility.
|
// Copying vertices and indices is the caller's responsibility.
|
||||||
func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader, uniforms []interface{}) {
|
func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||||
var srcs []*Image
|
var srcs []*Image
|
||||||
if src != nil {
|
if src != nil {
|
||||||
srcs = append(srcs, src)
|
srcs = append(srcs, src)
|
||||||
@ -286,7 +286,7 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16,
|
|||||||
if maybeCanAddDelayedCommand() {
|
if maybeCanAddDelayedCommand() {
|
||||||
if tryAddDelayedCommand(func() error {
|
if tryAddDelayedCommand(func() error {
|
||||||
// Arguments are not copied. Copying is the caller's responsibility.
|
// Arguments are not copied. Copying is the caller's responsibility.
|
||||||
i.DrawTriangles(src, vertices, indices, colorm, mode, filter, address, shader, uniforms)
|
i.DrawTriangles(src, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms)
|
||||||
return nil
|
return nil
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
@ -316,7 +316,7 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16,
|
|||||||
if src != nil {
|
if src != nil {
|
||||||
srcImg = src.img
|
srcImg = src.img
|
||||||
}
|
}
|
||||||
i.img.DrawTriangles(srcImg, vertices, indices, colorm, mode, filter, address, s, us)
|
i.img.DrawTriangles(srcImg, vertices, indices, colorm, mode, filter, address, sourceRegion, s, us)
|
||||||
i.invalidatePendingPixels()
|
i.invalidatePendingPixels()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,13 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/internal/thread"
|
"github.com/hajimehoshi/ebiten/internal/thread"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Region struct {
|
||||||
|
X float32
|
||||||
|
Y float32
|
||||||
|
Width float32
|
||||||
|
Height float32
|
||||||
|
}
|
||||||
|
|
||||||
type Graphics interface {
|
type Graphics interface {
|
||||||
SetThread(thread *thread.Thread)
|
SetThread(thread *thread.Thread)
|
||||||
Begin()
|
Begin()
|
||||||
@ -31,7 +38,6 @@ type Graphics interface {
|
|||||||
NewImage(width, height int) (Image, error)
|
NewImage(width, height int) (Image, error)
|
||||||
NewScreenFramebufferImage(width, height int) (Image, error)
|
NewScreenFramebufferImage(width, height int) (Image, error)
|
||||||
Reset() error
|
Reset() error
|
||||||
Draw(dst, src ImageID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address) error
|
|
||||||
SetVsyncEnabled(enabled bool)
|
SetVsyncEnabled(enabled bool)
|
||||||
FramebufferYDirection() YDirection
|
FramebufferYDirection() YDirection
|
||||||
NeedsRestoring() bool
|
NeedsRestoring() bool
|
||||||
@ -41,6 +47,11 @@ type Graphics interface {
|
|||||||
|
|
||||||
NewShader(program *shaderir.Program) (Shader, error)
|
NewShader(program *shaderir.Program) (Shader, error)
|
||||||
|
|
||||||
|
// Draw draws an image onto another image.
|
||||||
|
//
|
||||||
|
// TODO: Merge this into DrawShader.
|
||||||
|
Draw(dst, src ImageID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address, sourceRegion Region) error
|
||||||
|
|
||||||
// DrawShader draws the shader.
|
// DrawShader draws the shader.
|
||||||
//
|
//
|
||||||
// uniforms represents a colletion of uniform variables. The values must be one of these types:
|
// uniforms represents a colletion of uniform variables. The values must be one of these types:
|
||||||
|
@ -65,7 +65,7 @@ type command interface {
|
|||||||
NumIndices() int
|
NumIndices() int
|
||||||
AddNumVertices(n int)
|
AddNumVertices(n int)
|
||||||
AddNumIndices(n int)
|
AddNumIndices(n int)
|
||||||
CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader) bool
|
CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type size struct {
|
type size struct {
|
||||||
@ -143,7 +143,7 @@ func (q *commandQueue) appendIndices(indices []uint16, offset uint16) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
|
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
|
||||||
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst, src *Image, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader, uniforms []interface{}) {
|
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst, src *Image, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||||
if len(indices) > graphics.IndicesNum {
|
if len(indices) > graphics.IndicesNum {
|
||||||
panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesNum but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesNum: %d", len(indices), graphics.IndicesNum))
|
panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesNum but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesNum: %d", len(indices), graphics.IndicesNum))
|
||||||
}
|
}
|
||||||
@ -173,24 +173,33 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst, src *Image, vertices []f
|
|||||||
|
|
||||||
// TODO: If dst is the screen, reorder the command to be the last.
|
// TODO: If dst is the screen, reorder the command to be the last.
|
||||||
if !split && 0 < len(q.commands) {
|
if !split && 0 < len(q.commands) {
|
||||||
if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, src, color, mode, filter, address, shader) {
|
if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, src, color, mode, filter, address, sourceRegion, shader) {
|
||||||
last.AddNumVertices(len(vertices))
|
last.AddNumVertices(len(vertices))
|
||||||
last.AddNumIndices(len(indices))
|
last.AddNumIndices(len(indices))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if address != driver.AddressUnsafe {
|
||||||
|
w, h := src.InternalSize()
|
||||||
|
sourceRegion.X /= float32(w)
|
||||||
|
sourceRegion.Y /= float32(h)
|
||||||
|
sourceRegion.Width /= float32(w)
|
||||||
|
sourceRegion.Height /= float32(h)
|
||||||
|
}
|
||||||
|
|
||||||
c := &drawTrianglesCommand{
|
c := &drawTrianglesCommand{
|
||||||
dst: dst,
|
dst: dst,
|
||||||
src: src,
|
src: src,
|
||||||
nvertices: len(vertices),
|
nvertices: len(vertices),
|
||||||
nindices: len(indices),
|
nindices: len(indices),
|
||||||
color: color,
|
color: color,
|
||||||
mode: mode,
|
mode: mode,
|
||||||
filter: filter,
|
filter: filter,
|
||||||
address: address,
|
address: address,
|
||||||
shader: shader,
|
sourceRegion: sourceRegion,
|
||||||
uniforms: uniforms,
|
shader: shader,
|
||||||
|
uniforms: uniforms,
|
||||||
}
|
}
|
||||||
q.commands = append(q.commands, c)
|
q.commands = append(q.commands, c)
|
||||||
}
|
}
|
||||||
@ -314,16 +323,17 @@ func FlushCommands() error {
|
|||||||
|
|
||||||
// drawTrianglesCommand represents a drawing command to draw an image on another image.
|
// drawTrianglesCommand represents a drawing command to draw an image on another image.
|
||||||
type drawTrianglesCommand struct {
|
type drawTrianglesCommand struct {
|
||||||
dst *Image
|
dst *Image
|
||||||
src *Image
|
src *Image
|
||||||
nvertices int
|
nvertices int
|
||||||
nindices int
|
nindices int
|
||||||
color *affine.ColorM
|
color *affine.ColorM
|
||||||
mode driver.CompositeMode
|
mode driver.CompositeMode
|
||||||
filter driver.Filter
|
filter driver.Filter
|
||||||
address driver.Address
|
address driver.Address
|
||||||
shader *Shader
|
sourceRegion driver.Region
|
||||||
uniforms []interface{}
|
shader *Shader
|
||||||
|
uniforms []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *drawTrianglesCommand) String() string {
|
func (c *drawTrianglesCommand) String() string {
|
||||||
@ -409,7 +419,6 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
|
|||||||
|
|
||||||
if c.shader != nil {
|
if c.shader != nil {
|
||||||
us := make([]interface{}, len(c.uniforms))
|
us := make([]interface{}, len(c.uniforms))
|
||||||
|
|
||||||
for i := 0; i < len(c.uniforms); i++ {
|
for i := 0; i < len(c.uniforms); i++ {
|
||||||
switch v := c.uniforms[i].(type) {
|
switch v := c.uniforms[i].(type) {
|
||||||
case *Image:
|
case *Image:
|
||||||
@ -427,7 +436,7 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
|
|||||||
|
|
||||||
return theGraphicsDriver.DrawShader(c.dst.image.ID(), c.shader.shader.ID(), c.nindices, indexOffset, c.mode, us)
|
return theGraphicsDriver.DrawShader(c.dst.image.ID(), c.shader.shader.ID(), c.nindices, indexOffset, c.mode, us)
|
||||||
}
|
}
|
||||||
return theGraphicsDriver.Draw(c.dst.image.ID(), c.src.image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address)
|
return theGraphicsDriver.Draw(c.dst.image.ID(), c.src.image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.sourceRegion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *drawTrianglesCommand) NumVertices() int {
|
func (c *drawTrianglesCommand) NumVertices() int {
|
||||||
@ -448,7 +457,7 @@ func (c *drawTrianglesCommand) AddNumIndices(n int) {
|
|||||||
|
|
||||||
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
|
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
|
||||||
// with the drawTrianglesCommand c.
|
// with the drawTrianglesCommand c.
|
||||||
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader) bool {
|
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
|
||||||
// If a shader is used, commands are not merged.
|
// If a shader is used, commands are not merged.
|
||||||
//
|
//
|
||||||
// TODO: Merge shader commands considering uniform variables.
|
// TODO: Merge shader commands considering uniform variables.
|
||||||
@ -473,6 +482,9 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image,
|
|||||||
if c.address != address {
|
if c.address != address {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if c.sourceRegion != sourceRegion {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,7 +518,7 @@ func (c *replacePixelsCommand) AddNumVertices(n int) {
|
|||||||
func (c *replacePixelsCommand) AddNumIndices(n int) {
|
func (c *replacePixelsCommand) AddNumIndices(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader) bool {
|
func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,7 +555,7 @@ func (c *pixelsCommand) AddNumVertices(n int) {
|
|||||||
func (c *pixelsCommand) AddNumIndices(n int) {
|
func (c *pixelsCommand) AddNumIndices(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader) bool {
|
func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,7 +588,7 @@ func (c *disposeImageCommand) AddNumVertices(n int) {
|
|||||||
func (c *disposeImageCommand) AddNumIndices(n int) {
|
func (c *disposeImageCommand) AddNumIndices(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader) bool {
|
func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,7 +621,7 @@ func (c *disposeShaderCommand) AddNumVertices(n int) {
|
|||||||
func (c *disposeShaderCommand) AddNumIndices(n int) {
|
func (c *disposeShaderCommand) AddNumIndices(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader) bool {
|
func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,7 +660,7 @@ func (c *newImageCommand) AddNumVertices(n int) {
|
|||||||
func (c *newImageCommand) AddNumIndices(n int) {
|
func (c *newImageCommand) AddNumIndices(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader) bool {
|
func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,7 +696,7 @@ func (c *newScreenFramebufferImageCommand) AddNumVertices(n int) {
|
|||||||
func (c *newScreenFramebufferImageCommand) AddNumIndices(n int) {
|
func (c *newScreenFramebufferImageCommand) AddNumIndices(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader) bool {
|
func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,7 +731,7 @@ func (c *newShaderCommand) AddNumVertices(n int) {
|
|||||||
func (c *newShaderCommand) AddNumIndices(n int) {
|
func (c *newShaderCommand) AddNumIndices(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader) bool {
|
func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ func processSrc(src *Image) {
|
|||||||
//
|
//
|
||||||
// If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the
|
// If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the
|
||||||
// elements for the source image are not used.
|
// elements for the source image are not used.
|
||||||
func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader, uniforms []interface{}) {
|
func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||||
if i.lastCommand == lastCommandNone {
|
if i.lastCommand == lastCommandNone {
|
||||||
if !i.screen && mode != driver.CompositeModeClear {
|
if !i.screen && mode != driver.CompositeModeClear {
|
||||||
panic("graphicscommand: the image must be cleared first")
|
panic("graphicscommand: the image must be cleared first")
|
||||||
@ -175,7 +175,7 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16,
|
|||||||
}
|
}
|
||||||
i.resolveBufferedReplacePixels()
|
i.resolveBufferedReplacePixels()
|
||||||
|
|
||||||
theCommandQueue.EnqueueDrawTrianglesCommand(i, src, vertices, indices, clr, mode, filter, address, shader, uniforms)
|
theCommandQueue.EnqueueDrawTrianglesCommand(i, src, vertices, indices, clr, mode, filter, address, sourceRegion, shader, uniforms)
|
||||||
|
|
||||||
if i.lastCommand == lastCommandNone && !i.screen {
|
if i.lastCommand == lastCommandNone && !i.screen {
|
||||||
i.lastCommand = lastCommandClear
|
i.lastCommand = lastCommandClear
|
||||||
|
@ -44,7 +44,7 @@ func TestClear(t *testing.T) {
|
|||||||
|
|
||||||
vs := quadVertices(w/2, h/2)
|
vs := quadVertices(w/2, h/2)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
|
|
||||||
pix, err := dst.Pixels()
|
pix, err := dst.Pixels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -74,8 +74,8 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
|
|||||||
dst := NewImage(w, h)
|
dst := NewImage(w, h)
|
||||||
vs := quadVertices(w/2, h/2)
|
vs := quadVertices(w/2, h/2)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(clr, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(clr, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
|
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,14 +89,14 @@ func TestShader(t *testing.T) {
|
|||||||
dst := NewImage(w, h)
|
dst := NewImage(w, h)
|
||||||
vs := quadVertices(w, h)
|
vs := quadVertices(w, h)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(clr, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(clr, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
|
|
||||||
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
|
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
|
||||||
s := NewShader(&ir)
|
s := NewShader(&ir)
|
||||||
us := []interface{}{
|
us := []interface{}{
|
||||||
[]float32{0, 0},
|
[]float32{0, 0},
|
||||||
}
|
}
|
||||||
dst.DrawTriangles(nil, vs, is, nil, driver.CompositeModeSourceOver, 0, 0, s, us)
|
dst.DrawTriangles(nil, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
|
||||||
|
|
||||||
pix, err := dst.Pixels()
|
pix, err := dst.Pixels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -67,7 +67,6 @@ struct VertexIn {
|
|||||||
struct VertexOut {
|
struct VertexOut {
|
||||||
float4 position [[position]];
|
float4 position [[position]];
|
||||||
float2 tex;
|
float2 tex;
|
||||||
float4 tex_region;
|
|
||||||
float4 color;
|
float4 color;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -87,7 +86,6 @@ vertex VertexOut VertexShader(
|
|||||||
VertexOut out = {
|
VertexOut out = {
|
||||||
.position = projectionMatrix * float4(in.position, 0, 1),
|
.position = projectionMatrix * float4(in.position, 0, 1),
|
||||||
.tex = in.tex,
|
.tex = in.tex,
|
||||||
.tex_region = in.tex_region,
|
|
||||||
.color = in.color,
|
.color = in.color,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -102,17 +100,17 @@ float FloorMod(float x, float y) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<uint8_t address>
|
template<uint8_t address>
|
||||||
float2 AdjustTexelByAddress(float2 p, float4 tex_region);
|
float2 AdjustTexelByAddress(float2 p, float4 source_region);
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline float2 AdjustTexelByAddress<ADDRESS_CLAMP_TO_ZERO>(float2 p, float4 tex_region) {
|
inline float2 AdjustTexelByAddress<ADDRESS_CLAMP_TO_ZERO>(float2 p, float4 source_region) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline float2 AdjustTexelByAddress<ADDRESS_REPEAT>(float2 p, float4 tex_region) {
|
inline float2 AdjustTexelByAddress<ADDRESS_REPEAT>(float2 p, float4 source_region) {
|
||||||
float2 o = float2(tex_region[0], tex_region[1]);
|
float2 o = float2(source_region[0], source_region[1]);
|
||||||
float2 size = float2(tex_region[2] - tex_region[0], tex_region[3] - tex_region[1]);
|
float2 size = float2(source_region[2] - source_region[0], source_region[3] - source_region[1]);
|
||||||
return float2(FloorMod((p.x - o.x), size.x) + o.x, FloorMod((p.y - o.y), size.y) + o.y);
|
return float2(FloorMod((p.x - o.x), size.x) + o.x, FloorMod((p.y - o.y), size.y) + o.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +119,7 @@ struct ColorFromTexel;
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct ColorFromTexel<FILTER_NEAREST, ADDRESS_UNSAFE> {
|
struct ColorFromTexel<FILTER_NEAREST, ADDRESS_UNSAFE> {
|
||||||
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale) {
|
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale, constant float4& source_region) {
|
||||||
float2 p = v.tex;
|
float2 p = v.tex;
|
||||||
constexpr sampler texture_sampler(filter::nearest);
|
constexpr sampler texture_sampler(filter::nearest);
|
||||||
return texture.sample(texture_sampler, p);
|
return texture.sample(texture_sampler, p);
|
||||||
@ -130,12 +128,12 @@ struct ColorFromTexel<FILTER_NEAREST, ADDRESS_UNSAFE> {
|
|||||||
|
|
||||||
template<uint8_t address>
|
template<uint8_t address>
|
||||||
struct ColorFromTexel<FILTER_NEAREST, address> {
|
struct ColorFromTexel<FILTER_NEAREST, address> {
|
||||||
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale) {
|
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale, constant float4& source_region) {
|
||||||
float2 p = AdjustTexelByAddress<address>(v.tex, v.tex_region);
|
float2 p = AdjustTexelByAddress<address>(v.tex, source_region);
|
||||||
if (v.tex_region[0] <= p.x &&
|
if (source_region[0] <= p.x &&
|
||||||
v.tex_region[1] <= p.y &&
|
source_region[1] <= p.y &&
|
||||||
p.x < v.tex_region[2] &&
|
p.x < source_region[2] &&
|
||||||
p.y < v.tex_region[3]) {
|
p.y < source_region[3]) {
|
||||||
constexpr sampler texture_sampler(filter::nearest);
|
constexpr sampler texture_sampler(filter::nearest);
|
||||||
return texture.sample(texture_sampler, p);
|
return texture.sample(texture_sampler, p);
|
||||||
}
|
}
|
||||||
@ -145,7 +143,7 @@ struct ColorFromTexel<FILTER_NEAREST, address> {
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct ColorFromTexel<FILTER_LINEAR, ADDRESS_UNSAFE> {
|
struct ColorFromTexel<FILTER_LINEAR, ADDRESS_UNSAFE> {
|
||||||
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale) {
|
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale, constant float4& source_region) {
|
||||||
constexpr sampler texture_sampler(filter::nearest);
|
constexpr sampler texture_sampler(filter::nearest);
|
||||||
const float2 texel_size = 1 / source_size;
|
const float2 texel_size = 1 / source_size;
|
||||||
|
|
||||||
@ -166,7 +164,7 @@ struct ColorFromTexel<FILTER_LINEAR, ADDRESS_UNSAFE> {
|
|||||||
|
|
||||||
template<uint8_t address>
|
template<uint8_t address>
|
||||||
struct ColorFromTexel<FILTER_LINEAR, address> {
|
struct ColorFromTexel<FILTER_LINEAR, address> {
|
||||||
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale) {
|
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale, constant float4& source_region) {
|
||||||
constexpr sampler texture_sampler(filter::nearest);
|
constexpr sampler texture_sampler(filter::nearest);
|
||||||
const float2 texel_size = 1 / source_size;
|
const float2 texel_size = 1 / source_size;
|
||||||
|
|
||||||
@ -174,27 +172,27 @@ struct ColorFromTexel<FILTER_LINEAR, address> {
|
|||||||
// As all the vertex positions are aligned to 1/16 [pixel], this shiting should work in most cases.
|
// As all the vertex positions are aligned to 1/16 [pixel], this shiting should work in most cases.
|
||||||
float2 p0 = v.tex - texel_size / 2.0 + (texel_size / 512.0);
|
float2 p0 = v.tex - texel_size / 2.0 + (texel_size / 512.0);
|
||||||
float2 p1 = v.tex + texel_size / 2.0 + (texel_size / 512.0);
|
float2 p1 = v.tex + texel_size / 2.0 + (texel_size / 512.0);
|
||||||
p0 = AdjustTexelByAddress<address>(p0, v.tex_region);
|
p0 = AdjustTexelByAddress<address>(p0, source_region);
|
||||||
p1 = AdjustTexelByAddress<address>(p1, v.tex_region);
|
p1 = AdjustTexelByAddress<address>(p1, source_region);
|
||||||
|
|
||||||
float4 c0 = texture.sample(texture_sampler, p0);
|
float4 c0 = texture.sample(texture_sampler, p0);
|
||||||
float4 c1 = texture.sample(texture_sampler, float2(p1.x, p0.y));
|
float4 c1 = texture.sample(texture_sampler, float2(p1.x, p0.y));
|
||||||
float4 c2 = texture.sample(texture_sampler, float2(p0.x, p1.y));
|
float4 c2 = texture.sample(texture_sampler, float2(p0.x, p1.y));
|
||||||
float4 c3 = texture.sample(texture_sampler, p1);
|
float4 c3 = texture.sample(texture_sampler, p1);
|
||||||
|
|
||||||
if (p0.x < v.tex_region[0]) {
|
if (p0.x < source_region[0]) {
|
||||||
c0 = 0;
|
c0 = 0;
|
||||||
c2 = 0;
|
c2 = 0;
|
||||||
}
|
}
|
||||||
if (p0.y < v.tex_region[1]) {
|
if (p0.y < source_region[1]) {
|
||||||
c0 = 0;
|
c0 = 0;
|
||||||
c1 = 0;
|
c1 = 0;
|
||||||
}
|
}
|
||||||
if (v.tex_region[2] <= p1.x) {
|
if (source_region[2] <= p1.x) {
|
||||||
c1 = 0;
|
c1 = 0;
|
||||||
c3 = 0;
|
c3 = 0;
|
||||||
}
|
}
|
||||||
if (v.tex_region[3] <= p1.y) {
|
if (source_region[3] <= p1.y) {
|
||||||
c2 = 0;
|
c2 = 0;
|
||||||
c3 = 0;
|
c3 = 0;
|
||||||
}
|
}
|
||||||
@ -206,7 +204,7 @@ struct ColorFromTexel<FILTER_LINEAR, address> {
|
|||||||
|
|
||||||
template<uint8_t address>
|
template<uint8_t address>
|
||||||
struct ColorFromTexel<FILTER_SCREEN, address> {
|
struct ColorFromTexel<FILTER_SCREEN, address> {
|
||||||
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale) {
|
inline float4 Do(VertexOut v, texture2d<float> texture, constant float2& source_size, float scale, constant float4& source_region) {
|
||||||
constexpr sampler texture_sampler(filter::nearest);
|
constexpr sampler texture_sampler(filter::nearest);
|
||||||
const float2 texel_size = 1 / source_size;
|
const float2 texel_size = 1 / source_size;
|
||||||
|
|
||||||
@ -232,8 +230,9 @@ struct FragmentShaderImpl {
|
|||||||
constant float2& source_size,
|
constant float2& source_size,
|
||||||
constant float4x4& color_matrix_body,
|
constant float4x4& color_matrix_body,
|
||||||
constant float4& color_matrix_translation,
|
constant float4& color_matrix_translation,
|
||||||
constant float& scale) {
|
constant float& scale,
|
||||||
float4 c = ColorFromTexel<filter, address>().Do(v, texture, source_size, scale);
|
constant float4& source_region) {
|
||||||
|
float4 c = ColorFromTexel<filter, address>().Do(v, texture, source_size, scale, source_region);
|
||||||
if (useColorM) {
|
if (useColorM) {
|
||||||
c.rgb /= c.a + (1.0 - sign(c.a));
|
c.rgb /= c.a + (1.0 - sign(c.a));
|
||||||
c = (color_matrix_body * c) + color_matrix_translation;
|
c = (color_matrix_body * c) + color_matrix_translation;
|
||||||
@ -256,8 +255,9 @@ struct FragmentShaderImpl<useColorM, FILTER_SCREEN, address> {
|
|||||||
constant float2& source_size,
|
constant float2& source_size,
|
||||||
constant float4x4& color_matrix_body,
|
constant float4x4& color_matrix_body,
|
||||||
constant float4& color_matrix_translation,
|
constant float4& color_matrix_translation,
|
||||||
constant float& scale) {
|
constant float& scale,
|
||||||
return ColorFromTexel<FILTER_SCREEN, address>().Do(v, texture, source_size, scale);
|
constant float4& source_region) {
|
||||||
|
return ColorFromTexel<FILTER_SCREEN, address>().Do(v, texture, source_size, scale, source_region);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -274,9 +274,10 @@ struct FragmentShaderImpl<useColorM, FILTER_SCREEN, address> {
|
|||||||
constant float2& source_size [[buffer(2)]], \
|
constant float2& source_size [[buffer(2)]], \
|
||||||
constant float4x4& color_matrix_body [[buffer(3)]], \
|
constant float4x4& color_matrix_body [[buffer(3)]], \
|
||||||
constant float4& color_matrix_translation [[buffer(4)]], \
|
constant float4& color_matrix_translation [[buffer(4)]], \
|
||||||
constant float& scale [[buffer(5)]]) { \
|
constant float& scale [[buffer(5)]], \
|
||||||
|
constant float4& source_region [[buffer(6)]]) { \
|
||||||
return FragmentShaderImpl<useColorM, filter, address>().Do( \
|
return FragmentShaderImpl<useColorM, filter, address>().Do( \
|
||||||
v, texture, source_size, color_matrix_body, color_matrix_translation, scale); \
|
v, texture, source_size, color_matrix_body, color_matrix_translation, scale, source_region); \
|
||||||
}
|
}
|
||||||
|
|
||||||
FragmentShaderFunc(0, FILTER_NEAREST, ADDRESS_CLAMP_TO_ZERO)
|
FragmentShaderFunc(0, FILTER_NEAREST, ADDRESS_CLAMP_TO_ZERO)
|
||||||
@ -635,7 +636,9 @@ func (g *Graphics) Reset() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error {
|
func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, sourceRegion driver.Region) error {
|
||||||
|
// TODO: Use sourceRegion.
|
||||||
|
|
||||||
dst := g.images[dstID]
|
dst := g.images[dstID]
|
||||||
src := g.images[srcID]
|
src := g.images[srcID]
|
||||||
|
|
||||||
@ -709,6 +712,14 @@ func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset i
|
|||||||
scale := float32(dst.width) / float32(src.width)
|
scale := float32(dst.width) / float32(src.width)
|
||||||
rce.SetFragmentBytes(unsafe.Pointer(&scale), unsafe.Sizeof(scale), 5)
|
rce.SetFragmentBytes(unsafe.Pointer(&scale), unsafe.Sizeof(scale), 5)
|
||||||
|
|
||||||
|
sr := [...]float32{
|
||||||
|
sourceRegion.X,
|
||||||
|
sourceRegion.Y,
|
||||||
|
sourceRegion.X + sourceRegion.Width,
|
||||||
|
sourceRegion.Y + sourceRegion.Height,
|
||||||
|
}
|
||||||
|
rce.SetFragmentBytes(unsafe.Pointer(&sr[0]), unsafe.Sizeof(sr), 6)
|
||||||
|
|
||||||
if src != nil {
|
if src != nil {
|
||||||
rce.SetFragmentTexture(src.texture, 0)
|
rce.SetFragmentTexture(src.texture, 0)
|
||||||
} else {
|
} else {
|
||||||
|
@ -114,12 +114,10 @@ attribute vec2 tex;
|
|||||||
attribute vec4 tex_region;
|
attribute vec4 tex_region;
|
||||||
attribute vec4 color_scale;
|
attribute vec4 color_scale;
|
||||||
varying vec2 varying_tex;
|
varying vec2 varying_tex;
|
||||||
varying vec4 varying_tex_region;
|
|
||||||
varying vec4 varying_color_scale;
|
varying vec4 varying_color_scale;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
varying_tex = tex;
|
varying_tex = tex;
|
||||||
varying_tex_region = tex_region;
|
|
||||||
varying_color_scale = color_scale;
|
varying_color_scale = color_scale;
|
||||||
|
|
||||||
mat4 projection_matrix = mat4(
|
mat4 projection_matrix = mat4(
|
||||||
@ -143,6 +141,7 @@ precision mediump float;
|
|||||||
{{.Definitions}}
|
{{.Definitions}}
|
||||||
|
|
||||||
uniform sampler2D texture;
|
uniform sampler2D texture;
|
||||||
|
uniform vec4 source_region;
|
||||||
|
|
||||||
#if defined(USE_COLOR_MATRIX)
|
#if defined(USE_COLOR_MATRIX)
|
||||||
uniform mat4 color_matrix_body;
|
uniform mat4 color_matrix_body;
|
||||||
@ -156,7 +155,6 @@ uniform highp float scale;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
varying highp vec2 varying_tex;
|
varying highp vec2 varying_tex;
|
||||||
varying highp vec4 varying_tex_region;
|
|
||||||
varying highp vec4 varying_color_scale;
|
varying highp vec4 varying_color_scale;
|
||||||
|
|
||||||
highp float floorMod(highp float x, highp float y) {
|
highp float floorMod(highp float x, highp float y) {
|
||||||
@ -190,11 +188,11 @@ void main(void) {
|
|||||||
# if defined(ADDRESS_UNSAFE)
|
# if defined(ADDRESS_UNSAFE)
|
||||||
color = texture2D(texture, pos);
|
color = texture2D(texture, pos);
|
||||||
# else
|
# else
|
||||||
pos = adjustTexelByAddress(pos, varying_tex_region);
|
pos = adjustTexelByAddress(pos, source_region);
|
||||||
if (varying_tex_region[0] <= pos.x &&
|
if (source_region[0] <= pos.x &&
|
||||||
varying_tex_region[1] <= pos.y &&
|
source_region[1] <= pos.y &&
|
||||||
pos.x < varying_tex_region[2] &&
|
pos.x < source_region[2] &&
|
||||||
pos.y < varying_tex_region[3]) {
|
pos.y < source_region[3]) {
|
||||||
color = texture2D(texture, pos);
|
color = texture2D(texture, pos);
|
||||||
} else {
|
} else {
|
||||||
color = vec4(0, 0, 0, 0);
|
color = vec4(0, 0, 0, 0);
|
||||||
@ -212,8 +210,8 @@ void main(void) {
|
|||||||
highp vec2 p1 = pos + (texel_size) / 2.0 + (texel_size / 512.0);
|
highp vec2 p1 = pos + (texel_size) / 2.0 + (texel_size / 512.0);
|
||||||
|
|
||||||
# if !defined(ADDRESS_UNSAFE)
|
# if !defined(ADDRESS_UNSAFE)
|
||||||
p0 = adjustTexelByAddress(p0, varying_tex_region);
|
p0 = adjustTexelByAddress(p0, source_region);
|
||||||
p1 = adjustTexelByAddress(p1, varying_tex_region);
|
p1 = adjustTexelByAddress(p1, source_region);
|
||||||
# endif // defined(ADDRESS_UNSAFE)
|
# endif // defined(ADDRESS_UNSAFE)
|
||||||
|
|
||||||
vec4 c0 = texture2D(texture, p0);
|
vec4 c0 = texture2D(texture, p0);
|
||||||
@ -221,19 +219,19 @@ void main(void) {
|
|||||||
vec4 c2 = texture2D(texture, vec2(p0.x, p1.y));
|
vec4 c2 = texture2D(texture, vec2(p0.x, p1.y));
|
||||||
vec4 c3 = texture2D(texture, p1);
|
vec4 c3 = texture2D(texture, p1);
|
||||||
# if !defined(ADDRESS_UNSAFE)
|
# if !defined(ADDRESS_UNSAFE)
|
||||||
if (p0.x < varying_tex_region[0]) {
|
if (p0.x < source_region[0]) {
|
||||||
c0 = vec4(0, 0, 0, 0);
|
c0 = vec4(0, 0, 0, 0);
|
||||||
c2 = vec4(0, 0, 0, 0);
|
c2 = vec4(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
if (p0.y < varying_tex_region[1]) {
|
if (p0.y < source_region[1]) {
|
||||||
c0 = vec4(0, 0, 0, 0);
|
c0 = vec4(0, 0, 0, 0);
|
||||||
c1 = vec4(0, 0, 0, 0);
|
c1 = vec4(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
if (varying_tex_region[2] <= p1.x) {
|
if (source_region[2] <= p1.x) {
|
||||||
c1 = vec4(0, 0, 0, 0);
|
c1 = vec4(0, 0, 0, 0);
|
||||||
c3 = vec4(0, 0, 0, 0);
|
c3 = vec4(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
if (varying_tex_region[3] <= p1.y) {
|
if (source_region[3] <= p1.y) {
|
||||||
c2 = vec4(0, 0, 0, 0);
|
c2 = vec4(0, 0, 0, 0);
|
||||||
c3 = vec4(0, 0, 0, 0);
|
c3 = vec4(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,9 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
|
|||||||
g.context.elementArrayBufferSubData(indices)
|
g.context.elementArrayBufferSubData(indices)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error {
|
func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, sourceRegion driver.Region) error {
|
||||||
|
// TODO: Use sourceRegion.
|
||||||
|
|
||||||
destination := g.images[dst]
|
destination := g.images[dst]
|
||||||
source := g.images[src]
|
source := g.images[src]
|
||||||
|
|
||||||
@ -173,6 +175,14 @@ func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int,
|
|||||||
uniforms = append(uniforms, uniformVariable{
|
uniforms = append(uniforms, uniformVariable{
|
||||||
name: "viewport_size",
|
name: "viewport_size",
|
||||||
value: []float32{float32(vw), float32(vh)},
|
value: []float32{float32(vw), float32(vh)},
|
||||||
|
}, uniformVariable{
|
||||||
|
name: "source_region",
|
||||||
|
value: []float32{
|
||||||
|
sourceRegion.X,
|
||||||
|
sourceRegion.Y,
|
||||||
|
sourceRegion.X + sourceRegion.Width,
|
||||||
|
sourceRegion.Y + sourceRegion.Height,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if colorM != nil {
|
if colorM != nil {
|
||||||
|
@ -151,7 +151,7 @@ func (m *Mipmap) DrawImage(src *Mipmap, bounds image.Rectangle, geom GeoM, color
|
|||||||
if level == 0 {
|
if level == 0 {
|
||||||
vs := quadVertices(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, a, b, c, d, tx, ty, cr, cg, cb, ca, screen)
|
vs := quadVertices(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, a, b, c, d, tx, ty, cr, cg, cb, ca, screen)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, driver.AddressUnsafe, nil, nil)
|
m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
} else if buf := src.level(bounds, level); buf != nil {
|
} else if buf := src.level(bounds, level); buf != nil {
|
||||||
w, h := sizeForLevel(bounds.Dx(), bounds.Dy(), level)
|
w, h := sizeForLevel(bounds.Dx(), bounds.Dy(), level)
|
||||||
s := pow2(level)
|
s := pow2(level)
|
||||||
@ -161,12 +161,12 @@ func (m *Mipmap) DrawImage(src *Mipmap, bounds image.Rectangle, geom GeoM, color
|
|||||||
d *= s
|
d *= s
|
||||||
vs := quadVertices(0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca, false)
|
vs := quadVertices(0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca, false)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
m.orig.DrawTriangles(buf, vs, is, colorm, mode, filter, driver.AddressUnsafe, nil, nil)
|
m.orig.DrawTriangles(buf, vs, is, colorm, mode, filter, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
}
|
}
|
||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader, uniforms []interface{}) {
|
func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||||
// TODO: Use a mipmap? (#909)
|
// TODO: Use a mipmap? (#909)
|
||||||
|
|
||||||
if colorm != nil && colorm.ScaleOnly() {
|
if colorm != nil && colorm.ScaleOnly() {
|
||||||
@ -205,7 +205,7 @@ func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16
|
|||||||
srcOrig = src.orig
|
srcOrig = src.orig
|
||||||
}
|
}
|
||||||
|
|
||||||
m.orig.DrawTriangles(srcOrig, vertices, indices, colorm, mode, filter, address, s, us)
|
m.orig.DrawTriangles(srcOrig, vertices, indices, colorm, mode, filter, address, sourceRegion, s, us)
|
||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +268,7 @@ func (m *Mipmap) level(r image.Rectangle, level int) *shareable.Image {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s := shareable.NewImage(w2, h2, m.volatile)
|
s := shareable.NewImage(w2, h2, m.volatile)
|
||||||
s.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, nil, nil)
|
s.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
imgs[level] = s
|
imgs[level] = s
|
||||||
|
|
||||||
return imgs[level]
|
return imgs[level]
|
||||||
|
@ -69,15 +69,16 @@ func (p *Pixels) At(i, j int) (byte, byte, byte, byte) {
|
|||||||
|
|
||||||
// drawTrianglesHistoryItem is an item for history of draw-image commands.
|
// drawTrianglesHistoryItem is an item for history of draw-image commands.
|
||||||
type drawTrianglesHistoryItem struct {
|
type drawTrianglesHistoryItem struct {
|
||||||
image *Image
|
image *Image
|
||||||
vertices []float32
|
vertices []float32
|
||||||
indices []uint16
|
indices []uint16
|
||||||
colorm *affine.ColorM
|
colorm *affine.ColorM
|
||||||
mode driver.CompositeMode
|
mode driver.CompositeMode
|
||||||
filter driver.Filter
|
filter driver.Filter
|
||||||
address driver.Address
|
address driver.Address
|
||||||
shader *Shader
|
sourceRegion driver.Region
|
||||||
uniforms []interface{}
|
shader *Shader
|
||||||
|
uniforms []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image represents an image that can be restored when GL context is lost.
|
// Image represents an image that can be restored when GL context is lost.
|
||||||
@ -258,8 +259,7 @@ func fillImage(i *graphicscommand.Image, clr color.RGBA) {
|
|||||||
// Add 1 pixels for paddings.
|
// Add 1 pixels for paddings.
|
||||||
vs := quadVertices(0, 0, float32(dw), float32(dh), 1, 1, float32(sw-1), float32(sh-1), rf, gf, bf, af)
|
vs := quadVertices(0, 0, float32(dw), float32(dh), 1, 1, float32(sw-1), float32(sh-1), rf, gf, bf, af)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
|
i.DrawTriangles(emptyImage.image, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
i.DrawTriangles(emptyImage.image, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasePixelsForTesting returns the image's basePixels for testing.
|
// BasePixelsForTesting returns the image's basePixels for testing.
|
||||||
@ -369,7 +369,7 @@ func convertUniformVariables(uniforms []interface{}) []interface{} {
|
|||||||
// 9: Color G
|
// 9: Color G
|
||||||
// 10: Color B
|
// 10: Color B
|
||||||
// 11: Color Y
|
// 11: Color Y
|
||||||
func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader, uniforms []interface{}) {
|
func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||||
if i.priority {
|
if i.priority {
|
||||||
panic("restorable: DrawTriangles cannot be called on a priority image")
|
panic("restorable: DrawTriangles cannot be called on a priority image")
|
||||||
}
|
}
|
||||||
@ -397,7 +397,7 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16,
|
|||||||
if srcstale || i.screen || !needsRestoring() || i.volatile {
|
if srcstale || i.screen || !needsRestoring() || i.volatile {
|
||||||
i.makeStale()
|
i.makeStale()
|
||||||
} else {
|
} else {
|
||||||
i.appendDrawTrianglesHistory(img, vertices, indices, colorm, mode, filter, address, shader, uniforms)
|
i.appendDrawTrianglesHistory(img, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms)
|
||||||
}
|
}
|
||||||
var s *graphicscommand.Shader
|
var s *graphicscommand.Shader
|
||||||
if shader != nil {
|
if shader != nil {
|
||||||
@ -407,11 +407,11 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16,
|
|||||||
if img != nil {
|
if img != nil {
|
||||||
gimg = img.image
|
gimg = img.image
|
||||||
}
|
}
|
||||||
i.image.DrawTriangles(gimg, vertices, indices, colorm, mode, filter, address, s, convertUniformVariables(uniforms))
|
i.image.DrawTriangles(gimg, vertices, indices, colorm, mode, filter, address, sourceRegion, s, convertUniformVariables(uniforms))
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendDrawTrianglesHistory appends a draw-image history item to the image.
|
// appendDrawTrianglesHistory appends a draw-image history item to the image.
|
||||||
func (i *Image) appendDrawTrianglesHistory(image *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader, uniforms []interface{}) {
|
func (i *Image) appendDrawTrianglesHistory(image *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||||
if i.stale || i.volatile || i.screen {
|
if i.stale || i.volatile || i.screen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -430,15 +430,16 @@ func (i *Image) appendDrawTrianglesHistory(image *Image, vertices []float32, ind
|
|||||||
copy(is, indices)
|
copy(is, indices)
|
||||||
|
|
||||||
item := &drawTrianglesHistoryItem{
|
item := &drawTrianglesHistoryItem{
|
||||||
image: image,
|
image: image,
|
||||||
vertices: vs,
|
vertices: vs,
|
||||||
indices: is,
|
indices: is,
|
||||||
colorm: colorm,
|
colorm: colorm,
|
||||||
mode: mode,
|
mode: mode,
|
||||||
filter: filter,
|
filter: filter,
|
||||||
address: address,
|
address: address,
|
||||||
shader: shader,
|
sourceRegion: sourceRegion,
|
||||||
uniforms: uniforms,
|
shader: shader,
|
||||||
|
uniforms: uniforms,
|
||||||
}
|
}
|
||||||
i.drawTrianglesHistory = append(i.drawTrianglesHistory, item)
|
i.drawTrianglesHistory = append(i.drawTrianglesHistory, item)
|
||||||
}
|
}
|
||||||
@ -618,7 +619,7 @@ func (i *Image) restore() error {
|
|||||||
if c.shader != nil {
|
if c.shader != nil {
|
||||||
s = c.shader.shader
|
s = c.shader.shader
|
||||||
}
|
}
|
||||||
gimg.DrawTriangles(img, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, s, convertUniformVariables(c.uniforms))
|
gimg.DrawTriangles(img, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, c.sourceRegion, s, convertUniformVariables(c.uniforms))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(i.drawTrianglesHistory) > 0 {
|
if len(i.drawTrianglesHistory) > 0 {
|
||||||
|
@ -131,7 +131,7 @@ func TestRestoreChain(t *testing.T) {
|
|||||||
for i := 0; i < num-1; i++ {
|
for i := 0; i < num-1; i++ {
|
||||||
vs := quadVertices(1, 1, 0, 0)
|
vs := quadVertices(1, 1, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
imgs[i+1].DrawTriangles(imgs[i], vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
imgs[i+1].DrawTriangles(imgs[i], vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
}
|
}
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -173,10 +173,10 @@ func TestRestoreChain2(t *testing.T) {
|
|||||||
imgs[8].ReplacePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, 0, 0, w, h)
|
imgs[8].ReplacePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, 0, 0, w, h)
|
||||||
|
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
imgs[8].DrawTriangles(imgs[7], quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
imgs[8].DrawTriangles(imgs[7], quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
imgs[9].DrawTriangles(imgs[8], quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
imgs[9].DrawTriangles(imgs[8], quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
for i := 0; i < 7; i++ {
|
for i := 0; i < 7; i++ {
|
||||||
imgs[i+1].DrawTriangles(imgs[i], quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
imgs[i+1].DrawTriangles(imgs[i], quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
@ -216,10 +216,10 @@ func TestRestoreOverrideSource(t *testing.T) {
|
|||||||
clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff}
|
clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff}
|
||||||
img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h)
|
img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
img2.DrawTriangles(img1, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img2.DrawTriangles(img1, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
img3.DrawTriangles(img2, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img3.DrawTriangles(img2, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
|
img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
|
||||||
img1.DrawTriangles(img0, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img1.DrawTriangles(img0, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -298,23 +298,23 @@ func TestRestoreComplexGraph(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
vs := quadVertices(w, h, 0, 0)
|
vs := quadVertices(w, h, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
img3.DrawTriangles(img0, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img3.DrawTriangles(img0, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
vs = quadVertices(w, h, 1, 0)
|
vs = quadVertices(w, h, 1, 0)
|
||||||
img3.DrawTriangles(img1, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img3.DrawTriangles(img1, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
vs = quadVertices(w, h, 1, 0)
|
vs = quadVertices(w, h, 1, 0)
|
||||||
img4.DrawTriangles(img1, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img4.DrawTriangles(img1, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
vs = quadVertices(w, h, 2, 0)
|
vs = quadVertices(w, h, 2, 0)
|
||||||
img4.DrawTriangles(img2, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img4.DrawTriangles(img2, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
vs = quadVertices(w, h, 0, 0)
|
vs = quadVertices(w, h, 0, 0)
|
||||||
img5.DrawTriangles(img3, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img5.DrawTriangles(img3, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
vs = quadVertices(w, h, 0, 0)
|
vs = quadVertices(w, h, 0, 0)
|
||||||
img6.DrawTriangles(img3, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img6.DrawTriangles(img3, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
vs = quadVertices(w, h, 1, 0)
|
vs = quadVertices(w, h, 1, 0)
|
||||||
img6.DrawTriangles(img4, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img6.DrawTriangles(img4, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
vs = quadVertices(w, h, 0, 0)
|
vs = quadVertices(w, h, 0, 0)
|
||||||
img7.DrawTriangles(img2, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img7.DrawTriangles(img2, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
vs = quadVertices(w, h, 2, 0)
|
vs = quadVertices(w, h, 2, 0)
|
||||||
img7.DrawTriangles(img3, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img7.DrawTriangles(img3, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -406,8 +406,8 @@ func TestRestoreRecursive(t *testing.T) {
|
|||||||
img0.Dispose()
|
img0.Dispose()
|
||||||
}()
|
}()
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
img1.DrawTriangles(img0, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img1.DrawTriangles(img0, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
img0.DrawTriangles(img1, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img0.DrawTriangles(img1, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -501,7 +501,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
|
|||||||
|
|
||||||
vs := quadVertices(1, 1, 0, 0)
|
vs := quadVertices(1, 1, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
img1.DrawTriangles(img0, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img1.DrawTriangles(img0, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
|
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
|
||||||
|
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
@ -538,8 +538,8 @@ func TestDispose(t *testing.T) {
|
|||||||
defer img2.Dispose()
|
defer img2.Dispose()
|
||||||
|
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
img1.DrawTriangles(img2, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img1.DrawTriangles(img2, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
img0.DrawTriangles(img1, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img0.DrawTriangles(img1, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
img1.Dispose()
|
img1.Dispose()
|
||||||
|
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
@ -647,7 +647,7 @@ func TestReplacePixelsOnly(t *testing.T) {
|
|||||||
|
|
||||||
vs := quadVertices(1, 1, 0, 0)
|
vs := quadVertices(1, 1, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
img1.DrawTriangles(img0, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img1.DrawTriangles(img0, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
|
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
|
||||||
|
|
||||||
// BasePixelsForTesting is available without GPU accessing.
|
// BasePixelsForTesting is available without GPU accessing.
|
||||||
@ -700,7 +700,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
|
|||||||
src.ReplacePixels(pix, 0, 0, w, h)
|
src.ReplacePixels(pix, 0, 0, w, h)
|
||||||
vs := quadVertices(1, 1, 0, 0)
|
vs := quadVertices(1, 1, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
|
|
||||||
// Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
|
// Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
|
||||||
// stale.
|
// stale.
|
||||||
@ -721,7 +721,7 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
|
|||||||
|
|
||||||
vs := quadVertices(w, h, 0, 0)
|
vs := quadVertices(w, h, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
|
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
|
||||||
// ReplacePixels for a whole image doesn't panic.
|
// ReplacePixels for a whole image doesn't panic.
|
||||||
}
|
}
|
||||||
@ -739,7 +739,7 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) {
|
|||||||
|
|
||||||
vs := quadVertices(w, h, 0, 0)
|
vs := quadVertices(w, h, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
|
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -828,7 +828,7 @@ func TestFill2(t *testing.T) {
|
|||||||
dst := NewImage(w, h, false)
|
dst := NewImage(w, h, false)
|
||||||
vs := quadVertices(w, h, 0, 0)
|
vs := quadVertices(w, h, 0, 0)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
|
|
||||||
// Fill src with a different color. This should not affect dst.
|
// Fill src with a different color. This should not affect dst.
|
||||||
src.Fill(color.RGBA{0, 0xff, 0, 0xff})
|
src.Fill(color.RGBA{0, 0xff, 0, 0xff})
|
||||||
@ -867,7 +867,7 @@ func TestMutateSlices(t *testing.T) {
|
|||||||
vs := quadVertices(w, h, 0, 0)
|
vs := quadVertices(w, h, 0, 0)
|
||||||
is := make([]uint16, len(graphics.QuadIndices()))
|
is := make([]uint16, len(graphics.QuadIndices()))
|
||||||
copy(is, graphics.QuadIndices())
|
copy(is, graphics.QuadIndices())
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
for i := range vs {
|
for i := range vs {
|
||||||
vs[i] = 0
|
vs[i] = 0
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ func TestShader(t *testing.T) {
|
|||||||
us := []interface{}{
|
us := []interface{}{
|
||||||
[]float32{0, 0},
|
[]float32{0, 0},
|
||||||
}
|
}
|
||||||
img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, s, us)
|
img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
|
||||||
|
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -76,7 +76,7 @@ func TestShaderChain(t *testing.T) {
|
|||||||
[]float32{0, 0},
|
[]float32{0, 0},
|
||||||
imgs[i],
|
imgs[i],
|
||||||
}
|
}
|
||||||
imgs[i+1].DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, s, us)
|
imgs[i+1].DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
@ -118,7 +118,7 @@ func TestShaderMultipleSources(t *testing.T) {
|
|||||||
srcs[1],
|
srcs[1],
|
||||||
srcs[2],
|
srcs[2],
|
||||||
}
|
}
|
||||||
dst.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, s, us)
|
dst.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
|
||||||
|
|
||||||
// Clear one of the sources after DrawTriangles. dst should not be affected.
|
// Clear one of the sources after DrawTriangles. dst should not be affected.
|
||||||
srcs[0].Fill(color.RGBA{})
|
srcs[0].Fill(color.RGBA{})
|
||||||
@ -150,7 +150,7 @@ func TestShaderDispose(t *testing.T) {
|
|||||||
us := []interface{}{
|
us := []interface{}{
|
||||||
[]float32{0, 0},
|
[]float32{0, 0},
|
||||||
}
|
}
|
||||||
img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, s, us)
|
img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
|
||||||
|
|
||||||
// Dispose the shader. This should invalidates all the images using this shader i.e., all the images become
|
// Dispose the shader. This should invalidates all the images using this shader i.e., all the images become
|
||||||
// stale.
|
// stale.
|
||||||
|
@ -220,7 +220,7 @@ func (i *Image) ensureNotShared() {
|
|||||||
dx1, dy1, sx1, sy1, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
|
dx1, dy1, sx1, sy1, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
|
||||||
}
|
}
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
newImg.DrawTriangles(i.backend.restorable, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
newImg.DrawTriangles(i.backend.restorable, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
|
|
||||||
i.dispose(false)
|
i.dispose(false)
|
||||||
i.backend = &backend{
|
i.backend = &backend{
|
||||||
@ -309,7 +309,7 @@ func makeSharedIfNeeded(src *Image) {
|
|||||||
// 9: Color G
|
// 9: Color G
|
||||||
// 10: Color B
|
// 10: Color B
|
||||||
// 11: Color Y
|
// 11: Color Y
|
||||||
func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, shader *Shader, uniforms []interface{}) {
|
func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
// Do not use defer for performance.
|
// Do not use defer for performance.
|
||||||
|
|
||||||
@ -355,6 +355,10 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16,
|
|||||||
vertices[i*graphics.VertexFloatNum+6] += oxf
|
vertices[i*graphics.VertexFloatNum+6] += oxf
|
||||||
vertices[i*graphics.VertexFloatNum+7] += oyf
|
vertices[i*graphics.VertexFloatNum+7] += oyf
|
||||||
}
|
}
|
||||||
|
if address != driver.AddressUnsafe {
|
||||||
|
sourceRegion.X += oxf
|
||||||
|
sourceRegion.Y += oyf
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var s *restorable.Shader
|
var s *restorable.Shader
|
||||||
@ -376,7 +380,7 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16,
|
|||||||
if img != nil {
|
if img != nil {
|
||||||
r = img.backend.restorable
|
r = img.backend.restorable
|
||||||
}
|
}
|
||||||
i.backend.restorable.DrawTriangles(r, vertices, indices, colorm, mode, filter, address, s, us)
|
i.backend.restorable.DrawTriangles(r, vertices, indices, colorm, mode, filter, address, sourceRegion, s, us)
|
||||||
|
|
||||||
i.nonUpdatedCount = 0
|
i.nonUpdatedCount = 0
|
||||||
delete(imagesToMakeShared, i)
|
delete(imagesToMakeShared, i)
|
||||||
|
@ -96,7 +96,7 @@ func TestEnsureNotShared(t *testing.T) {
|
|||||||
// img4.ensureNotShared() should be called.
|
// img4.ensureNotShared() should be called.
|
||||||
vs := quadVertices(size/2, size/2, size/4, size/4, 1)
|
vs := quadVertices(size/2, size/2, size/4, size/4, 1)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
img4.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img4.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
want := false
|
want := false
|
||||||
if got := img4.IsSharedForTesting(); got != want {
|
if got := img4.IsSharedForTesting(); got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
@ -126,7 +126,7 @@ func TestEnsureNotShared(t *testing.T) {
|
|||||||
|
|
||||||
// Check further drawing doesn't cause panic.
|
// Check further drawing doesn't cause panic.
|
||||||
// This bug was fixed by 03dcd948.
|
// This bug was fixed by 03dcd948.
|
||||||
img4.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img4.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReshared(t *testing.T) {
|
func TestReshared(t *testing.T) {
|
||||||
@ -166,7 +166,7 @@ func TestReshared(t *testing.T) {
|
|||||||
// Use img1 as a render target.
|
// Use img1 as a render target.
|
||||||
vs := quadVertices(size, size, 0, 0, 1)
|
vs := quadVertices(size, size, 0, 0, 1)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
img1.DrawTriangles(img2, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img1.DrawTriangles(img2, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
if got, want := img1.IsSharedForTesting(), false; got != want {
|
if got, want := img1.IsSharedForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ func TestReshared(t *testing.T) {
|
|||||||
if err := MakeImagesSharedForTesting(); err != nil {
|
if err := MakeImagesSharedForTesting(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
img0.DrawTriangles(img1, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img0.DrawTriangles(img1, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
if got, want := img1.IsSharedForTesting(), false; got != want {
|
if got, want := img1.IsSharedForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ func TestReshared(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img0.DrawTriangles(img1, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img0.DrawTriangles(img1, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
if got, want := img1.IsSharedForTesting(), true; got != want {
|
if got, want := img1.IsSharedForTesting(), true; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -231,7 +231,7 @@ func TestReshared(t *testing.T) {
|
|||||||
if err := MakeImagesSharedForTesting(); err != nil {
|
if err := MakeImagesSharedForTesting(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
img0.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
img0.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
if got, want := img3.IsSharedForTesting(), false; got != want {
|
if got, want := img3.IsSharedForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -326,7 +326,7 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
|||||||
|
|
||||||
vs := quadVertices(w, h, 0, 0, 1)
|
vs := quadVertices(w, h, 0, 0, 1)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
dst.ReplacePixels(pix)
|
dst.ReplacePixels(pix)
|
||||||
|
|
||||||
pix, err := dst.Pixels(0, 0, w, h)
|
pix, err := dst.Pixels(0, 0, w, h)
|
||||||
@ -368,7 +368,7 @@ func TestSmallImages(t *testing.T) {
|
|||||||
|
|
||||||
vs := quadVertices(w, h, 0, 0, 1)
|
vs := quadVertices(w, h, 0, 0, 1)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
|
|
||||||
pix, err := dst.Pixels(0, 0, w, h)
|
pix, err := dst.Pixels(0, 0, w, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -410,7 +410,7 @@ func TestLongImages(t *testing.T) {
|
|||||||
const scale = 120
|
const scale = 120
|
||||||
vs := quadVertices(w, h, 0, 0, scale)
|
vs := quadVertices(w, h, 0, 0, scale)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, nil, nil)
|
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
|
||||||
|
|
||||||
pix, err := dst.Pixels(0, 0, dstW, dstH)
|
pix, err := dst.Pixels(0, 0, dstW, dstH)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user