graphics: Use source-border check even at DrawTriangles

DrawTriangles can now take a sub-image.
This commit is contained in:
Hajime Hoshi 2018-12-23 02:35:50 +09:00
parent 4149a56524
commit 189b8a17e9
4 changed files with 113 additions and 56 deletions

View File

@ -411,6 +411,7 @@ type Vertex struct {
DstY float32
// SrcX and SrcY represents a point on a source image.
// Note that SrcX/SrcY on a sub-image should be in its bounds.
SrcX float32
SrcY float32
@ -452,11 +453,6 @@ const MaxIndicesNum = graphics.IndicesNum
//
// The rule in which DrawTriangles works effectively is same as DrawImage's.
//
// In contrast to DrawImage, DrawTriangles doesn't care source image edges.
// This means that you might need to add 1px gap on a source region when you render an image by DrawTriangles.
// Note that Ebiten creates texture atlases internally, so you still have to care this even when
// you render a single image.
//
// When the image i is disposed, DrawTriangles does nothing.
//
// Internal mipmap is not used on DrawTriangles.
@ -468,10 +464,6 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
return
}
// TODO: Implement this.
if img.isSubimage() {
panic("using a subimage at DrawTriangles is not implemented")
}
if i.isSubimage() {
panic("render to a subimage is not implemented")
}
@ -499,8 +491,12 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
vs := make([]float32, len(vertices)*graphics.VertexFloatNum)
src := img.mipmap.original()
r := img.Bounds()
for idx, v := range vertices {
src.PutVertex(vs[idx*graphics.VertexFloatNum:(idx+1)*graphics.VertexFloatNum], float32(v.DstX), float32(v.DstY), v.SrcX, v.SrcY, v.ColorR, v.ColorG, v.ColorB, v.ColorA)
src.PutVertex(vs[idx*graphics.VertexFloatNum:(idx+1)*graphics.VertexFloatNum],
float32(v.DstX), float32(v.DstY), v.SrcX, v.SrcY,
float32(r.Min.X), float32(r.Min.Y), float32(r.Max.X), float32(r.Max.Y),
v.ColorR, v.ColorG, v.ColorB, v.ColorA)
}
i.mipmap.original().DrawImage(img.mipmap.original(), vs, indices, options.ColorM.impl, mode, filter)
i.disposeMipmaps()

View File

