Skip to content

Commit

Permalink
Expose ConvertImageCommand function in imagick.v2 (refs #220)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinfx committed Jan 4, 2020
1 parent 724f542 commit 4f53a66
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 1 deletion.
21 changes: 21 additions & 0 deletions examples/convert_command/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"

"gopkg.in/gographics/imagick.v3/imagick"
)

func main() {
imagick.Initialize()
defer imagick.Terminate()

ret, err := imagick.ConvertImageCommand([]string{
"convert", "logo:", "-resize", "100x100", "/tmp/out.png",
})
if err != nil {
panic(err)
}

fmt.Printf("Metadata:\n%s\n", ret.Meta)
}
1 change: 1 addition & 0 deletions examples/docker/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ module resizer

require gopkg.in/gographics/imagick.v2 v2.5.0

go 1.13
1 change: 1 addition & 0 deletions examples/docker/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gopkg.in/gographics/imagick.v2 v2.5.0/go.mod h1:of4TbGX8yMcpgWkWFjha7FsOFr+NjOJ5O1qtKU27Yj0=
78 changes: 78 additions & 0 deletions imagick/exception_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2013 Herbert G. Fischer. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package imagick

/*
#include <wand/MagickWand.h>
*/
import "C"

import "fmt"

// ExceptionInfo is an error type returned by certain
// New* API calls.
type ExceptionInfo struct {
kind ExceptionType
errno int
reason string
description string
}

// Create a new ExceptionInfo wrapper around a C ExceptionInfo ptr
func newExceptionInfo(errInfo *C.ExceptionInfo) *ExceptionInfo {
if errInfo == nil {
return nil
}

return &ExceptionInfo{
kind: ExceptionType(errInfo.severity),
errno: int(errInfo.error_number),
reason: C.GoString(errInfo.reason),
description: C.GoString(errInfo.description),
}
}

// Check if a given C ExceptionInfo ptr is an error.
// Returns a valid ExceptionInfo if there was an error,
// otherwise returns nil
func checkExceptionInfo(errInfo *C.ExceptionInfo) *ExceptionInfo {
if errInfo != nil && errInfo.error_number != 0 {
return newExceptionInfo(errInfo)
}

return nil
}

func (e *ExceptionInfo) Error() string {
if e == nil {
return ""
}
return fmt.Sprintf("%s (%d): %s %s",
e.kind.String(), e.Errno(), e.Reason(), e.Description())
}

// Errno returns the ExceptionInfo error number (non-zero if error)
func (e *ExceptionInfo) Errno() int {
if e == nil {
return 0
}
return e.errno
}

// Reason returns the string reason for the Exception
func (e *ExceptionInfo) Reason() string {
if e == nil {
return ""
}
return e.reason
}

// Description returns the string description for the Exception
func (e *ExceptionInfo) Description() string {
if e == nil {
return ""
}
return e.description
}
64 changes: 63 additions & 1 deletion imagick/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ package imagick

/*
#include <magick/MagickCore.h>
#include <wand/MagickWand.h>
*/
import "C"
import "runtime"
import (
"runtime"
"unsafe"
)

