mirror of
https://github.com/gen2brain/cbconvert
synced 2025-10-14 02:28:51 +02:00
Add support for AVIF
This commit is contained in:
14
README.md
14
README.md
@@ -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
|
||||
|
70
cbconvert.go
70
cbconvert.go
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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=
|
||||
|
@@ -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
1
go.mod
@@ -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
2
go.sum
@@ -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=
|
||||
|
Reference in New Issue
Block a user