feat: support multiple outputs

This commit is contained in:
Tomas Aparicio 2015-04-05 23:39:15 +02:00
parent d471c49348
commit ef10d7d7ec
9 changed files with 220 additions and 58 deletions

View File

@ -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 {

20
image_test.go Normal file
View File

@ -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)
}
}

View File

@ -48,6 +48,8 @@ type Options struct {
Extend int
Embed bool
Quality int
Compression int
Type ImageType
Rotate Angle
Flip Direction
Gravity Gravity

View File

@ -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
}

View File

@ -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
View File

@ -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"
}

50
type_test.go Normal file
View File

@ -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
View File

@ -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
View File

@ -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;
};