master
Tomas Aparicio 11 years ago
parent f63020b40f
commit e603ddd10a

@ -1,5 +1,62 @@
package bimg package bimg
import . "github.com/tj/go-debug" import (
"github.com/dustin/go-humanize"
. "github.com/tj/go-debug"
"runtime"
"strconv"
"time"
)
var debug = Debug("bimg") var debug = Debug("bimg")
// Print Go memory and garbage collector stats. Useful for debugging
func PrintMemoryStats() {
log := Debug("memory")
mem := memoryStats()
log("\u001b[33m---- Memory Dump Stats ----\u001b[39m")
log("Allocated: %s", humanize.Bytes(mem.Alloc))
log("Total Allocated: %s", humanize.Bytes(mem.TotalAlloc))
log("Memory Allocations: %d", mem.Mallocs)
log("Memory Frees: %d", mem.Frees)
log("Heap Allocated: %s", humanize.Bytes(mem.HeapAlloc))
log("Heap System: %s", humanize.Bytes(mem.HeapSys))
log("Heap In Use: %s", humanize.Bytes(mem.HeapInuse))
log("Heap Idle: %s", humanize.Bytes(mem.HeapIdle))
log("Heap OS Related: %s", humanize.Bytes(mem.HeapReleased))
log("Heap Objects: %s", humanize.Bytes(mem.HeapObjects))
log("Stack In Use: %s", humanize.Bytes(mem.StackInuse))
log("Stack System: %s", humanize.Bytes(mem.StackSys))
log("Stack Span In Use: %s", humanize.Bytes(mem.MSpanInuse))
log("Stack Cache In Use: %s", humanize.Bytes(mem.MCacheInuse))
log("Next GC cycle: %s", humanizeNano(mem.NextGC))
log("Last GC cycle: %s", humanize.Time(time.Unix(0, int64(mem.LastGC))))
log("\u001b[33m---- End Memory Dump ----\u001b[39m")
}
func memoryStats() runtime.MemStats {
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
return mem
}
func humanizeNano(n uint64) string {
var suffix string
switch {
case n > 1e9:
n /= 1e9
suffix = "s"
case n > 1e6:
n /= 1e6
suffix = "ms"
case n > 1e3:
n /= 1e3
suffix = "us"
default:
suffix = "ns"
}
return strconv.Itoa(int(n)) + suffix
}

