diff --git a/pkg/images/imgconv/default_format.go b/pkg/images/imgconv/default_format.go new file mode 100644 index 0000000000000000000000000000000000000000..d57a97020ec815d19471cfffa6125c1db6acb166 --- /dev/null +++ b/pkg/images/imgconv/default_format.go @@ -0,0 +1,28 @@ +package imgconv + +import ( + "image" + "image/gif" + "image/jpeg" + "image/png" + "io" + + "golang.org/x/image/bmp" + "golang.org/x/image/tiff" +) + +const ( + JPEG = "jpeg" + PNG = "png" + GIF = "gif" + TIFF = "tiff" + BMP = "bmp" +) + +func init() { + RegisterFormatEncoder(JPEG, func(w io.Writer, img image.Image) error { return jpeg.Encode(w, img, nil) }) + RegisterFormatEncoder(PNG, func(w io.Writer, img image.Image) error { return png.Encode(w, img) }) + RegisterFormatEncoder(GIF, func(w io.Writer, img image.Image) error { return gif.Encode(w, img, nil) }) + RegisterFormatEncoder(TIFF, func(w io.Writer, img image.Image) error { return tiff.Encode(w, img, nil) }) + RegisterFormatEncoder(BMP, func(w io.Writer, img image.Image) error { return bmp.Encode(w, img) }) +} diff --git a/pkg/images/imgconv/imgconv.go b/pkg/images/imgconv/imgconv.go new file mode 100644 index 0000000000000000000000000000000000000000..9f00fcb8676bffeda1fc9de3883adf4b806c1fba --- /dev/null +++ b/pkg/images/imgconv/imgconv.go @@ -0,0 +1,65 @@ +package imgconv + +import ( + "image" + "io" + "os" + "strings" + + "git.perx.ru/perxis/perxis-go/pkg/errors" +) + +var builtinFormats = map[string]string{ + "jpg": "jpeg", + "tif": "tiff", +} + +var defaultFormatEncoderRegistry = make(map[string]EncodeFunc) + +type EncodeFunc func(w io.Writer, img image.Image) error + +func RegisterFormatEncoder(format string, fn EncodeFunc) { + defaultFormatEncoderRegistry[format] = fn +} + +func Encode(w io.Writer, format string, img image.Image) error { + encoder, ok := defaultFormatEncoderRegistry[format] + if !ok { + return errors.Errorf("unknown format: %s", format) + } + err := encoder(w, img) + if err != nil { + return errors.Wrap(err, "encode image") + } + return nil +} + +func Decode(r io.Reader) (image.Image, string, error) { + img, ext, err := image.Decode(r) + if err != nil { + return nil, "", errors.Wrap(err, "decode image") + } + return img, ext, nil +} + +func Open(filename string) (image.Image, string, error) { + file, err := os.Open(filename) + if err != nil { + return nil, "", errors.Wrap(err, "open file") + } + defer file.Close() + img, ext, err := Decode(file) + if err != nil { + return nil, "", errors.Wrap(err, "decode file") + } + return img, ext, nil +} + +func NormalizeFormat(format string) string { + format = strings.ToLower(format) + format = strings.TrimPrefix(format, ".") + if v, ok := builtinFormats[format]; ok { + return v + } + return format +} diff --git a/pkg/images/imgconv/imgconv_test.go b/pkg/images/imgconv/imgconv_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4ee0f21950f78fbcb76109f127003ad64ff0ea07 --- /dev/null +++ b/pkg/images/imgconv/imgconv_test.go @@ -0,0 +1,142 @@ +package imgconv + +import ( + "bytes" + "image" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOpen(t *testing.T) { + _, ext, err := Open("testdata/1.jpeg") + if err != nil { + return + } + require.NoError(t, err) + require.Equal(t, JPEG, ext) +} + +func TestNormalizeFormat(t *testing.T) { + require.Equal(t, NormalizeFormat("jpg"), JPEG) + require.Equal(t, NormalizeFormat("png"), PNG) + require.Equal(t, NormalizeFormat("tif"), TIFF) + require.Equal(t, NormalizeFormat("any"), "any") + require.Equal(t, NormalizeFormat(".jpg"), JPEG) + require.Equal(t, NormalizeFormat(".gif"), GIF) + require.Equal(t, NormalizeFormat(".bmp"), BMP) + require.Equal(t, NormalizeFormat(".any"), "any") +} + +func TestEncode(t *testing.T) { + var tests = []struct { + name string + input string + wantErr bool + }{ + { + name: "unknown format", + input: "go", + wantErr: true, + }, + { + name: "jpeg format", + input: JPEG, + wantErr: false, + }, + { + name: "png format", + input: PNG, + wantErr: false, + }, + { + name: "gif format", + input: GIF, + wantErr: false, + }, + { + name: "tiff format", + input: TIFF, + wantErr: false, + }, + { + name: "bmp format", + input: BMP, + wantErr: false, + }, + } + img := image.NewRGBA(image.Rect(0, 0, 10, 10)) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := new(bytes.Buffer) + err := Encode(buf, tt.input, img) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestDecode(t *testing.T) { + var tests = []struct { + name string + input string + output string + wantErr bool + }{ + { + name: "unknown format", + input: "testdata/1.go", + wantErr: true, + }, + { + name: "jpeg format", + input: "testdata/1.jpeg", + output: JPEG, + wantErr: false, + }, + { + name: "png format", + input: "testdata/1.png", + output: PNG, + wantErr: false, + }, + { + name: "gif format", + input: "testdata/1.gif", + output: GIF, + wantErr: false, + }, + { + name: "tiff format", + input: "testdata/1.tiff", + output: TIFF, + wantErr: false, + }, + { + name: "bmp format", + input: "testdata/1.bmp", + output: BMP, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + file, err := os.Open(tt.input) + if err != nil { + t.Fatal(err) + } + defer file.Close() + _, ext, err := Decode(file) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.output, ext) + } + }) + } +} diff --git a/pkg/images/imgconv/testdata/1.bmp b/pkg/images/imgconv/testdata/1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1a4a8d81288b72efbe3ccfc90887b86f897b137e Binary files /dev/null and b/pkg/images/imgconv/testdata/1.bmp differ diff --git a/pkg/images/imgconv/testdata/1.gif b/pkg/images/imgconv/testdata/1.gif new file mode 100644 index 0000000000000000000000000000000000000000..73889f7e555aaf01ceb121d2b700a1b6ffd6428c Binary files /dev/null and b/pkg/images/imgconv/testdata/1.gif differ diff --git a/pkg/images/imgconv/testdata/1.go b/pkg/images/imgconv/testdata/1.go new file mode 100644 index 0000000000000000000000000000000000000000..c8eb7f369645cb9cf399939b2297f06f937daace --- /dev/null +++ b/pkg/images/imgconv/testdata/1.go @@ -0,0 +1,3 @@ +package testdata + +// hi diff --git a/pkg/images/imgconv/testdata/1.jpeg b/pkg/images/imgconv/testdata/1.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..8348e38780de3332ba2e1d39fdb81fd8bba92389 Binary files /dev/null and b/pkg/images/imgconv/testdata/1.jpeg differ diff --git a/pkg/images/imgconv/testdata/1.png b/pkg/images/imgconv/testdata/1.png new file mode 100644 index 0000000000000000000000000000000000000000..d047fa256d11a29ce71b27ae123a22a1870f084d Binary files /dev/null and b/pkg/images/imgconv/testdata/1.png differ diff --git a/pkg/images/imgconv/testdata/1.tiff b/pkg/images/imgconv/testdata/1.tiff new file mode 100644 index 0000000000000000000000000000000000000000..ca37358818808f0da0a11a3a9396e16035a04652 Binary files /dev/null and b/pkg/images/imgconv/testdata/1.tiff differ diff --git a/pkg/images/imgconv/testdata/1.webp b/pkg/images/imgconv/testdata/1.webp new file mode 100644 index 0000000000000000000000000000000000000000..122741b605f3121d393829ffb5b7a0924db13c86 Binary files /dev/null and b/pkg/images/imgconv/testdata/1.webp differ diff --git a/pkg/images/imgconv/webp.go b/pkg/images/imgconv/webp.go new file mode 100644 index 0000000000000000000000000000000000000000..0358d9ca76240229ac274e3e5751e332dd378127 --- /dev/null +++ b/pkg/images/imgconv/webp.go @@ -0,0 +1,23 @@ +//go:build webp + +package imgconv + +import ( + "image" + "io" + + "github.com/bep/gowebp/libwebp" + "github.com/bep/gowebp/libwebp/webpoptions" + + // Нужно включать в сборку для вызова регистрации декодера в стандартном + // пакете "images" для декодирования файлов webp. + _ "golang.org/x/image/webp" +) + +const ( + WEBP = "webp" +) + +func init() { + RegisterFormatEncoder(WEBP, func(w io.Writer, img image.Image) error { return libwebp.Encode(w, img, webpoptions.EncodingOptions{}) }) +} diff --git a/pkg/images/imgconv/webp_test.go b/pkg/images/imgconv/webp_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4574bfbd66593303a44fa2cd1ffca160786bf87b --- /dev/null +++ b/pkg/images/imgconv/webp_test.go @@ -0,0 +1,30 @@ +//go:build webp + +package imgconv + +import ( + "bytes" + "image" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEncodeWebP(t *testing.T) { + img := image.NewRGBA(image.Rect(0, 0, 10, 10)) + buf := new(bytes.Buffer) + err := Encode(buf, WEBP, img) + require.NoError(t, err) +} + +func TestDecodeWebP(t *testing.T) { + file, err := os.Open("testdata/1.webp") + if err != nil { + t.Fatal(err) + } + defer file.Close() + _, ext, err := Decode(file) + require.NoError(t, err) + require.Equal(t, WEBP, ext) +}