--- /dev/null
+\r
+#include <cstring>\r
+\r
+#include <v8.h>\r
+\r
+#include <node.h>\r
+#include <node_buffer.h>\r
+\r
+#include <magick/api.h>\r
+\r
+#define THROW_ERROR(TYPE, STR) \\r
+ return ThrowException(Exception::TYPE(String::New(STR)));\r
+\r
+#define REQ_ARGS(N) \\r
+ if (args.Length() < (N)) \\r
+ return ThrowException(Exception::TypeError( \\r
+ String::New("Expected " #N "arguments"))); \r
+\r
+#define REQ_STR_ARG(I, VAR) \\r
+ if (args.Length() <= (I) || !args[I]->IsString()) \\r
+ return ThrowException(Exception::TypeError( \\r
+ String::New("Argument " #I " must be a string"))); \\r
+ String::Utf8Value VAR(args[I]->ToString());\r
+\r
+#define REQ_INT_ARG(I, VAR) \\r
+ int VAR; \\r
+ if (args.Length() <= (I) || !args[I]->IsInt32()) \\r
+ return ThrowException(Exception::TypeError( \\r
+ String::New("Argument " #I " must be an integer"))); \\r
+ VAR = args[I]->Int32Value();\r
+\r
+\r
+#define REQ_IMG_ARG(I, VAR) \\r
+if (args.Length() <= (I) || !args[I]->IsObject()) \\r
+ return ThrowException(Exception::TypeError( \\r
+ String::New("Argument " #I " must be an object"))); \\r
+ Local<Object> _obj_ = Local<Object>::Cast(args[I]); \\r
+ MagickImage *VAR = ObjectWrap::Unwrap<MagickImage>(_obj_);\r
+ \r
+#define OPT_INT_ARG(I, VAR, DEFAULT) \\r
+ int VAR; \\r
+ if (args.Length() <= (I)) { \\r
+ VAR = (DEFAULT); \\r
+ } else if (args[I]->IsInt32()) { \\r
+ VAR = args[I]->Int32Value(); \\r
+ } else { \\r
+ return ThrowException(Exception::TypeError( \\r
+ String::New("Argument " #I " must be an integer"))); \\r
+ }\r
+\r
+ \r
+#define REQ_RECT_ARG(I, VAR) \\r
+ REQ_INT_ARG(I+0, x) \\r
+ REQ_INT_ARG(I+1, y) \\r
+ REQ_INT_ARG(I+2, width) \\r
+ REQ_INT_ARG(I+3, height) \\r
+ RectangleInfo VAR = { width, height, x, y };\r
+\r
+#define REQ_DOUBLE_ARG(I, VAR) \\r
+ double VAR; \\r
+ if (args.Length() <= (I) || !args[I]->IsNumber()) \\r
+ return ThrowException(Exception::TypeError( \\r
+ String::New("Argument " #I " must be a number"))); \\r
+ VAR = args[I]->NumberValue();\r
+\r
+#define REQ_EXT_ARG(I, VAR) \\r
+ if (args.Length() <= (I) || !args[I]->IsExternal()) \\r
+ return ThrowException(Exception::TypeError( \\r
+ String::New("Argument " #I " invalid"))); \\r
+ Local<External> VAR = Local<External>::Cast(args[I]);\r
+\r
+\r
+#define OPT_INT_ARG(I, VAR, DEFAULT) \\r
+ int VAR; \\r
+ if (args.Length() <= (I)) { \\r
+ VAR = (DEFAULT); \\r
+ } else if (args[I]->IsInt32()) { \\r
+ VAR = args[I]->Int32Value(); \\r
+ } else { \\r
+ return ThrowException(Exception::TypeError( \\r
+ String::New("Argument " #I " must be an integer"))); \\r
+ }\r
+\r
+#define OPT_DOUBLE_ARG(I, VAR, DEFAULT) \\r
+ int VAR; \\r
+ if (args.Length() <= (I)) { \\r
+ VAR = (DEFAULT); \\r
+ } else if (args[I]->IsNumber()) { \\r
+ VAR = args[I]->NumberValue(); \\r
+ } else { \\r
+ return ThrowException(Exception::TypeError( \\r
+ String::New("Argument " #I " must be a number"))); \\r
+ }\r
+\r
+using namespace node;\r
+using namespace v8;\r
+\r
+/*\r
+template <typename T> Handle<T> returnNewPointer(T ptr) {\r
+ HandleScope scope;\r
+ Local<Value> arg = External::New(ptr);\r
+ Persistent<Object> obj(T::Constructor->GetFunction()->NewInstance(1, &arg));\r
+ return scope.Close(obj);\r
+}\r
+*/\r
+\r
+\r
+#define IMAGE_METHOD(apiname, apiargs...) \\r
+ HandleScope scope; \\r
+ Handle<Value> out; \\r
+ ExceptionInfo exception; \\r
+ Image *result; \\r
+ MagickImage *image = ObjectWrap::Unwrap<MagickImage>(args.This()); \\r
+ GetExceptionInfo(&exception); \\r
+ result = apiname( *image, ##apiargs, &exception ); \\r
+ if (result) { \\r
+ Local<Object> object = constructorTemplate->GetFunction()->NewInstance(); \\r
+ MagickImage *magickImage = ObjectWrap::Unwrap<MagickImage>(object); \\r
+ magickImage->image = result; \\r
+ out = scope.Close(object); \\r
+ } else { \\r
+ CatchException(&exception); \\r
+ out = ThrowException(String::New("Unable to load image!")); \\r
+ } \\r
+ DestroyExceptionInfo(&exception); \\r
+ return out;\r
+\r
+class MagickImage : ObjectWrap {\r
+public:\r
+ \r
+ static Persistent<FunctionTemplate> constructorTemplate;\r
+ \r
+ Image* image;\r
+ \r
+ \r
+ \r
+ MagickImage(Image* i) : ObjectWrap(), image(i) {\r
+ \r
+ }\r
+ \r
+ ~MagickImage() {\r
+ if (image != NULL)\r
+ DestroyImage(image);\r
+ }\r
+ \r
+ operator Image* () const {\r
+ return image;\r
+ }\r
+ \r
+ \r
+ \r
+ static void Init(Handle<Object> target) {\r
+ \r
+ \r
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);\r
+ \r
+ //Create a new persistent function template based around "create"; this\r
+ //template is used as the prototype for making new instances of the object\r
+ constructorTemplate = Persistent<FunctionTemplate>::New(t);\r
+ \r
+ //This object has one internal field (i.e. a field hidden from javascript);\r
+ //This field is used to store a pointer to the image class\r
+ constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);\r
+ \r
+ //Give the class a name\r
+ constructorTemplate->SetClassName(String::NewSymbol("Image"));\r
+ \r
+ //All the methods for this class\r
+ NODE_SET_PROTOTYPE_METHOD(t, "thumbnail", thumbnail);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "sample", sample);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "scale", scale);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "resize", resize);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "chop", chop);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "crop", crop);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "extent", extent);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "flip", flip);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "flop", flop);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "affineTransform", affineTransform);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "rotate", rotate);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "shear", shear);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "contrast", contrast);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "equalize", equalize);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "gamma", gamma);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "level", level);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "levelChannel", levelChannel);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "modulate", modulate);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "negate", negate);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "normalize", normalize);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "attribute", attribute);\r
+ NODE_SET_PROTOTYPE_METHOD(t, "composite", composite);\r
+ \r
+ //Some getters\r
+ t->PrototypeTemplate()->SetAccessor(String::NewSymbol("buffer"), getBuffer, NULL, Handle<Value>(), PROHIBITS_OVERWRITING, ReadOnly);\r
+ t->PrototypeTemplate()->SetAccessor(String::NewSymbol("width"), getWidth, NULL, Handle<Value>(), PROHIBITS_OVERWRITING, ReadOnly);\r
+ t->PrototypeTemplate()->SetAccessor(String::NewSymbol("height"), getHeight, NULL, Handle<Value>(), PROHIBITS_OVERWRITING, ReadOnly);\r
+ }\r
+ \r
+ static Handle<Value> New(const Arguments &args) {\r
+ HandleScope scope;\r
+ MagickImage* magickImage = new MagickImage(NULL);\r
+ magickImage->Wrap(args.This());\r
+ return args.This();\r
+ }\r
+ \r
+ \r
+ static Handle<Value> getBuffer (Local<String> property, const AccessorInfo& info)\r
+ {\r
+ HandleScope scope;\r
+ ExceptionInfo exception;\r
+ size_t length;\r
+ ImageInfo *imageInfo = CloneImageInfo(NULL);\r
+ MagickImage *image = ObjectWrap::Unwrap<MagickImage>(info.This());\r
+ GetExceptionInfo(&exception);\r
+ void* data = ImageToBlob(imageInfo, *image, &length, &exception);\r
+ if (data) {\r
+ //http://sambro.is-super-awesome.com/2011/03/03/creating-a-proper-buffer-in-a-node-c-addon/\r
+ Buffer *slowBuffer = Buffer::New(length);\r
+ memcpy(Buffer::Data(slowBuffer), data, length);\r
+ Local<Object> globalObj = Context::GetCurrent()->Global();\r
+ Local<Function> bufferConstructor = Local<Function>::Cast(globalObj->Get(String::New("Buffer")));\r
+ Handle<Value> constructorArgs[3] = { slowBuffer->handle_, Integer::New(length), Integer::New(0) };\r
+ Local<Object> buffer = bufferConstructor->NewInstance(3, constructorArgs);\r
+ return scope.Close(buffer);\r
+ } else {\r
+ return ThrowException(String::New("Unable to convert image to blob!"));\r
+ }\r
+ \r
+ \r
+ }\r
+ \r
+ static Handle<Value> getWidth (Local<String> property, const AccessorInfo& info)\r
+ {\r
+ HandleScope scope;\r
+ MagickImage* image = ObjectWrap::Unwrap<MagickImage>(info.This());\r
+ Local<Number> result = Integer::New(image->image->columns);\r
+ return scope.Close(result);\r
+ }\r
+ \r
+ static Handle<Value> getHeight (Local<String> property, const AccessorInfo& info)\r
+ {\r
+ HandleScope scope;\r
+ MagickImage* image = ObjectWrap::Unwrap<MagickImage>(info.This());\r
+ Local<Number> result = Integer::New(image->image->rows);\r
+ return scope.Close(result);\r
+ }\r
+ \r
+ static Handle<Value> info(const Arguments &args) {\r
+ \r
+ }\r
+ \r
+ /**\r
+ * Create a new image from a buffer\r
+ */\r
+ static Handle<Value> create(const Arguments &args) {\r
+ \r
+ HandleScope scope;\r
+ \r
+ if (args.Length() < 1) { \r
+ return Undefined();\r
+ }\r
+ \r
+ Handle<Value> result;\r
+ ExceptionInfo exception;\r
+ const char* blob;\r
+ size_t length;\r
+ Image* image;\r
+ ImageInfo *imageInfo = CloneImageInfo(NULL);\r
+ \r
+ GetExceptionInfo(&exception);\r
+ \r
+ if (args[0]->IsString()) {\r
+ String::AsciiValue string(args[0]->ToString());\r
+ length = string.length();\r
+ blob = *string;\r
+ } else if (Buffer::HasInstance(args[0])) {\r
+ Local<Object> bufferIn=args[0]->ToObject();\r
+ length = Buffer::Length(bufferIn);\r
+ blob = Buffer::Data(bufferIn);\r
+ }\r
+ \r
+ image = BlobToImage(imageInfo, blob, length, &exception);\r
+ if (!image) {\r
+ CatchException(&exception);\r
+ result = ThrowException(String::New("Unable to load image!"));\r
+ }\r
+ else {\r
+ Local<Object> object = constructorTemplate->GetFunction()->NewInstance();\r
+ MagickImage *magickImage = ObjectWrap::Unwrap<MagickImage>(object);\r
+ magickImage->image = image;\r
+ result = scope.Close(object);\r
+ }\r
+ DestroyImageInfo(imageInfo);\r
+ DestroyExceptionInfo(&exception);\r
+ return result;\r
+ }\r
+ \r
+ static Handle<Value> thumbnail(const Arguments &args) {\r
+ REQ_INT_ARG(0, width)\r
+ REQ_INT_ARG(1, height)\r
+ IMAGE_METHOD(ThumbnailImage, width, height)\r
+ }\r
+ \r
+ static Handle<Value> sample(const Arguments &args) {\r
+ REQ_INT_ARG(0, width)\r
+ REQ_INT_ARG(1, height)\r
+ IMAGE_METHOD(SampleImage, width, height)\r
+ }\r
+ \r
+ static Handle<Value> scale(const Arguments &args) {\r
+ REQ_INT_ARG(0, width)\r
+ REQ_INT_ARG(1, height)\r
+ IMAGE_METHOD(ScaleImage, width, height)\r
+ }\r
+ \r
+ static Handle<Value> resize(const Arguments &args) {\r
+ REQ_INT_ARG(0, width)\r
+ REQ_INT_ARG(1, height)\r
+ OPT_INT_ARG(2, f, LanczosFilter)\r
+ OPT_DOUBLE_ARG(3, blur, 1.0)\r
+ FilterTypes filter = FilterTypes(f);\r
+ IMAGE_METHOD(ResizeImage, width, height, filter, blur)\r
+ \r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/transform.html#chopimage\r
+ static Handle<Value> chop(const Arguments &args) {\r
+ REQ_RECT_ARG(0, chopInfo)\r
+ IMAGE_METHOD(ChopImage, &chopInfo)\r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/transform.html#cropimage\r
+ static Handle<Value> crop(const Arguments &args) {\r
+ REQ_RECT_ARG(0, cropInfo)\r
+ IMAGE_METHOD(CropImage, &cropInfo)\r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/transform.html#extentimage\r
+ static Handle<Value> extent(const Arguments &args) {\r
+ REQ_RECT_ARG(0, geometry)\r
+ IMAGE_METHOD(ExtentImage, &geometry)\r
+ }\r
+ \r
+ static Handle<Value> flip(const Arguments &args) {\r
+ IMAGE_METHOD(FlipImage)\r
+ }\r
+ \r
+ static Handle<Value> flop(const Arguments &args) {\r
+ IMAGE_METHOD(FlopImage)\r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/shear.html#affinetransformimage\r
+ static Handle<Value> affineTransform(const Arguments &args) {\r
+ AffineMatrix affineMatrix;\r
+ IMAGE_METHOD(AffineTransformImage, &affineMatrix)\r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/shear.html#rotateimage\r
+ static Handle<Value> rotate(const Arguments &args) {\r
+ REQ_DOUBLE_ARG(0, degrees)\r
+ IMAGE_METHOD(RotateImage, degrees)\r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/shear.html#shearimage\r
+ static Handle<Value> shear(const Arguments &args) {\r
+ REQ_DOUBLE_ARG(0, x)\r
+ REQ_DOUBLE_ARG(1, y)\r
+ IMAGE_METHOD(ShearImage, x, y)\r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/enhance.html#contrastimage\r
+ static Handle<Value> contrast(const Arguments &args) {\r
+ REQ_INT_ARG(0, s)\r
+ //IMAGE_METHOD(ContrastImage, s)\r
+ return Undefined();\r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/enhance.html#equalizeimage\r
+ static Handle<Value> equalize(const Arguments &args) {\r
+ //IMAGE_METHOD(EqualizeImage)\r
+ return Undefined();\r
+ }\r
+ \r
+ static Handle<Value> gamma(const Arguments &args) {\r
+ //IMAGE_METHOD(GammaImage)\r
+ return Undefined();\r
+ }\r
+ \r
+ static Handle<Value> level(const Arguments &args) {\r
+ //IMAGE_METHOD(LevelImage)\r
+ return Undefined();\r
+ }\r
+ \r
+ static Handle<Value> levelChannel(const Arguments &args) {\r
+ //IMAGE_METHOD(LevelImageChannel)\r
+ return Undefined();\r
+ }\r
+ \r
+ static Handle<Value> modulate(const Arguments &args) {\r
+ //IMAGE_METHOD(ModulateImage)\r
+ return Undefined();\r
+ }\r
+ \r
+ static Handle<Value> negate(const Arguments &args) {\r
+ //IMAGE_METHOD(NegateImage)\r
+ return Undefined();\r
+ }\r
+ \r
+ static Handle<Value> normalize(const Arguments &args) {\r
+ //IMAGE_METHOD(NormalizeImage)\r
+ return Undefined();\r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/attribute.html#getimageattribute\r
+ //http://www.graphicsmagick.org/api/attribute.html#setimageattribute\r
+ static Handle<Value> attribute(const Arguments &args) {\r
+ //MagickImage *image = ObjectWrap::Unwrap<MagickImage>(args.This());\r
+ //ExceptionInfo exception;\r
+ return Undefined();\r
+ }\r
+ \r
+ //http://www.graphicsmagick.org/api/composite.html\r
+ static Handle<Value> composite(const Arguments &args) {\r
+ HandleScope scope;\r
+ Handle<Value> out;\r
+ CompositeOperator compose;\r
+ MagickImage *image = ObjectWrap::Unwrap<MagickImage>(args.This());\r
+ REQ_IMG_ARG(0, i)\r
+ REQ_INT_ARG(1, c)\r
+ OPT_INT_ARG(2, x, 0)\r
+ OPT_INT_ARG(3, y, 0)\r
+ const Image* compositeImage = *i;\r
+ compose = CompositeOperator(c);\r
+ if (CompositeImage( *image, compose, compositeImage, x, y ) == MagickPass) \r
+ return args.This();\r
+ else \r
+ return ThrowException(String::New("Unable to composite image!"));\r
+\r
+ }\r
+ \r
+};\r
+\r
+Persistent<FunctionTemplate> MagickImage::constructorTemplate;\r
+\r
+extern "C" {\r
+ static void init (Handle<Object> target)\r
+ {\r
+ InitializeMagick(NULL);\r
+ \r
+ \r
+ //http://www.graphicsmagick.org/api/types.html#filtertypes\r
+ NODE_DEFINE_CONSTANT(target, UndefinedFilter);\r
+ NODE_DEFINE_CONSTANT(target, PointFilter);\r
+ NODE_DEFINE_CONSTANT(target, BoxFilter);\r
+ NODE_DEFINE_CONSTANT(target, TriangleFilter);\r
+ NODE_DEFINE_CONSTANT(target, HermiteFilter);\r
+ NODE_DEFINE_CONSTANT(target, HanningFilter);\r
+ NODE_DEFINE_CONSTANT(target, HammingFilter);\r
+ NODE_DEFINE_CONSTANT(target, BlackmanFilter);\r
+ NODE_DEFINE_CONSTANT(target, GaussianFilter);\r
+ NODE_DEFINE_CONSTANT(target, QuadraticFilter);\r
+ NODE_DEFINE_CONSTANT(target, CubicFilter);\r
+ NODE_DEFINE_CONSTANT(target, CatromFilter);\r
+ NODE_DEFINE_CONSTANT(target, MitchellFilter);\r
+ NODE_DEFINE_CONSTANT(target, LanczosFilter);\r
+ NODE_DEFINE_CONSTANT(target, BesselFilter);\r
+ NODE_DEFINE_CONSTANT(target, SincFilter);\r
+ \r
+ //http://www.graphicsmagick.org/api/types.html#compositeoperator\r
+ NODE_DEFINE_CONSTANT(target, UndefinedCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, OverCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, InCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, OutCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, AtopCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, XorCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, PlusCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, MinusCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, AddCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, SubtractCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, DifferenceCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, BumpmapCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, CopyCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, CopyRedCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, CopyGreenCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, CopyBlueCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, CopyOpacityCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, ClearCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, DissolveCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, DisplaceCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, ModulateCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, ThresholdCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, NoCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, DarkenCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, LightenCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, HueCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, SaturateCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, ColorizeCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, LuminizeCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, ScreenCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, OverlayCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, CopyCyanCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, CopyMagentaCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, CopyYellowCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, CopyBlackCompositeOp);\r
+ NODE_DEFINE_CONSTANT(target, DivideCompositeOp);\r
+ \r
+ NODE_SET_METHOD(target, "image", MagickImage::create);\r
+ \r
+ MagickImage::Init(target);\r
+ }\r
+\r
+ NODE_MODULE(GraphicsMagick, init)\r
+}
\ No newline at end of file