@ -606,38 +606,96 @@ func TestImageEdge(t *testing.T) {
for _, s := range []float64{1, 0.5, 0.25} {
for _, f := range []Filter{FilterNearest, FilterLinear} {
for _, a := range angles {
img1.Clear()
op := &DrawImageOptions{}
w, h := img0Sub.Size()
op.GeoM.Translate(-float64(w)/2, -float64(h)/2)
op.GeoM.Scale(s, s)
op.GeoM.Rotate(a)
op.GeoM.Translate(img1Width/2, img1Height/2)
op.Filter = f
img1.DrawImage(img0Sub, op)
allTransparent := true
for j := 0; j < img1Height; j++ {
for i := 0; i < img1Width; i++ {
c := img1.At(i, j)
if c == transparent {
continue
for _, testDrawTriangles := range []bool{false, true} {
img1.Clear()
w, h := img0Sub.Size()
b := img0Sub.Bounds()
var geo GeoM
geo.Translate(-float64(w)/2, -float64(h)/2)
geo.Scale(s, s)
geo.Rotate(a)
geo.Translate(img1Width/2, img1Height/2)
if !testDrawTriangles {
op := &DrawImageOptions{}
op.GeoM = geo
op.Filter = f
img1.DrawImage(img0Sub, op)
} else {
op := &DrawTrianglesOptions{}
dx0, dy0 := geo.Apply(0, 0)
dx1, dy1 := geo.Apply(float64(w), 0)
dx2, dy2 := geo.Apply(0, float64(h))
dx3, dy3 := geo.Apply(float64(w), float64(h))
vs := []Vertex{
{
DstX: float32(dx0),
DstY: float32(dy0),
SrcX: float32(b.Min.X),
SrcY: float32(b.Min.Y),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
{
DstX: float32(dx1),
DstY: float32(dy1),
SrcX: float32(b.Max.X),
SrcY: float32(b.Min.Y),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
{
DstX: float32(dx2),
DstY: float32(dy2),
SrcX: float32(b.Min.X),
SrcY: float32(b.Max.Y),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
{
DstX: float32(dx3),
DstY: float32(dy3),
SrcX: float32(b.Max.X),
SrcY: float32(b.Max.Y),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
}
allTransparent = false
switch f {
case FilterNearest:
if c == red {
continue
}
case FilterLinear:
if _, g, b, _ := c.RGBA(); g == 0 && b == 0 {
continue
}
}
t.Fatalf("img1.At(%d, %d) (filter: %d, scale: %f, angle: %f) want: red or transparent, got: %v", i, j, f, s, a, c)
is := graphics.QuadIndices()
op.Filter = f
img1.DrawTriangles(vs, is, img0Sub, op)
}
allTransparent := true
for j := 0; j < img1Height; j++ {
for i := 0; i < img1Width; i++ {
c := img1.At(i, j)
if c == transparent {
continue
}
allTransparent = false
switch f {
case FilterNearest:
if c == red {
continue
}
case FilterLinear:
if _, g, b, _ := c.RGBA(); g == 0 && b == 0 {
continue
}
}
t.Fatalf("img1.At(%d, %d) (filter: %d, scale: %f, angle: %f, draw-triangles?: %t) want: red or transparent, got: %v", i, j, f, s, a, testDrawTriangles, c)
}
}
if allTransparent {
t.Fatalf("img1 (filter: %d, scale: %f, angle: %f, draw-triangles?: %t) is transparent but should not", f, s, a, testDrawTriangles)
}
}
if allTransparent {
t.Fatalf("img1 (filter: %d, scale: %f, angle: %f) is transparent but should not", f, s, a)
}
}
}

View File

@ -155,7 +155,7 @@ func QuadIndices() []uint16 {
return quadIndices
}
func PutVertex(vs []float32, width, height int, dx, dy, sx, sy float32, cr, cg, cb, ca float32) {
func PutVertex(vs []float32, width, height int, dx, dy, su, sv float32, u0, v0, u1, v1 float32, cr, cg, cb, ca float32) {
if !isPowerOf2(width) {
panic("not reached")
}
@ -163,21 +163,14 @@ func PutVertex(vs []float32, width, height int, dx, dy, sx, sy float32, cr, cg,
panic("not reached")
}
wf := float32(width)
hf := float32(height)
// Specify -1 for the source region, which means the source region is ignored.
//
// NaN would make more sense to represent an invalid state, but vertices including NaN values doesn't work on
// some machines (#696). Let's use negative numbers to represent such state.
vs[0] = dx
vs[1] = dy
vs[2] = sx / wf
vs[3] = sy / hf
vs[4] = 0
vs[5] = 0
vs[6] = 1
vs[7] = 1
vs[2] = su
vs[3] = sv
vs[4] = u0
vs[5] = v0
vs[6] = u1
vs[7] = v1
vs[8] = cr
vs[9] = cg
vs[10] = cb

View File

@ -197,13 +197,23 @@ func (i *Image) QuadVertices(sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32,
return graphics.QuadVertices(w, h, sx0+ox, sy0+oy, sx1+ox, sy1+oy, a, b, c, d, tx, ty, cr, cg, cb, ca)
}
func (i *Image) PutVertex(dest []float32, dx, dy, sx, sy float32, cr, cg, cb, ca float32) {
func (i *Image) PutVertex(dest []float32, dx, dy, sx, sy float32, bx0, by0, bx1, by1 float32,
cr, cg, cb, ca float32) {
if i.backend == nil {
i.allocate(true)
}
ox, oy, _, _ := i.region()
oxf, oyf := float32(ox), float32(oy)
w, h := i.backend.restorable.SizePowerOf2()
graphics.PutVertex(dest, w, h, dx, dy, sx+float32(ox), sy+float32(oy), cr, cg, cb, ca)
su := (sx + oxf) / float32(w)
sv := (sy + oyf) / float32(h)
u0 := (bx0 + oxf) / float32(w)
v0 := (by0 + oyf) / float32(h)
u1 := (bx1 + oxf) / float32(w)
v1 := (by1 + oyf) / float32(h)
graphics.PutVertex(dest, w, h, dx, dy, su, sv, u0, v0, u1, v1, cr, cg, cb, ca)
}
const MaxCountForShare = 10