diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3fbd98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +node_modules diff --git a/binding.gyp b/binding.gyp index 6db7232..1d42b4d 100644 --- a/binding.gyp +++ b/binding.gyp @@ -15,9 +15,15 @@ 'VERSION=0.5.0' ], 'sources': [ 'src/bindings.cpp', 'src/FreeImage.cpp', 'src/Image.cpp' ], + "include_dirs" : [ + "", - "keywords": [ - "jpeg", - "jpg", - "png", - "gif", - "bmp", - "jng", - "pnm", - "ppm", - "tga", - "tiff", - "psd", - "xbm", - "xpm", - "j2k", - "jpeg2000", - "jp2", - "sgi", - "exr", - "hdr", - "pict", - "raw", - "ico", - "wbmp", - "image", - "picture" - ], - "maintainers": [ - { - "name": "Mikael Bourges-Sevenier", - "email": "mikeseven@gmail.com", - "twitter": "msevenier" - } - ], - "licenses":[ - { - "type":"BSD", - "url": "http://github.com/mikeseven/node-image/raw/master/LICENSES" - } - ], - "repositories": [ - { - "type": "git", - "url": "https://mikeseven@github.com/mikeseven/node-image.git" - } - ], - "directories": { - "src": "src", - "lib": "lib", - "test": "test" - }, - "engines": { - "node": ">=0.5.9" - }, - "scripts": { - "install": "node-gyp rebuild" - } + "name": "node-image", + "version": "0.5.2", + "description": "A Node.JS wrapper around FreeImage", + "main": "lib/image.js", + "author": "Mikael Bourges-Sevenier ", + "keywords": [ + "jpeg", + "jpg", + "png", + "gif", + "bmp", + "jng", + "pnm", + "ppm", + "tga", + "tiff", + "psd", + "xbm", + "xpm", + "j2k", + "jpeg2000", + "jp2", + "sgi", + "exr", + "hdr", + "pict", + "raw", + "ico", + "wbmp", + "image", + "picture" + ], + "maintainers": [ + { + "name": "Mikael Bourges-Sevenier", + "email": "mikeseven@gmail.com", + "twitter": "msevenier" + } + ], + "licenses": [ + { + "type": "BSD", + "url": "http://github.com/mikeseven/node-image/raw/master/LICENSES" + } + ], + "repositories": [ + { + "type": "git", + "url": "https://mikeseven@github.com/mikeseven/node-image.git" + } + ], + "directories": { + "src": "src", + "lib": "lib", + "test": "test" + }, + "engines": { + "node": ">=0.5.9" + }, + "scripts": { + "install": "node-gyp rebuild" + }, + "dependencies": { + "nan": "~1.8.4" + } } - diff --git a/src/FreeImage.cpp b/src/FreeImage.cpp index 518d6be..f8674ef 100644 --- a/src/FreeImage.cpp +++ b/src/FreeImage.cpp @@ -5,9 +5,13 @@ ** (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. ** */ +#include "nan.h" + #include "FreeImage.h" #include "Image.h" +#include + #include using namespace std; @@ -27,36 +31,44 @@ FreeImage::~FreeImage() { } void FreeImage::Initialize(Handle target) { - HandleScope scope; + NanScope(); - Local t = FunctionTemplate::New(New); - constructor_template = Persistent::New(t); + Local t = NanNew(New); + NanAssignPersistent(constructor_template, t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(JS_STR("FreeImage")); + t->InstanceTemplate()->SetInternalFieldCount(1); + t->SetClassName(NanNew("FreeImage")); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "getVersion", getVersion); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "load", load); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "save", save); + NODE_SET_PROTOTYPE_METHOD(t, "getVersion", getVersion); + NODE_SET_PROTOTYPE_METHOD(t, "load", load); + NODE_SET_PROTOTYPE_METHOD(t, "loadFromMemory", loadFromMemory); + NODE_SET_PROTOTYPE_METHOD(t, "convertFromRawBits", convertFromRawBits); + NODE_SET_PROTOTYPE_METHOD(t, "save", save); - target->Set(JS_STR("FreeImage"), constructor_template->GetFunction()); + target->Set(NanNew("FreeImage"), t->GetFunction()); } +void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const char *message) { + cout << "Error: " << message << endl; +} -JS_METHOD(FreeImage::New) { - HandleScope scope; +NAN_METHOD(FreeImage::New) { + NanScope(); FreeImage *fi = new FreeImage(args.This()); fi->Wrap(args.This()); - return scope.Close(args.This()); + + FreeImage_SetOutputMessage(FreeImageErrorHandler); + + NanReturnThis(); } -JS_METHOD(FreeImage::getVersion) { - HandleScope scope; - return scope.Close(JS_STR(FreeImage_GetVersion())); +NAN_METHOD(FreeImage::getVersion) { + NanScope(); + NanReturnValue(NanNew(FreeImage_GetVersion())); } -JS_METHOD(FreeImage::load) { - HandleScope scope; +NAN_METHOD(FreeImage::load) { + NanScope(); String::Utf8Value filename(args[0]->ToString()); @@ -67,37 +79,66 @@ JS_METHOD(FreeImage::load) { dib = FreeImage_Load(fif, *filename); // check that dib does not contains pixels - if(!dib) return Undefined(); - if(!FreeImage_HasPixels(dib)) return Undefined(); - - //cout<<"dib "< obj = ObjectTemplate::New(); - obj->SetInternalFieldCount(1); - - Local image = obj->NewInstance(); - image->SetInternalField(0, External::New(dib)); - image->Set(JS_STR("width"), JS_INT(w=FreeImage_GetWidth(dib))); - image->Set(JS_STR("height"), JS_INT(h=FreeImage_GetHeight(dib))); - image->Set(JS_STR("bpp"), JS_INT(FreeImage_GetBPP(dib))); - image->Set(JS_STR("pitch"), JS_INT(pitch=FreeImage_GetPitch(dib))); - image->Set(JS_STR("type"), JS_INT(type)); - image->Set(JS_STR("redMask"), JS_INT(FreeImage_GetRedMask(dib))); - image->Set(JS_STR("greenMask"), JS_INT(FreeImage_GetGreenMask(dib))); - image->Set(JS_STR("blueMask"), JS_INT(FreeImage_GetBlueMask(dib))); - - BYTE *bits=FreeImage_GetBits(dib); - node::Buffer *buf = node::Buffer::New((char*)bits,h*pitch); - image->Set(JS_STR("buffer"), buf->handle_); - - return scope.Close(image);*/ - return scope.Close(Image::New(dib)->handle_); + if(!dib) NanReturnUndefined(); + if(!FreeImage_HasPixels(dib)) NanReturnUndefined(); + + NanReturnValue(NanObjectWrapHandle(Image::New(dib))); +} + +NAN_METHOD(FreeImage::loadFromMemory) { + NanScope(); + + Local bufferObj = args[0]->ToObject(); + BYTE* bufferData = (BYTE*) Buffer::Data(bufferObj); + size_t bufferLength = Buffer::Length(bufferObj); + + FIMEMORY *hmem = FreeImage_OpenMemory(bufferData, bufferLength); + + FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0); + FIBITMAP *dib = FreeImage_LoadFromMemory(fif, hmem, 0); + FreeImage_CloseMemory(hmem); + + // check that dib does not contains pixels + if(!dib) NanReturnUndefined(); + if(!FreeImage_HasPixels(dib)) NanReturnUndefined(); + + NanReturnValue(NanObjectWrapHandle(Image::New(dib))); +} + +NAN_METHOD(FreeImage::convertFromRawBits) { + NanScope(); + + Local bufferObj = args[0]->ToObject(); + BYTE* bufferData = (BYTE*) Buffer::Data(bufferObj); + + uint32_t width = args[1]->Int32Value(); + uint32_t height = args[2]->Int32Value(); + uint32_t pitch=width*4, bpp=32; + uint32_t redMask=0xFF000000, greenMask=0x00FF0000, blueMask=0x0000FF00; + BOOL topdown = FALSE; + + if(args.Length()>3) pitch=args[3]->Uint32Value(); + if(args.Length()>4) bpp=args[4]->Uint32Value(); + if(args.Length()>5) redMask=args[5]->Uint32Value(); + if(args.Length()>6) greenMask=args[6]->Uint32Value(); + if(args.Length()>7) blueMask=args[7]->Uint32Value(); + if(args.Length()>8) topdown=args[8]->BooleanValue(); + +// cout<<"convertFromRawBits: wxh: "<ToString()); cout<<"args length: "<(FreeImage_Save(format, image, *filename) == TRUE)); } } diff --git a/src/FreeImage.h b/src/FreeImage.h index 1e0f77c..4677b60 100644 --- a/src/FreeImage.h +++ b/src/FreeImage.h @@ -21,11 +21,13 @@ class FreeImage : public node::ObjectWrap public: static void Initialize(Handle target); - static JS_METHOD(New); + static NAN_METHOD(New); - static JS_METHOD(getVersion); - static JS_METHOD(load); - static JS_METHOD(save); + static NAN_METHOD(getVersion); + static NAN_METHOD(load); + static NAN_METHOD(loadFromMemory); + static NAN_METHOD(convertFromRawBits); + static NAN_METHOD(save); protected: ~FreeImage(); diff --git a/src/Image.cpp b/src/Image.cpp index aebfdcb..4a19c1b 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -5,6 +5,8 @@ ** (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. ** */ +#include "string.h" + #include "Image.h" #include @@ -18,116 +20,187 @@ namespace freeimage { Persistent Image::constructor_template; -Image::Image(Handle wrapper) : dib(NULL) {} +Image::Image(Handle wrapper) {} Image::~Image() { - cout<<"Deleting image"< internalField = NanObjectWrapHandle(this)->GetInternalField(0); + if (internalField->IsNull()) return; + + FIBITMAP *dib = static_cast(Local::Cast(internalField)->Value()); + FreeImage_Unload(dib); } void Image::Initialize(Handle target) { - HandleScope scope; + NanScope(); + + Local t = NanNew(New); + NanAssignPersistent(constructor_template, t); - Local t = FunctionTemplate::New(Image::New); - constructor_template = Persistent::New(t); + t->InstanceTemplate()->SetInternalFieldCount(1); + t->SetClassName(NanNew("Image")); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(JS_STR("Image")); + NODE_SET_PROTOTYPE_METHOD(t, "unload", unload); + NODE_SET_PROTOTYPE_METHOD(t, "save", save); + NODE_SET_PROTOTYPE_METHOD(t, "saveToMemory", saveToMemory); + NODE_SET_PROTOTYPE_METHOD(t, "convertTo32Bits", convertTo32Bits); + NODE_SET_PROTOTYPE_METHOD(t, "convertTo24Bits", convertTo24Bits); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "unload", unload); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "save", save); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "convertTo32Bits", convertTo32Bits); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "convertTo24Bits", convertTo24Bits); + NODE_SET_PROTOTYPE_METHOD(t, "flipHorizontal", flipHorizontal); + NODE_SET_PROTOTYPE_METHOD(t, "flipVertical", flipVertical); - target->Set(JS_STR("Image"), constructor_template->GetFunction()); + target->Set(NanNew("Image"), t->GetFunction()); } -JS_METHOD(Image::New) { - HandleScope scope; +NAN_METHOD(Image::New) { + NanScope(); + Image *fi = new Image(args.This()); fi->Wrap(args.This()); - return scope.Close(args.This()); + + NanReturnThis(); } Image *Image::New(FIBITMAP* dib) { + NanScope(); - HandleScope scope; - - Local arg = Integer::NewFromUnsigned(0); - Local obj = constructor_template->GetFunction()->NewInstance(1, &arg); + Local arg = NanNew(0); + Local obj = NanNew(constructor_template)->GetFunction()->NewInstance(1, &arg); Image *image = ObjectWrap::Unwrap(obj); - image->dib = dib; int w,h,pitch; FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib); - obj->SetInternalField(0, External::New(dib)); - obj->Set(JS_STR("width"), JS_INT(w=FreeImage_GetWidth(dib))); - obj->Set(JS_STR("height"), JS_INT(h=FreeImage_GetHeight(dib))); - obj->Set(JS_STR("bpp"), JS_INT((int)FreeImage_GetBPP(dib))); - obj->Set(JS_STR("pitch"), JS_INT(pitch=FreeImage_GetPitch(dib))); - obj->Set(JS_STR("type"), JS_INT(type)); - obj->Set(JS_STR("redMask"), JS_INT((int)FreeImage_GetRedMask(dib))); - obj->Set(JS_STR("greenMask"), JS_INT((int)FreeImage_GetGreenMask(dib))); - obj->Set(JS_STR("blueMask"), JS_INT((int)FreeImage_GetBlueMask(dib))); + obj->SetInternalField(0, NanNew(dib)); + obj->Set(NanNew("width"), NanNew(w=FreeImage_GetWidth(dib))); + obj->Set(NanNew("height"), NanNew(h=FreeImage_GetHeight(dib))); + obj->Set(NanNew("bpp"), NanNew((int)FreeImage_GetBPP(dib))); + obj->Set(NanNew("pitch"), NanNew(pitch=FreeImage_GetPitch(dib))); + obj->Set(NanNew("type"), NanNew(type)); + obj->Set(NanNew("redMask"), NanNew((int)FreeImage_GetRedMask(dib))); + obj->Set(NanNew("greenMask"), NanNew((int)FreeImage_GetGreenMask(dib))); + obj->Set(NanNew("blueMask"), NanNew((int)FreeImage_GetBlueMask(dib))); - BYTE *bits=FreeImage_GetBits(dib); - node::Buffer *buf = node::Buffer::New((char*)bits,h*pitch); - obj->Set(JS_STR("buffer"), buf->handle_); + BYTE *bits = FreeImage_GetBits(dib); + obj->Set(NanNew("buffer"), NanNewBufferHandle((char*) bits, h * pitch)); return image; } -JS_METHOD(Image::unload) { - HandleScope scope; - Local wrap = Local::Cast(args.This()->GetInternalField(0)); - FIBITMAP *dib=static_cast(wrap->Value()); - FreeImage_Unload(dib); - return Undefined(); +NAN_METHOD(Image::unload) { + NanScope(); + + Local internalField = args.This()->GetInternalField(0); + if (!internalField->IsNull()) { + FIBITMAP *dib = static_cast(Local::Cast(internalField)->Value()); + FreeImage_Unload(dib); + args.This()->SetInternalField(0, v8::Null(v8::Isolate::GetCurrent())); + } + + NanReturnUndefined(); } -JS_METHOD(Image::save) { - HandleScope scope; +NAN_METHOD(Image::save) { + NanScope(); Local wrap = Local::Cast(args.This()->GetInternalField(0)); FIBITMAP *dib=static_cast(wrap->Value()); - //cout<<"dib "<Uint32Value(); String::Utf8Value str(args[1]->ToString()); int flags=0; - if(!args[2]->IsUndefined()) { + if (!args[2]->IsUndefined()) { flags=args[2]->Int32Value(); } - cout<<"Saving image to "<<*str<<" format: "< wrap = Local::Cast(args.This()->GetInternalField(0)); + FIBITMAP *dib = static_cast(wrap->Value()); + // cout << "dib " << hex << dib << dec << endl; + + FREE_IMAGE_FORMAT fif = (FREE_IMAGE_FORMAT) args[0]->Uint32Value(); + + int flags = 0; + if (!args[1]->IsUndefined()) { + flags = args[1]->Int32Value(); + } + + if (fif == FIF_JPEG && FreeImage_GetBPP(dib) != 24) { + // FIBITMAP *old = dib; + dib = FreeImage_ConvertTo24Bits(dib); + // FreeImage_Unload(old); + } + + Local actualBuffer; + + BYTE *mem_buffer = NULL; + DWORD file_size; + FIMEMORY *hmem = FreeImage_OpenMemory(); + + FreeImage_SaveToMemory(fif, dib, hmem, flags); + FreeImage_AcquireMemory(hmem, &mem_buffer, &file_size); + + // This Buffer constructor actually copies the data + actualBuffer = NanNewBufferHandle((char *) mem_buffer, (size_t) file_size); + + FreeImage_CloseMemory(hmem); + + NanReturnValue(actualBuffer); } -JS_METHOD(Image::convertTo32Bits) { - HandleScope scope; +NAN_METHOD(Image::convertTo32Bits) { + NanScope(); + Local wrap = Local::Cast(args.This()->GetInternalField(0)); FIBITMAP *dib=static_cast(wrap->Value()); FIBITMAP *conv=FreeImage_ConvertTo32Bits(dib); - return scope.Close(Image::New(conv)->handle_); + NanReturnValue(NanObjectWrapHandle(Image::New(conv))); } -JS_METHOD(Image::convertTo24Bits) { - HandleScope scope; +NAN_METHOD(Image::convertTo24Bits) { + NanScope(); + Local wrap = Local::Cast(args.This()->GetInternalField(0)); FIBITMAP *dib=static_cast(wrap->Value()); FIBITMAP *conv=FreeImage_ConvertTo24Bits(dib); - return scope.Close(Image::New(conv)->handle_); + NanReturnValue(NanObjectWrapHandle(Image::New(conv))); +} + + +NAN_METHOD(Image::flipHorizontal) { + NanScope(); + + Local wrap = Local::Cast(args.This()->GetInternalField(0)); + FIBITMAP *dib = static_cast(wrap->Value()); + BOOL flip = FreeImage_FlipHorizontal(dib); + + NanReturnValue(NanNew(flip)); +} + +NAN_METHOD(Image::flipVertical) { + NanScope(); + + Local wrap = Local::Cast(args.This()->GetInternalField(0)); + FIBITMAP *dib = static_cast(wrap->Value()); + BOOL flip = FreeImage_FlipVertical(dib); + + NanReturnValue(NanNew(flip)); } } diff --git a/src/Image.h b/src/Image.h index be33a6e..f92721f 100644 --- a/src/Image.h +++ b/src/Image.h @@ -22,19 +22,21 @@ class Image : public node::ObjectWrap public: ~Image(); static void Initialize(Handle target); - static JS_METHOD(New); + static NAN_METHOD(New); static Image *New(FIBITMAP* image); - static JS_METHOD(unload); - static JS_METHOD(save); - static JS_METHOD(convertTo32Bits); - static JS_METHOD(convertTo24Bits); + static NAN_METHOD(unload); + static NAN_METHOD(save); + static NAN_METHOD(saveToMemory); + static NAN_METHOD(convertTo32Bits); + static NAN_METHOD(convertTo24Bits); + + static NAN_METHOD(flipHorizontal); + static NAN_METHOD(flipVertical); private: Image(Handle wrapper); static Persistent constructor_template; - - FIBITMAP *dib; }; }; diff --git a/src/common.h b/src/common.h index a5f7a48..db20b7c 100644 --- a/src/common.h +++ b/src/common.h @@ -11,47 +11,36 @@ #include #include - -#define JS_STR(...) v8::String::New(__VA_ARGS__) -#define JS_INT(val) v8::Integer::New(val) -#define JS_FLOAT(val) v8::Number::New(val) -#define JS_BOOL(val) v8::Boolean::New(val) -#define JS_METHOD(name) v8::Handle name(const v8::Arguments& args) -#define JS_EXCEPTION(reason) v8::ThrowException(v8::Exception::Error(JS_STR(reason))) -#define JS_RETHROW(tc) v8::Local::New(tc.Exception()); +#include "nan.h" #define REQ_ARGS(N) \ - if (args.Length() < (N)) \ - return ThrowException(Exception::TypeError( \ - String::New("Expected " #N "arguments"))); + if (args.Length() < (N)) \ + NanThrowTypeError("Expected " #N "arguments"); #define REQ_STR_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsString()) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a string"))); \ - String::Utf8Value VAR(args[I]->ToString()); + if (args.Length() <= (I) || !args[I]->IsString()) \ + NanThrowTypeError("Argument " #I " must be a string"); \ + String::Utf8Value VAR(args[I]->ToString()); #define REQ_EXT_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsExternal()) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " invalid"))); \ - Local VAR = Local::Cast(args[I]); + if (args.Length() <= (I) || !args[I]->IsExternal()) \ + NanThrowTypeError("Argument " #I " invalid"); \ + Local VAR = Local::Cast(args[I]); #define REQ_FUN_ARG(I, VAR) \ - if (args.Length() <= (I) || !args[I]->IsFunction()) \ - return ThrowException(Exception::TypeError( \ - String::New("Argument " #I " must be a function"))); \ - Local VAR = Local::Cast(args[I]); + if (args.Length() <= (I) || !args[I]->IsFunction()) \ + NanThrowTypeError("Argument " #I " must be a function"); \ + Local VAR = Local::Cast(args[I]); #define REQ_ERROR_THROW(error) if (ret == error) return JS_EXCEPTION(#error); template -static T* UnwrapThis(const v8::Arguments& args) { +static T* UnwrapThis(_NAN_METHOD_ARGS) { return node::ObjectWrap::Unwrap(args.This()); } template -static T* UnwrapThis(const v8::AccessorInfo& args) { +static T* UnwrapThis(_NAN_GETTER_ARGS) { return node::ObjectWrap::Unwrap(args.This()); } diff --git a/test/test-memory.js b/test/test-memory.js new file mode 100644 index 0000000..49b6804 --- /dev/null +++ b/test/test-memory.js @@ -0,0 +1,26 @@ +var Image=new require('../lib/image').Image + , util=require('util') + , fs=require('fs'); + +var log=console.log; + +function save(image, fif, filename, flags) { + console.log("Saving image to " + filename + " format: " + fif.toString(16) + " flags: " + flags.toString(16)); + var buffer = image.saveToMemory(fif, filename, flags); + fs.writeFileSync(filename, buffer); +} + +log('FreeImage version: '+Image.getVersion()); + +var path=__dirname+'/mike_scooter.jpg'; +var imageData = fs.readFileSync(path); +var image=Image.loadFromMemory(imageData); +log('Image '+path+': \n'+util.inspect(image)); + +image=image.convertTo32Bits(); + +save(image, Image.FIF_JPEG,"out_75.jpg",Image.JPEG_PROGRESSIVE | Image.JPEG_QUALITYGOOD); +save(image, Image.FIF_JPEG,"out_25.jpg",Image.JPEG_PROGRESSIVE | Image.JPEG_QUALITYAVERAGE); +save(image, Image.FIF_PNG,"out_9.png",Image.PNG_Z_BEST_COMPRESSION); +save(image, Image.FIF_PNG,"out_0.png",Image.PNG_Z_NO_COMPRESSION); +image.unload();