mirror of https://mirror.trustie.net/root/bimg.git
feat: support multiple outputs
This commit is contained in:
parent
d471c49348
commit
ef10d7d7ec
28
image.go
28
image.go
|
@ -9,7 +9,7 @@ func (i *Image) Resize(width int, height int) ([]byte, error) {
|
|||
Width: width,
|
||||
Height: height,
|
||||
}
|
||||
return Resize(i.buffer, options)
|
||||
return i.Process(options)
|
||||
}
|
||||
|
||||
func (i *Image) Extract(top int, left int, width int, height int) ([]byte, error) {
|
||||
|
@ -19,22 +19,36 @@ func (i *Image) Extract(top int, left int, width int, height int) ([]byte, error
|
|||
Top: top,
|
||||
Left: left,
|
||||
}
|
||||
return Resize(i.buffer, options)
|
||||
return i.Process(options)
|
||||
}
|
||||
|
||||
func (i *Image) Rotate(degrees Angle) ([]byte, error) {
|
||||
options := Options{Rotate: degrees}
|
||||
return Resize(i.buffer, options)
|
||||
func (i *Image) Rotate(a Angle) ([]byte, error) {
|
||||
options := Options{Rotate: a}
|
||||
return i.Process(options)
|
||||
}
|
||||
|
||||
func (i *Image) Flip() ([]byte, error) {
|
||||
options := Options{Flip: VERTICAL}
|
||||
return Resize(i.buffer, options)
|
||||
return i.Process(options)
|
||||
}
|
||||
|
||||
func (i *Image) Flop() ([]byte, error) {
|
||||
options := Options{Flip: HORIZONTAL}
|
||||
return Resize(i.buffer, options)
|
||||
return i.Process(options)
|
||||
}
|
||||
|
||||
func (i *Image) Convert(t ImageType) ([]byte, error) {
|
||||
options := Options{Type: t}
|
||||
return i.Process(options)
|
||||
}
|
||||
|
||||
func (i *Image) Process(o Options) ([]byte, error) {
|
||||
image, err := Resize(i.buffer, o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i.buffer = image
|
||||
return image, nil
|
||||
}
|
||||
|
||||
func (i *Image) Type() string {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package bimg
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestImageResize(t *testing.T) {
|
||||
data, _ := os.Open(path.Join("fixtures/test.jpg"))
|
||||
buf, err := ioutil.ReadAll(data)
|
||||
|
||||
image := NewImage(buf)
|
||||
|
||||
buf, err = image.Resize(300, 240)
|
||||
if err != nil {
|
||||
t.Errorf("Cannot process the image: %#v", err)
|
||||
}
|
||||
}
|
|
@ -48,6 +48,8 @@ type Options struct {
|
|||
Extend int
|
||||
Embed bool
|
||||
Quality int
|
||||
Compression int
|
||||
Type ImageType
|
||||
Rotate Angle
|
||||
Flip Direction
|
||||
Gravity Gravity
|
||||
|
|
21
resize.go
21
resize.go
|
@ -7,6 +7,7 @@ package bimg
|
|||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
)
|
||||
|
||||
|
@ -27,7 +28,7 @@ const (
|
|||
func Resize(buf []byte, o Options) ([]byte, error) {
|
||||
defer C.vips_thread_shutdown()
|
||||
|
||||
image, err := vipsRead(buf)
|
||||
image, imageType, err := vipsRead(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -36,6 +37,12 @@ func Resize(buf []byte, o Options) ([]byte, error) {
|
|||
if o.Quality == 0 {
|
||||
o.Quality = QUALITY
|
||||
}
|
||||
if o.Compression == 0 {
|
||||
o.Compression = 6
|
||||
}
|
||||
if o.Type == 0 {
|
||||
o.Type = imageType
|
||||
}
|
||||
|
||||
// get WxH
|
||||
inWidth := int(image.Xsize)
|
||||
|
@ -74,7 +81,17 @@ func Resize(buf []byte, o Options) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
buf, err = vipsSave(image, vipsSaveOptions{Quality: o.Quality})
|
||||
if IsTypeSupported(o.Type) == false {
|
||||
return nil, errors.New("Unsupported image output type")
|
||||
}
|
||||
|
||||
saveOptions := vipsSaveOptions{
|
||||
Quality: o.Quality,
|
||||
Type: o.Type,
|
||||
Compression: o.Compression,
|
||||
}
|
||||
|
||||
buf, err = vipsSave(image, saveOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -33,3 +33,31 @@ func TestResize(t *testing.T) {
|
|||
t.Fatal("Cannot save the image")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
options := Options{Width: 640, Height: 480, Crop: false, Type: PNG}
|
||||
img, err := os.Open("fixtures/test.jpg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer img.Close()
|
||||
|
||||
buf, err := ioutil.ReadAll(img)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newImg, err := Resize(buf, options)
|
||||
if err != nil {
|
||||
t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
|
||||
}
|
||||
|
||||
if DetermineImageType(newImg) != PNG {
|
||||
t.Fatal("Image is not png")
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile("result.png", newImg, 0644)
|
||||
if err != nil {
|
||||
t.Fatal("Cannot save the image")
|
||||
}
|
||||
}
|
||||
|
|
17
type.go
17
type.go
|
@ -1,7 +1,9 @@
|
|||
package bimg
|
||||
|
||||
type ImageType int
|
||||
|
||||
const (
|
||||
UNKNOWN = iota
|
||||
UNKNOWN ImageType = iota
|
||||
JPEG
|
||||
WEBP
|
||||
PNG
|
||||
|
@ -9,7 +11,7 @@ const (
|
|||
MAGICK
|
||||
)
|
||||
|
||||
func DetermineImageType(buf []byte) int {
|
||||
func DetermineImageType(buf []byte) ImageType {
|
||||
return vipsImageType(buf)
|
||||
}
|
||||
|
||||
|
@ -28,7 +30,7 @@ func DetermineImageTypeName(buf []byte) string {
|
|||
imageType = "png"
|
||||
break
|
||||
case imageCode == TIFF:
|
||||
imageType = "png"
|
||||
imageType = "tiff"
|
||||
break
|
||||
case imageCode == MAGICK:
|
||||
imageType = "magick"
|
||||
|
@ -37,3 +39,12 @@ func DetermineImageTypeName(buf []byte) string {
|
|||
|
||||
return imageType
|
||||
}
|
||||
|
||||
func IsTypeSupported(t ImageType) bool {
|
||||
return t == JPEG || t == PNG || t == WEBP
|
||||
}
|
||||
|
||||
func IsTypeNameSupported(t string) bool {
|
||||
return t == "jpeg" || t == "jpg" ||
|
||||
t == "png" || t == "webp"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package bimg
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeterminateImageType(t *testing.T) {
|
||||
files := []struct {
|
||||
name string
|
||||
expected ImageType
|
||||
}{
|
||||
{"test.jpg", JPEG},
|
||||
{"test.png", PNG},
|
||||
{"test.webp", WEBP},
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
img, _ := os.Open(path.Join("fixtures", file.name))
|
||||
buf, _ := ioutil.ReadAll(img)
|
||||
defer img.Close()
|
||||
|
||||
if DetermineImageType(buf) != file.expected {
|
||||
t.Fatal("Image type is not valid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeterminateImageTypeName(t *testing.T) {
|
||||
files := []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"test.jpg", "jpeg"},
|
||||
{"test.png", "png"},
|
||||
{"test.webp", "webp"},
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
img, _ := os.Open(path.Join("fixtures", file.name))
|
||||
buf, _ := ioutil.ReadAll(img)
|
||||
defer img.Close()
|
||||
|
||||
if DetermineImageTypeName(buf) != file.expected {
|
||||
t.Fatal("Image type is not valid")
|
||||
}
|
||||
}
|
||||
}
|
30
vips.go
30
vips.go
|
@ -60,12 +60,12 @@ func vipsFlip(image *C.struct__VipsImage, direction Direction) (*C.struct__VipsI
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func vipsRead(buf []byte) (*C.struct__VipsImage, error) {
|
||||
func vipsRead(buf []byte) (*C.struct__VipsImage, ImageType, error) {
|
||||
var image *C.struct__VipsImage
|
||||
imageType := vipsImageType(buf)
|
||||
|
||||
if imageType == UNKNOWN {
|
||||
return nil, errors.New("Input buffer contains unsupported image format")
|
||||
return nil, UNKNOWN, errors.New("Input buffer contains unsupported image format")
|
||||
}
|
||||
|
||||
// feed it
|
||||
|
@ -75,10 +75,10 @@ func vipsRead(buf []byte) (*C.struct__VipsImage, error) {
|
|||
|
||||
err := C.vips_init_image(imageBuf, length, imageTypeC, &image)
|
||||
if err != 0 {
|
||||
return nil, catchVipsError()
|
||||
return nil, UNKNOWN, catchVipsError()
|
||||
}
|
||||
|
||||
return image, nil
|
||||
return image, imageType, nil
|
||||
}
|
||||
|
||||
func vipsExtract(image *C.struct__VipsImage, left int, top int, width int, height int) (*C.struct__VipsImage, error) {
|
||||
|
@ -93,7 +93,7 @@ func vipsExtract(image *C.struct__VipsImage, left int, top int, width int, heigh
|
|||
return buf, nil
|
||||
}
|
||||
|
||||
func vipsImageType(buf []byte) int {
|
||||
func vipsImageType(buf []byte) ImageType {
|
||||
imageType := UNKNOWN
|
||||
|
||||
length := C.size_t(len(buf))
|
||||
|
@ -126,18 +126,32 @@ func vipsExifOrientation(image *C.struct__VipsImage) int {
|
|||
}
|
||||
|
||||
type vipsSaveOptions struct {
|
||||
Quality int
|
||||
Quality int
|
||||
Compression int
|
||||
Type ImageType
|
||||
}
|
||||
|
||||
func vipsSave(image *C.struct__VipsImage, o vipsSaveOptions) ([]byte, error) {
|
||||
var ptr unsafe.Pointer
|
||||
length := C.size_t(0)
|
||||
err := C.int(0)
|
||||
|
||||
err := C.vips_jpegsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0)
|
||||
switch {
|
||||
case o.Type == PNG:
|
||||
err = C.vips_pngsave_custom(image, &ptr, &length, 1, C.int(o.Compression), C.int(o.Quality), 0)
|
||||
break
|
||||
case o.Type == WEBP:
|
||||
err = C.vips_webpsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0)
|
||||
break
|
||||
default:
|
||||
err = C.vips_jpegsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0)
|
||||
break
|
||||
}
|
||||
|
||||
C.g_object_unref(C.gpointer(image))
|
||||
if err != 0 {
|
||||
return nil, catchVipsError()
|
||||
}
|
||||
C.g_object_unref(C.gpointer(image))
|
||||
|
||||
buf := C.GoBytes(ptr, C.int(length))
|
||||
// cleanup
|
||||
|
|
82
vips.h
82
vips.h
|
@ -36,24 +36,12 @@ vips_jpegload_buffer_shrink(void *buf, size_t len, VipsImage **out, int shrink)
|
|||
return vips_jpegload_buffer(buf, len, out, "shrink", shrink, NULL);
|
||||
};
|
||||
|
||||
int
|
||||
vips_webpload_buffer_seq(void *buf, size_t len, VipsImage **out)
|
||||
{
|
||||
return vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
};
|
||||
|
||||
int
|
||||
vips_flip_seq(VipsImage *in, VipsImage **out)
|
||||
{
|
||||
return vips_flip(in, out, VIPS_DIRECTION_HORIZONTAL, NULL);
|
||||
};
|
||||
|
||||
int
|
||||
vips_pngload_buffer_seq(void *buf, size_t len, VipsImage **out)
|
||||
{
|
||||
return vips_pngload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
};
|
||||
|
||||
int
|
||||
vips_shrink_0(VipsImage *in, VipsImage **out, double xshrink, double yshrink)
|
||||
{
|
||||
|
@ -82,32 +70,6 @@ vips_rotate(VipsImage *in, VipsImage **buf, int angle)
|
|||
return vips_rot(in, buf, rotate, NULL);
|
||||
};
|
||||
|
||||
int
|
||||
vips_init_image(void *buf, size_t len, int imageType, VipsImage **out) {
|
||||
int code = 1;
|
||||
|
||||
if (imageType == JPEG) {
|
||||
code = vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
} else if (imageType == PNG) {
|
||||
code = vips_pngload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
} else if (imageType == WEBP) {
|
||||
code = vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
} else if (imageType == TIFF) {
|
||||
code = vips_tiffload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
#if (VIPS_MAJOR_VERSION >= 8)
|
||||
} else if (imageType == MAGICK) {
|
||||
code = vips_magickload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (out != NULL) {
|
||||
// Listen for "postclose" signal to delete input buffer
|
||||
//g_signal_connect(out, "postclose", G_CALLBACK(vips_malloc_cb), buf);
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
|
||||
int
|
||||
vips_exif_orientation(VipsImage *image) {
|
||||
int orientation = 0;
|
||||
|
@ -144,3 +106,47 @@ vips_jpegsave_custom(VipsImage *in, void **buf, size_t *len, int strip, int qual
|
|||
{
|
||||
return vips_jpegsave_buffer(in, buf, len, "strip", strip, "Q", quality, "optimize_coding", TRUE, "interlace", interlace, NULL);
|
||||
};
|
||||
|
||||
int
|
||||
vips_pngsave_custom(VipsImage *in, void **buf, size_t *len, int strip, int compression, int quality, int interlace)
|
||||
{
|
||||
#if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42))
|
||||
return vips_pngsave_buffer(in, buf, len, "strip", FALSE, "compression", compression,
|
||||
"interlace", interlace, "filter", VIPS_FOREIGN_PNG_FILTER_NONE, NULL);
|
||||
#else
|
||||
return vips_pngsave_buffer(image, buf, len, "strip", FALSE, "compression", compression,
|
||||
"interlace", interlace, NULL);
|
||||
#endif
|
||||
};
|
||||
|
||||
int
|
||||
vips_webpsave_custom(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace)
|
||||
{
|
||||
return vips_webpsave_buffer(in, buf, len, "strip", strip, "Q", quality, "optimize_coding", TRUE, "interlace", interlace, NULL);
|
||||
};
|
||||
|
||||
int
|
||||
vips_init_image(void *buf, size_t len, int imageType, VipsImage **out) {
|
||||
int code = 1;
|
||||
|
||||
if (imageType == JPEG) {
|
||||
code = vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
} else if (imageType == PNG) {
|
||||
code = vips_pngload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
} else if (imageType == WEBP) {
|
||||
code = vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
} else if (imageType == TIFF) {
|
||||
code = vips_tiffload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
#if (VIPS_MAJOR_VERSION >= 8)
|
||||
} else if (imageType == MAGICK) {
|
||||
code = vips_magickload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (out != NULL) {
|
||||
// Listen for "postclose" signal to delete input buffer
|
||||
//g_signal_connect(out, "postclose", G_CALLBACK(vips_malloc_cb), buf);
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue