diff --git a/cmd/ffwebp/codecs.go b/cmd/ffwebp/codecs.go index c7db1fd..2eb08d2 100644 --- a/cmd/ffwebp/codecs.go +++ b/cmd/ffwebp/codecs.go @@ -5,4 +5,5 @@ import ( _ "github.com/coalaura/ffwebp/internal/codec/gif" _ "github.com/coalaura/ffwebp/internal/codec/jpeg" _ "github.com/coalaura/ffwebp/internal/codec/png" + _ "github.com/coalaura/ffwebp/internal/codec/tiff" ) diff --git a/internal/codec/png/png.go b/internal/codec/png/png.go index 203399e..bbdc0ba 100644 --- a/internal/codec/png/png.go +++ b/internal/codec/png/png.go @@ -33,11 +33,11 @@ func (impl) Extensions() []string { func (impl) Flags(flags []cli.Flag) []cli.Flag { return append(flags, &cli.IntFlag{ Name: "png.compression", - Usage: "PNG: compression level (0=default, 1=none, 2=speed, 3=best)", - Value: 0, + Usage: "PNG: compression level (0=none, 1=default, 2=speed, 3=best)", + Value: 1, Destination: &compression, Validator: func(value int) error { - if value < 0 || value > 4 { + if value < 0 || value > 3 { return fmt.Errorf("invalid compression level: %q", value) } @@ -76,8 +76,10 @@ func (impl) Encode(writer io.Writer, img image.Image, _ opts.Common) error { func compressionLevel(level int) png.CompressionLevel { switch level { - case 1: + case 0: return png.NoCompression + case 1: + return png.DefaultCompression case 2: return png.BestSpeed case 3: diff --git a/internal/codec/tiff/tiff.go b/internal/codec/tiff/tiff.go new file mode 100644 index 0000000..a78a544 --- /dev/null +++ b/internal/codec/tiff/tiff.go @@ -0,0 +1,102 @@ +package tiff + +import ( + "bytes" + "fmt" + "image" + "io" + + "golang.org/x/image/tiff" + + "github.com/coalaura/ffwebp/internal/codec" + "github.com/coalaura/ffwebp/internal/opts" + "github.com/urfave/cli/v3" +) + +var ( + compression int + predictor bool +) + +func init() { + codec.Register(impl{}) +} + +type impl struct{} + +func (impl) Name() string { + return "tiff" +} + +func (impl) Extensions() []string { + return []string{"tiff", "tif"} +} + +func (impl) Flags(flags []cli.Flag) []cli.Flag { + return append(flags, + &cli.IntFlag{ + Name: "tiff.compression", + Usage: "TIFF: compression (0=none, 1=deflate, 2=lzw, 3=ccitt3, 4=ccitt4)", + Value: 1, + Destination: &compression, + Validator: func(value int) error { + if value < 0 || value > 4 { + return fmt.Errorf("invalid compression: %d", value) + } + + return nil + }, + }, + &cli.BoolFlag{ + Name: "tiff.predictor", + Usage: "TIFF: enable differencing predictor (improves compression for photos)", + Value: false, + Destination: &predictor, + }, + ) +} + +func (impl) Sniff(reader io.ReaderAt) (int, error) { + magicLE := []byte{0x49, 0x49, 0x2A, 0x00} + magicBE := []byte{0x4D, 0x4D, 0x00, 0x2A} + + buf := make([]byte, 4) + + if _, err := reader.ReadAt(buf, 0); err != nil { + return 0, err + } + + if bytes.Equal(buf, magicLE) || bytes.Equal(buf, magicBE) { + return 100, nil + } + + return 0, nil +} + +func (impl) Decode(reader io.Reader) (image.Image, error) { + return tiff.Decode(reader) +} + +func (impl) Encode(writer io.Writer, img image.Image, options opts.Common) error { + return tiff.Encode(writer, img, &tiff.Options{ + Compression: compressionType(compression), + Predictor: predictor, + }) +} + +func compressionType(level int) tiff.CompressionType { + switch level { + case 0: + return tiff.Uncompressed + case 1: + return tiff.Deflate + case 2: + return tiff.LZW + case 3: + return tiff.CCITTGroup3 + case 4: + return tiff.CCITTGroup4 + default: + return tiff.Deflate + } +}