mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Less C, more C++ e.g. namespace, enum class
Improve image reference handling
This commit is contained in:
parent
e465306d97
commit
ee513ac7a7
252
src/common.cc
252
src/common.cc
@ -4,127 +4,161 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
// How many tasks are in the queue?
|
||||
volatile int counter_queue = 0;
|
||||
namespace sharp {
|
||||
|
||||
// How many tasks are being processed?
|
||||
volatile int counter_process = 0;
|
||||
// How many tasks are in the queue?
|
||||
volatile int counterQueue = 0;
|
||||
|
||||
// Filename extension checkers
|
||||
static bool ends_with(std::string const &str, std::string const &end) {
|
||||
return str.length() >= end.length() && 0 == str.compare(str.length() - end.length(), end.length(), end);
|
||||
}
|
||||
bool is_jpeg(std::string const &str) {
|
||||
return ends_with(str, ".jpg") || ends_with(str, ".jpeg") || ends_with(str, ".JPG") || ends_with(str, ".JPEG");
|
||||
}
|
||||
bool is_png(std::string const &str) {
|
||||
return ends_with(str, ".png") || ends_with(str, ".PNG");
|
||||
}
|
||||
bool is_webp(std::string const &str) {
|
||||
return ends_with(str, ".webp") || ends_with(str, ".WEBP");
|
||||
}
|
||||
bool is_tiff(std::string const &str) {
|
||||
return ends_with(str, ".tif") || ends_with(str, ".tiff") || ends_with(str, ".TIF") || ends_with(str, ".TIFF");
|
||||
}
|
||||
// How many tasks are being processed?
|
||||
volatile int counterProcess = 0;
|
||||
|
||||
// Buffer content checkers
|
||||
// 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);
|
||||
}
|
||||
bool IsJpeg(std::string const &str) {
|
||||
return EndsWith(str, ".jpg") || EndsWith(str, ".jpeg") || EndsWith(str, ".JPG") || EndsWith(str, ".JPEG");
|
||||
}
|
||||
bool IsPng(std::string const &str) {
|
||||
return EndsWith(str, ".png") || EndsWith(str, ".PNG");
|
||||
}
|
||||
bool IsWebp(std::string const &str) {
|
||||
return EndsWith(str, ".webp") || EndsWith(str, ".WEBP");
|
||||
}
|
||||
bool IsTiff(std::string const &str) {
|
||||
return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
|
||||
}
|
||||
|
||||
// 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)
|
||||
static bool buffer_is_tiff(char *buffer, size_t len) {
|
||||
return (
|
||||
len >= 4 && (
|
||||
(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')
|
||||
)
|
||||
);
|
||||
}
|
||||
static bool buffer_is_tiff(char *buffer, size_t len) {
|
||||
return (
|
||||
len >= 4 && (
|
||||
(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')
|
||||
)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned char const MARKER_JPEG[] = {0xff, 0xd8};
|
||||
unsigned char const MARKER_PNG[] = {0x89, 0x50};
|
||||
unsigned char const MARKER_WEBP[] = {0x52, 0x49};
|
||||
|
||||
/*
|
||||
Initialise a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
|
||||
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 (!vips_jpegload_buffer(buffer, length, image, "access", access, NULL)) {
|
||||
imageType = JPEG;
|
||||
}
|
||||
} else if (memcmp(MARKER_PNG, buffer, 2) == 0) {
|
||||
if (!vips_pngload_buffer(buffer, length, image, "access", access, NULL)) {
|
||||
imageType = PNG;
|
||||
}
|
||||
} else if (memcmp(MARKER_WEBP, buffer, 2) == 0) {
|
||||
if (!vips_webpload_buffer(buffer, length, image, "access", access, NULL)) {
|
||||
imageType = WEBP;
|
||||
}
|
||||
/*
|
||||
Determine image format of a buffer.
|
||||
*/
|
||||
ImageType DetermineImageType(void *buffer, size_t const length) {
|
||||
ImageType imageType = ImageType::UNKNOWN;
|
||||
if (length >= 4) {
|
||||
if (memcmp(MARKER_JPEG, buffer, 2) == 0) {
|
||||
imageType = ImageType::JPEG;
|
||||
} else if (memcmp(MARKER_PNG, buffer, 2) == 0) {
|
||||
imageType = ImageType::PNG;
|
||||
} else if (memcmp(MARKER_WEBP, buffer, 2) == 0) {
|
||||
imageType = ImageType::WEBP;
|
||||
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40)
|
||||
} else if (buffer_is_tiff(static_cast<char*>(buffer), length)) {
|
||||
if (!vips_tiffload_buffer(buffer, length, image, "access", access, NULL)) {
|
||||
imageType = TIFF;
|
||||
}
|
||||
} else if (buffer_is_tiff(static_cast<char*>(buffer), length)) {
|
||||
imageType = ImageType::TIFF;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return imageType;
|
||||
}
|
||||
return imageType;
|
||||
}
|
||||
|
||||
/*
|
||||
Initialise a VipsImage from a file.
|
||||
Returns the ImageType detected, if any.
|
||||
*/
|
||||
ImageType
|
||||
sharp_init_image_from_file(VipsImage **image, char const *file, VipsAccess const access) {
|
||||
ImageType imageType = UNKNOWN;
|
||||
if (vips_foreign_is_a("jpegload", file)) {
|
||||
if (!vips_jpegload(file, image, "access", access, NULL)) {
|
||||
imageType = JPEG;
|
||||
}
|
||||
} else if (vips_foreign_is_a("pngload", file)) {
|
||||
if (!vips_pngload(file, image, "access", access, NULL)) {
|
||||
imageType = PNG;
|
||||
}
|
||||
} else if (vips_foreign_is_a("webpload", file)) {
|
||||
if (!vips_webpload(file, image, "access", access, NULL)) {
|
||||
imageType = WEBP;
|
||||
}
|
||||
} else if (vips_foreign_is_a("tiffload", file)) {
|
||||
if (!vips_tiffload(file, image, "access", access, NULL)) {
|
||||
imageType = TIFF;
|
||||
}
|
||||
} else if(vips_foreign_is_a("magickload", file)) {
|
||||
if (!vips_magickload(file, image, "access", access, NULL)) {
|
||||
imageType = MAGICK;
|
||||
/*
|
||||
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) {
|
||||
VipsImage *image = NULL;
|
||||
if (imageType == ImageType::JPEG) {
|
||||
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;
|
||||
}
|
||||
return imageType;
|
||||
}
|
||||
|
||||
/*
|
||||
Does this image have an alpha channel?
|
||||
Uses colour space interpretation with number of channels to guess this.
|
||||
*/
|
||||
bool
|
||||
sharp_image_has_alpha(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)
|
||||
);
|
||||
}
|
||||
/*
|
||||
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)) {
|
||||
imageType = ImageType::JPEG;
|
||||
} else if (vips_foreign_is_a("pngload", file)) {
|
||||
imageType = ImageType::PNG;
|
||||
} else if (vips_foreign_is_a("webpload", file)) {
|
||||
imageType = ImageType::WEBP;
|
||||
} else if (vips_foreign_is_a("tiffload", file)) {
|
||||
imageType = ImageType::TIFF;
|
||||
} else if(vips_foreign_is_a("magickload", file)) {
|
||||
imageType = ImageType::MAGICK;
|
||||
}
|
||||
return imageType;
|
||||
}
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
int
|
||||
sharp_interpolator_window_size(char const *name) {
|
||||
VipsInterpolate *interpolator = vips_interpolate_new(name);
|
||||
int window_size = vips_interpolate_get_window_size(interpolator);
|
||||
g_object_unref(interpolator);
|
||||
return window_size;
|
||||
}
|
||||
/*
|
||||
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?
|
||||
Uses colour space interpretation with number of channels to guess this.
|
||||
*/
|
||||
bool 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)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
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,
|
||||
a window size of 3 means a 3x3 pixel grid is used for the calculation.
|
||||
*/
|
||||
int InterpolatorWindowSize(char const *name) {
|
||||
VipsInterpolate *interpolator = vips_interpolate_new(name);
|
||||
int window_size = vips_interpolate_get_window_size(interpolator);
|
||||
g_object_unref(interpolator);
|
||||
return window_size;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
95
src/common.h
95
src/common.h
@ -1,53 +1,66 @@
|
||||
#ifndef SHARP_COMMON_H
|
||||
#define SHARP_COMMON_H
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN,
|
||||
JPEG,
|
||||
PNG,
|
||||
WEBP,
|
||||
TIFF,
|
||||
MAGICK
|
||||
} ImageType;
|
||||
namespace sharp {
|
||||
|
||||
// Filename extension checkers
|
||||
bool is_jpeg(std::string const &str);
|
||||
bool is_png(std::string const &str);
|
||||
bool is_webp(std::string const &str);
|
||||
bool is_tiff(std::string const &str);
|
||||
enum class ImageType {
|
||||
UNKNOWN,
|
||||
JPEG,
|
||||
PNG,
|
||||
WEBP,
|
||||
TIFF,
|
||||
MAGICK
|
||||
};
|
||||
|
||||
// How many tasks are in the queue?
|
||||
extern volatile int counter_queue;
|
||||
// How many tasks are in the queue?
|
||||
extern volatile int counterQueue;
|
||||
|
||||
// How many tasks are being processed?
|
||||
extern volatile int counter_process;
|
||||
// How many tasks are being processed?
|
||||
extern volatile int counterProcess;
|
||||
|
||||
/*
|
||||
Initialise a VipsImage from a buffer. Supports JPEG, PNG and WebP.
|
||||
Returns the ImageType detected, if any.
|
||||
*/
|
||||
ImageType
|
||||
sharp_init_image_from_buffer(VipsImage **image, void *buffer, size_t const length, VipsAccess const access);
|
||||
// Filename extension checkers
|
||||
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 file.
|
||||
Returns the ImageType detected, if any.
|
||||
*/
|
||||
ImageType
|
||||
sharp_init_image_from_file(VipsImage **image, char const *file, VipsAccess const access);
|
||||
/*
|
||||
Determine image format of a buffer.
|
||||
*/
|
||||
ImageType DetermineImageType(void *buffer, size_t const length);
|
||||
|
||||
/*
|
||||
Does this image have an alpha channel?
|
||||
Uses colour space interpretation with number of channels to guess this.
|
||||
*/
|
||||
bool
|
||||
sharp_image_has_alpha(VipsImage *image);
|
||||
/*
|
||||
Determine image format of a file.
|
||||
*/
|
||||
ImageType DetermineImageType(char const *file);
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
int
|
||||
sharp_interpolator_window_size(char const *name);
|
||||
/*
|
||||
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?
|
||||
Uses colour space interpretation with number of channels to guess this.
|
||||
*/
|
||||
bool HasAlpha(VipsImage *image);
|
||||
|
||||
/*
|
||||
Get EXIF Orientation of image, if any.
|
||||
*/
|
||||
int ExifOrientation(VipsImage const *image);
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
int InterpolatorWindowSize(char const *name);
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "metadata.h"
|
||||
|
||||
using namespace v8;
|
||||
using namespace sharp;
|
||||
|
||||
struct MetadataBaton {
|
||||
// Input
|
||||
@ -36,49 +37,49 @@ class MetadataWorker : public NanAsyncWorker {
|
||||
|
||||
void Execute() {
|
||||
// Decrement queued task counter
|
||||
g_atomic_int_dec_and_test(&counter_queue);
|
||||
g_atomic_int_dec_and_test(&counterQueue);
|
||||
|
||||
ImageType imageType = UNKNOWN;
|
||||
VipsImage *image;
|
||||
ImageType imageType = ImageType::UNKNOWN;
|
||||
VipsImage *image = NULL;
|
||||
if (baton->bufferInLength > 1) {
|
||||
// From buffer
|
||||
imageType = sharp_init_image_from_buffer(&image, baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM);
|
||||
if (imageType == UNKNOWN) {
|
||||
imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
|
||||
if (imageType != ImageType::UNKNOWN) {
|
||||
image = InitImage(imageType, baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM);
|
||||
} else {
|
||||
(baton->err).append("Input buffer contains unsupported image format");
|
||||
}
|
||||
} else {
|
||||
// From file
|
||||
imageType = sharp_init_image_from_file(&image, baton->fileIn.c_str(), VIPS_ACCESS_RANDOM);
|
||||
if (imageType == UNKNOWN) {
|
||||
imageType = DetermineImageType(baton->fileIn.c_str());
|
||||
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");
|
||||
}
|
||||
}
|
||||
if (imageType != UNKNOWN) {
|
||||
if (image != NULL && imageType != ImageType::UNKNOWN) {
|
||||
// Image type
|
||||
switch (imageType) {
|
||||
case JPEG: baton->format = "jpeg"; break;
|
||||
case PNG: baton->format = "png"; break;
|
||||
case WEBP: baton->format = "webp"; break;
|
||||
case TIFF: baton->format = "tiff"; break;
|
||||
case MAGICK: baton->format = "magick"; break;
|
||||
case UNKNOWN: default: baton->format = "";
|
||||
case ImageType::JPEG: baton->format = "jpeg"; break;
|
||||
case ImageType::PNG: baton->format = "png"; break;
|
||||
case ImageType::WEBP: baton->format = "webp"; break;
|
||||
case ImageType::TIFF: baton->format = "tiff"; break;
|
||||
case ImageType::MAGICK: baton->format = "magick"; break;
|
||||
case ImageType::UNKNOWN: break;
|
||||
}
|
||||
// VipsImage attributes
|
||||
baton->width = image->Xsize;
|
||||
baton->height = image->Ysize;
|
||||
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
|
||||
baton->channels = image->Bands;
|
||||
baton->hasAlpha = sharp_image_has_alpha(image);
|
||||
// EXIF Orientation
|
||||
const char *exif;
|
||||
if (!vips_image_get_string(image, "exif-ifd0-Orientation", &exif)) {
|
||||
baton->orientation = atoi(&exif[0]);
|
||||
}
|
||||
}
|
||||
// Clean up
|
||||
if (imageType != UNKNOWN) {
|
||||
// Derived attributes
|
||||
baton->hasAlpha = HasAlpha(image);
|
||||
baton->orientation = ExifOrientation(image);
|
||||
// Drop image reference
|
||||
g_object_unref(image);
|
||||
}
|
||||
// Clean up
|
||||
vips_error_clear();
|
||||
vips_thread_shutdown();
|
||||
}
|
||||
@ -138,7 +139,7 @@ NAN_METHOD(metadata) {
|
||||
NanAsyncQueueWorker(new MetadataWorker(callback, baton));
|
||||
|
||||
// Increment queued task counter
|
||||
g_atomic_int_inc(&counter_queue);
|
||||
g_atomic_int_inc(&counterQueue);
|
||||
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
288
src/resize.cc
288
src/resize.cc
@ -11,20 +11,21 @@
|
||||
#include "resize.h"
|
||||
|
||||
using namespace v8;
|
||||
using namespace sharp;
|
||||
|
||||
typedef enum {
|
||||
enum class Canvas {
|
||||
CROP,
|
||||
MAX,
|
||||
EMBED
|
||||
} Canvas;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ANGLE_0,
|
||||
ANGLE_90,
|
||||
ANGLE_180,
|
||||
ANGLE_270,
|
||||
ANGLE_LAST
|
||||
} Angle;
|
||||
enum class Angle {
|
||||
D0,
|
||||
D90,
|
||||
D180,
|
||||
D270,
|
||||
DLAST
|
||||
};
|
||||
|
||||
struct ResizeBaton {
|
||||
std::string fileIn;
|
||||
@ -74,7 +75,7 @@ struct ResizeBaton {
|
||||
bufferOutLength(0),
|
||||
topOffsetPre(-1),
|
||||
topOffsetPost(-1),
|
||||
canvas(CROP),
|
||||
canvas(Canvas::CROP),
|
||||
gravity(0),
|
||||
flatten(false),
|
||||
blurRadius(0),
|
||||
@ -83,10 +84,14 @@ struct ResizeBaton {
|
||||
sharpenJagged(2.0),
|
||||
gamma(0.0),
|
||||
greyscale(false),
|
||||
angle(0),
|
||||
flip(false),
|
||||
flop(false),
|
||||
progressive(false),
|
||||
withoutEnlargement(false),
|
||||
quality(80),
|
||||
compressionLevel(6),
|
||||
withoutAdaptiveFiltering(false),
|
||||
withMetadata(false) {
|
||||
background[0] = 0.0;
|
||||
background[1] = 0.0;
|
||||
@ -106,43 +111,45 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
*/
|
||||
void Execute() {
|
||||
// Decrement queued task counter
|
||||
g_atomic_int_dec_and_test(&counter_queue);
|
||||
g_atomic_int_dec_and_test(&counterQueue);
|
||||
// Increment processing task counter
|
||||
g_atomic_int_inc(&counter_process);
|
||||
g_atomic_int_inc(&counterProcess);
|
||||
|
||||
// Hang image references from this hook object
|
||||
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
|
||||
|
||||
// Input
|
||||
ImageType inputImageType = UNKNOWN;
|
||||
VipsImage *image = vips_image_new();
|
||||
vips_object_local(hook, image);
|
||||
|
||||
ImageType inputImageType = ImageType::UNKNOWN;
|
||||
VipsImage *image;
|
||||
if (baton->bufferInLength > 1) {
|
||||
// From buffer
|
||||
inputImageType = sharp_init_image_from_buffer(&image, baton->bufferIn, baton->bufferInLength, baton->accessMethod);
|
||||
if (inputImageType == UNKNOWN) {
|
||||
inputImageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
|
||||
if (inputImageType != ImageType::UNKNOWN) {
|
||||
image = InitImage(inputImageType, baton->bufferIn, baton->bufferInLength, baton->accessMethod);
|
||||
} else {
|
||||
(baton->err).append("Input buffer contains unsupported image format");
|
||||
}
|
||||
} else {
|
||||
// From file
|
||||
inputImageType = sharp_init_image_from_file(&image, baton->fileIn.c_str(), baton->accessMethod);
|
||||
if (inputImageType == UNKNOWN) {
|
||||
inputImageType = DetermineImageType(baton->fileIn.c_str());
|
||||
if (inputImageType != ImageType::UNKNOWN) {
|
||||
image = InitImage(inputImageType, baton->fileIn.c_str(), baton->accessMethod);
|
||||
} else {
|
||||
(baton->err).append("File is of an unsupported image format");
|
||||
}
|
||||
}
|
||||
if (inputImageType == UNKNOWN) {
|
||||
if (inputImageType == ImageType::UNKNOWN) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
vips_object_local(hook, image);
|
||||
|
||||
// Pre extraction
|
||||
if (baton->topOffsetPre != -1) {
|
||||
VipsImage *extractedPre = vips_image_new();
|
||||
vips_object_local(hook, extractedPre);
|
||||
VipsImage *extractedPre;
|
||||
if (vips_extract_area(image, &extractedPre, baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, extractedPre);
|
||||
image = extractedPre;
|
||||
}
|
||||
|
||||
@ -154,7 +161,7 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
Angle rotation;
|
||||
bool flip;
|
||||
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
|
||||
int swap = inputWidth;
|
||||
inputWidth = inputHeight;
|
||||
@ -166,7 +173,7 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
}
|
||||
|
||||
// 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
|
||||
double factor;
|
||||
@ -174,9 +181,9 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
// Fixed width and height
|
||||
double xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width);
|
||||
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 (baton->canvas == MAX) {
|
||||
if (baton->canvas == Canvas::MAX) {
|
||||
if (xfactor > yfactor) {
|
||||
baton->height = round(static_cast<double>(inputHeight) / xfactor);
|
||||
} 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
|
||||
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) {
|
||||
factor = factor / 8;
|
||||
shrink_on_load = 8;
|
||||
@ -248,52 +255,51 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
}
|
||||
residual = static_cast<double>(shrink) / factor;
|
||||
// Reload input using shrink-on-load
|
||||
g_object_unref(image);
|
||||
VipsImage *shrunkOnLoad;
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
vips_object_local(hook, shrunkOnLoad);
|
||||
image = shrunkOnLoad;
|
||||
}
|
||||
|
||||
// Handle colour profile, if any, for non sRGB images
|
||||
if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
||||
// Get the input colour profile
|
||||
if (vips_image_get_typeof(image, VIPS_META_ICC_NAME)) {
|
||||
VipsImage *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)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, profile);
|
||||
image = profile;
|
||||
} else if (image->Type == VIPS_INTERPRETATION_CMYK) {
|
||||
VipsImage *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)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, profile);
|
||||
image = profile;
|
||||
}
|
||||
// Attempt to convert to sRGB colour space
|
||||
VipsImage *colourspaced = vips_image_new();
|
||||
vips_object_local(hook, colourspaced);
|
||||
VipsImage *colourspaced;
|
||||
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, colourspaced);
|
||||
image = colourspaced;
|
||||
}
|
||||
|
||||
// Flatten image to remove alpha channel
|
||||
if (baton->flatten && sharp_image_has_alpha(image)) {
|
||||
if (baton->flatten && HasAlpha(image)) {
|
||||
// Background colour
|
||||
VipsArrayDouble *background = vips_array_double_newv(
|
||||
3, // Ignore alpha channel as we're about to remove it
|
||||
@ -301,52 +307,48 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
baton->background[1],
|
||||
baton->background[2]
|
||||
);
|
||||
VipsImage *flattened = vips_image_new();
|
||||
vips_object_local(hook, flattened);
|
||||
VipsImage *flattened;
|
||||
if (vips_flatten(image, &flattened, "background", background, NULL)) {
|
||||
vips_area_unref(reinterpret_cast<VipsArea*>(background));
|
||||
return Error(baton, hook);
|
||||
};
|
||||
vips_area_unref(reinterpret_cast<VipsArea*>(background));
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, flattened);
|
||||
image = flattened;
|
||||
}
|
||||
|
||||
// Gamma encoding (darken)
|
||||
if (baton->gamma >= 1 && baton->gamma <= 3) {
|
||||
VipsImage *gammaEncoded = vips_image_new();
|
||||
vips_object_local(hook, gammaEncoded);
|
||||
VipsImage *gammaEncoded;
|
||||
if (vips_gamma(image, &gammaEncoded, "exponent", 1.0 / baton->gamma, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, gammaEncoded);
|
||||
image = gammaEncoded;
|
||||
}
|
||||
|
||||
// Convert to greyscale (linear, therefore after gamma encoding, if any)
|
||||
if (baton->greyscale) {
|
||||
VipsImage *greyscale = vips_image_new();
|
||||
vips_object_local(hook, greyscale);
|
||||
VipsImage *greyscale;
|
||||
if (vips_colourspace(image, &greyscale, VIPS_INTERPRETATION_B_W, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, greyscale);
|
||||
image = greyscale;
|
||||
}
|
||||
|
||||
if (shrink > 1) {
|
||||
VipsImage *shrunk = vips_image_new();
|
||||
vips_object_local(hook, shrunk);
|
||||
VipsImage *shrunk;
|
||||
// Use vips_shrink with the integral reduction
|
||||
if (vips_shrink(image, &shrunk, shrink, shrink, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, shrunk);
|
||||
image = shrunk;
|
||||
// Recalculate residual float based on dimensions of required vs shrunk images
|
||||
double shrunkWidth = shrunk->Xsize;
|
||||
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
|
||||
int swap = shrunkWidth;
|
||||
shrunkWidth = shrunkHeight;
|
||||
@ -354,7 +356,7 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
}
|
||||
double residualx = static_cast<double>(baton->width) / static_cast<double>(shrunkWidth);
|
||||
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);
|
||||
} else {
|
||||
residual = std::max(residualx, residualy);
|
||||
@ -365,98 +367,88 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
if (residual != 0) {
|
||||
// Apply variable blur radius of floor(residual) before large affine reductions
|
||||
if (residual >= 1) {
|
||||
VipsImage *blurred = vips_image_new();
|
||||
vips_object_local(hook, blurred);
|
||||
VipsImage *blurred;
|
||||
if (vips_gaussblur(image, &blurred, floor(residual), NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, blurred);
|
||||
image = blurred;
|
||||
}
|
||||
// Create interpolator - "bilinear" (default), "bicubic" or "nohalo"
|
||||
VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.c_str());
|
||||
vips_object_local(hook, interpolator);
|
||||
// Perform affine transformation
|
||||
VipsImage *affined = vips_image_new();
|
||||
vips_object_local(hook, affined);
|
||||
VipsImage *affined;
|
||||
if (vips_affine(image, &affined, residual, 0, 0, residual, "interpolate", interpolator, NULL)) {
|
||||
g_object_unref(interpolator);
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(interpolator);
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, affined);
|
||||
image = affined;
|
||||
}
|
||||
|
||||
// Rotate
|
||||
if (rotation != ANGLE_0) {
|
||||
VipsImage *rotated = vips_image_new();
|
||||
vips_object_local(hook, rotated);
|
||||
if (rotation != Angle::D0) {
|
||||
VipsImage *rotated;
|
||||
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, rotated);
|
||||
image = rotated;
|
||||
}
|
||||
|
||||
// Flip (mirror about Y axis)
|
||||
if (baton->flip) {
|
||||
VipsImage *flipped = vips_image_new();
|
||||
vips_object_local(hook, flipped);
|
||||
VipsImage *flipped;
|
||||
if (vips_flip(image, &flipped, VIPS_DIRECTION_VERTICAL, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, flipped);
|
||||
image = flipped;
|
||||
}
|
||||
|
||||
// Flop (mirror about X axis)
|
||||
if (baton->flop) {
|
||||
VipsImage *flopped = vips_image_new();
|
||||
vips_object_local(hook, flopped);
|
||||
VipsImage *flopped;
|
||||
if (vips_flip(image, &flopped, VIPS_DIRECTION_HORIZONTAL, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, flopped);
|
||||
image = flopped;
|
||||
}
|
||||
|
||||
// Crop/embed
|
||||
if (image->Xsize != baton->width || image->Ysize != baton->height) {
|
||||
if (baton->canvas == EMBED) {
|
||||
if (baton->canvas == Canvas::EMBED) {
|
||||
// Match background colour space, namely sRGB
|
||||
if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
||||
// Convert to sRGB colour space
|
||||
VipsImage *colourspaced = vips_image_new();
|
||||
vips_object_local(hook, colourspaced);
|
||||
VipsImage *colourspaced;
|
||||
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, colourspaced);
|
||||
image = colourspaced;
|
||||
}
|
||||
// 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
|
||||
VipsImage *black = vips_image_new();
|
||||
vips_object_local(hook, black);
|
||||
VipsImage *black;
|
||||
if (vips_black(&black, image->Xsize, image->Ysize, "bands", 1, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
vips_object_local(hook, black);
|
||||
// Invert to become non-transparent
|
||||
VipsImage *alpha = vips_image_new();
|
||||
vips_object_local(hook, alpha);
|
||||
VipsImage *alpha;
|
||||
if (vips_invert(black, &alpha, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(black);
|
||||
vips_object_local(hook, alpha);
|
||||
// Append alpha channel to existing image
|
||||
VipsImage *joined = vips_image_new();
|
||||
vips_object_local(hook, joined);
|
||||
VipsImage *joined;
|
||||
if (vips_bandjoin2(image, alpha, &joined, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(alpha);
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, joined);
|
||||
image = joined;
|
||||
}
|
||||
// Create background
|
||||
@ -473,8 +465,7 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
// Embed
|
||||
int left = (baton->width - image->Xsize) / 2;
|
||||
int top = (baton->height - image->Ysize) / 2;
|
||||
VipsImage *embedded = vips_image_new();
|
||||
vips_object_local(hook, embedded);
|
||||
VipsImage *embedded;
|
||||
if (vips_embed(image, &embedded, left, top, baton->width, baton->height,
|
||||
"extend", VIPS_EXTEND_BACKGROUND, "background", background, NULL
|
||||
)) {
|
||||
@ -482,7 +473,7 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
vips_area_unref(reinterpret_cast<VipsArea*>(background));
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, embedded);
|
||||
image = embedded;
|
||||
} else {
|
||||
// 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);
|
||||
int width = std::min(image->Xsize, baton->width);
|
||||
int height = std::min(image->Ysize, baton->height);
|
||||
VipsImage *extracted = vips_image_new();
|
||||
vips_object_local(hook, extracted);
|
||||
VipsImage *extracted;
|
||||
if (vips_extract_area(image, &extracted, left, top, width, height, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, extracted);
|
||||
image = extracted;
|
||||
}
|
||||
}
|
||||
|
||||
// Post extraction
|
||||
if (baton->topOffsetPost != -1) {
|
||||
VipsImage *extractedPost = vips_image_new();
|
||||
vips_object_local(hook, extractedPost);
|
||||
VipsImage *extractedPost;
|
||||
if (vips_extract_area(image, &extractedPost, baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, extractedPost);
|
||||
image = extractedPost;
|
||||
}
|
||||
|
||||
// Blur
|
||||
if (baton->blurRadius != 0) {
|
||||
VipsImage *blurred = vips_image_new();
|
||||
vips_object_local(hook, blurred);
|
||||
VipsImage *blurred;
|
||||
if (baton->blurRadius == -1) {
|
||||
// Fast, mild blur
|
||||
VipsImage *blur = vips_image_new_matrixv(3, 3,
|
||||
@ -533,14 +521,13 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, blurred);
|
||||
image = blurred;
|
||||
}
|
||||
|
||||
// Sharpen
|
||||
if (baton->sharpenRadius != 0) {
|
||||
VipsImage *sharpened = vips_image_new();
|
||||
vips_object_local(hook, sharpened);
|
||||
VipsImage *sharpened;
|
||||
if (baton->sharpenRadius == -1) {
|
||||
// Fast, mild sharpen
|
||||
VipsImage *sharpen = vips_image_new_matrixv(3, 3,
|
||||
@ -558,54 +545,51 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, sharpened);
|
||||
image = sharpened;
|
||||
}
|
||||
|
||||
// Gamma decoding (brighten)
|
||||
if (baton->gamma >= 1 && baton->gamma <= 3) {
|
||||
VipsImage *gammaDecoded = vips_image_new();
|
||||
vips_object_local(hook, gammaDecoded);
|
||||
VipsImage *gammaDecoded;
|
||||
if (vips_gamma(image, &gammaDecoded, "exponent", baton->gamma, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, gammaDecoded);
|
||||
image = gammaDecoded;
|
||||
}
|
||||
|
||||
// Convert to sRGB colour space, if not already
|
||||
if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
||||
VipsImage *colourspaced = vips_image_new();
|
||||
vips_object_local(hook, colourspaced);
|
||||
VipsImage *colourspaced;
|
||||
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, colourspaced);
|
||||
image = colourspaced;
|
||||
}
|
||||
|
||||
#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+
|
||||
if (baton->progressive) {
|
||||
VipsImage *cached = vips_image_new();
|
||||
vips_object_local(hook, cached);
|
||||
VipsImage *cached;
|
||||
if (vips_tilecache(image, &cached, "threaded", TRUE, "persistent", TRUE, "max_tiles", -1, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
g_object_unref(image);
|
||||
vips_object_local(hook, cached);
|
||||
image = cached;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Output
|
||||
if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == JPEG)) {
|
||||
if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == ImageType::JPEG)) {
|
||||
// Write JPEG to buffer
|
||||
if (vips_jpegsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
|
||||
"Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
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)
|
||||
// Select PNG row filter
|
||||
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
|
||||
@ -622,7 +606,7 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
}
|
||||
#endif
|
||||
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
|
||||
if (vips_webpsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
|
||||
"Q", baton->quality, NULL)) {
|
||||
@ -630,19 +614,19 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
}
|
||||
baton->outputFormat = "webp";
|
||||
} else {
|
||||
bool output_jpeg = is_jpeg(baton->output);
|
||||
bool output_png = is_png(baton->output);
|
||||
bool output_webp = is_webp(baton->output);
|
||||
bool output_tiff = is_tiff(baton->output);
|
||||
bool match_input = !(output_jpeg || output_png || output_webp || output_tiff);
|
||||
if (output_jpeg || (match_input && inputImageType == JPEG)) {
|
||||
bool outputJpeg = IsJpeg(baton->output);
|
||||
bool outputPng = IsPng(baton->output);
|
||||
bool outputWebp = IsWebp(baton->output);
|
||||
bool outputTiff = IsTiff(baton->output);
|
||||
bool matchInput = !(outputJpeg || outputPng || outputWebp || outputTiff);
|
||||
if (outputJpeg || (matchInput && inputImageType == ImageType::JPEG)) {
|
||||
// Write JPEG to file
|
||||
if (vips_jpegsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
|
||||
"Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
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)
|
||||
// Select PNG row filter
|
||||
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
|
||||
@ -659,14 +643,14 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
}
|
||||
#endif
|
||||
baton->outputFormat = "png";
|
||||
} else if (output_webp || (match_input && inputImageType == WEBP)) {
|
||||
} else if (outputWebp || (matchInput && inputImageType == ImageType::WEBP)) {
|
||||
// Write WEBP to file
|
||||
if (vips_webpsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
|
||||
"Q", baton->quality, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
baton->outputFormat = "webp";
|
||||
} else if (output_tiff || (match_input && inputImageType == TIFF)) {
|
||||
} else if (outputTiff || (matchInput && inputImageType == ImageType::TIFF)) {
|
||||
// Write TIFF to file
|
||||
if (vips_tiffsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
|
||||
"compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG, "Q", baton->quality, NULL)) {
|
||||
@ -675,12 +659,10 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
baton->outputFormat = "tiff";
|
||||
} else {
|
||||
(baton->err).append("Unsupported output " + baton->output);
|
||||
g_object_unref(image);
|
||||
return Error(baton, hook);
|
||||
}
|
||||
}
|
||||
// Clean up any dangling image references
|
||||
g_object_unref(image);
|
||||
g_object_unref(hook);
|
||||
// Clean up libvips' per-request data and threads
|
||||
vips_error_clear();
|
||||
@ -724,7 +706,7 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
delete baton;
|
||||
|
||||
// Decrement processing task counter
|
||||
g_atomic_int_dec_and_test(&counter_process);
|
||||
g_atomic_int_dec_and_test(&counterProcess);
|
||||
|
||||
// Return to JavaScript
|
||||
callback->Call(3, argv);
|
||||
@ -742,40 +724,25 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
*/
|
||||
std::tuple<Angle, bool>
|
||||
CalculateRotationAndFlip(int const angle, VipsImage const *input) {
|
||||
Angle rotate = ANGLE_0;
|
||||
Angle rotate = Angle::D0;
|
||||
bool flip = FALSE;
|
||||
if (angle == -1) {
|
||||
const char *exif;
|
||||
if (
|
||||
vips_image_get_typeof(input, "exif-ifd0-Orientation") != 0 &&
|
||||
!vips_image_get_string(input, "exif-ifd0-Orientation", &exif)
|
||||
) {
|
||||
if (exif[0] == 0x36) { // "6"
|
||||
rotate = ANGLE_90;
|
||||
} else if (exif[0] == 0x33) { // "3"
|
||||
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;
|
||||
}
|
||||
switch(ExifOrientation(input)) {
|
||||
case 6: rotate = Angle::D90; break;
|
||||
case 3: rotate = Angle::D180; break;
|
||||
case 8: rotate = Angle::D270; break;
|
||||
case 2: flip = TRUE; break; // flip 1
|
||||
case 7: flip = TRUE; rotate = Angle::D90; break; // flip 6
|
||||
case 4: flip = TRUE; rotate = Angle::D180; break; // flip 3
|
||||
case 5: flip = TRUE; rotate = Angle::D270; break; // flip 8
|
||||
}
|
||||
} else {
|
||||
if (angle == 90) {
|
||||
rotate = ANGLE_90;
|
||||
rotate = Angle::D90;
|
||||
} else if (angle == 180) {
|
||||
rotate = ANGLE_180;
|
||||
rotate = Angle::D180;
|
||||
} else if (angle == 270) {
|
||||
rotate = ANGLE_270;
|
||||
rotate = Angle::D270;
|
||||
}
|
||||
}
|
||||
return std::make_tuple(rotate, flip);
|
||||
@ -817,9 +784,12 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
Clear all thread-local data.
|
||||
*/
|
||||
void Error(ResizeBaton *baton, VipsObject *hook) {
|
||||
// Get libvips' error message
|
||||
(baton->err).append(vips_error_buffer());
|
||||
vips_error_clear();
|
||||
// Clean up any dangling image references
|
||||
g_object_unref(hook);
|
||||
// Clean up libvips' per-request data and threads
|
||||
vips_error_clear();
|
||||
vips_thread_shutdown();
|
||||
}
|
||||
};
|
||||
@ -860,11 +830,11 @@ NAN_METHOD(resize) {
|
||||
// Canvas option
|
||||
Local<String> canvas = options->Get(NanNew<String>("canvas"))->ToString();
|
||||
if (canvas->Equals(NanNew<String>("c"))) {
|
||||
baton->canvas = CROP;
|
||||
baton->canvas = Canvas::CROP;
|
||||
} else if (canvas->Equals(NanNew<String>("m"))) {
|
||||
baton->canvas = MAX;
|
||||
baton->canvas = Canvas::MAX;
|
||||
} else if (canvas->Equals(NanNew<String>("e"))) {
|
||||
baton->canvas = EMBED;
|
||||
baton->canvas = Canvas::EMBED;
|
||||
}
|
||||
// Background colour
|
||||
Local<Array> background = Local<Array>::Cast(options->Get(NanNew<String>("background")));
|
||||
@ -900,7 +870,7 @@ NAN_METHOD(resize) {
|
||||
NanAsyncQueueWorker(new ResizeWorker(callback, baton));
|
||||
|
||||
// Increment queued task counter
|
||||
g_atomic_int_inc(&counter_queue);
|
||||
g_atomic_int_inc(&counterQueue);
|
||||
|
||||
NanReturnUndefined();
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "utilities.h"
|
||||
|
||||
using namespace v8;
|
||||
using namespace sharp;
|
||||
|
||||
/*
|
||||
Get and set cache memory and item limits
|
||||
@ -58,8 +59,8 @@ NAN_METHOD(concurrency) {
|
||||
NAN_METHOD(counters) {
|
||||
NanScope();
|
||||
Local<Object> counters = NanNew<Object>();
|
||||
counters->Set(NanNew<String>("queue"), NanNew<Number>(counter_queue));
|
||||
counters->Set(NanNew<String>("process"), NanNew<Number>(counter_process));
|
||||
counters->Set(NanNew<String>("queue"), NanNew<Number>(counterQueue));
|
||||
counters->Set(NanNew<String>("process"), NanNew<Number>(counterProcess));
|
||||
NanReturnValue(counters);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user