mirror of
https://github.com/coalaura/ffwebp.git
synced 2025-07-18 06:14:34 +00:00
rewrite
This commit is contained in:
50
internal/codec/codec.go
Normal file
50
internal/codec/codec.go
Normal file
@ -0,0 +1,50 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"image"
|
||||
"io"
|
||||
|
||||
"github.com/coalaura/ffwebp/internal/opts"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
type Codec interface {
|
||||
Name() string
|
||||
|
||||
Flags([]cli.Flag) []cli.Flag
|
||||
Extensions() []string
|
||||
|
||||
Sniff(io.ReaderAt) (int, error)
|
||||
Decode(io.Reader) (image.Image, error)
|
||||
Encode(io.Writer, image.Image, opts.Common) error
|
||||
}
|
||||
|
||||
var codecs = map[string]Codec{}
|
||||
|
||||
func Register(c Codec) {
|
||||
codecs[c.Name()] = c
|
||||
}
|
||||
|
||||
func Flags(flags []cli.Flag) []cli.Flag {
|
||||
for _, codec := range codecs {
|
||||
flags = codec.Flags(flags)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func Get(name string) (Codec, bool) {
|
||||
c, ok := codecs[name]
|
||||
|
||||
return c, ok
|
||||
}
|
||||
|
||||
func All() []Codec {
|
||||
out := make([]Codec, 0, len(codecs))
|
||||
|
||||
for _, c := range codecs {
|
||||
out = append(out, c)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
72
internal/codec/detect.go
Normal file
72
internal/codec/detect.go
Normal file
@ -0,0 +1,72 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Sniff(reader io.Reader) (Codec, io.Reader, error) {
|
||||
buf, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ra := bytes.NewReader(buf)
|
||||
|
||||
var (
|
||||
guess Codec
|
||||
best int
|
||||
)
|
||||
|
||||
for _, codec := range codecs {
|
||||
confidence, err := codec.Sniff(ra)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if confidence > best {
|
||||
best = confidence
|
||||
guess = codec
|
||||
}
|
||||
}
|
||||
|
||||
if guess == nil {
|
||||
return nil, nil, errors.New("unknown format")
|
||||
}
|
||||
|
||||
return guess, bytes.NewReader(buf), nil
|
||||
}
|
||||
|
||||
func Detect(output, override string) (Codec, error) {
|
||||
if override != "" {
|
||||
codec, ok := codecs[override]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported output codec: %q", override)
|
||||
}
|
||||
|
||||
return codec, nil
|
||||
}
|
||||
|
||||
if output == "-" {
|
||||
return nil, errors.New("missing codec for output")
|
||||
}
|
||||
|
||||
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(output), "."))
|
||||
if ext == "" {
|
||||
return nil, fmt.Errorf("output filename %q has no extension", output)
|
||||
}
|
||||
|
||||
for _, codec := range codecs {
|
||||
for _, alias := range codec.Extensions() {
|
||||
if ext == strings.ToLower(alias) {
|
||||
return codec, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported or unknown file extension: %q", ext)
|
||||
}
|
56
internal/codec/jpeg/jpeg.go
Normal file
56
internal/codec/jpeg/jpeg.go
Normal file
@ -0,0 +1,56 @@
|
||||
package jpeg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"io"
|
||||
|
||||
"github.com/coalaura/ffwebp/internal/codec"
|
||||
"github.com/coalaura/ffwebp/internal/opts"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
type impl struct{}
|
||||
|
||||
func init() {
|
||||
codec.Register(impl{})
|
||||
}
|
||||
|
||||
func (impl) Name() string {
|
||||
return "jpeg"
|
||||
}
|
||||
|
||||
func (impl) Extensions() []string {
|
||||
return []string{"jpg", "jpeg", "jpe"}
|
||||
}
|
||||
|
||||
func (impl) Flags(flags []cli.Flag) []cli.Flag {
|
||||
return flags
|
||||
}
|
||||
|
||||
func (impl) Sniff(reader io.ReaderAt) (int, error) {
|
||||
marker := []byte{0xFF, 0xD8, 0xFF}
|
||||
|
||||
buf := make([]byte, 3)
|
||||
|
||||
if _, err := reader.ReadAt(buf, 0); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if bytes.Equal(buf, marker) {
|
||||
return 100, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (impl) Decode(reader io.Reader) (image.Image, error) {
|
||||
return jpeg.Decode(reader)
|
||||
}
|
||||
|
||||
func (impl) Encode(writer io.Writer, img image.Image, options opts.Common) error {
|
||||
return jpeg.Encode(writer, img, &jpeg.Options{
|
||||
Quality: options.Quality,
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user