mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Add boolean feature for bitwise image operations (#501)
This commit is contained in:
parent
99f960bf56
commit
d17e8d3450
15
docs/api.md
15
docs/api.md
@ -520,6 +520,21 @@ sharp('input.png')
|
|||||||
|
|
||||||
In the above example if `input.png` is a 3 channel RGB image, `output.png` will be a 1 channel grayscale image where each pixel `P = R & G & B`. For example, if `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]` then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
|
In the above example if `input.png` is a 3 channel RGB image, `output.png` will be a 1 channel grayscale image where each pixel `P = R & G & B`. For example, if `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]` then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
|
||||||
|
|
||||||
|
#### boolean(image, operation)
|
||||||
|
|
||||||
|
Perform a bitwise boolean operation with `image`.
|
||||||
|
|
||||||
|
`image` is one of the following.
|
||||||
|
|
||||||
|
* Buffer contianing PNG, WebP, GIF or SVG image data, or
|
||||||
|
* String containing the path to an image file
|
||||||
|
|
||||||
|
This operation creates an output image where each pixel is the result of the selected bitwise boolean `operation`, between the corresponding pixels of the input images. The boolean operation can be one of the following:
|
||||||
|
|
||||||
|
* `and` performs a bitwise and operation, like the c-operator `&`
|
||||||
|
* `or` performs a bitwise or operation, like the c-operator `|`
|
||||||
|
* `eor` performs a bitwise exclusive or operation, like the c-operator `^`
|
||||||
|
|
||||||
### Output
|
### Output
|
||||||
|
|
||||||
#### toFile(path, [callback])
|
#### toFile(path, [callback])
|
||||||
|
27
index.js
27
index.js
@ -91,6 +91,9 @@ var Sharp = function(input, options) {
|
|||||||
greyscale: false,
|
greyscale: false,
|
||||||
normalize: 0,
|
normalize: 0,
|
||||||
bandBoolOp: null,
|
bandBoolOp: null,
|
||||||
|
booleanOp: null,
|
||||||
|
booleanBufferIn: null,
|
||||||
|
booleanFileIn: '',
|
||||||
// overlay
|
// overlay
|
||||||
overlayFileIn: '',
|
overlayFileIn: '',
|
||||||
overlayBufferIn: null,
|
overlayBufferIn: null,
|
||||||
@ -365,6 +368,30 @@ Sharp.prototype.negate = function(negate) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bitwise boolean operations between images
|
||||||
|
*/
|
||||||
|
Sharp.prototype.boolean = function(operand, operator) {
|
||||||
|
if (isString(operand)) {
|
||||||
|
this.options.booleanFileIn = operand;
|
||||||
|
} else if (isBuffer(operand)) {
|
||||||
|
this.options.booleanBufferIn = operand;
|
||||||
|
} else {
|
||||||
|
throw new Error('Unsupported boolean operand ' + typeof operand);
|
||||||
|
}
|
||||||
|
if (!isString(operator)) {
|
||||||
|
throw new Error('Invalid boolean operation ' + operator);
|
||||||
|
}
|
||||||
|
operator = operator.toLowerCase();
|
||||||
|
var ops = ['and', 'or', 'eor'];
|
||||||
|
if(ops.indexOf(operator) == -1) {
|
||||||
|
throw new Error('Invalid boolean operation ' + operator);
|
||||||
|
}
|
||||||
|
this.options.booleanOp = operator;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Overlay with another image, using an optional gravity
|
Overlay with another image, using an optional gravity
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
} // namespace sharp
|
||||||
|
@ -123,6 +123,11 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
int MaximumImageAlpha(VipsInterpretation interpretation);
|
int MaximumImageAlpha(VipsInterpretation interpretation);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get VIPS Boolean operatoin type from string
|
||||||
|
*/
|
||||||
|
VipsOperationBoolean GetBooleanOperation(std::string opStr);
|
||||||
|
|
||||||
|
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
|
||||||
|
@ -399,6 +399,13 @@ namespace sharp {
|
|||||||
return image.bandbool(boolean);
|
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) {
|
VImage Trim(VImage image, int const tolerance) {
|
||||||
using sharp::MaximumImageAlpha;
|
using sharp::MaximumImageAlpha;
|
||||||
// An equivalent of ImageMagick's -trim in C++ ... automatically remove
|
// An equivalent of ImageMagick's -trim in C++ ... automatically remove
|
||||||
|
@ -87,6 +87,11 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
VImage Bandbool(VImage image, VipsOperationBoolean const boolean);
|
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
|
Trim an image
|
||||||
*/
|
*/
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
#include <node.h>
|
#include <node.h>
|
||||||
#include <node_buffer.h>
|
#include <node_buffer.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
#include "nan.h"
|
#include "nan.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@ -56,6 +59,7 @@ using sharp::EntropyCrop;
|
|||||||
using sharp::TileCache;
|
using sharp::TileCache;
|
||||||
using sharp::Threshold;
|
using sharp::Threshold;
|
||||||
using sharp::Bandbool;
|
using sharp::Bandbool;
|
||||||
|
using sharp::Boolean;
|
||||||
using sharp::Trim;
|
using sharp::Trim;
|
||||||
|
|
||||||
using sharp::ImageType;
|
using sharp::ImageType;
|
||||||
@ -78,17 +82,20 @@ using sharp::FreeCallback;
|
|||||||
using sharp::CalculateCrop;
|
using sharp::CalculateCrop;
|
||||||
using sharp::counterProcess;
|
using sharp::counterProcess;
|
||||||
using sharp::counterQueue;
|
using sharp::counterQueue;
|
||||||
|
using sharp::GetBooleanOperation;
|
||||||
|
|
||||||
|
typedef struct BufferContainer_t {
|
||||||
|
std::string name;
|
||||||
|
Local<Object> nodeBuf;
|
||||||
|
} BufferContainer;
|
||||||
|
|
||||||
class PipelineWorker : public AsyncWorker {
|
class PipelineWorker : public AsyncWorker {
|
||||||
public:
|
public:
|
||||||
PipelineWorker(Callback *callback, PipelineBaton *baton, Callback *queueListener,
|
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) {
|
AsyncWorker(callback), baton(baton), queueListener(queueListener), saveBuffers(saveBuffers) {
|
||||||
if (baton->bufferInLength > 0) {
|
for (const BufferContainer buf : saveBuffers) {
|
||||||
SaveToPersistent("bufferIn", bufferIn);
|
SaveToPersistent(buf.name.c_str(), buf.nodeBuf);
|
||||||
}
|
|
||||||
if (baton->overlayBufferInLength > 0) {
|
|
||||||
SaveToPersistent("overlayBufferIn", overlayBufferIn);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~PipelineWorker() {}
|
~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
|
// Apply per-channel Bandbool bitwise operations after all other operations
|
||||||
if (shouldBandbool) {
|
if (shouldBandbool) {
|
||||||
image = Bandbool(image, baton->bandBoolOp);
|
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
|
// Dispose of Persistent wrapper around input Buffers so they can be garbage collected
|
||||||
if (baton->bufferInLength > 0) {
|
for (const BufferContainer buf : saveBuffers) {
|
||||||
GetFromPersistent("bufferIn");
|
GetFromPersistent(buf.name.c_str());
|
||||||
}
|
|
||||||
if (baton->overlayBufferInLength > 0) {
|
|
||||||
GetFromPersistent("overlayBufferIn");
|
|
||||||
}
|
}
|
||||||
delete baton;
|
delete baton;
|
||||||
|
|
||||||
@ -1028,6 +1070,7 @@ class PipelineWorker : public AsyncWorker {
|
|||||||
private:
|
private:
|
||||||
PipelineBaton *baton;
|
PipelineBaton *baton;
|
||||||
Callback *queueListener;
|
Callback *queueListener;
|
||||||
|
std::vector<BufferContainer> saveBuffers;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Calculate the angle of rotation and need-to-flip for the output image.
|
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->overlayYOffset = attrAs<int32_t>(options, "overlayYOffset");
|
||||||
baton->overlayTile = attrAs<bool>(options, "overlayTile");
|
baton->overlayTile = attrAs<bool>(options, "overlayTile");
|
||||||
baton->overlayCutout = attrAs<bool>(options, "overlayCutout");
|
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
|
// Resize options
|
||||||
baton->withoutEnlargement = attrAs<bool>(options, "withoutEnlargement");
|
baton->withoutEnlargement = attrAs<bool>(options, "withoutEnlargement");
|
||||||
baton->crop = attrAs<int32_t>(options, "crop");
|
baton->crop = attrAs<int32_t>(options, "crop");
|
||||||
@ -1232,14 +1283,10 @@ NAN_METHOD(pipeline) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Bandbool operation
|
// Bandbool operation
|
||||||
std::string opStr = attrAsStr(options, "bandBoolOp");
|
baton->bandBoolOp = GetBooleanOperation(attrAsStr(options, "bandBoolOp"));
|
||||||
if(opStr == "and" ) {
|
|
||||||
baton->bandBoolOp = VIPS_OPERATION_BOOLEAN_AND;
|
// Boolean operation
|
||||||
} else if(opStr == "or") {
|
baton->booleanOp = GetBooleanOperation(attrAsStr(options, "booleanOp"));
|
||||||
baton->bandBoolOp = VIPS_OPERATION_BOOLEAN_OR;
|
|
||||||
} else if(opStr == "eor") {
|
|
||||||
baton->bandBoolOp = VIPS_OPERATION_BOOLEAN_EOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to notify of queue length changes
|
// Function to notify of queue length changes
|
||||||
Callback *queueListener = new Callback(
|
Callback *queueListener = new Callback(
|
||||||
@ -1248,7 +1295,15 @@ NAN_METHOD(pipeline) {
|
|||||||
|
|
||||||
// Join queue for worker thread
|
// Join queue for worker thread
|
||||||
Callback *callback = new Callback(info[1].As<Function>());
|
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
|
// Increment queued task counter
|
||||||
g_atomic_int_inc(&counterQueue);
|
g_atomic_int_inc(&counterQueue);
|
||||||
|
@ -39,6 +39,9 @@ struct PipelineBaton {
|
|||||||
int overlayYOffset;
|
int overlayYOffset;
|
||||||
bool overlayTile;
|
bool overlayTile;
|
||||||
bool overlayCutout;
|
bool overlayCutout;
|
||||||
|
std::string booleanFileIn;
|
||||||
|
char *booleanBufferIn;
|
||||||
|
size_t booleanBufferInLength;
|
||||||
int topOffsetPre;
|
int topOffsetPre;
|
||||||
int leftOffsetPre;
|
int leftOffsetPre;
|
||||||
int widthPre;
|
int widthPre;
|
||||||
@ -94,6 +97,7 @@ struct PipelineBaton {
|
|||||||
double convKernelScale;
|
double convKernelScale;
|
||||||
double convKernelOffset;
|
double convKernelOffset;
|
||||||
VipsOperationBoolean bandBoolOp;
|
VipsOperationBoolean bandBoolOp;
|
||||||
|
VipsOperationBoolean booleanOp;
|
||||||
int extractChannel;
|
int extractChannel;
|
||||||
int tileSize;
|
int tileSize;
|
||||||
int tileOverlap;
|
int tileOverlap;
|
||||||
@ -116,6 +120,7 @@ struct PipelineBaton {
|
|||||||
overlayYOffset(-1),
|
overlayYOffset(-1),
|
||||||
overlayTile(false),
|
overlayTile(false),
|
||||||
overlayCutout(false),
|
overlayCutout(false),
|
||||||
|
booleanBufferInLength(0),
|
||||||
topOffsetPre(-1),
|
topOffsetPre(-1),
|
||||||
topOffsetPost(-1),
|
topOffsetPost(-1),
|
||||||
channels(0),
|
channels(0),
|
||||||
@ -156,6 +161,7 @@ struct PipelineBaton {
|
|||||||
convKernelScale(0.0),
|
convKernelScale(0.0),
|
||||||
convKernelOffset(0.0),
|
convKernelOffset(0.0),
|
||||||
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
||||||
|
booleanOp(VIPS_OPERATION_BOOLEAN_LAST),
|
||||||
extractChannel(-1),
|
extractChannel(-1),
|
||||||
tileSize(256),
|
tileSize(256),
|
||||||
tileOverlap(0),
|
tileOverlap(0),
|
||||||
|
BIN
test/fixtures/booleanTest.jpg
vendored
Normal file
BIN
test/fixtures/booleanTest.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
test/fixtures/expected/boolean_and_result.jpg
vendored
Normal file
BIN
test/fixtures/expected/boolean_and_result.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
test/fixtures/expected/boolean_eor_result.jpg
vendored
Normal file
BIN
test/fixtures/expected/boolean_eor_result.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
BIN
test/fixtures/expected/boolean_or_result.jpg
vendored
Normal file
BIN
test/fixtures/expected/boolean_or_result.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
2
test/fixtures/index.js
vendored
2
test/fixtures/index.js
vendored
@ -95,6 +95,8 @@ module.exports = {
|
|||||||
inputPngStripesV: getPath('stripesV.png'),
|
inputPngStripesV: getPath('stripesV.png'),
|
||||||
inputPngStripesH: getPath('stripesH.png'),
|
inputPngStripesH: getPath('stripesH.png'),
|
||||||
|
|
||||||
|
inputJpgBooleanTest: getPath('booleanTest.jpg'),
|
||||||
|
|
||||||
inputV: getPath('vfile.v'),
|
inputV: getPath('vfile.v'),
|
||||||
|
|
||||||
outputJpg: getPath('output.jpg'),
|
outputJpg: getPath('output.jpg'),
|
||||||
|
103
test/unit/boolean.js
Normal file
103
test/unit/boolean.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var assert = require('assert');
|
||||||
|
var fixtures = require('../fixtures');
|
||||||
|
var sharp = require('../../index');
|
||||||
|
|
||||||
|
describe('Boolean operation between two images', function() {
|
||||||
|
|
||||||
|
it('\'and\' Operation, file', function(done) {
|
||||||
|
sharp(fixtures.inputJpg) //fixtures.inputJpg
|
||||||
|
.resize(320,240)
|
||||||
|
.boolean(fixtures.inputJpgBooleanTest, 'and')
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('boolean_and_result.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('\'and\' Operation, buffer', function(done) {
|
||||||
|
sharp(fixtures.inputJpg) //fixtures.inputJpg
|
||||||
|
.resize(320,240)
|
||||||
|
.boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), sharp.bool.and)
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('boolean_and_result.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('\'or\' Operation, file', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320,240)
|
||||||
|
.boolean(fixtures.inputJpgBooleanTest, sharp.bool.or)
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('boolean_or_result.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('\'or\' Operation, buffer', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320,240)
|
||||||
|
.boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), 'or')
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('boolean_or_result.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('\'eor\' Operation, file', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320,240)
|
||||||
|
.boolean(fixtures.inputJpgBooleanTest, 'eor')
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('boolean_eor_result.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('\'eor\' Operation, buffer', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320,240)
|
||||||
|
.boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), sharp.bool.eor)
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('boolean_eor_result.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid operation', function() {
|
||||||
|
assert.throws(function() {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), 'fail');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid operation, non-string', function() {
|
||||||
|
assert.throws(function() {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if('Invalid buffer input', function() {
|
||||||
|
assert.throws(function() {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320,240)
|
||||||
|
.boolean([],'eor');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user