Add support for AVIF

This commit is contained in:
Milan Nikolic
2022-09-08 23:38:58 +02:00
parent adbb86f9f7
commit 9da5b97f15
7 changed files with 85 additions and 8 deletions

View File

@@ -10,7 +10,7 @@ It can convert comics to different formats to fit your various devices.
* reads RAR, ZIP, 7Z, CBR, CBZ, CB7, CBT, PDF, EPUB, and plain directory
* always saves processed comics in CBZ (ZIP) archive format
* images can be converted to JPEG, PNG, TIFF, WEBP, or 4-Bit BMP (16 colors) file format
* images can be converted to JPEG, PNG, TIFF, WEBP, AVIF, or 4-Bit BMP (16 colors) file format
* rotate, flip, adjust brightness/contrast, adjust levels (Photoshop-like) or grayscale images
* resize algorithms (NearestNeighbor, Box, Linear, MitchellNetravali, CatmullRom, Gaussian, Lanczos)
* export covers from comics
@@ -18,8 +18,10 @@ It can convert comics to different formats to fit your various devices.
### Download
* [Windows](https://github.com/gen2brain/cbconvert/releases/download/0.7.0/cbconvert-0.7.0-windows-i686.zip)
* [Linux](https://github.com/gen2brain/cbconvert/releases/download/0.7.0/cbconvert-0.7.0-linux-x86_64.tar.gz)
* [Windows x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-windows-x86_64.zip)
* [Linux x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-linux-x86_64.tar.gz)
* [macOS x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-darwin-x86_64.tar.gz)
* [macOS aarch64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-darwin-aarch64.tar.gz)
### Using cbconvert in file managers to generate FreeDesktop thumbnails
@@ -55,9 +57,11 @@ This is what it looks like in the PCManFM file manager:
        --fit
            Best fit for required width and height (default "false")
        --format
            Image format, valid values are jpeg, png, tiff, bmp, webp (default "jpeg")
            Image format, valid values are jpeg, png, tiff, bmp, webp, avif (default "jpeg")
        --quality
            JPEG image quality (default "75")
            Image quality (default "75")
        --lossless
            Lossless compression (avif) (default "false")
        --filter
            0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos (default "2")
        --no-cover

View File

@@ -24,6 +24,7 @@ import (
"github.com/chai2010/webp"
_ "github.com/hotei/bmp"
"github.com/strukturag/libheif/go/heif"
"golang.org/x/image/tiff"
"github.com/disintegration/imaging"
@@ -64,10 +65,12 @@ var filters = map[int]imaging.ResampleFilter{
// Options type.
type Options struct {
// Image format, valid values are jpeg, png, tiff, bmp, webp
// Image format, valid values are jpeg, png, tiff, bmp, webp, avif
Format string
// JPEG image quality
Quality int
// Lossless compression (avif)
Lossless bool
// Image width
Width int
// Image height
@@ -208,6 +211,11 @@ func (c *Convertor) convertImage(ctx context.Context, img image.Image, index int
if err != nil {
return err
}
case "avif":
err = c.encodeImage(img, fileName)
if err != nil {
return err
}
}
return nil
@@ -579,6 +587,47 @@ func (c *Convertor) decodeImage(reader io.Reader, fileName string) (img image.Im
return
}
// decodeIM decodes image from reader (ImageMagick).
func (c *Convertor) decodeIM(reader io.Reader, fileName string) (img image.Image, err error) {
imagick.Initialize()
mw := imagick.NewMagickWand()
defer mw.Destroy()
var data []byte
var out interface{}
data, err = io.ReadAll(reader)
if err != nil {
return img, fmt.Errorf("decodeIM: %w", err)
}
err = mw.SetFilename(fileName)
if err != nil {
return img, fmt.Errorf("decodeIM: %w", err)
}
err = mw.ReadImageBlob(data)
if err != nil {
return img, fmt.Errorf("decodeIM: %w", err)
}
w := mw.GetImageWidth()
h := mw.GetImageHeight()
out, err = mw.ExportImagePixels(0, 0, w, h, "RGBA", imagick.PIXEL_CHAR)
if err != nil {
return img, fmt.Errorf("decodeIM: %w", err)
}
b := image.Rect(0, 0, int(w), int(h))
rgba := image.NewRGBA(b)
rgba.Pix = out.([]byte)
img = rgba
return
}
// encodeImage encodes image to file.
func (c *Convertor) encodeImage(img image.Image, fileName string) error {
file, err := os.Create(fileName)
@@ -600,6 +649,18 @@ func (c *Convertor) encodeImage(img image.Image, fileName string) error {
err = jpeg.Encode(file, img, &jpeg.Options{Quality: c.Opts.Quality})
case ".webp":
err = webp.Encode(file, img, &webp.Options{Quality: float32(c.Opts.Quality)})
case ".avif":
img = imageToRGBA(img)
lossLess := heif.LosslessModeDisabled
if c.Opts.Lossless {
lossLess = heif.LosslessModeEnabled
}
ctx, err := heif.EncodeFromImage(img, heif.CompressionAV1, c.Opts.Quality, lossLess, 0)
if err != nil {
return fmt.Errorf("encodeImage: %w", err)
}
err = ctx.WriteToFile(fileName)
}
if err != nil {
return fmt.Errorf("encodeImage: %w", err)
@@ -647,6 +708,11 @@ func (c *Convertor) encodeIM(i image.Image, fileName string) error {
_ = mw.WriteImage(fileName)
case ".jpg", ".jpeg":
_ = mw.SetImageFormat("JPEG")
_ = mw.SetImageCompressionQuality(uint(c.Opts.Quality))
_ = mw.WriteImage(fileName)
case ".avif":
_ = mw.SetImageFormat("AVIF")
_ = mw.SetImageCompressionQuality(uint(c.Opts.Quality))
_ = mw.WriteImage(fileName)
}
@@ -846,7 +912,7 @@ func (c *Convertor) isDocument(f string) bool {
// isImage checks if file is image.
func (c *Convertor) isImage(f string) bool {
var types = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".tif", ".webp"}
var types = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".tif", ".webp", ".avif"}
for _, t := range types {
if strings.ToLower(filepath.Ext(f)) == t {
return true

View File

@@ -20,6 +20,7 @@ require (
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/rivo/uniseg v0.3.4 // indirect
github.com/strukturag/libheif v1.13.0 // indirect
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect

View File

@@ -31,6 +31,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/strukturag/libheif v1.13.0 h1:SuLo/Fl/Nzbw0ixOya1YZSl0Xd27X4fgofGnJdvOHqI=
github.com/strukturag/libheif v1.13.0/go.mod h1:E/PNRlmVtrtj9j2AvBZlrO4dsBDu6KfwDZn7X1Ce8Ks=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=

View File

@@ -120,8 +120,9 @@ func parseFlags() (cbconvert.Options, []string) {
convert.IntVar(&opts.Width, "width", 0, "Image width")
convert.IntVar(&opts.Height, "height", 0, "Image height")
convert.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
convert.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp")
convert.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif")
convert.IntVar(&opts.Quality, "quality", 75, "Image quality")
convert.BoolVar(&opts.Lossless, "lossless", false, "Lossless compression (avif)")
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")

1
go.mod
View File

@@ -9,6 +9,7 @@ require (
github.com/gen2brain/go-fitz v1.20.1
github.com/gen2brain/go-unarr v0.1.6
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7
github.com/strukturag/libheif v1.13.0
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
gopkg.in/gographics/imagick.v3 v3.4.1

2
go.sum
View File

@@ -10,6 +10,8 @@ github.com/gen2brain/go-unarr v0.1.6 h1:2TtfIQ2dGuCkgEYa+vPE1ydcpkB3CtBbdYMfRSGL
github.com/gen2brain/go-unarr v0.1.6/go.mod h1:P05CsEe8jVEXhxqXqp9mFKUKFV0BKpFmtgNWf8Mcoos=
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7 h1:NlUATi3cllRJhpM4mfR9BxiLRXT83bcSLcOa+S8lrME=
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7/go.mod h1:Hku3FQ2laCEwSv7Z8YkC0er38jLaUycUCbsFkWMr+z4=
github.com/strukturag/libheif v1.13.0 h1:SuLo/Fl/Nzbw0ixOya1YZSl0Xd27X4fgofGnJdvOHqI=
github.com/strukturag/libheif v1.13.0/go.mod h1:E/PNRlmVtrtj9j2AvBZlrO4dsBDu6KfwDZn7X1Ce8Ks=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=