diff --git a/cbconvert.go b/cbconvert.go index 5b8ac42..4f6db73 100644 --- a/cbconvert.go +++ b/cbconvert.go @@ -27,6 +27,8 @@ type Options struct { 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 + // Lossless enables lossless compression (webp, avif, jxl), ignores quality + Lossless bool // Image width Width int // Image height diff --git a/cbconvert_convert.go b/cbconvert_convert.go index b1f097e..e71bc32 100644 --- a/cbconvert_convert.go +++ b/cbconvert_convert.go @@ -402,19 +402,19 @@ func (c *Converter) imageEncode(img image.Image, w io.Writer) error { 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}) + err = webp.Encode(w, img, webp.Options{Quality: c.Opts.Quality, Method: method, Lossless: c.Opts.Lossless}) case "avif": 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}) + err = avif.Encode(w, img, avif.Options{Quality: c.Opts.Quality, Speed: speed, Lossless: c.Opts.Lossless}) case "jxl": 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}) + err = jpegxl.Encode(w, img, jpegxl.Options{Quality: c.Opts.Quality, Effort: effort, Lossless: c.Opts.Lossless}) case "bmp": opts := &gobmp.EncoderOptions{} opts.SupportTransparency(false) diff --git a/cmd/cbconvert-gui/main.go b/cmd/cbconvert-gui/main.go index e3b2927..a3453e9 100644 --- a/cmd/cbconvert-gui/main.go +++ b/cmd/cbconvert-gui/main.go @@ -146,6 +146,7 @@ func options() cbconvert.Options { default: opts.Effort = -1 } + opts.Lossless = iup.GetHandle("Lossless").GetAttribute("VALUE") == "ON" opts.Grayscale = iup.GetHandle("Grayscale").GetAttribute("VALUE") == "ON" opts.Brightness = iup.GetHandle("Brightness").GetInt("VALUE") opts.Contrast = iup.GetHandle("Contrast").GetInt("VALUE") @@ -171,24 +172,30 @@ func setActive() { iup.GetHandle("RemoveAll").SetAttribute("ACTIVE", "YES") } - if opts.OutDir == "" { - iup.GetHandle("Thumbnail").SetAttributes(`ACTIVE=NO`) - iup.GetHandle("Cover").SetAttributes(`ACTIVE=NO`) - iup.GetHandle("Convert").SetAttributes(`ACTIVE=NO`) - if count > 0 { - iup.GetHandle("Thumbnail").SetAttributes(`ACTIVE=NO, TIP="Set Output Directory"`) - iup.GetHandle("Cover").SetAttributes(`ACTIVE=NO, TIP="Set Output Directory"`) - iup.GetHandle("Convert").SetAttributes(`ACTIVE=NO, TIP="Set Output Directory"`) - } - } else { - if count > 0 { - iup.GetHandle("Thumbnail").SetAttributes(`ACTIVE=YES, TIP=""`) - iup.GetHandle("Cover").SetAttributes(`ACTIVE=YES, TIP=""`) - iup.GetHandle("Convert").SetAttributes(`ACTIVE=YES, TIP=""`) + active := "YES" + var tip string + switch { + case count == 0 && opts.OutDir == "": + active, tip = "NO", "Add files and set output directory" + case count == 0: + active, tip = "NO", "Add files" + case opts.OutDir == "": + active, tip = "NO", "Set output directory" + } + + enabledTip := map[string]string{ + "Thumbnail": "Extract cover thumbnails", + "Cover": "Extract covers", + "Convert": "Convert files to the selected format", + } + + for _, h := range []string{"Thumbnail", "Cover", "Convert"} { + b := iup.GetHandle(h) + b.SetAttribute("ACTIVE", active) + if active == "YES" { + b.SetAttribute("TIP", enabledTip[h]) } else { - iup.GetHandle("Thumbnail").SetAttributes(`ACTIVE=NO`) - iup.GetHandle("Cover").SetAttributes(`ACTIVE=NO`) - iup.GetHandle("Convert").SetAttributes(`ACTIVE=NO`) + b.SetAttribute("TIP", tip) } } @@ -200,16 +207,21 @@ func setActive() { iup.GetHandle("VboxTransform").SetAttribute("ACTIVE", "YES") } - if (opts.Format == "jpeg" || opts.Format == "webp" || opts.Format == "avif" || opts.Format == "jxl") && !opts.NoConvert { + canLossless := opts.Format == "webp" || opts.Format == "avif" || opts.Format == "jxl" + losslessOn := canLossless && opts.Lossless + + if (opts.Format == "jpeg" || canLossless) && !opts.NoConvert && !losslessOn { iup.GetHandle("VboxQuality").SetAttribute("ACTIVE", "YES") } else { iup.GetHandle("VboxQuality").SetAttribute("ACTIVE", "NO") } - if (opts.Format == "webp" || opts.Format == "avif" || opts.Format == "jxl") && !opts.NoConvert { + if canLossless && !opts.NoConvert { iup.GetHandle("VboxEffort").SetAttribute("ACTIVE", "YES") + iup.GetHandle("Lossless").SetAttribute("ACTIVE", "YES") } else { iup.GetHandle("VboxEffort").SetAttribute("ACTIVE", "NO") + iup.GetHandle("Lossless").SetAttribute("ACTIVE", "NO") } if opts.Width != 0 && opts.Height != 0 && !opts.NoConvert { @@ -243,6 +255,7 @@ func setEffort(format string) { val.SetAttribute("EFFORTNAME", name) iup.GetHandle("LabelEffort").SetAttribute("TITLE", fmt.Sprintf("%s: %d", name, val.GetInt("VALUE"))) + iup.Refresh(iup.GetHandle("LabelEffort")) } func layout() iup.Ihandle { @@ -401,7 +414,7 @@ func tabs() iup.Ihandle { SetAttributes(`TIP="Process only files larger than minimum size"`), ), iup.Space().SetAttributes("EXPAND=HORIZONTAL"), - ).SetHandle("VboxInput").SetAttributes("MARGIN=5x5, GAP=5") + ).SetHandle("VboxInput").SetAttributes("NGAP=10") vboxOutput := iup.Vbox( iup.Vbox( @@ -430,37 +443,103 @@ func tabs() iup.Ihandle { "2": "TAR", }).SetHandle("Archive"), ), - ).SetHandle("VboxOutput").SetAttributes("MARGIN=5x5, GAP=5") + ).SetHandle("VboxOutput").SetAttributes("NGAP=10") - vboxImage := iup.Vbox( + vboxImage := iup.Hbox( iup.Vbox( - iup.Label("Format:"), - iup.List().SetAttributes(map[string]string{ - "DROPDOWN": "YES", - "VALUE": "1", - "1": "JPEG", - "2": "PNG", - "3": "TIFF", - "4": "BMP", - "5": "WEBP", - "6": "AVIF", - "7": "JXL", - }).SetHandle("Format"). - SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { - setEffort(strings.ToLower(ih.GetAttribute("VALUESTRING"))) - setActive() - previewPost() - - return iup.DEFAULT - })), - ), - iup.Vbox( - iup.Label("Size:"), - iup.Hbox( - iup.Text().SetAttributes(`CUEBANNER=" width", VISIBLECOLUMNS=4, MASK="/d*"`).SetHandle("Width"). - SetAttribute("TIP", "If one of, width or height is not set, the image aspect ratio is preserved"). + iup.Vbox( + iup.Label("Format:"), + iup.List().SetAttributes(map[string]string{ + "DROPDOWN": "YES", + "VALUE": "1", + "1": "JPEG", + "2": "PNG", + "3": "TIFF", + "4": "BMP", + "5": "WEBP", + "6": "AVIF", + "7": "JXL", + }).SetHandle("Format"). SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { + setEffort(strings.ToLower(ih.GetAttribute("VALUESTRING"))) setActive() + previewPost() + + return iup.DEFAULT + })), + ), + iup.Vbox( + iup.Label("Size:"), + iup.Hbox( + iup.Text().SetAttributes(`CUEBANNER="width", VISIBLECOLUMNS=6, MASK="/d*"`).SetHandle("Width"). + SetAttribute("TIP", "If one of, width or height is not set, the image aspect ratio is preserved"). + SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { + setActive() + 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 + })), + iup.Space().SetAttribute("SIZE", "2"), + iup.Label("x"), + iup.Space().SetAttribute("SIZE", "2"), + iup.Text().SetAttributes(`CUEBANNER="height", VISIBLECOLUMNS=6, MASK="/d*"`).SetHandle("Height"). + SetAttribute("TIP", "If one of, width or height is not set, the image aspect ratio is preserved"). + SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { + setActive() + 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 + })), + ).SetAttributes("ALIGNMENT=ACENTER, NMARGIN=0"), + ), + iup.Vbox( + iup.Toggle(" Best Fit").SetHandle("Fit"). + SetAttributes(`TIP="Best fit for required width and height"`), + ), + iup.Vbox( + iup.Label("Resize Filter:"), + iup.List().SetAttributes(map[string]string{ + "DROPDOWN": "YES", + "VALUE": "3", + "TIP": "Linear is the bilinear filter, smooth and reasonably fast", + "1": "NearestNeighbor", + "2": "Box", + "3": "Linear", + "4": "MitchellNetravali", + "5": "CatmullRom", + "6": "Gaussian", + "7": "Lanczos", + }).SetHandle("Filter").SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(onFilterChanged)), + ), + ).SetAttributes("NGAP=10"), + iup.Space().SetAttribute("SIZE", "15"), + iup.Vbox( + iup.Vbox( + iup.Hbox( + iup.Label("Quality: "), + iup.Label("75").SetHandle("LabelQuality"), + ).SetAttributes("NMARGIN=0"), + iup.Val("").SetAttributes(`MIN=0, MAX=100, VALUE=75, SHOWTICKS=10`).SetHandle("Quality"). + SetAttribute("TIP", "Quality affects JPEG, WEBP, AVIF and JXL"). + SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { + iup.GetHandle("LabelQuality").SetAttribute("TITLE", ih.GetInt("VALUE")) + iup.Refresh(iup.GetHandle("LabelQuality")) ih.SetAttribute("MYVALUE", ih.GetInt("VALUE")) return iup.DEFAULT @@ -473,11 +552,14 @@ func tabs() iup.Ihandle { return iup.DEFAULT })), - iup.Label("x"), - iup.Text().SetAttributes(`CUEBANNER=" height", VISIBLECOLUMNS=4, MASK="/d*"`).SetHandle("Height"). - SetAttribute("TIP", "If one of, width or height is not set, the image aspect ratio is preserved"). + ).SetHandle("VboxQuality"), + iup.Vbox( + iup.Label("Effort:").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 { - setActive() + iup.GetHandle("LabelEffort").SetAttribute("TITLE", fmt.Sprintf("%s: %d", ih.GetAttribute("EFFORTNAME"), ih.GetInt("VALUE"))) + iup.Refresh(iup.GetHandle("LabelEffort")) ih.SetAttribute("MYVALUE", ih.GetInt("VALUE")) return iup.DEFAULT @@ -490,89 +572,40 @@ func tabs() iup.Ihandle { return iup.DEFAULT })), - ).SetAttributes("ALIGNMENT=ACENTER, MARGIN=0"), - ), - iup.Vbox( - iup.Toggle(" Best Fit").SetHandle("Fit"). - SetAttributes(`TIP="Best fit for required width and height"`), - ), - iup.Vbox( - iup.Label("Resize Filter:"), - iup.List().SetAttributes(map[string]string{ - "DROPDOWN": "YES", - "VALUE": "3", - "TIP": "Linear is the bilinear filter, smooth and reasonably fast", - "1": "NearestNeighbor", - "2": "Box", - "3": "Linear", - "4": "MitchellNetravali", - "5": "CatmullRom", - "6": "Gaussian", - "7": "Lanczos", - }).SetHandle("Filter").SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(onFilterChanged)), - ), - iup.Vbox( - iup.Hbox( - iup.Label("Quality: "), - iup.Label("75").SetHandle("LabelQuality"), - ).SetAttributes("MARGIN=0"), - iup.Val("").SetAttributes(`MIN=0, MAX=100, VALUE=75, SHOWTICKS=10`).SetHandle("Quality"). - SetAttribute("TIP", "Quality affects JPEG, WEBP, AVIF and JXL"). - SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { - iup.GetHandle("LabelQuality").SetAttribute("TITLE", 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") != "" { + ).SetHandle("VboxEffort"), + iup.Vbox( + iup.Toggle(" Lossless").SetHandle("Lossless"). + SetAttributes(`TIP="Lossless compression (WEBP, AVIF, JXL), ignores quality"`). + SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { + setActive() previewPost() - } - ih.SetAttribute("MYVALUE", "") - return iup.DEFAULT - })), - ).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") != "" { + return iup.DEFAULT + })), + ), + iup.Vbox( + iup.Toggle(" Grayscale").SetHandle("Grayscale"). + SetAttributes(`TIP="Convert images to grayscale (monochromatic)"`). + SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { previewPost() - } - ih.SetAttribute("MYVALUE", "") - return iup.DEFAULT - })), - ).SetHandle("VboxEffort"), - iup.Vbox( - iup.Toggle(" Grayscale").SetHandle("Grayscale"). - SetAttributes(`TIP="Convert images to grayscale (monochromatic)"`). - SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { - previewPost() - - return iup.DEFAULT - })), - ), - ).SetHandle("VboxImage").SetAttributes("MARGIN=5x5, GAP=5") + return iup.DEFAULT + })), + ), + ).SetAttributes("NGAP=10"), + ).SetHandle("VboxImage") vboxTransform := iup.Vbox( iup.Vbox( iup.Hbox( iup.Label("Brightness: "), iup.Label("0").SetHandle("LabelBrightness"), - ).SetAttributes("ALIGNMENT=ACENTER, MARGIN=0"), + ).SetAttributes("ALIGNMENT=ACENTER, NMARGIN=0"), iup.Val("").SetAttributes(`MIN=-100, MAX=100, VALUE=0, SHOWTICKS=10`).SetHandle("Brightness"). SetAttributes(`TIP="Adjust the brightness of the images"`). SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { iup.GetHandle("LabelBrightness").SetAttribute("TITLE", iup.GetHandle("Brightness").GetInt("VALUE")) + iup.Refresh(iup.GetHandle("LabelBrightness")) ih.SetAttribute("MYVALUE", ih.GetInt("VALUE")) return iup.DEFAULT @@ -590,11 +623,12 @@ func tabs() iup.Ihandle { iup.Hbox( iup.Label("Contrast: "), iup.Label("0").SetHandle("LabelContrast"), - ).SetAttributes("ALIGNMENT=ACENTER, MARGIN=0"), + ).SetAttributes("ALIGNMENT=ACENTER, NMARGIN=0"), iup.Val("").SetAttributes(`MIN=-100, MAX=100, VALUE=0, SHOWTICKS=10`).SetHandle("Contrast"). SetAttributes(`TIP="Adjust the contrast of the images"`). SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int { iup.GetHandle("LabelContrast").SetAttribute("TITLE", iup.GetHandle("Contrast").GetInt("VALUE")) + iup.Refresh(iup.GetHandle("LabelContrast")) ih.SetAttribute("MYVALUE", ih.GetInt("VALUE")) return iup.DEFAULT @@ -624,45 +658,51 @@ func tabs() iup.Ihandle { return iup.DEFAULT })), ), - ).SetHandle("VboxTransform").SetAttributes("MARGIN=5x5, GAP=5") + ).SetHandle("VboxTransform").SetAttributes("NGAP=10") return iup.Tabs( - vboxInput.SetAttributes("TABTITLE=Input"), - vboxOutput.SetAttributes("TABTITLE=Output"), - vboxImage.SetAttributes("TABTITLE=Image"), - vboxTransform.SetAttributes("TABTITLE=Transform"), - ).SetHandle("Tabs").SetAttributes("MINSIZE=320x400, EXPAND=HORIZONTAL, MULTILINE=YES") + vboxInput.SetAttributes("TABTITLE=Input, NMARGIN=10x10"), + vboxOutput.SetAttributes("TABTITLE=Output, NMARGIN=10x10"), + vboxImage.SetAttributes("TABTITLE=Image, NMARGIN=10x10"), + vboxTransform.SetAttributes("TABTITLE=Transform, NMARGIN=10x10"), + ).SetHandle("Tabs") } func buttons() iup.Ihandle { + addFiles := iup.Button("Add &Files...").SetHandle("AddFiles").SetAttributes("PADDING=DEFAULTBUTTONPADDING"). + SetCallback("ACTION", iup.ActionFunc(onAddFiles)) + addDir := iup.Button("Add &Dir...").SetHandle("AddDir").SetAttributes("PADDING=DEFAULTBUTTONPADDING"). + SetCallback("ACTION", iup.ActionFunc(onAddDir)) + remove := iup.Button("Remove").SetHandle("Remove").SetAttributes("PADDING=DEFAULTBUTTONPADDING"). + SetCallback("ACTION", iup.ActionFunc(onRemove)) + removeAll := iup.Button("Remove All").SetHandle("RemoveAll").SetAttributes("PADDING=DEFAULTBUTTONPADDING"). + SetCallback("ACTION", iup.ActionFunc(onRemoveAll)) + thumbnail := iup.Button("Thumbnail").SetHandle("Thumbnail").SetAttributes("PADDING=DEFAULTBUTTONPADDING"). + SetCallback("ACTION", iup.ActionFunc(onThumbnail)) + cover := iup.Button("Cover").SetHandle("Cover").SetAttributes("PADDING=DEFAULTBUTTONPADDING"). + SetCallback("ACTION", iup.ActionFunc(onCover)) + convert := iup.Button("&Convert").SetHandle("Convert").SetAttributes("PADDING=DEFAULTBUTTONPADDING"). + SetCallback("ACTION", iup.ActionFunc(onConvert)) + + iup.Normalizer(addFiles, addDir, remove, removeAll, thumbnail, cover, convert).SetAttribute("NORMALIZE", "BOTH") + return iup.Vbox( - iup.Frame( - iup.Vbox( - iup.Button("Add &Files...").SetHandle("AddFiles").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING"). - SetCallback("ACTION", iup.ActionFunc(onAddFiles)), - iup.Button("Add &Dir...").SetHandle("AddDir").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING"). - SetCallback("ACTION", iup.ActionFunc(onAddDir)), - iup.Button("Remove").SetHandle("Remove").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING"). - SetCallback("ACTION", iup.ActionFunc(onRemove)), - iup.Button("Remove All").SetHandle("RemoveAll").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING"). - SetCallback("ACTION", iup.ActionFunc(onRemoveAll)), - ).SetAttributes("NGAP=5"), + iup.Vbox( + addFiles, + addDir, + remove, + removeAll, + ).SetAttribute("NGAP", "2"), + iup.Space().SetAttribute("SIZE", "x5"), + iup.Vbox( + thumbnail, + cover, + ).SetAttribute("NGAP", "2"), + iup.Space().SetAttribute("SIZE", "x5"), + iup.Vbox( + convert, ), - iup.Frame( - iup.Vbox( - iup.Button("Thumbnail").SetHandle("Thumbnail").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING"). - SetCallback("ACTION", iup.ActionFunc(onThumbnail)), - iup.Button("Cover").SetHandle("Cover").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING"). - SetCallback("ACTION", iup.ActionFunc(onCover)), - ).SetAttributes("NGAP=5"), - ), - iup.Frame( - iup.Vbox( - iup.Button("&Convert").SetHandle("Convert").SetAttributes("EXPAND=HORIZONTAL, PADDING=DEFAULTBUTTONPADDING"). - SetCallback("ACTION", iup.ActionFunc(onConvert)), - ), - ), - ).SetHandle("Buttons").SetAttributes("ALIGNMENT=ACENTER, NGAP=10") + ).SetHandle("Buttons").SetAttributes("ALIGNMENT=ACENTER") } func status() iup.Ihandle { @@ -670,10 +710,10 @@ func status() iup.Ihandle { loading(), iup.Fill(), iup.Label("File 1 of 1").SetHandle("LabelStatus1").SetAttributes("VISIBLE=NO"), - iup.Space().SetAttribute("SIZE", "5x0"), + iup.Space().SetAttribute("SIZE", "5"), iup.Label("(000/000)").SetHandle("LabelStatus2").SetAttributes("VISIBLE=NO"), - iup.Space().SetAttribute("SIZE", "5x0"), - iup.ProgressBar().SetAttributes("RASTERSIZE=200x15, VISIBLE=NO").SetHandle("ProgressBar"). + iup.Space().SetAttribute("SIZE", "5"), + iup.ProgressBar().SetAttributes("RASTERSIZE=200x, VISIBLE=NO").SetHandle("ProgressBar"). SetCallback("POSTMESSAGE_CB", iup.PostMessageFunc(func(ih iup.Ihandle, s string, i int, p any) int { switch s { case "convert": diff --git a/cmd/cbconvert/main.go b/cmd/cbconvert/main.go index d438471..e2323bd 100644 --- a/cmd/cbconvert/main.go +++ b/cmd/cbconvert/main.go @@ -168,6 +168,7 @@ func parseFlags() (cbconvert.Options, []string) { convert.StringVar(&opts.Archive, "archive", "zip", "Archive format, valid values are zip, tar") 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.BoolVar(&opts.Lossless, "lossless", false, "Lossless compression (webp, avif, jxl), ignores quality") 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.NoRGB, "no-rgb", false, "Do not convert images that have RGB colorspace") @@ -190,6 +191,7 @@ func parseFlags() (cbconvert.Options, []string) { 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.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.BoolVar(&opts.Lossless, "lossless", false, "Lossless compression (webp, avif, jxl), ignores quality") 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.IntVar(&opts.Size, "size", 0, "Process only files larger than size (in MB)") @@ -220,7 +222,7 @@ func parseFlags() (cbconvert.Options, []string) { fmt.Fprintf(os.Stderr, "Usage: %s [] [file1 dir1 ... fileOrDirN]\n\n", filepath.Base(os.Args[0])) fmt.Fprintf(os.Stderr, "\nCommands:\n") fmt.Fprintf(os.Stderr, "\n convert\n \tConvert archive or document\n\n") - order := []string{"width", "height", "fit", "format", "archive", "quality", "effort", "filter", "no-cover", "no-rgb", + order := []string{"width", "height", "fit", "format", "archive", "quality", "effort", "lossless", "filter", "no-cover", "no-rgb", "no-nonimage", "no-convert", "grayscale", "rotate", "brightness", "contrast", "suffix", "outdir", "size", "recursive", "quiet"} for _, name := range order { f := convert.Lookup(name) @@ -228,7 +230,7 @@ func parseFlags() (cbconvert.Options, []string) { fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue) } fmt.Fprintf(os.Stderr, "\n cover\n \tExtract cover\n\n") - order = []string{"width", "height", "fit", "format", "quality", "effort", "filter", "outdir", "size", "recursive", "quiet"} + order = []string{"width", "height", "fit", "format", "quality", "effort", "lossless", "filter", "outdir", "size", "recursive", "quiet"} for _, name := range order { f := cover.Lookup(name) fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)