mirror of
https://github.com/lovell/sharp.git
synced 2025-12-19 07:15:08 +01:00
Add boolean feature for bitwise image operations (#501)
This commit is contained in:
committed by
Lovell Fuller
parent
99f960bf56
commit
d17e8d3450
@@ -330,4 +330,13 @@ namespace sharp {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Get VIPS Boolean operatoin type from string
|
||||
*/
|
||||
VipsOperationBoolean GetBooleanOperation(std::string opStr) {
|
||||
return static_cast<VipsOperationBoolean>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_OPERATION_BOOLEAN, opStr.data())
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace sharp
|
||||
|
||||
@@ -123,6 +123,11 @@ namespace sharp {
|
||||
*/
|
||||
int MaximumImageAlpha(VipsInterpretation interpretation);
|
||||
|
||||
/*
|
||||
Get VIPS Boolean operatoin type from string
|
||||
*/
|
||||
VipsOperationBoolean GetBooleanOperation(std::string opStr);
|
||||
|
||||
|
||||
} // namespace sharp
|
||||
|
||||
|
||||
@@ -399,6 +399,13 @@ namespace sharp {
|
||||
return image.bandbool(boolean);
|
||||
}
|
||||
|
||||
/*
|
||||
Perform bitwise boolean operation between images
|
||||
*/
|
||||
VImage Boolean(VImage image, VImage imageR, VipsOperationBoolean const boolean) {
|
||||
return image.boolean(imageR, boolean);
|
||||
}
|
||||
|
||||
VImage Trim(VImage image, int const tolerance) {
|
||||
using sharp::MaximumImageAlpha;
|
||||
// An equivalent of ImageMagick's -trim in C++ ... automatically remove
|
||||
|
||||
@@ -87,6 +87,11 @@ namespace sharp {
|
||||
*/
|
||||
VImage Bandbool(VImage image, VipsOperationBoolean const boolean);
|
||||
|
||||
/*
|
||||
Perform bitwise boolean operation between images
|
||||
*/
|
||||
VImage Boolean(VImage image, VImage imageR, VipsOperationBoolean const boolean);
|
||||
|
||||
/*
|
||||
Trim an image
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <node.h>
|
||||
#include <node_buffer.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "nan.h"
|
||||
|
||||
#include "common.h"
|
||||
@@ -56,6 +59,7 @@ using sharp::EntropyCrop;
|
||||
using sharp::TileCache;
|
||||
using sharp::Threshold;
|
||||
using sharp::Bandbool;
|
||||
using sharp::Boolean;
|
||||
using sharp::Trim;
|
||||
|
||||
using sharp::ImageType;
|
||||
@@ -78,19 +82,22 @@ 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;
|
||||
} BufferContainer;
|
||||
|
||||
class PipelineWorker : public AsyncWorker {
|
||||
public:
|
||||
PipelineWorker(Callback *callback, PipelineBaton *baton, Callback *queueListener,
|
||||
const Local<Object> &bufferIn, const Local<Object> &overlayBufferIn) :
|
||||
AsyncWorker(callback), baton(baton), queueListener(queueListener) {
|
||||
if (baton->bufferInLength > 0) {
|
||||
SaveToPersistent("bufferIn", bufferIn);
|
||||
}
|
||||
if (baton->overlayBufferInLength > 0) {
|
||||
SaveToPersistent("overlayBufferIn", overlayBufferIn);
|
||||
}
|
||||
const std::vector<BufferContainer> saveBuffers) :
|
||||
AsyncWorker(callback), baton(baton), queueListener(queueListener), saveBuffers(saveBuffers) {
|
||||
for (const BufferContainer buf : saveBuffers) {
|
||||
SaveToPersistent(buf.name.c_str(), buf.nodeBuf);
|
||||
}
|
||||
}
|
||||
~PipelineWorker() {}
|
||||
|
||||
/*
|
||||
@@ -784,6 +791,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) {
|
||||
// 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);
|
||||
@@ -1007,11 +1052,8 @@ class PipelineWorker : public AsyncWorker {
|
||||
}
|
||||
|
||||
// Dispose of Persistent wrapper around input Buffers so they can be garbage collected
|
||||
if (baton->bufferInLength > 0) {
|
||||
GetFromPersistent("bufferIn");
|
||||
}
|
||||
if (baton->overlayBufferInLength > 0) {
|
||||
GetFromPersistent("overlayBufferIn");
|
||||
for (const BufferContainer buf : saveBuffers) {
|
||||
GetFromPersistent(buf.name.c_str());
|
||||
}
|
||||
delete baton;
|
||||
|
||||
@@ -1028,6 +1070,7 @@ class PipelineWorker : public AsyncWorker {
|
||||
private:
|
||||
PipelineBaton *baton;
|
||||
Callback *queueListener;
|
||||
std::vector<BufferContainer> saveBuffers;
|
||||
|
||||
/*
|
||||
Calculate the angle of rotation and need-to-flip for the output image.
|
||||
@@ -1155,6 +1198,14 @@ NAN_METHOD(pipeline) {
|
||||
baton->overlayYOffset = attrAs<int32_t>(options, "overlayYOffset");
|
||||
baton->overlayTile = attrAs<bool>(options, "overlayTile");
|
||||
baton->overlayCutout = attrAs<bool>(options, "overlayCutout");
|
||||
// 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);
|
||||
}
|
||||
// Resize options
|
||||
baton->withoutEnlargement = attrAs<bool>(options, "withoutEnlargement");
|
||||
baton->crop = attrAs<int32_t>(options, "crop");
|
||||
@@ -1232,14 +1283,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(
|
||||
@@ -1248,7 +1295,15 @@ 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;
|
||||
if (baton->bufferInLength)
|
||||
saveBuffers.push_back({"bufferIn", bufferIn});
|
||||
if (baton->overlayBufferInLength)
|
||||
saveBuffers.push_back({"overlayBufferIn", overlayBufferIn});
|
||||
if (baton->booleanBufferInLength)
|
||||
saveBuffers.push_back({"booleanBufferIn", booleanBufferIn});
|
||||
AsyncQueueWorker(new PipelineWorker(callback, baton, queueListener, saveBuffers));
|
||||
|
||||
// Increment queued task counter
|
||||
g_atomic_int_inc(&counterQueue);
|
||||
|
||||
@@ -39,6 +39,9 @@ struct PipelineBaton {
|
||||
int overlayYOffset;
|
||||
bool overlayTile;
|
||||
bool overlayCutout;
|
||||
std::string booleanFileIn;
|
||||
char *booleanBufferIn;
|
||||
size_t booleanBufferInLength;
|
||||
int topOffsetPre;
|
||||
int leftOffsetPre;
|
||||
int widthPre;
|
||||
@@ -94,6 +97,7 @@ struct PipelineBaton {
|
||||
double convKernelScale;
|
||||
double convKernelOffset;
|
||||
VipsOperationBoolean bandBoolOp;
|
||||
VipsOperationBoolean booleanOp;
|
||||
int extractChannel;
|
||||
int tileSize;
|
||||
int tileOverlap;
|
||||
@@ -116,6 +120,7 @@ struct PipelineBaton {
|
||||
overlayYOffset(-1),
|
||||
overlayTile(false),
|
||||
overlayCutout(false),
|
||||
booleanBufferInLength(0),
|
||||
topOffsetPre(-1),
|
||||
topOffsetPost(-1),
|
||||
channels(0),
|
||||
@@ -156,6 +161,7 @@ struct PipelineBaton {
|
||||
convKernelScale(0.0),
|
||||
convKernelOffset(0.0),
|
||||
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
||||
booleanOp(VIPS_OPERATION_BOOLEAN_LAST),
|
||||
extractChannel(-1),
|
||||
tileSize(256),
|
||||
tileOverlap(0),
|
||||
|
||||
Reference in New Issue
Block a user