14 Commits

Author SHA1 Message Date
Milan Nikolic 8ba065e68b Use pkgconfig 2023-01-12 13:18:42 +01:00
Milan Nikolic 9cd5616b48 Build linux/arm64 binary 2023-01-12 13:08:21 +01:00
Milan Nikolic 13a3b6e23e Update README.md 2023-01-12 10:06:09 +01:00
Milan Nikolic 9a047d1977 Fallback to IM decoding for broken images, issue #15 2023-01-12 10:03:45 +01:00
Milan Nikolic d6e7248112 Case-insensitive for coverName, issue #13 2023-01-12 09:10:24 +01:00
Milan Nikolic 9eb20db167 Handle errors 2023-01-12 08:45:32 +01:00
Milan Nikolic 04d2dd79d5 Handle no-convert for directories, issue #9 2023-01-12 08:44:00 +01:00
Milan Nikolic 2b2788ed6e Update modules 2022-12-08 12:38:38 +01:00
Milan Nikolic 164e7bfd75 Update modules 2022-12-08 12:35:53 +01:00
Milan Nikolic 27da60ed76 Update modules 2022-12-08 12:28:08 +01:00
Milan Nikolic 0963cb1762 Update README.md 2022-09-09 10:01:14 +02:00
Milan Nikolic 443c4e6aaa Update README.md 2022-09-08 23:44:56 +02:00
Milan Nikolic 9da5b97f15 Add support for AVIF 2022-09-08 23:38:58 +02:00
Milan Nikolic adbb86f9f7 Add more architectures 2022-09-08 23:36:47 +02:00
8 changed files with 270 additions and 95 deletions
+11 -7
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 * reads RAR, ZIP, 7Z, CBR, CBZ, CB7, CBT, PDF, EPUB, and plain directory
* always saves processed comics in CBZ (ZIP) archive format * 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 * rotate, flip, adjust brightness/contrast, adjust levels (Photoshop-like) or grayscale images
* resize algorithms (NearestNeighbor, Box, Linear, MitchellNetravali, CatmullRom, Gaussian, Lanczos) * resize algorithms (NearestNeighbor, Box, Linear, MitchellNetravali, CatmullRom, Gaussian, Lanczos)
* export covers from comics * export covers from comics
@@ -18,8 +18,10 @@ It can convert comics to different formats to fit your various devices.
### Download ### Download
* [Windows](https://github.com/gen2brain/cbconvert/releases/download/0.7.0/cbconvert-0.7.0-windows-i686.zip) * [Windows x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-windows-x86_64.zip)
* [Linux](https://github.com/gen2brain/cbconvert/releases/download/0.7.0/cbconvert-0.7.0-linux-x86_64.tar.gz) * [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.zip)
* [macOS aarch64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-darwin-aarch64.zip)
### Using cbconvert in file managers to generate FreeDesktop thumbnails ### Using cbconvert in file managers to generate FreeDesktop thumbnails
@@ -29,7 +31,7 @@ Copy cbconvert cli binary to your PATH and create file ~/.local/share/thumbnaile
[Thumbnailer Entry] [Thumbnailer Entry]
TryExec=cbconvert TryExec=cbconvert
Exec=cbconvert thumbnail --quiet --width %s --outfile %o %i Exec=cbconvert thumbnail --quiet --width %s --outfile %o %i
MimeType=application/pdf;application/x-pdf;image/pdf;application/x-cbz;application/x-cbr;application/x-cb7;application/x-cbt;application/epub+zip; MimeType=application/pdf;application/x-pdf;image/pdf;application/x-cbz;application/x-cbr;application/x-cb7;application/x-cbt;application/epub+zip;application/vnd.comicbook-rar;application/vnd.comicbook+zip;
``` ```
This is what it looks like in the PCManFM file manager: This is what it looks like in the PCManFM file manager:
@@ -55,9 +57,11 @@ This is what it looks like in the PCManFM file manager:
        --fit         --fit
            Best fit for required width and height (default "false")             Best fit for required width and height (default "false")
        --format         --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         --quality
            JPEG image quality (default "75")             Image quality (default "75")
        --lossless
            Lossless compression (avif) (default "false")
        --filter         --filter
            0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos (default "2")             0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos (default "2")
        --no-cover         --no-cover
@@ -166,6 +170,6 @@ Extract covers to ~/covers dir for all supported files found in the directory, L
### Compile ### Compile
Install ImageMagick 7 libraries and headers and then install to GOBIN: Install ImageMagick7 and libheif (with libaom) libraries and headers and then install to GOBIN:
`go install github.com/gen2brain/cbconvert/cmd/cbconvert@latest` `go install github.com/gen2brain/cbconvert/cmd/cbconvert@latest`
+150 -42
View File
@@ -24,6 +24,7 @@ import (
"github.com/chai2010/webp" "github.com/chai2010/webp"
_ "github.com/hotei/bmp" _ "github.com/hotei/bmp"
"github.com/strukturag/libheif/go/heif"
"golang.org/x/image/tiff" "golang.org/x/image/tiff"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
@@ -64,10 +65,12 @@ var filters = map[int]imaging.ResampleFilter{
// Options type. // Options type.
type Options struct { 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 Format string
// JPEG image quality // JPEG image quality
Quality int Quality int
// Lossless compression (avif)
Lossless bool
// Image width // Image width
Width int Width int
// Image height // Image height
@@ -182,17 +185,7 @@ func (c *Convertor) convertImage(ctx context.Context, img image.Image, index int
} }
switch c.Opts.Format { switch c.Opts.Format {
case "jpeg": case "jpeg", "png", "tiff", "webp", "avif":
err = c.encodeImage(img, fileName)
if err != nil {
return err
}
case "png":
err = c.encodeImage(img, fileName)
if err != nil {
return err
}
case "tiff":
err = c.encodeImage(img, fileName) err = c.encodeImage(img, fileName)
if err != nil { if err != nil {
return err return err
@@ -203,11 +196,6 @@ func (c *Convertor) convertImage(ctx context.Context, img image.Image, index int
if err != nil { if err != nil {
return err return err
} }
case "webp":
err = c.encodeImage(img, fileName)
if err != nil {
return err
}
} }
return nil return nil
@@ -288,9 +276,14 @@ func (c *Convertor) levelImage(img image.Image) (image.Image, error) {
} }
blob := mw.GetImageBlob() blob := mw.GetImageBlob()
i, err := c.decodeImage(bytes.NewReader(blob), "levels")
var i image.Image
i, err = c.decodeImage(bytes.NewReader(blob), "levels")
if err != nil { if err != nil {
return img, fmt.Errorf("levelImage: %w", err) i, err = c.decodeIM(bytes.NewReader(blob), "levels")
if err != nil {
return nil, fmt.Errorf("levelImage: %w", err)
}
} }
return i, nil return i, nil
@@ -389,10 +382,14 @@ func (c *Convertor) convertArchive(fileName string) error {
continue continue
} }
img, err := c.decodeImage(bytes.NewReader(data), pathName) var img image.Image
img, err = c.decodeImage(bytes.NewReader(data), pathName)
if err != nil {
img, err = c.decodeIM(bytes.NewReader(data), pathName)
if err != nil { if err != nil {
return fmt.Errorf("convertArchive: %w", err) return fmt.Errorf("convertArchive: %w", err)
} }
}
if cover == pathName && c.Opts.NoCover { if cover == pathName && c.Opts.NoCover {
img = c.transformImage(img) img = c.transformImage(img)
@@ -461,7 +458,7 @@ func (c *Convertor) convertDirectory(dirPath string) error {
if c.isNonImage(img) && !c.Opts.NoNonImage { if c.isNonImage(img) && !c.Opts.NoNonImage {
err = c.copyFile(file, filepath.Join(c.Workdir, filepath.Base(img))) err = c.copyFile(file, filepath.Join(c.Workdir, filepath.Base(img)))
if err != nil { if err != nil {
return fmt.Errorf("convertArchive: %w", err) return fmt.Errorf("convertDirectory: %w", err)
} }
err = file.Close() err = file.Close()
@@ -471,12 +468,24 @@ func (c *Convertor) convertDirectory(dirPath string) error {
continue continue
} else if c.isImage(img) { } else if c.isImage(img) {
if c.Opts.NoConvert {
i, err := c.decodeImage(file, img) err = c.copyFile(file, filepath.Join(c.Workdir, filepath.Base(img)))
if err != nil { if err != nil {
return fmt.Errorf("convertDirectory: %w", err) return fmt.Errorf("convertDirectory: %w", err)
} }
continue
}
var i image.Image
i, err = c.decodeImage(file, img)
if err != nil {
i, err = c.decodeIM(file, img)
if err != nil {
return fmt.Errorf("coverDirectory: %w", err)
}
}
if c.Opts.NoRGB && !c.isGrayScale(i) { if c.Opts.NoRGB && !c.isGrayScale(i) {
i = c.transformImage(i) i = c.transformImage(i)
err = c.encodeImage(i, filepath.Join(c.Workdir, filepath.Base(img))) err = c.encodeImage(i, filepath.Join(c.Workdir, filepath.Base(img)))
@@ -579,6 +588,47 @@ func (c *Convertor) decodeImage(reader io.Reader, fileName string) (img image.Im
return 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. // encodeImage encodes image to file.
func (c *Convertor) encodeImage(img image.Image, fileName string) error { func (c *Convertor) encodeImage(img image.Image, fileName string) error {
file, err := os.Create(fileName) file, err := os.Create(fileName)
@@ -600,6 +650,18 @@ func (c *Convertor) encodeImage(img image.Image, fileName string) error {
err = jpeg.Encode(file, img, &jpeg.Options{Quality: c.Opts.Quality}) err = jpeg.Encode(file, img, &jpeg.Options{Quality: c.Opts.Quality})
case ".webp": case ".webp":
err = webp.Encode(file, img, &webp.Options{Quality: float32(c.Opts.Quality)}) 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 { if err != nil {
return fmt.Errorf("encodeImage: %w", err) return fmt.Errorf("encodeImage: %w", err)
@@ -647,6 +709,11 @@ func (c *Convertor) encodeIM(i image.Image, fileName string) error {
_ = mw.WriteImage(fileName) _ = mw.WriteImage(fileName)
case ".jpg", ".jpeg": case ".jpg", ".jpeg":
_ = mw.SetImageFormat("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) _ = mw.WriteImage(fileName)
} }
@@ -699,10 +766,14 @@ func (c *Convertor) coverArchive(fileName string) (image.Image, error) {
return nil, fmt.Errorf("coverArchive: %w", err) return nil, fmt.Errorf("coverArchive: %w", err)
} }
img, err := c.decodeImage(bytes.NewReader(data), cover) var img image.Image
img, err = c.decodeImage(bytes.NewReader(data), cover)
if err != nil {
img, err = c.decodeIM(bytes.NewReader(data), cover)
if err != nil { if err != nil {
return nil, fmt.Errorf("coverArchive: %w", err) return nil, fmt.Errorf("coverArchive: %w", err)
} }
}
return img, nil return img, nil
} }
@@ -740,10 +811,14 @@ func (c *Convertor) coverDirectory(dir string) (image.Image, error) {
} }
defer file.Close() defer file.Close()
img, err := c.decodeImage(file, cover) var img image.Image
img, err = c.decodeImage(file, cover)
if err != nil {
img, err = c.decodeIM(file, cover)
if err != nil { if err != nil {
return nil, fmt.Errorf("coverDirectory: %w", err) return nil, fmt.Errorf("coverDirectory: %w", err)
} }
}
return img, nil return img, nil
} }
@@ -846,7 +921,7 @@ func (c *Convertor) isDocument(f string) bool {
// isImage checks if file is image. // isImage checks if file is image.
func (c *Convertor) isImage(f string) bool { 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 { for _, t := range types {
if strings.ToLower(filepath.Ext(f)) == t { if strings.ToLower(filepath.Ext(f)) == t {
return true return true
@@ -917,16 +992,29 @@ func (c *Convertor) coverName(images []string) string {
return "" return ""
} }
for _, i := range images { lower := make([]string, 0)
e := c.baseNoExt(i) for idx, img := range images {
if strings.HasPrefix(i, "cover") || strings.HasPrefix(i, "front") || img = strings.ToLower(img)
strings.HasSuffix(e, "cover") || strings.HasSuffix(e, "front") { lower = append(lower, img)
return i ext := c.baseNoExt(img)
if strings.HasPrefix(img, "cover") || strings.HasPrefix(img, "front") ||
strings.HasSuffix(ext, "cover") || strings.HasSuffix(ext, "front") {
return images[idx]
} }
} }
sort.Sort(sortorder.Natural(images)) sort.Sort(sortorder.Natural(lower))
return images[0] cover := lower[0]
for idx, img := range images {
img = strings.ToLower(img)
if img == cover {
return images[idx]
}
}
return ""
} }
// coverImage returns cover as image.Image. // coverImage returns cover as image.Image.
@@ -1100,16 +1188,36 @@ func (c *Convertor) ExtractThumbnail(filename string, info os.FileInfo) error {
fname = abs fname = abs
} }
_ = mw.SetImageFormat("PNG") err = mw.SetImageFormat("PNG")
_ = mw.SetImageProperty("Software", "CBconvert") if err != nil {
_ = mw.SetImageProperty("Description", "Thumbnail of "+furi) return fmt.Errorf("extractThumbnail: %w", err)
_ = mw.SetImageProperty("Thumb::URI", furi) }
_ = mw.SetImageProperty("Thumb::MTime", strconv.FormatInt(info.ModTime().Unix(), 10)) err = mw.SetImageProperty("Software", "CBconvert")
_ = mw.SetImageProperty("Thumb::Size", strconv.FormatInt(info.Size(), 10)) if err != nil {
_ = mw.SetImageProperty("Thumb::Mimetype", mime.TypeByExtension(filepath.Ext(filename))) return fmt.Errorf("extractThumbnail: %w", err)
}
err = mw.SetImageProperty("Description", "Thumbnail of "+furi)
if err != nil {
return fmt.Errorf("extractThumbnail: %w", err)
}
err = mw.SetImageProperty("Thumb::URI", furi)
if err != nil {
return fmt.Errorf("extractThumbnail: %w", err)
}
err = mw.SetImageProperty("Thumb::MTime", strconv.FormatInt(info.ModTime().Unix(), 10))
if err != nil {
return fmt.Errorf("extractThumbnail: %w", err)
}
err = mw.SetImageProperty("Thumb::Size", strconv.FormatInt(info.Size(), 10))
if err != nil {
return fmt.Errorf("extractThumbnail: %w", err)
}
err = mw.SetImageProperty("Thumb::Mimetype", mime.TypeByExtension(filepath.Ext(filename)))
if err != nil {
return fmt.Errorf("extractThumbnail: %w", err)
}
_ = mw.WriteImage(fname) return mw.WriteImage(fname)
return nil
} }
// Convert converts comic book. // Convert converts comic book.
+7 -8
View File
@@ -1,11 +1,9 @@
module cbconvert module github.com/gen2brain/cbconvert/cmd/cbconvert
go 1.19 go 1.19
replace github.com/gen2brain/cbconvert => ../../
require ( require (
github.com/gen2brain/cbconvert v0.0.0-20170124143008-5df10a58ee74 github.com/gen2brain/cbconvert v0.0.0-20220909080114-0963cb176280
github.com/schollz/progressbar/v3 v3.10.0 github.com/schollz/progressbar/v3 v3.10.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
) )
@@ -14,15 +12,16 @@ require (
github.com/chai2010/webp v1.1.1 // indirect github.com/chai2010/webp v1.1.1 // indirect
github.com/disintegration/imaging v1.6.2 // indirect github.com/disintegration/imaging v1.6.2 // indirect
github.com/fvbommel/sortorder v1.0.2 // indirect github.com/fvbommel/sortorder v1.0.2 // indirect
github.com/gen2brain/go-fitz v1.20.1 // indirect github.com/gen2brain/go-fitz v1.20.2 // indirect
github.com/gen2brain/go-unarr v0.1.6 // indirect github.com/gen2brain/go-unarr v0.1.6 // indirect
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7 // indirect github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/rivo/uniseg v0.3.4 // indirect github.com/rivo/uniseg v0.3.4 // indirect
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect github.com/strukturag/libheif v1.13.0 // indirect
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect golang.org/x/image v0.2.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
gopkg.in/gographics/imagick.v3 v3.4.1 // indirect gopkg.in/gographics/imagick.v3 v3.4.2 // indirect
) )
+32 -8
View File
@@ -7,8 +7,10 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo= github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/gen2brain/go-fitz v1.20.1 h1:i5GPe/58q/gbNqa2mO+ZcTymwowbJEsDOXp7D0JwxgU= github.com/gen2brain/cbconvert v0.0.0-20220909080114-0963cb176280 h1:wtlm/hA4mVv/CcUCm3gWGOH5oA3qNodLh7zoGjQO7cA=
github.com/gen2brain/go-fitz v1.20.1/go.mod h1:UZAxMETTDK4UPpuh80HaRpPzgkSibUihXVzwj2ip5oQ= github.com/gen2brain/cbconvert v0.0.0-20220909080114-0963cb176280/go.mod h1:GO5h+s7160FK66rth/0QS06HgXFwE57jaP3OTX/8Rv0=
github.com/gen2brain/go-fitz v1.20.2 h1:4FPJCU/ImQ32oojBsYn/+oTkRORxbAhAA+Yw1Fm97MA=
github.com/gen2brain/go-fitz v1.20.2/go.mod h1:YbQPODTC/UnQ/RK4JyD3zfpDQ19UKiV85nMMT3XpT0s=
github.com/gen2brain/go-unarr v0.1.6 h1:2TtfIQ2dGuCkgEYa+vPE1ydcpkB3CtBbdYMfRSGLdA8= github.com/gen2brain/go-unarr v0.1.6 h1:2TtfIQ2dGuCkgEYa+vPE1ydcpkB3CtBbdYMfRSGLdA8=
github.com/gen2brain/go-unarr v0.1.6/go.mod h1:P05CsEe8jVEXhxqXqp9mFKUKFV0BKpFmtgNWf8Mcoos= 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 h1:NlUATi3cllRJhpM4mfR9BxiLRXT83bcSLcOa+S8lrME=
@@ -31,19 +33,41 @@ 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/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 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/gographics/imagick.v3 v3.4.1 h1:CClNBnd1UGxH9KAl4Vuwx+jgNRkyKN+cHlbuFPyt+KU= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
gopkg.in/gographics/imagick.v3 v3.4.1/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/gographics/imagick.v3 v3.4.2 h1:vk6oildvhRBVSBfQ4X3raJstApYSeK6CZsyzoSOZk58=
gopkg.in/gographics/imagick.v3 v3.4.2/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA=
+2 -1
View File
@@ -120,8 +120,9 @@ func parseFlags() (cbconvert.Options, []string) {
convert.IntVar(&opts.Width, "width", 0, "Image width") convert.IntVar(&opts.Width, "width", 0, "Image width")
convert.IntVar(&opts.Height, "height", 0, "Image height") convert.IntVar(&opts.Height, "height", 0, "Image height")
convert.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and 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.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.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")
+61 -25
View File
@@ -1,37 +1,73 @@
#!/usr/bin/env bash #!/usr/bin/env bash
MUSL="/usr/x86_64-pc-linux-musl" MUSL_x86_64="/usr/x86_64-pc-linux-musl"
MINGW="/usr/i686-w64-mingw32" MUSL_aarch64="/usr/aarch64-pc-linux-musl"
MINGW_x86_64="/usr/x86_64-w64-mingw32"
MACOS_x86_64="/usr/x86_64-apple-darwin"
MACOS_aarch64="/usr/aarch64-apple-darwin"
VERSION="`git --git-dir ../../.git describe --tags --abbrev=0 2>/dev/null || echo '0.0.0'`" VERSION="`git --git-dir ../../.git describe --tags --abbrev=0 2>/dev/null || echo '0.0.0'`"
BUILDDIR="cbconvert-${VERSION}" BUILDDIR="cbconvert-${VERSION}"; mkdir -p ${BUILDDIR}
mkdir -p ${BUILDDIR}
CC=x86_64-pc-linux-musl-gcc \ CC=x86_64-pc-linux-musl-gcc \
PKG_CONFIG="x86_64-pc-linux-musl-pkg-config" \ PKG_CONFIG="x86_64-pc-linux-musl-pkg-config" \
PKG_CONFIG_PATH="$MUSL/usr/lib/pkgconfig" \ PKG_CONFIG_PATH="$MUSL_x86_64/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$MUSL/usr/lib/pkgconfig" \ PKG_CONFIG_LIBDIR="$MUSL_x86_64/usr/lib/pkgconfig" \
CGO_CFLAGS="-I$MUSL/usr/include" \ CGO_CFLAGS="-I$MUSL_x86_64/usr/include" \
CGO_LDFLAGS="-L$MUSL/usr/lib" \ CGO_LDFLAGS="-L$MUSL_x86_64/usr/lib" \
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \ CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
go build -tags 'extlib static' -v -o ${BUILDDIR}/cbconvert -ldflags "-linkmode external -s -w '-extldflags=-static'" go build -trimpath -tags 'extlib pkgconfig' -v -o ${BUILDDIR}/cbconvert -ldflags "-linkmode external -s -w '-extldflags=-static'" && \
cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && tar -czf "${BUILDDIR}-linux-x86_64.tar.gz" ${BUILDDIR} cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && tar -czf "${BUILDDIR}-linux-x86_64.tar.gz" ${BUILDDIR}
rm -rf ${BUILDDIR} rm -rf ${BUILDDIR}
BUILDDIR="cbconvert-${VERSION}"; mkdir -p ${BUILDDIR}
BUILDDIR="cbconvert-${VERSION}" CC=aarch64-pc-linux-musl-gcc \
mkdir -p ${BUILDDIR} PKG_CONFIG="aarch64-pc-linux-musl-pkg-config" \
PKG_CONFIG_PATH="$MUSL_aarch64/usr/lib/pkgconfig" \
CC=i686-w64-mingw32-gcc \ PKG_CONFIG_LIBDIR="$MUSL_aarch64/usr/lib/pkgconfig" \
PKG_CONFIG="/usr/bin/i686-w64-mingw32-pkg-config" \ CGO_CFLAGS="-I$MUSL_aarch64/usr/include" \
PKG_CONFIG_PATH="$MINGW/usr/lib/pkgconfig" \ CGO_LDFLAGS="-L$MUSL_aarch64/usr/lib" \
PKG_CONFIG_LIBDIR="$MINGW/usr/lib/pkgconfig" \ CGO_ENABLED=1 GOOS=linux GOARCH=arm64 \
CGO_CFLAGS="-I$MINGW/usr/include" \ go build -trimpath -tags 'extlib pkgconfig' -v -o ${BUILDDIR}/cbconvert -ldflags "-linkmode external -s -w '-extldflags=-static'" && \
CGO_LDFLAGS="-L$MINGW/usr/lib" \ cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && tar -czf "${BUILDDIR}-linux-aarch64.tar.gz" ${BUILDDIR}
CGO_ENABLED=1 GOOS=windows GOARCH=386 \ rm -rf ${BUILDDIR}
go build -tags 'extlib static' -v -o ${BUILDDIR}/cbconvert.exe -ldflags "-s -w '-extldflags=-static -Wl,--allow-multiple-definition'"
BUILDDIR="cbconvert-${VERSION}"; mkdir -p ${BUILDDIR}
cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && zip -rq "${BUILDDIR}-windows-i686.zip" ${BUILDDIR} CC=x86_64-w64-mingw32-gcc \
PKG_CONFIG="/usr/bin/x86_64-w64-mingw32-pkg-config" \
PKG_CONFIG_PATH="$MINGW_x86_64/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$MINGW_x86_64/usr/lib/pkgconfig" \
CGO_CFLAGS="-I$MINGW_x86_64/usr/include" \
CGO_LDFLAGS="-L$MINGW_x86_64/usr/lib" \
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 \
go build -trimpath -tags 'extlib pkgconfig' -v -o ${BUILDDIR}/cbconvert.exe -ldflags "-s -w '-extldflags=-static -Wl,--allow-multiple-definition'" && \
cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && zip -rq "${BUILDDIR}-windows-x86_64.zip" ${BUILDDIR}
rm -rf ${BUILDDIR}
export OSXCROSS_PKG_CONFIG_USE_NATIVE_VARIABLES=1
BUILDDIR="cbconvert-${VERSION}"; mkdir -p ${BUILDDIR}
PATH=${PATH}:${MACOS_x86_64}/bin \
CC=x86_64-apple-darwin21.1-clang \
PKG_CONFIG="x86_64-apple-darwin21.1-pkg-config" \
PKG_CONFIG_PATH="$MACOS_x86_64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$MACOS_x86_64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
CGO_CFLAGS="-I$MACOS_x86_64/usr/include -I$MACOS_x86_64/macports/pkgs/opt/local/include" \
CGO_LDFLAGS="-L$MACOS_x86_64/SDK/MacOSX12.1.sdk/usr/lib -L$MACOS_x86_64/macports/pkgs/opt/local/lib -mmacosx-version-min=10.13" \
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 \
go build -trimpath -tags 'extlib pkgconfig' -v -o ${BUILDDIR}/cbconvert -ldflags "-linkmode external -s -w" && \
cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && zip -rq "${BUILDDIR}-darwin-x86_64.zip" ${BUILDDIR}
rm -rf ${BUILDDIR}
export OSXCROSS_PKG_CONFIG_USE_NATIVE_VARIABLES=1
BUILDDIR="cbconvert-${VERSION}"; mkdir -p ${BUILDDIR}
PATH=${PATH}:${MACOS_aarch64}/bin \
CC=aarch64-apple-darwin21.1-clang \
PKG_CONFIG="aarch64-apple-darwin21.1-pkg-config" \
PKG_CONFIG_PATH="$MACOS_aarch64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$MACOS_aarch64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
CGO_CFLAGS="-I$MACOS_aarch64/usr/include -I$MACOS_aarch64/macports/pkgs/opt/local/include" \
CGO_LDFLAGS="-L$MACOS_aarch64/SDK/MacOSX12.1.sdk/usr/lib -L$MACOS_aarch64/macports/pkgs/opt/local/lib -mmacosx-version-min=10.13" \
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 \
go build -trimpath -tags 'extlib pkgconfig' -v -o ${BUILDDIR}/cbconvert -ldflags "-linkmode external -s -w" && \
cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && zip -rq "${BUILDDIR}-darwin-aarch64.zip" ${BUILDDIR}
rm -rf ${BUILDDIR} rm -rf ${BUILDDIR}
+1
View File
@@ -9,6 +9,7 @@ require (
github.com/gen2brain/go-fitz v1.20.1 github.com/gen2brain/go-fitz v1.20.1
github.com/gen2brain/go-unarr v0.1.6 github.com/gen2brain/go-unarr v0.1.6
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7 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/image v0.0.0-20220902085622-e7cb96979f69
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
gopkg.in/gographics/imagick.v3 v3.4.1 gopkg.in/gographics/imagick.v3 v3.4.1
+2
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/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 h1:NlUATi3cllRJhpM4mfR9BxiLRXT83bcSLcOa+S8lrME=
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7/go.mod h1:Hku3FQ2laCEwSv7Z8YkC0er38jLaUycUCbsFkWMr+z4= 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-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 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=