Less C, more C++ e.g. namespace, enum class

Improve image reference handling
This commit is contained in:
Lovell Fuller 2014-11-11 18:28:23 +00:00
parent e465306d97
commit ee513ac7a7
5 changed files with 354 additions and 335 deletions

View File

@ -4,127 +4,161 @@
#include "common.h" #include "common.h"
// How many tasks are in the queue? namespace sharp {
volatile int counter_queue = 0;
// How many tasks are being processed? // How many tasks are in the queue?
volatile int counter_process = 0; volatile int counterQueue = 0;
// Filename extension checkers // How many tasks are being processed?
static bool ends_with(std::string const &str, std::string const &end) { volatile int counterProcess = 0;
// Filename extension checkers
static bool EndsWith(std::string const &str, std::string const &end) {
return str.length() >= end.length() && 0 == str.compare(str.length() - end.length(), end.length(), end); return str.length() >= end.length() && 0 == str.compare(str.length() - end.length(), end.length(), end);
} }
bool is_jpeg(std::string const &str) { bool IsJpeg(std::string const &str) {
return ends_with(str, ".jpg") || ends_with(str, ".jpeg") || ends_with(str, ".JPG") || ends_with(str, ".JPEG"); return EndsWith(str, ".jpg") || EndsWith(str, ".jpeg") || EndsWith(str, ".JPG") || EndsWith(str, ".JPEG");
} }
bool is_png(std::string const &str) { bool IsPng(std::string const &str) {
return ends_with(str, ".png") || ends_with(str, ".PNG"); return EndsWith(str, ".png") || EndsWith(str, ".PNG");
} }
bool is_webp(std::string const &str) { bool IsWebp(std::string const &str) {
return ends_with(str, ".webp") || ends_with(str, ".WEBP"); return EndsWith(str, ".webp") || EndsWith(str, ".WEBP");
} }
bool is_tiff(std::string const &str) { bool IsTiff(std::string const &str) {
return ends_with(str, ".tif") || ends_with(str, ".tiff") || ends_with(str, ".TIF") || ends_with(str, ".TIFF"); return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
} }
// Buffer content checkers // Buffer content checkers
unsigned char const MARKER_JPEG[] = {0xff, 0xd8};
unsigned char const MARKER_PNG[] = {0x89, 0x50};
unsigned char const MARKER_WEBP[] = {0x52, 0x49};
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40) #if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40)
static bool buffer_is_tiff(char *buffer, size_t len) { static bool buffer_is_tiff(char *buffer, size_t len) {
return ( return (
len >= 4 && ( len >= 4 && (
(buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == '\0' && (buffer[3] == '*' || buffer[3] == '+')) || (buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == '\0' && (buffer[3] == '*' || buffer[3] == '+')) ||
(buffer[0] == 'I' && buffer[1] == 'I' && (buffer[2] == '*' || buffer[2] == '+') && buffer[3] == '\0') (buffer[0] == 'I' && buffer[1] == 'I' && (buffer[2] == '*' || buffer[2] == '+') && buffer[3] == '\0')
) )
); );
} }
#endif #endif
unsigned char const MARKER_JPEG[] = {0xff, 0xd8}; /*
unsigned char const MARKER_PNG[] = {0x89, 0x50}; Determine image format of a buffer.
unsigned char const MARKER_WEBP[] = {0x52, 0x49}; */
ImageType DetermineImageType(void *buffer, size_t const length) {
/* ImageType imageType = ImageType::UNKNOWN;
Initialise a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF. if (length >= 4) {
Returns the ImageType detected, if any.
*/
ImageType
sharp_init_image_from_buffer(VipsImage **image, void *buffer, size_t const length, VipsAccess const access) {
ImageType imageType = UNKNOWN;
if (memcmp(MARKER_JPEG, buffer, 2) == 0) { if (memcmp(MARKER_JPEG, buffer, 2) == 0) {
if (!vips_jpegload_buffer(buffer, length, image, "access", access, NULL)) { imageType = ImageType::JPEG;
imageType = JPEG;
}
} else if (memcmp(MARKER_PNG, buffer, 2) == 0) { } else if (memcmp(MARKER_PNG, buffer, 2) == 0) {
if (!vips_pngload_buffer(buffer, length, image, "access", access, NULL)) { imageType = ImageType::PNG;
imageType = PNG;
}
} else if (memcmp(MARKER_WEBP, buffer, 2) == 0) { } else if (memcmp(MARKER_WEBP, buffer, 2) == 0) {
if (!vips_webpload_buffer(buffer, length, image, "access", access, NULL)) { imageType = ImageType::WEBP;
imageType = WEBP;
}
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40) #if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40)
} else if (buffer_is_tiff(static_cast<char*>(buffer), length)) { } else if (buffer_is_tiff(static_cast<char*>(buffer), length)) {
if (!vips_tiffload_buffer(buffer, length, image, "access", access, NULL)) { imageType = ImageType::TIFF;
imageType = TIFF;
}
#endif #endif
} }
}
return imageType; return imageType;
} }
/* /*
Initialise a VipsImage from a file. Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
Returns the ImageType detected, if any. */
*/ VipsImage* InitImage(ImageType imageType, void *buffer, size_t const length, VipsAccess const access) {
ImageType VipsImage *image = NULL;
sharp_init_image_from_file(VipsImage **image, char const *file, VipsAccess const access) { if (imageType == ImageType::JPEG) {
ImageType imageType = UNKNOWN; vips_jpegload_buffer(buffer, length, &image, "access", access, NULL);
} else if (imageType == ImageType::PNG) {
vips_pngload_buffer(buffer, length, &image, "access", access, NULL);
} else if (imageType == ImageType::WEBP) {
vips_webpload_buffer(buffer, length, &image, "access", access, NULL);
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40)
} else if (imageType == ImageType::TIFF) {
vips_tiffload_buffer(buffer, length, &image, "access", access, NULL);
#endif
}
return image;
}
/*
Inpect the first 2-4 bytes of a file to determine image format
*/
ImageType DetermineImageType(char const *file) {
ImageType imageType = ImageType::UNKNOWN;
if (vips_foreign_is_a("jpegload", file)) { if (vips_foreign_is_a("jpegload", file)) {
if (!vips_jpegload(file, image, "access", access, NULL)) { imageType = ImageType::JPEG;
imageType = JPEG;
}
} else if (vips_foreign_is_a("pngload", file)) { } else if (vips_foreign_is_a("pngload", file)) {
if (!vips_pngload(file, image, "access", access, NULL)) { imageType = ImageType::PNG;
imageType = PNG;
}
} else if (vips_foreign_is_a("webpload", file)) { } else if (vips_foreign_is_a("webpload", file)) {
if (!vips_webpload(file, image, "access", access, NULL)) { imageType = ImageType::WEBP;
imageType = WEBP;
}
} else if (vips_foreign_is_a("tiffload", file)) { } else if (vips_foreign_is_a("tiffload", file)) {
if (!vips_tiffload(file, image, "access", access, NULL)) { imageType = ImageType::TIFF;
imageType = TIFF;
}
} else if(vips_foreign_is_a("magickload", file)) { } else if(vips_foreign_is_a("magickload", file)) {
if (!vips_magickload(file, image, "access", access, NULL)) { imageType = ImageType::MAGICK;
imageType = MAGICK;
}
} }
return imageType; return imageType;
} }
/* /*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(ImageType imageType, char const *file, VipsAccess const access) {
VipsImage *image = NULL;
if (imageType == ImageType::JPEG) {
vips_jpegload(file, &image, "access", access, NULL);
} else if (imageType == ImageType::PNG) {
vips_pngload(file, &image, "access", access, NULL);
} else if (imageType == ImageType::WEBP) {
vips_webpload(file, &image, "access", access, NULL);
} else if (imageType == ImageType::TIFF) {
vips_tiffload(file, &image, "access", access, NULL);
} else if (imageType == ImageType::MAGICK) {
vips_magickload(file, &image, "access", access, NULL);
}
return image;
}
/*
Does this image have an alpha channel? Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this. Uses colour space interpretation with number of channels to guess this.
*/ */
bool bool HasAlpha(VipsImage *image) {
sharp_image_has_alpha(VipsImage *image) {
return ( return (
(image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) || (image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) ||
(image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) || (image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) ||
(image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK) (image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK)
); );
} }
/* /*
Get EXIF Orientation of image, if any.
*/
int ExifOrientation(VipsImage const *image) {
int orientation = 0;
const char *exif;
if (
vips_image_get_typeof(image, "exif-ifd0-Orientation") != 0 &&
!vips_image_get_string(image, "exif-ifd0-Orientation", &exif)
) {
orientation = atoi(&exif[0]);
}
return orientation;
}
/*
Returns the window size for the named interpolator. For example, Returns the window size for the named interpolator. For example,
a window size of 3 means a 3x3 pixel grid is used for the calculation. a window size of 3 means a 3x3 pixel grid is used for the calculation.
*/ */
int int InterpolatorWindowSize(char const *name) {
sharp_interpolator_window_size(char const *name) {
VipsInterpolate *interpolator = vips_interpolate_new(name); VipsInterpolate *interpolator = vips_interpolate_new(name);
int window_size = vips_interpolate_get_window_size(interpolator); int window_size = vips_interpolate_get_window_size(interpolator);
g_object_unref(interpolator); g_object_unref(interpolator);
return window_size; return window_size;
} }
} // namespace

