diff --git a/image.go b/image.go index 3843b99e9..046026e12 100644 --- a/image.go +++ b/image.go @@ -333,7 +333,14 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o is := make([]uint16, len(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 { @@ -402,7 +409,7 @@ func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, sha is := make([]uint16, len(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. diff --git a/internal/buffered/image.go b/internal/buffered/image.go index c935fe1ea..83dcdffa1 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -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. // // 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 if src != nil { srcs = append(srcs, src) @@ -286,7 +286,7 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, if maybeCanAddDelayedCommand() { if tryAddDelayedCommand(func() error { // 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 @@ -316,7 +316,7 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, if src != nil { 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() } diff --git a/internal/driver/graphics.go b/internal/driver/graphics.go index 21dd52e0c..2b1b45397 100644 --- a/internal/driver/graphics.go +++ b/internal/driver/graphics.go @@ -22,6 +22,13 @@ import ( "github.com/hajimehoshi/ebiten/internal/thread" ) +type Region struct { + X float32 + Y float32 + Width float32 + Height float32 +} + type Graphics interface { SetThread(thread *thread.Thread) Begin() @@ -31,7 +38,6 @@ type Graphics interface { NewImage(width, height int) (Image, error) NewScreenFramebufferImage(width, height int) (Image, error) Reset() error - Draw(dst, src ImageID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address) error SetVsyncEnabled(enabled bool) FramebufferYDirection() YDirection NeedsRestoring() bool @@ -41,6 +47,11 @@ type Graphics interface { 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. // // uniforms represents a colletion of uniform variables. The values must be one of these types: diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index cf3b49f75..144b2660c 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -65,7 +65,7 @@ type command interface { NumIndices() int AddNumVertices(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 { @@ -143,7 +143,7 @@ func (q *commandQueue) appendIndices(indices []uint16, offset uint16) { } // 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 { 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. 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.AddNumIndices(len(indices)) 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{ - dst: dst, - src: src, - nvertices: len(vertices), - nindices: len(indices), - color: color, - mode: mode, - filter: filter, - address: address, - shader: shader, - uniforms: uniforms, + dst: dst, + src: src, + nvertices: len(vertices), + nindices: len(indices), + color: color, + mode: mode, + filter: filter, + address: address, + sourceRegion: sourceRegion, + shader: shader, + uniforms: uniforms, } 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. type drawTrianglesCommand struct { - dst *Image - src *Image - nvertices int - nindices int - color *affine.ColorM - mode driver.CompositeMode - filter driver.Filter - address driver.Address - shader *Shader - uniforms []interface{} + dst *Image + src *Image + nvertices int + nindices int + color *affine.ColorM + mode driver.CompositeMode + filter driver.Filter + address driver.Address + sourceRegion driver.Region + shader *Shader + uniforms []interface{} } func (c *drawTrianglesCommand) String() string { @@ -409,7 +419,6 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error { if c.shader != nil { us := make([]interface{}, len(c.uniforms)) - for i := 0; i < len(c.uniforms); i++ { switch v := c.uniforms[i].(type) { 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.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 { @@ -448,7 +457,7 @@ func (c *drawTrianglesCommand) AddNumIndices(n int) { // CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged // 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. // // TODO: Merge shader commands considering uniform variables. @@ -473,6 +482,9 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, if c.address != address { return false } + if c.sourceRegion != sourceRegion { + return false + } return true } @@ -506,7 +518,7 @@ func (c *replacePixelsCommand) AddNumVertices(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 } @@ -543,7 +555,7 @@ func (c *pixelsCommand) AddNumVertices(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 } @@ -576,7 +588,7 @@ func (c *disposeImageCommand) AddNumVertices(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 } @@ -609,7 +621,7 @@ func (c *disposeShaderCommand) AddNumVertices(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 } @@ -648,7 +660,7 @@ func (c *newImageCommand) AddNumVertices(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 } @@ -684,7 +696,7 @@ func (c *newScreenFramebufferImageCommand) AddNumVertices(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 } @@ -719,7 +731,7 @@ func (c *newShaderCommand) AddNumVertices(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 } diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 37e67f5f7..c9cb3b11d 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -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 // 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.screen && mode != driver.CompositeModeClear { panic("graphicscommand: the image must be cleared first") @@ -175,7 +175,7 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, } 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 { i.lastCommand = lastCommandClear diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index 93f69a8d0..c96ac07e6 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -44,7 +44,7 @@ func TestClear(t *testing.T) { vs := quadVertices(w/2, h/2) 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() if err != nil { @@ -74,8 +74,8 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) { dst := NewImage(w, h) vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() - dst.DrawTriangles(clr, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, nil, nil) - dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, 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, driver.Region{}, nil, nil) dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1) } @@ -89,14 +89,14 @@ func TestShader(t *testing.T) { dst := NewImage(w, h) vs := quadVertices(w, h) 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) s := NewShader(&ir) us := []interface{}{ []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() if err != nil { diff --git a/internal/graphicsdriver/metal/graphics.go b/internal/graphicsdriver/metal/graphics.go index bbf921360..76c2faff6 100644 --- a/internal/graphicsdriver/metal/graphics.go +++ b/internal/graphicsdriver/metal/graphics.go @@ -67,7 +67,6 @@ struct VertexIn { struct VertexOut { float4 position [[position]]; float2 tex; - float4 tex_region; float4 color; }; @@ -87,7 +86,6 @@ vertex VertexOut VertexShader( VertexOut out = { .position = projectionMatrix * float4(in.position, 0, 1), .tex = in.tex, - .tex_region = in.tex_region, .color = in.color, }; @@ -102,17 +100,17 @@ float FloorMod(float x, float y) { } template -float2 AdjustTexelByAddress(float2 p, float4 tex_region); +float2 AdjustTexelByAddress(float2 p, float4 source_region); template<> -inline float2 AdjustTexelByAddress(float2 p, float4 tex_region) { +inline float2 AdjustTexelByAddress(float2 p, float4 source_region) { return p; } template<> -inline float2 AdjustTexelByAddress(float2 p, float4 tex_region) { - float2 o = float2(tex_region[0], tex_region[1]); - float2 size = float2(tex_region[2] - tex_region[0], tex_region[3] - tex_region[1]); +inline float2 AdjustTexelByAddress(float2 p, float4 source_region) { + float2 o = float2(source_region[0], source_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); } @@ -121,7 +119,7 @@ struct ColorFromTexel; template<> struct ColorFromTexel { - inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale) { + inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale, constant float4& source_region) { float2 p = v.tex; constexpr sampler texture_sampler(filter::nearest); return texture.sample(texture_sampler, p); @@ -130,12 +128,12 @@ struct ColorFromTexel { template struct ColorFromTexel { - inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale) { - float2 p = AdjustTexelByAddress
(v.tex, v.tex_region); - if (v.tex_region[0] <= p.x && - v.tex_region[1] <= p.y && - p.x < v.tex_region[2] && - p.y < v.tex_region[3]) { + inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale, constant float4& source_region) { + float2 p = AdjustTexelByAddress
(v.tex, source_region); + if (source_region[0] <= p.x && + source_region[1] <= p.y && + p.x < source_region[2] && + p.y < source_region[3]) { constexpr sampler texture_sampler(filter::nearest); return texture.sample(texture_sampler, p); } @@ -145,7 +143,7 @@ struct ColorFromTexel { template<> struct ColorFromTexel { - inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale) { + inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale, constant float4& source_region) { constexpr sampler texture_sampler(filter::nearest); const float2 texel_size = 1 / source_size; @@ -166,7 +164,7 @@ struct ColorFromTexel { template struct ColorFromTexel { - inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale) { + inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale, constant float4& source_region) { constexpr sampler texture_sampler(filter::nearest); const float2 texel_size = 1 / source_size; @@ -174,27 +172,27 @@ struct ColorFromTexel { // 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 p1 = v.tex + texel_size / 2.0 + (texel_size / 512.0); - p0 = AdjustTexelByAddress
(p0, v.tex_region); - p1 = AdjustTexelByAddress
(p1, v.tex_region); + p0 = AdjustTexelByAddress
(p0, source_region); + p1 = AdjustTexelByAddress
(p1, source_region); float4 c0 = texture.sample(texture_sampler, p0); float4 c1 = texture.sample(texture_sampler, float2(p1.x, p0.y)); float4 c2 = texture.sample(texture_sampler, float2(p0.x, p1.y)); float4 c3 = texture.sample(texture_sampler, p1); - if (p0.x < v.tex_region[0]) { + if (p0.x < source_region[0]) { c0 = 0; c2 = 0; } - if (p0.y < v.tex_region[1]) { + if (p0.y < source_region[1]) { c0 = 0; c1 = 0; } - if (v.tex_region[2] <= p1.x) { + if (source_region[2] <= p1.x) { c1 = 0; c3 = 0; } - if (v.tex_region[3] <= p1.y) { + if (source_region[3] <= p1.y) { c2 = 0; c3 = 0; } @@ -206,7 +204,7 @@ struct ColorFromTexel { template struct ColorFromTexel { - inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale) { + inline float4 Do(VertexOut v, texture2d texture, constant float2& source_size, float scale, constant float4& source_region) { constexpr sampler texture_sampler(filter::nearest); const float2 texel_size = 1 / source_size; @@ -232,8 +230,9 @@ struct FragmentShaderImpl { constant float2& source_size, constant float4x4& color_matrix_body, constant float4& color_matrix_translation, - constant float& scale) { - float4 c = ColorFromTexel().Do(v, texture, source_size, scale); + constant float& scale, + constant float4& source_region) { + float4 c = ColorFromTexel().Do(v, texture, source_size, scale, source_region); if (useColorM) { c.rgb /= c.a + (1.0 - sign(c.a)); c = (color_matrix_body * c) + color_matrix_translation; @@ -256,8 +255,9 @@ struct FragmentShaderImpl { constant float2& source_size, constant float4x4& color_matrix_body, constant float4& color_matrix_translation, - constant float& scale) { - return ColorFromTexel().Do(v, texture, source_size, scale); + constant float& scale, + constant float4& source_region) { + return ColorFromTexel().Do(v, texture, source_size, scale, source_region); } }; @@ -274,9 +274,10 @@ struct FragmentShaderImpl { constant float2& source_size [[buffer(2)]], \ constant float4x4& color_matrix_body [[buffer(3)]], \ 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().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) @@ -635,7 +636,9 @@ func (g *Graphics) Reset() error { 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] 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) 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 { rce.SetFragmentTexture(src.texture, 0) } else { diff --git a/internal/graphicsdriver/opengl/defaultshader.go b/internal/graphicsdriver/opengl/defaultshader.go index 822b1028a..03b76f611 100644 --- a/internal/graphicsdriver/opengl/defaultshader.go +++ b/internal/graphicsdriver/opengl/defaultshader.go @@ -114,12 +114,10 @@ attribute vec2 tex; attribute vec4 tex_region; attribute vec4 color_scale; varying vec2 varying_tex; -varying vec4 varying_tex_region; varying vec4 varying_color_scale; void main(void) { varying_tex = tex; - varying_tex_region = tex_region; varying_color_scale = color_scale; mat4 projection_matrix = mat4( @@ -143,6 +141,7 @@ precision mediump float; {{.Definitions}} uniform sampler2D texture; +uniform vec4 source_region; #if defined(USE_COLOR_MATRIX) uniform mat4 color_matrix_body; @@ -156,7 +155,6 @@ uniform highp float scale; #endif varying highp vec2 varying_tex; -varying highp vec4 varying_tex_region; varying highp vec4 varying_color_scale; highp float floorMod(highp float x, highp float y) { @@ -190,11 +188,11 @@ void main(void) { # if defined(ADDRESS_UNSAFE) color = texture2D(texture, pos); # else - pos = adjustTexelByAddress(pos, varying_tex_region); - if (varying_tex_region[0] <= pos.x && - varying_tex_region[1] <= pos.y && - pos.x < varying_tex_region[2] && - pos.y < varying_tex_region[3]) { + pos = adjustTexelByAddress(pos, source_region); + if (source_region[0] <= pos.x && + source_region[1] <= pos.y && + pos.x < source_region[2] && + pos.y < source_region[3]) { color = texture2D(texture, pos); } else { 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); # if !defined(ADDRESS_UNSAFE) - p0 = adjustTexelByAddress(p0, varying_tex_region); - p1 = adjustTexelByAddress(p1, varying_tex_region); + p0 = adjustTexelByAddress(p0, source_region); + p1 = adjustTexelByAddress(p1, source_region); # endif // defined(ADDRESS_UNSAFE) vec4 c0 = texture2D(texture, p0); @@ -221,19 +219,19 @@ void main(void) { vec4 c2 = texture2D(texture, vec2(p0.x, p1.y)); vec4 c3 = texture2D(texture, p1); # if !defined(ADDRESS_UNSAFE) - if (p0.x < varying_tex_region[0]) { + if (p0.x < source_region[0]) { c0 = 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); 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); 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); c3 = vec4(0, 0, 0, 0); } diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 3c6b37638..c9c3b2760 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -149,7 +149,9 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) { 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] source := g.images[src] @@ -173,6 +175,14 @@ func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, uniforms = append(uniforms, uniformVariable{ name: "viewport_size", 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 { diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index d79d07208..2d9ab5fd6 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -151,7 +151,7 @@ func (m *Mipmap) DrawImage(src *Mipmap, bounds image.Rectangle, geom GeoM, color 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) 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 { w, h := sizeForLevel(bounds.Dx(), bounds.Dy(), level) s := pow2(level) @@ -161,12 +161,12 @@ func (m *Mipmap) DrawImage(src *Mipmap, bounds image.Rectangle, geom GeoM, color d *= s vs := quadVertices(0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca, false) 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() } -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) if colorm != nil && colorm.ScaleOnly() { @@ -205,7 +205,7 @@ func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16 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() } @@ -268,7 +268,7 @@ func (m *Mipmap) level(r image.Rectangle, level int) *shareable.Image { return nil } 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 return imgs[level] diff --git a/internal/restorable/image.go b/internal/restorable/image.go index de74c4b04..fc1c8362a 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -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. type drawTrianglesHistoryItem struct { - image *Image - vertices []float32 - indices []uint16 - colorm *affine.ColorM - mode driver.CompositeMode - filter driver.Filter - address driver.Address - shader *Shader - uniforms []interface{} + 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{} } // 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. vs := quadVertices(0, 0, float32(dw), float32(dh), 1, 1, float32(sw-1), float32(sh-1), rf, gf, bf, af) is := graphics.QuadIndices() - - i.DrawTriangles(emptyImage.image, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, nil, nil) + i.DrawTriangles(emptyImage.image, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) } // BasePixelsForTesting returns the image's basePixels for testing. @@ -369,7 +369,7 @@ func convertUniformVariables(uniforms []interface{}) []interface{} { // 9: Color G // 10: Color B // 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 { 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 { i.makeStale() } 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 if shader != nil { @@ -407,11 +407,11 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, if img != nil { 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. -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 { return } @@ -430,15 +430,16 @@ func (i *Image) appendDrawTrianglesHistory(image *Image, vertices []float32, ind copy(is, indices) item := &drawTrianglesHistoryItem{ - image: image, - vertices: vs, - indices: is, - colorm: colorm, - mode: mode, - filter: filter, - address: address, - shader: shader, - uniforms: uniforms, + image: image, + vertices: vs, + indices: is, + colorm: colorm, + mode: mode, + filter: filter, + address: address, + sourceRegion: sourceRegion, + shader: shader, + uniforms: uniforms, } i.drawTrianglesHistory = append(i.drawTrianglesHistory, item) } @@ -618,7 +619,7 @@ func (i *Image) restore() error { if c.shader != nil { 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 { diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index e088ed15d..3cee48ed1 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -131,7 +131,7 @@ func TestRestoreChain(t *testing.T) { for i := 0; i < num-1; i++ { vs := quadVertices(1, 1, 0, 0) 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 { 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) is := graphics.QuadIndices() - imgs[8].DrawTriangles(imgs[7], 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, 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, driver.Region{}, nil, nil) 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 { @@ -216,10 +216,10 @@ func TestRestoreOverrideSource(t *testing.T) { clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff} img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h) is := graphics.QuadIndices() - img2.DrawTriangles(img1, 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, 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, driver.Region{}, nil, nil) 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 { t.Fatal(err) } @@ -298,23 +298,23 @@ func TestRestoreComplexGraph(t *testing.T) { }() vs := quadVertices(w, h, 0, 0) 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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 { t.Fatal(err) } @@ -406,8 +406,8 @@ func TestRestoreRecursive(t *testing.T) { img0.Dispose() }() is := graphics.QuadIndices() - img1.DrawTriangles(img0, 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, 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, driver.Region{}, nil, nil) if err := ResolveStaleImages(); err != nil { t.Fatal(err) } @@ -501,7 +501,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) { vs := quadVertices(1, 1, 0, 0) 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) if err := ResolveStaleImages(); err != nil { @@ -538,8 +538,8 @@ func TestDispose(t *testing.T) { defer img2.Dispose() is := graphics.QuadIndices() - img1.DrawTriangles(img2, 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, 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, driver.Region{}, nil, nil) img1.Dispose() if err := ResolveStaleImages(); err != nil { @@ -647,7 +647,7 @@ func TestReplacePixelsOnly(t *testing.T) { vs := quadVertices(1, 1, 0, 0) 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) // BasePixelsForTesting is available without GPU accessing. @@ -700,7 +700,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) { src.ReplacePixels(pix, 0, 0, w, h) vs := quadVertices(1, 1, 0, 0) 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 // stale. @@ -721,7 +721,7 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0) 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) // ReplacePixels for a whole image doesn't panic. } @@ -739,7 +739,7 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0) 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) } @@ -828,7 +828,7 @@ func TestFill2(t *testing.T) { dst := NewImage(w, h, false) vs := quadVertices(w, h, 0, 0) 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. src.Fill(color.RGBA{0, 0xff, 0, 0xff}) @@ -867,7 +867,7 @@ func TestMutateSlices(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := make([]uint16, len(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 { vs[i] = 0 } diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go index 362c963dd..8e2f45e1a 100644 --- a/internal/restorable/shader_test.go +++ b/internal/restorable/shader_test.go @@ -38,7 +38,7 @@ func TestShader(t *testing.T) { us := []interface{}{ []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 { t.Fatal(err) @@ -76,7 +76,7 @@ func TestShaderChain(t *testing.T) { []float32{0, 0}, 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 { @@ -118,7 +118,7 @@ func TestShaderMultipleSources(t *testing.T) { srcs[1], 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. srcs[0].Fill(color.RGBA{}) @@ -150,7 +150,7 @@ func TestShaderDispose(t *testing.T) { us := []interface{}{ []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 // stale. diff --git a/internal/shareable/image.go b/internal/shareable/image.go index 95cb9f793..386b334da 100644 --- a/internal/shareable/image.go +++ b/internal/shareable/image.go @@ -220,7 +220,7 @@ func (i *Image) ensureNotShared() { dx1, dy1, sx1, sy1, sx0, sy0, sx1, sy1, 1, 1, 1, 1, } 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.backend = &backend{ @@ -309,7 +309,7 @@ func makeSharedIfNeeded(src *Image) { // 9: Color G // 10: Color B // 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() // 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+7] += oyf } + if address != driver.AddressUnsafe { + sourceRegion.X += oxf + sourceRegion.Y += oyf + } } var s *restorable.Shader @@ -376,7 +380,7 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, if img != nil { 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 delete(imagesToMakeShared, i) diff --git a/internal/shareable/image_test.go b/internal/shareable/image_test.go index 90711ef61..a396e4df0 100644 --- a/internal/shareable/image_test.go +++ b/internal/shareable/image_test.go @@ -96,7 +96,7 @@ func TestEnsureNotShared(t *testing.T) { // img4.ensureNotShared() should be called. vs := quadVertices(size/2, size/2, size/4, size/4, 1) 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 if got := img4.IsSharedForTesting(); 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. // 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) { @@ -166,7 +166,7 @@ func TestReshared(t *testing.T) { // Use img1 as a render target. vs := quadVertices(size, size, 0, 0, 1) 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 { t.Errorf("got: %v, want: %v", got, want) } @@ -176,7 +176,7 @@ func TestReshared(t *testing.T) { if err := MakeImagesSharedForTesting(); err != nil { 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 { 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 { t.Errorf("got: %v, want: %v", got, want) } @@ -231,7 +231,7 @@ func TestReshared(t *testing.T) { if err := MakeImagesSharedForTesting(); err != nil { 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 { t.Errorf("got: %v, want: %v", got, want) } @@ -326,7 +326,7 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) 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) pix, err := dst.Pixels(0, 0, w, h) @@ -368,7 +368,7 @@ func TestSmallImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) 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) if err != nil { @@ -410,7 +410,7 @@ func TestLongImages(t *testing.T) { const scale = 120 vs := quadVertices(w, h, 0, 0, scale) 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) if err != nil {