Skip to content

Commit d2ce648

Browse files
Christopher MurphyChristopher Murphy
authored andcommitted
fix: Optimize PNG compression for large files.
1 parent d1a9997 commit d2ce648

File tree

7 files changed

+26
-16
lines changed

7 files changed

+26
-16
lines changed

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Vue 2 + Vuex. `App.vue` is the root with a sidebar + view router pattern (no vue
5656
Key components: `Editor.vue` (drag-drop + file list), `Settings.vue` (quality/size/format config), `Stats.vue` (lifetime stats display).
5757

5858
## Key Dependencies
59-
- **Go**: `wailsapp/wails/v2`, `chai2010/webp`, `disintegration/imaging`, `muesli/smartcrop`, `foobaz/lossypng`
59+
- **Go**: `wailsapp/wails/v2`, `chai2010/webp`, `disintegration/imaging`, `muesli/smartcrop`
6060
- **JS**: Vue 2.6, Vuex 3, Tailwind CSS 1.9
6161

6262
## Wails v2 Notes

backend/image/file.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"os"
1414
"path"
1515
"path/filepath"
16+
"time"
1617

1718
"github.com/disintegration/imaging"
1819
"github.com/muesli/smartcrop"
@@ -130,14 +131,20 @@ func (f *File) Write(c *config.Config, ctx context.Context) error {
130131
}
131132
}
132133
}
134+
t0 := time.Now()
133135
buf, err := encToBuf(f.Image, c.App)
136+
runtime.LogInfof(ctx, "perf: encode %s (%dx%d) to %s: %s", f.Name,
137+
f.Image.Bounds().Max.X, f.Image.Bounds().Max.Y,
138+
c.App.Target, time.Since(t0))
134139
dest := path.Join(c.App.OutDir, c.App.Prefix+f.Name+c.App.Suffix+"."+c.App.Target)
135140
if err != nil {
136141
return err
137142
}
143+
t1 := time.Now()
138144
if err = os.WriteFile(dest, buf.Bytes(), 0666); err != nil {
139145
return err
140146
}
147+
runtime.LogInfof(ctx, "perf: write %s (%d bytes): %s", dest, len(buf.Bytes()), time.Since(t1))
141148
f.ConvertedFile = filepath.Clean(dest)
142149
f.IsConverted = true
143150
return nil

backend/image/filemanager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ func (fm *FileManager) SelectFiles() ([]SelectFileMeta, error) {
110110
runtime.LogErrorf(fm.ctx, "failed to decode file %s: %v", p, err)
111111
continue
112112
}
113+
file.Data = nil // release raw bytes; image is now in file.Image
113114
fm.Files = append(fm.Files, file)
114115
runtime.LogInfof(fm.ctx, "added file to file manager via dialog: %s", file.Name)
115116

backend/png/png.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ package png
22

33
import (
44
"bytes"
5-
"github.com/foobaz/lossypng/lossypng"
65
"image"
76
"image/png"
87
"io"
98
)
109

11-
const qMax = 20
12-
1310
// Options represent PNG encoding options.
1411
type Options struct {
1512
Quality int `json:"quality"`
@@ -25,15 +22,23 @@ func DecodePNG(r io.Reader) (image.Image, error) {
2522
}
2623

2724
// EncodePNG encodes an image into PNG and returns a buffer.
25+
// Lossless compression is used; the Quality option controls the compression
26+
// level (higher quality = less compression effort = faster encode).
2827
func EncodePNG(i image.Image, o *Options) (buf bytes.Buffer, err error) {
29-
c := lossypng.Compress(i, 2, qualityFactor(o.Quality))
30-
err = png.Encode(&buf, c)
28+
enc := &png.Encoder{CompressionLevel: compressionLevel(o.Quality)}
29+
err = enc.Encode(&buf, i)
3130
return buf, err
3231
}
3332

34-
// qualityFactor normalizes the PNG quality factor from a max of 20, where 0 is
35-
// no conversion.
36-
func qualityFactor(q int) int {
37-
f := q / 100
38-
return qMax - (f * qMax)
33+
// compressionLevel maps a quality value (0–100) to a png.CompressionLevel.
34+
// Lower quality = more aggressive compression (smaller file, slower encode).
35+
func compressionLevel(q int) png.CompressionLevel {
36+
switch {
37+
case q >= 80:
38+
return png.BestSpeed
39+
case q >= 40:
40+
return png.DefaultCompression
41+
default:
42+
return png.BestCompression
43+
}
3944
}

backend/webp/webp.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type Options struct {
1313
Quality int `json:"quality"`
1414
}
1515

16-
// DecodeWebp a webp file and return an image.
16+
// DecodeWebp decodes a webp file and returns an image.
1717
func DecodeWebp(r io.Reader) (image.Image, error) {
1818
i, err := webp.Decode(r)
1919
if err != nil {
@@ -22,7 +22,7 @@ func DecodeWebp(r io.Reader) (image.Image, error) {
2222
return i, nil
2323
}
2424

25-
// EncodeWebp encodes an image into webp and returns a buffer.
25+
// EncodeWebp encodes an image into WebP and returns a buffer.
2626
func EncodeWebp(i image.Image, o *Options) (buf bytes.Buffer, err error) {
2727
if err = webp.Encode(&buf, i, &webp.Options{Lossless: o.Lossless, Quality: float32(o.Quality)}); err != nil {
2828
return buf, err

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ go 1.22.0
55
require (
66
github.com/chai2010/webp v1.1.0
77
github.com/disintegration/imaging v1.6.2
8-
github.com/foobaz/lossypng v0.0.0-20200814224715-48fa8819852a
98
github.com/muesli/smartcrop v0.3.0
109
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
1110
github.com/wailsapp/wails/v2 v2.11.0

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
66
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
77
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
88
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
9-
github.com/foobaz/lossypng v0.0.0-20200814224715-48fa8819852a h1:0TYY/syyvt/+y5PWAkybgG2o6zHY+UrI3fuixaSeRoI=
10-
github.com/foobaz/lossypng v0.0.0-20200814224715-48fa8819852a/go.mod h1:wRxTcIExb9GZAgOr1wrQuOZBkyoZNQi7znUmeyKTciA=
119
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
1210
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
1311
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=

0 commit comments

Comments
 (0)