View File

@ -1,53 +1,66 @@
#ifndef SHARP_COMMON_H #ifndef SHARP_COMMON_H
#define SHARP_COMMON_H #define SHARP_COMMON_H
typedef enum { namespace sharp {
enum class ImageType {
UNKNOWN, UNKNOWN,
JPEG, JPEG,
PNG, PNG,
WEBP, WEBP,
TIFF, TIFF,
MAGICK MAGICK
} ImageType; };
// Filename extension checkers // How many tasks are in the queue?
bool is_jpeg(std::string const &str); extern volatile int counterQueue;
bool is_png(std::string const &str);
bool is_webp(std::string const &str);
bool is_tiff(std::string const &str);
// How many tasks are in the queue? // How many tasks are being processed?
extern volatile int counter_queue; extern volatile int counterProcess;
// How many tasks are being processed? // Filename extension checkers
extern volatile int counter_process; bool IsJpeg(std::string const &str);
bool IsPng(std::string const &str);
bool IsWebp(std::string const &str);
bool IsTiff(std::string const &str);
/* /*
Initialise a VipsImage from a buffer. Supports JPEG, PNG and WebP. Determine image format of a buffer.
Returns the ImageType detected, if any. */
*/ ImageType DetermineImageType(void *buffer, size_t const length);
ImageType
sharp_init_image_from_buffer(VipsImage **image, void *buffer, size_t const length, VipsAccess const access);
/* /*
Initialise a VipsImage from a file. Determine image format of a file.
Returns the ImageType detected, if any. */
*/ ImageType DetermineImageType(char const *file);
ImageType
sharp_init_image_from_file(VipsImage **image, char const *file, VipsAccess const access);
/* /*
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
*/
VipsImage* InitImage(ImageType imageType, void *buffer, size_t const length, VipsAccess const access);
/*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(ImageType imageType, char const *file, VipsAccess const access);
/*
Does this image have an alpha channel? Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this. Uses colour space interpretation with number of channels to guess this.
*/ */
bool bool HasAlpha(VipsImage *image);
sharp_image_has_alpha(VipsImage *image);
/* /*
Get EXIF Orientation of image, if any.
*/
int ExifOrientation(VipsImage const *image);
/*
Returns the window size for the named interpolator. For example, Returns the window size for the named interpolator. For example,
a window size of 3 means a 3x3 pixel grid is used for the calculation. a window size of 3 means a 3x3 pixel grid is used for the calculation.
*/ */
int int InterpolatorWindowSize(char const *name);
sharp_interpolator_window_size(char const *name);
} // namespace
#endif #endif

View File

@ -7,6 +7,7 @@
#include "metadata.h" #include "metadata.h"
using namespace v8; using namespace v8;
using namespace sharp;
struct MetadataBaton { struct MetadataBaton {
// Input // Input
@ -36,49 +37,49 @@ class MetadataWorker : public NanAsyncWorker {
void Execute() { void Execute() {
// Decrement queued task counter // Decrement queued task counter
g_atomic_int_dec_and_test(&counter_queue); g_atomic_int_dec_and_test(&counterQueue);
ImageType imageType = UNKNOWN; ImageType imageType = ImageType::UNKNOWN;
VipsImage *image; VipsImage *image = NULL;
if (baton->bufferInLength > 1) { if (baton->bufferInLength > 1) {
// From buffer // From buffer
imageType = sharp_init_image_from_buffer(&image, baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM); imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
if (imageType == UNKNOWN) { if (imageType != ImageType::UNKNOWN) {
image = InitImage(imageType, baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM);
} else {
(baton->err).append("Input buffer contains unsupported image format"); (baton->err).append("Input buffer contains unsupported image format");
} }
} else { } else {
// From file // From file
imageType = sharp_init_image_from_file(&image, baton->fileIn.c_str(), VIPS_ACCESS_RANDOM); imageType = DetermineImageType(baton->fileIn.c_str());
if (imageType == UNKNOWN) { 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"); (baton->err).append("File is of an unsupported image format");
} }
} }
if (imageType != UNKNOWN) { if (image != NULL && imageType != ImageType::UNKNOWN) {
// Image type // Image type
switch (imageType) { switch (imageType) {
case JPEG: baton->format = "jpeg"; break; case ImageType::JPEG: baton->format = "jpeg"; break;
case PNG: baton->format = "png"; break; case ImageType::PNG: baton->format = "png"; break;
case WEBP: baton->format = "webp"; break; case ImageType::WEBP: baton->format = "webp"; break;
case TIFF: baton->format = "tiff"; break; case ImageType::TIFF: baton->format = "tiff"; break;
case MAGICK: baton->format = "magick"; break; case ImageType::MAGICK: baton->format = "magick"; break;
case UNKNOWN: default: baton->format = ""; case ImageType::UNKNOWN: break;
} }
// VipsImage attributes // VipsImage attributes
baton->width = image->Xsize; baton->width = image->Xsize;
baton->height = image->Ysize; baton->height = image->Ysize;
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type); baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
baton->channels = image->Bands; baton->channels = image->Bands;
baton->hasAlpha = sharp_image_has_alpha(image); // Derived attributes
// EXIF Orientation baton->hasAlpha = HasAlpha(image);
const char *exif; baton->orientation = ExifOrientation(image);
if (!vips_image_get_string(image, "exif-ifd0-Orientation", &exif)) { // Drop image reference
baton->orientation = atoi(&exif[0]);
}
}
// Clean up
if (imageType != UNKNOWN) {
g_object_unref(image); g_object_unref(image);
} }
// Clean up
vips_error_clear(); vips_error_clear();
vips_thread_shutdown(); vips_thread_shutdown();
} }
@ -138,7 +139,7 @@ NAN_METHOD(metadata) {
NanAsyncQueueWorker(new MetadataWorker(callback, baton)); NanAsyncQueueWorker(new MetadataWorker(callback, baton));
// Increment queued task counter // Increment queued task counter
g_atomic_int_inc(&counter_queue); g_atomic_int_inc(&counterQueue);
NanReturnUndefined(); NanReturnUndefined();
} }

View File

