139 lines
2.8 KiB
Go
139 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
_ "embed"
|
|
"fmt"
|
|
"image"
|
|
"image/draw"
|
|
"image/jpeg"
|
|
"image/png"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"slices"
|
|
"strings"
|
|
"time"
|
|
|
|
tk "modernc.org/tk9.0"
|
|
)
|
|
|
|
//go:embed gopher.png
|
|
var gopher []byte
|
|
|
|
var validFileTypes = []tk.FileType{
|
|
{
|
|
TypeName: "JPEG",
|
|
Extensions: []string{".jpg", ".jpeg"},
|
|
},
|
|
{
|
|
TypeName: "GIF",
|
|
Extensions: []string{".gif"},
|
|
},
|
|
{
|
|
TypeName: "PNG",
|
|
Extensions: []string{".png"},
|
|
},
|
|
{
|
|
TypeName: "SVG",
|
|
Extensions: []string{".svg"},
|
|
},
|
|
}
|
|
|
|
var metaPressed bool
|
|
|
|
func isType(filename string, desired string) bool {
|
|
splode := strings.Split(filename, ".")
|
|
if len(splode) < 2 {
|
|
return false
|
|
}
|
|
ext := fmt.Sprintf(".%s", splode[len(splode)-1])
|
|
for _, ft := range validFileTypes {
|
|
if slices.Contains(ft.Extensions, ext) && ft.TypeName == desired {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func jpegToPng(in io.Reader) (*bytes.Buffer, error) {
|
|
start := time.Now()
|
|
buf := new(bytes.Buffer)
|
|
|
|
img, err := jpeg.Decode(in)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to decode jpeg: %w", err)
|
|
}
|
|
|
|
b := img.Bounds()
|
|
dst := image.NewNRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
|
|
draw.Draw(dst, dst.Bounds(), img, b.Min, draw.Src)
|
|
|
|
if err := (&png.Encoder{
|
|
CompressionLevel: -2,
|
|
}).Encode(buf, dst); err != nil {
|
|
return nil, fmt.Errorf("unable to encode png: %w", err)
|
|
}
|
|
end := time.Now()
|
|
log.Printf("jpeg to png took %v", end.Sub(start))
|
|
return buf, nil
|
|
}
|
|
|
|
func loadImage(img *tk.LabelWidget) func() {
|
|
return func() {
|
|
files := tk.GetOpenFile(tk.Filetypes(validFileTypes))
|
|
if len(files) > 0 {
|
|
f, err := os.Open(files[0])
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return
|
|
}
|
|
var r io.Reader
|
|
if isType(files[0], "JPEG") {
|
|
r, err = jpegToPng(f)
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return
|
|
}
|
|
} else {
|
|
r = f
|
|
}
|
|
i, err := io.ReadAll(r)
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return
|
|
}
|
|
img.Configure(tk.Image(tk.NewPhoto(tk.Data(i))))
|
|
}
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
img := tk.Label(tk.Image(tk.NewPhoto(tk.Data(gopher))))
|
|
|
|
menubar := tk.Menu()
|
|
fileMenu := menubar.Menu()
|
|
fileMenu.AddCommand(tk.Lbl("Open"), tk.Underline(0), tk.Accelerator("Meta+O"), tk.Command(loadImage(img)))
|
|
menubar.AddCascade(tk.Lbl("File"), tk.Underline(0), tk.Mnu(fileMenu))
|
|
|
|
tk.Bind(tk.App, "<KeyPress>", tk.Command(func(e *tk.Event) {
|
|
if e.Keysym == "Meta_L" || e.Keysym == "Meta_R" {
|
|
metaPressed = true
|
|
}
|
|
if e.Keysym == "o" && metaPressed {
|
|
loadImage(img)()
|
|
}
|
|
}))
|
|
tk.Bind(tk.App, "<KeyRelease>", tk.Command(func(e *tk.Event) {
|
|
if e.Keysym == "Meta_L" || e.Keysym == "Meta_R" {
|
|
metaPressed = false
|
|
}
|
|
}))
|
|
tk.Bind(tk.App, "<TouchpadScroll>", tk.Command(func(e *tk.Event) {
|
|
// TODO: this event doesn't contain deltas, which means I can't tell if I'm scrolling "in" or "out"
|
|
}))
|
|
|
|
tk.Pack(img)
|
|
tk.App.Configure(tk.Mnu(menubar)).Center().Wait()
|
|
}
|