|
|
|
@ -1,15 +1,21 @@
@@ -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 {
@@ -22,6 +28,8 @@ type Application struct {
|
|
|
|
|
|
|
|
|
|
canvases []*canvas |
|
|
|
|
|
|
|
|
|
active int |
|
|
|
|
|
|
|
|
|
spinnerIndex int |
|
|
|
|
|
|
|
|
|
hoverImg *ebiten.Image |
|
|
|
@ -30,35 +38,95 @@ type Application struct {
@@ -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 {
@@ -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 {
@@ -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) {
@@ -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() {
@@ -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) |
|
|
|
|