From 3abbe26f7f50251edb90adc10a4671b192b25009 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 14 Nov 2018 00:34:26 +0900 Subject: [PATCH] Remove dependency on internal/png on mobiles and browsers Fixes #735 --- doc.go | 4 +- imagedumper_desktop.go | 213 ++++++++++++++++++++++++++++++++++++++ imagedumper_notdesktop.go | 25 +++++ run.go | 172 ------------------------------ 4 files changed, 240 insertions(+), 174 deletions(-) create mode 100644 imagedumper_desktop.go create mode 100644 imagedumper_notdesktop.go diff --git a/doc.go b/doc.go index 2317b6d88..bcb3f26c8 100644 --- a/doc.go +++ b/doc.go @@ -40,11 +40,11 @@ // 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. +// by pressing Q key. This works only on desktops. // // The EBITEN_INTERNAL_IMAGES_KEY environment variable specifies the key // to dump all the internal images. This is valid only when the build tag -// 'ebitendebug' is specified. +// 'ebitendebug' is specified. This works only on desktops. // // In the API document, 'the main thread' means the goroutine in init(), main() and their callees without 'go' // statement. It is assured that 'the main thread' runs on the OS main thread. There are some Ebiten functions that diff --git a/imagedumper_desktop.go b/imagedumper_desktop.go new file mode 100644 index 000000000..655cc9e78 --- /dev/null +++ b/imagedumper_desktop.go @@ -0,0 +1,213 @@ +// Copyright 2018 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !android +// +build !js +// +build !ios + +package ebiten + +import ( + "fmt" + "image" + "io/ioutil" + "os" + "path/filepath" + "time" + + "github.com/hajimehoshi/ebiten/internal/png" + "github.com/hajimehoshi/ebiten/internal/shareable" +) + +// availableFilename returns a filename that is valid as a new file or directory. +func availableFilename(prefix, postfix string) (string, error) { + const datetimeFormat = "20060102030405" + + now := time.Now() + name := fmt.Sprintf("%s%s%s", prefix, now.Format(datetimeFormat), postfix) + for i := 1; ; i++ { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + break + } + if !os.IsNotExist(err) { + return "", err + } + } + name = fmt.Sprintf("%s%s_%d%s", prefix, now.Format(datetimeFormat), i, postfix) + } + return name, nil +} + +func takeScreenshot(screen *Image) error { + dump := func() (string, error) { + f, err := ioutil.TempFile("", "") + if err != nil { + return "", err + } + defer f.Close() + + if err := png.Encode(f, screen); err != nil { + return "", err + } + return f.Name(), nil + } + + name, err := dump() + if err != nil { + return err + } + + newname, err := availableFilename("screenshot_", ".png") + if err != nil { + return err + } + + if err := os.Rename(name, newname); err != nil { + return err + } + + if _, err := fmt.Fprintf(os.Stderr, "Saved screenshot: %s\n", newname); err != nil { + return err + } + return nil +} + +func dumpInternalImages() error { + dir, err := availableFilename("internalimages_", "") + if err != nil { + return err + } + + if err := os.Mkdir(dir, 0755); 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 + } + + for i, img := range shareable.Images() { + if err := dump(img, i); err != nil { + return err + } + } + + if _, err := fmt.Fprintf(os.Stderr, "Dumped the internal images at: %s\n", dir); err != nil { + return err + } + return nil +} + +type imageDumper struct { + f func(screen *Image) error + + keyState map[Key]int + + hasScreenshotKey bool + screenshotKey Key + toTakeScreenshot bool + + hasDumpInternalImagesKey bool + dumpInternalImagesKey Key + toDumpInternalImages bool +} + +func (i *imageDumper) update(screen *Image) error { + const ( + envScreenshotKey = "EBITEN_SCREENSHOT_KEY" + envInternalImagesKey = "EBITEN_INTERNAL_IMAGES_KEY" + ) + + if err := i.f(screen); err != nil { + return err + } + + // If keyState is nil, all values are not initialized. + if i.keyState == nil { + i.keyState = map[Key]int{} + + if keyname := os.Getenv(envScreenshotKey); keyname != "" { + if key, ok := keyNameToKey(keyname); ok { + i.hasScreenshotKey = true + i.screenshotKey = key + } + } + + if keyname := os.Getenv(envInternalImagesKey); keyname != "" { + if isDebug() { + if key, ok := keyNameToKey(keyname); ok { + i.hasDumpInternalImagesKey = true + i.dumpInternalImagesKey = key + } + } else { + fmt.Fprintf(os.Stderr, "%s is disabled. Specify a build tag 'ebitendebug' to enable it.\n", envInternalImagesKey) + } + } + } + + keys := map[Key]struct{}{} + if i.hasScreenshotKey { + keys[i.screenshotKey] = struct{}{} + } + if i.hasDumpInternalImagesKey { + keys[i.dumpInternalImagesKey] = 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.hasDumpInternalImagesKey && key == i.dumpInternalImagesKey { + i.toDumpInternalImages = true + } + } + } else { + i.keyState[key] = 0 + } + } + + if IsDrawingSkipped() { + return nil + } + + if i.toTakeScreenshot { + i.toTakeScreenshot = false + if err := takeScreenshot(screen); err != nil { + return err + } + } + + if i.toDumpInternalImages { + i.toDumpInternalImages = false + if err := dumpInternalImages(); err != nil { + return err + } + } + + return nil +} diff --git a/imagedumper_notdesktop.go b/imagedumper_notdesktop.go new file mode 100644 index 000000000..5bf8c2a38 --- /dev/null +++ b/imagedumper_notdesktop.go @@ -0,0 +1,25 @@ +// Copyright 2018 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build android js ios + +package ebiten + +type imageDumper struct { + f func(screen *Image) error +} + +func (i *imageDumper) update(screen *Image) error { + return i.f(screen) +} diff --git a/run.go b/run.go index e73f5642f..80d38b478 100644 --- a/run.go +++ b/run.go @@ -15,17 +15,10 @@ package ebiten import ( - "fmt" "image" - "io/ioutil" - "os" - "path/filepath" "sync/atomic" - "time" "github.com/hajimehoshi/ebiten/internal/clock" - "github.com/hajimehoshi/ebiten/internal/png" - "github.com/hajimehoshi/ebiten/internal/shareable" "github.com/hajimehoshi/ebiten/internal/ui" ) @@ -107,171 +100,6 @@ func run(width, height int, scale float64, title string, g *graphicsContext, mai return nil } -type imageDumper struct { - f func(screen *Image) error - - keyState map[Key]int - - hasScreenshotKey bool - screenshotKey Key - toTakeScreenshot bool - - hasDumpInternalImagesKey bool - dumpInternalImagesKey Key - toDumpInternalImages bool -} - -// availableFilename returns a filename that is valid as a new file or directory. -func availableFilename(prefix, postfix string) (string, error) { - const datetimeFormat = "20060102030405" - - now := time.Now() - name := fmt.Sprintf("%s%s%s", prefix, now.Format(datetimeFormat), postfix) - for i := 1; ; i++ { - if _, err := os.Stat(name); err != nil { - if os.IsNotExist(err) { - break - } - if !os.IsNotExist(err) { - return "", err - } - } - name = fmt.Sprintf("%s%s_%d%s", prefix, now.Format(datetimeFormat), i, postfix) - } - return name, nil -} - -func (i *imageDumper) update(screen *Image) error { - const ( - envScreenshotKey = "EBITEN_SCREENSHOT_KEY" - envInternalImagesKey = "EBITEN_INTERNAL_IMAGES_KEY" - ) - - if err := i.f(screen); err != nil { - return err - } - - // If keyState is nil, all values are not initialized. - if i.keyState == nil { - i.keyState = map[Key]int{} - - if keyname := os.Getenv(envScreenshotKey); keyname != "" { - if key, ok := keyNameToKey(keyname); ok { - i.hasScreenshotKey = true - i.screenshotKey = key - } - } - - if keyname := os.Getenv(envInternalImagesKey); keyname != "" { - if isDebug() { - if key, ok := keyNameToKey(keyname); ok { - i.hasDumpInternalImagesKey = true - i.dumpInternalImagesKey = key - } - } else { - fmt.Fprintf(os.Stderr, "%s is disabled. Specify a build tag 'ebitendebug' to enable it.\n", envInternalImagesKey) - } - } - } - - keys := map[Key]struct{}{} - if i.hasScreenshotKey { - keys[i.screenshotKey] = struct{}{} - } - if i.hasDumpInternalImagesKey { - keys[i.dumpInternalImagesKey] = 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.hasDumpInternalImagesKey && key == i.dumpInternalImagesKey { - i.toDumpInternalImages = true - } - } - } else { - i.keyState[key] = 0 - } - } - - if IsDrawingSkipped() { - return nil - } - - if i.toTakeScreenshot { - dump := func() (string, error) { - f, err := ioutil.TempFile("", "") - if err != nil { - return "", err - } - defer f.Close() - - if err := png.Encode(f, screen); err != nil { - return "", err - } - return f.Name(), nil - } - - name, err := dump() - if err != nil { - return err - } - - newname, err := availableFilename("screenshot_", ".png") - if err != nil { - return err - } - - if err := os.Rename(name, newname); err != nil { - return err - } - - i.toTakeScreenshot = false - fmt.Fprintf(os.Stderr, "Saved screenshot: %s\n", newname) - } - - if i.toDumpInternalImages { - dir, err := availableFilename("internalimages_", "") - if err != nil { - return err - } - - if err := os.Mkdir(dir, 0755); 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 - } - - for i, img := range shareable.Images() { - if err := dump(img, i); err != nil { - return err - } - } - - i.toDumpInternalImages = false - - fmt.Fprintf(os.Stderr, "Dumped the internal images at: %s\n", dir) - } - - return nil -} - // Run runs the game. // f is a function which is called at every frame. // The argument (*Image) is the render target that represents the screen.