mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Refactor internal 'resize' to more apt 'pipeline'
Refactor 'composite' C to C++ 'operations'
This commit is contained in:
parent
e2c53b59ce
commit
36be0453dd
@ -3,9 +3,9 @@
|
|||||||
'target_name': 'sharp',
|
'target_name': 'sharp',
|
||||||
'sources': [
|
'sources': [
|
||||||
'src/common.cc',
|
'src/common.cc',
|
||||||
'src/composite.c',
|
|
||||||
'src/metadata.cc',
|
'src/metadata.cc',
|
||||||
'src/resize.cc',
|
'src/operations.cc',
|
||||||
|
'src/pipeline.cc',
|
||||||
'src/sharp.cc',
|
'src/sharp.cc',
|
||||||
'src/utilities.cc'
|
'src/utilities.cc'
|
||||||
],
|
],
|
||||||
|
24
index.js
24
index.js
@ -567,7 +567,7 @@ Sharp.prototype.toFile = function(output, callback) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.options.output = output;
|
this.options.output = output;
|
||||||
return this._sharp(callback);
|
return this._pipeline(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@ -577,7 +577,7 @@ Sharp.prototype.toFile = function(output, callback) {
|
|||||||
Write output to a Buffer
|
Write output to a Buffer
|
||||||
*/
|
*/
|
||||||
Sharp.prototype.toBuffer = function(callback) {
|
Sharp.prototype.toBuffer = function(callback) {
|
||||||
return this._sharp(callback);
|
return this._pipeline(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -640,7 +640,7 @@ Sharp.prototype.toFormat = function(format) {
|
|||||||
Sharp.prototype._read = function() {
|
Sharp.prototype._read = function() {
|
||||||
if (!this.options.streamOut) {
|
if (!this.options.streamOut) {
|
||||||
this.options.streamOut = true;
|
this.options.streamOut = true;
|
||||||
this._sharp();
|
this._pipeline();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -648,18 +648,18 @@ Sharp.prototype._read = function() {
|
|||||||
Invoke the C++ image processing pipeline
|
Invoke the C++ image processing pipeline
|
||||||
Supports callback, stream and promise variants
|
Supports callback, stream and promise variants
|
||||||
*/
|
*/
|
||||||
Sharp.prototype._sharp = function(callback) {
|
Sharp.prototype._pipeline = function(callback) {
|
||||||
var that = this;
|
var that = this;
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
// output=file/buffer
|
// output=file/buffer
|
||||||
if (this.options.streamIn) {
|
if (this.options.streamIn) {
|
||||||
// output=file/buffer, input=stream
|
// output=file/buffer, input=stream
|
||||||
this.on('finish', function() {
|
this.on('finish', function() {
|
||||||
sharp.resize(that.options, callback);
|
sharp.pipeline(that.options, callback);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// output=file/buffer, input=file/buffer
|
// output=file/buffer, input=file/buffer
|
||||||
sharp.resize(this.options, callback);
|
sharp.pipeline(this.options, callback);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
} else if (this.options.streamOut) {
|
} else if (this.options.streamOut) {
|
||||||
@ -667,9 +667,9 @@ Sharp.prototype._sharp = function(callback) {
|
|||||||
if (this.options.streamIn) {
|
if (this.options.streamIn) {
|
||||||
// output=stream, input=stream
|
// output=stream, input=stream
|
||||||
this.on('finish', function() {
|
this.on('finish', function() {
|
||||||
sharp.resize(that.options, function(err, data) {
|
sharp.pipeline(that.options, function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
that.emit('error', new Error(err));
|
that.emit('error', err);
|
||||||
} else {
|
} else {
|
||||||
that.push(data);
|
that.push(data);
|
||||||
}
|
}
|
||||||
@ -678,9 +678,9 @@ Sharp.prototype._sharp = function(callback) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// output=stream, input=file/buffer
|
// output=stream, input=file/buffer
|
||||||
sharp.resize(this.options, function(err, data) {
|
sharp.pipeline(this.options, function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
that.emit('error', new Error(err));
|
that.emit('error', err);
|
||||||
} else {
|
} else {
|
||||||
that.push(data);
|
that.push(data);
|
||||||
}
|
}
|
||||||
@ -694,7 +694,7 @@ Sharp.prototype._sharp = function(callback) {
|
|||||||
// output=promise, input=stream
|
// output=promise, input=stream
|
||||||
return new BluebirdPromise(function(resolve, reject) {
|
return new BluebirdPromise(function(resolve, reject) {
|
||||||
that.on('finish', function() {
|
that.on('finish', function() {
|
||||||
sharp.resize(that.options, function(err, data) {
|
sharp.pipeline(that.options, function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@ -706,7 +706,7 @@ Sharp.prototype._sharp = function(callback) {
|
|||||||
} else {
|
} else {
|
||||||
// output=promise, input=file/buffer
|
// output=promise, input=file/buffer
|
||||||
return new BluebirdPromise(function(resolve, reject) {
|
return new BluebirdPromise(function(resolve, reject) {
|
||||||
sharp.resize(that.options, function(err, data) {
|
sharp.pipeline(that.options, function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
|
212
src/composite.c
212
src/composite.c
@ -1,212 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <vips/vips.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
const int ALPHA_BAND_INDEX = 3;
|
|
||||||
const int NUM_COLOR_BANDS = 3;
|
|
||||||
|
|
||||||
// TODO: Copied from `common.cc`. Deduplicate once this becomes a C++ module.
|
|
||||||
int HasAlpha(VipsImage *image) {
|
|
||||||
return (
|
|
||||||
(image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) ||
|
|
||||||
(image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) ||
|
|
||||||
(image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Composite images `src` and `dst` with premultiplied alpha channel and output
|
|
||||||
image with premultiplied alpha.
|
|
||||||
*/
|
|
||||||
int Composite(VipsObject *context, VipsImage *srcPremultiplied, VipsImage *dstPremultiplied, VipsImage **outPremultiplied) {
|
|
||||||
if (srcPremultiplied->Bands != 4 || dstPremultiplied->Bands != 4)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Extract RGB bands:
|
|
||||||
VipsImage *srcRGBPremultiplied;
|
|
||||||
if (vips_extract_band(srcPremultiplied, &srcRGBPremultiplied, 0, "n", NUM_COLOR_BANDS, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, srcRGBPremultiplied);
|
|
||||||
|
|
||||||
VipsImage *dstRGBPremultiplied;
|
|
||||||
if (vips_extract_band(dstPremultiplied, &dstRGBPremultiplied, 0, "n", NUM_COLOR_BANDS, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, dstRGBPremultiplied);
|
|
||||||
|
|
||||||
// Extract alpha bands:
|
|
||||||
VipsImage *srcAlpha;
|
|
||||||
if (vips_extract_band(srcPremultiplied, &srcAlpha, ALPHA_BAND_INDEX, "n", 1, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, srcAlpha);
|
|
||||||
|
|
||||||
VipsImage *dstAlpha;
|
|
||||||
if (vips_extract_band(dstPremultiplied, &dstAlpha, ALPHA_BAND_INDEX, "n", 1, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, dstAlpha);
|
|
||||||
|
|
||||||
// Compute normalized input alpha channels:
|
|
||||||
VipsImage *srcAlphaNormalized;
|
|
||||||
if (vips_linear1(srcAlpha, &srcAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, srcAlphaNormalized);
|
|
||||||
|
|
||||||
VipsImage *dstAlphaNormalized;
|
|
||||||
if (vips_linear1(dstAlpha, &dstAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, dstAlphaNormalized);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compute normalized output alpha channel:
|
|
||||||
//
|
|
||||||
// References:
|
|
||||||
// - http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
|
|
||||||
// - https://github.com/jcupitt/ruby-vips/issues/28#issuecomment-9014826
|
|
||||||
//
|
|
||||||
// out_a = src_a + dst_a * (1 - src_a)
|
|
||||||
// ^^^^^^^^^^^
|
|
||||||
// t0
|
|
||||||
// ^^^^^^^^^^^^^^^^^^^
|
|
||||||
// t1
|
|
||||||
VipsImage *t0;
|
|
||||||
if (vips_linear1(srcAlphaNormalized, &t0, -1.0, 1.0, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, t0);
|
|
||||||
|
|
||||||
VipsImage *t1;
|
|
||||||
if (vips_multiply(dstAlphaNormalized, t0, &t1, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, t1);
|
|
||||||
|
|
||||||
VipsImage *outAlphaNormalized;
|
|
||||||
if (vips_add(srcAlphaNormalized, t1, &outAlphaNormalized, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, outAlphaNormalized);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Compute output RGB channels:
|
|
||||||
//
|
|
||||||
// Wikipedia:
|
|
||||||
// out_rgb = (src_rgb * src_a + dst_rgb * dst_a * (1 - src_a)) / out_a
|
|
||||||
// ^^^^^^^^^^^
|
|
||||||
// t0
|
|
||||||
//
|
|
||||||
// Omit division by `out_a` since `Compose` is supposed to output a
|
|
||||||
// premultiplied RGBA image as reversal of premultiplication is handled
|
|
||||||
// externally.
|
|
||||||
//
|
|
||||||
VipsImage *t2;
|
|
||||||
if (vips_multiply(dstRGBPremultiplied, t0, &t2, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, t2);
|
|
||||||
|
|
||||||
VipsImage *outRGBPremultiplied;
|
|
||||||
if (vips_add(srcRGBPremultiplied, t2, &outRGBPremultiplied, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, outRGBPremultiplied);
|
|
||||||
|
|
||||||
// Denormalize output alpha channel:
|
|
||||||
VipsImage *outAlpha;
|
|
||||||
if (vips_linear1(outAlphaNormalized, &outAlpha, 255.0, 0.0, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, outAlpha);
|
|
||||||
|
|
||||||
// Combine RGB and alpha channel into output image:
|
|
||||||
VipsImage *joined;
|
|
||||||
if (vips_bandjoin2(outRGBPremultiplied, outAlpha, &joined, NULL))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Return a reference to the composited output image:
|
|
||||||
*outPremultiplied = joined;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Premultiply alpha channel of `image`.
|
|
||||||
*/
|
|
||||||
int Premultiply(VipsObject *context, VipsImage *image, VipsImage **out) {
|
|
||||||
VipsImage *imagePremultiplied;
|
|
||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION >= 9 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 1))
|
|
||||||
|
|
||||||
if (vips_premultiply(image, &imagePremultiplied, NULL))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
VipsImage *imageRGB;
|
|
||||||
if (vips_extract_band(image, &imageRGB, 0, "n", NUM_COLOR_BANDS, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, imageRGB);
|
|
||||||
|
|
||||||
VipsImage *imageAlpha;
|
|
||||||
if (vips_extract_band(image, &imageAlpha, ALPHA_BAND_INDEX, "n", 1, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, imageAlpha);
|
|
||||||
|
|
||||||
VipsImage *imageAlphaNormalized;
|
|
||||||
if (vips_linear1(imageAlpha, &imageAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, imageAlphaNormalized);
|
|
||||||
|
|
||||||
VipsImage *imageRGBPremultiplied;
|
|
||||||
if (vips_multiply(imageRGB, imageAlphaNormalized, &imageRGBPremultiplied, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, imageRGBPremultiplied);
|
|
||||||
|
|
||||||
if (vips_bandjoin2(imageRGBPremultiplied, imageAlpha, &imagePremultiplied, NULL))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Return a reference to the premultiplied output image:
|
|
||||||
*out = imagePremultiplied;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Unpremultiply alpha channel of `image`.
|
|
||||||
*/
|
|
||||||
int Unpremultiply(VipsObject *context, VipsImage *image, VipsImage **out) {
|
|
||||||
VipsImage *imageUnpremultiplied;
|
|
||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION >= 9 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 1))
|
|
||||||
|
|
||||||
if (vips_unpremultiply(image, &imageUnpremultiplied, NULL))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
VipsImage *imageRGBPremultipliedTransformed;
|
|
||||||
if (vips_extract_band(image, &imageRGBPremultipliedTransformed, 0, "n", NUM_COLOR_BANDS, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, imageRGBPremultipliedTransformed);
|
|
||||||
|
|
||||||
VipsImage *imageAlphaTransformed;
|
|
||||||
if (vips_extract_band(image, &imageAlphaTransformed, ALPHA_BAND_INDEX, "n", 1, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, imageAlphaTransformed);
|
|
||||||
|
|
||||||
VipsImage *imageAlphaNormalizedTransformed;
|
|
||||||
if (vips_linear1(imageAlphaTransformed, &imageAlphaNormalizedTransformed, 1.0 / 255.0, 0.0, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, imageAlphaNormalizedTransformed);
|
|
||||||
|
|
||||||
VipsImage *imageRGBUnpremultipliedTransformed;
|
|
||||||
if (vips_divide(imageRGBPremultipliedTransformed, imageAlphaNormalizedTransformed, &imageRGBUnpremultipliedTransformed, NULL))
|
|
||||||
return -1;
|
|
||||||
vips_object_local(context, imageRGBUnpremultipliedTransformed);
|
|
||||||
|
|
||||||
if (vips_bandjoin2(imageRGBUnpremultipliedTransformed, imageAlphaTransformed, &imageUnpremultiplied, NULL))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Return a reference to the unpremultiplied output image:
|
|
||||||
*out = imageUnpremultiplied;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
#ifndef SRC_COMPOSITE_H_
|
|
||||||
#define SRC_COMPOSITE_H_
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out);
|
|
||||||
int Premultiply(VipsObject *context, VipsImage *image, VipsImage **out);
|
|
||||||
int Unpremultiply(VipsObject *context, VipsImage *image, VipsImage **out);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // SRC_COMPOSITE_H_
|
|
191
src/operations.cc
Executable file
191
src/operations.cc
Executable file
@ -0,0 +1,191 @@
|
|||||||
|
#include <vips/vips.h>
|
||||||
|
|
||||||
|
#include "operations.h"
|
||||||
|
|
||||||
|
namespace sharp {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Composite images `src` and `dst` with premultiplied alpha channel and output
|
||||||
|
image with premultiplied alpha.
|
||||||
|
*/
|
||||||
|
int Composite(VipsObject *context, VipsImage *srcPremultiplied, VipsImage *dstPremultiplied, VipsImage **outPremultiplied) {
|
||||||
|
if (srcPremultiplied->Bands != 4 || dstPremultiplied->Bands != 4)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Extract RGB bands:
|
||||||
|
VipsImage *srcRGBPremultiplied;
|
||||||
|
if (vips_extract_band(srcPremultiplied, &srcRGBPremultiplied, 0, "n", srcPremultiplied->Bands - 1, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, srcRGBPremultiplied);
|
||||||
|
|
||||||
|
VipsImage *dstRGBPremultiplied;
|
||||||
|
if (vips_extract_band(dstPremultiplied, &dstRGBPremultiplied, 0, "n", dstPremultiplied->Bands - 1, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, dstRGBPremultiplied);
|
||||||
|
|
||||||
|
// Extract alpha bands:
|
||||||
|
VipsImage *srcAlpha;
|
||||||
|
if (vips_extract_band(srcPremultiplied, &srcAlpha, srcPremultiplied->Bands - 1, "n", 1, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, srcAlpha);
|
||||||
|
|
||||||
|
VipsImage *dstAlpha;
|
||||||
|
if (vips_extract_band(dstPremultiplied, &dstAlpha, dstPremultiplied->Bands - 1, "n", 1, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, dstAlpha);
|
||||||
|
|
||||||
|
// Compute normalized input alpha channels:
|
||||||
|
VipsImage *srcAlphaNormalized;
|
||||||
|
if (vips_linear1(srcAlpha, &srcAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, srcAlphaNormalized);
|
||||||
|
|
||||||
|
VipsImage *dstAlphaNormalized;
|
||||||
|
if (vips_linear1(dstAlpha, &dstAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, dstAlphaNormalized);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compute normalized output alpha channel:
|
||||||
|
//
|
||||||
|
// References:
|
||||||
|
// - http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
|
||||||
|
// - https://github.com/jcupitt/ruby-vips/issues/28#issuecomment-9014826
|
||||||
|
//
|
||||||
|
// out_a = src_a + dst_a * (1 - src_a)
|
||||||
|
// ^^^^^^^^^^^
|
||||||
|
// t0
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^
|
||||||
|
// t1
|
||||||
|
VipsImage *t0;
|
||||||
|
if (vips_linear1(srcAlphaNormalized, &t0, -1.0, 1.0, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, t0);
|
||||||
|
|
||||||
|
VipsImage *t1;
|
||||||
|
if (vips_multiply(dstAlphaNormalized, t0, &t1, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, t1);
|
||||||
|
|
||||||
|
VipsImage *outAlphaNormalized;
|
||||||
|
if (vips_add(srcAlphaNormalized, t1, &outAlphaNormalized, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, outAlphaNormalized);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compute output RGB channels:
|
||||||
|
//
|
||||||
|
// Wikipedia:
|
||||||
|
// out_rgb = (src_rgb * src_a + dst_rgb * dst_a * (1 - src_a)) / out_a
|
||||||
|
// ^^^^^^^^^^^
|
||||||
|
// t0
|
||||||
|
//
|
||||||
|
// Omit division by `out_a` since `Compose` is supposed to output a
|
||||||
|
// premultiplied RGBA image as reversal of premultiplication is handled
|
||||||
|
// externally.
|
||||||
|
//
|
||||||
|
VipsImage *t2;
|
||||||
|
if (vips_multiply(dstRGBPremultiplied, t0, &t2, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, t2);
|
||||||
|
|
||||||
|
VipsImage *outRGBPremultiplied;
|
||||||
|
if (vips_add(srcRGBPremultiplied, t2, &outRGBPremultiplied, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, outRGBPremultiplied);
|
||||||
|
|
||||||
|
// Denormalize output alpha channel:
|
||||||
|
VipsImage *outAlpha;
|
||||||
|
if (vips_linear1(outAlphaNormalized, &outAlpha, 255.0, 0.0, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, outAlpha);
|
||||||
|
|
||||||
|
// Combine RGB and alpha channel into output image:
|
||||||
|
VipsImage *joined;
|
||||||
|
if (vips_bandjoin2(outRGBPremultiplied, outAlpha, &joined, NULL))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Return a reference to the composited output image
|
||||||
|
*outPremultiplied = joined;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Premultiply alpha channel of `image`.
|
||||||
|
*/
|
||||||
|
int Premultiply(VipsObject *context, VipsImage *image, VipsImage **out) {
|
||||||
|
VipsImage *imagePremultiplied;
|
||||||
|
|
||||||
|
#if (VIPS_MAJOR_VERSION >= 9 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 1))
|
||||||
|
if (vips_premultiply(image, &imagePremultiplied, NULL))
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
VipsImage *imageRGB;
|
||||||
|
if (vips_extract_band(image, &imageRGB, 0, "n", image->Bands - 1, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, imageRGB);
|
||||||
|
|
||||||
|
VipsImage *imageAlpha;
|
||||||
|
if (vips_extract_band(image, &imageAlpha, image->Bands - 1, "n", 1, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, imageAlpha);
|
||||||
|
|
||||||
|
VipsImage *imageAlphaNormalized;
|
||||||
|
if (vips_linear1(imageAlpha, &imageAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, imageAlphaNormalized);
|
||||||
|
|
||||||
|
VipsImage *imageRGBPremultiplied;
|
||||||
|
if (vips_multiply(imageRGB, imageAlphaNormalized, &imageRGBPremultiplied, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, imageRGBPremultiplied);
|
||||||
|
|
||||||
|
if (vips_bandjoin2(imageRGBPremultiplied, imageAlpha, &imagePremultiplied, NULL))
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Return a reference to the premultiplied output image
|
||||||
|
*out = imagePremultiplied;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unpremultiply alpha channel of `image`.
|
||||||
|
*/
|
||||||
|
int Unpremultiply(VipsObject *context, VipsImage *image, VipsImage **out) {
|
||||||
|
VipsImage *imageUnpremultiplied;
|
||||||
|
|
||||||
|
#if (VIPS_MAJOR_VERSION >= 9 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 1))
|
||||||
|
if (vips_unpremultiply(image, &imageUnpremultiplied, NULL))
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
VipsImage *imageRGBPremultipliedTransformed;
|
||||||
|
if (vips_extract_band(image, &imageRGBPremultipliedTransformed, 0, "n", image->Bands - 1, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, imageRGBPremultipliedTransformed);
|
||||||
|
|
||||||
|
VipsImage *imageAlphaTransformed;
|
||||||
|
if (vips_extract_band(image, &imageAlphaTransformed, image->Bands - 1, "n", 1, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, imageAlphaTransformed);
|
||||||
|
|
||||||
|
VipsImage *imageAlphaNormalizedTransformed;
|
||||||
|
if (vips_linear1(imageAlphaTransformed, &imageAlphaNormalizedTransformed, 1.0 / 255.0, 0.0, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, imageAlphaNormalizedTransformed);
|
||||||
|
|
||||||
|
VipsImage *imageRGBUnpremultipliedTransformed;
|
||||||
|
if (vips_divide(imageRGBPremultipliedTransformed, imageAlphaNormalizedTransformed, &imageRGBUnpremultipliedTransformed, NULL))
|
||||||
|
return -1;
|
||||||
|
vips_object_local(context, imageRGBUnpremultipliedTransformed);
|
||||||
|
|
||||||
|
if (vips_bandjoin2(imageRGBUnpremultipliedTransformed, imageAlphaTransformed, &imageUnpremultiplied, NULL))
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Return a reference to the unpremultiplied output image
|
||||||
|
*out = imageUnpremultiplied;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sharp
|
24
src/operations.h
Executable file
24
src/operations.h
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef SRC_OPERATIONS_H_
|
||||||
|
#define SRC_OPERATIONS_H_
|
||||||
|
|
||||||
|
namespace sharp {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Composite images `src` and `dst` with premultiplied alpha channel and output
|
||||||
|
image with premultiplied alpha.
|
||||||
|
*/
|
||||||
|
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Premultiply alpha channel of `image`.
|
||||||
|
*/
|
||||||
|
int Premultiply(VipsObject *context, VipsImage *image, VipsImage **out);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unpremultiply alpha channel of `image`.
|
||||||
|
*/
|
||||||
|
int Unpremultiply(VipsObject *context, VipsImage *image, VipsImage **out);
|
||||||
|
|
||||||
|
} // namespace sharp
|
||||||
|
|
||||||
|
#endif // SRC_OPERATIONS_H_
|
@ -8,8 +8,8 @@
|
|||||||
#include "nan.h"
|
#include "nan.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "composite.h"
|
#include "operations.h"
|
||||||
#include "resize.h"
|
#include "pipeline.h"
|
||||||
|
|
||||||
using v8::Handle;
|
using v8::Handle;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
@ -22,6 +22,10 @@ using v8::Array;
|
|||||||
using v8::Function;
|
using v8::Function;
|
||||||
using v8::Exception;
|
using v8::Exception;
|
||||||
|
|
||||||
|
using sharp::Composite;
|
||||||
|
using sharp::Premultiply;
|
||||||
|
using sharp::Unpremultiply;
|
||||||
|
|
||||||
using sharp::ImageType;
|
using sharp::ImageType;
|
||||||
using sharp::DetermineImageType;
|
using sharp::DetermineImageType;
|
||||||
using sharp::InitImage;
|
using sharp::InitImage;
|
||||||
@ -53,7 +57,7 @@ enum class Angle {
|
|||||||
DLAST
|
DLAST
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResizeBaton {
|
struct PipelineBaton {
|
||||||
std::string fileIn;
|
std::string fileIn;
|
||||||
char *bufferIn;
|
char *bufferIn;
|
||||||
size_t bufferInLength;
|
size_t bufferInLength;
|
||||||
@ -105,7 +109,7 @@ struct ResizeBaton {
|
|||||||
int tileSize;
|
int tileSize;
|
||||||
int tileOverlap;
|
int tileOverlap;
|
||||||
|
|
||||||
ResizeBaton():
|
PipelineBaton():
|
||||||
bufferInLength(0),
|
bufferInLength(0),
|
||||||
limitInputPixels(0),
|
limitInputPixels(0),
|
||||||
outputFormat(""),
|
outputFormat(""),
|
||||||
@ -154,12 +158,12 @@ static void DeleteBuffer(VipsObject *object, char *buffer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResizeWorker : public NanAsyncWorker {
|
class PipelineWorker : public NanAsyncWorker {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ResizeWorker(NanCallback *callback, ResizeBaton *baton, NanCallback *queueListener) :
|
PipelineWorker(NanCallback *callback, PipelineBaton *baton, NanCallback *queueListener) :
|
||||||
NanAsyncWorker(callback), baton(baton), queueListener(queueListener) {}
|
NanAsyncWorker(callback), baton(baton), queueListener(queueListener) {}
|
||||||
~ResizeWorker() {}
|
~PipelineWorker() {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
libuv worker
|
libuv worker
|
||||||
@ -1099,7 +1103,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ResizeBaton *baton;
|
PipelineBaton *baton;
|
||||||
NanCallback *queueListener;
|
NanCallback *queueListener;
|
||||||
VipsObject *hook;
|
VipsObject *hook;
|
||||||
|
|
||||||
@ -1207,13 +1211,13 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
resize(options, output, callback)
|
pipeline(options, output, callback)
|
||||||
*/
|
*/
|
||||||
NAN_METHOD(resize) {
|
NAN_METHOD(pipeline) {
|
||||||
NanScope();
|
NanScope();
|
||||||
|
|
||||||
// V8 objects are converted to non-V8 types held in the baton struct
|
// V8 objects are converted to non-V8 types held in the baton struct
|
||||||
ResizeBaton *baton = new ResizeBaton;
|
PipelineBaton *baton = new PipelineBaton;
|
||||||
Local<Object> options = args[0]->ToObject();
|
Local<Object> options = args[0]->ToObject();
|
||||||
|
|
||||||
// Input filename
|
// Input filename
|
||||||
@ -1300,7 +1304,7 @@ NAN_METHOD(resize) {
|
|||||||
|
|
||||||
// Join queue for worker thread
|
// Join queue for worker thread
|
||||||
NanCallback *callback = new NanCallback(args[1].As<Function>());
|
NanCallback *callback = new NanCallback(args[1].As<Function>());
|
||||||
NanAsyncQueueWorker(new ResizeWorker(callback, baton, queueListener));
|
NanAsyncQueueWorker(new PipelineWorker(callback, baton, queueListener));
|
||||||
|
|
||||||
// Increment queued task counter
|
// Increment queued task counter
|
||||||
g_atomic_int_inc(&counterQueue);
|
g_atomic_int_inc(&counterQueue);
|
8
src/pipeline.h
Executable file
8
src/pipeline.h
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef SRC_PIPELINE_H_
|
||||||
|
#define SRC_PIPELINE_H_
|
||||||
|
|
||||||
|
#include "nan.h"
|
||||||
|
|
||||||
|
NAN_METHOD(pipeline);
|
||||||
|
|
||||||
|
#endif // SRC_PIPELINE_H_
|
@ -1,8 +0,0 @@
|
|||||||
#ifndef SRC_RESIZE_H_
|
|
||||||
#define SRC_RESIZE_H_
|
|
||||||
|
|
||||||
#include "nan.h"
|
|
||||||
|
|
||||||
NAN_METHOD(resize);
|
|
||||||
|
|
||||||
#endif // SRC_RESIZE_H_
|
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
#include "resize.h"
|
#include "pipeline.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
extern "C" void init(v8::Handle<v8::Object> target) {
|
extern "C" void init(v8::Handle<v8::Object> target) {
|
||||||
@ -18,7 +18,7 @@ extern "C" void init(v8::Handle<v8::Object> target) {
|
|||||||
|
|
||||||
// Methods available to JavaScript
|
// Methods available to JavaScript
|
||||||
NODE_SET_METHOD(target, "metadata", metadata);
|
NODE_SET_METHOD(target, "metadata", metadata);
|
||||||
NODE_SET_METHOD(target, "resize", resize);
|
NODE_SET_METHOD(target, "pipeline", pipeline);
|
||||||
NODE_SET_METHOD(target, "cache", cache);
|
NODE_SET_METHOD(target, "cache", cache);
|
||||||
NODE_SET_METHOD(target, "concurrency", concurrency);
|
NODE_SET_METHOD(target, "concurrency", concurrency);
|
||||||
NODE_SET_METHOD(target, "counters", counters);
|
NODE_SET_METHOD(target, "counters", counters);
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#include "nan.h"
|
#include "nan.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "operations.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "composite.h"
|
|
||||||
|
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
@ -13,13 +13,6 @@ using v8::Number;
|
|||||||
using v8::String;
|
using v8::String;
|
||||||
using v8::Boolean;
|
using v8::Boolean;
|
||||||
|
|
||||||
using sharp::DetermineImageType;
|
|
||||||
using sharp::ImageType;
|
|
||||||
using sharp::InitImage;
|
|
||||||
using sharp::HasAlpha;
|
|
||||||
using sharp::counterQueue;
|
|
||||||
using sharp::counterProcess;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get and set cache memory and item limits
|
Get and set cache memory and item limits
|
||||||
*/
|
*/
|
||||||
@ -68,6 +61,9 @@ NAN_METHOD(concurrency) {
|
|||||||
Get internal counters (queued tasks, processing tasks)
|
Get internal counters (queued tasks, processing tasks)
|
||||||
*/
|
*/
|
||||||
NAN_METHOD(counters) {
|
NAN_METHOD(counters) {
|
||||||
|
using sharp::counterProcess;
|
||||||
|
using sharp::counterQueue;
|
||||||
|
|
||||||
NanScope();
|
NanScope();
|
||||||
Local<Object> counters = NanNew<Object>();
|
Local<Object> counters = NanNew<Object>();
|
||||||
counters->Set(NanNew<String>("queue"), NanNew<Number>(counterQueue));
|
counters->Set(NanNew<String>("queue"), NanNew<Number>(counterQueue));
|
||||||
@ -154,6 +150,12 @@ NAN_METHOD(format) {
|
|||||||
between two images of the same dimensions and number of channels.
|
between two images of the same dimensions and number of channels.
|
||||||
*/
|
*/
|
||||||
NAN_METHOD(_maxColourDistance) {
|
NAN_METHOD(_maxColourDistance) {
|
||||||
|
using sharp::Premultiply;
|
||||||
|
using sharp::DetermineImageType;
|
||||||
|
using sharp::ImageType;
|
||||||
|
using sharp::InitImage;
|
||||||
|
using sharp::HasAlpha;
|
||||||
|
|
||||||
NanScope();
|
NanScope();
|
||||||
|
|
||||||
// Create "hook" VipsObject to hang image references from
|
// Create "hook" VipsObject to hang image references from
|
||||||
|
Loading…
x
Reference in New Issue
Block a user