mirror of
https://github.com/talgo-cloud/bimg.git
synced 2026-03-16 10:55:54 -07:00
parent
885b315035
commit
d471c49348
5 changed files with 168 additions and 47 deletions
50
image.go
50
image.go
|
|
@ -1,12 +1,46 @@
|
||||||
package bimg
|
package bimg
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo pkg-config: vips
|
|
||||||
#include "vips/vips.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
type Image struct {
|
type Image struct {
|
||||||
buf []byte
|
buffer []byte
|
||||||
image *C.struct__VipsImage
|
}
|
||||||
|
|
||||||
|
func (i *Image) Resize(width int, height int) ([]byte, error) {
|
||||||
|
options := Options{
|
||||||
|
Width: width,
|
||||||
|
Height: height,
|
||||||
|
}
|
||||||
|
return Resize(i.buffer, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Extract(top int, left int, width int, height int) ([]byte, error) {
|
||||||
|
options := Options{
|
||||||
|
Width: width,
|
||||||
|
Height: height,
|
||||||
|
Top: top,
|
||||||
|
Left: left,
|
||||||
|
}
|
||||||
|
return Resize(i.buffer, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Rotate(degrees Angle) ([]byte, error) {
|
||||||
|
options := Options{Rotate: degrees}
|
||||||
|
return Resize(i.buffer, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Flip() ([]byte, error) {
|
||||||
|
options := Options{Flip: VERTICAL}
|
||||||
|
return Resize(i.buffer, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Flop() ([]byte, error) {
|
||||||
|
options := Options{Flip: HORIZONTAL}
|
||||||
|
return Resize(i.buffer, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Type() string {
|
||||||
|
return DetermineImageTypeName(i.buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImage(buf []byte) *Image {
|
||||||
|
return &Image{buf}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
options.go
22
options.go
|
|
@ -22,18 +22,14 @@ func (i Interpolator) String() string {
|
||||||
return interpolations[i]
|
return interpolations[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Rotation struct {
|
type Angle int
|
||||||
angle int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Rotation) calculate() int {
|
const (
|
||||||
angle := a.angle
|
D0 Angle = C.VIPS_ANGLE_D0
|
||||||
divisor := angle % 90
|
D90 Angle = C.VIPS_ANGLE_D90
|
||||||
if divisor != 0 {
|
D180 Angle = C.VIPS_ANGLE_D180
|
||||||
angle = a.angle - divisor
|
D270 Angle = C.VIPS_ANGLE_D270
|
||||||
}
|
)
|
||||||
return angle
|
|
||||||
}
|
|
||||||
|
|
||||||
type Direction int
|
type Direction int
|
||||||
|
|
||||||
|
|
@ -45,12 +41,14 @@ const (
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Height int
|
Height int
|
||||||
Width int
|
Width int
|
||||||
|
Top int
|
||||||
|
Left int
|
||||||
Crop bool
|
Crop bool
|
||||||
Enlarge bool
|
Enlarge bool
|
||||||
Extend int
|
Extend int
|
||||||
Embed bool
|
Embed bool
|
||||||
Quality int
|
Quality int
|
||||||
Rotate int
|
Rotate Angle
|
||||||
Flip Direction
|
Flip Direction
|
||||||
Gravity Gravity
|
Gravity Gravity
|
||||||
Interpolator Interpolator
|
Interpolator Interpolator
|
||||||
|
|
|
||||||
96
resize.go
96
resize.go
|
|
@ -16,8 +16,15 @@ const (
|
||||||
NOHALO
|
NOHALO
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CENTRE Gravity = iota
|
||||||
|
NORTH
|
||||||
|
EAST
|
||||||
|
SOUTH
|
||||||
|
WEST
|
||||||
|
)
|
||||||
|
|
||||||
func Resize(buf []byte, o Options) ([]byte, error) {
|
func Resize(buf []byte, o Options) ([]byte, error) {
|
||||||
// detect (if possible) the file type
|
|
||||||
defer C.vips_thread_shutdown()
|
defer C.vips_thread_shutdown()
|
||||||
|
|
||||||
image, err := vipsRead(buf)
|
image, err := vipsRead(buf)
|
||||||
|
|
@ -34,7 +41,6 @@ func Resize(buf []byte, o Options) ([]byte, error) {
|
||||||
inWidth := int(image.Xsize)
|
inWidth := int(image.Xsize)
|
||||||
inHeight := int(image.Ysize)
|
inHeight := int(image.Ysize)
|
||||||
|
|
||||||
// crop
|
|
||||||
if o.Crop {
|
if o.Crop {
|
||||||
left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity)
|
left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity)
|
||||||
o.Width = int(math.Min(float64(inWidth), float64(o.Width)))
|
o.Width = int(math.Min(float64(inWidth), float64(o.Width)))
|
||||||
|
|
@ -45,9 +51,24 @@ func Resize(buf []byte, o Options) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotate
|
rotation, flip := calculateRotationAndFlip(image, o.Rotate)
|
||||||
|
if flip {
|
||||||
|
o.Flip = HORIZONTAL
|
||||||
|
}
|
||||||
|
if rotation != D0 {
|
||||||
|
o.Rotate = rotation
|
||||||
|
}
|
||||||
|
|
||||||
if o.Rotate > 0 {
|
if o.Rotate > 0 {
|
||||||
image, err = Rotate(image, Rotation{o.Rotate})
|
rotation := calculateRotation(o.Rotate)
|
||||||
|
image, err = vipsRotate(image, rotation)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.Flip > 0 {
|
||||||
|
image, err = vipsFlip(image, o.Flip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -62,21 +83,9 @@ func Resize(buf []byte, o Options) ([]byte, error) {
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Rotate(image *C.struct__VipsImage, r Rotation) (*C.struct__VipsImage, error) {
|
|
||||||
//vips := &Vips{}
|
|
||||||
return vipsRotate(image, r.calculate())
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
CENTRE Gravity = iota
|
|
||||||
NORTH
|
|
||||||
EAST
|
|
||||||
SOUTH
|
|
||||||
WEST
|
|
||||||
)
|
|
||||||
|
|
||||||
func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
|
func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
|
||||||
left, top := 0, 0
|
left, top := 0, 0
|
||||||
|
|
||||||
switch gravity {
|
switch gravity {
|
||||||
case NORTH:
|
case NORTH:
|
||||||
left = (inWidth - outWidth + 1) / 2
|
left = (inWidth - outWidth + 1) / 2
|
||||||
|
|
@ -92,5 +101,58 @@ func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity)
|
||||||
left = (inWidth - outWidth + 1) / 2
|
left = (inWidth - outWidth + 1) / 2
|
||||||
top = (inHeight - outHeight + 1) / 2
|
top = (inHeight - outHeight + 1) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
return left, top
|
return left, top
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calculateRotationAndFlip(image *C.struct__VipsImage, angle Angle) (Angle, bool) {
|
||||||
|
rotate := D0
|
||||||
|
flip := false
|
||||||
|
|
||||||
|
if angle == -1 {
|
||||||
|
switch vipsExifOrientation(image) {
|
||||||
|
case 6:
|
||||||
|
rotate = D90
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
rotate = D180
|
||||||
|
break
|
||||||
|
case 8:
|
||||||
|
rotate = D270
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
flip = true
|
||||||
|
break // flip 1
|
||||||
|
case 7:
|
||||||
|
flip = true
|
||||||
|
rotate = D90
|
||||||
|
break // flip 6
|
||||||
|
case 4:
|
||||||
|
flip = true
|
||||||
|
rotate = D180
|
||||||
|
break // flip 3
|
||||||
|
case 5:
|
||||||
|
flip = true
|
||||||
|
rotate = D270
|
||||||
|
break // flip 8
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if angle == 90 {
|
||||||
|
rotate = D90
|
||||||
|
} else if angle == 180 {
|
||||||
|
rotate = D180
|
||||||
|
} else if angle == 270 {
|
||||||
|
rotate = D270
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotate, flip
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateRotation(angle Angle) Angle {
|
||||||
|
divisor := angle % 90
|
||||||
|
if divisor != 0 {
|
||||||
|
angle = angle - divisor
|
||||||
|
}
|
||||||
|
return angle
|
||||||
|
}
|
||||||
|
|
|
||||||
23
vips.go
23
vips.go
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
type vipsImage *C.struct__VipsImage
|
type vipsImage C.struct__VipsImage
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
|
@ -34,13 +34,13 @@ type Vips struct {
|
||||||
buf []byte
|
buf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func vipsRotate(image *C.struct__VipsImage, degrees int) (*C.struct__VipsImage, error) {
|
func vipsRotate(image *C.struct__VipsImage, angle Angle) (*C.struct__VipsImage, error) {
|
||||||
var out *C.struct__VipsImage
|
var out *C.struct__VipsImage
|
||||||
|
|
||||||
err := C.vips_rotate(image, &out, C.int(degrees))
|
err := C.vips_rotate(image, &out, C.int(angle))
|
||||||
C.g_object_unref(C.gpointer(image))
|
C.g_object_unref(C.gpointer(image))
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return nil, vipsError()
|
return nil, catchVipsError()
|
||||||
}
|
}
|
||||||
defer C.g_object_unref(C.gpointer(out))
|
defer C.g_object_unref(C.gpointer(out))
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ func vipsFlip(image *C.struct__VipsImage, direction Direction) (*C.struct__VipsI
|
||||||
err := C.vips_flip_seq(image, &out)
|
err := C.vips_flip_seq(image, &out)
|
||||||
C.g_object_unref(C.gpointer(image))
|
C.g_object_unref(C.gpointer(image))
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return nil, vipsError()
|
return nil, catchVipsError()
|
||||||
}
|
}
|
||||||
defer C.g_object_unref(C.gpointer(out))
|
defer C.g_object_unref(C.gpointer(out))
|
||||||
|
|
||||||
|
|
@ -74,9 +74,8 @@ func vipsRead(buf []byte) (*C.struct__VipsImage, error) {
|
||||||
imageTypeC := C.int(imageType)
|
imageTypeC := C.int(imageType)
|
||||||
|
|
||||||
err := C.vips_init_image(imageBuf, length, imageTypeC, &image)
|
err := C.vips_init_image(imageBuf, length, imageTypeC, &image)
|
||||||
//err := C.vips_jpegload_buffer_seq(imageBuf, length, &image)
|
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return nil, vipsError()
|
return nil, catchVipsError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return image, nil
|
return image, nil
|
||||||
|
|
@ -88,7 +87,7 @@ func vipsExtract(image *C.struct__VipsImage, left int, top int, width int, heigh
|
||||||
err := C.vips_extract_area_0(image, &buf, C.int(left), C.int(top), C.int(width), C.int(height))
|
err := C.vips_extract_area_0(image, &buf, C.int(left), C.int(top), C.int(width), C.int(height))
|
||||||
C.g_object_unref(C.gpointer(image))
|
C.g_object_unref(C.gpointer(image))
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return nil, vipsError()
|
return nil, catchVipsError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
|
|
@ -122,6 +121,10 @@ func vipsImageType(buf []byte) int {
|
||||||
return imageType
|
return imageType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vipsExifOrientation(image *C.struct__VipsImage) int {
|
||||||
|
return int(C.vips_exif_orientation(image))
|
||||||
|
}
|
||||||
|
|
||||||
type vipsSaveOptions struct {
|
type vipsSaveOptions struct {
|
||||||
Quality int
|
Quality int
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +135,7 @@ func vipsSave(image *C.struct__VipsImage, o vipsSaveOptions) ([]byte, error) {
|
||||||
|
|
||||||
err := C.vips_jpegsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0)
|
err := C.vips_jpegsave_custom(image, &ptr, &length, 1, C.int(o.Quality), 0)
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return nil, vipsError()
|
return nil, catchVipsError()
|
||||||
}
|
}
|
||||||
C.g_object_unref(C.gpointer(image))
|
C.g_object_unref(C.gpointer(image))
|
||||||
|
|
||||||
|
|
@ -143,7 +146,7 @@ func vipsSave(image *C.struct__VipsImage, o vipsSaveOptions) ([]byte, error) {
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func vipsError() error {
|
func catchVipsError() error {
|
||||||
s := C.GoString(C.vips_error_buffer())
|
s := C.GoString(C.vips_error_buffer())
|
||||||
C.vips_error_clear()
|
C.vips_error_clear()
|
||||||
C.vips_thread_shutdown()
|
C.vips_thread_shutdown()
|
||||||
|
|
|
||||||
24
vips.h
24
vips.h
|
|
@ -11,6 +11,12 @@ enum types {
|
||||||
MAGICK
|
MAGICK
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
vips_malloc_cb(VipsObject *object, char *buf)
|
||||||
|
{
|
||||||
|
g_free(buf);
|
||||||
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
vips_affine_interpolator(VipsImage *in, VipsImage **out, double a, double b, double c, double d, VipsInterpolate *interpolator)
|
vips_affine_interpolator(VipsImage *in, VipsImage **out, double a, double b, double c, double d, VipsInterpolate *interpolator)
|
||||||
{
|
{
|
||||||
|
|
@ -94,9 +100,27 @@ vips_init_image(void *buf, size_t len, int imageType, VipsImage **out) {
|
||||||
#endif
|
#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;
|
return code;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
vips_exif_orientation(VipsImage *image) {
|
||||||
|
int orientation = 0;
|
||||||
|
const char **exif;
|
||||||
|
if (
|
||||||
|
vips_image_get_typeof(image, "exif-ifd0-Orientation") != 0 &&
|
||||||
|
!vips_image_get_string(image, "exif-ifd0-Orientation", exif)
|
||||||
|
) {
|
||||||
|
orientation = atoi(exif[0]);
|
||||||
|
}
|
||||||
|
return orientation;
|
||||||
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
vips_embed_extend(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend)
|
vips_embed_extend(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue