Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial work to port the library to GD. resizing works

  • Loading branch information...
commit 0cbef6776ca6739bdfa94da10cb67fea07fe4b7e 1 parent 37d5cb6
@tobi authored
Showing with 281 additions and 290 deletions.
  1. +14 −0 example/test2.go
  2. +70 −0 gd.go
  3. +43 −139 image.go
  4. +154 −151 image_test.go
View
14 example/test2.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+ "github.com/tobi/mogrify-go"
+)
+
+func main() {
+ img := mogrify.NewImage()
+ defer img.Destroy()
+
+ if err := img.OpenFile("../assets/example.com.png"); err != nil {
+ panic(err.Error())
+ }
+}
View
70 gd.go
@@ -0,0 +1,70 @@
+package mogrify
+
+// #cgo LDFLAGS: -lgd
+// #include <gd.h>
+import "C"
+
+import (
+ // "bytes"
+ "unsafe"
+)
+
+type gdImage struct {
+ img *C.gdImage
+}
+
+func img(img *C.gdImage) *gdImage {
+ image := &gdImage{img}
+ if image.invalid() {
+ return nil
+ }
+ return image
+}
+
+func assertGd(img *C.gdImage) bool {
+ return (*img).pixels != nil
+}
+
+func gdCreate(sx, sy int) *gdImage {
+ return img(C.gdImageCreate(C.int(sx), C.int(sy)))
+}
+
+func gdCreateFromJpeg(buffer []byte) *gdImage {
+ return img(C.gdImageCreateFromJpegPtr(C.int(len(buffer)), unsafe.Pointer(&buffer[0])))
+}
+
+func gdCreateFromGif(buffer []byte) *gdImage {
+ return img(C.gdImageCreateFromGifPtr(C.int(len(buffer)), unsafe.Pointer(&buffer[0])))
+}
+
+func gdCreateFromPng(buffer []byte) *gdImage {
+ return img(C.gdImageCreateFromGifPtr(C.int(len(buffer)), unsafe.Pointer(&buffer[0])))
+}
+
+func (p *gdImage) gdDestroy() {
+ if p != nil && p.img != nil {
+ C.gdImageDestroy(p.img)
+ }
+}
+
+func (p *gdImage) invalid() bool {
+ return p == nil || (*p.img).pixels == nil
+}
+
+func (p *gdImage) width() int {
+ return int((*p.img).sx)
+}
+
+func (p *gdImage) height() int {
+ return int((*p.img).sy)
+}
+
+func (p *gdImage) gdCopyResampled(dst *gdImage, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH int) {
+ C.gdImageCopyResampled(dst.img, p.img, C.int(dstX), C.int(dstY), C.int(srcX), C.int(srcY),
+ C.int(dstW), C.int(dstH), C.int(srcW), C.int(srcH))
+}
+
+func (p *gdImage) gdCopyResized(dst *gdImage, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH int) {
+ C.gdImageCopyResized(dst.img, p.img, C.int(dstX), C.int(dstY), C.int(srcX), C.int(srcY),
+ C.int(dstW), C.int(dstH), C.int(srcW), C.int(srcH))
+}
View
182 image.go
@@ -1,188 +1,92 @@
package mogrify
-/*
-#cgo CFLAGS: -fopenmp -I/usr/local/include/ImageMagick -I/usr/include/ImageMagick
-#cgo LDFLAGS: -lMagickCore -lMagickWand
-#include <wand/magick_wand.h>
-*/
-import "C"
-
import (
"bytes"
"errors"
"fmt"
"io"
- "sync"
- "unsafe"
)
-var once sync.Once
-
var (
BlobEmpty = errors.New("blob was empty")
)
-type Image struct {
- wand *C.MagickWand
-}
-
-type ImageError struct {
- message string
- severity int
-}
-
-func (e *ImageError) Error() string {
- return fmt.Sprintf("GraphicsMagick: %s severity: %d", e.message, e.severity)
-}
-
-func Open(filename string) *Image {
- image := NewImage()
-
- if image.OpenFile(filename) == nil {
- return image
- }
- return nil
-}
-
-func (img *Image) error() error {
- var ex C.ExceptionType
-
- char_ptr := C.MagickGetException(img.wand, &ex)
- defer C.MagickRelinquishMemory(unsafe.Pointer(char_ptr))
+type Format int
- return &ImageError{C.GoString(char_ptr), int(ex)}
-}
-
-func NewImage() *Image {
- image := new(Image)
- image.wand = C.NewMagickWand()
+const (
+ PNG Format = iota
+ GIF
+ JPG
+)
- if image.wand == nil {
- panic(image.error())
- }
+var (
+ JpgLoadError = errors.New("Jpeg cannot be loaded")
+ resampleError = errors.New("Resampling failed")
+)
- return image
+type Image struct {
+ format Format
+ gd *gdImage
+ // ReadFrom func(r Reader) (n int64, err error)
}
-func (img *Image) OpenFile(filename string) error {
- cfilename := C.CString(filename)
- defer C.free(unsafe.Pointer(cfilename))
-
- status := C.MagickReadImage(img.wand, cfilename)
-
- if status == C.MagickFalse {
- return img.error()
- }
- return nil
+func NewImage(format Format) *Image {
+ return &Image{}
}
-func (img *Image) ReadFrom(reader io.Reader) (n int64, err error) {
- buffer := bytes.NewBuffer(nil)
-
+func (img *Image) ReadFromJpeg(reader io.Reader) (n int64, err error) {
+ var buffer bytes.Buffer
n, err = buffer.ReadFrom(reader)
if err != nil {
return
}
- return n, img.OpenBlob(buffer.Bytes())
+ gd := gdCreateFromJpeg(buffer.Bytes())
+ if gd == nil {
+ return n, JpgLoadError
+ }
+ img.Destroy()
+ img.format = JPG
+ img.gd = gd
+
+ return
}
func (img *Image) WriteTo(writer io.Writer) (n int64, err error) {
- buffer, err := img.Blob()
- if err != nil {
- return 0, err
- }
-
- return bytes.NewBuffer(buffer).WriteTo(writer)
+ return 0, nil
}
-func (img *Image) Width() int64 {
- return int64(C.MagickGetImageWidth(img.wand))
+func (img *Image) Width() int {
+ return img.gd.width()
}
-func (img *Image) Height() int64 {
- return int64(C.MagickGetImageHeight(img.wand))
+func (img *Image) Height() int {
+ return img.gd.height()
}
func (img *Image) Dimensions() string {
return fmt.Sprintf("%dx%d", img.Width(), img.Height())
}
-func (img *Image) OpenBlob(bytes []byte) error {
- if len(bytes) < 1 {
- return BlobEmpty
- }
-
- status := C.MagickReadImageBlob(img.wand, unsafe.Pointer(&bytes[0]), C.size_t(len(bytes)))
-
- if status == C.MagickFalse {
- return img.error()
- }
- return nil
-}
-
-func (img *Image) Blob() ([]byte, error) {
- var len C.size_t
- char_ptr := C.MagickGetImageBlob(img.wand, &len)
-
- if char_ptr == nil {
- return nil, img.error()
- }
-
- defer C.MagickRelinquishMemory(unsafe.Pointer(char_ptr))
-
- return C.GoBytes(unsafe.Pointer(char_ptr), C.int(len)), nil
-}
-
-func (img *Image) Write(writer io.Writer) (int, error) {
- bytes, err := img.Blob()
+func (img *Image) CopyResized(width, height int) (*Image, error) {
+ dst := gdCreate(width, height)
+ img.gd.gdCopyResized(dst, 0, 0, 0, 0, width, height, img.gd.width(), img.gd.height())
- if err != nil {
- return 0, img.error()
- }
-
- return writer.Write(bytes)
-}
-
-func (img *Image) Resize(width, height uint) error {
- res := C.MagickResizeImage(img.wand, C.size_t(width), C.size_t(height), C.GaussianFilter, 1)
-
- if res == C.MagickFalse {
- return img.error()
- }
- return nil
+ return &Image{img.format, dst}, nil
}
-func (img *Image) NewTransformation(crop, geometry string) (*Image, error) {
- ccrop := C.CString(crop)
- defer C.free(unsafe.Pointer(ccrop))
+func (img *Image) CopyResampled(width, height int) (*Image, error) {
+ dst := gdCreate(width, height)
+ img.gd.gdCopyResampled(dst, 0, 0, 0, 0, width, height, img.gd.width(), img.gd.height())
- cgeometry := C.CString(geometry)
- defer C.free(unsafe.Pointer(cgeometry))
-
- wand := C.MagickTransformImage(img.wand, ccrop, cgeometry)
-
- if wand == nil {
- return nil, img.error()
+ if dst.invalid() {
+ return nil, resampleError
}
- return &Image{(*C.MagickWand)(wand)}, nil
-}
-
-func (img *Image) SaveFile(filename string) error {
- cfilename := C.CString(filename)
- defer C.free(unsafe.Pointer(cfilename))
-
- status := C.MagickWriteImage(img.wand, cfilename)
- if status == C.MagickFalse {
- return img.error()
- }
- return nil
+ return &Image{img.format, dst}, nil
}
func (img *Image) Destroy() {
- if img.wand != nil {
- C.DestroyMagickWand(img.wand)
- }
+ img.gd.gdDestroy()
}
View
305 image_test.go
@@ -1,38 +1,41 @@
package mogrify
import (
- "io/ioutil"
- "log"
"os"
- "runtime"
+
"testing"
)
func assertDimension(t *testing.T, img *Image, expected string) {
if actual := img.Dimensions(); actual != expected {
- t.Fatalf("Got wrong dimensions expected:%s got %s", expected, actual)
+ t.Errorf("Got wrong dimensions expected:%s got %s", expected, actual)
}
}
-func TestOpenExisting(t *testing.T) {
- img := Open("./assets/image.jpg")
- if img == nil {
- t.Fail()
- }
- img.Destroy()
+func asset(asset string) *Image {
+ file, _ := os.Open("./assets/image.jpg")
+ defer file.Close()
+
+ image := NewImage(JPG)
+ image.ReadFromJpeg(file)
+ return image
}
-func TestOpenNonExisting(t *testing.T) {
- if Open("./assets/image_does_not_exist.jpg") != nil {
- t.Fail()
- }
+func TestOpenExisting(t *testing.T) {
+ file, _ := os.Open("./assets/image.jpg")
+ defer file.Close()
+
+ image := NewImage(JPG)
+ defer image.Destroy()
+
+ image.ReadFromJpeg(file)
+
+ assertDimension(t, image, "600x399")
}
func TestHeightWidth(t *testing.T) {
- img := Open("./assets/image.jpg")
- if img == nil {
- t.Fail()
- }
+ img := asset("./assets/image.jpg")
+ defer img.Destroy()
if img.Width() != 600 {
@@ -45,192 +48,192 @@ func TestHeightWidth(t *testing.T) {
}
func TestResizeSuccess(t *testing.T) {
- img := Open("./assets/image.jpg")
-
- if img == nil {
- t.Fail()
- return
- }
-
+ img := asset("./assets/image.jpg")
defer img.Destroy()
- status := img.Resize(50, 50)
- if status != nil {
- log.Printf("resize failed %s", status)
- t.Fail()
+ resized, err := img.CopyResized(50, 50)
+ if err != nil {
+ t.Error(err)
}
+ defer resized.Destroy()
- if img.Width() != 50 || img.Height() != 50 {
- log.Printf("size was %dx%d", img.Width(), img.Height())
- t.Fail()
- }
+ assertDimension(t, resized, "50x50")
}
-func TestResizeFailure(t *testing.T) {
- img := Open("./assets/image.jpg")
+func TestResampleSuccess(t *testing.T) {
+ img := asset("./assets/image.jpg")
+ defer img.Destroy()
- if img == nil {
- t.Fail()
- return
+ resized, err := img.CopyResampled(50, 50)
+ if err != nil {
+ t.Error(err)
}
+ defer resized.Destroy()
+
+ assertDimension(t, resized, "50x50")
+}
+func TestResampleFailure(t *testing.T) {
+ img := asset("./assets/image.jpg")
defer img.Destroy()
- status := img.Resize(0, 50)
- if status == nil {
- t.Fail()
+ resized, err := img.CopyResampled(0, 50)
+ if err == nil {
+ t.Fatalf("This should have failed...")
}
+
+ assertDimension(t, resized, "50x50")
}
-func TestSaveToSuccess(t *testing.T) {
- img := Open("./assets/image.jpg")
+// func TestSaveToSuccess(t *testing.T) {
+// img := Open("./assets/image.jpg")
- if img == nil {
- t.Fail()
- return
- }
+// if img == nil {
+// t.Fail()
+// return
+// }
- defer img.Destroy()
+// defer img.Destroy()
- res := img.SaveFile("/tmp/img.jpg")
- if res != nil {
- t.Fail()
- }
-}
+// res := img.SaveFile("/tmp/img.jpg")
+// if res != nil {
+// t.Fail()
+// }
+// }
-func TestSaveToFailure(t *testing.T) {
- img := Open("./assets/image.jpg")
+// func TestSaveToFailure(t *testing.T) {
+// img := Open("./assets/image.jpg")
- if img == nil {
- t.Fail()
- return
- }
+// if img == nil {
+// t.Fail()
+// return
+// }
- defer img.Destroy()
+// defer img.Destroy()
- res := img.SaveFile("/dgksjogdsksdgsdkgsd;lfsd-does-not-exist/img.jpg")
- if res == nil {
- t.Fail()
- }
-}
+// res := img.SaveFile("/dgksjogdsksdgsdkgsd;lfsd-does-not-exist/img.jpg")
+// if res == nil {
+// t.Fail()
+// }
+// }
-func TestOpenBlopSuccess(t *testing.T) {
- bytes, _ := ioutil.ReadFile("./assets/image.jpg")
+// func TestOpenBlopSuccess(t *testing.T) {
+// bytes, _ := ioutil.ReadFile("./assets/image.jpg")
- img := NewImage()
- res := img.OpenBlob(bytes)
- defer img.Destroy()
+// img := NewImage()
+// res := img.OpenBlob(bytes)
+// defer img.Destroy()
- if res != nil {
- t.FailNow()
- }
+// if res != nil {
+// t.FailNow()
+// }
- assertDimension(t, img, "600x399")
-}
+// assertDimension(t, img, "600x399")
+// }
-func TestOpenBlopSuccessPng(t *testing.T) {
- bytes, _ := ioutil.ReadFile("./assets/example.com.png")
+// func TestOpenBlopSuccessPng(t *testing.T) {
+// bytes, _ := ioutil.ReadFile("./assets/example.com.png")
- img := NewImage()
- res := img.OpenBlob(bytes)
- defer img.Destroy()
+// img := NewImage()
+// res := img.OpenBlob(bytes)
+// defer img.Destroy()
- if res != nil {
- t.FailNow()
- }
+// if res != nil {
+// t.FailNow()
+// }
- if dim := img.Dimensions(); dim != "1280x500" {
- t.Fatalf("Got wrong dimensions expected:1280x500 got %s", dim)
- }
-}
+// if dim := img.Dimensions(); dim != "1280x500" {
+// t.Fatalf("Got wrong dimensions expected:1280x500 got %s", dim)
+// }
+// }
-func TestOpenBlopFailure(t *testing.T) {
+// func TestOpenBlopFailure(t *testing.T) {
- img := NewImage()
- res := img.OpenBlob([]byte{'a'})
- defer img.Destroy()
+// img := NewImage()
+// res := img.OpenBlob([]byte{'a'})
+// defer img.Destroy()
- if res == nil {
- t.Fail()
- }
+// if res == nil {
+// t.Fail()
+// }
- res = img.OpenBlob([]byte{})
+// res = img.OpenBlob([]byte{})
- if res == nil {
- t.Fail()
- }
-}
+// if res == nil {
+// t.Fail()
+// }
+// }
-func TestSaveToBlob(t *testing.T) {
- img := Open("./assets/image.jpg")
- defer img.Destroy()
+// func TestSaveToBlob(t *testing.T) {
+// img := Open("./assets/image.jpg")
+// defer img.Destroy()
- fp, err := os.Create("/tmp/img3.jpg")
- if err != nil {
- t.Fail()
- }
+// fp, err := os.Create("/tmp/img3.jpg")
+// if err != nil {
+// t.Fail()
+// }
- defer fp.Close()
+// defer fp.Close()
- _, err = img.Write(fp)
+// _, err = img.Write(fp)
- if err != nil {
- t.Fail()
- }
+// if err != nil {
+// t.Fail()
+// }
-}
+// }
-func TestTransformation(t *testing.T) {
- img := Open("./assets/image.jpg")
- defer img.Destroy()
+// func TestTransformation(t *testing.T) {
+// img := Open("./assets/image.jpg")
+// defer img.Destroy()
- img2, err := img.NewTransformation("", "100x50>")
+// img2, err := img.NewTransformation("", "100x50>")
- if err != nil {
- t.FailNow()
- return
- }
+// if err != nil {
+// t.FailNow()
+// return
+// }
- defer img2.Destroy()
+// defer img2.Destroy()
- assertDimension(t, img2, "75x50")
+// assertDimension(t, img2, "75x50")
- img3, err := img.NewTransformation("", "100x50!")
- defer img3.Destroy()
+// img3, err := img.NewTransformation("", "100x50!")
+// defer img3.Destroy()
- if err != nil {
- t.FailNow()
- return
- }
+// if err != nil {
+// t.FailNow()
+// return
+// }
- assertDimension(t, img3, "100x50")
+// assertDimension(t, img3, "100x50")
- //img2.SaveFile("/tmp/img4.jpg")
-}
+// //img2.SaveFile("/tmp/img4.jpg")
+// }
-func TestReadFrom(t *testing.T) {
- file, _ := os.Open("./assets/image.jpg")
- image := NewImage()
- image.ReadFrom(file)
- assertDimension(t, image, "100x50")
-}
+// func TestReadFrom(t *testing.T) {
+// file, _ := os.Open("./assets/image.jpg")
+// image := NewImage()
+// image.ReadFrom(file)
+// assertDimension(t, image, "100x50")
+// }
-func BenchmarkAndMemoryTest(b *testing.B) {
- var before runtime.MemStats
- var after runtime.MemStats
+// func BenchmarkAndMemoryTest(b *testing.B) {
+// var before runtime.MemStats
+// var after runtime.MemStats
- runtime.ReadMemStats(&before)
+// runtime.ReadMemStats(&before)
- work := func() {
- img := Open("./assets/image.jpg")
- img.Destroy()
- }
+// work := func() {
+// img := Open("./assets/image.jpg")
+// img.Destroy()
+// }
- for i := 0; i < 100; i++ {
- work()
- }
+// for i := 0; i < 100; i++ {
+// work()
+// }
- runtime.ReadMemStats(&after)
+// runtime.ReadMemStats(&after)
- log.Printf("sys memory before: %d after %d - diff: %d", before.HeapSys, after.HeapSys, after.HeapSys-before.HeapSys)
-}
+// log.Printf("sys memory before: %d after %d - diff: %d", before.HeapSys, after.HeapSys, after.HeapSys-before.HeapSys)
+// }
Please sign in to comment.
Something went wrong with that request. Please try again.