├── .gitignore ├── README.md ├── env.sh ├── examples ├── input │ ├── test.png │ └── test2.jpg └── output │ └── .empty ├── paint.go ├── paint_test.go └── wand ├── const.go ├── magick_image.go ├── magick_wand.go ├── magick_wand_test.go ├── pixel_wand.go └── pixel_wand_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Terry-Mao/paint 2 | 3 | `Terry-Mao/paint` is an image processing library based on ImageMagick for golang. 4 | 5 | ## Requeriments 6 | 7 | ImageMagick's MagickWand development files are required. 8 | 9 | ```sh 10 | # OSX 11 | $ brew install imagemagick 12 | 13 | # Arch Linux 14 | $ sudo pacman -S extra/imagemagick 15 | 16 | # Debian 17 | $ sudo aptitude install libmagickwand-dev 18 | ``` 19 | 20 | ## Installation 21 | 22 | Just pull `Terry-Mao/paint` from github using `go get`: 23 | 24 | ```sh 25 | # download the code 26 | $ go get github.com/Terry-Mao/paint 27 | ``` 28 | 29 | ## Usage 30 | 31 | ```go 32 | package main 33 | 34 | import ( 35 | "github.com/Terry-Mao/paint" 36 | "github.com/Terry-Mao/paint/wand" 37 | ) 38 | 39 | func main() { 40 | wand.Genesis() 41 | defer wand.Terminus() 42 | w := wand.NewMagickWand() 43 | defer w.Destroy() 44 | 45 | if err := w.ReadImage("./examples/input/test2.jpg"); err != nil { 46 | t.Error(err) 47 | } 48 | 49 | if err := paint.Thumbnail(w, 302, 126); err != nil { 50 | t.Error(err) 51 | } 52 | 53 | if err := w.WriteImage("./examples/output/test2-thumbnail.jpg"); err != nil { 54 | t.Error(err) 55 | } 56 | 57 | } 58 | ``` 59 | 60 | ## Documentation 61 | 62 | Read the `Terry-Mao/paint` documentation from a terminal 63 | 64 | ```go 65 | $ go doc github.com/Terry-Mao/paint 66 | $ go doc github.com/Terry-Mao/paint/magickwand 67 | ``` 68 | 69 | Alternatively, you can [paint](http://go.pkgdoc.org/github.com/Terry-Mao/paint) and [paint/wand](http://go.pkgdoc.org/github.com/Terry-Mao/paint/wand) online. 70 | -------------------------------------------------------------------------------- /env.sh: -------------------------------------------------------------------------------- 1 | export CGO_CFLAGS="-I`pkg-config --cflags MagickWand`" 2 | export CGO_LDFLAGS="-I`pkg-config --libs MagickWand`" -------------------------------------------------------------------------------- /examples/input/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Terry-Mao/paint/164a0e19ff474158f97d58b2b45bb1ba6c5c2a52/examples/input/test.png -------------------------------------------------------------------------------- /examples/input/test2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Terry-Mao/paint/164a0e19ff474158f97d58b2b45bb1ba6c5c2a52/examples/input/test2.jpg -------------------------------------------------------------------------------- /examples/output/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Terry-Mao/paint/164a0e19ff474158f97d58b2b45bb1ba6c5c2a52/examples/output/.empty -------------------------------------------------------------------------------- /paint.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Terry.Mao. All rights reserved. 2 | 3 | // Package paint providers most used image function. 4 | package paint 5 | 6 | import ( 7 | "github.com/Terry-Mao/paint/wand" 8 | ) 9 | 10 | // Converts the current image into a thumbnail of the specified width and 11 | // height. 12 | func Thumbnail(w *wand.MagickWand, width, height uint) error { 13 | // Resize 14 | return w.ResizeImage(width, height, wand.GaussianFilter, 1.0) 15 | } 16 | -------------------------------------------------------------------------------- /paint_test.go: -------------------------------------------------------------------------------- 1 | package paint 2 | 3 | import ( 4 | "github.com/Terry-Mao/paint/wand" 5 | "testing" 6 | ) 7 | 8 | func TestThumbnail(t *testing.T) { 9 | 10 | wand.Genesis() 11 | defer wand.Terminus() 12 | w := wand.NewMagickWand() 13 | defer w.Destroy() 14 | 15 | if err := w.ReadImage("./examples/input/test2.jpg"); err != nil { 16 | t.Error(err) 17 | } 18 | 19 | if err := Thumbnail(w, 302, 126); err != nil { 20 | t.Error(err) 21 | } 22 | 23 | if err := w.WriteImage("./examples/output/test2-thumbnail.jpg"); err != nil { 24 | t.Error(err) 25 | } 26 | } 27 | 28 | func BenchmarkThumbnail(b *testing.B) { 29 | wand.Genesis() 30 | defer wand.Terminus() 31 | w := wand.NewMagickWand() 32 | defer w.Destroy() 33 | 34 | b.StopTimer() 35 | b.StartTimer() 36 | 37 | for i := 0; i < 1000; i++ { 38 | if err := w.ReadImage("./examples/input/test2.jpg"); err != nil { 39 | panic(err) 40 | } 41 | 42 | if err := Thumbnail(w, 302, 126); err != nil { 43 | panic(err) 44 | } 45 | 46 | if err := w.WriteImage("./examples/output/test2-thumbnail.jpg"); err != nil { 47 | panic(err) 48 | } 49 | 50 | w.Clear() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /wand/const.go: -------------------------------------------------------------------------------- 1 | package wand 2 | 3 | /* 4 | #cgo pkg-config: MagickWand 5 | 6 | #include 7 | */ 8 | import "C" 9 | 10 | const ( 11 | /* FilterTypes */ 12 | BesselFilter = int(C.BesselFilter) 13 | BlackmanFilter = int(C.BlackmanFilter) 14 | BoxFilter = int(C.BoxFilter) 15 | CatromFilter = int(C.CatromFilter) 16 | GaussianFilter = int(C.GaussianFilter) 17 | HanningFilter = int(C.HanningFilter) 18 | HermiteFilter = int(C.HermiteFilter) 19 | LanczosFilter = int(C.LanczosFilter) 20 | MitchellFilter = int(C.MitchellFilter) 21 | SincFilter = int(C.SincFilter) 22 | TriangleFilter = int(C.TriangleFilter) 23 | 24 | /* Compression */ 25 | UndefinedCompression = int(C.UndefinedCompression) 26 | NoCompression = int(C.NoCompression) 27 | BZipCompression = int(C.BZipCompression) 28 | DXT1Compression = int(C.DXT1Compression) 29 | DXT3Compression = int(C.DXT3Compression) 30 | DXT5Compression = int(C.DXT5Compression) 31 | FaxCompression = int(C.FaxCompression) 32 | Group4Compression = int(C.Group4Compression) 33 | JPEGCompression = int(C.JPEGCompression) 34 | JPEG2000Compression = int(C.JPEG2000Compression) 35 | LosslessJPEGCompression = int(C.LosslessJPEGCompression) 36 | LZWCompression = int(C.LZWCompression) 37 | RLECompression = int(C.RLECompression) 38 | ZipCompression = int(C.ZipCompression) 39 | ZipSCompression = int(C.ZipSCompression) 40 | PizCompression = int(C.PizCompression) 41 | Pxr24Compression = int(C.Pxr24Compression) 42 | B44Compression = int(C.B44Compression) 43 | B44ACompression = int(C.B44ACompression) 44 | 45 | /* Composite */ 46 | UndefinedCompositeOp = int(C.UndefinedCompositeOp) 47 | NoCompositeOp = int(C.NoCompositeOp) 48 | ModulusAddCompositeOp = int(C.ModulusAddCompositeOp) 49 | AtopCompositeOp = int(C.AtopCompositeOp) 50 | BlendCompositeOp = int(C.BlendCompositeOp) 51 | BumpmapCompositeOp = int(C.BumpmapCompositeOp) 52 | ChangeMaskCompositeOp = int(C.ChangeMaskCompositeOp) 53 | ClearCompositeOp = int(C.ClearCompositeOp) 54 | ColorBurnCompositeOp = int(C.ColorBurnCompositeOp) 55 | ColorDodgeCompositeOp = int(C.ColorDodgeCompositeOp) 56 | ColorizeCompositeOp = int(C.ColorizeCompositeOp) 57 | CopyBlackCompositeOp = int(C.CopyBlackCompositeOp) 58 | CopyBlueCompositeOp = int(C.CopyBlueCompositeOp) 59 | CopyCompositeOp = int(C.CopyCompositeOp) 60 | CopyCyanCompositeOp = int(C.CopyCyanCompositeOp) 61 | CopyGreenCompositeOp = int(C.CopyGreenCompositeOp) 62 | CopyMagentaCompositeOp = int(C.CopyMagentaCompositeOp) 63 | CopyOpacityCompositeOp = int(C.CopyOpacityCompositeOp) 64 | CopyRedCompositeOp = int(C.CopyRedCompositeOp) 65 | CopyYellowCompositeOp = int(C.CopyYellowCompositeOp) 66 | DarkenCompositeOp = int(C.DarkenCompositeOp) 67 | DstAtopCompositeOp = int(C.DstAtopCompositeOp) 68 | DstCompositeOp = int(C.DstCompositeOp) 69 | DstInCompositeOp = int(C.DstInCompositeOp) 70 | DstOutCompositeOp = int(C.DstOutCompositeOp) 71 | DstOverCompositeOp = int(C.DstOverCompositeOp) 72 | DifferenceCompositeOp = int(C.DifferenceCompositeOp) 73 | DisplaceCompositeOp = int(C.DisplaceCompositeOp) 74 | DissolveCompositeOp = int(C.DissolveCompositeOp) 75 | ExclusionCompositeOp = int(C.ExclusionCompositeOp) 76 | HardLightCompositeOp = int(C.HardLightCompositeOp) 77 | HueCompositeOp = int(C.HueCompositeOp) 78 | InCompositeOp = int(C.InCompositeOp) 79 | LightenCompositeOp = int(C.LightenCompositeOp) 80 | LinearLightCompositeOp = int(C.LinearLightCompositeOp) 81 | LuminizeCompositeOp = int(C.LuminizeCompositeOp) 82 | MinusDstCompositeOp = int(C.MinusDstCompositeOp) 83 | ModulateCompositeOp = int(C.ModulateCompositeOp) 84 | MultiplyCompositeOp = int(C.MultiplyCompositeOp) 85 | OutCompositeOp = int(C.OutCompositeOp) 86 | OverCompositeOp = int(C.OverCompositeOp) 87 | OverlayCompositeOp = int(C.OverlayCompositeOp) 88 | PlusCompositeOp = int(C.PlusCompositeOp) 89 | ReplaceCompositeOp = int(C.ReplaceCompositeOp) 90 | SaturateCompositeOp = int(C.SaturateCompositeOp) 91 | ScreenCompositeOp = int(C.ScreenCompositeOp) 92 | SoftLightCompositeOp = int(C.SoftLightCompositeOp) 93 | SrcAtopCompositeOp = int(C.SrcAtopCompositeOp) 94 | SrcCompositeOp = int(C.SrcCompositeOp) 95 | SrcInCompositeOp = int(C.SrcInCompositeOp) 96 | SrcOutCompositeOp = int(C.SrcOutCompositeOp) 97 | SrcOverCompositeOp = int(C.SrcOverCompositeOp) 98 | ModulusSubtractCompositeOp = int(C.ModulusSubtractCompositeOp) 99 | ThresholdCompositeOp = int(C.ThresholdCompositeOp) 100 | XorCompositeOp = int(C.XorCompositeOp) 101 | /* new add */ 102 | DivideDstCompositeOp = int(C.DivideDstCompositeOp) 103 | DistortCompositeOp = int(C.DistortCompositeOp) 104 | BlurCompositeOp = int(C.BlurCompositeOp) 105 | PegtopLightCompositeOp = int(C.PegtopLightCompositeOp) 106 | VividLightCompositeOp = int(C.VividLightCompositeOp) 107 | PinLightCompositeOp = int(C.PinLightCompositeOp) 108 | LinearDodgeCompositeOp = int(C.LinearDodgeCompositeOp) 109 | LinearBurnCompositeOp = int(C.LinearBurnCompositeOp) 110 | MathematicsCompositeOp = int(C.MathematicsCompositeOp) 111 | DivideSrcCompositeOp = int(C.DivideSrcCompositeOp) 112 | MinusSrcCompositeOp = int(C.MinusSrcCompositeOp) 113 | DarkenIntensityCompositeOp = int(C.DarkenIntensityCompositeOp) 114 | LightenIntensityCompositeOp = int(C.LightenIntensityCompositeOp) 115 | 116 | // exception 117 | UndefinedException = 0 118 | WarningException = 300 119 | ResourceLimitWarning = 300 120 | TypeWarning = 305 121 | OptionWarning = 310 122 | DelegateWarning = 315 123 | MissingDelegateWarning = 320 124 | CorruptImageWarning = 325 125 | FileOpenWarning = 330 126 | BlobWarning = 335 127 | StreamWarning = 340 128 | CacheWarning = 345 129 | CoderWarning = 350 130 | FilterWarning = 352 131 | ModuleWarning = 355 132 | DrawWarning = 360 133 | ImageWarning = 365 134 | WandWarning = 370 135 | RandomWarning = 375 136 | XServerWarning = 380 137 | MonitorWarning = 385 138 | RegistryWarning = 390 139 | ConfigureWarning = 395 140 | PolicyWarning = 399 141 | ErrorException = 400 142 | ResourceLimitError = 400 143 | TypeError = 405 144 | OptionError = 410 145 | DelegateError = 415 146 | MissingDelegateError = 420 147 | CorruptImageError = 425 148 | FileOpenError = 430 149 | BlobError = 435 150 | StreamError = 440 151 | CacheError = 445 152 | CoderError = 450 153 | FilterError = 452 154 | ModuleError = 455 155 | DrawError = 460 156 | ImageError = 465 157 | WandError = 470 158 | RandomError = 475 159 | XServerError = 480 160 | MonitorError = 485 161 | RegistryError = 490 162 | ConfigureError = 495 163 | PolicyError = 499 164 | FatalErrorException = 700 165 | ResourceLimitFatalError = 700 166 | TypeFatalError = 705 167 | OptionFatalError = 710 168 | DelegateFatalError = 715 169 | MissingDelegateFatalError = 720 170 | CorruptImageFatalError = 725 171 | FileOpenFatalError = 730 172 | BlobFatalError = 735 173 | StreamFatalError = 740 174 | CacheFatalError = 745 175 | CoderFatalError = 750 176 | FilterFatalError = 752 177 | ModuleFatalError = 755 178 | DrawFatalError = 760 179 | ImageFatalError = 765 180 | WandFatalError = 770 181 | RandomFatalError = 775 182 | XServerFatalError = 780 183 | MonitorFatalError = 785 184 | RegistryFatalError = 790 185 | ConfigureFatalError = 795 186 | PolicyFatalError = 79 187 | ) 188 | -------------------------------------------------------------------------------- /wand/magick_image.go: -------------------------------------------------------------------------------- 1 | package wand 2 | 3 | /* 4 | #cgo pkg-config: MagickWand 5 | 6 | #include 7 | */ 8 | import "C" 9 | 10 | import ( 11 | "fmt" 12 | "unsafe" 13 | ) 14 | 15 | /* Composite one image onto another at the specified offset. */ 16 | func (w *MagickWand) CompositeImage(srcWand *MagickWand, compose, x, y int) error { 17 | if C.MagickCompositeImage(w.wand, srcWand.wand, C.CompositeOperator(compose), C.ssize_t(x), C.ssize_t(y)) == C.MagickFalse { 18 | eStr, eCode := w.Exception() 19 | return fmt.Errorf("CompositeImage() failed : [%d] %s", eCode, eStr) 20 | } 21 | 22 | return nil 23 | } 24 | 25 | /* Reads an image or image sequence from a blob. */ 26 | func (w *MagickWand) ReadImageBlob(blob []byte, length uint) error { 27 | if C.MagickReadImageBlob(w.wand, unsafe.Pointer(&blob[0]), C.size_t(length)) == C.MagickFalse { 28 | eStr, eCode := w.Exception() 29 | return fmt.Errorf("ReadImageBlob() failed : [%d] %s", eCode, eStr) 30 | } 31 | 32 | return nil 33 | } 34 | 35 | /* Implements direct to memory image formats. */ 36 | func (w *MagickWand) ImageBlob(length *uint) []byte { 37 | blobPtr := unsafe.Pointer(C.MagickGetImageBlob(w.wand, (*C.size_t)(unsafe.Pointer(length)))) 38 | blob := C.GoBytes(blobPtr, C.int(int(*length))) 39 | // need free blob memory 40 | C.MagickRelinquishMemory(blobPtr) 41 | return blob 42 | } 43 | 44 | /* Reads an image or image sequence. */ 45 | func (w *MagickWand) ReadImage(fileName string) error { 46 | if C.MagickReadImage(w.wand, C.CString(fileName)) == C.MagickFalse { 47 | eStr, eCode := w.Exception() 48 | return fmt.Errorf("ReadImage() failed : [%d] %s", eCode, eStr) 49 | } 50 | 51 | return nil 52 | } 53 | 54 | /* Writes an image to the specified filename. */ 55 | func (w *MagickWand) WriteImage(fileName string) error { 56 | if C.MagickWriteImage(w.wand, C.CString(fileName)) == C.MagickFalse { 57 | eStr, eCode := w.Exception() 58 | return fmt.Errorf("WriteImage() failed : [%d] %s", eCode, eStr) 59 | } 60 | 61 | return nil 62 | } 63 | 64 | /* Scales an image to the desired dimensions. */ 65 | func (w *MagickWand) ResizeImage(columns, rows uint, filter int, blur float64) error { 66 | if C.MagickResizeImage(w.wand, C.size_t(columns), C.size_t(rows), C.FilterTypes(filter), C.double(blur)) == C.MagickFalse { 67 | eStr, eCode := w.Exception() 68 | return fmt.Errorf("Resize() failed : [%d] %s", eCode, eStr) 69 | } 70 | 71 | return nil 72 | } 73 | 74 | /* Sets the image compression quality. */ 75 | func (w *MagickWand) SetImageCompressionQuality(quality uint) error { 76 | if C.MagickSetImageCompressionQuality(w.wand, C.size_t(quality)) == C.MagickFalse { 77 | eStr, eCode := w.Exception() 78 | return fmt.Errorf("SetImageCompressionQuality() failed : [%d] %s", eCode, eStr) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | /* Gets the image compression quality. */ 85 | func (w *MagickWand) ImageCompressionQuality() uint { 86 | return uint(C.MagickGetImageCompressionQuality(w.wand)) 87 | } 88 | 89 | /* Sets the image compression. */ 90 | func (w *MagickWand) SetImageCompression(compression int) error { 91 | if C.MagickSetImageCompression(w.wand, C.CompressionType(compression)) == C.MagickFalse { 92 | eStr, eCode := w.Exception() 93 | return fmt.Errorf("SetImageCompression() failed : [%d] %s", eCode, eStr) 94 | } 95 | 96 | return nil 97 | } 98 | 99 | /* Gets the image compression. */ 100 | func (w *MagickWand) ImageCompression() int { 101 | return int(C.MagickGetImageCompression(w.wand)) 102 | } 103 | 104 | /* Sets the format of a particular image in a sequence */ 105 | func (w *MagickWand) SetImageFormat(format string) error { 106 | if C.MagickSetImageFormat(w.wand, C.CString(format)) == C.MagickFalse { 107 | eStr, eCode := w.Exception() 108 | return fmt.Errorf("SetImageFormat() failed : [%d] %s", eCode, eStr) 109 | } 110 | 111 | return nil 112 | } 113 | 114 | /* Gets the format of a particular image in a sequence. */ 115 | func (w *MagickWand) ImageFormat() string { 116 | return C.GoString(C.MagickGetImageFormat(w.wand)) 117 | } 118 | 119 | /* Gets the image height. */ 120 | func (w *MagickWand) ImageHeight() uint { 121 | return uint(C.MagickGetImageHeight(w.wand)) 122 | } 123 | 124 | /* Gets the image width. */ 125 | func (w *MagickWand) ImageWidth() uint { 126 | return uint(C.MagickGetImageWidth(w.wand)) 127 | } 128 | 129 | /* Extracts a region of the image. */ 130 | func (w *MagickWand) CropImage(width, height uint, x, y int) error { 131 | if C.MagickCropImage(w.wand, C.size_t(width), C.size_t(height), C.ssize_t(x), C.ssize_t(y)) == C.MagickFalse { 132 | eStr, eCode := w.Exception() 133 | return fmt.Errorf("CropImage() failed : [%d] %s", eCode, eStr) 134 | } 135 | 136 | return nil 137 | } 138 | 139 | /* Adaptively resize image with data dependent triangulation. */ 140 | func (w *MagickWand) AdaptiveResizeImage(columns, rows uint) error { 141 | if C.MagickAdaptiveResizeImage(w.wand, C.size_t(columns), C.size_t(rows)) == C.MagickFalse { 142 | eStr, eCode := w.Exception() 143 | return fmt.Errorf("AdaptiveResizeImage() failed : [%d] %s", eCode, eStr) 144 | } 145 | 146 | return nil 147 | } 148 | 149 | // Sets the image background color. 150 | func (w *MagickWand) SetImageBackgroundColor(bg *PixelWand) error { 151 | if C.MagickSetImageBackgroundColor(w.wand, bg.wand) == C.MagickFalse { 152 | eStr, eCode := w.Exception() 153 | return fmt.Errorf("SetImageBackgroundColor() failed : [%d] %s", eCode, eStr) 154 | } 155 | 156 | return nil 157 | } 158 | 159 | // Returns the image background color. 160 | func (w *MagickWand) ImageBackgroundColor(bg *PixelWand) error { 161 | if C.MagickGetImageBackgroundColor(w.wand, bg.wand) == C.MagickFalse { 162 | eStr, eCode := w.Exception() 163 | return fmt.Errorf("GetImageBackgroundColor() failed : [%d] %s", eCode, eStr) 164 | } 165 | 166 | return nil 167 | } 168 | 169 | // Adds a blank image canvas of the specified size and background color to the 170 | // wand. 171 | func (w *MagickWand) NewImage(cols, rows uint, bg *PixelWand) error { 172 | if C.MagickNewImage(w.wand, C.size_t(cols), C.size_t(rows), bg.wand) == C.MagickFalse { 173 | eStr, eCode := w.Exception() 174 | return fmt.Errorf("NewImage() failed : [%d] %s", eCode, eStr) 175 | } 176 | 177 | return nil 178 | } 179 | -------------------------------------------------------------------------------- /wand/magick_wand.go: -------------------------------------------------------------------------------- 1 | // Package wand providers Imagemagick functions. 2 | package wand 3 | 4 | /* 5 | #cgo pkg-config: MagickWand 6 | 7 | #include 8 | */ 9 | import "C" 10 | 11 | import ( 12 | "unsafe" 13 | ) 14 | 15 | /* MagickWand wrap the struct of Imagemagick's MagickWand */ 16 | type MagickWand struct { 17 | wand *C.MagickWand 18 | } 19 | 20 | /* Initializes the MagickWand environment. */ 21 | func Genesis() { 22 | C.MagickWandGenesis() 23 | } 24 | 25 | /* Terminates the MagickWand environment. */ 26 | func Terminus() { 27 | C.MagickWandTerminus() 28 | } 29 | 30 | /* 31 | Create a wand required for all other methods in the API. A panic() is 32 | thrown if there is not enough memory to allocate the wand. Use Destroy() to 33 | dispose of the wand when it is no longer needed. 34 | */ 35 | func NewMagickWand() *MagickWand { 36 | return &MagickWand{wand: C.NewMagickWand()} 37 | } 38 | 39 | /* 40 | clears resources associated with the wand, leaving the wand blank, and 41 | ready to be used for a new set of images. 42 | */ 43 | func (w *MagickWand) Clear() { 44 | C.ClearMagickWand(w.wand) 45 | } 46 | 47 | /* Deallocates memory associated with an MagickWand. */ 48 | func (w *MagickWand) Destroy() { 49 | C.DestroyMagickWand(w.wand) 50 | } 51 | 52 | /* Returns the severity, reason, and description of any error that occurs when 53 | using other methods in this API. 54 | */ 55 | func (w *MagickWand) Exception() (string, int) { 56 | var severity C.ExceptionType 57 | errPtr := C.MagickGetException(w.wand, &severity) 58 | C.MagickClearException(w.wand) 59 | err := C.GoString(errPtr) 60 | C.MagickRelinquishMemory(unsafe.Pointer(errPtr)) 61 | return err, int(severity) 62 | } 63 | 64 | // Resets the wand iterator 65 | func (w *MagickWand) ResetIterator() { 66 | C.MagickResetIterator(w.wand) 67 | } 68 | -------------------------------------------------------------------------------- /wand/magick_wand_test.go: -------------------------------------------------------------------------------- 1 | package wand 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "os" 7 | "testing" 8 | ) 9 | 10 | func TestNewMagickWand(t *testing.T) { 11 | Genesis() 12 | wand := NewMagickWand() 13 | wand.Destroy() 14 | Terminus() 15 | } 16 | 17 | func TestMagickWandClear(t *testing.T) { 18 | Genesis() 19 | wand := NewMagickWand() 20 | wand.Clear() 21 | wand.Destroy() 22 | Terminus() 23 | } 24 | 25 | func TestReadImageBlob(t *testing.T) { 26 | Genesis() 27 | defer Terminus() 28 | wand := NewMagickWand() 29 | defer wand.Destroy() 30 | 31 | file, err := os.Open("../examples/input/test.png") 32 | if err != nil { 33 | t.Error(err) 34 | } 35 | 36 | defer file.Close() 37 | 38 | buf := &bytes.Buffer{} 39 | num, err := io.Copy(buf, file) 40 | if err != nil { 41 | t.Error(err) 42 | } 43 | 44 | if err = wand.ReadImageBlob(buf.Bytes(), uint(num)); err != nil { 45 | t.Error(err) 46 | } 47 | } 48 | 49 | func TestReadImage(t *testing.T) { 50 | Genesis() 51 | defer Terminus() 52 | wand := NewMagickWand() 53 | defer wand.Destroy() 54 | 55 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 56 | t.Error(err) 57 | } 58 | } 59 | 60 | func TestWriteImage(t *testing.T) { 61 | Genesis() 62 | defer Terminus() 63 | wand := NewMagickWand() 64 | defer wand.Destroy() 65 | 66 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 67 | t.Error(err) 68 | } 69 | 70 | if err := wand.WriteImage("../examples/output/test.png"); err != nil { 71 | t.Error(err) 72 | } 73 | } 74 | 75 | func TestImageBlob(t *testing.T) { 76 | var length uint 77 | Genesis() 78 | defer Terminus() 79 | wand := NewMagickWand() 80 | defer wand.Destroy() 81 | 82 | file, err := os.Open("../examples/input/test.png") 83 | if err != nil { 84 | t.Error(err) 85 | } 86 | 87 | defer file.Close() 88 | 89 | buf := &bytes.Buffer{} 90 | num, err := io.Copy(buf, file) 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | 95 | if err = wand.ReadImageBlob(buf.Bytes(), uint(num)); err != nil { 96 | t.Error(err) 97 | } 98 | 99 | blob := wand.ImageBlob(&length) 100 | file1, err := os.OpenFile("../examples/output/test-blob.png", os.O_CREATE|os.O_WRONLY, 0644) 101 | if err != nil { 102 | t.Error(err) 103 | } 104 | defer file1.Close() 105 | 106 | file1.Write(blob) 107 | } 108 | 109 | func TestCropImage(t *testing.T) { 110 | Genesis() 111 | defer Terminus() 112 | wand := NewMagickWand() 113 | defer wand.Destroy() 114 | 115 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 116 | t.Error(err) 117 | } 118 | 119 | if err := wand.CropImage(100, 100, 100, 0); err != nil { 120 | t.Error(err) 121 | } 122 | 123 | if err := wand.WriteImage("../examples/output/test-crop.png"); err != nil { 124 | t.Error(err) 125 | } 126 | } 127 | 128 | func TestAdaptiveResizeImage(t *testing.T) { 129 | Genesis() 130 | defer Terminus() 131 | wand := NewMagickWand() 132 | defer wand.Destroy() 133 | 134 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 135 | t.Error(err) 136 | } 137 | 138 | if err := wand.AdaptiveResizeImage(800, 600); err != nil { 139 | t.Error(err) 140 | } 141 | 142 | if err := wand.WriteImage("../examples/output/test-adaptiveresize.png"); err != nil { 143 | t.Error(err) 144 | } 145 | } 146 | 147 | func TestResizeImage(t *testing.T) { 148 | Genesis() 149 | defer Terminus() 150 | wand := NewMagickWand() 151 | defer wand.Destroy() 152 | 153 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 154 | t.Error(err) 155 | } 156 | 157 | if err := wand.ResizeImage(192, 108, GaussianFilter, 1.0); err != nil { 158 | t.Error(err) 159 | } 160 | 161 | if err := wand.WriteImage("../examples/output/test-resize.png"); err != nil { 162 | t.Error(err) 163 | } 164 | } 165 | 166 | func TestImageHeight(t *testing.T) { 167 | Genesis() 168 | defer Terminus() 169 | wand := NewMagickWand() 170 | defer wand.Destroy() 171 | 172 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 173 | t.Error(err) 174 | } 175 | 176 | if wand.ImageHeight() != 1080 { 177 | t.Errorf("Height(%d) not equals 1080", wand.ImageHeight()) 178 | } 179 | } 180 | 181 | func TestImageWidth(t *testing.T) { 182 | Genesis() 183 | defer Terminus() 184 | wand := NewMagickWand() 185 | defer wand.Destroy() 186 | 187 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 188 | t.Error(err) 189 | } 190 | 191 | if wand.ImageWidth() != 1920 { 192 | t.Errorf("Width(%d) not equals 1080", wand.ImageWidth()) 193 | } 194 | } 195 | 196 | func TestSetImageCompressionQuality(t *testing.T) { 197 | Genesis() 198 | defer Terminus() 199 | wand := NewMagickWand() 200 | defer wand.Destroy() 201 | 202 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 203 | t.Error(err) 204 | } 205 | 206 | if err := wand.SetImageCompressionQuality(1); err != nil { 207 | t.Error(err) 208 | } 209 | 210 | if err := wand.WriteImage("../examples/output/test-quality.png"); err != 211 | nil { 212 | t.Error(err) 213 | } 214 | } 215 | 216 | func TestImageCompressionQuality(t *testing.T) { 217 | Genesis() 218 | defer Terminus() 219 | wand := NewMagickWand() 220 | defer wand.Destroy() 221 | 222 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 223 | t.Error(err) 224 | } 225 | 226 | if wand.ImageCompressionQuality() != 0 { 227 | t.Errorf("Quality(%d) not equals 100", wand.ImageCompressionQuality()) 228 | } 229 | 230 | if err := wand.WriteImage("../examples/output/test-quality.png"); err != nil { 231 | t.Error(err) 232 | } 233 | } 234 | 235 | func TestSetImageCompression(t *testing.T) { 236 | Genesis() 237 | defer Terminus() 238 | wand := NewMagickWand() 239 | defer wand.Destroy() 240 | 241 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 242 | t.Error(err) 243 | } 244 | 245 | if err := wand.SetImageCompression(JPEGCompression); err != nil { 246 | t.Error(err) 247 | } 248 | 249 | if err := wand.WriteImage("../examples/output/test-compression.jpg"); err != nil { 250 | t.Error(err) 251 | } 252 | } 253 | 254 | func TestImageCompression(t *testing.T) { 255 | Genesis() 256 | defer Terminus() 257 | wand := NewMagickWand() 258 | defer wand.Destroy() 259 | 260 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 261 | t.Error(err) 262 | } 263 | 264 | if wand.ImageCompression() != ZipCompression { 265 | t.Error("Compression(%d) not equanls ZipCompression", int(wand.ImageCompression())) 266 | } 267 | } 268 | 269 | func TestSetImageFormat(t *testing.T) { 270 | Genesis() 271 | defer Terminus() 272 | wand := NewMagickWand() 273 | defer wand.Destroy() 274 | 275 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 276 | t.Error(err) 277 | } 278 | 279 | if err := wand.SetImageFormat("JPEG"); err != nil { 280 | t.Error(err) 281 | } 282 | 283 | if err := wand.WriteImage("../examples/output/test-format.jpg"); err != nil { 284 | t.Error(err) 285 | } 286 | } 287 | 288 | func TestImageFormat(t *testing.T) { 289 | Genesis() 290 | defer Terminus() 291 | wand := NewMagickWand() 292 | defer wand.Destroy() 293 | 294 | if err := wand.ReadImage("../examples/input/test.png"); err != nil { 295 | t.Error(err) 296 | } 297 | 298 | if wand.ImageFormat() != "PNG" { 299 | t.Errorf("Format(%s) not equanls PNG", wand.ImageFormat()) 300 | } 301 | } 302 | 303 | func TestNewImage(t *testing.T) { 304 | Genesis() 305 | defer Terminus() 306 | wand := NewMagickWand() 307 | defer wand.Destroy() 308 | bg := NewPixelWand() 309 | defer bg.Destroy() 310 | if err := bg.SetColor("red"); err != nil { 311 | t.Error(err) 312 | } 313 | 314 | if err := wand.NewImage(300, 300, bg); err != nil { 315 | t.Error(err) 316 | } 317 | 318 | if err := wand.WriteImage("../examples/output/test-new-image.jpg"); err != nil { 319 | t.Error(err) 320 | } 321 | } 322 | 323 | func TestCompositeImage(t *testing.T) { 324 | Genesis() 325 | defer Terminus() 326 | wand := NewMagickWand() 327 | defer wand.Destroy() 328 | bg := NewPixelWand() 329 | defer bg.Destroy() 330 | if err := bg.SetColor("red"); err != nil { 331 | t.Error(err) 332 | } 333 | 334 | if err := wand.NewImage(300, 300, bg); err != nil { 335 | t.Error(err) 336 | } 337 | 338 | wand1 := NewMagickWand() 339 | defer wand1.Destroy() 340 | if err := wand1.ReadImage("../examples/input/test.png"); err != nil { 341 | t.Error(err) 342 | } 343 | 344 | if err := wand1.CompositeImage(wand, AtopCompositeOp, 0, 0); err != nil { 345 | t.Error(err) 346 | } 347 | 348 | if err := wand1.WriteImage("../examples/output/test-composite-image.jpg"); err != nil { 349 | t.Error(err) 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /wand/pixel_wand.go: -------------------------------------------------------------------------------- 1 | package wand 2 | 3 | /* 4 | #cgo pkg-config: MagickWand 5 | 6 | #include 7 | */ 8 | import "C" 9 | 10 | import ( 11 | "fmt" 12 | "unsafe" 13 | ) 14 | 15 | // PixelWand wrap the struct of Imagemagick's PixelWand 16 | type PixelWand struct { 17 | wand *C.PixelWand 18 | } 19 | 20 | // Returns a new pixel wand. 21 | func NewPixelWand() *PixelWand { 22 | return &PixelWand{wand: C.NewPixelWand()} 23 | } 24 | 25 | // Clears resources associated with the wand. 26 | func (p *PixelWand) Clear() { 27 | C.ClearPixelWand(p.wand) 28 | } 29 | 30 | // Deallocates resources associated with a PixelWand. 31 | func (p *PixelWand) Destroy() { 32 | C.DestroyPixelWand(p.wand) 33 | } 34 | 35 | // Returns the severity, reason, and description of any error that occurs 36 | // when using other methods in this API. 37 | func (p *PixelWand) Exception() (string, int) { 38 | var severity C.ExceptionType 39 | errPtr := C.PixelGetException(p.wand, &severity) 40 | C.PixelClearException(p.wand) 41 | err := C.GoString(errPtr) 42 | C.MagickRelinquishMemory(unsafe.Pointer(errPtr)) 43 | return err, int(severity) 44 | } 45 | 46 | // Sets the color of the pixel wand with a string (e.g. "blue", "#0000ff", 47 | // "rgb(0,0,255)", "cmyk(100,100,100,10)", etc.). 48 | func (p *PixelWand) SetColor(color string) error { 49 | if C.PixelSetColor(p.wand, C.CString(color)) == C.MagickFalse { 50 | eStr, eCode := p.Exception() 51 | return fmt.Errorf("SetColor() failed : [%d] %s", eStr, eCode) 52 | } 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /wand/pixel_wand_test.go: -------------------------------------------------------------------------------- 1 | 2 | package wand 3 | 4 | import ( 5 | "testing" 6 | ) 7 | 8 | func TestNewPixelWand(t *testing.T) { 9 | Genesis() 10 | wand := NewPixelWand() 11 | wand.Destroy() 12 | Terminus() 13 | } 14 | 15 | func TestPixelClear(t *testing.T) { 16 | Genesis() 17 | wand := NewPixelWand() 18 | wand.Clear() 19 | wand.Destroy() 20 | Terminus() 21 | } 22 | 23 | func TestSetColor(t *testing.T) { 24 | Genesis() 25 | defer Terminus() 26 | wand := NewPixelWand() 27 | defer wand.Destroy() 28 | if err := wand.SetColor("white"); err != nil { 29 | t.Error(err) 30 | } 31 | 32 | wand.Clear() 33 | } 34 | --------------------------------------------------------------------------------