From 8c09ef11789f31a98f7ca7d396b0413d3b40082d Mon Sep 17 00:00:00 2001 From: Laura Date: Wed, 22 Jan 2025 16:58:09 +0100 Subject: [PATCH] various improvements --- build.cmd | 8 +- ffwebp_test.go | 49 ++++++------ flags.go | 187 -------------------------------------------- format.go | 47 ++++++----- go.mod | 3 +- go.sum | 6 ++ help.go | 73 ++++++++++------- image.go | 31 +++----- log.go | 10 ++- main.go | 69 +++++----------- options.go | 208 +++++++++++++++++++++++++++++++++++++++++++++---- version.go | 2 +- 12 files changed, 329 insertions(+), 364 deletions(-) delete mode 100644 flags.go diff --git a/build.cmd b/build.cmd index 24e4f66..a981f78 100644 --- a/build.cmd +++ b/build.cmd @@ -1,10 +1,6 @@ @echo off -if not exist bin ( - mkdir bin -) - echo Building... -go build -o bin/ffwebp.exe +go build -o %USERPROFILE%/.bin/ffwebp.exe -echo Done \ No newline at end of file +echo Done diff --git a/ffwebp_test.go b/ffwebp_test.go index c0c7ea1..a09f013 100644 --- a/ffwebp_test.go +++ b/ffwebp_test.go @@ -4,9 +4,7 @@ import ( "bytes" "image/png" "log" - "os/exec" - "path/filepath" - "strings" + "os" "testing" ) @@ -28,40 +26,43 @@ var ( ) func TestFFWebP(t *testing.T) { - exe, err := filepath.Abs("bin/ffwebp.exe") - if err != nil { - log.Fatalf("Failed to get absolute path for ffwebp.exe: %v\n", err) - } + opts.Silent = true for _, file := range TestFiles { log.Printf("Testing file: %s\n", file) - cmd := exec.Command(exe, "-i", file, "-f", "png", "-s") - - // Capture the output (which is expected to be a PNG image) - var stdout bytes.Buffer - - cmd.Stdout = &stdout - - // Run the command - err := cmd.Run() + in, err := os.OpenFile(file, os.O_RDONLY, 0) if err != nil { - out := strings.TrimSpace(stdout.String()) - - log.Println(" - FAILED") - log.Fatalf("Test failed for file: %s (%s)\n", file, out) + log.Fatalf("Failed to read %s: %v", file, err) } - // Decode the captured stdout output as a PNG image - img, err := png.Decode(&stdout) + defer in.Close() + + img, err := ReadImage(in) + if err != nil { + log.Fatalf("Failed to decode %s: %v", file, err) + } + + before := img.Bounds() + + var result bytes.Buffer + + err = WriteImage(&result, img, "png") + if err != nil { + log.Fatalf("Failed to encode png image: %v", err) + } + + img, err = png.Decode(&result) if err != nil { log.Println(" - FAILED") log.Fatalf("Failed to decode PNG image: %v\n", err) } - if img == nil { + after := img.Bounds() + + if before.Max.X != after.Max.X || before.Max.Y != after.Max.Y { log.Println(" - FAILED") - log.Fatalf("No image data returned for file: %s\n", file) + log.Fatalf("Invalid image (%dx%d != %dx%d) for file: %s\n", before.Max.X, before.Max.Y, after.Max.X, after.Max.Y, file) } log.Println(" - PASSED") diff --git a/flags.go b/flags.go deleted file mode 100644 index bfbf545..0000000 --- a/flags.go +++ /dev/null @@ -1,187 +0,0 @@ -package main - -import ( - "os" - "strconv" - "strings" -) - -type Argument struct { - IsNil bool - Name string - Value string -} - -type Arguments struct { - Arguments map[string]Argument -} - -var ( - arguments Arguments -) - -// I don't like golang flags package -func init() { - arguments = Arguments{ - Arguments: make(map[string]Argument), - } - - var ( - arg string - val string - index int - - current Argument - ) - - for i := 1; i < len(os.Args); i++ { - arg = os.Args[i] - - if arg[0] == '-' && len(arg) > 1 { - if arg[1] == '-' { - index = strings.Index(arg[2:], "=") - - if index >= 0 { - val = "" - - if index+1 < len(arg) { - val = arg[2+index+1:] - } - - arguments.Set(Argument{ - Name: arg[2 : 2+index], - Value: val, - }) - } else { - arguments.Set(Argument{ - Name: arg[2:], - }) - } - - current = Argument{} - } else { - current = Argument{ - Name: arg[1:], - } - } - } else { - current.Value = arg - - arguments.Set(current) - - current = Argument{} - } - } - - if current.Name != "" { - arguments.Set(current) - } -} - -func (a *Arguments) Set(arg Argument) { - a.Arguments[arg.Name] = arg -} - -func (a *Arguments) Get(short, long string) Argument { - arg, ok := a.Arguments[short] - - if !ok && long != short { - arg, ok = a.Arguments[long] - } - - if !ok { - return Argument{ - IsNil: true, - Name: long, - } - } - - return arg -} - -func (a *Arguments) GetString(short, long string) string { - return a.Get(short, long).String() -} - -func (a *Arguments) GetBool(short, long string, def bool) bool { - return a.Get(short, long).Bool(def) -} - -func (a *Arguments) GetInt64(short, long string, def, min, max int64) int64 { - return a.Get(short, long).Int64(def, min, max) -} - -func (a *Arguments) GetUint64(short, long string, def, min, max uint64) uint64 { - return a.Get(short, long).Uint64(def, min, max) -} - -func (a *Arguments) GetFloat64(short, long string, def, min, max float64) float64 { - return a.Get(short, long).Float64(def, min, max) -} - -func (a Argument) String() string { - return a.Value -} - -func (a Argument) Bool(def bool) bool { - if a.IsNil { - return def - } - - if a.Value == "false" || a.Value == "0" { - return false - } - - return true -} - -func (a Argument) Int64(def, min, max int64) int64 { - if a.IsNil { - return def - } - - i, err := strconv.ParseInt(a.Value, 10, 64) - if err != nil { - return def - } - - return minmax(i, min, max) -} - -func (a Argument) Uint64(def, min, max uint64) uint64 { - if a.IsNil { - return def - } - - i, err := strconv.ParseUint(a.Value, 10, 64) - if err != nil { - return def - } - - return minmax(i, min, max) -} - -func (a Argument) Float64(def, min, max float64) float64 { - if a.IsNil { - return def - } - - i, err := strconv.ParseFloat(a.Value, 64) - if err != nil { - return def - } - - return minmax(i, min, max) -} - -func minmax[T int64 | uint64 | float64](val, min, max T) T { - if min != 0 && val < min { - return min - } - - if max != 0 && val > max { - return max - } - - return val -} diff --git a/format.go b/format.go index 21fce30..654bb23 100644 --- a/format.go +++ b/format.go @@ -7,11 +7,10 @@ import ( "image/jpeg" "image/png" "io" - "os" "path/filepath" "strings" - "github.com/biessek/golang-ico" + ico "github.com/biessek/golang-ico" "github.com/gen2brain/avif" "github.com/gen2brain/heic" "github.com/gen2brain/jpegxl" @@ -22,35 +21,35 @@ import ( var ( OutputFormats = []string{ - "jpeg", - "png", - "webp", - "gif", - "bmp", - "tiff", "avif", - "jxl", + "bmp", + "gif", "ico", + "jpeg", + "jxl", + "png", + "tiff", + "webp", } InputFormats = []string{ - "jpeg", - "png", - "webp", - "gif", - "bmp", - "tiff", "avif", - "jxl", - "ico", + "bmp", + "gif", "heic", "heif", + "ico", + "jpeg", + "jxl", + "png", + "tiff", + "webp", } ) type Decoder func(io.Reader) (image.Image, error) -func GetDecoderFromContent(in *os.File) (Decoder, error) { +func GetDecoderFromContent(in io.ReadSeeker) (Decoder, error) { buffer := make([]byte, 128) _, err := in.Read(buffer) @@ -139,31 +138,31 @@ func IsJpegXL(buffer []byte) bool { return len(buffer) > 12 && string(buffer[:4]) == "JXL " } -func OutputFormatFromPath(path string) string { +func GetFormatFromPath(path string) string { ext := strings.ToLower(filepath.Ext(path)) switch ext { case ".webp", ".riff": return "webp" - case ".jpg", ".jpeg": + case ".jpg", ".jpeg", ".jpe", ".jif", ".jfif": return "jpeg" case ".png": return "png" - case ".gif": + case ".gif", ".giff": return "gif" - case ".bmp": + case ".bmp", ".dib", ".rle": return "bmp" case ".tiff", ".tif": return "tiff" case ".avif", ".avifs": return "avif" - case ".jxl": + case ".jxl", ".jxls": return "jxl" case ".ico": return "ico" } - return "webp" + return "" } func IsValidOutputFormat(format string) bool { diff --git a/go.mod b/go.mod index 821dd33..ee20998 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,10 @@ module ffwebp -go 1.23.1 +go 1.23.4 require ( github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670 + github.com/coalaura/arguments v1.5.2 github.com/gen2brain/avif v0.3.2 github.com/gen2brain/heic v0.3.1 github.com/gen2brain/jpegxl v0.3.1 diff --git a/go.sum b/go.sum index 1ab44a6..267fd29 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670 h1:FQPKKjDhzG0T4ew6dm6MGrXb4PRAi8ZmTuYuxcF62BM= github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670/go.mod h1:iRWAFbKXMMkVQyxZ1PfGlkBr1TjATx1zy2MRprV7A3Q= +github.com/coalaura/arguments v1.5.0 h1:apdZXxINepy8qCHYKJYEnLHvV6VbYgsIMPbZCWZAPQY= +github.com/coalaura/arguments v1.5.0/go.mod h1:F5cdI+Gn1qi5K6qqvAdxdTD2TXkny+gTKU0o6NN1MlU= +github.com/coalaura/arguments v1.5.1 h1:Gc7uODI3WlcVxmQpxoUQF7Y2j3EMDpSlVqhmGMtLC8Q= +github.com/coalaura/arguments v1.5.1/go.mod h1:F5cdI+Gn1qi5K6qqvAdxdTD2TXkny+gTKU0o6NN1MlU= +github.com/coalaura/arguments v1.5.2 h1:hRLKo6XmAzCDOS/unCUVAIYl3WU/i6QX59nBh0T31cw= +github.com/coalaura/arguments v1.5.2/go.mod h1:F5cdI+Gn1qi5K6qqvAdxdTD2TXkny+gTKU0o6NN1MlU= github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/gen2brain/avif v0.3.2 h1:XUR0CBl5n4ISFJE8/pc1RMEKt5KUVoW8InctN+M7+DQ= diff --git a/help.go b/help.go index 794c510..2a3ee7f 100644 --- a/help.go +++ b/help.go @@ -3,46 +3,63 @@ package main import ( "fmt" "os" - "sort" - "strings" + + "github.com/coalaura/arguments" ) func help() { - if !arguments.GetBool("h", "help", false) { + if !opts.Help { return } - info(" __ __ _") - info(" / _|/ _| | |") - info("| |_| |___ _____| |__ _ __") - info("| _| _\\ \\ /\\ / / _ \\ '_ \\| '_ \\") - info("| | | | \\ V V / __/ |_) | |_) |") - info("|_| |_| \\_/\\_/ \\___|_.__/| .__/") - info(" | |") - info(" %s |_|", Version) + println(" __ __ _") + println(" / _|/ _| | |") + println("| |_| |___ _____| |__ _ __") + println("| _| _\\ \\ /\\ / / _ \\ '_ \\| '_ \\") + println("| | | | \\ V V / __/ |_) | |_) |") + println("|_| |_| \\_/\\_/ \\___|_.__/| .__/") + println(" | |") + fmt.Printf(" %s |_|\n", Version) - info("\nffwebp -i [output] [options]") + println("\nffwebp [options] [output]\n") - var max int + arguments.ShowHelp(true) - for name := range options { - if len(name) > max { - max = len(name) - } - } + b := arguments.NewBuilder(true) - var formatted []string + b.WriteRune('\n') + b.Mute() + b.WriteString(" - ") + b.Name() + b.WriteString("Input formats") + b.Mute() + b.WriteString(": ") + values(b, InputFormats) - for name, help := range options { - formatted = append(formatted, fmt.Sprintf(" - %-*s: %s", max, name, help)) - } + b.WriteRune('\n') + b.Mute() + b.WriteString(" - ") + b.Name() + b.WriteString("Output formats") + b.Mute() + b.WriteString(": ") + values(b, OutputFormats) - sort.Strings(formatted) - - info(strings.Join(formatted, "\n")) - - info("\nInput formats: %s", strings.Join(InputFormats, ", ")) - info("Output formats: %s", strings.Join(OutputFormats, ", ")) + println(b.String()) os.Exit(0) } + +func values(b *arguments.Builder, v []string) { + for i, value := range v { + if i > 0 { + b.Mute() + b.WriteString(", ") + } + + b.Value() + b.WriteString(value) + } + + b.Reset() +} diff --git a/image.go b/image.go index 664a656..b4a26c0 100644 --- a/image.go +++ b/image.go @@ -5,10 +5,9 @@ import ( "image" "image/gif" "image/jpeg" - "image/png" - "os" + "io" - "github.com/biessek/golang-ico" + ico "github.com/biessek/golang-ico" "github.com/gen2brain/avif" "github.com/gen2brain/jpegxl" "github.com/gen2brain/webp" @@ -16,23 +15,7 @@ import ( "golang.org/x/image/tiff" ) -var ( - options = map[string]string{ - "c / colors": "Number of colors (1-256) (gif)", - "e / effort": "Encoder effort level (0-10) (jxl)", - "f / format": "Output format (avif, bmp, gif, jpeg, jxl, png, tiff, webp)", - "h / help": "Show this help page", - "l / lossless": "Use lossless compression (webp)", - "m / method": "Encoder method (0=fast, 6=slower-better) (webp)", - "r / ratio": "YCbCr subsample-ratio (0=444, 1=422, 2=420, 3=440, 4=411, 5=410) (avif)", - "s / silent": "Do not print any output", - "q / quality": "Set quality (0-100) (avif, jpeg, jxl, webp)", - "x / exact": "Preserve RGB values in transparent area (webp)", - "z / compression": "Compression type (0=uncompressed, 1=deflate, 2=lzw, 3=ccittgroup3, 4=ccittgroup4) (tiff)", - } -) - -func ReadImage(input *os.File) (image.Image, error) { +func ReadImage(input io.ReadSeeker) (image.Image, error) { decoder, err := GetDecoderFromContent(input) if err != nil { return nil, err @@ -41,7 +24,7 @@ func ReadImage(input *os.File) (image.Image, error) { return decoder(input) } -func WriteImage(output *os.File, img image.Image, format string) error { +func WriteImage(output io.Writer, img image.Image, format string) error { switch format { case "webp": options := GetWebPOptions() @@ -56,7 +39,11 @@ func WriteImage(output *os.File, img image.Image, format string) error { return jpeg.Encode(output, img, options) case "png": - return png.Encode(output, img) + encoder := GetPNGOptions() + + LogPNGOptions(encoder) + + return encoder.Encode(output, img) case "gif": options := GetGifOptions() diff --git a/log.go b/log.go index e8d1109..0cf8d1f 100644 --- a/log.go +++ b/log.go @@ -14,11 +14,13 @@ func info(fm string, args ...interface{}) { return } - fmt.Printf(fm+"\n", args...) + fmt.Printf(fm, args...) + fmt.Println() } -func fatalf(code int, fm string, args ...interface{}) { - fmt.Printf("ERROR: "+fm+"\n", args...) +func fatalf(fm string, args ...interface{}) { + fmt.Printf("ERROR: "+fm, args...) + fmt.Println() - os.Exit(code) + os.Exit(1) } diff --git a/main.go b/main.go index 481cdac..650fca1 100644 --- a/main.go +++ b/main.go @@ -5,77 +5,46 @@ import ( ) func main() { - help() + parse() - silent = arguments.GetBool("s", "silent", false) + info("Reading input image...") - // Read input file - input := arguments.GetString("i", "input") - - var in *os.File - - if input == "" { - in = os.Stdin - } else { - var err error - - in, err = os.OpenFile(input, os.O_RDONLY, 0) - if err != nil { - fatalf(1, "Failed to open input file: %s", err) - } - } - - // Read image - if in == os.Stdin { - info("Decoding input from stdin...") - } else { - info("Decoding input image...") - } - - img, err := ReadImage(in) + in, err := os.OpenFile(opts.Input, os.O_RDONLY, 0) if err != nil { - fatalf(4, "Failed to read image: %s", err) + fatalf("Failed to open input file: %s", err) } - // Read output format - format := arguments.GetString("f", "format") - - // Read output file - output := arguments.GetString("", "") + defer in.Close() var out *os.File - if output == "" { - if format == "" { - format = "webp" - } + if opts.Output == "" { + opts.Silent = true out = os.Stdout - silent = true } else { - var err error - - if format == "" { - format = OutputFormatFromPath(output) - } - - out, err = os.OpenFile(output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644) + out, err = os.OpenFile(opts.Output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644) if err != nil { - fatalf(2, "Failed to open output file: %s", err) + fatalf("Failed to open output file: %s", err) } + + defer out.Close() } - if !IsValidOutputFormat(format) { - fatalf(3, "Invalid output format: %s", format) + info("Decoding input image...") + + img, err := ReadImage(in) + if err != nil { + fatalf("Failed to read image: %s", err) } - info("Using output format: %s", format) + info("Using output format: %s", opts.Format) // Write image info("Encoding output image...") - err = WriteImage(out, img, format) + err = WriteImage(out, img, opts.Format) if err != nil { - fatalf(5, "Failed to write image: %s", err) + fatalf("Failed to write image: %s", err) } } diff --git a/options.go b/options.go index 24860c9..22b1a4c 100644 --- a/options.go +++ b/options.go @@ -4,19 +4,160 @@ import ( "image" "image/gif" "image/jpeg" + "image/png" + "github.com/coalaura/arguments" "github.com/gen2brain/avif" "github.com/gen2brain/jpegxl" "github.com/gen2brain/webp" "golang.org/x/image/tiff" ) +type Options struct { + Help bool + Input string + Output string + + Silent bool + NumColors int + Effort int + Format string + Lossless bool + Method int + Ratio int + Quality int + Exact bool + Compression int + Level int + Speed int +} + +var opts = Options{ + Help: false, + Input: "", + Output: "", + + Silent: false, + NumColors: 256, + Effort: 10, + Format: "", + Lossless: false, + Method: 6, + Ratio: 0, + Quality: 90, + Exact: false, + Compression: 2, + Level: 2, + Speed: 0, +} + +func parse() { + // General options + arguments.Register("help", 'h', &opts.Help).WithHelp("Show this help message") + arguments.Register("silent", 's', &opts.Silent).WithHelp("Do not print any output") + arguments.Register("format", 'f', &opts.Format).WithHelp("Output format (avif, bmp, gif, jpeg, jxl, png, tiff, webp, ico)") + + // Common image options + arguments.Register("quality", 'q', &opts.Quality).WithHelp("[avif|jpeg|jxl|webp] Quality level (1-100)") + + // AVIF + arguments.Register("ratio", 'r', &opts.Ratio).WithHelp("[avif] YCbCr subsample-ratio (0=444, 1=422, 2=420, 3=440, 4=411, 5=410)") + arguments.Register("speed", 'p', &opts.Speed).WithHelp("[avif] Encoder speed level (0=fast, 10=slower-better)") + + // GIF + arguments.Register("colors", 'c', &opts.NumColors).WithHelp("[gif] Number of colors to use (1-256)") + + // JXL + arguments.Register("effort", 'e', &opts.Effort).WithHelp("[jxl] Encoder effort level (0=fast, 10=slower-better)") + + // PNG + arguments.Register("level", 'g', &opts.Level).WithHelp("[png] Compression level (0=no-compression, 1=best-speed, 2=best-compression)") + + // TIFF + arguments.Register("compression", 't', &opts.Compression).WithHelp("[tiff] Compression type (0=uncompressed, 1=deflate, 2=lzw, 3=ccittgroup3, 4=ccittgroup4)") + + // WebP + arguments.Register("exact", 'x', &opts.Exact).WithHelp("[webp] Preserve RGB values in transparent area") + arguments.Register("lossless", 'l', &opts.Lossless).WithHelp("[webp] Use lossless compression") + arguments.Register("method", 'm', &opts.Method).WithHelp("[webp] Encoder method (0=fast, 6=slower-better)") + + arguments.Parse() + + help() + + if len(arguments.Args) < 1 { + fatalf("Missing input file") + } + + opts.Input = arguments.Args[0] + + if len(arguments.Args) > 1 { + opts.Output = arguments.Args[1] + } + + if opts.Format != "" && !IsValidOutputFormat(opts.Format) { + fatalf("Invalid output format: %s", opts.Format) + } + + // Resolve format from output file + if opts.Format == "" && opts.Output != "" { + opts.Format = GetFormatFromPath(opts.Output) + } + + // Otherwise resolve format from input file + if opts.Format == "" { + opts.Format = GetFormatFromPath(opts.Input) + } + + // Or default to webp + if opts.Format == "" { + opts.Format = "webp" + } else if opts.Format == "jpg" { + opts.Format = "jpeg" + } + + // NumColors must be between 1 and 256 + if opts.NumColors < 1 || opts.NumColors > 256 { + opts.NumColors = 256 + } + + // Effort must be between 0 and 10 + if opts.Effort < 0 || opts.Effort > 10 { + opts.Effort = 10 + } + + // Method must be between 0 and 6 + if opts.Method < 0 || opts.Method > 6 { + opts.Method = 6 + } + + // Quality must be between 1 and 100 + if opts.Quality < 1 || opts.Quality > 100 { + opts.Quality = 90 + } + + // Ratio must be between 0 and 5 + if opts.Ratio < 0 || opts.Ratio > 5 { + opts.Ratio = 0 + } + + // Compression must be between 0 and 4 + if opts.Compression < 0 || opts.Compression > 4 { + opts.Compression = 2 + } + + // Level must be between 0 and 2 + if opts.Level < 0 || opts.Level > 2 { + opts.Level = 2 + } +} + func GetWebPOptions() webp.Options { return webp.Options{ - Lossless: arguments.GetBool("l", "lossless", false), - Quality: int(arguments.GetUint64("q", "quality", 100, 0, 100)), - Method: int(arguments.GetUint64("m", "method", 4, 0, 6)), - Exact: arguments.GetBool("x", "exact", false), + Lossless: opts.Lossless, + Quality: opts.Quality, + Method: opts.Method, + Exact: opts.Exact, } } @@ -30,7 +171,7 @@ func LogWebPOptions(options webp.Options) { func GetJpegOptions() *jpeg.Options { return &jpeg.Options{ - Quality: int(arguments.GetUint64("q", "quality", 100, 0, 100)), + Quality: opts.Quality, } } @@ -39,9 +180,20 @@ func LogJpegOptions(options *jpeg.Options) { info(" - quality: %v", options.Quality) } +func GetPNGOptions() *png.Encoder { + return &png.Encoder{ + CompressionLevel: GetPNGCompressionLevel(), + } +} + +func LogPNGOptions(encoder *png.Encoder) { + info("Using output options:") + info(" - level: %s", PNGCompressionLevelToString(encoder.CompressionLevel)) +} + func GetGifOptions() *gif.Options { return &gif.Options{ - NumColors: int(arguments.GetUint64("c", "colors", 256, 0, 256)), + NumColors: opts.NumColors, } } @@ -63,9 +215,9 @@ func LogTiffOptions(options *tiff.Options) { func GetAvifOptions() avif.Options { return avif.Options{ - Quality: int(arguments.GetUint64("q", "quality", 100, 0, 100)), - QualityAlpha: int(arguments.GetUint64("qa", "quality-alpha", 100, 0, 100)), - Speed: int(arguments.GetUint64("s", "speed", 6, 0, 10)), + Quality: opts.Quality, + QualityAlpha: opts.Quality, + Speed: opts.Speed, ChromaSubsampling: GetAvifYCbCrSubsampleRatio(), } } @@ -80,8 +232,8 @@ func LogAvifOptions(options avif.Options) { func GetJxlOptions() jpegxl.Options { return jpegxl.Options{ - Quality: int(arguments.GetUint64("q", "quality", 100, 0, 100)), - Effort: int(arguments.GetUint64("e", "effort", 7, 0, 10)), + Quality: opts.Quality, + Effort: opts.Effort, } } @@ -92,9 +244,7 @@ func LogJxlOptions(options jpegxl.Options) { } func GetTiffCompressionType() tiff.CompressionType { - compression := arguments.GetUint64("z", "compression", 1, 0, 4) - - switch compression { + switch opts.Compression { case 0: return tiff.Uncompressed case 1: @@ -128,9 +278,7 @@ func TiffCompressionTypeToString(compression tiff.CompressionType) string { } func GetAvifYCbCrSubsampleRatio() image.YCbCrSubsampleRatio { - sampleRatio := arguments.GetUint64("r", "sample-ratio", 0, 0, 5) - - switch sampleRatio { + switch opts.Ratio { case 0: return image.YCbCrSubsampleRatio444 case 1: @@ -147,3 +295,29 @@ func GetAvifYCbCrSubsampleRatio() image.YCbCrSubsampleRatio { return image.YCbCrSubsampleRatio444 } + +func GetPNGCompressionLevel() png.CompressionLevel { + switch opts.Level { + case 0: + return png.NoCompression + case 1: + return png.BestSpeed + case 2: + return png.BestCompression + } + + return png.BestCompression +} + +func PNGCompressionLevelToString(level png.CompressionLevel) string { + switch level { + case png.NoCompression: + return "no-compression" + case png.BestSpeed: + return "best-speed" + case png.BestCompression: + return "best-compression" + default: + return "unknown" + } +} diff --git a/version.go b/version.go index 15baba7..c3205e5 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,3 @@ package main -const Version = "development" +const Version = " dev"