graphics: Add DrawImageOptions.SourceRect

This commit is contained in:
Hajime Hoshi 2017-01-18 01:35:21 +09:00
parent 8614599c5c
commit de7215f3fc
4 changed files with 109 additions and 124 deletions

View File

@ -76,25 +76,6 @@ func (s *Sprites) Update() {
} }
} }
func (s *Sprites) Len() int {
return s.num
}
func (s *Sprites) Dst(i int) (x0, y0, x1, y1 int) {
if s.num <= i {
return 0, 0, 0, 0
}
ss := s.sprites[i]
return ss.x, ss.y, ss.x + ebitenImageWidth, ss.y + ebitenImageHeight
}
func (s *Sprites) Src(i int) (x0, y0, x1, y1 int) {
if s.num <= i {
return 0, 0, 0, 0
}
return 0, 0, ebitenImageWidth, ebitenImageHeight
}
const ( const (
MinSprites = 0 MinSprites = 0
MaxSprites = 50000 MaxSprites = 50000
@ -102,6 +83,13 @@ const (
var sprites = &Sprites{make([]*Sprite, MaxSprites), 500} var sprites = &Sprites{make([]*Sprite, MaxSprites), 500}
var op *ebiten.DrawImageOptions
func init() {
op = &ebiten.DrawImageOptions{}
op.ColorM.Scale(1.0, 1.0, 1.0, 0.5)
}
func update(screen *ebiten.Image) error { func update(screen *ebiten.Image) error {
if ebiten.IsKeyPressed(ebiten.KeyLeft) { if ebiten.IsKeyPressed(ebiten.KeyLeft) {
sprites.num -= 20 sprites.num -= 20
@ -120,15 +108,18 @@ func update(screen *ebiten.Image) error {
if ebiten.IsRunningSlowly() { if ebiten.IsRunningSlowly() {
return nil return nil
} }
op := &ebiten.DrawImageOptions{ for i := 0; i < sprites.num; i++ {
ImageParts: sprites, s := sprites.sprites[i]
op.GeoM = ebiten.GeoM{}
op.GeoM.Translate(float64(s.x), float64(s.y))
screen.DrawImage(ebitenImage, op)
} }
op.ColorM.Scale(1.0, 1.0, 1.0, 0.5)
screen.DrawImage(ebitenImage, op)
msg := fmt.Sprintf(`FPS: %0.2f msg := fmt.Sprintf(`FPS: %0.2f
Num of sprites: %d Num of sprites: %d
Press <- or -> to change the number of sprites`, ebiten.CurrentFPS(), sprites.Len()) Press <- or -> to change the number of sprites`, ebiten.CurrentFPS(), sprites.num)
ebitenutil.DebugPrint(screen, msg) if err := ebitenutil.DebugPrint(screen, msg); err != nil {
return err
}
return nil return nil
} }

View File

@ -79,8 +79,7 @@ func (i *Image) Fill(clr color.Color) error {
// After determining parts to draw, this applies the geometry matrix and the color matrix. // After determining parts to draw, this applies the geometry matrix and the color matrix.
// //
// Here are the default values: // Here are the default values:
// ImageParts: (0, 0) - (source width, source height) to (0, 0) - (source width, source height) // SourceRect: nil. When SourceRect is nil, the whole source image is used.
// (i.e. the whole source image)
// GeoM: Identity matrix // GeoM: Identity matrix
// ColorM: Identity matrix (that changes no colors) // ColorM: Identity matrix (that changes no colors)
// CompositeMode: CompositeModeSourceOver (regular alpha blending) // CompositeMode: CompositeModeSourceOver (regular alpha blending)
@ -94,7 +93,7 @@ func (i *Image) Fill(clr color.Color) error {
// When image is as same as i, DrawImage panics. // When image is as same as i, DrawImage panics.
// //
// DrawImage always returns nil as of 1.5.0-alpha. // DrawImage always returns nil as of 1.5.0-alpha.
func (i *Image) DrawImage(image *Image, options *DrawImageOptions) error { func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
if i.restorable == nil { if i.restorable == nil {
return nil return nil
} }
@ -104,26 +103,45 @@ func (i *Image) DrawImage(image *Image, options *DrawImageOptions) error {
options = &DrawImageOptions{} options = &DrawImageOptions{}
} }
parts := options.ImageParts parts := options.ImageParts
if parts == nil { // Parts is deprecated. This implementations is for backward compatibility.
// Check options.Parts for backward-compatibility. if parts == nil && options.Parts != nil {
dparts := options.Parts parts = imageParts(options.Parts)
if dparts != nil {
parts = imageParts(dparts)
} else {
w, h := image.restorable.Size()
parts = &wholeImage{w, h}
}
} }
w, h := image.restorable.Size() // ImageParts is deprecated. This implementations is for backward compatibility.
vs := vertices(parts, w, h, &options.GeoM.impl) if parts != nil {
if len(vs) == 0 { l := parts.Len()
for idx := 0; idx < l; idx++ {
sx0, sy0, sx1, sy1 := parts.Src(idx)
dx0, dy0, dx1, dy1 := parts.Dst(idx)
op := &DrawImageOptions{
ColorM: options.ColorM,
CompositeMode: options.CompositeMode,
}
r := image.Rect(sx0, sy0, sx1, sy1)
op.SourceRect = &r
op.GeoM.Scale(
float64(dx1-dx0)/float64(sx1-sx0),
float64(dy1-dy0)/float64(sy1-sy0))
op.GeoM.Translate(float64(dx0), float64(dy0))
op.GeoM.Concat(options.GeoM)
i.DrawImage(img, op)
}
return nil return nil
} }
if i == image { w, h := img.restorable.Size()
panic("ebiten: Image.DrawImage: image must be different from the receiver") sx0, sy0, sx1, sy1 := 0, 0, w, h
if r := options.SourceRect; r != nil {
sx0 = r.Min.X
sy0 = r.Min.Y
sx1 = r.Max.X
sy1 = r.Max.Y
}
vs := vertices(sx0, sy0, sx1, sy1, w, h, &options.GeoM.impl)
if i == img {
panic("ebiten: Image.DrawImage: img must be different from the receiver")
} }
mode := opengl.CompositeMode(options.CompositeMode) mode := opengl.CompositeMode(options.CompositeMode)
i.restorable.DrawImage(image.restorable, vs, &options.ColorM.impl, mode) i.restorable.DrawImage(img.restorable, vs, &options.ColorM.impl, mode)
return nil return nil
} }
@ -203,12 +221,15 @@ func (i *Image) ReplacePixels(p []uint8) error {
// A DrawImageOptions represents options to render an image on an image. // A DrawImageOptions represents options to render an image on an image.
type DrawImageOptions struct { type DrawImageOptions struct {
ImageParts ImageParts SourceRect *image.Rectangle
GeoM GeoM GeoM GeoM
ColorM ColorM ColorM ColorM
CompositeMode CompositeMode CompositeMode CompositeMode
// Deprecated (as of 1.1.0-alpha): Use ImageParts instead. // Deprecated (as of 1.5.0-alpha): Use Part instead.
ImageParts ImageParts
// Deprecated (as of 1.1.0-alpha): Use Part instead.
Parts []ImagePart Parts []ImagePart
} }

View File

@ -18,13 +18,13 @@ import (
"image" "image"
) )
// An ImagePart is deprecated (as of 1.1.0-alpha): Use ImageParts instead. // An ImagePart is deprecated (as of 1.1.0-alpha): Use SourceRect instead.
type ImagePart struct { type ImagePart struct {
Dst image.Rectangle Dst image.Rectangle
Src image.Rectangle Src image.Rectangle
} }
// An ImageParts represents the parts of the destination image and the parts of the source image. // An ImageParts is deprecated (as of 1.5.0-alpha): Use SourceRect instead.
type ImageParts interface { type ImageParts interface {
Len() int Len() int
Dst(i int) (x0, y0, x1, y1 int) Dst(i int) (x0, y0, x1, y1 int)
@ -47,20 +47,3 @@ func (p imageParts) Src(i int) (x0, y0, x1, y1 int) {
src := &p[i].Src src := &p[i].Src
return src.Min.X, src.Min.Y, src.Max.X, src.Max.Y return src.Min.X, src.Min.Y, src.Max.X, src.Max.Y
} }
type wholeImage struct {
width int
height int
}
func (w *wholeImage) Len() int {
return 1
}
func (w *wholeImage) Dst(i int) (x0, y0, x1, y1 int) {
return 0, 0, w.width, w.height
}
func (w *wholeImage) Src(i int) (x0, y0, x1, y1 int) {
return 0, 0, w.width, w.height
}

View File

@ -27,10 +27,12 @@ const texelAdjustment = 256
var quadFloat32Num = graphics.QuadVertexSizeInBytes() / 4 var quadFloat32Num = graphics.QuadVertexSizeInBytes() / 4
func vertices(parts ImageParts, width, height int, geo *affine.GeoM) []float32 { func vertices(sx0, sy0, sx1, sy1 int, width, height int, geo *affine.GeoM) []float32 {
if sx0 == sx1 || sy0 == sy1 {
return nil
}
// TODO: This function should be in graphics package? // TODO: This function should be in graphics package?
l := parts.Len() vs := make([]float32, quadFloat32Num)
vs := make([]float32, l*quadFloat32Num)
a, b, c, d, tx, ty := geo.Elements() a, b, c, d, tx, ty := geo.Elements()
g0 := float32(a) g0 := float32(a)
g1 := float32(b) g1 := float32(b)
@ -48,66 +50,54 @@ func vertices(parts ImageParts, width, height int, geo *affine.GeoM) []float32 {
} }
wf := float32(w) wf := float32(w)
hf := float32(h) hf := float32(h)
n := 0 x0, y0, x1, y1 := float32(0), float32(0), float32(sx1-sx0), float32(sy1-sy0)
for i := 0; i < l; i++ { u0, v0, u1, v1 := float32(sx0)/wf, float32(sy0)/hf, float32(sx1)/wf, float32(sy1)/hf
dx0, dy0, dx1, dy1 := parts.Dst(i) // Adjust texels to fix a problem that outside texels are used (#317).
if dx0 == dx1 || dy0 == dy1 { u1 -= 1.0 / wf / texelAdjustment
continue v1 -= 1.0 / hf / texelAdjustment
} vs[0] = x0
x0, y0, x1, y1 := float32(dx0), float32(dy0), float32(dx1), float32(dy1) vs[1] = y0
sx0, sy0, sx1, sy1 := parts.Src(i) vs[2] = u0
if sx0 == sx1 || sy0 == sy1 { vs[3] = v0
continue vs[4] = g0
} vs[5] = g1
u0, v0, u1, v1 := float32(sx0)/wf, float32(sy0)/hf, float32(sx1)/wf, float32(sy1)/hf vs[6] = g2
// Adjust texels to fix a problem that outside texels are used (#317). vs[7] = g3
u1 -= 1.0 / wf / texelAdjustment vs[8] = g4
v1 -= 1.0 / hf / texelAdjustment vs[9] = g5
vs[n] = x0
vs[n+1] = y0
vs[n+2] = u0
vs[n+3] = v0
vs[n+4] = g0
vs[n+5] = g1
vs[n+6] = g2
vs[n+7] = g3
vs[n+8] = g4
vs[n+9] = g5
vs[n+10] = x1 vs[10] = x1
vs[n+11] = y0 vs[11] = y0
vs[n+12] = u1 vs[12] = u1
vs[n+13] = v0 vs[13] = v0
vs[n+14] = g0 vs[14] = g0
vs[n+15] = g1 vs[15] = g1
vs[n+16] = g2 vs[16] = g2
vs[n+17] = g3 vs[17] = g3
vs[n+18] = g4 vs[18] = g4
vs[n+19] = g5 vs[19] = g5
vs[n+20] = x0 vs[20] = x0
vs[n+21] = y1 vs[21] = y1
vs[n+22] = u0 vs[22] = u0
vs[n+23] = v1 vs[23] = v1
vs[n+24] = g0 vs[24] = g0
vs[n+25] = g1 vs[25] = g1
vs[n+26] = g2 vs[26] = g2
vs[n+27] = g3 vs[27] = g3
vs[n+28] = g4 vs[28] = g4
vs[n+29] = g5 vs[29] = g5
vs[n+30] = x1 vs[30] = x1
vs[n+31] = y1 vs[31] = y1
vs[n+32] = u1 vs[32] = u1
vs[n+33] = v1 vs[33] = v1
vs[n+34] = g0 vs[34] = g0
vs[n+35] = g1 vs[35] = g1
vs[n+36] = g2 vs[36] = g2
vs[n+37] = g3 vs[37] = g3
vs[n+38] = g4 vs[38] = g4
vs[n+39] = g5 vs[39] = g5
n += quadFloat32Num
}
return vs return vs
} }