@ -11,20 +11,21 @@
#include "resize.h" #include "resize.h"
using namespace v8; using namespace v8;
using namespace sharp;
typedef enum { enum class Canvas {
CROP, CROP,
MAX, MAX,
EMBED EMBED
} Canvas; };
typedef enum { enum class Angle {
ANGLE_0, D0,
ANGLE_90, D90,
ANGLE_180, D180,
ANGLE_270, D270,
ANGLE_LAST DLAST
} Angle; };
struct ResizeBaton { struct ResizeBaton {
std::string fileIn; std::string fileIn;
@ -74,7 +75,7 @@ struct ResizeBaton {
bufferOutLength(0), bufferOutLength(0),
topOffsetPre(-1), topOffsetPre(-1),
topOffsetPost(-1), topOffsetPost(-1),
canvas(CROP), canvas(Canvas::CROP),
gravity(0), gravity(0),
flatten(false), flatten(false),
blurRadius(0), blurRadius(0),
@ -83,10 +84,14 @@ struct ResizeBaton {
sharpenJagged(2.0), sharpenJagged(2.0),
gamma(0.0), gamma(0.0),
greyscale(false), greyscale(false),
angle(0),
flip(false), flip(false),
flop(false), flop(false),
progressive(false), progressive(false),
withoutEnlargement(false), withoutEnlargement(false),
quality(80),
compressionLevel(6),
withoutAdaptiveFiltering(false),
withMetadata(false) { withMetadata(false) {
background[0] = 0.0; background[0] = 0.0;
background[1] = 0.0; background[1] = 0.0;
@ -106,43 +111,45 @@ class ResizeWorker : public NanAsyncWorker {
*/ */
void Execute() { void Execute() {
// Decrement queued task counter // Decrement queued task counter
g_atomic_int_dec_and_test(&counter_queue); g_atomic_int_dec_and_test(&counterQueue);
// Increment processing task counter // Increment processing task counter
g_atomic_int_inc(&counter_process); g_atomic_int_inc(&counterProcess);
// Hang image references from this hook object // Hang image references from this hook object
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new()); VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
// Input // Input
ImageType inputImageType = UNKNOWN; ImageType inputImageType = ImageType::UNKNOWN;
VipsImage *image = vips_image_new(); VipsImage *image;
vips_object_local(hook, image);
if (baton->bufferInLength > 1) { if (baton->bufferInLength > 1) {
// From buffer // From buffer
inputImageType = sharp_init_image_from_buffer(&image, baton->bufferIn, baton->bufferInLength, baton->accessMethod); inputImageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
if (inputImageType == UNKNOWN) { if (inputImageType != ImageType::UNKNOWN) {
image = InitImage(inputImageType, baton->bufferIn, baton->bufferInLength, baton->accessMethod);
} else {
(baton->err).append("Input buffer contains unsupported image format"); (baton->err).append("Input buffer contains unsupported image format");
} }
} else { } else {
// From file // From file
inputImageType = sharp_init_image_from_file(&image, baton->fileIn.c_str(), baton->accessMethod); inputImageType = DetermineImageType(baton->fileIn.c_str());
if (inputImageType == UNKNOWN) { if (inputImageType != ImageType::UNKNOWN) {
image = InitImage(inputImageType, baton->fileIn.c_str(), baton->accessMethod);
} else {
(baton->err).append("File is of an unsupported image format"); (baton->err).append("File is of an unsupported image format");
} }
} }
if (inputImageType == UNKNOWN) { if (inputImageType == ImageType::UNKNOWN) {
return Error(baton, hook); return Error(baton, hook);
} }
vips_object_local(hook, image);
// Pre extraction // Pre extraction
if (baton->topOffsetPre != -1) { if (baton->topOffsetPre != -1) {
VipsImage *extractedPre = vips_image_new(); VipsImage *extractedPre;
vips_object_local(hook, extractedPre);
if (vips_extract_area(image, &extractedPre, baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, NULL)) { if (vips_extract_area(image, &extractedPre, baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, extractedPre);
image = extractedPre; image = extractedPre;
} }
@ -154,7 +161,7 @@ class ResizeWorker : public NanAsyncWorker {
Angle rotation; Angle rotation;
bool flip; bool flip;
std::tie(rotation, flip) = CalculateRotationAndFlip(baton->angle, image); std::tie(rotation, flip) = CalculateRotationAndFlip(baton->angle, image);
if (rotation == ANGLE_90 || rotation == ANGLE_270) { if (rotation == Angle::D90 || rotation == Angle::D270) {
// Swap input output width and height when rotating by 90 or 270 degrees // Swap input output width and height when rotating by 90 or 270 degrees
int swap = inputWidth; int swap = inputWidth;
inputWidth = inputHeight; inputWidth = inputHeight;
@ -166,7 +173,7 @@ class ResizeWorker : public NanAsyncWorker {
} }
// Get window size of interpolator, used for determining shrink vs affine // Get window size of interpolator, used for determining shrink vs affine
int interpolatorWindowSize = sharp_interpolator_window_size(baton->interpolator.c_str()); int interpolatorWindowSize = InterpolatorWindowSize(baton->interpolator.c_str());
// Scaling calculations // Scaling calculations
double factor; double factor;
@ -174,9 +181,9 @@ class ResizeWorker : public NanAsyncWorker {
// Fixed width and height // Fixed width and height
double xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width); double xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width);
double yfactor = static_cast<double>(inputHeight) / static_cast<double>(baton->height); double yfactor = static_cast<double>(inputHeight) / static_cast<double>(baton->height);
factor = (baton->canvas == CROP) ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor); factor = (baton->canvas == Canvas::CROP) ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor);
// if max is set, we need to compute the real size of the thumb image // if max is set, we need to compute the real size of the thumb image
if (baton->canvas == MAX) { if (baton->canvas == Canvas::MAX) {
if (xfactor > yfactor) { if (xfactor > yfactor) {
baton->height = round(static_cast<double>(inputHeight) / xfactor); baton->height = round(static_cast<double>(inputHeight) / xfactor);
} else { } else {
@ -226,7 +233,7 @@ class ResizeWorker : public NanAsyncWorker {
// Try to use libjpeg shrink-on-load, but not when applying gamma correction or pre-resize extract // Try to use libjpeg shrink-on-load, but not when applying gamma correction or pre-resize extract
int shrink_on_load = 1; int shrink_on_load = 1;
if (inputImageType == JPEG && shrink >= 2 && baton->gamma == 0 && baton->topOffsetPre == -1) { if (inputImageType == ImageType::JPEG && shrink >= 2 && baton->gamma == 0 && baton->topOffsetPre == -1) {
if (shrink >= 8) { if (shrink >= 8) {
factor = factor / 8; factor = factor / 8;
shrink_on_load = 8; shrink_on_load = 8;
@ -248,52 +255,51 @@ class ResizeWorker : public NanAsyncWorker {
} }
residual = static_cast<double>(shrink) / factor; residual = static_cast<double>(shrink) / factor;
// Reload input using shrink-on-load // Reload input using shrink-on-load
g_object_unref(image); VipsImage *shrunkOnLoad;
if (baton->bufferInLength > 1) { if (baton->bufferInLength > 1) {
if (vips_jpegload_buffer(baton->bufferIn, baton->bufferInLength, &image, "shrink", shrink_on_load, NULL)) { if (vips_jpegload_buffer(baton->bufferIn, baton->bufferInLength, &shrunkOnLoad, "shrink", shrink_on_load, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
} else { } else {
if (vips_jpegload((baton->fileIn).c_str(), &image, "shrink", shrink_on_load, NULL)) { if (vips_jpegload((baton->fileIn).c_str(), &shrunkOnLoad, "shrink", shrink_on_load, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
} }
vips_object_local(hook, shrunkOnLoad);
image = shrunkOnLoad;
} }
// Handle colour profile, if any, for non sRGB images // Handle colour profile, if any, for non sRGB images
if (image->Type != VIPS_INTERPRETATION_sRGB) { if (image->Type != VIPS_INTERPRETATION_sRGB) {
// Get the input colour profile // Get the input colour profile
if (vips_image_get_typeof(image, VIPS_META_ICC_NAME)) { if (vips_image_get_typeof(image, VIPS_META_ICC_NAME)) {
VipsImage *profile;
// Use embedded profile // Use embedded profile
VipsImage *profile = vips_image_new();
vips_object_local(hook, profile);
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "embedded", TRUE, NULL)) { if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "embedded", TRUE, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, profile);
image = profile; image = profile;
} else if (image->Type == VIPS_INTERPRETATION_CMYK) { } else if (image->Type == VIPS_INTERPRETATION_CMYK) {
VipsImage *profile;
// CMYK with no embedded profile // CMYK with no embedded profile
VipsImage *profile = vips_image_new();
vips_object_local(hook, profile);
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "input_profile", (baton->iccProfileCmyk).c_str(), NULL)) { if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "input_profile", (baton->iccProfileCmyk).c_str(), NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, profile);
image = profile; image = profile;
} }
// Attempt to convert to sRGB colour space // Attempt to convert to sRGB colour space
VipsImage *colourspaced = vips_image_new(); VipsImage *colourspaced;
vips_object_local(hook, colourspaced);
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) { if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, colourspaced);
image = colourspaced; image = colourspaced;
} }
// Flatten image to remove alpha channel // Flatten image to remove alpha channel
if (baton->flatten && sharp_image_has_alpha(image)) { if (baton->flatten && HasAlpha(image)) {
// Background colour // Background colour
VipsArrayDouble *background = vips_array_double_newv( VipsArrayDouble *background = vips_array_double_newv(
3, // Ignore alpha channel as we're about to remove it 3, // Ignore alpha channel as we're about to remove it
@ -301,52 +307,48 @@ class ResizeWorker : public NanAsyncWorker {
baton->background[1], baton->background[1],
baton->background[2] baton->background[2]
); );
VipsImage *flattened = vips_image_new(); VipsImage *flattened;
vips_object_local(hook, flattened);
if (vips_flatten(image, &flattened, "background", background, NULL)) { if (vips_flatten(image, &flattened, "background", background, NULL)) {
vips_area_unref(reinterpret_cast<VipsArea*>(background)); vips_area_unref(reinterpret_cast<VipsArea*>(background));
return Error(baton, hook); return Error(baton, hook);
}; };
vips_area_unref(reinterpret_cast<VipsArea*>(background)); vips_area_unref(reinterpret_cast<VipsArea*>(background));
g_object_unref(image); vips_object_local(hook, flattened);
image = flattened; image = flattened;
} }
// Gamma encoding (darken) // Gamma encoding (darken)
if (baton->gamma >= 1 && baton->gamma <= 3) { if (baton->gamma >= 1 && baton->gamma <= 3) {
VipsImage *gammaEncoded = vips_image_new(); VipsImage *gammaEncoded;
vips_object_local(hook, gammaEncoded);
if (vips_gamma(image, &gammaEncoded, "exponent", 1.0 / baton->gamma, NULL)) { if (vips_gamma(image, &gammaEncoded, "exponent", 1.0 / baton->gamma, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, gammaEncoded);
image = gammaEncoded; image = gammaEncoded;
} }
// Convert to greyscale (linear, therefore after gamma encoding, if any) // Convert to greyscale (linear, therefore after gamma encoding, if any)
if (baton->greyscale) { if (baton->greyscale) {
VipsImage *greyscale = vips_image_new(); VipsImage *greyscale;
vips_object_local(hook, greyscale);
if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, NULL)) { if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, greyscale);
image = greyscale; image = greyscale;
} }
if (shrink > 1) { if (shrink > 1) {
VipsImage *shrunk = vips_image_new(); VipsImage *shrunk;
vips_object_local(hook, shrunk);
// Use vips_shrink with the integral reduction // Use vips_shrink with the integral reduction
if (vips_shrink(image, &shrunk, shrink, shrink, NULL)) { if (vips_shrink(image, &shrunk, shrink, shrink, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, shrunk);
image = shrunk; image = shrunk;
// Recalculate residual float based on dimensions of required vs shrunk images // Recalculate residual float based on dimensions of required vs shrunk images
double shrunkWidth = shrunk->Xsize; double shrunkWidth = shrunk->Xsize;
double shrunkHeight = shrunk->Ysize; double shrunkHeight = shrunk->Ysize;
if (rotation == ANGLE_90 || rotation == ANGLE_270) { if (rotation == Angle::D90 || rotation == Angle::D270) {
// Swap input output width and height when rotating by 90 or 270 degrees // Swap input output width and height when rotating by 90 or 270 degrees
int swap = shrunkWidth; int swap = shrunkWidth;
shrunkWidth = shrunkHeight; shrunkWidth = shrunkHeight;
@ -354,7 +356,7 @@ class ResizeWorker : public NanAsyncWorker {
} }
double residualx = static_cast<double>(baton->width) / static_cast<double>(shrunkWidth); double residualx = static_cast<double>(baton->width) / static_cast<double>(shrunkWidth);
double residualy = static_cast<double>(baton->height) / static_cast<double>(shrunkHeight); double residualy = static_cast<double>(baton->height) / static_cast<double>(shrunkHeight);
if (baton->canvas == EMBED) { if (baton->canvas == Canvas::EMBED) {
residual = std::min(residualx, residualy); residual = std::min(residualx, residualy);
} else { } else {
residual = std::max(residualx, residualy); residual = std::max(residualx, residualy);
@ -365,98 +367,88 @@ class ResizeWorker : public NanAsyncWorker {
if (residual != 0) { if (residual != 0) {
// Apply variable blur radius of floor(residual) before large affine reductions // Apply variable blur radius of floor(residual) before large affine reductions
if (residual >= 1) { if (residual >= 1) {
VipsImage *blurred = vips_image_new(); VipsImage *blurred;
vips_object_local(hook, blurred);
if (vips_gaussblur(image, &blurred, floor(residual), NULL)) { if (vips_gaussblur(image, &blurred, floor(residual), NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, blurred);
image = blurred; image = blurred;
} }
// Create interpolator - "bilinear" (default), "bicubic" or "nohalo" // Create interpolator - "bilinear" (default), "bicubic" or "nohalo"
VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.c_str()); VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.c_str());
vips_object_local(hook, interpolator);
// Perform affine transformation // Perform affine transformation
VipsImage *affined = vips_image_new(); VipsImage *affined;
vips_object_local(hook, affined);
if (vips_affine(image, &affined, residual, 0, 0, residual, "interpolate", interpolator, NULL)) { if (vips_affine(image, &affined, residual, 0, 0, residual, "interpolate", interpolator, NULL)) {
g_object_unref(interpolator);
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(interpolator); vips_object_local(hook, affined);
g_object_unref(image);
image = affined; image = affined;
} }
// Rotate // Rotate
if (rotation != ANGLE_0) { if (rotation != Angle::D0) {
VipsImage *rotated = vips_image_new(); VipsImage *rotated;
vips_object_local(hook, rotated);
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) { if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, rotated);
image = rotated; image = rotated;
} }
// Flip (mirror about Y axis) // Flip (mirror about Y axis)
if (baton->flip) { if (baton->flip) {
VipsImage *flipped = vips_image_new(); VipsImage *flipped;
vips_object_local(hook, flipped);
if (vips_flip(image, &flipped, VIPS_DIRECTION_VERTICAL, NULL)) { if (vips_flip(image, &flipped, VIPS_DIRECTION_VERTICAL, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, flipped);
image = flipped; image = flipped;
} }
// Flop (mirror about X axis) // Flop (mirror about X axis)
if (baton->flop) { if (baton->flop) {
VipsImage *flopped = vips_image_new(); VipsImage *flopped;
vips_object_local(hook, flopped);
if (vips_flip(image, &flopped, VIPS_DIRECTION_HORIZONTAL, NULL)) { if (vips_flip(image, &flopped, VIPS_DIRECTION_HORIZONTAL, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, flopped);
image = flopped; image = flopped;
} }
// Crop/embed // Crop/embed
if (image->Xsize != baton->width || image->Ysize != baton->height) { if (image->Xsize != baton->width || image->Ysize != baton->height) {
if (baton->canvas == EMBED) { if (baton->canvas == Canvas::EMBED) {
// Match background colour space, namely sRGB // Match background colour space, namely sRGB
if (image->Type != VIPS_INTERPRETATION_sRGB) { if (image->Type != VIPS_INTERPRETATION_sRGB) {
// Convert to sRGB colour space // Convert to sRGB colour space
VipsImage *colourspaced = vips_image_new(); VipsImage *colourspaced;
vips_object_local(hook, colourspaced);
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) { if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, colourspaced);
image = colourspaced; image = colourspaced;
} }
// Add non-transparent alpha channel, if required // Add non-transparent alpha channel, if required
if (baton->background[3] < 255.0 && !sharp_image_has_alpha(image)) { if (baton->background[3] < 255.0 && !HasAlpha(image)) {
// Create single-channel transparency // Create single-channel transparency
VipsImage *black = vips_image_new(); VipsImage *black;
vips_object_local(hook, black);
if (vips_black(&black, image->Xsize, image->Ysize, "bands", 1, NULL)) { if (vips_black(&black, image->Xsize, image->Ysize, "bands", 1, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
vips_object_local(hook, black);
// Invert to become non-transparent // Invert to become non-transparent
VipsImage *alpha = vips_image_new(); VipsImage *alpha;
vips_object_local(hook, alpha);
if (vips_invert(black, &alpha, NULL)) { if (vips_invert(black, &alpha, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(black); vips_object_local(hook, alpha);
// Append alpha channel to existing image // Append alpha channel to existing image
VipsImage *joined = vips_image_new(); VipsImage *joined;
vips_object_local(hook, joined);
if (vips_bandjoin2(image, alpha, &joined, NULL)) { if (vips_bandjoin2(image, alpha, &joined, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(alpha); vips_object_local(hook, joined);
g_object_unref(image);
image = joined; image = joined;
} }
// Create background // Create background
@ -473,8 +465,7 @@ class ResizeWorker : public NanAsyncWorker {
// Embed // Embed
int left = (baton->width - image->Xsize) / 2; int left = (baton->width - image->Xsize) / 2;
int top = (baton->height - image->Ysize) / 2; int top = (baton->height - image->Ysize) / 2;
VipsImage *embedded = vips_image_new(); VipsImage *embedded;
vips_object_local(hook, embedded);
if (vips_embed(image, &embedded, left, top, baton->width, baton->height, if (vips_embed(image, &embedded, left, top, baton->width, baton->height,
"extend", VIPS_EXTEND_BACKGROUND, "background", background, NULL "extend", VIPS_EXTEND_BACKGROUND, "background", background, NULL
)) { )) {
@ -482,7 +473,7 @@ class ResizeWorker : public NanAsyncWorker {
return Error(baton, hook); return Error(baton, hook);
} }
vips_area_unref(reinterpret_cast<VipsArea*>(background)); vips_area_unref(reinterpret_cast<VipsArea*>(background));
g_object_unref(image); vips_object_local(hook, embedded);
image = embedded; image = embedded;
} else { } else {
// Crop/max // Crop/max
@ -491,31 +482,28 @@ class ResizeWorker : public NanAsyncWorker {
std::tie(left, top) = CalculateCrop(image->Xsize, image->Ysize, baton->width, baton->height, baton->gravity); std::tie(left, top) = CalculateCrop(image->Xsize, image->Ysize, baton->width, baton->height, baton->gravity);
int width = std::min(image->Xsize, baton->width); int width = std::min(image->Xsize, baton->width);
int height = std::min(image->Ysize, baton->height); int height = std::min(image->Ysize, baton->height);
VipsImage *extracted = vips_image_new(); VipsImage *extracted;
vips_object_local(hook, extracted);
if (vips_extract_area(image, &extracted, left, top, width, height, NULL)) { if (vips_extract_area(image, &extracted, left, top, width, height, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, extracted);
image = extracted; image = extracted;
} }
} }
// Post extraction // Post extraction
if (baton->topOffsetPost != -1) { if (baton->topOffsetPost != -1) {
VipsImage *extractedPost = vips_image_new(); VipsImage *extractedPost;
vips_object_local(hook, extractedPost);
if (vips_extract_area(image, &extractedPost, baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, NULL)) { if (vips_extract_area(image, &extractedPost, baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, extractedPost);
image = extractedPost; image = extractedPost;
} }
// Blur // Blur
if (baton->blurRadius != 0) { if (baton->blurRadius != 0) {
VipsImage *blurred = vips_image_new(); VipsImage *blurred;
vips_object_local(hook, blurred);
if (baton->blurRadius == -1) { if (baton->blurRadius == -1) {
// Fast, mild blur // Fast, mild blur
VipsImage *blur = vips_image_new_matrixv(3, 3, VipsImage *blur = vips_image_new_matrixv(3, 3,
@ -533,14 +521,13 @@ class ResizeWorker : public NanAsyncWorker {
return Error(baton, hook); return Error(baton, hook);
} }
} }
g_object_unref(image); vips_object_local(hook, blurred);
image = blurred; image = blurred;
} }
// Sharpen // Sharpen
if (baton->sharpenRadius != 0) { if (baton->sharpenRadius != 0) {
VipsImage *sharpened = vips_image_new(); VipsImage *sharpened;
vips_object_local(hook, sharpened);
if (baton->sharpenRadius == -1) { if (baton->sharpenRadius == -1) {
// Fast, mild sharpen // Fast, mild sharpen
VipsImage *sharpen = vips_image_new_matrixv(3, 3, VipsImage *sharpen = vips_image_new_matrixv(3, 3,
@ -558,54 +545,51 @@ class ResizeWorker : public NanAsyncWorker {
return Error(baton, hook); return Error(baton, hook);
} }
} }
g_object_unref(image); vips_object_local(hook, sharpened);
image = sharpened; image = sharpened;
} }
// Gamma decoding (brighten) // Gamma decoding (brighten)
if (baton->gamma >= 1 && baton->gamma <= 3) { if (baton->gamma >= 1 && baton->gamma <= 3) {
VipsImage *gammaDecoded = vips_image_new(); VipsImage *gammaDecoded;
vips_object_local(hook, gammaDecoded);
if (vips_gamma(image, &gammaDecoded, "exponent", baton->gamma, NULL)) { if (vips_gamma(image, &gammaDecoded, "exponent", baton->gamma, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, gammaDecoded);
image = gammaDecoded; image = gammaDecoded;
} }
// Convert to sRGB colour space, if not already // Convert to sRGB colour space, if not already
if (image->Type != VIPS_INTERPRETATION_sRGB) { if (image->Type != VIPS_INTERPRETATION_sRGB) {
VipsImage *colourspaced = vips_image_new(); VipsImage *colourspaced;
vips_object_local(hook, colourspaced);
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) { if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, colourspaced);
image = colourspaced; image = colourspaced;
} }
#if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5) #if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5)
// Generate image tile cache when interlace output is required - no longer required as of libvips 7.40.5+ // Generate image tile cache when interlace output is required - no longer required as of libvips 7.40.5+
if (baton->progressive) { if (baton->progressive) {
VipsImage *cached = vips_image_new(); VipsImage *cached;
vips_object_local(hook, cached);
if (vips_tilecache(image, &cached, "threaded", TRUE, "persistent", TRUE, "max_tiles", -1, NULL)) { if (vips_tilecache(image, &cached, "threaded", TRUE, "persistent", TRUE, "max_tiles", -1, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
g_object_unref(image); vips_object_local(hook, cached);
image = cached; image = cached;
} }
#endif #endif
// Output // Output
if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == JPEG)) { if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == ImageType::JPEG)) {
// Write JPEG to buffer // Write JPEG to buffer
if (vips_jpegsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata, if (vips_jpegsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
"Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) { "Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
baton->outputFormat = "jpeg"; baton->outputFormat = "jpeg";
} else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == PNG)) { } else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == ImageType::PNG)) {
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 41) #if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 41)
// Select PNG row filter // Select PNG row filter
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL; int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
@ -622,7 +606,7 @@ class ResizeWorker : public NanAsyncWorker {
} }
#endif #endif
baton->outputFormat = "png"; baton->outputFormat = "png";
} else if (baton->output == "__webp" || (baton->output == "__input" && inputImageType == WEBP)) { } else if (baton->output == "__webp" || (baton->output == "__input" && inputImageType == ImageType::WEBP)) {
// Write WEBP to buffer // Write WEBP to buffer
if (vips_webpsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata, if (vips_webpsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
"Q", baton->quality, NULL)) { "Q", baton->quality, NULL)) {
@ -630,19 +614,19 @@ class ResizeWorker : public NanAsyncWorker {
} }
baton->outputFormat = "webp"; baton->outputFormat = "webp";
} else { } else {
bool output_jpeg = is_jpeg(baton->output); bool outputJpeg = IsJpeg(baton->output);
bool output_png = is_png(baton->output); bool outputPng = IsPng(baton->output);
bool output_webp = is_webp(baton->output); bool outputWebp = IsWebp(baton->output);
bool output_tiff = is_tiff(baton->output); bool outputTiff = IsTiff(baton->output);
bool match_input = !(output_jpeg || output_png || output_webp || output_tiff); bool matchInput = !(outputJpeg || outputPng || outputWebp || outputTiff);
if (output_jpeg || (match_input && inputImageType == JPEG)) { if (outputJpeg || (matchInput && inputImageType == ImageType::JPEG)) {
// Write JPEG to file // Write JPEG to file
if (vips_jpegsave(image, baton->output.c_str(), "strip", !baton->withMetadata, if (vips_jpegsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) { "Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
baton->outputFormat = "jpeg"; baton->outputFormat = "jpeg";
} else if (output_png || (match_input && inputImageType == PNG)) { } else if (outputPng || (matchInput && inputImageType == ImageType::PNG)) {
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 41) #if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 41)
// Select PNG row filter // Select PNG row filter
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL; int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
@ -659,14 +643,14 @@ class ResizeWorker : public NanAsyncWorker {
} }
#endif #endif
baton->outputFormat = "png"; baton->outputFormat = "png";
} else if (output_webp || (match_input && inputImageType == WEBP)) { } else if (outputWebp || (matchInput && inputImageType == ImageType::WEBP)) {
// Write WEBP to file // Write WEBP to file
if (vips_webpsave(image, baton->output.c_str(), "strip", !baton->withMetadata, if (vips_webpsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"Q", baton->quality, NULL)) { "Q", baton->quality, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
baton->outputFormat = "webp"; baton->outputFormat = "webp";
} else if (output_tiff || (match_input && inputImageType == TIFF)) { } else if (outputTiff || (matchInput && inputImageType == ImageType::TIFF)) {
// Write TIFF to file // Write TIFF to file
if (vips_tiffsave(image, baton->output.c_str(), "strip", !baton->withMetadata, if (vips_tiffsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG, "Q", baton->quality, NULL)) { "compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG, "Q", baton->quality, NULL)) {
@ -675,12 +659,10 @@ class ResizeWorker : public NanAsyncWorker {
baton->outputFormat = "tiff"; baton->outputFormat = "tiff";
} else { } else {
(baton->err).append("Unsupported output " + baton->output); (baton->err).append("Unsupported output " + baton->output);
g_object_unref(image);
return Error(baton, hook); return Error(baton, hook);
} }
} }
// Clean up any dangling image references // Clean up any dangling image references
g_object_unref(image);
g_object_unref(hook); g_object_unref(hook);
// Clean up libvips' per-request data and threads // Clean up libvips' per-request data and threads
vips_error_clear(); vips_error_clear();
@ -724,7 +706,7 @@ class ResizeWorker : public NanAsyncWorker {
delete baton; delete baton;
// Decrement processing task counter // Decrement processing task counter
g_atomic_int_dec_and_test(&counter_process); g_atomic_int_dec_and_test(&counterProcess);
// Return to JavaScript // Return to JavaScript
callback->Call(3, argv); callback->Call(3, argv);
@ -742,40 +724,25 @@ class ResizeWorker : public NanAsyncWorker {
*/ */
std::tuple<Angle, bool> std::tuple<Angle, bool>
CalculateRotationAndFlip(int const angle, VipsImage const *input) { CalculateRotationAndFlip(int const angle, VipsImage const *input) {
Angle rotate = ANGLE_0; Angle rotate = Angle::D0;
bool flip = FALSE; bool flip = FALSE;
if (angle == -1) { if (angle == -1) {
const char *exif; switch(ExifOrientation(input)) {
if ( case 6: rotate = Angle::D90; break;
vips_image_get_typeof(input, "exif-ifd0-Orientation") != 0 && case 3: rotate = Angle::D180; break;
!vips_image_get_string(input, "exif-ifd0-Orientation", &exif) case 8: rotate = Angle::D270; break;
) { case 2: flip = TRUE; break; // flip 1
if (exif[0] == 0x36) { // "6" case 7: flip = TRUE; rotate = Angle::D90; break; // flip 6
rotate = ANGLE_90; case 4: flip = TRUE; rotate = Angle::D180; break; // flip 3
} else if (exif[0] == 0x33) { // "3" case 5: flip = TRUE; rotate = Angle::D270; break; // flip 8
rotate = ANGLE_180;
} else if (exif[0] == 0x38) { // "8"
rotate = ANGLE_270;
} else if (exif[0] == 0x32) { // "2" (flip 1)
flip = TRUE;
} else if (exif[0] == 0x37) { // "7" (flip 6)
rotate = ANGLE_90;
flip = TRUE;
} else if (exif[0] == 0x34) { // "4" (flip 3)
rotate = ANGLE_180;
flip = TRUE;
} else if (exif[0] == 0x35) { // "5" (flip 8)
rotate = ANGLE_270;
flip = TRUE;
}
} }
} else { } else {
if (angle == 90) { if (angle == 90) {
rotate = ANGLE_90; rotate = Angle::D90;
} else if (angle == 180) { } else if (angle == 180) {
rotate = ANGLE_180; rotate = Angle::D180;
} else if (angle == 270) { } else if (angle == 270) {
rotate = ANGLE_270; rotate = Angle::D270;
} }
} }
return std::make_tuple(rotate, flip); return std::make_tuple(rotate, flip);
@ -817,9 +784,12 @@ class ResizeWorker : public NanAsyncWorker {
Clear all thread-local data. Clear all thread-local data.
*/ */
void Error(ResizeBaton *baton, VipsObject *hook) { void Error(ResizeBaton *baton, VipsObject *hook) {
// Get libvips' error message
(baton->err).append(vips_error_buffer()); (baton->err).append(vips_error_buffer());
vips_error_clear(); // Clean up any dangling image references
g_object_unref(hook); g_object_unref(hook);
// Clean up libvips' per-request data and threads
vips_error_clear();
vips_thread_shutdown(); vips_thread_shutdown();
} }
}; };
@ -860,11 +830,11 @@ NAN_METHOD(resize) {
// Canvas option // Canvas option
Local<String> canvas = options->Get(NanNew<String>("canvas"))->ToString(); Local<String> canvas = options->Get(NanNew<String>("canvas"))->ToString();
if (canvas->Equals(NanNew<String>("c"))) { if (canvas->Equals(NanNew<String>("c"))) {
baton->canvas = CROP; baton->canvas = Canvas::CROP;
} else if (canvas->Equals(NanNew<String>("m"))) { } else if (canvas->Equals(NanNew<String>("m"))) {
baton->canvas = MAX; baton->canvas = Canvas::MAX;
} else if (canvas->Equals(NanNew<String>("e"))) { } else if (canvas->Equals(NanNew<String>("e"))) {
baton->canvas = EMBED; baton->canvas = Canvas::EMBED;
} }
// Background colour // Background colour
Local<Array> background = Local<Array>::Cast(options->Get(NanNew<String>("background"))); Local<Array> background = Local<Array>::Cast(options->Get(NanNew<String>("background")));
@ -900,7 +870,7 @@ NAN_METHOD(resize) {
NanAsyncQueueWorker(new ResizeWorker(callback, baton)); NanAsyncQueueWorker(new ResizeWorker(callback, baton));
// Increment queued task counter // Increment queued task counter
g_atomic_int_inc(&counter_queue); g_atomic_int_inc(&counterQueue);
NanReturnUndefined(); NanReturnUndefined();
} }

View File

@ -7,6 +7,7 @@
#include "utilities.h" #include "utilities.h"
using namespace v8; using namespace v8;
using namespace sharp;
/* /*
Get and set cache memory and item limits Get and set cache memory and item limits
@ -58,8 +59,8 @@ NAN_METHOD(concurrency) {
NAN_METHOD(counters) { NAN_METHOD(counters) {
NanScope(); NanScope();
Local<Object> counters = NanNew<Object>(); Local<Object> counters = NanNew<Object>();
counters->Set(NanNew<String>("queue"), NanNew<Number>(counter_queue)); counters->Set(NanNew<String>("queue"), NanNew<Number>(counterQueue));
counters->Set(NanNew<String>("process"), NanNew<Number>(counter_process)); counters->Set(NanNew<String>("process"), NanNew<Number>(counterProcess));
NanReturnValue(counters); NanReturnValue(counters);
} }