mirror of
https://github.com/gen2brain/cbconvert
synced 2026-06-30 09:11:54 +02:00
Add Effort option
This commit is contained in:
@@ -25,6 +25,8 @@ type Options struct {
|
|||||||
Archive string
|
Archive string
|
||||||
// JPEG image quality
|
// JPEG image quality
|
||||||
Quality int
|
Quality int
|
||||||
|
// Encoder speed/effort, format-specific: webp method 0-6, avif speed 0-10, jxl effort 1-10; -1 uses the format default
|
||||||
|
Effort int
|
||||||
// Image width
|
// Image width
|
||||||
Width int
|
Width int
|
||||||
// Image height
|
// Image height
|
||||||
@@ -125,6 +127,7 @@ func NewOptions() Options {
|
|||||||
o.Format = "jpeg"
|
o.Format = "jpeg"
|
||||||
o.Archive = "zip"
|
o.Archive = "zip"
|
||||||
o.Quality = 75
|
o.Quality = 75
|
||||||
|
o.Effort = -1
|
||||||
o.Filter = 2
|
o.Filter = 2
|
||||||
|
|
||||||
return o
|
return o
|
||||||
|
|||||||
+15
-3
@@ -398,11 +398,23 @@ func (c *Converter) imageEncode(img image.Image, w io.Writer) error {
|
|||||||
opts.DCTMethod = jpegli.DefaultDCTMethod
|
opts.DCTMethod = jpegli.DefaultDCTMethod
|
||||||
err = jpegli.Encode(w, img, opts)
|
err = jpegli.Encode(w, img, opts)
|
||||||
case "webp":
|
case "webp":
|
||||||
err = webp.Encode(w, img, webp.Options{Quality: c.Opts.Quality, Method: webp.DefaultMethod})
|
method := webp.DefaultMethod
|
||||||
|
if c.Opts.Effort >= 0 {
|
||||||
|
method = min(max(c.Opts.Effort, 0), 6)
|
||||||
|
}
|
||||||
|
err = webp.Encode(w, img, webp.Options{Quality: c.Opts.Quality, Method: method})
|
||||||
case "avif":
|
case "avif":
|
||||||
err = avif.Encode(w, img, avif.Options{Quality: c.Opts.Quality, Speed: avif.DefaultSpeed})
|
speed := avif.DefaultSpeed
|
||||||
|
if c.Opts.Effort >= 0 {
|
||||||
|
speed = min(max(c.Opts.Effort, 0), 10)
|
||||||
|
}
|
||||||
|
err = avif.Encode(w, img, avif.Options{Quality: c.Opts.Quality, Speed: speed})
|
||||||
case "jxl":
|
case "jxl":
|
||||||
err = jpegxl.Encode(w, img, jpegxl.Options{Quality: c.Opts.Quality, Effort: jpegxl.DefaultEffort})
|
effort := jpegxl.DefaultEffort
|
||||||
|
if c.Opts.Effort >= 0 {
|
||||||
|
effort = min(max(c.Opts.Effort, 1), 10)
|
||||||
|
}
|
||||||
|
err = jpegxl.Encode(w, img, jpegxl.Options{Quality: c.Opts.Quality, Effort: effort})
|
||||||
case "bmp":
|
case "bmp":
|
||||||
opts := &gobmp.EncoderOptions{}
|
opts := &gobmp.EncoderOptions{}
|
||||||
opts.SupportTransparency(false)
|
opts.SupportTransparency(false)
|
||||||
|
|||||||
@@ -140,6 +140,12 @@ func options() cbconvert.Options {
|
|||||||
opts.Fit = iup.GetHandle("Fit").GetAttribute("VALUE") == "ON"
|
opts.Fit = iup.GetHandle("Fit").GetAttribute("VALUE") == "ON"
|
||||||
opts.Filter = iup.GetHandle("Filter").GetInt("VALUE") - 1
|
opts.Filter = iup.GetHandle("Filter").GetInt("VALUE") - 1
|
||||||
opts.Quality = iup.GetHandle("Quality").GetInt("VALUE")
|
opts.Quality = iup.GetHandle("Quality").GetInt("VALUE")
|
||||||
|
switch opts.Format {
|
||||||
|
case "webp", "avif", "jxl":
|
||||||
|
opts.Effort = iup.GetHandle("Effort").GetInt("VALUE")
|
||||||
|
default:
|
||||||
|
opts.Effort = -1
|
||||||
|
}
|
||||||
opts.Grayscale = iup.GetHandle("Grayscale").GetAttribute("VALUE") == "ON"
|
opts.Grayscale = iup.GetHandle("Grayscale").GetAttribute("VALUE") == "ON"
|
||||||
opts.Brightness = iup.GetHandle("Brightness").GetInt("VALUE")
|
opts.Brightness = iup.GetHandle("Brightness").GetInt("VALUE")
|
||||||
opts.Contrast = iup.GetHandle("Contrast").GetInt("VALUE")
|
opts.Contrast = iup.GetHandle("Contrast").GetInt("VALUE")
|
||||||
@@ -200,6 +206,12 @@ func setActive() {
|
|||||||
iup.GetHandle("VboxQuality").SetAttribute("ACTIVE", "NO")
|
iup.GetHandle("VboxQuality").SetAttribute("ACTIVE", "NO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.Format == "webp" || opts.Format == "avif" || opts.Format == "jxl") && !opts.NoConvert {
|
||||||
|
iup.GetHandle("VboxEffort").SetAttribute("ACTIVE", "YES")
|
||||||
|
} else {
|
||||||
|
iup.GetHandle("VboxEffort").SetAttribute("ACTIVE", "NO")
|
||||||
|
}
|
||||||
|
|
||||||
if opts.Width != 0 && opts.Height != 0 && !opts.NoConvert {
|
if opts.Width != 0 && opts.Height != 0 && !opts.NoConvert {
|
||||||
iup.GetHandle("Fit").SetAttribute("ACTIVE", "YES")
|
iup.GetHandle("Fit").SetAttribute("ACTIVE", "YES")
|
||||||
} else {
|
} else {
|
||||||
@@ -207,6 +219,32 @@ func setActive() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setEffort(format string) {
|
||||||
|
val := iup.GetHandle("Effort")
|
||||||
|
|
||||||
|
var name string
|
||||||
|
|
||||||
|
switch format {
|
||||||
|
case "webp":
|
||||||
|
val.SetAttributes("MIN=0, MAX=6, SHOWTICKS=7, VALUE=4")
|
||||||
|
val.SetAttribute("TIP", "WEBP method, higher is better/slower (0-6, default 4)")
|
||||||
|
name = "Method"
|
||||||
|
case "avif":
|
||||||
|
val.SetAttributes("MIN=0, MAX=10, SHOWTICKS=11, VALUE=10")
|
||||||
|
val.SetAttribute("TIP", "AVIF speed, higher is faster/worse (0-10, default 10)")
|
||||||
|
name = "Speed"
|
||||||
|
case "jxl":
|
||||||
|
val.SetAttributes("MIN=1, MAX=10, SHOWTICKS=10, VALUE=7")
|
||||||
|
val.SetAttribute("TIP", "JXL effort, higher is better/slower (1-10, default 7)")
|
||||||
|
name = "Effort"
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val.SetAttribute("EFFORTNAME", name)
|
||||||
|
iup.GetHandle("LabelEffort").SetAttribute("TITLE", fmt.Sprintf("%s: %d", name, val.GetInt("VALUE")))
|
||||||
|
}
|
||||||
|
|
||||||
func layout() iup.Ihandle {
|
func layout() iup.Ihandle {
|
||||||
return iup.Vbox(
|
return iup.Vbox(
|
||||||
iup.Hbox(
|
iup.Hbox(
|
||||||
@@ -409,6 +447,7 @@ func tabs() iup.Ihandle {
|
|||||||
"7": "JXL",
|
"7": "JXL",
|
||||||
}).SetHandle("Format").
|
}).SetHandle("Format").
|
||||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||||
|
setEffort(strings.ToLower(ih.GetAttribute("VALUESTRING")))
|
||||||
setActive()
|
setActive()
|
||||||
previewPost()
|
previewPost()
|
||||||
|
|
||||||
@@ -494,6 +533,25 @@ func tabs() iup.Ihandle {
|
|||||||
return iup.DEFAULT
|
return iup.DEFAULT
|
||||||
})),
|
})),
|
||||||
).SetHandle("VboxQuality"),
|
).SetHandle("VboxQuality"),
|
||||||
|
iup.Vbox(
|
||||||
|
iup.Label("").SetHandle("LabelEffort"),
|
||||||
|
iup.Val("").SetAttributes(`MIN=0, MAX=10, VALUE=0, SHOWTICKS=11`).SetHandle("Effort").
|
||||||
|
SetAttribute("TIP", "Encoder speed/effort (WEBP, AVIF, JXL)").
|
||||||
|
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||||
|
iup.GetHandle("LabelEffort").SetAttribute("TITLE", fmt.Sprintf("%s: %d", ih.GetAttribute("EFFORTNAME"), ih.GetInt("VALUE")))
|
||||||
|
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
||||||
|
|
||||||
|
return iup.DEFAULT
|
||||||
|
})).
|
||||||
|
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
||||||
|
if ih.GetAttribute("MYVALUE") != "" {
|
||||||
|
previewPost()
|
||||||
|
}
|
||||||
|
ih.SetAttribute("MYVALUE", "")
|
||||||
|
|
||||||
|
return iup.DEFAULT
|
||||||
|
})),
|
||||||
|
).SetHandle("VboxEffort"),
|
||||||
iup.Vbox(
|
iup.Vbox(
|
||||||
iup.Toggle(" Grayscale").SetHandle("Grayscale").
|
iup.Toggle(" Grayscale").SetHandle("Grayscale").
|
||||||
SetAttributes(`TIP="Convert images to grayscale (monochromatic)"`).
|
SetAttributes(`TIP="Convert images to grayscale (monochromatic)"`).
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
convert.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif, jxl")
|
convert.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif, jxl")
|
||||||
convert.StringVar(&opts.Archive, "archive", "zip", "Archive format, valid values are zip, tar")
|
convert.StringVar(&opts.Archive, "archive", "zip", "Archive format, valid values are zip, tar")
|
||||||
convert.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
convert.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
||||||
|
convert.IntVar(&opts.Effort, "effort", -1, "Encoder speed/effort, format-specific (webp method 0-6, avif speed 0-10, jxl effort 1-10), -1 uses the format default")
|
||||||
convert.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
|
convert.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
|
||||||
convert.BoolVar(&opts.NoCover, "no-cover", false, "Do not convert the cover image")
|
convert.BoolVar(&opts.NoCover, "no-cover", false, "Do not convert the cover image")
|
||||||
convert.BoolVar(&opts.NoRGB, "no-rgb", false, "Do not convert images that have RGB colorspace")
|
convert.BoolVar(&opts.NoRGB, "no-rgb", false, "Do not convert images that have RGB colorspace")
|
||||||
@@ -188,6 +189,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
cover.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
cover.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
||||||
cover.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif")
|
cover.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif")
|
||||||
cover.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
cover.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
||||||
|
cover.IntVar(&opts.Effort, "effort", -1, "Encoder speed/effort, format-specific (webp method 0-6, avif speed 0-10, jxl effort 1-10), -1 uses the format default")
|
||||||
cover.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
|
cover.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
|
||||||
cover.StringVar(&opts.OutDir, "outdir", ".", "Output directory")
|
cover.StringVar(&opts.OutDir, "outdir", ".", "Output directory")
|
||||||
cover.IntVar(&opts.Size, "size", 0, "Process only files larger than size (in MB)")
|
cover.IntVar(&opts.Size, "size", 0, "Process only files larger than size (in MB)")
|
||||||
@@ -218,7 +220,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
fmt.Fprintf(os.Stderr, "Usage: %s <command> [<flags>] [file1 dir1 ... fileOrDirN]\n\n", filepath.Base(os.Args[0]))
|
fmt.Fprintf(os.Stderr, "Usage: %s <command> [<flags>] [file1 dir1 ... fileOrDirN]\n\n", filepath.Base(os.Args[0]))
|
||||||
fmt.Fprintf(os.Stderr, "\nCommands:\n")
|
fmt.Fprintf(os.Stderr, "\nCommands:\n")
|
||||||
fmt.Fprintf(os.Stderr, "\n convert\n \tConvert archive or document\n\n")
|
fmt.Fprintf(os.Stderr, "\n convert\n \tConvert archive or document\n\n")
|
||||||
order := []string{"width", "height", "fit", "format", "archive", "quality", "filter", "no-cover", "no-rgb",
|
order := []string{"width", "height", "fit", "format", "archive", "quality", "effort", "filter", "no-cover", "no-rgb",
|
||||||
"no-nonimage", "no-convert", "grayscale", "rotate", "brightness", "contrast", "suffix", "outdir", "size", "recursive", "quiet"}
|
"no-nonimage", "no-convert", "grayscale", "rotate", "brightness", "contrast", "suffix", "outdir", "size", "recursive", "quiet"}
|
||||||
for _, name := range order {
|
for _, name := range order {
|
||||||
f := convert.Lookup(name)
|
f := convert.Lookup(name)
|
||||||
@@ -226,7 +228,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "\n cover\n \tExtract cover\n\n")
|
fmt.Fprintf(os.Stderr, "\n cover\n \tExtract cover\n\n")
|
||||||
order = []string{"width", "height", "fit", "format", "quality", "filter", "outdir", "size", "recursive", "quiet"}
|
order = []string{"width", "height", "fit", "format", "quality", "effort", "filter", "outdir", "size", "recursive", "quiet"}
|
||||||
for _, name := range order {
|
for _, name := range order {
|
||||||
f := cover.Lookup(name)
|
f := cover.Lookup(name)
|
||||||
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
||||||
@@ -297,7 +299,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
return opts, args
|
return opts, args
|
||||||
}
|
}
|
||||||
|
|
||||||
// piped checks if we have a piped stdin.
|
// piped checks if we have piped stdin.
|
||||||
func piped() bool {
|
func piped() bool {
|
||||||
f, err := os.Stdin.Stat()
|
f, err := os.Stdin.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -311,7 +313,7 @@ func piped() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// lines returns slice of lines from reader.
|
// lines returns slice of lines from the reader.
|
||||||
func lines(r io.Reader) []string {
|
func lines(r io.Reader) []string {
|
||||||
data := make([]string, 0)
|
data := make([]string, 0)
|
||||||
scanner := bufio.NewScanner(r)
|
scanner := bufio.NewScanner(r)
|
||||||
|
|||||||
Reference in New Issue
Block a user