@revizly/sharp 0.33.2-revizly13

Sign up to get free protection for your applications and to get access to all the features.
package/src/metadata.h ADDED
@@ -0,0 +1,82 @@
1
+ // Copyright 2013 Lovell Fuller and others.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ #ifndef SRC_METADATA_H_
5
+ #define SRC_METADATA_H_
6
+
7
+ #include <string>
8
+ #include <napi.h>
9
+
10
+ #include "./common.h"
11
+
12
+ struct MetadataBaton {
13
+ // Input
14
+ sharp::InputDescriptor *input;
15
+ // Output
16
+ std::string format;
17
+ int width;
18
+ int height;
19
+ std::string space;
20
+ int channels;
21
+ std::string depth;
22
+ int density;
23
+ std::string chromaSubsampling;
24
+ bool isProgressive;
25
+ int paletteBitDepth;
26
+ int pages;
27
+ int pageHeight;
28
+ int loop;
29
+ std::vector<int> delay;
30
+ int pagePrimary;
31
+ std::string compression;
32
+ std::string resolutionUnit;
33
+ std::string formatMagick;
34
+ std::vector<std::pair<int, int>> levels;
35
+ int subifds;
36
+ std::vector<double> background;
37
+ bool hasProfile;
38
+ bool hasAlpha;
39
+ int orientation;
40
+ char *exif;
41
+ size_t exifLength;
42
+ char *icc;
43
+ size_t iccLength;
44
+ char *iptc;
45
+ size_t iptcLength;
46
+ char *xmp;
47
+ size_t xmpLength;
48
+ char *tifftagPhotoshop;
49
+ size_t tifftagPhotoshopLength;
50
+ std::string err;
51
+
52
+ MetadataBaton():
53
+ input(nullptr),
54
+ width(0),
55
+ height(0),
56
+ channels(0),
57
+ density(0),
58
+ isProgressive(false),
59
+ paletteBitDepth(0),
60
+ pages(0),
61
+ pageHeight(0),
62
+ loop(-1),
63
+ pagePrimary(-1),
64
+ subifds(0),
65
+ hasProfile(false),
66
+ hasAlpha(false),
67
+ orientation(0),
68
+ exif(nullptr),
69
+ exifLength(0),
70
+ icc(nullptr),
71
+ iccLength(0),
72
+ iptc(nullptr),
73
+ iptcLength(0),
74
+ xmp(nullptr),
75
+ xmpLength(0),
76
+ tifftagPhotoshop(nullptr),
77
+ tifftagPhotoshopLength(0) {}
78
+ };
79
+
80
+ Napi::Value metadata(const Napi::CallbackInfo& info);
81
+
82
+ #endif // SRC_METADATA_H_
@@ -0,0 +1,471 @@
1
+ // Copyright 2013 Lovell Fuller and others.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ #include <algorithm>
5
+ #include <functional>
6
+ #include <memory>
7
+ #include <tuple>
8
+ #include <vector>
9
+ #include <vips/vips8>
10
+
11
+ #include "common.h"
12
+ #include "operations.h"
13
+
14
+ using vips::VImage;
15
+ using vips::VError;
16
+
17
+ namespace sharp {
18
+ /*
19
+ * Tint an image using the provided RGB.
20
+ */
21
+ VImage Tint(VImage image, std::vector<double> const tint) {
22
+ std::vector<double> const tintLab = (VImage::black(1, 1) + tint)
23
+ .colourspace(VIPS_INTERPRETATION_LAB, VImage::option()->set("source_space", VIPS_INTERPRETATION_sRGB))
24
+ .getpoint(0, 0);
25
+ // LAB identity function
26
+ VImage identityLab = VImage::identity(VImage::option()->set("bands", 3))
27
+ .colourspace(VIPS_INTERPRETATION_LAB, VImage::option()->set("source_space", VIPS_INTERPRETATION_sRGB));
28
+ // Scale luminance range, 0.0 to 1.0
29
+ VImage l = identityLab[0] / 100;
30
+ // Weighting functions
31
+ VImage weightL = 1.0 - 4.0 * ((l - 0.5) * (l - 0.5));
32
+ VImage weightAB = (weightL * tintLab).extract_band(1, VImage::option()->set("n", 2));
33
+ identityLab = identityLab[0].bandjoin(weightAB);
34
+ // Convert lookup table to sRGB
35
+ VImage lut = identityLab.colourspace(VIPS_INTERPRETATION_sRGB,
36
+ VImage::option()->set("source_space", VIPS_INTERPRETATION_LAB));
37
+ // Original colourspace
38
+ VipsInterpretation typeBeforeTint = image.interpretation();
39
+ if (typeBeforeTint == VIPS_INTERPRETATION_RGB) {
40
+ typeBeforeTint = VIPS_INTERPRETATION_sRGB;
41
+ }
42
+ // Apply lookup table
43
+ if (HasAlpha(image)) {
44
+ VImage alpha = image[image.bands() - 1];
45
+ image = RemoveAlpha(image)
46
+ .colourspace(VIPS_INTERPRETATION_B_W)
47
+ .maplut(lut)
48
+ .colourspace(typeBeforeTint)
49
+ .bandjoin(alpha);
50
+ } else {
51
+ image = image
52
+ .colourspace(VIPS_INTERPRETATION_B_W)
53
+ .maplut(lut)
54
+ .colourspace(typeBeforeTint);
55
+ }
56
+ return image;
57
+ }
58
+
59
+ /*
60
+ * Stretch luminance to cover full dynamic range.
61
+ */
62
+ VImage Normalise(VImage image, int const lower, int const upper) {
63
+ // Get original colourspace
64
+ VipsInterpretation typeBeforeNormalize = image.interpretation();
65
+ if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
66
+ typeBeforeNormalize = VIPS_INTERPRETATION_sRGB;
67
+ }
68
+ // Convert to LAB colourspace
69
+ VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
70
+ // Extract luminance
71
+ VImage luminance = lab[0];
72
+
73
+ // Find luminance range
74
+ int const min = lower == 0 ? luminance.min() : luminance.percent(lower);
75
+ int const max = upper == 100 ? luminance.max() : luminance.percent(upper);
76
+
77
+ if (std::abs(max - min) > 1) {
78
+ // Extract chroma
79
+ VImage chroma = lab.extract_band(1, VImage::option()->set("n", 2));
80
+ // Calculate multiplication factor and addition
81
+ double f = 100.0 / (max - min);
82
+ double a = -(min * f);
83
+ // Scale luminance, join to chroma, convert back to original colourspace
84
+ VImage normalized = luminance.linear(f, a).bandjoin(chroma).colourspace(typeBeforeNormalize);
85
+ // Attach original alpha channel, if any
86
+ if (HasAlpha(image)) {
87
+ // Extract original alpha channel
88
+ VImage alpha = image[image.bands() - 1];
89
+ // Join alpha channel to normalised image
90
+ return normalized.bandjoin(alpha);
91
+ } else {
92
+ return normalized;
93
+ }
94
+ }
95
+ return image;
96
+ }
97
+
98
+ /*
99
+ * Contrast limiting adapative histogram equalization (CLAHE)
100
+ */
101
+ VImage Clahe(VImage image, int const width, int const height, int const maxSlope) {
102
+ return image.hist_local(width, height, VImage::option()->set("max_slope", maxSlope));
103
+ }
104
+
105
+ /*
106
+ * Gamma encoding/decoding
107
+ */
108
+ VImage Gamma(VImage image, double const exponent) {
109
+ if (HasAlpha(image)) {
110
+ // Separate alpha channel
111
+ VImage alpha = image[image.bands() - 1];
112
+ return RemoveAlpha(image).gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
113
+ } else {
114
+ return image.gamma(VImage::option()->set("exponent", exponent));
115
+ }
116
+ }
117
+
118
+ /*
119
+ * Flatten image to remove alpha channel
120
+ */
121
+ VImage Flatten(VImage image, std::vector<double> flattenBackground) {
122
+ double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
123
+ std::vector<double> background {
124
+ flattenBackground[0] * multiplier,
125
+ flattenBackground[1] * multiplier,
126
+ flattenBackground[2] * multiplier
127
+ };
128
+ return image.flatten(VImage::option()->set("background", background));
129
+ }
130
+
131
+ /**
132
+ * Produce the "negative" of the image.
133
+ */
134
+ VImage Negate(VImage image, bool const negateAlpha) {
135
+ if (HasAlpha(image) && !negateAlpha) {
136
+ // Separate alpha channel
137
+ VImage alpha = image[image.bands() - 1];
138
+ return RemoveAlpha(image).invert().bandjoin(alpha);
139
+ } else {
140
+ return image.invert();
141
+ }
142
+ }
143
+
144
+ /*
145
+ * Gaussian blur. Use sigma of -1.0 for fast blur.
146
+ */
147
+ VImage Blur(VImage image, double const sigma) {
148
+ if (sigma == -1.0) {
149
+ // Fast, mild blur - averages neighbouring pixels
150
+ VImage blur = VImage::new_matrixv(3, 3,
151
+ 1.0, 1.0, 1.0,
152
+ 1.0, 1.0, 1.0,
153
+ 1.0, 1.0, 1.0);
154
+ blur.set("scale", 9.0);
155
+ return image.conv(blur);
156
+ } else {
157
+ // Slower, accurate Gaussian blur
158
+ return StaySequential(image, VIPS_ACCESS_SEQUENTIAL).gaussblur(sigma);
159
+ }
160
+ }
161
+
162
+ /*
163
+ * Convolution with a kernel.
164
+ */
165
+ VImage Convolve(VImage image, int const width, int const height,
166
+ double const scale, double const offset,
167
+ std::unique_ptr<double[]> const &kernel_v
168
+ ) {
169
+ VImage kernel = VImage::new_from_memory(
170
+ kernel_v.get(),
171
+ width * height * sizeof(double),
172
+ width,
173
+ height,
174
+ 1,
175
+ VIPS_FORMAT_DOUBLE);
176
+ kernel.set("scale", scale);
177
+ kernel.set("offset", offset);
178
+
179
+ return image.conv(kernel);
180
+ }
181
+
182
+ /*
183
+ * Recomb with a Matrix of the given bands/channel size.
184
+ * Eg. RGB will be a 3x3 matrix.
185
+ */
186
+ VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix) {
187
+ double *m = matrix.get();
188
+ image = image.colourspace(VIPS_INTERPRETATION_sRGB);
189
+ return image
190
+ .recomb(image.bands() == 3
191
+ ? VImage::new_from_memory(
192
+ m, 9 * sizeof(double), 3, 3, 1, VIPS_FORMAT_DOUBLE
193
+ )
194
+ : VImage::new_matrixv(4, 4,
195
+ m[0], m[1], m[2], 0.0,
196
+ m[3], m[4], m[5], 0.0,
197
+ m[6], m[7], m[8], 0.0,
198
+ 0.0, 0.0, 0.0, 1.0));
199
+ }
200
+
201
+ VImage Modulate(VImage image, double const brightness, double const saturation,
202
+ int const hue, double const lightness) {
203
+ VipsInterpretation colourspaceBeforeModulate = image.interpretation();
204
+ if (HasAlpha(image)) {
205
+ // Separate alpha channel
206
+ VImage alpha = image[image.bands() - 1];
207
+ return RemoveAlpha(image)
208
+ .colourspace(VIPS_INTERPRETATION_LCH)
209
+ .linear(
210
+ { brightness, saturation, 1},
211
+ { lightness, 0.0, static_cast<double>(hue) }
212
+ )
213
+ .colourspace(colourspaceBeforeModulate)
214
+ .bandjoin(alpha);
215
+ } else {
216
+ return image
217
+ .colourspace(VIPS_INTERPRETATION_LCH)
218
+ .linear(
219
+ { brightness, saturation, 1 },
220
+ { lightness, 0.0, static_cast<double>(hue) }
221
+ )
222
+ .colourspace(colourspaceBeforeModulate);
223
+ }
224
+ }
225
+
226
+ /*
227
+ * Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
228
+ */
229
+ VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
230
+ double const x1, double const y2, double const y3) {
231
+ if (sigma == -1.0) {
232
+ // Fast, mild sharpen
233
+ VImage sharpen = VImage::new_matrixv(3, 3,
234
+ -1.0, -1.0, -1.0,
235
+ -1.0, 32.0, -1.0,
236
+ -1.0, -1.0, -1.0);
237
+ sharpen.set("scale", 24.0);
238
+ return image.conv(sharpen);
239
+ } else {
240
+ // Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas
241
+ VipsInterpretation colourspaceBeforeSharpen = image.interpretation();
242
+ if (colourspaceBeforeSharpen == VIPS_INTERPRETATION_RGB) {
243
+ colourspaceBeforeSharpen = VIPS_INTERPRETATION_sRGB;
244
+ }
245
+ return image
246
+ .sharpen(VImage::option()
247
+ ->set("sigma", sigma)
248
+ ->set("m1", m1)
249
+ ->set("m2", m2)
250
+ ->set("x1", x1)
251
+ ->set("y2", y2)
252
+ ->set("y3", y3))
253
+ .colourspace(colourspaceBeforeSharpen);
254
+ }
255
+ }
256
+
257
+ VImage Threshold(VImage image, double const threshold, bool const thresholdGrayscale) {
258
+ if (!thresholdGrayscale) {
259
+ return image >= threshold;
260
+ }
261
+ return image.colourspace(VIPS_INTERPRETATION_B_W) >= threshold;
262
+ }
263
+
264
+ /*
265
+ Perform boolean/bitwise operation on image color channels - results in one channel image
266
+ */
267
+ VImage Bandbool(VImage image, VipsOperationBoolean const boolean) {
268
+ image = image.bandbool(boolean);
269
+ return image.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_B_W));
270
+ }
271
+
272
+ /*
273
+ Perform bitwise boolean operation between images
274
+ */
275
+ VImage Boolean(VImage image, VImage imageR, VipsOperationBoolean const boolean) {
276
+ return image.boolean(imageR, boolean);
277
+ }
278
+
279
+ /*
280
+ Trim an image
281
+ */
282
+ VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt) {
283
+ if (image.width() < 3 && image.height() < 3) {
284
+ throw VError("Image to trim must be at least 3x3 pixels");
285
+ }
286
+ if (background.size() == 0) {
287
+ // Top-left pixel provides the default background colour if none is given
288
+ background = image.extract_area(0, 0, 1, 1)(0, 0);
289
+ } else if (sharp::Is16Bit(image.interpretation())) {
290
+ for (size_t i = 0; i < background.size(); i++) {
291
+ background[i] *= 256.0;
292
+ }
293
+ threshold *= 256.0;
294
+ }
295
+ std::vector<double> backgroundAlpha({ background.back() });
296
+ if (HasAlpha(image)) {
297
+ background.pop_back();
298
+ } else {
299
+ background.resize(image.bands());
300
+ }
301
+ int left, top, width, height;
302
+ left = image.find_trim(&top, &width, &height, VImage::option()
303
+ ->set("background", background)
304
+ ->set("line_art", lineArt)
305
+ ->set("threshold", threshold));
306
+ if (HasAlpha(image)) {
307
+ // Search alpha channel (A)
308
+ int leftA, topA, widthA, heightA;
309
+ VImage alpha = image[image.bands() - 1];
310
+ leftA = alpha.find_trim(&topA, &widthA, &heightA, VImage::option()
311
+ ->set("background", backgroundAlpha)
312
+ ->set("line_art", lineArt)
313
+ ->set("threshold", threshold));
314
+ if (widthA > 0 && heightA > 0) {
315
+ if (width > 0 && height > 0) {
316
+ // Combined bounding box (B)
317
+ int const leftB = std::min(left, leftA);
318
+ int const topB = std::min(top, topA);
319
+ int const widthB = std::max(left + width, leftA + widthA) - leftB;
320
+ int const heightB = std::max(top + height, topA + heightA) - topB;
321
+ return image.extract_area(leftB, topB, widthB, heightB);
322
+ } else {
323
+ // Use alpha only
324
+ return image.extract_area(leftA, topA, widthA, heightA);
325
+ }
326
+ }
327
+ }
328
+ if (width > 0 && height > 0) {
329
+ return image.extract_area(left, top, width, height);
330
+ }
331
+ return image;
332
+ }
333
+
334
+ /*
335
+ * Calculate (a * in + b)
336
+ */
337
+ VImage Linear(VImage image, std::vector<double> const a, std::vector<double> const b) {
338
+ size_t const bands = static_cast<size_t>(image.bands());
339
+ if (a.size() > bands) {
340
+ throw VError("Band expansion using linear is unsupported");
341
+ }
342
+ bool const uchar = !Is16Bit(image.interpretation());
343
+ if (HasAlpha(image) && a.size() != bands && (a.size() == 1 || a.size() == bands - 1 || bands - 1 == 1)) {
344
+ // Separate alpha channel
345
+ VImage alpha = image[bands - 1];
346
+ return RemoveAlpha(image).linear(a, b, VImage::option()->set("uchar", uchar)).bandjoin(alpha);
347
+ } else {
348
+ return image.linear(a, b, VImage::option()->set("uchar", uchar));
349
+ }
350
+ }
351
+
352
+ /*
353
+ * Unflatten
354
+ */
355
+ VImage Unflatten(VImage image) {
356
+ if (HasAlpha(image)) {
357
+ VImage alpha = image[image.bands() - 1];
358
+ VImage noAlpha = RemoveAlpha(image);
359
+ return noAlpha.bandjoin(alpha & (noAlpha.colourspace(VIPS_INTERPRETATION_B_W) < 255));
360
+ } else {
361
+ return image.bandjoin(image.colourspace(VIPS_INTERPRETATION_B_W) < 255);
362
+ }
363
+ }
364
+
365
+ /*
366
+ * Ensure the image is in a given colourspace
367
+ */
368
+ VImage EnsureColourspace(VImage image, VipsInterpretation colourspace) {
369
+ if (colourspace != VIPS_INTERPRETATION_LAST && image.interpretation() != colourspace) {
370
+ image = image.colourspace(colourspace,
371
+ VImage::option()->set("source_space", image.interpretation()));
372
+ }
373
+ return image;
374
+ }
375
+
376
+ /*
377
+ * Split and crop each frame, reassemble, and update pageHeight.
378
+ */
379
+ VImage CropMultiPage(VImage image, int left, int top, int width, int height,
380
+ int nPages, int *pageHeight) {
381
+ if (top == 0 && height == *pageHeight) {
382
+ // Fast path; no need to adjust the height of the multi-page image
383
+ return image.extract_area(left, 0, width, image.height());
384
+ } else {
385
+ std::vector<VImage> pages;
386
+ pages.reserve(nPages);
387
+
388
+ // Split the image into cropped frames
389
+ image = StaySequential(image, VIPS_ACCESS_SEQUENTIAL);
390
+ for (int i = 0; i < nPages; i++) {
391
+ pages.push_back(
392
+ image.extract_area(left, *pageHeight * i + top, width, height));
393
+ }
394
+
395
+ // Reassemble the frames into a tall, thin image
396
+ VImage assembled = VImage::arrayjoin(pages,
397
+ VImage::option()->set("across", 1));
398
+
399
+ // Update the page height
400
+ *pageHeight = height;
401
+
402
+ return assembled;
403
+ }
404
+ }
405
+
406
+ /*
407
+ * Split into frames, embed each frame, reassemble, and update pageHeight.
408
+ */
409
+ VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
410
+ VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight) {
411
+ if (top == 0 && height == *pageHeight) {
412
+ // Fast path; no need to adjust the height of the multi-page image
413
+ return image.embed(left, 0, width, image.height(), VImage::option()
414
+ ->set("extend", extendWith)
415
+ ->set("background", background));
416
+ } else if (left == 0 && width == image.width()) {
417
+ // Fast path; no need to adjust the width of the multi-page image
418
+ std::vector<VImage> pages;
419
+ pages.reserve(nPages);
420
+
421
+ // Rearrange the tall image into a vertical grid
422
+ image = image.grid(*pageHeight, nPages, 1);
423
+
424
+ // Do the embed on the wide image
425
+ image = image.embed(0, top, image.width(), height, VImage::option()
426
+ ->set("extend", extendWith)
427
+ ->set("background", background));
428
+
429
+ // Split the wide image into frames
430
+ for (int i = 0; i < nPages; i++) {
431
+ pages.push_back(
432
+ image.extract_area(width * i, 0, width, height));
433
+ }
434
+
435
+ // Reassemble the frames into a tall, thin image
436
+ VImage assembled = VImage::arrayjoin(pages,
437
+ VImage::option()->set("across", 1));
438
+
439
+ // Update the page height
440
+ *pageHeight = height;
441
+
442
+ return assembled;
443
+ } else {
444
+ std::vector<VImage> pages;
445
+ pages.reserve(nPages);
446
+
447
+ // Split the image into frames
448
+ for (int i = 0; i < nPages; i++) {
449
+ pages.push_back(
450
+ image.extract_area(0, *pageHeight * i, image.width(), *pageHeight));
451
+ }
452
+
453
+ // Embed each frame in the target size
454
+ for (int i = 0; i < nPages; i++) {
455
+ pages[i] = pages[i].embed(left, top, width, height, VImage::option()
456
+ ->set("extend", extendWith)
457
+ ->set("background", background));
458
+ }
459
+
460
+ // Reassemble the frames into a tall, thin image
461
+ VImage assembled = VImage::arrayjoin(pages,
462
+ VImage::option()->set("across", 1));
463
+
464
+ // Update the page height
465
+ *pageHeight = height;
466
+
467
+ return assembled;
468
+ }
469
+ }
470
+
471
+ } // namespace sharp
@@ -0,0 +1,125 @@
1
+ // Copyright 2013 Lovell Fuller and others.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ #ifndef SRC_OPERATIONS_H_
5
+ #define SRC_OPERATIONS_H_
6
+
7
+ #include <algorithm>
8
+ #include <functional>
9
+ #include <memory>
10
+ #include <tuple>
11
+ #include <vips/vips8>
12
+
13
+ using vips::VImage;
14
+
15
+ namespace sharp {
16
+
17
+ /*
18
+ * Tint an image using the provided RGB.
19
+ */
20
+ VImage Tint(VImage image, std::vector<double> const tint);
21
+
22
+ /*
23
+ * Stretch luminance to cover full dynamic range.
24
+ */
25
+ VImage Normalise(VImage image, int const lower, int const upper);
26
+
27
+ /*
28
+ * Contrast limiting adapative histogram equalization (CLAHE)
29
+ */
30
+ VImage Clahe(VImage image, int const width, int const height, int const maxSlope);
31
+
32
+ /*
33
+ * Gamma encoding/decoding
34
+ */
35
+ VImage Gamma(VImage image, double const exponent);
36
+
37
+ /*
38
+ * Flatten image to remove alpha channel
39
+ */
40
+ VImage Flatten(VImage image, std::vector<double> flattenBackground);
41
+
42
+ /*
43
+ * Produce the "negative" of the image.
44
+ */
45
+ VImage Negate(VImage image, bool const negateAlpha);
46
+
47
+ /*
48
+ * Gaussian blur. Use sigma of -1.0 for fast blur.
49
+ */
50
+ VImage Blur(VImage image, double const sigma);
51
+
52
+ /*
53
+ * Convolution with a kernel.
54
+ */
55
+ VImage Convolve(VImage image, int const width, int const height,
56
+ double const scale, double const offset, std::unique_ptr<double[]> const &kernel_v);
57
+
58
+ /*
59
+ * Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
60
+ */
61
+ VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
62
+ double const x1, double const y2, double const y3);
63
+
64
+ /*
65
+ Threshold an image
66
+ */
67
+ VImage Threshold(VImage image, double const threshold, bool const thresholdColor);
68
+
69
+ /*
70
+ Perform boolean/bitwise operation on image color channels - results in one channel image
71
+ */
72
+ VImage Bandbool(VImage image, VipsOperationBoolean const boolean);
73
+
74
+ /*
75
+ Perform bitwise boolean operation between images
76
+ */
77
+ VImage Boolean(VImage image, VImage imageR, VipsOperationBoolean const boolean);
78
+
79
+ /*
80
+ Trim an image
81
+ */
82
+ VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt);
83
+
84
+ /*
85
+ * Linear adjustment (a * in + b)
86
+ */
87
+ VImage Linear(VImage image, std::vector<double> const a, std::vector<double> const b);
88
+
89
+ /*
90
+ * Unflatten
91
+ */
92
+ VImage Unflatten(VImage image);
93
+
94
+ /*
95
+ * Recomb with a Matrix of the given bands/channel size.
96
+ * Eg. RGB will be a 3x3 matrix.
97
+ */
98
+ VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix);
99
+
100
+ /*
101
+ * Modulate brightness, saturation, hue and lightness
102
+ */
103
+ VImage Modulate(VImage image, double const brightness, double const saturation,
104
+ int const hue, double const lightness);
105
+
106
+ /*
107
+ * Ensure the image is in a given colourspace
108
+ */
109
+ VImage EnsureColourspace(VImage image, VipsInterpretation colourspace);
110
+
111
+ /*
112
+ * Split and crop each frame, reassemble, and update pageHeight.
113
+ */
114
+ VImage CropMultiPage(VImage image, int left, int top, int width, int height,
115
+ int nPages, int *pageHeight);
116
+
117
+ /*
118
+ * Split into frames, embed each frame, reassemble, and update pageHeight.
119
+ */
120
+ VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
121
+ VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight);
122
+
123
+ } // namespace sharp
124
+
125
+ #endif // SRC_OPERATIONS_H_