diff --git a/doc.go b/doc.go index 1e5941a38..e64bc1597 100644 --- a/doc.go +++ b/doc.go @@ -37,9 +37,12 @@ // ebiten.Run(update, 320, 240, 2, "Your game's title") // } // -// The EBITEN_SCREENSHOT_KEY environment variable specified the key +// The EBITEN_SCREENSHOT_KEY environment variable specifies the key // to take a screenshot. For example, if you run your game with // `EBITEN_SCREENSHOT_KEY=q`, you can take a game screen's screenshot // by pressing Q key. The image file is saved at the current directory // with the name screen*.png. +// +// The EBITEN_DUMP_IMAGES_KEY environment variable specifies the key +// to dump all the internal images. package ebiten diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 0bd92eec7..42d38aa3f 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -15,6 +15,8 @@ package restorable import ( + "image" + "github.com/hajimehoshi/ebiten/internal/graphics" ) @@ -71,6 +73,39 @@ func Restore() error { return theImages.restore() } +func Images() ([]image.Image, error) { + var imgs []image.Image + for img := range theImages.images { + if img.volatile { + continue + } + if img.screen { + continue + } + + w, h := img.Size() + pix := make([]byte, 4*w*h) + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + c, err := img.At(i, j) + if err != nil { + return nil, err + } + pix[4*(i+j*w)] = byte(c.R) + pix[4*(i+j*w)+1] = byte(c.G) + pix[4*(i+j*w)+2] = byte(c.B) + pix[4*(i+j*w)+3] = byte(c.A) + } + } + imgs = append(imgs, &image.RGBA{ + Pix: pix, + Stride: 4 * w, + Rect: image.Rect(0, 0, w, h), + }) + } + return imgs, nil +} + // add adds img to the images. func (i *images) add(img *Image) { i.images[img] = struct{}{} diff --git a/internal/shareable/shareable.go b/internal/shareable/shareable.go index 670c3308e..aea892802 100644 --- a/internal/shareable/shareable.go +++ b/internal/shareable/shareable.go @@ -16,6 +16,7 @@ package shareable import ( "fmt" + "image" "image/color" "runtime" @@ -322,3 +323,9 @@ func BackendNumForTesting() int { defer backendsM.Unlock() return len(theBackends) } + +func Images() ([]image.Image, error) { + backendsM.Lock() + defer backendsM.Unlock() + return restorable.Images() +} diff --git a/run.go b/run.go index dd655a07d..7c0213bca 100644 --- a/run.go +++ b/run.go @@ -17,12 +17,15 @@ package ebiten import ( "fmt" "image" + "io/ioutil" "os" + "path/filepath" "sync/atomic" "github.com/hajimehoshi/ebiten/internal/clock" "github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/png" + "github.com/hajimehoshi/ebiten/internal/shareable" "github.com/hajimehoshi/ebiten/internal/ui" ) @@ -99,6 +102,10 @@ type imageDumper struct { hasScreenshotKey bool screenshotKey Key toTakeScreenshot bool + + hasDumpImagesKey bool + dumpImagesKey Key + toDumpImages bool } func (i *imageDumper) update(screen *Image) error { @@ -116,18 +123,43 @@ func (i *imageDumper) update(screen *Image) error { i.screenshotKey = key } } - } - - if i.hasScreenshotKey && IsKeyPressed(i.screenshotKey) { - i.keyState[i.screenshotKey]++ - if i.keyState[i.screenshotKey] == 1 { - i.toTakeScreenshot = true + if keyname := os.Getenv("EBITEN_DUMP_IMAGES_KEY"); keyname != "" { + if key, ok := keyNameToKey(keyname); ok { + i.hasDumpImagesKey = true + i.dumpImagesKey = key + } } - } else { - i.keyState[i.screenshotKey] = 0 } - if i.toTakeScreenshot && !IsRunningSlowly() { + keys := map[Key]struct{}{} + if i.hasScreenshotKey { + keys[i.screenshotKey] = struct{}{} + } + if i.hasDumpImagesKey { + keys[i.dumpImagesKey] = struct{}{} + } + + for key := range keys { + if IsKeyPressed(key) { + i.keyState[key]++ + if i.keyState[key] == 1 { + if i.hasScreenshotKey && key == i.screenshotKey { + i.toTakeScreenshot = true + } + if i.hasDumpImagesKey && key == i.dumpImagesKey { + i.toDumpImages = true + } + } + } else { + i.keyState[key] = 0 + } + } + + if IsRunningSlowly() { + return nil + } + + if i.toTakeScreenshot { filename := "screenshot.png" idx := 0 for { @@ -148,6 +180,42 @@ func (i *imageDumper) update(screen *Image) error { } i.toTakeScreenshot = false } + + if i.toDumpImages { + dir, err := ioutil.TempDir("", "ebiten_textures") + if err != nil { + return err + } + + dump := func(img image.Image, index int) error { + filename := filepath.Join(dir, fmt.Sprintf("%d.png", index)) + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + if err := png.Encode(f, img); err != nil { + return err + } + return nil + } + + images, err := shareable.Images() + if err != nil { + return err + } + for i, img := range images { + if err := dump(img, i); err != nil { + return err + } + } + + i.toDumpImages = false + + fmt.Fprintf(os.Stderr, "Dumped texture at: %s\n", dir) + } + return nil }