type Image struct {
img *C.Image
Expand All @@ -20,3 +24,61 @@ func NewMagickImage(info *ImageInfo, width, height uint, background *MagickPixel
runtime.KeepAlive(background)
return ret
}

// ImageCommandResult is returned by a call to
// ConvertImageCommand. It contains the ImageInfo
// and Metadata string generated by the convert command.
type ImageCommandResult struct {
Info *ImageInfo
Meta string
}

/*
ConvertImageCommand reads one or more images, applies one or more image
processing operations, and writes out the image in the same or differing
format.
The first item in the args list is expected to be the program name, ie "convert".
*/
func ConvertImageCommand(args []string) (*ImageCommandResult, error) {
size := len(args)

cmdArr := make([]*C.char, size)
for i, s := range args {
cmdArr[i] = C.CString(s)
}

empty := C.CString("")
metaStr := C.AcquireString(empty)
C.free(unsafe.Pointer(empty))

defer func() {
for i := range cmdArr {
C.free(unsafe.Pointer(cmdArr[i]))
}

C.DestroyString(metaStr)
}()

imageInfo := newImageInfo()

var exc *C.ExceptionInfo = C.AcquireExceptionInfo()
defer C.DestroyExceptionInfo(exc)

ok := C.ConvertImageCommand(
imageInfo.info,
C.int(size), // argc
&cmdArr[0], // argv
&metaStr, // metadata
exc, // exception
)
if C.int(ok) == 0 {
imageInfo.Destroy()
return nil, newExceptionInfo(exc)
}

ret := &ImageCommandResult{
Info: imageInfo,
Meta: C.GoString(metaStr),
}
return ret, nil
}
18 changes: 18 additions & 0 deletions imagick/image_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,25 @@ package imagick
#include <magick/MagickCore.h>
*/
import "C"
import "runtime"

type ImageInfo struct {
info *C.ImageInfo
}

func newImageInfo() *ImageInfo {
ptr := C.AcquireImageInfo()
C.GetImageInfo(ptr)
imageInfo := &ImageInfo{ptr}
runtime.SetFinalizer(imageInfo, Destroy)
return imageInfo
}

// Destroy the ImageInfo immediately.
// This will also be called automatically during garbage collection.
func (ii *ImageInfo) Destroy() {
if ii.info != nil {
C.DestroyImageInfo(ii.info)
ii.info = nil
}
}
62 changes: 62 additions & 0 deletions imagick/image_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2013 Herbert G. Fischer. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package imagick

import (
"fmt"
"io/ioutil"
"os"
"testing"
)

func init() {
Initialize()
}

// TODO(justinfx): Need to expand test to try and
// produce and catch an error from NewPixelWand
func TestNewMagickImage(t *testing.T) {
info := newImageInfo()
defer info.Destroy()

pp, err := QueryMagickColor("blue")
if err != nil {
t.Fatal(err.Error())
}

img := NewMagickImage(info, 100, 100, pp)
if img == nil {
t.Fatal("nil MagickImage")
}
}

func ExampleConvertImageCommand() {
ret, err := ConvertImageCommand([]string{
"convert", "logo:", "-resize", "100x100", "/tmp/out.png",
})
if err != nil {
panic(err)
}
fmt.Println("Meta:", ret.Meta)
}

func TestConvertImageCommand(t *testing.T) {
tmp, err := ioutil.TempFile("", "imagick_test")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmp.Name())

ret, err := ConvertImageCommand([]string{
"convert", "logo:", "-resize", "100x100", tmp.Name(),
})
if err != nil {
t.Fatalf("command failed: %v", err)
}

if ret.Meta == "" {
t.Fatal("got empty metadata string from command result")
}
}
66 changes: 66 additions & 0 deletions imagick/magick_pixel_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,77 @@ package imagick
#include <magick/MagickCore.h>
*/
import "C"
import "unsafe"

// MagickPixelPacket is constructed using NewMagickPixelPacket
type MagickPixelPacket struct {
mpp *C.MagickPixelPacket
}

func (p *MagickPixelPacket) Red() float64 {
return float64(p.mpp.red)
}

func (p *MagickPixelPacket) SetRed(value float64) {
p.mpp.red = C.MagickRealType(value)
}

func (p *MagickPixelPacket) Green() float64 {
return float64(p.mpp.green)
}

func (p *MagickPixelPacket) SetGreen(value float64) {
p.mpp.green = C.MagickRealType(value)
}

func (p *MagickPixelPacket) Blue() float64 {
return float64(p.mpp.blue)
}

func (p *MagickPixelPacket) SetBlue(value float64) {
p.mpp.blue = C.MagickRealType(value)
}

func (p *MagickPixelPacket) Opacity() float64 {
return float64(p.mpp.opacity)
}

func (p *MagickPixelPacket) SetOpacity(value float64) {
p.mpp.opacity = C.MagickRealType(value)
}

func (p *MagickPixelPacket) Index() float64 {
return float64(p.mpp.index)
}

func (p *MagickPixelPacket) SetIndex(value float64) {
p.mpp.index = C.MagickRealType(value)
}

func NewMagickPixelPacket() *MagickPixelPacket {
var mpp C.MagickPixelPacket
return &MagickPixelPacket{mpp: &mpp}
}

func newMagickPixelPacketFromCAPI(mpp *C.MagickPixelPacket) *MagickPixelPacket {
return &MagickPixelPacket{mpp}
}

// QueryMagickColor returns the red, green, blue, and opacity
// intensities for a given color name.
func QueryMagickColor(colorName string) (*MagickPixelPacket, error) {
cstr := C.CString(colorName)
defer C.free(unsafe.Pointer(cstr))

var mpp C.MagickPixelPacket

var exc *C.ExceptionInfo = C.AcquireExceptionInfo()
defer C.DestroyExceptionInfo(exc)

ok := C.QueryMagickColor(cstr, &mpp, exc)
if C.int(ok) == 0 {
return nil, newExceptionInfo(exc)
}

return newMagickPixelPacketFromCAPI(&mpp), nil
}

0 comments on commit 4f53a66

Please sign in to comment.