master
Tomas Aparicio 11 years ago
parent f63020b40f
commit e603ddd10a

@ -1,5 +1,62 @@
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")
// 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)
}
// 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)
func (i *Image) Rotate(a Angle) ([]byte, error) {
options := Options{Rotate: a}

@ -104,6 +104,31 @@ func TestImageThumbnail(t *testing.T) {
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) {
buf, err := initImage("test.jpg").Flip()
if err != nil {

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

@ -8,7 +8,6 @@ import "C"
import (
"errors"
"fmt"
"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)
if err != nil {
return nil, err
@ -119,6 +118,12 @@ func Resize(buf []byte, o Options) ([]byte, error) {
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
buf, err = vipsSave(image, saveOptions)
if err != nil {
@ -146,7 +151,7 @@ func extractImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage,
break
case o.Top > 0 || o.Left > 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 {
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 direction Direction = -1
rotation, flip := calculateRotationAndFlip(image, o.Rotate)
if flip {
o.Flip = flip
}
if rotation > D0 && o.Rotate == 0 {
o.Rotate = rotation
if o.NoAutoRotate == false {
rotation, flip := calculateRotationAndFlip(image, o.Rotate)
if flip {
o.Flip = flip
}
if rotation > D0 && o.Rotate == 0 {
o.Rotate = rotation
}
}
if o.Rotate > 0 {
@ -185,6 +192,36 @@ func rotateImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, e
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) {
// Use vips_shrink with the integral reduction
image, err := vipsShrink(image, shrink)
@ -235,7 +272,6 @@ func imageCalculations(o *Options, inWidth, inHeight int) float64 {
factor := 1.0
xfactor := float64(inWidth) / float64(o.Width)
yfactor := float64(inHeight) / float64(o.Height)
defer debug("Image calculations: %dx%d", o.Width, o.Height)
switch {
// Fixed width and height

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

@ -25,6 +25,12 @@ type vipsSaveOptions struct {
Type ImageType
}
type VipsMemoryInfo struct {
Memory int64
MemoryHighwater int64
Allocations int64
}
func init() {
if C.VIPS_MAJOR_VERSION <= 7 && C.VIPS_MINOR_VERSION < 40 {
panic("unsupported old vips version!")
@ -34,7 +40,7 @@ func init() {
}
// 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() {
m.Lock()
runtime.LockOSThread()
@ -70,6 +76,15 @@ func VipsDebug() {
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) {
var out *C.struct__VipsImage
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
}
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) {
var image *C.struct__VipsImage
imageType := vipsImageType(buf)
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))
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 {
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) {
var image *C.struct__VipsImage
istring := C.CString(i.String())
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 {
imageType := UNKNOWN
if len(buf) == 0 {
return imageType
}
length := C.size_t(len(buf))
imageBuf := unsafe.Pointer(&buf[0])
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())
C.vips_error_clear()
C.vips_thread_shutdown()
// clean image memory buffer?
return errors.New(s)
}

@ -91,6 +91,18 @@ vips_enum_nick_bridge(VipsImage *image) {
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
vips_embed_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend)
{

Loading…
Cancel
Save