-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a boolean operator that performs bitwise boolean operations between images #501
Changes from 7 commits
4a752c0
1cdff03
1fc05ed
4ae3f46
cb2bb43
539b6cc
6501696
4c7f339
e2cda35
3737463
9ca12ea
c4f42f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,6 +56,7 @@ using sharp::EntropyCrop; | |
using sharp::TileCache; | ||
using sharp::Threshold; | ||
using sharp::Bandbool; | ||
using sharp::Boolean; | ||
|
||
using sharp::ImageType; | ||
using sharp::ImageTypeId; | ||
|
@@ -76,19 +77,25 @@ using sharp::FreeCallback; | |
using sharp::CalculateCrop; | ||
using sharp::counterProcess; | ||
using sharp::counterQueue; | ||
using sharp::GetBooleanOperation; | ||
|
||
typedef struct BufferContainer_t { | ||
std::string name; | ||
Local<Object> nodeBuf; | ||
size_t length; | ||
} BufferContainer; | ||
|
||
class PipelineWorker : public AsyncWorker { | ||
public: | ||
PipelineWorker(Callback *callback, PipelineBaton *baton, Callback *queueListener, | ||
const Local<Object> &bufferIn, const Local<Object> &overlayBufferIn) : | ||
const std::vector<BufferContainer> saveBuffers) : | ||
AsyncWorker(callback), baton(baton), queueListener(queueListener) { | ||
if (baton->bufferInLength > 0) { | ||
SaveToPersistent("bufferIn", bufferIn); | ||
} | ||
if (baton->overlayBufferInLength > 0) { | ||
SaveToPersistent("overlayBufferIn", overlayBufferIn); | ||
for (const BufferContainer b : saveBuffers) { | ||
if (b.length > 0) { | ||
SaveToPersistent(b.name.c_str(), b.nodeBuf); | ||
} | ||
} | ||
} | ||
~PipelineWorker() {} | ||
|
||
/* | ||
|
@@ -777,6 +784,44 @@ class PipelineWorker : public AsyncWorker { | |
} | ||
} | ||
|
||
// Apply bitwise boolean operation between images | ||
if (baton->booleanOp != VIPS_OPERATION_BOOLEAN_LAST && | ||
(baton->booleanBufferInLength > 0 || !baton->booleanFileIn.empty())) { | ||
VImage booleanImage; | ||
ImageType booleanImageType = ImageType::UNKNOWN; | ||
if (baton->booleanBufferInLength > 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like there's potential for extracting this |
||
// Buffer input for boolean operation | ||
booleanImageType = DetermineImageType(baton->booleanBufferIn, baton->booleanBufferInLength); | ||
if (booleanImageType != ImageType::UNKNOWN) { | ||
try { | ||
booleanImage = VImage::new_from_buffer(baton->booleanBufferIn, baton->booleanBufferInLength, | ||
nullptr, VImage::option()->set("access", baton->accessMethod)); | ||
} catch (...) { | ||
(baton->err).append("Boolean operation buffer has corrupt header"); | ||
booleanImageType = ImageType::UNKNOWN; | ||
} | ||
} else { | ||
(baton->err).append("Boolean operation buffer contains unsupported image format"); | ||
} | ||
} else if (!baton->booleanFileIn.empty()) { | ||
// File input for boolean operation | ||
booleanImageType = DetermineImageType(baton->booleanFileIn.data()); | ||
if (booleanImageType != ImageType::UNKNOWN) { | ||
try { | ||
booleanImage = VImage::new_from_file(baton->booleanFileIn.data(), | ||
VImage::option()->set("access", baton->accessMethod)); | ||
} catch (...) { | ||
(baton->err).append("Boolean operation file has corrupt header"); | ||
} | ||
} | ||
} | ||
if (booleanImageType == ImageType::UNKNOWN) { | ||
return Error(); | ||
} | ||
// Apply the boolean operation | ||
image = Boolean(image, booleanImage, baton->booleanOp); | ||
} | ||
|
||
// Apply per-channel Bandbool bitwise operations after all other operations | ||
if (shouldBandbool) { | ||
image = Bandbool(image, baton->bandBoolOp); | ||
|
@@ -989,6 +1034,9 @@ class PipelineWorker : public AsyncWorker { | |
if (baton->overlayBufferInLength > 0) { | ||
GetFromPersistent("overlayBufferIn"); | ||
} | ||
if (baton->booleanBufferInLength > 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about a single call to |
||
GetFromPersistent("booleanBufferIn"); | ||
} | ||
delete baton; | ||
|
||
// Decrement processing task counter | ||
|
@@ -1126,6 +1174,14 @@ NAN_METHOD(pipeline) { | |
baton->overlayBufferInLength = node::Buffer::Length(overlayBufferIn); | ||
baton->overlayBufferIn = node::Buffer::Data(overlayBufferIn); | ||
} | ||
// Boolean options | ||
baton->booleanFileIn = attrAsStr(options, "booleanFileIn"); | ||
Local<Object> booleanBufferIn; | ||
if (node::Buffer::HasInstance(Get(options, New("booleanBufferIn").ToLocalChecked()).ToLocalChecked())) { | ||
booleanBufferIn = Get(options, New("booleanBufferIn").ToLocalChecked()).ToLocalChecked().As<Object>(); | ||
baton->booleanBufferInLength = node::Buffer::Length(booleanBufferIn); | ||
baton->booleanBufferIn = node::Buffer::Data(booleanBufferIn); | ||
} | ||
baton->overlayGravity = attrAs<int32_t>(options, "overlayGravity"); | ||
baton->overlayXOffset = attrAs<int32_t>(options, "overlayXOffset"); | ||
baton->overlayYOffset = attrAs<int32_t>(options, "overlayYOffset"); | ||
|
@@ -1203,14 +1259,10 @@ NAN_METHOD(pipeline) { | |
} | ||
} | ||
// Bandbool operation | ||
std::string opStr = attrAsStr(options, "bandBoolOp"); | ||
if(opStr == "and" ) { | ||
baton->bandBoolOp = VIPS_OPERATION_BOOLEAN_AND; | ||
} else if(opStr == "or") { | ||
baton->bandBoolOp = VIPS_OPERATION_BOOLEAN_OR; | ||
} else if(opStr == "eor") { | ||
baton->bandBoolOp = VIPS_OPERATION_BOOLEAN_EOR; | ||
} | ||
baton->bandBoolOp = GetBooleanOperation(attrAsStr(options, "bandBoolOp")); | ||
|
||
// Boolean operation | ||
baton->booleanOp = GetBooleanOperation(attrAsStr(options, "booleanOp")); | ||
|
||
// Function to notify of queue length changes | ||
Callback *queueListener = new Callback( | ||
|
@@ -1219,7 +1271,12 @@ NAN_METHOD(pipeline) { | |
|
||
// Join queue for worker thread | ||
Callback *callback = new Callback(info[1].As<Function>()); | ||
AsyncQueueWorker(new PipelineWorker(callback, baton, queueListener, bufferIn, overlayBufferIn)); | ||
|
||
std::vector<BufferContainer> saveBuffers; | ||
saveBuffers.push_back({"bufferIn", bufferIn, baton->bufferInLength}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we only append buffers to the vector where their length is >0 then we don't need to store the length. |
||
saveBuffers.push_back({"overlayBufferIn", overlayBufferIn, baton->overlayBufferInLength}); | ||
saveBuffers.push_back({"booleanBufferIn", booleanBufferIn, baton->booleanBufferInLength}); | ||
AsyncQueueWorker(new PipelineWorker(callback, baton, queueListener, saveBuffers)); | ||
|
||
// Increment queued task counter | ||
g_atomic_int_inc(&counterQueue); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably auto-generate a suitable "key" (the first parameter, a string) for
SaveToPersistent
based on the memory location, perhaps something like http://stackoverflow.com/a/7850160/4625124