mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-12 20:18:59 +01:00
parent
58f4feda8d
commit
8b82667df1
@ -41,6 +41,7 @@ const (
|
||||
var (
|
||||
gophersImage *ebiten.Image
|
||||
rotate = false
|
||||
clip = false
|
||||
counter = 0
|
||||
)
|
||||
|
||||
@ -53,13 +54,20 @@ func update(screen *ebiten.Image) error {
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyR) {
|
||||
rotate = !rotate
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyC) {
|
||||
clip = !clip
|
||||
}
|
||||
|
||||
if ebiten.IsDrawingSkipped() {
|
||||
return nil
|
||||
}
|
||||
|
||||
s := 1.5 / math.Pow(1.01, float64(counter))
|
||||
ebitenutil.DebugPrint(screen, fmt.Sprintf("Minifying images (Nearest filter vs Linear filter): Press R to rotate the images.\nScale: %0.2f", s))
|
||||
msg := fmt.Sprintf(`Minifying images (Nearest filter vs Linear filter):
|
||||
Press R to rotate the images.
|
||||
Press C to clip the images.
|
||||
Scale: %0.2f`, s)
|
||||
ebitenutil.DebugPrint(screen, msg)
|
||||
|
||||
for i, f := range []ebiten.Filter{ebiten.FilterNearest, ebiten.FilterLinear} {
|
||||
w, h := gophersImage.Size()
|
||||
@ -73,6 +81,10 @@ func update(screen *ebiten.Image) error {
|
||||
op.GeoM.Scale(s, s)
|
||||
op.GeoM.Translate(32+float64(i*w)*s+float64(i*4), 64)
|
||||
op.Filter = f
|
||||
if clip {
|
||||
r := image.Rect(10, 10, 100, 100)
|
||||
op.SourceRect = &r
|
||||
}
|
||||
screen.DrawImage(gophersImage, op)
|
||||
}
|
||||
|
||||
|
77
image.go
77
image.go
@ -37,12 +37,13 @@ func init() {
|
||||
|
||||
type mipmap struct {
|
||||
orig *shareable.Image
|
||||
imgs []*shareable.Image
|
||||
imgs map[image.Rectangle][]*shareable.Image
|
||||
}
|
||||
|
||||
func newMipmap(s *shareable.Image) *mipmap {
|
||||
return &mipmap{
|
||||
orig: s,
|
||||
imgs: map[image.Rectangle][]*shareable.Image{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,18 +51,26 @@ func (m *mipmap) original() *shareable.Image {
|
||||
return m.orig
|
||||
}
|
||||
|
||||
func (m *mipmap) level(level int) *shareable.Image {
|
||||
func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
|
||||
if level == 0 {
|
||||
return m.orig
|
||||
}
|
||||
|
||||
idx := level - 1
|
||||
w, h := m.orig.Size()
|
||||
if len(m.imgs) > 0 {
|
||||
w, h = m.imgs[len(m.imgs)-1].Size()
|
||||
imgs, ok := m.imgs[r]
|
||||
if !ok {
|
||||
imgs = []*shareable.Image{}
|
||||
m.imgs[r] = imgs
|
||||
}
|
||||
for len(m.imgs) < idx+1 {
|
||||
src := m.level(len(m.imgs))
|
||||
idx := level - 1
|
||||
|
||||
size := r.Size()
|
||||
w, h := size.X, size.Y
|
||||
if len(imgs) > 0 {
|
||||
w, h = imgs[len(imgs)-1].Size()
|
||||
}
|
||||
|
||||
for len(imgs) < idx+1 {
|
||||
src := m.level(r, len(imgs))
|
||||
w2 := w / 2
|
||||
h2 := h / 2
|
||||
if w2 == 0 || h2 == 0 {
|
||||
@ -73,17 +82,24 @@ func (m *mipmap) level(level int) *shareable.Image {
|
||||
} else {
|
||||
s = shareable.NewImage(w2, h2)
|
||||
}
|
||||
vs := src.QuadVertices(0, 0, w, h, 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1)
|
||||
var vs []float32
|
||||
if len(imgs) == 0 {
|
||||
vs = src.QuadVertices(r.Min.X, r.Min.Y, r.Max.X, r.Max.Y, 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1)
|
||||
} else {
|
||||
vs = src.QuadVertices(0, 0, w, h, 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1)
|
||||
}
|
||||
is := graphicsutil.QuadIndices()
|
||||
s.DrawImage(src, vs, is, nil, opengl.CompositeModeCopy, graphics.FilterLinear)
|
||||
m.imgs = append(m.imgs, s)
|
||||
imgs = append(imgs, s)
|
||||
w = w2
|
||||
h = h2
|
||||
}
|
||||
if len(m.imgs) <= idx {
|
||||
m.imgs[r] = imgs
|
||||
|
||||
if len(imgs) <= idx {
|
||||
return nil
|
||||
}
|
||||
return m.imgs[idx]
|
||||
return imgs[idx]
|
||||
}
|
||||
|
||||
func (m *mipmap) isDisposed() bool {
|
||||
@ -97,10 +113,12 @@ func (m *mipmap) dispose() {
|
||||
}
|
||||
|
||||
func (m *mipmap) disposeMipmaps() {
|
||||
for _, img := range m.imgs {
|
||||
img.Dispose()
|
||||
for _, a := range m.imgs {
|
||||
for _, img := range a {
|
||||
img.Dispose()
|
||||
}
|
||||
}
|
||||
m.imgs = nil
|
||||
m.imgs = map[image.Rectangle][]*shareable.Image{}
|
||||
}
|
||||
|
||||
// Image represents a rectangle set of pixels.
|
||||
@ -346,19 +364,8 @@ func (i *Image) drawImage(img *Image, options *DrawImageOptions) {
|
||||
level = 6
|
||||
}
|
||||
|
||||
if level > 0 {
|
||||
s := 1 << uint(level)
|
||||
a *= float32(s)
|
||||
b *= float32(s)
|
||||
c *= float32(s)
|
||||
d *= float32(s)
|
||||
sx0 = sx0 / s
|
||||
sy0 = sy0 / s
|
||||
sx1 = sx1 / s
|
||||
sy1 = sy1 / s
|
||||
}
|
||||
|
||||
if src := img.mipmap.level(level); src != nil {
|
||||
// TODO: Move this logic to mipmap?
|
||||
if src := img.mipmap.level(image.Rect(sx0, sy0, sx1, sy1), level); src != nil {
|
||||
colorm := options.ColorM.impl
|
||||
cr, cg, cb, ca := float32(1), float32(1), float32(1), float32(1)
|
||||
if colorm.ScaleOnly() {
|
||||
@ -368,12 +375,24 @@ func (i *Image) drawImage(img *Image, options *DrawImageOptions) {
|
||||
cb = body[10]
|
||||
ca = body[15]
|
||||
}
|
||||
vs := src.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, cr, cg, cb, ca)
|
||||
var vs []float32
|
||||
if level == 0 {
|
||||
vs = src.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, cr, cg, cb, ca)
|
||||
} else {
|
||||
w, h := src.Size()
|
||||
s := 1 << uint(level)
|
||||
a *= float32(s)
|
||||
b *= float32(s)
|
||||
c *= float32(s)
|
||||
d *= float32(s)
|
||||
vs = src.QuadVertices(0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca)
|
||||
}
|
||||
is := graphicsutil.QuadIndices()
|
||||
|
||||
if colorm.ScaleOnly() {
|
||||
colorm = nil
|
||||
}
|
||||
|
||||
i.mipmap.original().DrawImage(src, vs, is, colorm, mode, filter)
|
||||
}
|
||||
i.disposeMipmaps()
|
||||
|
@ -508,11 +508,16 @@ func TestImageFill(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #317, #558
|
||||
// Issue #317, #558, #724
|
||||
func TestImageEdge(t *testing.T) {
|
||||
const (
|
||||
img0Width = 16
|
||||
img0Height = 16
|
||||
img0Width = 16
|
||||
img0Height = 16
|
||||
img0InnerWidth = 6
|
||||
img0InnerHeight = 6
|
||||
img0OffsetWidth = (img0Width - img0InnerWidth) / 2
|
||||
img0OffsetHeight = (img0Height - img0InnerHeight) / 2
|
||||
|
||||
img1Width = 32
|
||||
img1Height = 32
|
||||
)
|
||||
@ -522,7 +527,8 @@ func TestImageEdge(t *testing.T) {
|
||||
for i := 0; i < img0Width; i++ {
|
||||
idx := 4 * (i + j*img0Width)
|
||||
switch {
|
||||
case j < img0Height/2:
|
||||
case img0OffsetWidth <= i && i < img0Width-img0OffsetWidth &&
|
||||
img0InnerHeight <= j && j < img0Height-img0InnerHeight:
|
||||
pixels[idx] = 0xff
|
||||
pixels[idx+1] = 0
|
||||
pixels[idx+2] = 0
|
||||
@ -544,40 +550,45 @@ func TestImageEdge(t *testing.T) {
|
||||
for a := 0; a < 1440; a++ {
|
||||
angles = append(angles, float64(a)/1440*2*math.Pi)
|
||||
}
|
||||
for a := 0; a < 4096; a++ {
|
||||
for a := 0; a < 4096; a += 3 {
|
||||
// a++ should be fine, but it takes long to test.
|
||||
angles = append(angles, float64(a)/4096*2*math.Pi)
|
||||
}
|
||||
|
||||
for _, f := range []Filter{FilterNearest, FilterLinear} {
|
||||
for _, a := range angles {
|
||||
img1.Clear()
|
||||
op := &DrawImageOptions{}
|
||||
w, h := img0.Size()
|
||||
r := image.Rect(0, 0, w, h/2)
|
||||
op.SourceRect = &r
|
||||
op.GeoM.Translate(-float64(img0Width)/2, -float64(img0Height)/2)
|
||||
op.GeoM.Rotate(a)
|
||||
op.GeoM.Translate(img1Width/2, img1Height/2)
|
||||
op.Filter = f
|
||||
img1.DrawImage(img0, op)
|
||||
for j := 0; j < img1Height; j++ {
|
||||
for i := 0; i < img1Width; i++ {
|
||||
c := img1.At(i, j)
|
||||
if c == transparent {
|
||||
continue
|
||||
}
|
||||
switch f {
|
||||
case FilterNearest:
|
||||
if c == red {
|
||||
for _, s := range []float64{1, 0.5, 0.25} {
|
||||
for _, f := range []Filter{FilterNearest, FilterLinear} {
|
||||
for _, a := range angles {
|
||||
img1.Clear()
|
||||
op := &DrawImageOptions{}
|
||||
r := image.Rect(img0OffsetWidth, img0InnerHeight, img0Width-img0OffsetWidth, img0Height-img0InnerHeight)
|
||||
op.SourceRect = &r
|
||||
|
||||
w, h := img0.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(img0, op)
|
||||
for j := 0; j < img1Height; j++ {
|
||||
for i := 0; i < img1Width; i++ {
|
||||
c := img1.At(i, j)
|
||||
if c == transparent {
|
||||
continue
|
||||
}
|
||||
case FilterLinear:
|
||||
_, g, b, _ := c.RGBA()
|
||||
if g == 0 && b == 0 {
|
||||
continue
|
||||
switch f {
|
||||
case FilterNearest:
|
||||
if c == red {
|
||||
continue
|
||||
}
|
||||
case FilterLinear:
|
||||
_, g, b, _ := c.RGBA()
|
||||
if g == 0 && b == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
t.Errorf("img1.At(%d, %d) (filter: %d, scale: %f, angle: %f) want: red or transparent, got: %v", i, j, f, s, a, c)
|
||||
}
|
||||
t.Errorf("img1.At(%d, %d) (filter: %d, angle: %f) want: red or transparent, got: %v", i, j, f, a, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user