diff --git a/src/common.cc b/src/common.cc index 3399b7bd..79d10eea 100755 --- a/src/common.cc +++ b/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(buffer), length)) { - if (!vips_tiffload_buffer(buffer, length, image, "access", access, NULL)) { - imageType = TIFF; - } + } else if (buffer_is_tiff(static_cast(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 diff --git a/src/common.h b/src/common.h index f79e2302..7913f7bf 100755 --- a/src/common.h +++ b/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 diff --git a/src/metadata.cc b/src/metadata.cc index 38802ef1..031a7c27 100755 --- a/src/metadata.cc +++ b/src/metadata.cc @@ -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(); } diff --git a/src/resize.cc b/src/resize.cc index ddd015f4..26c6cdbf 100755 --- a/src/resize.cc +++ b/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(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(inputWidth) / static_cast(baton->width); double yfactor = static_cast(inputHeight) / static_cast(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(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(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(background)); return Error(baton, hook); }; vips_area_unref(reinterpret_cast(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(baton->width) / static_cast(shrunkWidth); double residualy = static_cast(baton->height) / static_cast(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(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(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 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 canvas = options->Get(NanNew("canvas"))->ToString(); if (canvas->Equals(NanNew("c"))) { - baton->canvas = CROP; + baton->canvas = Canvas::CROP; } else if (canvas->Equals(NanNew("m"))) { - baton->canvas = MAX; + baton->canvas = Canvas::MAX; } else if (canvas->Equals(NanNew("e"))) { - baton->canvas = EMBED; + baton->canvas = Canvas::EMBED; } // Background colour Local background = Local::Cast(options->Get(NanNew("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(); } diff --git a/src/utilities.cc b/src/utilities.cc index 0ee2fd82..ca3037b8 100755 --- a/src/utilities.cc +++ b/src/utilities.cc @@ -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 counters = NanNew(); - counters->Set(NanNew("queue"), NanNew(counter_queue)); - counters->Set(NanNew("process"), NanNew(counter_process)); + counters->Set(NanNew("queue"), NanNew(counterQueue)); + counters->Set(NanNew("process"), NanNew(counterProcess)); NanReturnValue(counters); }