diff --git a/canvas/README.md b/canvas/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..71fe4b305487c700369a8b11166a00486b65abbf
--- /dev/null
+++ b/canvas/README.md
@@ -0,0 +1,89 @@
+## gosexy/canvas
+
+``gosexy/canvas`` is an image processing library based on ImageMagick's MagickWand, for the Go programming language.
+
+## Requeriments
+
+### Mac OSX
+
+The ImageMagick's header files are required. If you're using ``brew`` the installation is straightforward.
+
+    $ brew install imagemagick
+
+### Debian
+
+Debian has an old version of MagickWand, in order to install gocanvas we need to install the old version and then upgrade it.
+
+Getting the old version of MagickWand along all its dependencies.
+
+    $ sudo aptitude install libmagickwand-dev
+
+Installing a newer version of ImageMagick over the old files.
+
+    $ sudo su
+    # cd /usr/local/src
+    # wget http://www.imagemagick.org/download/ImageMagick.tar.gz
+    # tar xvzf ImageMagick.tar.gz
+    # cd ImageMagick-6.x.y
+    # ./configure --prefix=/usr
+    # make
+    # make install
+
+### Arch Linux
+
+Arch Linux already has a recent version of MagickWand.
+
+    $ sudo pacman -S extra/imagemagick
+
+### Windows
+
+Choose your [favorite binary](http://imagemagick.com/script/binary-releases.php#windows) and try it.
+
+### Other OS
+
+Please, follow the [install from source](http://imagemagick.com/script/install-source.php?ImageMagick=9uv1bcgofrv21mhftmlk4v1465) tutorial.
+
+## Installation
+
+After installing ImageMagick's header files, pull gocanvas from github:
+
+    $ go get github.com/xiam/gosexy/canvas
+
+## Updating
+
+After installing, you can use `go get -u github.com/xiam/gosexy/canvas` to keep up to date.
+
+## Usage
+
+    package main
+
+    import "github.com/xiam/gosexy/canvas"
+
+    func main() {
+      cv := canvas.New()
+      defer cv.Destroy()
+
+      // Opening some image from disk.
+      opened := cv.Open("examples/input/example.png")
+
+      if opened {
+
+        // Photo auto orientation based on EXIF tags.
+        canvas.AutoOrientate()
+
+        // Creating a squared thumbnail
+        canvas.Thumbnail(100, 100)
+
+        // Saving the thumbnail to disk.
+        canvas.Write("examples/output/example-thumbnail.png")
+
+      }
+    }
+
+## Documentation
+
+You can read ``gosexy/canvas`` documentation from a terminal
+
+    $ go doc github.com/xiam/gosexy/canvas
+
+Or you can [browse it](http://go.pkgdoc.org/github.com/xiam/gosexy/canvas) online.
diff --git a/canvas/canvas.go b/canvas/canvas.go
new file mode 100644
index 0000000000000000000000000000000000000000..024c8a748f2005e2663ccc2e51ae85357d479417
--- /dev/null
+++ b/canvas/canvas.go
@@ -0,0 +1,637 @@
+/*
+  Copyright (c) 2012 JosĂŠ Carlos Nieto, http://xiam.menteslibres.org/
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package canvas
+
+/*
+#cgo LDFLAGS: -lMagickWand -lMagickCore
+#cgo CFLAGS: -fopenmp -I/usr/include/ImageMagick  
+
+#include <wand/magick_wand.h>
+
+char *MagickGetPropertyName(char **properties, size_t index) {
+  return properties[index];
+}
+*/
+import "C"
+
+import "math"
+
+import "fmt"
+
+import "unsafe"
+
+import "strings"
+
+import "strconv"
+
+var (
+	STROKE_BUTT_CAP   = uint(C.ButtCap)
+	STROKE_ROUND_CAP  = uint(C.RoundCap)
+	STROKE_SQUARE_CAP = uint(C.SquareCap)
+
+	STROKE_MITER_JOIN = uint(C.MiterJoin)
+	STROKE_ROUND_JOIN = uint(C.RoundJoin)
+	STROKE_BEVEL_JOIN = uint(C.BevelJoin)
+
+	FILL_EVEN_ODD_RULE = uint(C.EvenOddRule)
+	FILL_NON_ZERO_RULE = uint(C.NonZeroRule)
+
+	RAD_TO_DEG = 180 / math.Pi
+	DEG_TO_RAD = math.Pi / 180
+
+	UNDEFINED_ORIENTATION    = uint(C.UndefinedOrientation)
+	TOP_LEFT_ORIENTATION     = uint(C.TopLeftOrientation)
+	TOP_RIGHT_ORIENTATION    = uint(C.TopRightOrientation)
+	BOTTOM_RIGHT_ORIENTATION = uint(C.BottomRightOrientation)
+	BOTTOM_LEFT_ORIENTATION  = uint(C.BottomLeftOrientation)
+	LEFT_TOP_ORIENTATION     = uint(C.LeftTopOrientation)
+	RIGHT_TOP_ORIENTATION    = uint(C.RightTopOrientation)
+	RIGHT_BOTTOM_ORIENTATION = uint(C.RightBottomOrientation)
+	LEFT_BOTTOM_ORIENTATION  = uint(C.LeftBottomOrientation)
+)
+
+type Canvas struct {
+	wand *C.MagickWand
+
+	fg *C.PixelWand
+	bg *C.PixelWand
+
+	drawing *C.DrawingWand
+
+	fill   *C.PixelWand
+	stroke *C.PixelWand
+
+	filename string
+	width    string
+	height   string
+}
+
+// Private: returns wand's hexadecimal color.
+func getPixelHexColor(p *C.PixelWand) string {
+	var rgb [3]float64
+
+	rgb[0] = float64(C.PixelGetRed(p))
+	rgb[1] = float64(C.PixelGetGreen(p))
+	rgb[2] = float64(C.PixelGetBlue(p))
+
+	return fmt.Sprintf("#%02x%02x%02x", int(rgb[0]*255.0), int(rgb[1]*255.0), int(rgb[2]*255.0))
+}
+
+// Private: returns MagickTrue or MagickFalse 
+func magickBoolean(value bool) C.MagickBooleanType {
+	if value == true {
+		return C.MagickTrue
+	}
+	return C.MagickFalse
+}
+
+// Initializes the canvas environment.
+func (cv Canvas) Init() {
+	C.MagickWandGenesis()
+}
+
+// Opens an image file, returns true on success.
+func (cv Canvas) Open(filename string) bool {
+	cv.filename = filename
+	status := C.MagickReadImage(cv.wand, C.CString(cv.filename))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Auto-orientates canvas based on its original image's EXIF metadata
+func (cv Canvas) AutoOrientate() bool {
+
+	data := cv.Metadata()
+
+	orientation, err := strconv.Atoi(data["exif:Orientation"])
+
+	if err != nil {
+		return false
+	}
+
+	switch uint(orientation) {
+	case TOP_LEFT_ORIENTATION:
+		// normal
+
+	case TOP_RIGHT_ORIENTATION:
+		cv.Flop()
+
+	case BOTTOM_RIGHT_ORIENTATION:
+		cv.RotateCanvas(math.Pi)
+
+	case BOTTOM_LEFT_ORIENTATION:
+		cv.Flip()
+
+	case LEFT_TOP_ORIENTATION:
+		cv.Flip()
+		cv.RotateCanvas(-math.Pi / 2)
+
+	case RIGHT_TOP_ORIENTATION:
+		cv.RotateCanvas(-math.Pi / 2)
+
+	case RIGHT_BOTTOM_ORIENTATION:
+		cv.Flop()
+		cv.RotateCanvas(-math.Pi / 2)
+
+	case LEFT_BOTTOM_ORIENTATION:
+		cv.RotateCanvas(math.Pi / 2)
+
+	default:
+		return false
+	}
+
+	C.MagickSetImageOrientation(cv.wand, (C.OrientationType)(TOP_LEFT_ORIENTATION))
+	cv.SetMetadata("exif:Orientation", (string)(TOP_LEFT_ORIENTATION))
+
+	return true
+}
+
+// Returns all metadata keys from the currently loaded image.
+func (cv Canvas) Metadata() map[string]string {
+	var n C.size_t
+	var i C.size_t
+
+	var value *C.char
+	var key *C.char
+
+	data := make(map[string]string)
+
+	properties := C.MagickGetImageProperties(cv.wand, C.CString("*"), &n)
+
+	for i = 0; i < n; i++ {
+		key = C.MagickGetPropertyName(properties, i)
+		value = C.MagickGetImageProperty(cv.wand, key)
+
+		data[strings.Trim(C.GoString(key), " ")] = strings.Trim(C.GoString(value), " ")
+
+		C.MagickRelinquishMemory(unsafe.Pointer(value))
+		C.MagickRelinquishMemory(unsafe.Pointer(key))
+	}
+
+	return data
+}
+
+// Associates a metadata key with its value.
+func (cv Canvas) SetMetadata(key string, value string) {
+	C.MagickSetImageProperty(cv.wand, C.CString(key), C.CString(value))
+}
+
+// Creates a horizontal mirror image by reflecting the pixels around the central y-axis.
+func (cv Canvas) Flop() bool {
+	status := C.MagickFlopImage(cv.wand)
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Creates a vertical mirror image by reflecting the pixels around the central x-axis.
+func (cv Canvas) Flip() bool {
+	status := C.MagickFlipImage(cv.wand)
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Creates a centered thumbnail of the canvas.
+func (cv Canvas) Thumbnail(width uint, height uint) bool {
+
+	var ratio float64
+
+	// Normalizing image.
+
+	ratio = math.Min(float64(cv.Width())/float64(width), float64(cv.Height())/float64(height))
+
+	if ratio < 1.0 {
+		// Origin image is smaller than the thumbnail image.
+		max := uint(math.Max(float64(width), float64(height)))
+
+		// Empty replacement buffer with transparent background.
+		replacement := New()
+
+		replacement.SetBackgroundColor("none")
+
+		replacement.Blank(max, max)
+
+		// Putting original image in the center of the replacement canvas.
+		replacement.AppendCanvas(cv, int(int(width-cv.Width())/2), int(int(height-cv.Height())/2))
+
+		// Replacing wand
+		C.DestroyMagickWand(cv.wand)
+
+		cv.wand = C.CloneMagickWand(replacement.wand)
+
+	} else {
+		// Is bigger, just resizing.
+		cv.Resize(uint(float64(cv.Width())/ratio), uint(float64(cv.Height())/ratio))
+	}
+
+	// Now we have an image that we can use to crop the thumbnail from.
+	cv.Crop(int(int(cv.Width()-width)/2), int(int(cv.Height()-height)/2), width, height)
+
+	return true
+
+}
+
+// Puts a canvas on top of the current one.
+func (cv Canvas) AppendCanvas(source Canvas, x int, y int) bool {
+	status := C.MagickCompositeImage(cv.wand, source.wand, C.OverCompositeOp, C.ssize_t(x), C.ssize_t(y))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Rotates the whole canvas.
+func (cv Canvas) RotateCanvas(rad float64) {
+	C.MagickRotateImage(cv.wand, cv.bg, C.double(RAD_TO_DEG*rad))
+}
+
+// Returns canvas' width.
+func (cv Canvas) Width() uint {
+	return uint(C.MagickGetImageWidth(cv.wand))
+}
+
+// Returns canvas' height.
+func (cv Canvas) Height() uint {
+	return uint(C.MagickGetImageHeight(cv.wand))
+}
+
+// Writes canvas to a file, returns true on success.
+func (cv Canvas) Write(filename string) bool {
+	cv.Update()
+	status := C.MagickWriteImage(cv.wand, C.CString(filename))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Changes the size of the canvas, returns true on success.
+func (cv Canvas) Resize(width uint, height uint) bool {
+	status := C.MagickResizeImage(cv.wand, C.size_t(width), C.size_t(height), C.GaussianFilter, C.double(1.0))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Adaptively changes the size of the canvas, returns true on success.
+func (cv Canvas) AdaptiveResize(width uint, height uint) bool {
+	status := C.MagickAdaptiveResizeImage(cv.wand, C.size_t(width), C.size_t(height))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Changes the compression quality of the canvas. Ranges from 1 (lowest) to 100 (highest).
+func (cv Canvas) SetQuality(quality uint) bool {
+	status := C.MagickSetImageCompressionQuality(cv.wand, C.size_t(quality))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Returns the compression quality of the canvas. Ranges from 1 (lowest) to 100 (highest).
+func (cv Canvas) Quality() uint {
+	return uint(C.MagickGetImageCompressionQuality(cv.wand))
+}
+
+/*
+// Sets canvas's foreground color.
+func (cv Canvas) SetColor(color string) (bool) {
+  status := C.PixelSetColor(cv.fg, C.CString(color))
+  if status == C.MagickFalse {
+    return false
+  }
+  return true
+}
+*/
+
+// Sets canvas' background color.
+func (cv Canvas) SetBackgroundColor(color string) bool {
+	C.PixelSetColor(cv.bg, C.CString(color))
+	status := C.MagickSetImageBackgroundColor(cv.wand, cv.bg)
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Returns canvas' background color.
+func (cv Canvas) BackgroundColor() string {
+	return getPixelHexColor(cv.bg)
+}
+
+// Sets antialiasing setting for the current drawing stroke.
+func (cv Canvas) SetStrokeAntialias(value bool) {
+	C.DrawSetStrokeAntialias(cv.drawing, magickBoolean(value))
+}
+
+// Returns antialiasing setting for the current drawing stroke.
+func (cv Canvas) StrokeAntialias() bool {
+	value := C.DrawGetStrokeAntialias(cv.drawing)
+	if value == C.MagickTrue {
+		return true
+	}
+	return false
+}
+
+// Sets the width of the stroke on the current drawing surface.
+func (cv Canvas) SetStrokeWidth(value float64) {
+	C.DrawSetStrokeWidth(cv.drawing, C.double(value))
+}
+
+// Returns the width of the stroke on the current drawing surface.
+func (cv Canvas) StrokeWidth() float64 {
+	return float64(C.DrawGetStrokeWidth(cv.drawing))
+}
+
+// Sets the opacity of the stroke on the current drawing surface.
+func (cv Canvas) SetStrokeOpacity(value float64) {
+	C.DrawSetStrokeOpacity(cv.drawing, C.double(value))
+}
+
+// Returns the opacity of the stroke on the current drawing surface.
+func (cv Canvas) StrokeOpacity() float64 {
+	return float64(C.DrawGetStrokeOpacity(cv.drawing))
+}
+
+// Sets the type of the line cap on the current drawing surface.
+func (cv Canvas) SetStrokeLineCap(value uint) {
+	C.DrawSetStrokeLineCap(cv.drawing, C.LineCap(value))
+}
+
+// Returns the type of the line cap on the current drawing surface.
+func (cv Canvas) StrokeLineCap() uint {
+	return uint(C.DrawGetStrokeLineCap(cv.drawing))
+}
+
+// Sets the type of the line join on the current drawing surface.
+func (cv Canvas) SetStrokeLineJoin(value uint) {
+	C.DrawSetStrokeLineJoin(cv.drawing, C.LineJoin(value))
+}
+
+// Returns the type of the line join on the current drawing surface.
+func (cv Canvas) StrokeLineJoin() uint {
+	return uint(C.DrawGetStrokeLineJoin(cv.drawing))
+}
+
+/*
+func (cv Canvas) SetFillRule(value int) {
+  C.DrawSetFillRule(cv.drawing, C.FillRule(value))
+}
+*/
+
+// Sets the fill color for enclosed areas on the current drawing surface.
+func (cv Canvas) SetFillColor(color string) {
+	C.PixelSetColor(cv.fill, C.CString(color))
+	C.DrawSetFillColor(cv.drawing, cv.fill)
+}
+
+// Returns the fill color for enclosed areas on the current drawing surface.
+func (cv Canvas) FillColor() string {
+	return getPixelHexColor(cv.fill)
+}
+
+// Sets the stroke color on the current drawing surface.
+func (cv Canvas) SetStrokeColor(color string) {
+	C.PixelSetColor(cv.stroke, C.CString(color))
+	C.DrawSetStrokeColor(cv.drawing, cv.stroke)
+}
+
+// Returns the stroke color on the current drawing surface.
+func (cv Canvas) StrokeColor() string {
+	return getPixelHexColor(cv.stroke)
+}
+
+// Draws a circle over the current drawing surface.
+func (cv Canvas) Circle(radius float64) {
+	C.DrawCircle(cv.drawing, C.double(0), C.double(0), C.double(radius), C.double(0))
+}
+
+// Draws a rectangle over the current drawing surface.
+func (cv Canvas) Rectangle(x float64, y float64) {
+	C.DrawRectangle(cv.drawing, C.double(0), C.double(0), C.double(x), C.double(y))
+}
+
+// Moves the current coordinate system origin to the specified coordinate.
+func (cv Canvas) Translate(x float64, y float64) {
+	C.DrawTranslate(cv.drawing, C.double(x), C.double(y))
+}
+
+// Applies a scaling factor to the units of the current coordinate system.
+func (cv Canvas) Scale(x float64, y float64) {
+	C.DrawScale(cv.drawing, C.double(x), C.double(y))
+}
+
+// Draws a line starting on the current coordinate system origin and ending on the specified coordinates.
+func (cv Canvas) Line(x float64, y float64) {
+	C.DrawLine(cv.drawing, C.double(0), C.double(0), C.double(x), C.double(y))
+}
+
+/*
+func (cv Canvas) Skew(x float64, y float64) {
+  C.DrawSkewX(cv.drawing, C.double(x))
+  C.DrawSkewY(cv.drawing, C.double(y))
+}
+*/
+
+// Applies a rotation of a given angle (in radians) on the current coordinate system.
+func (cv Canvas) Rotate(rad float64) {
+	deg := RAD_TO_DEG * rad
+	C.DrawRotate(cv.drawing, C.double(deg))
+}
+
+// Draws an ellipse centered at the current coordinate system's origin.
+func (cv Canvas) Ellipse(a float64, b float64) {
+	C.DrawEllipse(cv.drawing, C.double(0), C.double(0), C.double(a), C.double(b), 0, 360)
+}
+
+// Clones the current drawing surface and stores it in a stack.
+func (cv Canvas) PushDrawing() bool {
+	status := C.PushDrawingWand(cv.drawing)
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Destroys the current drawing surface and returns the latest surface that was pushed to the stack.
+func (cv Canvas) PopDrawing() bool {
+	status := C.PopDrawingWand(cv.drawing)
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Copies a drawing surface to the canvas.
+func (cv Canvas) Update() {
+	C.MagickDrawImage(cv.wand, cv.drawing)
+}
+
+// Destroys canvas.
+func (cv Canvas) Destroy() {
+	if cv.wand != nil {
+		C.DestroyMagickWand(cv.wand)
+	}
+	C.MagickWandTerminus()
+}
+
+// Creates an empty canvas of the given dimensions.
+func (cv Canvas) Blank(width uint, height uint) bool {
+	status := C.MagickNewImage(cv.wand, C.size_t(width), C.size_t(height), cv.bg)
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Convolves the canvas with a Gaussian function given its standard deviation.
+func (cv Canvas) Blur(sigma float64) bool {
+	status := C.MagickBlurImage(cv.wand, C.double(0), C.double(sigma))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Adaptively blurs the image by blurring less intensely near the edges and more intensely far from edges.
+func (cv Canvas) AdaptiveBlur(sigma float64) bool {
+	status := C.MagickAdaptiveBlurImage(cv.wand, C.double(0), C.double(sigma))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Adds random noise to the canvas.
+func (cv Canvas) AddNoise() bool {
+	status := C.MagickAddNoiseImage(cv.wand, C.GaussianNoise)
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Removes a region of a canvas and collapses the canvas to occupy the removed portion.
+func (cv Canvas) Chop(x int, y int, width uint, height uint) bool {
+	status := C.MagickChopImage(cv.wand, C.size_t(width), C.size_t(height), C.ssize_t(x), C.ssize_t(y))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Extracts a region from the canvas.
+func (cv Canvas) Crop(x int, y int, width uint, height uint) bool {
+	status := C.MagickCropImage(cv.wand, C.size_t(width), C.size_t(height), C.ssize_t(x), C.ssize_t(y))
+	if status == C.MagickFalse {
+		return false
+	}
+	return true
+}
+
+// Adjusts the canvas's brightness given a factor (-1.0 thru 1.0)
+func (cv Canvas) SetBrightness(factor float64) bool {
+
+	factor = math.Max(-1, factor)
+	factor = math.Min(1, factor)
+
+	status := C.MagickModulateImage(cv.wand, C.double(100+factor*100.0), C.double(100), C.double(100))
+
+	if status == C.MagickFalse {
+		return false
+	}
+
+	return true
+}
+
+// Adjusts the canvas's saturation given a factor (-1.0 thru 1.0)
+func (cv Canvas) SetSaturation(factor float64) bool {
+
+	factor = math.Max(-1, factor)
+	factor = math.Min(1, factor)
+
+	status := C.MagickModulateImage(cv.wand, C.double(100), C.double(100+factor*100.0), C.double(100))
+
+	if status == C.MagickFalse {
+		return false
+	}
+
+	return true
+}
+
+// Adjusts the canvas's hue given a factor (-1.0 thru 1.0)
+func (cv Canvas) SetHue(factor float64) bool {
+
+	factor = math.Max(-1, factor)
+	factor = math.Min(1, factor)
+
+	status := C.MagickModulateImage(cv.wand, C.double(100), C.double(100), C.double(100+factor*100.0))
+
+	if status == C.MagickFalse {
+		return false
+	}
+
+	return true
+}
+
+// Returns a new canvas object.
+func New() *Canvas {
+	cv := &Canvas{}
+
+	cv.Init()
+
+	cv.wand = C.NewMagickWand()
+
+	cv.fg = C.NewPixelWand()
+	cv.bg = C.NewPixelWand()
+
+	cv.fill = C.NewPixelWand()
+	cv.stroke = C.NewPixelWand()
+
+	cv.drawing = C.NewDrawingWand()
+
+	//cv.SetColor("#ffffff")
+	cv.SetBackgroundColor("none")
+
+	cv.SetStrokeColor("#ffffff")
+	cv.SetStrokeAntialias(true)
+	cv.SetStrokeWidth(1.0)
+	cv.SetStrokeOpacity(1.0)
+	cv.SetStrokeLineCap(STROKE_ROUND_CAP)
+	cv.SetStrokeLineJoin(STROKE_ROUND_JOIN)
+
+	//cv.SetFillRule(FILL_EVEN_ODD_RULE)
+	cv.SetFillColor("#888888")
+
+	return cv
+}
diff --git a/canvas/canvas_test.go b/canvas/canvas_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2b720f4b4d69fa072b455536dff0f9b0fe7ddee0
--- /dev/null
+++ b/canvas/canvas_test.go
@@ -0,0 +1,332 @@
+package canvas
+
+import "testing"
+
+import "math"
+
+/*
+  Example image is form Yuko Honda
+  http://www.flickr.com/photos/yukop/6779040884/
+*/
+
+func TestOpenWrite(t *testing.T) {
+	canvas := New()
+
+	opened := canvas.Open("examples/input/example.png")
+
+	if opened {
+		canvas.AutoOrientate()
+
+		canvas.SetQuality(90)
+
+		canvas.Write("examples/output/example.jpg")
+	}
+
+	canvas.Destroy()
+}
+
+func TestThumbnail(t *testing.T) {
+	canvas := New()
+
+	opened := canvas.Open("examples/input/example.png")
+
+	if opened {
+		canvas.AutoOrientate()
+
+		canvas.Thumbnail(100, 100)
+
+		canvas.Write("examples/output/example-thumbnail.png")
+	}
+
+	canvas.Destroy()
+}
+
+func TestResize(t *testing.T) {
+	canvas := New()
+
+	opened := canvas.Open("examples/input/example.png")
+
+	if opened {
+		canvas.Resize(100, 100)
+		canvas.Write("examples/output/example-100x100.png")
+	}
+
+	canvas.Destroy()
+}
+
+func TestBlank(t *testing.T) {
+	canvas := New()
+
+	canvas.SetBackgroundColor("#00ff00")
+
+	success := canvas.Blank(400, 400)
+
+	if success {
+		canvas.Write("examples/output/example-blank.png")
+	}
+
+	canvas.Destroy()
+}
+
+func TestSettersAndGetters(t *testing.T) {
+
+	canvas := New()
+
+	success := canvas.Blank(400, 400)
+	if success != true {
+		t.Errorf("Could not create blank image.")
+	}
+
+	const backgroundColor = "#112233"
+
+	canvas.SetBackgroundColor(backgroundColor)
+
+	if gotBackgroundColor := canvas.BackgroundColor(); gotBackgroundColor != backgroundColor {
+		t.Errorf("Got %s, expecting %s", gotBackgroundColor, backgroundColor)
+	}
+
+	const strokeAntialias = true
+
+	canvas.SetStrokeAntialias(strokeAntialias)
+
+	if gotStrokeAntialias := canvas.StrokeAntialias(); gotStrokeAntialias != strokeAntialias {
+		t.Errorf("Got %t, expecting %t.", gotStrokeAntialias, strokeAntialias)
+	}
+
+	const strokeWidth = 2.0
+
+	canvas.SetStrokeWidth(strokeWidth)
+
+	if gotStrokeWidth := canvas.StrokeWidth(); gotStrokeWidth != strokeWidth {
+		t.Errorf("Got %f, expecting %f.", gotStrokeWidth, strokeWidth)
+	}
+
+	const strokeOpacity = 1.0
+
+	canvas.SetStrokeOpacity(strokeOpacity)
+
+	if gotStrokeOpacity := canvas.StrokeOpacity(); gotStrokeOpacity != strokeOpacity {
+		t.Errorf("Got %f, expecting %f.", gotStrokeOpacity, strokeOpacity)
+	}
+
+	strokeLineCap := STROKE_SQUARE_CAP
+
+	canvas.SetStrokeLineCap(strokeLineCap)
+
+	if gotStrokeLineCap := canvas.StrokeLineCap(); gotStrokeLineCap != strokeLineCap {
+		t.Errorf("Got %d, expecting %d.", gotStrokeLineCap, strokeLineCap)
+	}
+
+	strokeLineJoin := STROKE_ROUND_JOIN
+
+	canvas.SetStrokeLineJoin(strokeLineJoin)
+
+	if gotStrokeLineJoin := canvas.StrokeLineJoin(); gotStrokeLineJoin != strokeLineJoin {
+		t.Errorf("Got %d, expecting %d.", gotStrokeLineJoin, strokeLineJoin)
+	}
+
+	const fillColor = "#112233"
+
+	canvas.SetFillColor(fillColor)
+
+	if gotFillColor := canvas.FillColor(); gotFillColor != fillColor {
+		t.Errorf("Got %s, expecting %s", gotFillColor, fillColor)
+	}
+
+	const strokeColor = "#112233"
+
+	canvas.SetStrokeColor(strokeColor)
+
+	if gotStrokeColor := canvas.StrokeColor(); gotStrokeColor != strokeColor {
+		t.Errorf("Got %s, expecting %s", gotStrokeColor, strokeColor)
+	}
+
+	const quality = 76
+
+	canvas.SetQuality(quality)
+
+	if gotQuality := canvas.Quality(); gotQuality != quality {
+		t.Errorf("Got %d, expecting %d", gotQuality, quality)
+	}
+
+	canvas.Destroy()
+}
+
+func TestDrawLine(t *testing.T) {
+
+	canvas := New()
+
+	canvas.SetBackgroundColor("#000000")
+
+	success := canvas.Blank(400, 400)
+
+	if success {
+
+		canvas.Translate(200, 200)
+		canvas.SetStrokeWidth(10)
+		canvas.SetStrokeColor("#ffffff")
+		canvas.Line(100, 100)
+
+		canvas.Write("examples/output/example-line.png")
+	}
+
+	canvas.Destroy()
+}
+
+func TestDrawCircle(t *testing.T) {
+	canvas := New()
+
+	canvas.SetBackgroundColor("#000000")
+
+	success := canvas.Blank(400, 400)
+
+	if success {
+
+		canvas.SetFillColor("#ff0000")
+
+		canvas.PushDrawing()
+		canvas.Translate(200, 200)
+		canvas.SetStrokeWidth(5)
+		canvas.SetStrokeColor("#ffffff")
+		canvas.Circle(100)
+		canvas.PopDrawing()
+
+		canvas.PushDrawing()
+		canvas.Translate(100, 100)
+		canvas.SetStrokeWidth(3)
+		canvas.SetStrokeColor("#ffffff")
+		canvas.Circle(20)
+		canvas.PopDrawing()
+
+		canvas.Write("examples/output/example-circle.png")
+	}
+
+	canvas.Destroy()
+}
+
+func TestDrawRectangle(t *testing.T) {
+	canvas := New()
+
+	canvas.SetBackgroundColor("#000000")
+
+	success := canvas.Blank(400, 400)
+
+	if success {
+
+		canvas.SetFillColor("#ff0000")
+
+		canvas.Translate(200-50, 200+75)
+		canvas.SetStrokeWidth(5)
+		canvas.SetStrokeColor("#ffffff")
+		canvas.Rectangle(100, -150)
+
+		canvas.Write("examples/output/example-rectangle.png")
+	}
+
+	canvas.Destroy()
+}
+
+func TestDrawEllipse(t *testing.T) {
+	canvas := New()
+
+	success := canvas.Blank(400, 400)
+
+	if success {
+
+		canvas.SetFillColor("#ff0000")
+
+		canvas.PushDrawing()
+		canvas.Translate(200, 200)
+		canvas.Rotate(math.Pi / 3)
+		canvas.Ellipse(50, 180)
+		canvas.PopDrawing()
+
+		canvas.SetFillColor("#ff00ff")
+
+		canvas.PushDrawing()
+		canvas.Translate(200, 200)
+		canvas.Rotate(-math.Pi / 3)
+		canvas.Ellipse(25, 90)
+		canvas.PopDrawing()
+
+		canvas.Write("examples/output/example-ellipse.png")
+	}
+
+	canvas.Destroy()
+}
+
+func TestBlur(t *testing.T) {
+	canvas := New()
+	defer canvas.Destroy()
+
+	opened := canvas.Open("examples/input/example.png")
+
+	if opened {
+		canvas.Blur(3)
+		canvas.Write("examples/output/example-blur.png")
+	}
+}
+
+func TestModulate(t *testing.T) {
+	canvas := New()
+	defer canvas.Destroy()
+
+	opened := canvas.Open("examples/input/example.png")
+
+	if opened {
+		canvas.SetBrightness(-0.5)
+		canvas.SetHue(0.2)
+		canvas.SetSaturation(0.9)
+		canvas.Write("examples/output/example-modulate.png")
+	}
+}
+
+func TestAdaptive(t *testing.T) {
+
+	canvas := New()
+	defer canvas.Destroy()
+
+	opened := canvas.Open("examples/input/example.png")
+
+	if opened {
+		canvas.AdaptiveBlur(1.2)
+		canvas.AdaptiveResize(100, 100)
+		canvas.Write("examples/output/example-adaptive.png")
+	}
+}
+
+func TestNoise(t *testing.T) {
+	canvas := New()
+	defer canvas.Destroy()
+
+	opened := canvas.Open("examples/input/example.png")
+
+	if opened {
+		canvas.AddNoise()
+		canvas.Write("examples/output/example-noise.png")
+	}
+}
+
+func TestChop(t *testing.T) {
+	canvas := New()
+	defer canvas.Destroy()
+
+	opened := canvas.Open("examples/input/example.png")
+
+	if opened {
+		canvas.Chop(0, 0, 100, 50)
+		canvas.Write("examples/output/example-chop.png")
+	}
+}
+
+func TestCrop(t *testing.T) {
+	canvas := New()
+	defer canvas.Destroy()
+
+	opened := canvas.Open("examples/input/example.png")
+
+	if opened {
+		canvas.Crop(100, 200, 200, 100)
+		canvas.Write("examples/output/example-crop.png")
+	}
+}
diff --git a/canvas/examples/input/example.png b/canvas/examples/input/example.png
new file mode 100644
index 0000000000000000000000000000000000000000..a159f593fd0d926fc4b24dddb46908bbeff7e605
Binary files /dev/null and b/canvas/examples/input/example.png differ
diff --git a/canvas/examples/output/empty b/canvas/examples/output/empty
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391