aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2024-05-28 10:08:04 -0400
committerXe Iaso <me@xeiaso.net>2024-05-28 10:09:26 -0400
commit479407203e6d6654974a4bd340f576cb0b167f55 (patch)
treed3c78f9ad4fee1ab1e51b735218764be27ce799f
parent79d43f5be33f97b36402ec8de7db51e36b1e7f45 (diff)
downloadx-479407203e6d6654974a4bd340f576cb0b167f55.tar.xz
x-479407203e6d6654974a4bd340f576cb0b167f55.zip
internal/avif: remove this package
Replace this with a bunch of smaller packages that embed WebAssembly libraries for the following functionality: * AVIF encoding * JXL encoding * WEBP encoding Also include HEIC decoding and add JXL encoding support to uploud/xedn Signed-off-by: Xe Iaso <me@xeiaso.net>
-rw-r--r--.go.mod.sri2
-rw-r--r--cmd/uploud/main.go316
-rw-r--r--cmd/xedn/imgoptimize.go10
-rw-r--r--cmd/xedn/uplodr/main.go56
-rw-r--r--go.mod6
-rw-r--r--go.sum12
-rw-r--r--internal/avif/COPYING121
-rw-r--r--internal/avif/README.md117
-rw-r--r--internal/avif/av1.c215
-rw-r--r--internal/avif/av1.h44
-rw-r--r--internal/avif/avif.go201
-rw-r--r--internal/avif/example_test.go42
-rw-r--r--internal/avif/hacker-nest.avifbin22522 -> 0 bytes
-rw-r--r--internal/avif/mp4.go799
14 files changed, 250 insertions, 1691 deletions
diff --git a/.go.mod.sri b/.go.mod.sri
index 7cc1de8..91bf166 100644
--- a/.go.mod.sri
+++ b/.go.mod.sri
@@ -1 +1 @@
-sha256-dumTVs9kNBwk6glAbRDz/phNyIub/oH3e77rq4GVfTU=
+sha256-aMyVdkAqM0jygChfI6+nTJc7VC45cN43oA1rn6SZzc4=
diff --git a/cmd/uploud/main.go b/cmd/uploud/main.go
index cef798d..b9691e0 100644
--- a/cmd/uploud/main.go
+++ b/cmd/uploud/main.go
@@ -15,30 +15,130 @@ import (
"log"
"os"
"path/filepath"
- "runtime"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
- "github.com/chai2010/webp"
"github.com/disintegration/imaging"
+ "github.com/gen2brain/avif"
+ _ "github.com/gen2brain/heic"
+ "github.com/gen2brain/jpegxl"
+ "github.com/gen2brain/webp"
+ "golang.org/x/sync/errgroup"
"within.website/x/internal"
- "within.website/x/internal/avif"
"within.website/x/tigris"
)
var (
- b2Bucket = flag.String("tigris-bucket", "xedn", "Tigris bucket to dump things to")
-
- avifQuality = flag.Int("avif-quality", 24, "AVIF quality (higher is worse quality)")
avifEncoderSpeed = flag.Int("avif-encoder-speed", 0, "AVIF encoder speed (higher is faster)")
-
- jpegQuality = flag.Int("jpeg-quality", 85, "JPEG quality (lower means lower file size)")
-
- webpQuality = flag.Int("webp-quality", 50, "WEBP quality (higher is worse quality)")
+ jxlEffort = flag.Int("jxl-effort", 7, "JPEG XL encoding effort in the range [1,10]. Sets encoder effort/speed level without affecting decoding speed. Default is 7.")
+ imageQuality = flag.Int("image-quality", 85, "image quality (lower means lower file size)")
+ tigrisBucket = flag.String("tigris-bucket", "xedn", "Tigris bucket to dump things to")
+ webpMethod = flag.Int("webp-method", 4, "WebP encoding method (0-6, 0 is fastest-worst, 6 is slowest-best)")
noEncode = flag.Bool("no-encode", false, "if set, just upload the file directly without encoding")
)
+func main() {
+ internal.HandleStartup()
+
+ if flag.NArg() != 2 {
+ log.Fatalf("usage: %s <filename/folder> <b2 path>", os.Args[0])
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ td, err := os.MkdirTemp("", "uploud")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.RemoveAll(td)
+
+ st, err := os.Stat(flag.Arg(0))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ if st.IsDir() {
+ files, err := os.ReadDir(flag.Arg(0))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, finfo := range files {
+ if !*noEncode {
+ if err := processImage(filepath.Join(flag.Arg(0), finfo.Name()), td); err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ if _, err := copyFile(filepath.Join(flag.Arg(0), finfo.Name()), filepath.Join(td, finfo.Name())); err != nil {
+ log.Fatal(err)
+ }
+ }
+ }
+ } else {
+ if !*noEncode {
+ if err := processImage(flag.Arg(0), td); err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ if _, err := copyFile(flag.Arg(0), filepath.Join(td, filepath.Base(flag.Arg(0)))); err != nil {
+ log.Fatal(err)
+ }
+ }
+ }
+
+ files, err := os.ReadDir(td)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ s3c, err := tigris.Client(context.Background())
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, finfo := range files {
+ log.Printf("uploading %s", finfo.Name())
+ fin, err := os.Open(filepath.Join(td, finfo.Name()))
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer fin.Close()
+
+ st, err := fin.Stat()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ shaSum, err := hashFileSha256(fin)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ md5Sum, err := hashFileMD5(fin)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ _, err = s3c.PutObject(ctx,
+ &s3.PutObjectInput{
+ Body: fin,
+ Bucket: tigrisBucket,
+ Key: aws.String(flag.Arg(1) + "/" + finfo.Name()),
+ ContentType: aws.String(mimeTypes[filepath.Ext(finfo.Name())]),
+ ContentLength: aws.Int64(st.Size()),
+ ChecksumSHA256: aws.String(shaSum),
+ ContentMD5: aws.String(md5Sum),
+ },
+ tigris.WithCreateObjectIfNotExists(),
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
func doAVIF(src image.Image, dstPath string) error {
dst, err := os.Create(dstPath)
if err != nil {
@@ -46,11 +146,12 @@ func doAVIF(src image.Image, dstPath string) error {
}
defer dst.Close()
- err = avif.Encode(dst, src, &avif.Options{
- Threads: runtime.GOMAXPROCS(0),
- Speed: *avifEncoderSpeed,
- Quality: *avifQuality,
+ err = avif.Encode(dst, src, avif.Options{
+ Quality: *imageQuality,
+ QualityAlpha: *imageQuality,
+ Speed: *avifEncoderSpeed,
})
+
if err != nil {
return err
}
@@ -60,6 +161,27 @@ func doAVIF(src image.Image, dstPath string) error {
return nil
}
+func doJXL(src image.Image, dstPath string) error {
+ dst, err := os.Create(dstPath)
+ if err != nil {
+ log.Fatalf("Can't create destination file: %v", err)
+ }
+ defer dst.Close()
+
+ err = jpegxl.Encode(dst, src, jpegxl.Options{
+ Quality: *imageQuality,
+ Effort: *jxlEffort,
+ })
+
+ if err != nil {
+ return err
+ }
+
+ log.Printf("Encoded JPEG XL at %s", dstPath)
+
+ return nil
+}
+
func doWEBP(src image.Image, dstPath string) error {
fout, err := os.Create(dstPath)
if err != nil {
@@ -67,7 +189,10 @@ func doWEBP(src image.Image, dstPath string) error {
}
defer fout.Close()
- err = webp.Encode(fout, src, &webp.Options{Quality: float32(*webpQuality)})
+ err = webp.Encode(fout, src, webp.Options{
+ Quality: *imageQuality,
+ Method: *webpMethod,
+ })
if err != nil {
return err
}
@@ -88,7 +213,7 @@ func doJPEG(src image.Image, dstPath string) error {
}
defer fout.Close()
- if err := jpeg.Encode(fout, src, &jpeg.Options{Quality: *jpegQuality}); err != nil {
+ if err := jpeg.Encode(fout, src, &jpeg.Options{Quality: *imageQuality}); err != nil {
return err
}
@@ -104,7 +229,10 @@ func resizeSmol(src image.Image, dstPath string) error {
}
defer fout.Close()
- dstImg := imaging.Resize(src, 800, 0, imaging.Lanczos)
+ dstImg := src
+ if src.Bounds().Dx() > 800 {
+ dstImg = imaging.Resize(src, 800, 0, imaging.Lanczos)
+ }
enc := png.Encoder{
CompressionLevel: png.BestCompression,
@@ -131,19 +259,49 @@ func processImage(fname, tempDir string) error {
return err
}
- if err := doAVIF(src, filepath.Join(tempDir, fnameBase+".avif")); err != nil {
- return err
- }
+ eg, _ := errgroup.WithContext(context.Background())
- if err := doWEBP(src, filepath.Join(tempDir, fnameBase+".webp")); err != nil {
- return err
- }
+ eg.Go(func() error {
+ if err := doAVIF(src, filepath.Join(tempDir, fnameBase+".avif")); err != nil {
+ return fmt.Errorf("avif: %w", err)
+ }
- if err := doJPEG(src, filepath.Join(tempDir, fnameBase+".jpg")); err != nil {
- return err
- }
+ return nil
+ })
+
+ eg.Go(func() error {
+ if err := doJXL(src, filepath.Join(tempDir, fnameBase+".jxl")); err != nil {
+ return fmt.Errorf("jxl: %w", err)
+ }
+
+ return nil
+ })
+
+ eg.Go(func() error {
+ if err := doWEBP(src, filepath.Join(tempDir, fnameBase+".webp")); err != nil {
+ return fmt.Errorf("webp: %w", err)
+ }
+
+ return nil
+ })
- if err := resizeSmol(src, filepath.Join(tempDir, fnameBase+"-smol.png")); err != nil {
+ eg.Go(func() error {
+ if err := doJPEG(src, filepath.Join(tempDir, fnameBase+".jpg")); err != nil {
+ return fmt.Errorf("webp: %w", err)
+ }
+
+ return nil
+ })
+
+ eg.Go(func() error {
+ if err := resizeSmol(src, filepath.Join(tempDir, fnameBase+"-smol.png")); err != nil {
+ return fmt.Errorf("smol: %w", err)
+ }
+
+ return nil
+ })
+
+ if err := eg.Wait(); err != nil {
return err
}
@@ -175,111 +333,13 @@ func copyFile(src, dst string) (int64, error) {
return nBytes, err
}
-func main() {
- internal.HandleStartup()
-
- if flag.NArg() != 2 {
- log.Fatalf("usage: %s <filename/folder> <b2 path>", os.Args[0])
- }
-
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
- td, err := os.MkdirTemp("", "uploud")
- if err != nil {
- log.Fatal(err)
- }
- defer os.RemoveAll(td)
-
- st, err := os.Stat(flag.Arg(0))
- if err != nil {
- log.Fatal(err)
- }
-
- if st.IsDir() {
- files, err := os.ReadDir(flag.Arg(0))
- if err != nil {
- log.Fatal(err)
- }
-
- for _, finfo := range files {
- if !*noEncode {
- if err := processImage(filepath.Join(flag.Arg(0), finfo.Name()), td); err != nil {
- log.Fatal(err)
- }
- } else {
- if _, err := copyFile(filepath.Join(flag.Arg(0), finfo.Name()), filepath.Join(td, finfo.Name())); err != nil {
- log.Fatal(err)
- }
- }
- }
- } else {
- if !*noEncode {
- if err := processImage(flag.Arg(0), td); err != nil {
- log.Fatal(err)
- }
- } else {
- if _, err := copyFile(flag.Arg(0), filepath.Join(td, filepath.Base(flag.Arg(0)))); err != nil {
- log.Fatal(err)
- }
- }
- }
-
- files, err := os.ReadDir(td)
- if err != nil {
- log.Fatal(err)
- }
-
- s3c, err := tigris.Client(context.Background())
- if err != nil {
- log.Fatal(err)
- }
-
- for _, finfo := range files {
- log.Printf("uploading %s", finfo.Name())
- fin, err := os.Open(filepath.Join(td, finfo.Name()))
- if err != nil {
- log.Fatal(err)
- }
- defer fin.Close()
-
- st, err := fin.Stat()
- if err != nil {
- log.Fatal(err)
- }
-
- shaSum, err := hashFileSha256(fin)
- if err != nil {
- log.Fatal(err)
- }
-
- md5Sum, err := hashFileMD5(fin)
- if err != nil {
- log.Fatal(err)
- }
-
- _, err = s3c.PutObject(ctx,
- &s3.PutObjectInput{
- Body: fin,
- Bucket: b2Bucket,
- Key: aws.String(flag.Arg(1) + "/" + finfo.Name()),
- ContentType: aws.String(mimeTypes[filepath.Ext(finfo.Name())]),
- ContentLength: aws.Int64(st.Size()),
- ChecksumSHA256: aws.String(shaSum),
- ContentMD5: aws.String(md5Sum),
- },
- tigris.WithCreateObjectIfNotExists(),
- )
- if err != nil {
- log.Fatal(err)
- }
- }
-}
-
var mimeTypes = map[string]string{
".avif": "image/avif",
".webp": "image/webp",
".jpg": "image/jpeg",
+ ".jpeg": "image/jpeg",
+ ".heic": "image/heic",
+ ".jxl": "image/jxl",
".png": "image/png",
".svg": "image/svg+xml",
".wasm": "application/wasm",
diff --git a/cmd/xedn/imgoptimize.go b/cmd/xedn/imgoptimize.go
index 5b509e7..c82c362 100644
--- a/cmd/xedn/imgoptimize.go
+++ b/cmd/xedn/imgoptimize.go
@@ -15,12 +15,14 @@ import (
"strings"
"time"
- "github.com/chai2010/webp"
"github.com/disintegration/imaging"
+ "github.com/gen2brain/avif"
+ _ "github.com/gen2brain/heic"
+ _ "github.com/gen2brain/jpegxl"
+ "github.com/gen2brain/webp"
"go.etcd.io/bbolt"
"golang.org/x/sync/singleflight"
"tailscale.com/metrics"
- "within.website/x/internal/avif"
)
type OptimizedImageServer struct {
@@ -138,11 +140,11 @@ func (ois *OptimizedImageServer) ResizeTo(widthPixels int, character, mood, form
return nil, err
}
case "webp":
- if err := webp.Encode(&result, dstImg, &webp.Options{Quality: 95}); err != nil {
+ if err := webp.Encode(&result, dstImg, webp.Options{Quality: 95}); err != nil {
return nil, err
}
case "avif":
- if err := avif.Encode(&result, dstImg, &avif.Options{Quality: 48, Speed: avif.MaxSpeed}); err != nil {
+ if err := avif.Encode(&result, dstImg, avif.Options{Quality: 95, Speed: 7}); err != nil {
return nil, err
}
default:
diff --git a/cmd/xedn/uplodr/main.go b/cmd/xedn/uplodr/main.go
index e1a9c10..148a0e6 100644
--- a/cmd/xedn/uplodr/main.go
+++ b/cmd/xedn/uplodr/main.go
@@ -14,18 +14,19 @@ import (
"net"
"os"
"path/filepath"
- "runtime"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
- "github.com/chai2010/webp"
"github.com/disintegration/imaging"
+ "github.com/gen2brain/avif"
+ _ "github.com/gen2brain/heic"
+ "github.com/gen2brain/jpegxl"
+ "github.com/gen2brain/webp"
"google.golang.org/grpc"
"within.website/x/cmd/xedn/uplodr/pb"
"within.website/x/internal"
- "within.website/x/internal/avif"
"within.website/x/tigris"
)
@@ -40,12 +41,10 @@ var (
tigrisBucket = flag.String("bucket-name", "xedn", "Tigris bucket to dump things to")
- avifQuality = flag.Int("avif-quality", 8, "AVIF quality (higher is worse quality)")
avifEncoderSpeed = flag.Int("avif-encoder-speed", 0, "AVIF encoder speed (higher is faster)")
-
- jpegQuality = flag.Int("jpeg-quality", 90, "JPEG quality (lower means lower file size)")
-
- webpQuality = flag.Int("webp-quality", 9, "WEBP quality (higher is worse quality)")
+ jxlEffort = flag.Int("jxl-effort", 7, "JPEG XL encoding effort in the range [1,10]. Sets encoder effort/speed level without affecting decoding speed. Default is 7.")
+ imageQuality = flag.Int("image-quality", 85, "image quality (lower means lower file size)")
+ webpMethod = flag.Int("webp-method", 4, "WebP encoding method (0-6, 0 is fastest-worst, 6 is slowest-best)")
)
func main() {
@@ -195,7 +194,7 @@ func (s *Server) Upload(ctx context.Context, ur *pb.UploadReq) (*pb.UploadResp,
errs = append(errs, fmt.Errorf("while uploading %s to b2: %w", path, err))
continue
}
- slog.Debug("uploaded", "to", "tigris", "key", key)
+ slog.Debug("uploaded", "to", "b2", "key", key)
result = append(result, &pb.Variant{
Url: fmt.Sprintf("https://cdn.xeiaso.net/file/christine-static/%s/%s", ur.GetFolder(), fname),
@@ -221,16 +220,36 @@ func doAVIF(src image.Image, dstPath string) error {
}
defer dst.Close()
- err = avif.Encode(dst, src, &avif.Options{
- Threads: runtime.GOMAXPROCS(0),
- Speed: *avifEncoderSpeed,
- Quality: *avifQuality,
+ if err := avif.Encode(dst, src, avif.Options{
+ Quality: *imageQuality,
+ QualityAlpha: *imageQuality,
+ Speed: *avifEncoderSpeed,
+ }); err != nil {
+ return err
+ }
+
+ log.Printf("Encoded AVIF at %s", dstPath)
+
+ return nil
+}
+
+func doJXL(src image.Image, dstPath string) error {
+ dst, err := os.Create(dstPath)
+ if err != nil {
+ log.Fatalf("Can't create destination file: %v", err)
+ }
+ defer dst.Close()
+
+ err = jpegxl.Encode(dst, src, jpegxl.Options{
+ Quality: *imageQuality,
+ Effort: *jxlEffort,
})
+
if err != nil {
return err
}
- log.Printf("Encoded AVIF at %s", dstPath)
+ log.Printf("Encoded JPEG XL at %s", dstPath)
return nil
}
@@ -242,7 +261,10 @@ func doWEBP(src image.Image, dstPath string) error {
}
defer fout.Close()
- err = webp.Encode(fout, src, &webp.Options{Quality: float32(*webpQuality)})
+ err = webp.Encode(fout, src, webp.Options{
+ Quality: *imageQuality,
+ Method: *webpMethod,
+ })
if err != nil {
return err
}
@@ -263,7 +285,9 @@ func doJPEG(src image.Image, dstPath string) error {
}
defer fout.Close()
- if err := jpeg.Encode(fout, src, &jpeg.Options{Quality: *jpegQuality}); err != nil {
+ if err := jpeg.Encode(fout, src, &jpeg.Options{
+ Quality: *imageQuality,
+ }); err != nil {
return err
}
diff --git a/go.mod b/go.mod
index fcfbd0d..97a7286 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,6 @@ require (
github.com/bwmarrin/discordgo v0.28.1
github.com/c-bata/go-prompt v0.2.6
github.com/cenkalti/backoff/v4 v4.3.0
- github.com/chai2010/webp v1.1.1
github.com/danrusei/gobot-bsky v0.1.0
github.com/disintegration/imaging v1.6.2
github.com/dop251/goja v0.0.0-20230531210528-d7324b2d74f7
@@ -23,6 +22,10 @@ require (
github.com/expr-lang/expr v1.16.3
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51
github.com/fogleman/primitive v0.0.0-20190214200932-673f57e7b1b5
+ github.com/gen2brain/avif v0.3.1
+ github.com/gen2brain/heic v0.3.0
+ github.com/gen2brain/jpegxl v0.3.0
+ github.com/gen2brain/webp v0.4.4
github.com/go-interpreter/wagon v0.6.0
github.com/go-shiori/go-readability v0.0.0-20230421032831-c66949dfc0ad
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0
@@ -104,6 +107,7 @@ require (
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
+ github.com/ebitengine/purego v0.7.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanw/esbuild v0.19.11 // indirect
github.com/fasthttp/router v1.5.0 // indirect
diff --git a/go.sum b/go.sum
index ef85a3b..e6d49cb 100644
--- a/go.sum
+++ b/go.sum
@@ -213,8 +213,6 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
-github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
@@ -288,6 +286,8 @@ github.com/eaburns/peggy v1.0.2/go.mod h1:X2pbl0EV5erfnK8kSGwo0lBCgMGokvR1E6KerA
github.com/eaburns/pretty v0.0.0-20170305202417-362524b72369/go.mod h1:iW/TU1T4mA4w2KzqNbBCjacPFdJ9PfGvNSxr8ajT/iM=
github.com/eaburns/pretty v1.0.0 h1:00W1wrrtMXUSqLPN0txS8j7g9qFXy6nA5vZVqVQOo6w=
github.com/eaburns/pretty v1.0.0/go.mod h1:retcK8A0KEgdmb0nuxhvyxixwCmEPO7SKlK0IJhjg8A=
+github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
+github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
@@ -331,6 +331,14 @@ github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADi
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/gaissmai/bart v0.4.1 h1:G1t58voWkNmT47lBDawH5QhtTDsdqRIO+ftq5x4P9Ls=
github.com/gaissmai/bart v0.4.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
+github.com/gen2brain/avif v0.3.1 h1:womS2LKvhS/dSR3zIKUxtJW+riGlY48akGWqc+YgHtE=
+github.com/gen2brain/avif v0.3.1/go.mod h1:s9sI2zo2cF6EdyRVCtnIfwL/Qb3k0TkOIEsz6ovK1ms=
+github.com/gen2brain/heic v0.3.0 h1:YDw7cerzjnxmb+/o5RAEpRy9j4jsFYCh9DuP1NDOc7Q=
+github.com/gen2brain/heic v0.3.0/go.mod h1:+x0Y/m2EP1kd6mWvC131B3IK4eoKtLBBqJJ1uJB8CT8=
+github.com/gen2brain/jpegxl v0.3.0 h1:hfnZykNPnyVksr6gtHVoiEuOHfJwXnrJLtP4RN1B7Lk=
+github.com/gen2brain/jpegxl v0.3.0/go.mod h1:zi+BuuV3cTYRG3dt6dS0nT3DxVLZXkQUCIOirPEzKXk=
+github.com/gen2brain/webp v0.4.4 h1:1cpydwbHid5ReumxE3kHWua9eimKONjeEo5CxeUcAtE=
+github.com/gen2brain/webp v0.4.4/go.mod h1:4B2bZAeutMBvabnV96t3S06eW3wWfr6uU5nw4X5j+zc=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
diff --git a/internal/avif/COPYING b/internal/avif/COPYING
deleted file mode 100644
index 0e259d4..0000000
--- a/internal/avif/COPYING
+++ /dev/null
@@ -1,121 +0,0 @@
-Creative Commons Legal Code
-
-CC0 1.0 Universal
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
- PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
- THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
- HEREUNDER.
-
-Statement of Purpose
-
-The laws of most jurisdictions throughout the world automatically confer
-exclusive Copyright and Related Rights (defined below) upon the creator
-and subsequent owner(s) (each and all, an "owner") of an original work of
-authorship and/or a database (each, a "Work").
-
-Certain owners wish to permanently relinquish those rights to a Work for
-the purpose of contributing to a commons of creative, cultural and
-scientific works ("Commons") that the public can reliably and without fear
-of later claims of infringement build upon, modify, incorporate in other
-works, reuse and redistribute as freely as possible in any form whatsoever
-and for any purposes, including without limitation commercial purposes.
-These owners may contribute to the Commons to promote the ideal of a free
-culture and the further production of creative, cultural and scientific
-works, or to gain reputation or greater distribution for their Work in
-part through the use and efforts of others.
-
-For these and/or other purposes and motivations, and without any
-expectation of additional consideration or compensation, the person
-associating CC0 with a Work (the "Affirmer"), to the extent that he or she
-is an owner of Copyright and Related Rights in the Work, voluntarily
-elects to apply CC0 to the Work and publicly distribute the Work under its
-terms, with knowledge of his or her Copyright and Related Rights in the
-Work and the meaning and intended legal effect of CC0 on those rights.
-
-1. Copyright and Related Rights. A Work made available under CC0 may be
-protected by copyright and related or neighboring rights ("Copyright and
-Related Rights"). Copyright and Related Rights include, but are not
-limited to, the following:
-
- i. the right to reproduce, adapt, distribute, perform, display,
- communicate, and translate a Work;
- ii. moral rights retained by the original author(s) and/or performer(s);
-iii. publicity and privacy rights pertaining to a person's image or
- likeness depicted in a Work;
- iv. rights protecting against unfair competition in regards to a Work,
- subject to the limitations in paragraph 4(a), below;
- v. rights protecting the extraction, dissemination, use and reuse of data
- in a Work;
- vi. database rights (such as those arising under Directive 96/9/EC of the
- European Parliament and of the Council of 11 March 1996 on the legal
- protection of databases, and under any national implementation
- thereof, including any amended or successor version of such
- directive); and
-vii. other similar, equivalent or corresponding rights throughout the
- world based on applicable law or treaty, and any national
- implementations thereof.
-
-2. Waiver. To the greatest extent permitted by, but not in contravention
-of, applicable law, Affirmer hereby overtly, fully, permanently,
-irrevocably and unconditionally waives, abandons, and surrenders all of
-Affirmer's Copyright and Related Rights and associated claims and causes
-of action, whether now known or unknown (including existing as well as
-future claims and causes of action), in the Work (i) in all territories
-worldwide, (ii) for the maximum duration provided by applicable law or
-treaty (including future time extensions), (iii) in any current or future
-medium and for any number of copies, and (iv) for any purpose whatsoever,
-including without limitation commercial, advertising or promotional
-purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
-member of the public at large and to the detriment of Affirmer's heirs and
-successors, fully intending that such Waiver shall not be subject to
-revocation, rescission, cancellation, termination, or any other legal or
-equitable action to disrupt the quiet enjoyment of the Work by the public
-as contemplated by Affirmer's express Statement of Purpose.
-
-3. Public License Fallback. Should any part of the Waiver for any reason
-be judged legally invalid or ineffective under applicable law, then the
-Waiver shall be preserved to the maximum extent permitted taking into
-account Affirmer's express Statement of Purpose. In addition, to the
-extent the Waiver is so judged Affirmer hereby grants to each affected
-person a royalty-free, non transferable, non sublicensable, non exclusive,
-irrevocable and unconditional license to exercise Affirmer's Copyright and
-Related Rights in the Work (i) in all territories worldwide, (ii) for the
-maximum duration provided by applicable law or treaty (including future
-time extensions), (iii) in any current or future medium and for any number
-of copies, and (iv) for any purpose whatsoever, including without
-limitation commercial, advertising or promotional purposes (the
-"License"). The License shall be deemed effective as of the date CC0 was
-applied by Affirmer to the Work. Should any part of the License for any
-reason be judged legally invalid or ineffective under applicable law, such
-partial invalidity or ineffectiveness shall not invalidate the remainder
-of the License, and in such case Affirmer hereby affirms that he or she
-will not (i) exercise any of his or her remaining Copyright and Related
-Rights in the Work or (ii) assert any associated claims and causes of
-action with respect to the Work, in either case contrary to Affirmer's
-express Statement of Purpose.
-
-4. Limitations and Disclaimers.
-
- a. No trademark or patent rights held by Affirmer are waived, abandoned,
- surrendered, licensed or otherwise affected by this document.
- b. Affirmer offers the Work as-is and makes no representations or
- warranties of any kind concerning the Work, express, implied,
- statutory or otherwise, including without limitation warranties of
- title, merchantability, fitness for a particular purpose, non
- infringement, or the absence of latent or other defects, accuracy, or
- the present or absence of errors, whether or not discoverable, all to
- the greatest extent permissible under applicable law.
- c. Affirmer disclaims responsibility for clearing rights of other persons
- that may apply to the Work or any use thereof, including without
- limitation any person's Copyright and Related Rights in the Work.
- Further, Affirmer disclaims responsibility for obtaining any necessary
- consents, permissions or other rights required for any use of the
- Work.
- d. Affirmer understands and acknowledges that Creative Commons is not a
- party to this document and has no duty or obligation with respect to
- this CC0 or use of the Work.
diff --git a/internal/avif/README.md b/internal/avif/README.md
deleted file mode 100644
index 9c9fef6..0000000
--- a/internal/avif/README.md
+++ /dev/null
@@ -1,117 +0,0 @@
-# go-avif [![Build Status](https://travis-ci.org/Kagami/go-avif.svg?branch=master)](https://travis-ci.org/Kagami/go-avif) [![GoDoc](https://godoc.org/github.com/Kagami/go-avif?status.svg)](https://godoc.org/github.com/Kagami/go-avif)
-
-go-avif implements
-AVIF ([AV1 Still Image File Format](https://aomediacodec.github.io/av1-avif/))
-encoder for Go using libaom, the [high quality](https://github.com/Kagami/av1-bench)
-AV1 codec.
-
-## Requirements
-
-Make sure libaom is installed. On typical Linux distro just run:
-
-#### Debian (and derivatives):
-```bash
-sudo apt-get install libaom-dev
-```
-
-#### RHEL (and derivatives):
-```bash
-sudo dnf install libaom-devel
-```
-
-## Usage
-
-To use go-avif in your Go code:
-
-```go
-import "github.com/Kagami/go-avif"
-```
-
-To install go-avif in your $GOPATH:
-
-```bash
-go get github.com/Kagami/go-avif
-```
-
-For further details see [GoDoc documentation](https://godoc.org/github.com/Kagami/go-avif).
-
-## Example
-
-```go
-package main
-
-import (
- "image"
- _ "image/jpeg"
- "log"
- "os"
-
- "github.com/Kagami/go-avif"
-)
-
-func main() {
- if len(os.Args) != 3 {
- log.Fatalf("Usage: %s src.jpg dst.avif", os.Args[0])
- }
-
- srcPath := os.Args[1]
- src, err := os.Open(srcPath)
- if err != nil {
- log.Fatalf("Can't open sorce file: %v", err)
- }
-
- dstPath := os.Args[2]
- dst, err := os.Create(dstPath)
- if err != nil {
- log.Fatalf("Can't create destination file: %v", err)
- }
-
- img, _, err := image.Decode(src)
- if err != nil {
- log.Fatalf("Can't decode source file: %v", err)
- }
-
- err = avif.Encode(dst, img, nil)
- if err != nil {
- log.Fatalf("Can't encode source image: %v", err)
- }
-
- log.Printf("Encoded AVIF at %s", dstPath)
-}
-```
-
-## CLI
-
-go-avif comes with handy CLI utility `avif`. It supports encoding of JPEG and
-PNG files to AVIF:
-
-```bash
-# Compile and put avif binary to $GOPATH/bin
-go get github.com/Kagami/go-avif/...
-
-# Encode JPEG to AVIF with default settings
-avif -e cat.jpg -o kitty.avif
-
-# Encode PNG with slowest speed
-avif -e dog.png -o doggy.avif --best -q 15
-
-# Lossless encoding
-avif -e pig.png -o piggy.avif --lossless
-
-# Show help
-avif -h
-```
-
-Static 64-bit builds for Windows, macOS and Linux are available at
-[releases page](https://github.com/Kagami/go-avif/releases). They include
-latest libaom from git at the moment of build.
-
-## Display
-
-To display resulting AVIF files take a look at software listed
-[here](https://github.com/AOMediaCodec/av1-avif/wiki#demuxers--players). E.g.
-use [avif.js](https://kagami.github.io/avif.js/) web viewer.
-
-## License
-
-go-avif is licensed under [CC0](COPYING).
diff --git a/internal/avif/av1.c b/internal/avif/av1.c
deleted file mode 100644
index 24cbeb8..0000000
--- a/internal/avif/av1.c
+++ /dev/null
@@ -1,215 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <aom/aom_encoder.h>
-#include <aom/aomcx.h>
-#include "av1.h"
-
-#define SET_CODEC_CONTROL(ctrl, val) \
- {if (aom_codec_control(ctx, ctrl, val)) return AVIF_ERROR_CODEC_INIT;}
-
-typedef struct {
- aom_img_fmt_t fmt;
- int dst_c_dec_h;
- int dst_c_dec_v;
- int bps;
- int bytes_per_sample;
-} avif_format;
-
-static avif_format convert_subsampling(const avif_subsampling subsampling) {
- avif_format fmt = { 0 };
- switch (subsampling) {
- case AVIF_SUBSAMPLING_I420:
- fmt.fmt = AOM_IMG_FMT_I420;
- fmt.dst_c_dec_h = 2;
- fmt.dst_c_dec_v = 2;
- fmt.bps = 12;
- fmt.bytes_per_sample = 1;
- break;
- default:
- assert(0);
- }
- return fmt;
-}
-
-// We don't use aom_img_wrap() because it forces padding for odd picture
-// sizes (c) libaom/common/y4minput.c
-static void convert_frame(const avif_frame *frame, aom_image_t *aom_frame) {
- memset(aom_frame, 0, sizeof(*aom_frame));
- avif_format fmt = convert_subsampling(frame->subsampling);
- aom_frame->fmt = fmt.fmt;
- aom_frame->w = aom_frame->d_w = frame->width;
- aom_frame->h = aom_frame->d_h = frame->height;
- aom_frame->x_chroma_shift = fmt.dst_c_dec_h >> 1;
- aom_frame->y_chroma_shift = fmt.dst_c_dec_v >> 1;
- aom_frame->bps = fmt.bps;
- int pic_sz = frame->width * frame->height * fmt.bytes_per_sample;
- int c_w = (frame->width + fmt.dst_c_dec_h - 1) / fmt.dst_c_dec_h;
- c_w *= fmt.bytes_per_sample;
- int c_h = (frame->height + fmt.dst_c_dec_v - 1) / fmt.dst_c_dec_v;
- int c_sz = c_w * c_h;
- aom_frame->stride[AOM_PLANE_Y] = frame->width * fmt.bytes_per_sample;
- aom_frame->stride[AOM_PLANE_U] = aom_frame->stride[AOM_PLANE_V] = c_w;
- aom_frame->planes[AOM_PLANE_Y] = frame->data;
- aom_frame->planes[AOM_PLANE_U] = frame->data + pic_sz;
- aom_frame->planes[AOM_PLANE_V] = frame->data + pic_sz + c_sz;
-}
-
-static int get_frame_stats(aom_codec_ctx_t *ctx,
- const aom_image_t *frame,
- aom_fixed_buf_t *stats) {
- if (aom_codec_encode(ctx, frame, 1/*pts*/, 1/*duration*/, 0/*flags*/))
- return AVIF_ERROR_FRAME_ENCODE;
-
- const aom_codec_cx_pkt_t *pkt = NULL;
- aom_codec_iter_t iter = NULL;
- int got_pkts = 0;
- while ((pkt = aom_codec_get_cx_data(ctx, &iter)) != NULL) {
- got_pkts = 1;
- if (pkt->kind == AOM_CODEC_STATS_PKT) {
- const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
- const size_t pkt_si