diff --git a/imagedumper_notdesktop.go b/imagedumper_mobile.go similarity index 93% rename from imagedumper_notdesktop.go rename to imagedumper_mobile.go index c598ffde1..6b6a6ef81 100644 --- a/imagedumper_notdesktop.go +++ b/imagedumper_mobile.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build android || ios || js -// +build android ios js +//go:build android || ios +// +build android ios package ebiten diff --git a/imagedumper_desktop.go b/imagedumper_notmobile.go similarity index 96% rename from imagedumper_desktop.go rename to imagedumper_notmobile.go index 7a0e25043..45271d5fe 100644 --- a/imagedumper_desktop.go +++ b/imagedumper_notmobile.go @@ -12,14 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !android && !ios && !js -// +build !android,!ios,!js +//go:build !android && !ios +// +build !android,!ios package ebiten import ( + "errors" "fmt" "os" + "syscall" "time" "github.com/hajimehoshi/ebiten/v2/internal/debug" @@ -34,7 +36,7 @@ func availableFilename(prefix, postfix string) (string, error) { 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) { + if os.IsNotExist(err) || errors.Is(err, syscall.ENOSYS) { break } if !os.IsNotExist(err) { diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 9bd3cd93d..724573c76 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -15,11 +15,9 @@ package graphicscommand import ( - "fmt" "image" - "os" + "io" "sort" - "strings" "github.com/hajimehoshi/ebiten/v2/internal/affine" "github.com/hajimehoshi/ebiten/v2/internal/debug" @@ -190,25 +188,17 @@ func (i *Image) IsInvalidated() bool { return i.image.IsInvalidated() } -// Dump dumps the image to the specified path. -// In the path, '*' is replaced with the image's ID. +// dumpTo dumps the image to the specified writer. // // If blackbg is true, any alpha values in the dumped image will be 255. // // This is for testing usage. -func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool, rect image.Rectangle) error { +func (i *Image) dumpTo(w io.Writer, graphicsDriver graphicsdriver.Graphics, blackbg bool, rect image.Rectangle) error { // Screen image cannot be dumped. if i.screen { return nil } - path = strings.ReplaceAll(path, "*", fmt.Sprintf("%d", i.id)) - f, err := os.Create(path) - if err != nil { - return err - } - defer f.Close() - pix := make([]byte, 4*i.width*i.height) if err := i.ReadPixels(graphicsDriver, pix); err != nil { return err @@ -220,13 +210,14 @@ func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackb } } - if err := png.Encode(f, (&image.RGBA{ + if err := png.Encode(w, (&image.RGBA{ Pix: pix, Stride: 4 * i.width, Rect: image.Rect(0, 0, i.width, i.height), }).SubImage(rect)); err != nil { return err } + return nil } diff --git a/internal/graphicscommand/image_js.go b/internal/graphicscommand/image_js.go new file mode 100644 index 000000000..8a6a12aa1 --- /dev/null +++ b/internal/graphicscommand/image_js.go @@ -0,0 +1,58 @@ +// Copyright 2022 The Ebitengine 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. + +package graphicscommand + +import ( + "bytes" + "image" + "path/filepath" + "strconv" + "strings" + "syscall/js" + + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" +) + +func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool, rect image.Rectangle) error { + // Screen image cannot be dumped. + if i.screen { + return nil + } + + path = strings.ReplaceAll(path, "*", strconv.Itoa(i.id)) + path = filepath.Base(path) + + global := js.Global() + + buf := &bytes.Buffer{} + if err := i.dumpTo(buf, graphicsDriver, blackbg, rect); err != nil { + return err + } + + jsData := global.Get("Uint8Array").New(buf.Len()) + js.CopyBytesToJS(jsData, buf.Bytes()) + + a := global.Get("document").Call("createElement", "a") + blob := global.Get("Blob").New( + []interface{}{jsData}, + map[string]interface{}{"type": "image/png"}, + ) + a.Set("href", global.Get("URL").Call("createObjectURL", blob)) + a.Set("download", path) + a.Call("click") + + return nil +} + diff --git a/internal/graphicscommand/image_notjs.go b/internal/graphicscommand/image_notjs.go new file mode 100644 index 000000000..10ba4afc1 --- /dev/null +++ b/internal/graphicscommand/image_notjs.go @@ -0,0 +1,47 @@ +// Copyright 2022 The Ebitengine 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. + +//go:build !js +// +build !js + +package graphicscommand + +import ( + "image" + "os" + "strconv" + "strings" + + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" +) + +func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool, rect image.Rectangle) error { + // Screen image cannot be dumped. + if i.screen { + return nil + } + + path = strings.ReplaceAll(path, "*", strconv.Itoa(i.id)) + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + if err := i.dumpTo(f, graphicsDriver, blackbg, rect); err != nil { + return err + } + + return nil +}