sharp 0.20.5 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/binding.gyp +5 -0
- package/docs/api-channel.md +16 -0
- package/docs/api-colour.md +1 -19
- package/docs/api-composite.md +2 -3
- package/docs/api-constructor.md +2 -2
- package/docs/api-input.md +6 -2
- package/docs/api-operation.md +40 -115
- package/docs/api-output.md +3 -0
- package/docs/api-resize.md +159 -109
- package/docs/api-utility.md +4 -7
- package/docs/changelog.md +87 -0
- package/docs/index.md +10 -4
- package/docs/install.md +18 -38
- package/docs/performance.md +14 -21
- package/install/dll-copy.js +3 -2
- package/install/libvips.js +55 -36
- package/lib/channel.js +18 -0
- package/lib/colour.js +34 -15
- package/lib/composite.js +2 -3
- package/lib/constructor.js +19 -41
- package/lib/input.js +7 -3
- package/lib/libvips.js +38 -6
- package/lib/operation.js +27 -118
- package/lib/output.js +22 -2
- package/lib/platform.js +4 -1
- package/lib/resize.js +308 -104
- package/lib/utility.js +5 -8
- package/package.json +20 -13
- package/src/common.cc +50 -13
- package/src/common.h +12 -10
- package/src/libvips/cplusplus/VImage.cpp +95 -95
- package/src/libvips/cplusplus/vips-operators.cpp +268 -185
- package/src/metadata.cc +15 -0
- package/src/metadata.h +3 -0
- package/src/operations.cc +27 -54
- package/src/operations.h +6 -1
- package/src/pipeline.cc +81 -89
- package/src/pipeline.h +25 -13
- package/src/stats.cc +11 -8
- package/src/stats.h +3 -1
- package/src/utilities.cc +1 -1
package/src/metadata.cc
CHANGED
|
@@ -62,6 +62,12 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|
|
62
62
|
if (sharp::HasDensity(image)) {
|
|
63
63
|
baton->density = sharp::GetDensity(image);
|
|
64
64
|
}
|
|
65
|
+
if (image.get_typeof("jpeg-chroma-subsample") == VIPS_TYPE_REF_STRING) {
|
|
66
|
+
baton->chromaSubsampling = image.get_string("jpeg-chroma-subsample");
|
|
67
|
+
}
|
|
68
|
+
if (image.get_typeof("interlaced") == G_TYPE_INT) {
|
|
69
|
+
baton->isProgressive = image.get_int("interlaced") == 1;
|
|
70
|
+
}
|
|
65
71
|
baton->hasProfile = sharp::HasProfile(image);
|
|
66
72
|
// Derived attributes
|
|
67
73
|
baton->hasAlpha = sharp::HasAlpha(image);
|
|
@@ -117,6 +123,9 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|
|
117
123
|
// Metadata Object
|
|
118
124
|
v8::Local<v8::Object> info = New<v8::Object>();
|
|
119
125
|
Set(info, New("format").ToLocalChecked(), New<v8::String>(baton->format).ToLocalChecked());
|
|
126
|
+
if (baton->input->bufferLength > 0) {
|
|
127
|
+
Set(info, New("size").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->input->bufferLength)));
|
|
128
|
+
}
|
|
120
129
|
Set(info, New("width").ToLocalChecked(), New<v8::Uint32>(baton->width));
|
|
121
130
|
Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(baton->height));
|
|
122
131
|
Set(info, New("space").ToLocalChecked(), New<v8::String>(baton->space).ToLocalChecked());
|
|
@@ -125,6 +134,12 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|
|
125
134
|
if (baton->density > 0) {
|
|
126
135
|
Set(info, New("density").ToLocalChecked(), New<v8::Uint32>(baton->density));
|
|
127
136
|
}
|
|
137
|
+
if (!baton->chromaSubsampling.empty()) {
|
|
138
|
+
Set(info,
|
|
139
|
+
New("chromaSubsampling").ToLocalChecked(),
|
|
140
|
+
New<v8::String>(baton->chromaSubsampling).ToLocalChecked());
|
|
141
|
+
}
|
|
142
|
+
Set(info, New("isProgressive").ToLocalChecked(), New<v8::Boolean>(baton->isProgressive));
|
|
128
143
|
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
|
|
129
144
|
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
|
|
130
145
|
if (baton->orientation > 0) {
|
package/src/metadata.h
CHANGED
|
@@ -31,6 +31,8 @@ struct MetadataBaton {
|
|
|
31
31
|
int channels;
|
|
32
32
|
std::string depth;
|
|
33
33
|
int density;
|
|
34
|
+
std::string chromaSubsampling;
|
|
35
|
+
bool isProgressive;
|
|
34
36
|
bool hasProfile;
|
|
35
37
|
bool hasAlpha;
|
|
36
38
|
int orientation;
|
|
@@ -50,6 +52,7 @@ struct MetadataBaton {
|
|
|
50
52
|
height(0),
|
|
51
53
|
channels(0),
|
|
52
54
|
density(0),
|
|
55
|
+
isProgressive(false),
|
|
53
56
|
hasProfile(false),
|
|
54
57
|
hasAlpha(false),
|
|
55
58
|
orientation(0),
|
package/src/operations.cc
CHANGED
|
@@ -28,6 +28,16 @@ using vips::VError;
|
|
|
28
28
|
|
|
29
29
|
namespace sharp {
|
|
30
30
|
|
|
31
|
+
/*
|
|
32
|
+
Removes alpha channel, if any.
|
|
33
|
+
*/
|
|
34
|
+
VImage RemoveAlpha(VImage image) {
|
|
35
|
+
if (HasAlpha(image)) {
|
|
36
|
+
image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));
|
|
37
|
+
}
|
|
38
|
+
return image;
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
/*
|
|
32
42
|
Composite overlayImage over image at given position
|
|
33
43
|
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
|
@@ -68,7 +78,7 @@ namespace sharp {
|
|
|
68
78
|
//
|
|
69
79
|
// References:
|
|
70
80
|
// - http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
|
|
71
|
-
// - https://github.com/
|
|
81
|
+
// - https://github.com/libvips/ruby-vips/issues/28#issuecomment-9014826
|
|
72
82
|
//
|
|
73
83
|
// out_a = src_a + dst_a * (1 - src_a)
|
|
74
84
|
// ^^^^^^^^^^^
|
|
@@ -223,10 +233,8 @@ namespace sharp {
|
|
|
223
233
|
VImage Gamma(VImage image, double const exponent) {
|
|
224
234
|
if (HasAlpha(image)) {
|
|
225
235
|
// Separate alpha channel
|
|
226
|
-
VImage imageWithoutAlpha = image.extract_band(0,
|
|
227
|
-
VImage::option()->set("n", image.bands() - 1));
|
|
228
236
|
VImage alpha = image[image.bands() - 1];
|
|
229
|
-
return
|
|
237
|
+
return RemoveAlpha(image).gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
|
|
230
238
|
} else {
|
|
231
239
|
return image.gamma(VImage::option()->set("exponent", exponent));
|
|
232
240
|
}
|
|
@@ -316,55 +324,22 @@ namespace sharp {
|
|
|
316
324
|
return image.boolean(imageR, boolean);
|
|
317
325
|
}
|
|
318
326
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
// we need to smooth the image, subtract the background from every pixel, take
|
|
335
|
-
// the absolute value of the difference, then threshold
|
|
336
|
-
VImage mask = (image.median(3) - background).abs() > (max * tolerance / 100);
|
|
337
|
-
|
|
338
|
-
// sum mask rows and columns, then search for the first non-zero sum in each
|
|
339
|
-
// direction
|
|
340
|
-
VImage rows;
|
|
341
|
-
VImage columns = mask.project(&rows);
|
|
342
|
-
|
|
343
|
-
VImage profileLeftV;
|
|
344
|
-
VImage profileLeftH = columns.profile(&profileLeftV);
|
|
345
|
-
|
|
346
|
-
VImage profileRightV;
|
|
347
|
-
VImage profileRightH = columns.fliphor().profile(&profileRightV);
|
|
348
|
-
|
|
349
|
-
VImage profileTopV;
|
|
350
|
-
VImage profileTopH = rows.profile(&profileTopV);
|
|
351
|
-
|
|
352
|
-
VImage profileBottomV;
|
|
353
|
-
VImage profileBottomH = rows.flipver().profile(&profileBottomV);
|
|
354
|
-
|
|
355
|
-
int left = static_cast<int>(floor(profileLeftV.min()));
|
|
356
|
-
int right = columns.width() - static_cast<int>(floor(profileRightV.min()));
|
|
357
|
-
int top = static_cast<int>(floor(profileTopH.min()));
|
|
358
|
-
int bottom = rows.height() - static_cast<int>(floor(profileBottomH.min()));
|
|
359
|
-
|
|
360
|
-
int width = right - left;
|
|
361
|
-
int height = bottom - top;
|
|
362
|
-
|
|
363
|
-
if (width <= 0 || height <= 0) {
|
|
327
|
+
/*
|
|
328
|
+
Trim an image
|
|
329
|
+
*/
|
|
330
|
+
VImage Trim(VImage image, double const threshold) {
|
|
331
|
+
// Top-left pixel provides the background colour
|
|
332
|
+
VImage background = image.extract_area(0, 0, 1, 1);
|
|
333
|
+
if (HasAlpha(background)) {
|
|
334
|
+
background = background.flatten();
|
|
335
|
+
}
|
|
336
|
+
int top, width, height;
|
|
337
|
+
int const left = image.find_trim(&top, &width, &height, VImage::option()
|
|
338
|
+
->set("background", background(0, 0))
|
|
339
|
+
->set("threshold", threshold));
|
|
340
|
+
if (width == 0 || height == 0) {
|
|
364
341
|
throw VError("Unexpected error while trimming. Try to lower the tolerance");
|
|
365
342
|
}
|
|
366
|
-
|
|
367
|
-
// and now crop the original image
|
|
368
343
|
return image.extract_area(left, top, width, height);
|
|
369
344
|
}
|
|
370
345
|
|
|
@@ -374,10 +349,8 @@ namespace sharp {
|
|
|
374
349
|
VImage Linear(VImage image, double const a, double const b) {
|
|
375
350
|
if (HasAlpha(image)) {
|
|
376
351
|
// Separate alpha channel
|
|
377
|
-
VImage imageWithoutAlpha = image.extract_band(0,
|
|
378
|
-
VImage::option()->set("n", image.bands() - 1));
|
|
379
352
|
VImage alpha = image[image.bands() - 1];
|
|
380
|
-
return
|
|
353
|
+
return RemoveAlpha(image).linear(a, b).bandjoin(alpha);
|
|
381
354
|
} else {
|
|
382
355
|
return image.linear(a, b);
|
|
383
356
|
}
|
package/src/operations.h
CHANGED
|
@@ -25,6 +25,11 @@ using vips::VImage;
|
|
|
25
25
|
|
|
26
26
|
namespace sharp {
|
|
27
27
|
|
|
28
|
+
/*
|
|
29
|
+
Removes alpha channel, if any.
|
|
30
|
+
*/
|
|
31
|
+
VImage RemoveAlpha(VImage image);
|
|
32
|
+
|
|
28
33
|
/*
|
|
29
34
|
Alpha composite src over dst with given gravity.
|
|
30
35
|
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
|
|
@@ -95,7 +100,7 @@ namespace sharp {
|
|
|
95
100
|
/*
|
|
96
101
|
Trim an image
|
|
97
102
|
*/
|
|
98
|
-
VImage Trim(VImage image,
|
|
103
|
+
VImage Trim(VImage image, double const threshold);
|
|
99
104
|
|
|
100
105
|
/*
|
|
101
106
|
* Linear adjustment (a * in + b)
|
package/src/pipeline.cc
CHANGED
|
@@ -100,8 +100,10 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// Trim
|
|
103
|
-
if (baton->
|
|
104
|
-
image = sharp::Trim(image, baton->
|
|
103
|
+
if (baton->trimThreshold > 0.0) {
|
|
104
|
+
image = sharp::Trim(image, baton->trimThreshold);
|
|
105
|
+
baton->trimOffsetLeft = image.xoffset();
|
|
106
|
+
baton->trimOffsetTop = image.yoffset();
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
// Pre extraction
|
|
@@ -233,7 +235,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
233
235
|
if (
|
|
234
236
|
xshrink == yshrink && xshrink >= 2 * shrink_on_load_factor &&
|
|
235
237
|
(inputImageType == ImageType::JPEG || inputImageType == ImageType::WEBP) &&
|
|
236
|
-
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->
|
|
238
|
+
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0
|
|
237
239
|
) {
|
|
238
240
|
if (xshrink >= 8 * shrink_on_load_factor) {
|
|
239
241
|
xfactor = xfactor / 8;
|
|
@@ -318,9 +320,9 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
318
320
|
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
|
|
319
321
|
// Background colour
|
|
320
322
|
std::vector<double> background {
|
|
321
|
-
baton->
|
|
322
|
-
baton->
|
|
323
|
-
baton->
|
|
323
|
+
baton->flattenBackground[0] * multiplier,
|
|
324
|
+
baton->flattenBackground[1] * multiplier,
|
|
325
|
+
baton->flattenBackground[2] * multiplier
|
|
324
326
|
};
|
|
325
327
|
image = image.flatten(VImage::option()
|
|
326
328
|
->set("background", background));
|
|
@@ -421,35 +423,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
421
423
|
// Crop/embed
|
|
422
424
|
if (image.width() != baton->width || image.height() != baton->height) {
|
|
423
425
|
if (baton->canvas == Canvas::EMBED) {
|
|
424
|
-
// Scale up 8-bit values to match 16-bit input image
|
|
425
|
-
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
|
|
426
|
-
// Create background colour
|
|
427
426
|
std::vector<double> background;
|
|
428
|
-
|
|
429
|
-
background = {
|
|
430
|
-
multiplier * baton->background[0],
|
|
431
|
-
multiplier * baton->background[1],
|
|
432
|
-
multiplier * baton->background[2]
|
|
433
|
-
};
|
|
434
|
-
} else {
|
|
435
|
-
// Convert sRGB to greyscale
|
|
436
|
-
background = { multiplier * (
|
|
437
|
-
0.2126 * baton->background[0] +
|
|
438
|
-
0.7152 * baton->background[1] +
|
|
439
|
-
0.0722 * baton->background[2])
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
// Add alpha channel to background colour
|
|
443
|
-
if (baton->background[3] < 255.0 || HasAlpha(image)) {
|
|
444
|
-
background.push_back(baton->background[3] * multiplier);
|
|
445
|
-
}
|
|
446
|
-
// Ensure background colour uses correct colourspace
|
|
447
|
-
background = sharp::GetRgbaAsColourspace(background, image.interpretation());
|
|
448
|
-
// Add non-transparent alpha channel, if required
|
|
449
|
-
if (baton->background[3] < 255.0 && !HasAlpha(image)) {
|
|
450
|
-
image = image.bandjoin(
|
|
451
|
-
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
|
|
452
|
-
}
|
|
427
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground);
|
|
453
428
|
|
|
454
429
|
// Embed
|
|
455
430
|
|
|
@@ -463,7 +438,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
463
438
|
int width = std::max(image.width(), baton->width);
|
|
464
439
|
int height = std::max(image.height(), baton->height);
|
|
465
440
|
std::tie(left, top) = sharp::CalculateEmbedPosition(
|
|
466
|
-
image.width(), image.height(), baton->width, baton->height, baton->
|
|
441
|
+
image.width(), image.height(), baton->width, baton->height, baton->position);
|
|
467
442
|
|
|
468
443
|
image = image.embed(left, top, width, height, VImage::option()
|
|
469
444
|
->set("extend", VIPS_EXTEND_BACKGROUND)
|
|
@@ -474,12 +449,12 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
474
449
|
(image.width() > baton->width || image.height() > baton->height)
|
|
475
450
|
) {
|
|
476
451
|
// Crop/max/min
|
|
477
|
-
if (baton->
|
|
452
|
+
if (baton->position < 9) {
|
|
478
453
|
// Gravity-based crop
|
|
479
454
|
int left;
|
|
480
455
|
int top;
|
|
481
456
|
std::tie(left, top) = sharp::CalculateCrop(
|
|
482
|
-
image.width(), image.height(), baton->width, baton->height, baton->
|
|
457
|
+
image.width(), image.height(), baton->width, baton->height, baton->position);
|
|
483
458
|
int width = std::min(image.width(), baton->width);
|
|
484
459
|
int height = std::min(image.height(), baton->height);
|
|
485
460
|
image = image.extract_area(left, top, width, height);
|
|
@@ -495,7 +470,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
495
470
|
->set("access", baton->accessMethod)
|
|
496
471
|
->set("threaded", TRUE));
|
|
497
472
|
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
|
498
|
-
->set("interesting", baton->
|
|
473
|
+
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION));
|
|
499
474
|
baton->hasCropOffset = true;
|
|
500
475
|
baton->cropOffsetLeft = static_cast<int>(image.xoffset());
|
|
501
476
|
baton->cropOffsetTop = static_cast<int>(image.yoffset());
|
|
@@ -503,6 +478,13 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
503
478
|
}
|
|
504
479
|
}
|
|
505
480
|
|
|
481
|
+
// Rotate by degree
|
|
482
|
+
if (baton->rotationAngle != 0.0) {
|
|
483
|
+
std::vector<double> background;
|
|
484
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground);
|
|
485
|
+
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
|
486
|
+
}
|
|
487
|
+
|
|
506
488
|
// Post extraction
|
|
507
489
|
if (baton->topOffsetPost != -1) {
|
|
508
490
|
image = image.extract_area(
|
|
@@ -511,35 +493,9 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
511
493
|
|
|
512
494
|
// Extend edges
|
|
513
495
|
if (baton->extendTop > 0 || baton->extendBottom > 0 || baton->extendLeft > 0 || baton->extendRight > 0) {
|
|
514
|
-
// Scale up 8-bit values to match 16-bit input image
|
|
515
|
-
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
|
|
516
|
-
// Create background colour
|
|
517
496
|
std::vector<double> background;
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
multiplier * baton->background[0],
|
|
521
|
-
multiplier * baton->background[1],
|
|
522
|
-
multiplier * baton->background[2]
|
|
523
|
-
};
|
|
524
|
-
} else {
|
|
525
|
-
// Convert sRGB to greyscale
|
|
526
|
-
background = { multiplier * (
|
|
527
|
-
0.2126 * baton->background[0] +
|
|
528
|
-
0.7152 * baton->background[1] +
|
|
529
|
-
0.0722 * baton->background[2])
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
// Add alpha channel to background colour
|
|
533
|
-
if (baton->background[3] < 255.0 || HasAlpha(image)) {
|
|
534
|
-
background.push_back(baton->background[3] * multiplier);
|
|
535
|
-
}
|
|
536
|
-
// Ensure background colour uses correct colourspace
|
|
537
|
-
background = sharp::GetRgbaAsColourspace(background, image.interpretation());
|
|
538
|
-
// Add non-transparent alpha channel, if required
|
|
539
|
-
if (baton->background[3] < 255.0 && !HasAlpha(image)) {
|
|
540
|
-
image = image.bandjoin(
|
|
541
|
-
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
|
|
542
|
-
}
|
|
497
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->extendBackground);
|
|
498
|
+
|
|
543
499
|
// Embed
|
|
544
500
|
baton->width = image.width() + baton->extendLeft + baton->extendRight;
|
|
545
501
|
baton->height = image.height() + baton->extendTop + baton->extendBottom;
|
|
@@ -694,10 +650,19 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
694
650
|
(baton->err).append("Cannot extract channel from image. Too few channels in image.");
|
|
695
651
|
return Error();
|
|
696
652
|
}
|
|
653
|
+
VipsInterpretation const interpretation = sharp::Is16Bit(image.interpretation())
|
|
654
|
+
? VIPS_INTERPRETATION_GREY16
|
|
655
|
+
: VIPS_INTERPRETATION_B_W;
|
|
697
656
|
image = image
|
|
698
657
|
.extract_band(baton->extractChannel)
|
|
699
|
-
.copy(VImage::option()->set("interpretation",
|
|
658
|
+
.copy(VImage::option()->set("interpretation", interpretation));
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Remove alpha channel, if any
|
|
662
|
+
if (baton->removeAlpha) {
|
|
663
|
+
image = sharp::RemoveAlpha(image);
|
|
700
664
|
}
|
|
665
|
+
|
|
701
666
|
// Convert image to sRGB, if not already
|
|
702
667
|
if (sharp::Is16Bit(image.interpretation())) {
|
|
703
668
|
image = image.cast(VIPS_FORMAT_USHORT);
|
|
@@ -733,6 +698,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
733
698
|
->set("interlace", baton->jpegProgressive)
|
|
734
699
|
->set("no_subsample", baton->jpegChromaSubsampling == "4:4:4")
|
|
735
700
|
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
701
|
+
->set("quant_table", baton->jpegQuantisationTable)
|
|
736
702
|
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
737
703
|
->set("optimize_scans", baton->jpegOptimiseScans)
|
|
738
704
|
->set("optimize_coding", baton->jpegOptimiseCoding)));
|
|
@@ -848,6 +814,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
848
814
|
->set("interlace", baton->jpegProgressive)
|
|
849
815
|
->set("no_subsample", baton->jpegChromaSubsampling == "4:4:4")
|
|
850
816
|
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
817
|
+
->set("quant_table", baton->jpegQuantisationTable)
|
|
851
818
|
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
852
819
|
->set("optimize_scans", baton->jpegOptimiseScans)
|
|
853
820
|
->set("optimize_coding", baton->jpegOptimiseCoding));
|
|
@@ -883,10 +850,6 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
883
850
|
if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
|
|
884
851
|
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
|
|
885
852
|
}
|
|
886
|
-
// Cast pixel values to float, if required
|
|
887
|
-
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
|
|
888
|
-
image = image.cast(VIPS_FORMAT_FLOAT);
|
|
889
|
-
}
|
|
890
853
|
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
891
854
|
->set("strip", !baton->withMetadata)
|
|
892
855
|
->set("Q", baton->tiffQuality)
|
|
@@ -927,6 +890,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
927
890
|
{"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
|
|
928
891
|
{"no_subsample", baton->jpegChromaSubsampling == "4:4:4" ? "TRUE": "FALSE"},
|
|
929
892
|
{"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
|
|
893
|
+
{"quant_table", std::to_string(baton->jpegQuantisationTable)},
|
|
930
894
|
{"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
|
|
931
895
|
{"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
|
|
932
896
|
{"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
|
|
@@ -934,14 +898,22 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
934
898
|
suffix = AssembleSuffixString(extname, options);
|
|
935
899
|
}
|
|
936
900
|
// Write DZ to file
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
901
|
+
vips::VOption *options = VImage::option()
|
|
902
|
+
->set("strip", !baton->withMetadata)
|
|
903
|
+
->set("tile_size", baton->tileSize)
|
|
904
|
+
->set("overlap", baton->tileOverlap)
|
|
905
|
+
->set("container", baton->tileContainer)
|
|
906
|
+
->set("layout", baton->tileLayout)
|
|
907
|
+
->set("suffix", const_cast<char*>(suffix.data()))
|
|
908
|
+
->set("angle", CalculateAngleRotation(baton->tileAngle));
|
|
909
|
+
|
|
910
|
+
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
|
|
911
|
+
// not passing anything - libvips will handle choice
|
|
912
|
+
if (baton->tileDepth < VIPS_FOREIGN_DZ_DEPTH_LAST) {
|
|
913
|
+
options->set("depth", baton->tileDepth);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
|
|
945
917
|
baton->formatOut = "dz";
|
|
946
918
|
} else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
|
|
947
919
|
(willMatchInput && inputImageType == ImageType::VIPS)) {
|
|
@@ -996,6 +968,12 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|
|
996
968
|
Set(info, New("cropOffsetTop").ToLocalChecked(),
|
|
997
969
|
New<v8::Int32>(static_cast<int32_t>(baton->cropOffsetTop)));
|
|
998
970
|
}
|
|
971
|
+
if (baton->trimThreshold > 0.0) {
|
|
972
|
+
Set(info, New("trimOffsetLeft").ToLocalChecked(),
|
|
973
|
+
New<v8::Int32>(static_cast<int32_t>(baton->trimOffsetLeft)));
|
|
974
|
+
Set(info, New("trimOffsetTop").ToLocalChecked(),
|
|
975
|
+
New<v8::Int32>(static_cast<int32_t>(baton->trimOffsetTop)));
|
|
976
|
+
}
|
|
999
977
|
|
|
1000
978
|
if (baton->bufferOutLength > 0) {
|
|
1001
979
|
// Pass ownership of output data to Buffer instance
|
|
@@ -1127,6 +1105,7 @@ NAN_METHOD(pipeline) {
|
|
|
1127
1105
|
using sharp::AttrTo;
|
|
1128
1106
|
using sharp::AttrAs;
|
|
1129
1107
|
using sharp::AttrAsStr;
|
|
1108
|
+
using sharp::AttrAsRgba;
|
|
1130
1109
|
using sharp::CreateInputDescriptor;
|
|
1131
1110
|
|
|
1132
1111
|
// Input Buffers must not undergo GC compaction during processing
|
|
@@ -1170,11 +1149,6 @@ NAN_METHOD(pipeline) {
|
|
|
1170
1149
|
} else if (canvas == "ignore_aspect") {
|
|
1171
1150
|
baton->canvas = Canvas::IGNORE_ASPECT;
|
|
1172
1151
|
}
|
|
1173
|
-
// Background colour
|
|
1174
|
-
v8::Local<v8::Object> background = AttrAs<v8::Object>(options, "background");
|
|
1175
|
-
for (unsigned int i = 0; i < 4; i++) {
|
|
1176
|
-
baton->background[i] = AttrTo<double>(background, i);
|
|
1177
|
-
}
|
|
1178
1152
|
// Tint chroma
|
|
1179
1153
|
baton->tintA = AttrTo<double>(options, "tintA");
|
|
1180
1154
|
baton->tintB = AttrTo<double>(options, "tintB");
|
|
@@ -1189,8 +1163,8 @@ NAN_METHOD(pipeline) {
|
|
|
1189
1163
|
}
|
|
1190
1164
|
// Resize options
|
|
1191
1165
|
baton->withoutEnlargement = AttrTo<bool>(options, "withoutEnlargement");
|
|
1192
|
-
baton->
|
|
1193
|
-
baton->
|
|
1166
|
+
baton->position = AttrTo<int32_t>(options, "position");
|
|
1167
|
+
baton->resizeBackground = AttrAsRgba(options, "resizeBackground");
|
|
1194
1168
|
baton->kernel = AttrAsStr(options, "kernel");
|
|
1195
1169
|
baton->fastShrinkOnLoad = AttrTo<bool>(options, "fastShrinkOnLoad");
|
|
1196
1170
|
// Join Channel Options
|
|
@@ -1208,6 +1182,7 @@ NAN_METHOD(pipeline) {
|
|
|
1208
1182
|
}
|
|
1209
1183
|
// Operators
|
|
1210
1184
|
baton->flatten = AttrTo<bool>(options, "flatten");
|
|
1185
|
+
baton->flattenBackground = AttrAsRgba(options, "flattenBackground");
|
|
1211
1186
|
baton->negate = AttrTo<bool>(options, "negate");
|
|
1212
1187
|
baton->blurSigma = AttrTo<double>(options, "blurSigma");
|
|
1213
1188
|
baton->medianSize = AttrTo<uint32_t>(options, "medianSize");
|
|
@@ -1216,7 +1191,7 @@ NAN_METHOD(pipeline) {
|
|
|
1216
1191
|
baton->sharpenJagged = AttrTo<double>(options, "sharpenJagged");
|
|
1217
1192
|
baton->threshold = AttrTo<int32_t>(options, "threshold");
|
|
1218
1193
|
baton->thresholdGrayscale = AttrTo<bool>(options, "thresholdGrayscale");
|
|
1219
|
-
baton->
|
|
1194
|
+
baton->trimThreshold = AttrTo<double>(options, "trimThreshold");
|
|
1220
1195
|
baton->gamma = AttrTo<double>(options, "gamma");
|
|
1221
1196
|
baton->linearA = AttrTo<double>(options, "linearA");
|
|
1222
1197
|
baton->linearB = AttrTo<double>(options, "linearB");
|
|
@@ -1224,6 +1199,8 @@ NAN_METHOD(pipeline) {
|
|
|
1224
1199
|
baton->normalise = AttrTo<bool>(options, "normalise");
|
|
1225
1200
|
baton->useExifOrientation = AttrTo<bool>(options, "useExifOrientation");
|
|
1226
1201
|
baton->angle = AttrTo<int32_t>(options, "angle");
|
|
1202
|
+
baton->rotationAngle = AttrTo<double>(options, "rotationAngle");
|
|
1203
|
+
baton->rotationBackground = AttrAsRgba(options, "rotationBackground");
|
|
1227
1204
|
baton->rotateBeforePreExtract = AttrTo<bool>(options, "rotateBeforePreExtract");
|
|
1228
1205
|
baton->flip = AttrTo<bool>(options, "flip");
|
|
1229
1206
|
baton->flop = AttrTo<bool>(options, "flop");
|
|
@@ -1231,7 +1208,10 @@ NAN_METHOD(pipeline) {
|
|
|
1231
1208
|
baton->extendBottom = AttrTo<int32_t>(options, "extendBottom");
|
|
1232
1209
|
baton->extendLeft = AttrTo<int32_t>(options, "extendLeft");
|
|
1233
1210
|
baton->extendRight = AttrTo<int32_t>(options, "extendRight");
|
|
1211
|
+
baton->extendBackground = AttrAsRgba(options, "extendBackground");
|
|
1234
1212
|
baton->extractChannel = AttrTo<int32_t>(options, "extractChannel");
|
|
1213
|
+
|
|
1214
|
+
baton->removeAlpha = AttrTo<bool>(options, "removeAlpha");
|
|
1235
1215
|
if (HasAttr(options, "boolean")) {
|
|
1236
1216
|
baton->boolean = CreateInputDescriptor(AttrAs<v8::Object>(options, "boolean"), buffersToPersist);
|
|
1237
1217
|
baton->booleanOp = sharp::GetBooleanOperation(AttrAsStr(options, "booleanOp"));
|
|
@@ -1266,6 +1246,7 @@ NAN_METHOD(pipeline) {
|
|
|
1266
1246
|
baton->jpegProgressive = AttrTo<bool>(options, "jpegProgressive");
|
|
1267
1247
|
baton->jpegChromaSubsampling = AttrAsStr(options, "jpegChromaSubsampling");
|
|
1268
1248
|
baton->jpegTrellisQuantisation = AttrTo<bool>(options, "jpegTrellisQuantisation");
|
|
1249
|
+
baton->jpegQuantisationTable = AttrTo<uint32_t>(options, "jpegQuantisationTable");
|
|
1269
1250
|
baton->jpegOvershootDeringing = AttrTo<bool>(options, "jpegOvershootDeringing");
|
|
1270
1251
|
baton->jpegOptimiseScans = AttrTo<bool>(options, "jpegOptimiseScans");
|
|
1271
1252
|
baton->jpegOptimiseCoding = AttrTo<bool>(options, "jpegOptimiseCoding");
|
|
@@ -1307,10 +1288,21 @@ NAN_METHOD(pipeline) {
|
|
|
1307
1288
|
baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_DZ;
|
|
1308
1289
|
}
|
|
1309
1290
|
baton->tileFormat = AttrAsStr(options, "tileFormat");
|
|
1291
|
+
std::string tileDepth = AttrAsStr(options, "tileDepth");
|
|
1292
|
+
if (tileDepth == "onetile") {
|
|
1293
|
+
baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_ONETILE;
|
|
1294
|
+
} else if (tileDepth == "one") {
|
|
1295
|
+
baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_ONE;
|
|
1296
|
+
} else if (tileDepth == "onepixel") {
|
|
1297
|
+
baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_ONEPIXEL;
|
|
1298
|
+
} else {
|
|
1299
|
+
// signal that we do not want to pass any value to dzSave
|
|
1300
|
+
baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_LAST;
|
|
1301
|
+
}
|
|
1310
1302
|
// Force random access for certain operations
|
|
1311
1303
|
if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL && (
|
|
1312
|
-
baton->
|
|
1313
|
-
baton->
|
|
1304
|
+
baton->trimThreshold > 0.0 || baton->normalise ||
|
|
1305
|
+
baton->position == 16 || baton->position == 17)) {
|
|
1314
1306
|
baton->accessMethod = VIPS_ACCESS_RANDOM;
|
|
1315
1307
|
}
|
|
1316
1308
|
|