#include #include #include "nan.h" #include "common.h" #include "metadata.h" using namespace v8; using namespace sharp; struct MetadataBaton { // Input std::string fileIn; void* bufferIn; size_t bufferInLength; // Output std::string format; int width; int height; std::string space; int channels; bool hasProfile; bool hasAlpha; int orientation; std::string err; MetadataBaton(): bufferInLength(0), orientation(0) {} }; class MetadataWorker : public NanAsyncWorker { public: MetadataWorker(NanCallback *callback, MetadataBaton *baton) : NanAsyncWorker(callback), baton(baton) {} ~MetadataWorker() {} void Execute() { // Decrement queued task counter g_atomic_int_dec_and_test(&counterQueue); ImageType imageType = ImageType::UNKNOWN; VipsImage *image = NULL; if (baton->bufferInLength > 1) { // From buffer imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength); if (imageType != ImageType::UNKNOWN) { image = InitImage(imageType, baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM); } else { (baton->err).append("Input buffer contains unsupported image format"); } } else { // From file imageType = DetermineImageType(baton->fileIn.c_str()); if (imageType != ImageType::UNKNOWN) { image = InitImage(imageType, baton->fileIn.c_str(), VIPS_ACCESS_RANDOM); } else { (baton->err).append("File is of an unsupported image format"); } } if (image != NULL && imageType != ImageType::UNKNOWN) { // Image type switch (imageType) { case ImageType::JPEG: baton->format = "jpeg"; break; case ImageType::PNG: baton->format = "png"; break; case ImageType::WEBP: baton->format = "webp"; break; case ImageType::TIFF: baton->format = "tiff"; break; case ImageType::MAGICK: baton->format = "magick"; break; case ImageType::UNKNOWN: break; } // VipsImage attributes baton->width = image->Xsize; baton->height = image->Ysize; baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type); baton->channels = image->Bands; baton->hasProfile = (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? TRUE : FALSE; // Derived attributes baton->hasAlpha = HasAlpha(image); baton->orientation = ExifOrientation(image); // Drop image reference g_object_unref(image); } // Clean up vips_error_clear(); vips_thread_shutdown(); } void HandleOKCallback () { NanScope(); Handle argv[2] = { NanNull(), NanNull() }; if (!baton->err.empty()) { // Error argv[0] = Exception::Error(NanNew(baton->err.data(), baton->err.size())); } else { // Metadata Object Local info = NanNew(); info->Set(NanNew("format"), NanNew(baton->format)); info->Set(NanNew("width"), NanNew(baton->width)); info->Set(NanNew("height"), NanNew(baton->height)); info->Set(NanNew("space"), NanNew(baton->space)); info->Set(NanNew("channels"), NanNew(baton->channels)); info->Set(NanNew("hasProfile"), NanNew(baton->hasProfile)); info->Set(NanNew("hasAlpha"), NanNew(baton->hasAlpha)); if (baton->orientation > 0) { info->Set(NanNew("orientation"), NanNew(baton->orientation)); } argv[1] = info; } delete baton; // Return to JavaScript callback->Call(2, argv); } private: MetadataBaton* baton; }; /* metadata(options, callback) */ NAN_METHOD(metadata) { NanScope(); // V8 objects are converted to non-V8 types held in the baton struct MetadataBaton *baton = new MetadataBaton; Local options = args[0]->ToObject(); // Input filename baton->fileIn = *String::Utf8Value(options->Get(NanNew("fileIn"))->ToString()); // Input Buffer object if (options->Get(NanNew("bufferIn"))->IsObject()) { Local buffer = options->Get(NanNew("bufferIn"))->ToObject(); baton->bufferInLength = node::Buffer::Length(buffer); baton->bufferIn = node::Buffer::Data(buffer); } // Join queue for worker thread NanCallback *callback = new NanCallback(args[1].As()); NanAsyncQueueWorker(new MetadataWorker(callback, baton)); // Increment queued task counter g_atomic_int_inc(&counterQueue); NanReturnUndefined(); }