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/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/jcupitt/ruby-vips/issues/28#issuecomment-9014826
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 imageWithoutAlpha.gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
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
- VImage Trim(VImage image, int const tolerance) {
320
- using sharp::MaximumImageAlpha;
321
- // An equivalent of ImageMagick's -trim in C++ ... automatically remove
322
- // "boring" image edges.
323
-
324
- // We use .project to sum the rows and columns of a 0/255 mask image, the first
325
- // non-zero row or column is the object edge. We make the mask image with an
326
- // amount-different-from-background image plus a threshold.
327
-
328
- // find the value of the pixel at (0, 0) ... we will search for all pixels
329
- // significantly different from this
330
- std::vector<double> background = image(0, 0);
331
-
332
- double const max = MaximumImageAlpha(image.interpretation());
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 imageWithoutAlpha.linear(a, b).bandjoin(alpha);
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, int const tolerance);
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->trimTolerance != 0) {
104
- image = sharp::Trim(image, baton->trimTolerance);
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->trimTolerance == 0
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->background[0] * multiplier,
322
- baton->background[1] * multiplier,
323
- baton->background[2] * multiplier
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
- if (image.bands() > 2) {
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->embed);
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->crop < 9) {
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->crop);
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->crop == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION));
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
- if (image.bands() > 2) {
519
- background = {
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", VIPS_INTERPRETATION_B_W));
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
- image.dzsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
938
- ->set("strip", !baton->withMetadata)
939
- ->set("tile_size", baton->tileSize)
940
- ->set("overlap", baton->tileOverlap)
941
- ->set("container", baton->tileContainer)
942
- ->set("layout", baton->tileLayout)
943
- ->set("suffix", const_cast<char*>(suffix.data()))
944
- ->set("angle", CalculateAngleRotation(baton->tileAngle)));
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->crop = AttrTo<int32_t>(options, "crop");
1193
- baton->embed = AttrTo<int32_t>(options, "embed");
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->trimTolerance = AttrTo<int32_t>(options, "trimTolerance");
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->trimTolerance != 0 || baton->normalise ||
1313
- baton->crop == 16 || baton->crop == 17)) {
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