mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
ebiten: add NewImageFromImageWithOptions
Closes #2013 Closes #2017 Closes #2124
This commit is contained in:
parent
aee2e67242
commit
acd70d6e34
57
image.go
57
image.go
@ -815,6 +815,8 @@ type NewImageOptions struct {
|
|||||||
// If DrawImage is called on a new image created by NewImageOptions,
|
// If DrawImage is called on a new image created by NewImageOptions,
|
||||||
// for example, the center of scaling and rotating is (0, 0), that might not be a left-upper position.
|
// for example, the center of scaling and rotating is (0, 0), that might not be a left-upper position.
|
||||||
//
|
//
|
||||||
|
// If options is nil, the default setting is used.
|
||||||
|
//
|
||||||
// NewImageWithOptions should be called only when necessary.
|
// NewImageWithOptions should be called only when necessary.
|
||||||
// For example, you should avoid to call NewImageWithOptions every Update or Draw call.
|
// For example, you should avoid to call NewImageWithOptions every Update or Draw call.
|
||||||
// Reusing the same image by Clear is much more efficient than creating a new image.
|
// Reusing the same image by Clear is much more efficient than creating a new image.
|
||||||
@ -859,19 +861,62 @@ func newImage(bounds image.Rectangle, imageType atlas.ImageType) *Image {
|
|||||||
//
|
//
|
||||||
// NewImageFromImage panics if RunGame already finishes.
|
// NewImageFromImage panics if RunGame already finishes.
|
||||||
//
|
//
|
||||||
// The returned image's origin is always (0, 0). The source's bounds are not respected.
|
// The returned image's left-upper position is always (0, 0). The source's bounds are not respected.
|
||||||
func NewImageFromImage(source image.Image) *Image {
|
func NewImageFromImage(source image.Image) *Image {
|
||||||
if isRunGameEnded() {
|
return NewImageFromImageWithOptions(source, nil)
|
||||||
panic(fmt.Sprintf("ebiten: NewImage cannot be called after RunGame finishes"))
|
}
|
||||||
|
|
||||||
|
// NewImageFromImageOptions represents options for NewImageFromImage.
|
||||||
|
type NewImageFromImageOptions struct {
|
||||||
|
// Unmanaged represents whether the image is unmanaged or not.
|
||||||
|
// The default (zero) value is false, that means the image is managed.
|
||||||
|
//
|
||||||
|
// An unmanged image is never on an internal automatic texture atlas.
|
||||||
|
// A regular image is a part of an internal texture atlas, and locating them is done automatically in Ebitengine.
|
||||||
|
// NewUnmanagedImage is useful when you want finer controls over the image for performance and memory reasons.
|
||||||
|
Unmanaged bool
|
||||||
|
|
||||||
|
// PreserveBounds represents whether the new image's bounds are the same as the given image.
|
||||||
|
// The default (zero) value is false, that means the new image's left-upper position is adjusted to (0, 0).
|
||||||
|
PreserveBounds bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageFromImageWithOptions creates a new image with the given image (source) with the given options.
|
||||||
|
//
|
||||||
|
// If source's width or height is less than 1 or more than device-dependent maximum size, NewImageFromImageWithOptions panics.
|
||||||
|
//
|
||||||
|
// If options is nil, the default setting is used.
|
||||||
|
//
|
||||||
|
// NewImageFromImageWithOptions should be called only when necessary.
|
||||||
|
// For example, you should avoid to call NewImageFromImageWithOptions every Update or Draw call.
|
||||||
|
// Reusing the same image by Clear and ReplacePixels is much more efficient than creating a new image.
|
||||||
|
//
|
||||||
|
// NewImageFromImageWithOptions panics if RunGame already finishes.
|
||||||
|
func NewImageFromImageWithOptions(source image.Image, options *NewImageFromImageOptions) *Image {
|
||||||
|
if options == nil {
|
||||||
|
options = &NewImageFromImageOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
size := source.Bounds().Size()
|
var r image.Rectangle
|
||||||
i := NewImage(size.X, size.Y)
|
if options.PreserveBounds {
|
||||||
|
r = source.Bounds()
|
||||||
|
} else {
|
||||||
|
size := source.Bounds().Size()
|
||||||
|
r = image.Rect(0, 0, size.X, size.Y)
|
||||||
|
}
|
||||||
|
i := NewImageWithOptions(r, &NewImageOptions{
|
||||||
|
Unmanaged: options.Unmanaged,
|
||||||
|
})
|
||||||
|
|
||||||
// If the given image is an Ebitengine image, use DrawImage instead of reading pixels from the source.
|
// If the given image is an Ebitengine image, use DrawImage instead of reading pixels from the source.
|
||||||
// This works even before the game loop runs.
|
// This works even before the game loop runs.
|
||||||
if source, ok := source.(*Image); ok {
|
if source, ok := source.(*Image); ok {
|
||||||
i.DrawImage(source, nil)
|
op := &DrawImageOptions{}
|
||||||
|
if options.PreserveBounds {
|
||||||
|
b := source.Bounds()
|
||||||
|
op.GeoM.Translate(float64(b.Min.X), float64(b.Min.Y))
|
||||||
|
}
|
||||||
|
i.DrawImage(source, op)
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3045,7 +3045,7 @@ func TestImageOptionsNegativeBoundsDrawImage(t *testing.T) {
|
|||||||
want = color.RGBA{0xff, 0xff, 0xff, 0xff}
|
want = color.RGBA{0xff, 0xff, 0xff, 0xff}
|
||||||
}
|
}
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3115,6 +3115,65 @@ func TestImageOptionsNegativeBoundsDrawTriangles(t *testing.T) {
|
|||||||
if -2 <= i && i < 2 && -3 <= j && j < 3 {
|
if -2 <= i && i < 2 && -3 <= j && j < 3 {
|
||||||
want = color.RGBA{0xff, 0xff, 0xff, 0xff}
|
want = color.RGBA{0xff, 0xff, 0xff, 0xff}
|
||||||
}
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImageFromImageOptions(t *testing.T) {
|
||||||
|
r := image.Rect(-2, -3, 4, 5)
|
||||||
|
pix := make([]byte, 4*r.Dx()*r.Dy())
|
||||||
|
for i := range pix {
|
||||||
|
pix[i] = 0xff
|
||||||
|
}
|
||||||
|
src := &image.RGBA{
|
||||||
|
Pix: pix,
|
||||||
|
Stride: 4 * 2,
|
||||||
|
Rect: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
op := &ebiten.NewImageFromImageOptions{
|
||||||
|
PreserveBounds: true,
|
||||||
|
}
|
||||||
|
img := ebiten.NewImageFromImageWithOptions(src, op)
|
||||||
|
if got, want := img.Bounds(), r; got != want {
|
||||||
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := r.Min.Y; j < r.Max.Y; j++ {
|
||||||
|
for i := r.Min.X; i < r.Max.X; i++ {
|
||||||
|
got := img.At(i, j)
|
||||||
|
want := color.RGBA{0xff, 0xff, 0xff, 0xff}
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImageFromEbitenImageOptions(t *testing.T) {
|
||||||
|
r := image.Rect(-2, -3, 4, 5)
|
||||||
|
src := ebiten.NewImageWithOptions(r, nil)
|
||||||
|
pix := make([]byte, 4*r.Dx()*r.Dy())
|
||||||
|
for i := range pix {
|
||||||
|
pix[i] = 0xff
|
||||||
|
}
|
||||||
|
src.ReplacePixels(pix)
|
||||||
|
|
||||||
|
op := &ebiten.NewImageFromImageOptions{
|
||||||
|
PreserveBounds: true,
|
||||||
|
}
|
||||||
|
img := ebiten.NewImageFromImageWithOptions(src, op)
|
||||||
|
if got, want := img.Bounds(), r; got != want {
|
||||||
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := r.Min.Y; j < r.Max.Y; j++ {
|
||||||
|
for i := r.Min.X; i < r.Max.X; i++ {
|
||||||
|
got := img.At(i, j)
|
||||||
|
want := color.RGBA{0xff, 0xff, 0xff, 0xff}
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user