@ -75,6 +75,23 @@ func (i *Image) Thumbnail(pixels int) ([]byte, error) {
return i.Process(options) return i.Process(options)
} }
// Insert an image. Alias to Watermark()
func (i *Image) Insert(image []byte, left, top int) ([]byte, error) {
return i.Watermark(image, left, top)
}
// Insert an image to the existent one as watermark
func (i *Image) Watermark(image []byte, left, top int) ([]byte, error) {
options := Options{
Insert: Insert{
Buffer: image,
Top: top,
Left: left,
},
}
return i.Process(options)
}
// Rotate the image by given angle degrees (0, 90, 180 or 270) // Rotate the image by given angle degrees (0, 90, 180 or 270)
func (i *Image) Rotate(a Angle) ([]byte, error) { func (i *Image) Rotate(a Angle) ([]byte, error) {
options := Options{Rotate: a} options := Options{Rotate: a}

@ -104,6 +104,31 @@ func TestImageThumbnail(t *testing.T) {
Write("fixtures/test_thumbnail_out.jpg", buf) Write("fixtures/test_thumbnail_out.jpg", buf)
} }
func TestImageWatermark(t *testing.T) {
image := initImage("test.jpg")
_, err := image.Crop(800, 600, NORTH)
if err != nil {
t.Errorf("Cannot process the image: %#v", err)
}
insert, _ := Read("fixtures/watermark.png")
buf, err := image.Watermark(insert, 10, 10)
if err != nil {
t.Error(err)
}
err = assertSize(buf, 800, 600)
if err != nil {
//t.Error(err)
}
if DetermineImageType(buf) != PNG {
//t.Fatal("Image is not jpeg")
}
Write("fixtures/test_watermark_out.jpg", buf)
}
func TestImageFlip(t *testing.T) { func TestImageFlip(t *testing.T) {
buf, err := initImage("test.jpg").Flip() buf, err := initImage("test.jpg").Flip()
if err != nil { if err != nil {

@ -55,6 +55,12 @@ const (
VERTICAL Direction = C.VIPS_DIRECTION_VERTICAL VERTICAL Direction = C.VIPS_DIRECTION_VERTICAL
) )
type Insert struct {
Top int
Left int
Buffer []byte
}
type Options struct { type Options struct {
Height int Height int
Width int Width int
@ -70,7 +76,9 @@ type Options struct {
Embed bool Embed bool
Flip bool Flip bool
Flop bool Flop bool
NoAutoRotate bool
Rotate Angle Rotate Angle
Insert Insert
Gravity Gravity Gravity Gravity
Type ImageType Type ImageType
Interpolator Interpolator Interpolator Interpolator

@ -8,7 +8,6 @@ import "C"
import ( import (
"errors" "errors"
"fmt"
"math" "math"
) )
@ -107,7 +106,7 @@ func Resize(buf []byte, o Options) ([]byte, error) {
} }
} }
// Rotate / flip image if necessary based on EXIF metadata // Rotate / flip image if necessary
image, err = rotateImage(image, o) image, err = rotateImage(image, o)
if err != nil { if err != nil {
return nil, err return nil, err
@ -119,6 +118,12 @@ func Resize(buf []byte, o Options) ([]byte, error) {
Compression: o.Compression, Compression: o.Compression,
} }
// Insert an image if necessary
image, err = insertImage(image, imageType, o.Insert, saveOptions)
if err != nil {
return nil, err
}
// Finally save as buffer // Finally save as buffer
buf, err = vipsSave(image, saveOptions) buf, err = vipsSave(image, saveOptions)
if err != nil { if err != nil {
@ -146,7 +151,7 @@ func extractImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage,
break break
case o.Top > 0 || o.Left > 0: case o.Top > 0 || o.Left > 0:
if o.AreaWidth == 0 || o.AreaHeight == 0 { if o.AreaWidth == 0 || o.AreaHeight == 0 {
err = errors.New(fmt.Sprintf("Invalid area to extract %dx%d", o.AreaWidth, o.AreaHeight)) err = errors.New("Area to extract cannot be 0")
} else { } else {
image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight) image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight)
} }
@ -160,12 +165,14 @@ func rotateImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, e
var err error var err error
var direction Direction = -1 var direction Direction = -1
rotation, flip := calculateRotationAndFlip(image, o.Rotate) if o.NoAutoRotate == false {
if flip { rotation, flip := calculateRotationAndFlip(image, o.Rotate)
o.Flip = flip if flip {
} o.Flip = flip
if rotation > D0 && o.Rotate == 0 { }
o.Rotate = rotation if rotation > D0 && o.Rotate == 0 {
o.Rotate = rotation
}
} }
if o.Rotate > 0 { if o.Rotate > 0 {
@ -185,6 +192,36 @@ func rotateImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, e
return image, err return image, err
} }
// WIP
func insertImage(image *C.struct__VipsImage, t ImageType, o Insert, save vipsSaveOptions) (*C.struct__VipsImage, error) {
if len(o.Buffer) == 0 {
return image, nil
}
insert, imageType, err := vipsRead(o.Buffer)
if err != nil {
return nil, err
}
if imageType != t {
save.Type = t
debug("Image type insert: %s", save.Type)
buf, err := vipsSave(insert, save)
if err != nil {
return nil, err
}
insert, imageType, err = vipsRead(buf)
debug("New type image: %s", imageType)
if err != nil {
return nil, err
}
}
debug("Insert images: %#v", insert)
return vipsInsert(image, insert, o.Left, o.Top)
}
func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) { func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) {
// Use vips_shrink with the integral reduction // Use vips_shrink with the integral reduction
image, err := vipsShrink(image, shrink) image, err := vipsShrink(image, shrink)
@ -235,7 +272,6 @@ func imageCalculations(o *Options, inWidth, inHeight int) float64 {
factor := 1.0 factor := 1.0
xfactor := float64(inWidth) / float64(o.Width) xfactor := float64(inWidth) / float64(o.Width)
yfactor := float64(inHeight) / float64(o.Height) yfactor := float64(inHeight) / float64(o.Height)
defer debug("Image calculations: %dx%d", o.Width, o.Height)
switch { switch {
// Fixed width and height // Fixed width and height

@ -175,12 +175,11 @@ func BenchmarkCrop(b *testing.B) {
options := Options{ options := Options{
Width: 800, Width: 800,
Height: 600, Height: 600,
Crop: true,
} }
runBenchmarkResize("test.jpg", options, b) runBenchmarkResize("test.jpg", options, b)
} }
func BenchmarkExtract(b *testing.B) { func BenchmarkExtractJpeg(b *testing.B) {
options := Options{ options := Options{
Top: 100, Top: 100,
Left: 50, Left: 50,

@ -25,6 +25,12 @@ type vipsSaveOptions struct {
Type ImageType Type ImageType
} }
type VipsMemoryInfo struct {
Memory int64
MemoryHighwater int64
Allocations int64
}
func init() { func init() {
if C.VIPS_MAJOR_VERSION <= 7 && C.VIPS_MINOR_VERSION < 40 { if C.VIPS_MAJOR_VERSION <= 7 && C.VIPS_MINOR_VERSION < 40 {
panic("unsupported old vips version!") panic("unsupported old vips version!")
@ -34,7 +40,7 @@ func init() {
} }
// Explicit thread-safe start of libvips. // Explicit thread-safe start of libvips.
// You should only call this function if you previously shutdown libvips // Only call this function if you've previously shutdown libvips
func Initialize() { func Initialize() {
m.Lock() m.Lock()
runtime.LockOSThread() runtime.LockOSThread()
@ -70,6 +76,15 @@ func VipsDebug() {
C.im__print_all() C.im__print_all()
} }
// Get the allocated memory by vips in bytes
func VipsMemory() VipsMemoryInfo {
return VipsMemoryInfo{
Memory: int64(C.vips_tracked_get_mem()),
MemoryHighwater: int64(C.vips_tracked_get_mem_highwater()),
Allocations: int64(C.vips_tracked_get_allocs()),
}
}
func vipsRotate(image *C.struct__VipsImage, angle Angle) (*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
defer C.g_object_unref(C.gpointer(image)) defer C.g_object_unref(C.gpointer(image))
@ -94,20 +109,33 @@ func vipsFlip(image *C.struct__VipsImage, direction Direction) (*C.struct__VipsI
return out, nil return out, nil
} }
func vipsInsert(image *C.struct__VipsImage, sub *C.struct__VipsImage, left, top int) (*C.struct__VipsImage, error) {
var out *C.struct__VipsImage
var cache *C.struct__VipsImage
defer C.g_object_unref(C.gpointer(image))
defer C.g_object_unref(C.gpointer(sub))
err = C.vips_insert_bridge(image, sub, &out, C.int(left), C.int(top))
if err != 0 {
return nil, catchVipsError()
}
return out, nil
}
func vipsRead(buf []byte) (*C.struct__VipsImage, ImageType, error) { func vipsRead(buf []byte) (*C.struct__VipsImage, ImageType, error) {
var image *C.struct__VipsImage var image *C.struct__VipsImage
imageType := vipsImageType(buf) imageType := vipsImageType(buf)
if imageType == UNKNOWN { if imageType == UNKNOWN {
return nil, UNKNOWN, errors.New("Input buffer contains unsupported image format") return nil, UNKNOWN, errors.New("Unsupported image format")
} }
// feed it
length := C.size_t(len(buf)) length := C.size_t(len(buf))
imageBuf := unsafe.Pointer(&buf[0]) imageBuf := unsafe.Pointer(&buf[0])
imageTypeC := C.int(imageType)
err := C.vips_init_image(imageBuf, length, imageTypeC, &image) err := C.vips_init_image(imageBuf, length, C.int(imageType), &image)
if err != 0 { if err != 0 {
return nil, UNKNOWN, catchVipsError() return nil, UNKNOWN, catchVipsError()
} }
@ -201,7 +229,6 @@ func vipsEmbed(input *C.struct__VipsImage, left, top, width, height, extend int)
func vipsAffine(input *C.struct__VipsImage, residual float64, i Interpolator) (*C.struct__VipsImage, error) { func vipsAffine(input *C.struct__VipsImage, residual float64, i Interpolator) (*C.struct__VipsImage, error) {
var image *C.struct__VipsImage var image *C.struct__VipsImage
istring := C.CString(i.String()) istring := C.CString(i.String())
interpolator := C.vips_interpolate_new(istring) interpolator := C.vips_interpolate_new(istring)
@ -221,6 +248,10 @@ func vipsAffine(input *C.struct__VipsImage, residual float64, i Interpolator) (*
func vipsImageType(buf []byte) ImageType { func vipsImageType(buf []byte) ImageType {
imageType := UNKNOWN imageType := UNKNOWN
if len(buf) == 0 {
return imageType
}
length := C.size_t(len(buf)) length := C.size_t(len(buf))
imageBuf := unsafe.Pointer(&buf[0]) imageBuf := unsafe.Pointer(&buf[0])
bufferType := C.GoString(C.vips_foreign_find_load_buffer(imageBuf, length)) bufferType := C.GoString(C.vips_foreign_find_load_buffer(imageBuf, length))
@ -270,6 +301,5 @@ 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()
// clean image memory buffer?
return errors.New(s) return errors.New(s)
} }

@ -91,6 +91,18 @@ vips_enum_nick_bridge(VipsImage *image) {
return vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type); return vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
}; };
int
vips_insert_bridge(VipsImage *in, VipsImage *sub, VipsImage **out, int left, int top)
{
return vips_insert(in, sub, out, left, top, NULL);
};
int
vips_bandjoin2_bridge(VipsImage *in, VipsImage *sub, VipsImage **out)
{
return vips_bandjoin2(in, sub, out, NULL);
};
int int
vips_embed_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend) vips_embed_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend)
{ {

Loading…
Cancel
Save