|
|
|
@ -1,15 +1,21 @@
|
|
|
|
|
package application
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"image"
|
|
|
|
|
"image/color"
|
|
|
|
|
"image/gif"
|
|
|
|
|
"image/jpeg"
|
|
|
|
|
"image/png"
|
|
|
|
|
"log"
|
|
|
|
|
"math"
|
|
|
|
|
"os"
|
|
|
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
|
|
|
"golang.org/x/image/bmp"
|
|
|
|
|
"golang.org/x/image/colornames"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
@ -22,6 +28,8 @@ type Application struct {
|
|
|
|
|
|
|
|
|
|
canvases []*canvas
|
|
|
|
|
|
|
|
|
|
active int
|
|
|
|
|
|
|
|
|
|
spinnerIndex int
|
|
|
|
|
|
|
|
|
|
hoverImg *ebiten.Image
|
|
|
|
@ -30,35 +38,95 @@ type Application struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewApplication() *Application {
|
|
|
|
|
a := &Application{
|
|
|
|
|
app := &Application{
|
|
|
|
|
op: &ebiten.DrawImageOptions{},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a.addCanvas(8, 8)
|
|
|
|
|
app.addNewCanvas(8, 8)
|
|
|
|
|
|
|
|
|
|
c := app.activeCanvas()
|
|
|
|
|
c.img.Fill(colornames.Purple)
|
|
|
|
|
c.scale = 256
|
|
|
|
|
|
|
|
|
|
app.drawHoverImage()
|
|
|
|
|
|
|
|
|
|
a.canvases[0].img.Fill(colornames.Purple)
|
|
|
|
|
return app
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a.activeCanvas().scale = 256
|
|
|
|
|
a.hoverImg = ebiten.NewImage(int(a.canvases[0].scale), int(a.canvases[0].scale)) // TODO
|
|
|
|
|
a.drawHoverImage()
|
|
|
|
|
func (app *Application) readFile(p string) (image.Image, error) {
|
|
|
|
|
f, err := os.OpenFile(p, os.O_RDONLY, 0600)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer f.Close()
|
|
|
|
|
p = strings.ToLower(p)
|
|
|
|
|
if strings.HasSuffix(p, ".bmp") {
|
|
|
|
|
img, err := bmp.Decode(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to decode BMP file: %s", err)
|
|
|
|
|
}
|
|
|
|
|
return img, nil
|
|
|
|
|
} else if strings.HasSuffix(p, ".gif") {
|
|
|
|
|
img, err := gif.Decode(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to decode GIF file: %s", err)
|
|
|
|
|
}
|
|
|
|
|
return img, nil
|
|
|
|
|
} else if strings.HasSuffix(p, ".jpg") || strings.HasSuffix(p, ".jpeg") {
|
|
|
|
|
img, err := jpeg.Decode(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to decode JPG file: %s", err)
|
|
|
|
|
}
|
|
|
|
|
return img, nil
|
|
|
|
|
} else if strings.HasSuffix(p, ".png") {
|
|
|
|
|
img, err := png.Decode(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to decode PNG file: %s", err)
|
|
|
|
|
}
|
|
|
|
|
return img, nil
|
|
|
|
|
} else if strings.HasSuffix(p, ".tff") || strings.HasSuffix(p, ".tiff") {
|
|
|
|
|
img, err := png.Decode(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to decode PNG file: %s", err)
|
|
|
|
|
}
|
|
|
|
|
return img, nil
|
|
|
|
|
}
|
|
|
|
|
return nil, errors.New("unknown image format")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return a
|
|
|
|
|
func (app *Application) OpenFile(p string) {
|
|
|
|
|
img, err := app.readFile(p)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
c := NewCanvas(img.Bounds().Dx(), img.Bounds().Dy())
|
|
|
|
|
c.img = ebiten.NewImageFromImage(img)
|
|
|
|
|
app.addCanvas(c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (app *Application) addCanvas(w, h int) {
|
|
|
|
|
func (app *Application) addCanvas(c *canvas) {
|
|
|
|
|
app.canvases = append(app.canvases, c)
|
|
|
|
|
app.active++
|
|
|
|
|
|
|
|
|
|
app.drawHoverImage()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (app *Application) addNewCanvas(w, h int) {
|
|
|
|
|
c := NewCanvas(w, h)
|
|
|
|
|
app.canvases = append(app.canvases, c)
|
|
|
|
|
|
|
|
|
|
app.drawHoverImage()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (app *Application) activeCanvas() *canvas {
|
|
|
|
|
return app.canvases[0]
|
|
|
|
|
return app.canvases[app.active]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (app *Application) screenToCanvas(x, y int) (int, int) {
|
|
|
|
|
c := app.activeCanvas()
|
|
|
|
|
|
|
|
|
|
cx, cy := int(float64(x)/c.scale), int(float64(y)/c.scale)
|
|
|
|
|
return cx, cy
|
|
|
|
|
cx, cy := float64(x)/c.scale, float64(y)/c.scale
|
|
|
|
|
cx, cy = cx+c.x, cy+c.y
|
|
|
|
|
return int(cx), int(cy)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (app *Application) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
|
|
|
|
@ -86,20 +154,22 @@ func (app *Application) Update() error {
|
|
|
|
|
scroll = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
offset := scroll * (app.canvases[0].scale / 7)
|
|
|
|
|
app.canvases[0].scale += offset
|
|
|
|
|
if app.canvases[0].scale < .001 {
|
|
|
|
|
app.canvases[0].scale = .001
|
|
|
|
|
} else if app.canvases[0].scale > 1000 {
|
|
|
|
|
app.canvases[0].scale = 1000
|
|
|
|
|
c := app.activeCanvas()
|
|
|
|
|
|
|
|
|
|
offset := scroll * (c.scale / 7)
|
|
|
|
|
c.scale += offset
|
|
|
|
|
if c.scale < .001 {
|
|
|
|
|
c.scale = .001
|
|
|
|
|
} else if c.scale > 1000 {
|
|
|
|
|
c.scale = 1000
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hoverSize := int(math.Ceil(app.canvases[0].scale))
|
|
|
|
|
if hoverSize < 1 {
|
|
|
|
|
hoverSize = 1
|
|
|
|
|
if c.scale > 10 {
|
|
|
|
|
c.scale = float64(int(c.scale))
|
|
|
|
|
} else {
|
|
|
|
|
c.scale = math.Trunc(c.scale*10000) / 10000
|
|
|
|
|
}
|
|
|
|
|
hoverSize += 2
|
|
|
|
|
app.hoverImg = ebiten.NewImage(hoverSize, hoverSize)
|
|
|
|
|
|
|
|
|
|
app.drawHoverImage()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -111,29 +181,43 @@ func (app *Application) Update() error {
|
|
|
|
|
c.img.Set(cx, cy, colornames.White)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c := app.activeCanvas()
|
|
|
|
|
panSize := 2 / c.scale
|
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyRight) {
|
|
|
|
|
c.x += panSize
|
|
|
|
|
}
|
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyLeft) {
|
|
|
|
|
c.x -= panSize
|
|
|
|
|
}
|
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyUp) {
|
|
|
|
|
c.y -= panSize
|
|
|
|
|
}
|
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyDown) {
|
|
|
|
|
c.y += panSize
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (app *Application) Draw(screen *ebiten.Image) {
|
|
|
|
|
// TODO translate and scale by camera pos
|
|
|
|
|
// Draw canvas.
|
|
|
|
|
c := app.activeCanvas()
|
|
|
|
|
app.op.GeoM.Reset()
|
|
|
|
|
app.op.GeoM.Translate(-c.x, -c.y)
|
|
|
|
|
app.op.GeoM.Scale(c.scale, c.scale)
|
|
|
|
|
screen.DrawImage(c.img, app.op)
|
|
|
|
|
|
|
|
|
|
// Draw hover outline.
|
|
|
|
|
cx, cy := app.screenToCanvas(app.cursorX, app.cursorY)
|
|
|
|
|
p := c.pixelScreenRect(cx, cy)
|
|
|
|
|
|
|
|
|
|
app.op.GeoM.Reset()
|
|
|
|
|
app.op.GeoM.Translate(float64(p.Min.X)-1, float64(p.Min.Y)-1)
|
|
|
|
|
app.op.GeoM.Translate(float64(p.Min.X), float64(p.Min.Y))
|
|
|
|
|
screen.DrawImage(app.hoverImg, app.op)
|
|
|
|
|
|
|
|
|
|
// Print debug information.
|
|
|
|
|
debugInfo := fmt.Sprintf("SCA %0.2f\nFPS %c %0.0f", app.activeCanvas().scale, spinner[app.spinnerIndex], ebiten.CurrentFPS())
|
|
|
|
|
|
|
|
|
|
debugBox := image.NewRGBA(image.Rect(10, 20, 200, 200))
|
|
|
|
|
debugImg := ebiten.NewImageFromImage(debugBox)
|
|
|
|
|
ebitenutil.DebugPrint(debugImg, debugInfo)
|
|
|
|
|
|
|
|
|
|
app.op.GeoM.Reset()
|
|
|
|
|
app.op.GeoM.Translate(3, 0)
|
|
|
|
|
app.op.GeoM.Scale(2, 2)
|
|
|
|
@ -146,6 +230,15 @@ func (app *Application) Draw(screen *ebiten.Image) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (app *Application) drawHoverImage() {
|
|
|
|
|
c := app.activeCanvas()
|
|
|
|
|
hoverImgSize := int(c.scale)
|
|
|
|
|
if hoverImgSize < 1 {
|
|
|
|
|
hoverImgSize = 1
|
|
|
|
|
}
|
|
|
|
|
if app.hoverImg == nil || app.hoverImg.Bounds().Dx() != hoverImgSize || app.hoverImg.Bounds().Dy() != hoverImgSize {
|
|
|
|
|
app.hoverImg = ebiten.NewImage(hoverImgSize, hoverImgSize)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var colorWhite bool
|
|
|
|
|
nextColor := func() color.Color {
|
|
|
|
|
colorWhite = !colorWhite
|
|
|
|
@ -160,24 +253,24 @@ func (app *Application) drawHoverImage() {
|
|
|
|
|
img.Clear()
|
|
|
|
|
bounds := img.Bounds()
|
|
|
|
|
|
|
|
|
|
hoverSize := 2
|
|
|
|
|
outlineWidth := 2
|
|
|
|
|
if app.activeCanvas().scale >= 1000 {
|
|
|
|
|
hoverSize = 6
|
|
|
|
|
outlineWidth = 6
|
|
|
|
|
} else if app.activeCanvas().scale >= 200 {
|
|
|
|
|
hoverSize = 5
|
|
|
|
|
outlineWidth = 5
|
|
|
|
|
} else if app.activeCanvas().scale >= 100 {
|
|
|
|
|
hoverSize = 4
|
|
|
|
|
outlineWidth = 4
|
|
|
|
|
} else if app.activeCanvas().scale >= 10 {
|
|
|
|
|
hoverSize = 3
|
|
|
|
|
outlineWidth = 3
|
|
|
|
|
}
|
|
|
|
|
for y := 0; y < hoverSize; y++ {
|
|
|
|
|
for y := 0; y < outlineWidth; y++ {
|
|
|
|
|
for x := 0; x < bounds.Max.X; x++ {
|
|
|
|
|
c := nextColor()
|
|
|
|
|
img.Set(x, bounds.Min.Y+y, c)
|
|
|
|
|
img.Set(x, bounds.Max.Y-y, c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for x := 0; x < hoverSize; x++ {
|
|
|
|
|
for x := 0; x < outlineWidth; x++ {
|
|
|
|
|
for y := 0; y < bounds.Max.Y; y++ {
|
|
|
|
|
c := nextColor()
|
|
|
|
|
img.Set(bounds.Min.X+x, y, c)
|
|
|
|
|