sharp 0.33.5 → 0.34.0-rc.1

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/binding.gyp CHANGED
@@ -12,13 +12,13 @@
12
12
  'sharp_libvips_lib_dir': '<!(node -p "require(\'../lib/libvips\').buildSharpLibvipsLibDir()")'
13
13
  },
14
14
  'targets': [{
15
- 'target_name': 'libvips-cpp',
15
+ 'target_name': 'libvips-cpp-<(vips_version)',
16
16
  'conditions': [
17
17
  ['OS == "win"', {
18
18
  # Build libvips C++ binding for Windows due to MSVC std library ABI changes
19
19
  'type': 'shared_library',
20
20
  'defines': [
21
- 'VIPS_CPLUSPLUS_EXPORTS',
21
+ '_VIPS_PUBLIC=__declspec(dllexport)',
22
22
  '_ALLOW_KEYWORD_MACROS'
23
23
  ],
24
24
  'sources': [
@@ -45,6 +45,9 @@
45
45
  'Release': {
46
46
  'msvs_settings': {
47
47
  'VCCLCompilerTool': {
48
+ "AdditionalOptions": [
49
+ "/std:c++17"
50
+ ],
48
51
  'ExceptionHandling': 1,
49
52
  'Optimization': 1,
50
53
  'WholeProgramOptimization': 'true'
@@ -83,7 +86,7 @@
83
86
  ],
84
87
  'dependencies': [
85
88
  '<!(node -p "require(\'node-addon-api\').gyp")',
86
- 'libvips-cpp'
89
+ 'libvips-cpp-<(vips_version)'
87
90
  ],
88
91
  'variables': {
89
92
  'conditions': [
@@ -149,7 +152,7 @@
149
152
  ['OS == "mac"', {
150
153
  'link_settings': {
151
154
  'libraries': [
152
- 'libvips-cpp.42.dylib'
155
+ 'libvips-cpp.<(vips_version).dylib'
153
156
  ]
154
157
  },
155
158
  'xcode_settings': {
@@ -169,9 +172,10 @@
169
172
  ],
170
173
  'link_settings': {
171
174
  'libraries': [
172
- '-l:libvips-cpp.so.42'
175
+ '-l:libvips-cpp.so.<(vips_version)'
173
176
  ],
174
177
  'ldflags': [
178
+ '-lstdc++fs',
175
179
  '-Wl,-s',
176
180
  '-Wl,--disable-new-dtags',
177
181
  '-Wl,-z,nodelete',
@@ -207,14 +211,14 @@
207
211
  }]
208
212
  ],
209
213
  'cflags_cc': [
210
- '-std=c++0x',
214
+ '-std=c++17',
211
215
  '-fexceptions',
212
216
  '-Wall',
213
217
  '-Os'
214
218
  ],
215
219
  'xcode_settings': {
216
- 'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
217
- 'MACOSX_DEPLOYMENT_TARGET': '10.13',
220
+ 'CLANG_CXX_LANGUAGE_STANDARD': 'c++17',
221
+ 'MACOSX_DEPLOYMENT_TARGET': '10.15',
218
222
  'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
219
223
  'GCC_ENABLE_CPP_RTTI': 'YES',
220
224
  'OTHER_CPLUSPLUSFLAGS': [
@@ -234,6 +238,9 @@
234
238
  ['OS == "win"', {
235
239
  'msvs_settings': {
236
240
  'VCCLCompilerTool': {
241
+ "AdditionalOptions": [
242
+ "/std:c++17"
243
+ ],
237
244
  'ExceptionHandling': 1,
238
245
  'Optimization': 1,
239
246
  'WholeProgramOptimization': 'true'
package/src/common.cc CHANGED
@@ -109,6 +109,10 @@ namespace sharp {
109
109
  if (HasAttr(input, "subifd")) {
110
110
  descriptor->subifd = AttrAsInt32(input, "subifd");
111
111
  }
112
+ // // PDF background color
113
+ if (HasAttr(input, "pdfBackground")) {
114
+ descriptor->pdfBackground = AttrAsVectorOfDouble(input, "pdfBackground");
115
+ }
112
116
  // Create new image
113
117
  if (HasAttr(input, "createChannels")) {
114
118
  descriptor->createChannels = AttrAsUint32(input, "createChannels");
@@ -156,12 +160,34 @@ namespace sharp {
156
160
  descriptor->textWrap = AttrAsEnum<VipsTextWrap>(input, "textWrap", VIPS_TYPE_TEXT_WRAP);
157
161
  }
158
162
  }
163
+ // Join images together
164
+ if (HasAttr(input, "joinAnimated")) {
165
+ descriptor->joinAnimated = AttrAsBool(input, "joinAnimated");
166
+ }
167
+ if (HasAttr(input, "joinAcross")) {
168
+ descriptor->joinAcross = AttrAsUint32(input, "joinAcross");
169
+ }
170
+ if (HasAttr(input, "joinShim")) {
171
+ descriptor->joinShim = AttrAsUint32(input, "joinShim");
172
+ }
173
+ if (HasAttr(input, "joinBackground")) {
174
+ descriptor->joinBackground = AttrAsVectorOfDouble(input, "joinBackground");
175
+ }
176
+ if (HasAttr(input, "joinHalign")) {
177
+ descriptor->joinHalign = AttrAsEnum<VipsAlign>(input, "joinHalign", VIPS_TYPE_ALIGN);
178
+ }
179
+ if (HasAttr(input, "joinValign")) {
180
+ descriptor->joinValign = AttrAsEnum<VipsAlign>(input, "joinValign", VIPS_TYPE_ALIGN);
181
+ }
159
182
  // Limit input images to a given number of pixels, where pixels = width * height
160
183
  descriptor->limitInputPixels = static_cast<uint64_t>(AttrAsInt64(input, "limitInputPixels"));
161
- // Allow switch from random to sequential access
162
- descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
184
+ if (HasAttr(input, "access")) {
185
+ descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
186
+ }
163
187
  // Remove safety features and allow unlimited input
164
188
  descriptor->unlimited = AttrAsBool(input, "unlimited");
189
+ // Use the EXIF orientation to auto orient the image
190
+ descriptor->autoOrient = AttrAsBool(input, "autoOrient");
165
191
  return descriptor;
166
192
  }
167
193
 
@@ -244,6 +270,7 @@ namespace sharp {
244
270
  case ImageType::FITS: id = "fits"; break;
245
271
  case ImageType::EXR: id = "exr"; break;
246
272
  case ImageType::JXL: id = "jxl"; break;
273
+ case ImageType::RAD: id = "rad"; break;
247
274
  case ImageType::VIPS: id = "vips"; break;
248
275
  case ImageType::RAW: id = "raw"; break;
249
276
  case ImageType::UNKNOWN: id = "unknown"; break;
@@ -290,6 +317,8 @@ namespace sharp {
290
317
  { "VipsForeignLoadOpenexr", ImageType::EXR },
291
318
  { "VipsForeignLoadJxlFile", ImageType::JXL },
292
319
  { "VipsForeignLoadJxlBuffer", ImageType::JXL },
320
+ { "VipsForeignLoadRadFile", ImageType::RAD },
321
+ { "VipsForeignLoadRadBuffer", ImageType::RAD },
293
322
  { "VipsForeignLoadVips", ImageType::VIPS },
294
323
  { "VipsForeignLoadVipsFile", ImageType::VIPS },
295
324
  { "VipsForeignLoadRaw", ImageType::RAW }
@@ -402,6 +431,9 @@ namespace sharp {
402
431
  if (imageType == ImageType::TIFF) {
403
432
  option->set("subifd", descriptor->subifd);
404
433
  }
434
+ if (imageType == ImageType::PDF) {
435
+ option->set("background", descriptor->pdfBackground);
436
+ }
405
437
  image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
406
438
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
407
439
  image = SetDensity(image, descriptor->density);
@@ -506,6 +538,9 @@ namespace sharp {
506
538
  if (imageType == ImageType::TIFF) {
507
539
  option->set("subifd", descriptor->subifd);
508
540
  }
541
+ if (imageType == ImageType::PDF) {
542
+ option->set("background", descriptor->pdfBackground);
543
+ }
509
544
  image = VImage::new_from_file(descriptor->file.data(), option);
510
545
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
511
546
  image = SetDensity(image, descriptor->density);
@@ -560,14 +595,6 @@ namespace sharp {
560
595
  return image;
561
596
  }
562
597
 
563
- /*
564
- Does this image have an alpha channel?
565
- Uses colour space interpretation with number of channels to guess this.
566
- */
567
- bool HasAlpha(VImage image) {
568
- return image.has_alpha();
569
- }
570
-
571
598
  static void* RemoveExifCallback(VipsImage *image, char const *field, GValue *value, void *data) {
572
599
  std::vector<std::string> *fieldNames = static_cast<std::vector<std::string> *>(data);
573
600
  std::string fieldName(field);
@@ -976,13 +1003,13 @@ namespace sharp {
976
1003
  };
977
1004
  }
978
1005
  // Add alpha channel to alphaColour colour
979
- if (colour[3] < 255.0 || HasAlpha(image)) {
1006
+ if (colour[3] < 255.0 || image.has_alpha()) {
980
1007
  alphaColour.push_back(colour[3] * multiplier);
981
1008
  }
982
1009
  // Ensure alphaColour colour uses correct colourspace
983
1010
  alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation(), premultiply);
984
1011
  // Add non-transparent alpha channel, if required
985
- if (colour[3] < 255.0 && !HasAlpha(image)) {
1012
+ if (colour[3] < 255.0 && !image.has_alpha()) {
986
1013
  image = image.bandjoin(
987
1014
  VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier).cast(image.format()));
988
1015
  }
@@ -990,10 +1017,10 @@ namespace sharp {
990
1017
  }
991
1018
 
992
1019
  /*
993
- Removes alpha channel, if any.
1020
+ Removes alpha channels, if any.
994
1021
  */
995
1022
  VImage RemoveAlpha(VImage image) {
996
- if (HasAlpha(image)) {
1023
+ while (image.bands() > 1 && image.has_alpha()) {
997
1024
  image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));
998
1025
  }
999
1026
  return image;
@@ -1003,7 +1030,7 @@ namespace sharp {
1003
1030
  Ensures alpha channel, if missing.
1004
1031
  */
1005
1032
  VImage EnsureAlpha(VImage image, double const value) {
1006
- if (!HasAlpha(image)) {
1033
+ if (!image.has_alpha()) {
1007
1034
  std::vector<double> alpha;
1008
1035
  alpha.push_back(value * sharp::MaximumImageAlpha(image.interpretation()));
1009
1036
  image = image.bandjoin_const(alpha);
package/src/common.h CHANGED
@@ -15,18 +15,14 @@
15
15
  // Verify platform and compiler compatibility
16
16
 
17
17
  #if (VIPS_MAJOR_VERSION < 8) || \
18
- (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 15) || \
19
- (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 15 && VIPS_MICRO_VERSION < 3)
20
- #error "libvips version 8.15.3+ is required - please see https://sharp.pixelplumbing.com/install"
18
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 16) || \
19
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 16 && VIPS_MICRO_VERSION < 1)
20
+ #error "libvips version 8.16.1+ is required - please see https://sharp.pixelplumbing.com/install"
21
21
  #endif
22
22
 
23
- #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
24
- #error "GCC version 4.6+ is required for C++11 features - please see https://sharp.pixelplumbing.com/install"
25
- #endif
26
-
27
- #if (defined(__clang__) && defined(__has_feature))
28
- #if (!__has_feature(cxx_range_for))
29
- #error "clang version 3.0+ is required for C++11 features - please see https://sharp.pixelplumbing.com/install"
23
+ #if defined(__has_include)
24
+ #if !__has_include(<filesystem>)
25
+ #error "C++17 compiler required - please see https://sharp.pixelplumbing.com/install"
30
26
  #endif
31
27
  #endif
32
28
 
@@ -37,6 +33,7 @@ namespace sharp {
37
33
  struct InputDescriptor { // NOLINT(runtime/indentation_namespace)
38
34
  std::string name;
39
35
  std::string file;
36
+ bool autoOrient;
40
37
  char *buffer;
41
38
  VipsFailOn failOn;
42
39
  uint64_t limitInputPixels;
@@ -74,13 +71,21 @@ namespace sharp {
74
71
  int textSpacing;
75
72
  VipsTextWrap textWrap;
76
73
  int textAutofitDpi;
74
+ bool joinAnimated;
75
+ int joinAcross;
76
+ int joinShim;
77
+ std::vector<double> joinBackground;
78
+ VipsAlign joinHalign;
79
+ VipsAlign joinValign;
80
+ std::vector<double> pdfBackground;
77
81
 
78
82
  InputDescriptor():
83
+ autoOrient(false),
79
84
  buffer(nullptr),
80
85
  failOn(VIPS_FAIL_ON_WARNING),
81
86
  limitInputPixels(0x3FFF * 0x3FFF),
82
87
  unlimited(false),
83
- access(VIPS_ACCESS_RANDOM),
88
+ access(VIPS_ACCESS_SEQUENTIAL),
84
89
  bufferLength(0),
85
90
  isBuffer(false),
86
91
  density(72.0),
@@ -108,7 +113,14 @@ namespace sharp {
108
113
  textRgba(false),
109
114
  textSpacing(0),
110
115
  textWrap(VIPS_TEXT_WRAP_WORD),
111
- textAutofitDpi(0) {}
116
+ textAutofitDpi(0),
117
+ joinAnimated(false),
118
+ joinAcross(1),
119
+ joinShim(0),
120
+ joinBackground{ 0.0, 0.0, 0.0, 255.0 },
121
+ joinHalign(VIPS_ALIGN_LOW),
122
+ joinValign(VIPS_ALIGN_LOW),
123
+ pdfBackground{ 255.0, 255.0, 255.0, 255.0 } {}
112
124
  };
113
125
 
114
126
  // Convenience methods to access the attributes of a Napi::Object
@@ -147,6 +159,7 @@ namespace sharp {
147
159
  FITS,
148
160
  EXR,
149
161
  JXL,
162
+ RAD,
150
163
  VIPS,
151
164
  RAW,
152
165
  UNKNOWN,
@@ -232,12 +245,6 @@ namespace sharp {
232
245
  */
233
246
  VImage SetProfile(VImage image, std::pair<char*, size_t> icc);
234
247
 
235
- /*
236
- Does this image have an alpha channel?
237
- Uses colour space interpretation with number of channels to guess this.
238
- */
239
- bool HasAlpha(VImage image);
240
-
241
248
  /*
242
249
  Remove all EXIF-related image fields.
243
250
  */
@@ -368,7 +375,7 @@ namespace sharp {
368
375
  std::tuple<VImage, std::vector<double>> ApplyAlpha(VImage image, std::vector<double> colour, bool premultiply);
369
376
 
370
377
  /*
371
- Removes alpha channel, if any.
378
+ Removes alpha channels, if any.
372
379
  */
373
380
  VImage RemoveAlpha(VImage image);
374
381
 
package/src/metadata.cc CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include <numeric>
5
5
  #include <vector>
6
+ #include <cmath>
6
7
 
7
8
  #include <napi.h>
8
9
  #include <vips/vips8>
@@ -47,8 +48,11 @@ class MetadataWorker : public Napi::AsyncWorker {
47
48
  if (image.get_typeof("interlaced") == G_TYPE_INT) {
48
49
  baton->isProgressive = image.get_int("interlaced") == 1;
49
50
  }
50
- if (image.get_typeof("palette-bit-depth") == G_TYPE_INT) {
51
- baton->paletteBitDepth = image.get_int("palette-bit-depth");
51
+ if (image.get_typeof(VIPS_META_PALETTE) == G_TYPE_INT) {
52
+ baton->isPalette = image.get_int(VIPS_META_PALETTE);
53
+ }
54
+ if (image.get_typeof(VIPS_META_BITS_PER_SAMPLE) == G_TYPE_INT) {
55
+ baton->bitsPerSample = image.get_int(VIPS_META_BITS_PER_SAMPLE);
52
56
  }
53
57
  if (image.get_typeof(VIPS_META_N_PAGES) == G_TYPE_INT) {
54
58
  baton->pages = image.get_int(VIPS_META_N_PAGES);
@@ -91,7 +95,7 @@ class MetadataWorker : public Napi::AsyncWorker {
91
95
  baton->background = image.get_array_double("background");
92
96
  }
93
97
  // Derived attributes
94
- baton->hasAlpha = sharp::HasAlpha(image);
98
+ baton->hasAlpha = image.has_alpha();
95
99
  baton->orientation = sharp::ExifOrientation(image);
96
100
  // EXIF
97
101
  if (image.get_typeof(VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
@@ -171,8 +175,13 @@ class MetadataWorker : public Napi::AsyncWorker {
171
175
  info.Set("chromaSubsampling", baton->chromaSubsampling);
172
176
  }
173
177
  info.Set("isProgressive", baton->isProgressive);
174
- if (baton->paletteBitDepth > 0) {
175
- info.Set("paletteBitDepth", baton->paletteBitDepth);
178
+ info.Set("isPalette", baton->isPalette);
179
+ if (baton->bitsPerSample > 0) {
180
+ info.Set("bitsPerSample", baton->bitsPerSample);
181
+ if (baton->isPalette) {
182
+ // Deprecated, remove with libvips 8.17.0
183
+ info.Set("paletteBitDepth", baton->bitsPerSample);
184
+ }
176
185
  }
177
186
  if (baton->pages > 0) {
178
187
  info.Set("pages", baton->pages);
@@ -218,21 +227,30 @@ class MetadataWorker : public Napi::AsyncWorker {
218
227
  info.Set("subifds", baton->subifds);
219
228
  }
220
229
  if (!baton->background.empty()) {
230
+ Napi::Object background = Napi::Object::New(env);
221
231
  if (baton->background.size() == 3) {
222
- Napi::Object background = Napi::Object::New(env);
223
232
  background.Set("r", baton->background[0]);
224
233
  background.Set("g", baton->background[1]);
225
234
  background.Set("b", baton->background[2]);
226
- info.Set("background", background);
227
235
  } else {
228
- info.Set("background", baton->background[0]);
236
+ background.Set("gray", round(baton->background[0] * 100 / 255));
229
237
  }
238
+ info.Set("background", background);
230
239
  }
231
240
  info.Set("hasProfile", baton->hasProfile);
232
241
  info.Set("hasAlpha", baton->hasAlpha);
233
242
  if (baton->orientation > 0) {
234
243
  info.Set("orientation", baton->orientation);
235
244
  }
245
+ Napi::Object autoOrient = Napi::Object::New(env);
246
+ info.Set("autoOrient", autoOrient);
247
+ if (baton->orientation >= 5) {
248
+ autoOrient.Set("width", baton->height);
249
+ autoOrient.Set("height", baton->width);
250
+ } else {
251
+ autoOrient.Set("width", baton->width);
252
+ autoOrient.Set("height", baton->height);
253
+ }
236
254
  if (baton->exifLength > 0) {
237
255
  info.Set("exif", Napi::Buffer<char>::NewOrCopy(env, baton->exif, baton->exifLength, sharp::FreeCallback));
238
256
  }
package/src/metadata.h CHANGED
@@ -24,7 +24,8 @@ struct MetadataBaton {
24
24
  int density;
25
25
  std::string chromaSubsampling;
26
26
  bool isProgressive;
27
- int paletteBitDepth;
27
+ bool isPalette;
28
+ int bitsPerSample;
28
29
  int pages;
29
30
  int pageHeight;
30
31
  int loop;
@@ -59,7 +60,8 @@ struct MetadataBaton {
59
60
  channels(0),
60
61
  density(0),
61
62
  isProgressive(false),
62
- paletteBitDepth(0),
63
+ isPalette(false),
64
+ bitsPerSample(0),
63
65
  pages(0),
64
66
  pageHeight(0),
65
67
  loop(-1),
package/src/operations.cc CHANGED
@@ -40,7 +40,7 @@ namespace sharp {
40
40
  typeBeforeTint = VIPS_INTERPRETATION_sRGB;
41
41
  }
42
42
  // Apply lookup table
43
- if (HasAlpha(image)) {
43
+ if (image.has_alpha()) {
44
44
  VImage alpha = image[image.bands() - 1];
45
45
  image = RemoveAlpha(image)
46
46
  .colourspace(VIPS_INTERPRETATION_B_W)
@@ -83,7 +83,7 @@ namespace sharp {
83
83
  // Scale luminance, join to chroma, convert back to original colourspace
84
84
  VImage normalized = luminance.linear(f, a).bandjoin(chroma).colourspace(typeBeforeNormalize);
85
85
  // Attach original alpha channel, if any
86
- if (HasAlpha(image)) {
86
+ if (image.has_alpha()) {
87
87
  // Extract original alpha channel
88
88
  VImage alpha = image[image.bands() - 1];
89
89
  // Join alpha channel to normalised image
@@ -106,7 +106,7 @@ namespace sharp {
106
106
  * Gamma encoding/decoding
107
107
  */
108
108
  VImage Gamma(VImage image, double const exponent) {
109
- if (HasAlpha(image)) {
109
+ if (image.has_alpha()) {
110
110
  // Separate alpha channel
111
111
  VImage alpha = image[image.bands() - 1];
112
112
  return RemoveAlpha(image).gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
@@ -132,7 +132,7 @@ namespace sharp {
132
132
  * Produce the "negative" of the image.
133
133
  */
134
134
  VImage Negate(VImage image, bool const negateAlpha) {
135
- if (HasAlpha(image) && !negateAlpha) {
135
+ if (image.has_alpha() && !negateAlpha) {
136
136
  // Separate alpha channel
137
137
  VImage alpha = image[image.bands() - 1];
138
138
  return RemoveAlpha(image).invert().bandjoin(alpha);
@@ -205,7 +205,7 @@ namespace sharp {
205
205
  VImage Modulate(VImage image, double const brightness, double const saturation,
206
206
  int const hue, double const lightness) {
207
207
  VipsInterpretation colourspaceBeforeModulate = image.interpretation();
208
- if (HasAlpha(image)) {
208
+ if (image.has_alpha()) {
209
209
  // Separate alpha channel
210
210
  VImage alpha = image[image.bands() - 1];
211
211
  return RemoveAlpha(image)
@@ -297,7 +297,7 @@ namespace sharp {
297
297
  threshold *= 256.0;
298
298
  }
299
299
  std::vector<double> backgroundAlpha({ background.back() });
300
- if (HasAlpha(image)) {
300
+ if (image.has_alpha()) {
301
301
  background.pop_back();
302
302
  } else {
303
303
  background.resize(image.bands());
@@ -307,7 +307,7 @@ namespace sharp {
307
307
  ->set("background", background)
308
308
  ->set("line_art", lineArt)
309
309
  ->set("threshold", threshold));
310
- if (HasAlpha(image)) {
310
+ if (image.has_alpha()) {
311
311
  // Search alpha channel (A)
312
312
  int leftA, topA, widthA, heightA;
313
313
  VImage alpha = image[image.bands() - 1];
@@ -344,7 +344,7 @@ namespace sharp {
344
344
  throw VError("Band expansion using linear is unsupported");
345
345
  }
346
346
  bool const uchar = !Is16Bit(image.interpretation());
347
- if (HasAlpha(image) && a.size() != bands && (a.size() == 1 || a.size() == bands - 1 || bands - 1 == 1)) {
347
+ if (image.has_alpha() && a.size() != bands && (a.size() == 1 || a.size() == bands - 1 || bands - 1 == 1)) {
348
348
  // Separate alpha channel
349
349
  VImage alpha = image[bands - 1];
350
350
  return RemoveAlpha(image).linear(a, b, VImage::option()->set("uchar", uchar)).bandjoin(alpha);
@@ -357,7 +357,7 @@ namespace sharp {
357
357
  * Unflatten
358
358
  */
359
359
  VImage Unflatten(VImage image) {
360
- if (HasAlpha(image)) {
360
+ if (image.has_alpha()) {
361
361
  VImage alpha = image[image.bands() - 1];
362
362
  VImage noAlpha = RemoveAlpha(image);
363
363
  return noAlpha.bandjoin(alpha & (noAlpha.colourspace(VIPS_INTERPRETATION_B_W) < 255));
@@ -472,4 +472,26 @@ namespace sharp {
472
472
  }
473
473
  }
474
474
 
475
+ /*
476
+ * Dilate an image
477
+ */
478
+ VImage Dilate(VImage image, int const width) {
479
+ int const maskWidth = 2 * width + 1;
480
+ VImage mask = VImage::new_matrix(maskWidth, maskWidth);
481
+ return image.morph(
482
+ mask,
483
+ VIPS_OPERATION_MORPHOLOGY_DILATE).invert();
484
+ }
485
+
486
+ /*
487
+ * Erode an image
488
+ */
489
+ VImage Erode(VImage image, int const width) {
490
+ int const maskWidth = 2 * width + 1;
491
+ VImage mask = VImage::new_matrix(maskWidth, maskWidth);
492
+ return image.morph(
493
+ mask,
494
+ VIPS_OPERATION_MORPHOLOGY_ERODE).invert();
495
+ }
496
+
475
497
  } // namespace sharp
package/src/operations.h CHANGED
@@ -120,6 +120,15 @@ namespace sharp {
120
120
  VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
121
121
  VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight);
122
122
 
123
+ /*
124
+ * Dilate an image
125
+ */
126
+ VImage Dilate(VImage image, int const maskWidth);
127
+
128
+ /*
129
+ * Erode an image
130
+ */
131
+ VImage Erode(VImage image, int const maskWidth);
123
132
  } // namespace sharp
124
133
 
125
134
  #endif // SRC_OPERATIONS_H_