/src/libvips/libvips/conversion/composite.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* composite an array of images with PDF operators |
2 | | * |
3 | | * 25/9/17 |
4 | | * - from bandjoin.c |
5 | | * 30/11/17 |
6 | | * - add composite2 class, to make a nice CLI interface |
7 | | * 30/1/18 |
8 | | * - remove number of images limit |
9 | | * - allow one mode ... reused for all joins |
10 | | * 11/8/18 [medakk] |
11 | | * - x/y params let you position images |
12 | | * 27/11/18 |
13 | | * - don't stop on first non-transparent image [felixbuenemann, GDmac] |
14 | | * 6/12/18 |
15 | | * - do our own subimage positioning |
16 | | * 8/5/19 |
17 | | * - revise in/out/dest-in/dest-out to make smoother alpha |
18 | | */ |
19 | | |
20 | | /* |
21 | | |
22 | | This file is part of VIPS. |
23 | | |
24 | | VIPS is free software; you can redistribute it and/or modify |
25 | | it under the terms of the GNU Lesser General Public License as published by |
26 | | the Free Software Foundation; either version 2 of the License, or |
27 | | (at your option) any later version. |
28 | | |
29 | | This program is distributed in the hope that it will be useful, |
30 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
31 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
32 | | GNU Lesser General Public License for more details. |
33 | | |
34 | | You should have received a copy of the GNU Lesser General Public License |
35 | | along with this program; if not, write to the Free Software |
36 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
37 | | 02110-1301 USA |
38 | | |
39 | | */ |
40 | | |
41 | | /* |
42 | | |
43 | | These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
44 | | |
45 | | */ |
46 | | |
47 | | /* |
48 | | #define VIPS_DEBUG |
49 | | */ |
50 | | |
51 | | #ifdef HAVE_CONFIG_H |
52 | | #include <config.h> |
53 | | #endif /*HAVE_CONFIG_H*/ |
54 | | #include <glib/gi18n-lib.h> |
55 | | |
56 | | #include <stdio.h> |
57 | | #include <string.h> |
58 | | #ifdef _MSC_VER |
59 | | #include <cstdlib> |
60 | | #else |
61 | | #include <stdlib.h> |
62 | | #endif |
63 | | #include <math.h> |
64 | | |
65 | | #if defined(HAVE__ALIGNED_MALLOC) || defined(HAVE_MEMALIGN) |
66 | | #include <malloc.h> |
67 | | #endif |
68 | | |
69 | | #include <vips/vips.h> |
70 | | #include <vips/internal.h> |
71 | | #include <vips/debug.h> |
72 | | |
73 | | #include "pconversion.h" |
74 | | |
75 | | /* Maximum number of image bands. |
76 | | */ |
77 | 0 | #define MAX_BANDS (64) |
78 | | |
79 | | /* Uncomment to disable the vector path ... handy for debugging. |
80 | | #undef HAVE_VECTOR_ARITH |
81 | | */ |
82 | | |
83 | | /* We have a vector path with gcc's vector attr. |
84 | | */ |
85 | | #ifdef HAVE_VECTOR_ARITH |
86 | | /* A vector of four floats. |
87 | | */ |
88 | | typedef float v4f __attribute__((vector_size(4 * sizeof(float)), aligned(16))); |
89 | | #endif /*HAVE_VECTOR_ARITH*/ |
90 | | |
91 | | typedef struct _VipsCompositeBase { |
92 | | VipsConversion parent_instance; |
93 | | |
94 | | /* The input images. |
95 | | */ |
96 | | VipsArrayImage *in; |
97 | | |
98 | | /* For N input images, 1 blend mode or N - 1 blend modes. |
99 | | */ |
100 | | VipsArrayInt *mode; |
101 | | |
102 | | /* Compositing space. This defaults to RGB, or B_W if we only have |
103 | | * G and GA inputs. |
104 | | */ |
105 | | VipsInterpretation compositing_space; |
106 | | |
107 | | /* Set if the input images have already been premultiplied. |
108 | | */ |
109 | | gboolean premultiplied; |
110 | | |
111 | | /* The x and y positions for each image in the stack. There are n - 1 |
112 | | * of these, since image 0 is always positioned at (0, 0). Set by |
113 | | * subclasses. Can be NULL. |
114 | | */ |
115 | | int *x_offset; |
116 | | int *y_offset; |
117 | | |
118 | | /* A rect for the position of each input image. For each output region, |
119 | | * we composite the set of input images which intersect that area. |
120 | | */ |
121 | | VipsRect *subimages; |
122 | | |
123 | | /* The number of non-alpha bands we are blending. |
124 | | */ |
125 | | int bands; |
126 | | |
127 | | /* The maximum value for each band, set from the image interpretation. |
128 | | * This is used to scale each band to 0 - 1. |
129 | | */ |
130 | | double max_band[MAX_BANDS + 1]; |
131 | | |
132 | | /* TRUE if all our modes are skippable, ie. we can avoid compositing |
133 | | * the whole stack for every pixel request. |
134 | | */ |
135 | | gboolean skippable; |
136 | | |
137 | | } VipsCompositeBase; |
138 | | |
139 | | typedef VipsConversionClass VipsCompositeBaseClass; |
140 | | |
141 | | /* We need C linkage for this. |
142 | | */ |
143 | | extern "C" { |
144 | | G_DEFINE_ABSTRACT_TYPE(VipsCompositeBase, vips_composite_base, |
145 | | VIPS_TYPE_CONVERSION); |
146 | | } |
147 | | |
148 | | static void |
149 | | vips_composite_base_dispose(GObject *gobject) |
150 | 0 | { |
151 | 0 | VipsCompositeBase *composite = (VipsCompositeBase *) gobject; |
152 | |
|
153 | 0 | if (composite->in) { |
154 | 0 | vips_area_unref((VipsArea *) composite->in); |
155 | 0 | composite->in = NULL; |
156 | 0 | } |
157 | 0 | if (composite->mode) { |
158 | 0 | vips_area_unref((VipsArea *) composite->mode); |
159 | 0 | composite->mode = NULL; |
160 | 0 | } |
161 | 0 | VIPS_FREE(composite->subimages); |
162 | |
|
163 | 0 | G_OBJECT_CLASS(vips_composite_base_parent_class)->dispose(gobject); |
164 | 0 | } |
165 | | |
166 | | /* Our sequence value. |
167 | | */ |
168 | | typedef struct { |
169 | | #ifdef HAVE_VECTOR_ARITH |
170 | | /* max_band as a vector, for the RGBA case. This must be |
171 | | * defined first to ensure that the member is aligned |
172 | | * on a 16-byte boundary. |
173 | | */ |
174 | | v4f max_band_vec; |
175 | | #endif /*HAVE_VECTOR_ARITH*/ |
176 | | |
177 | | VipsCompositeBase *composite; |
178 | | |
179 | | /* Full set of input regions, each made on the corresponding input |
180 | | * image. |
181 | | */ |
182 | | VipsRegion **input_regions; |
183 | | |
184 | | /* We then vips_region_prepare_to() to one of this set of regions, |
185 | | * each defined on the base image. |
186 | | */ |
187 | | VipsRegion **composite_regions; |
188 | | |
189 | | /* Number of input regions which intersect this request rect. |
190 | | */ |
191 | | int n; |
192 | | |
193 | | /* For each of @n above (inputs which intersect this request), the |
194 | | * index of the input image we need. We can use this index to get the |
195 | | * position, input region and composite region. |
196 | | */ |
197 | | int *enabled; |
198 | | |
199 | | /* For each enabled image, an input pointer. |
200 | | */ |
201 | | VipsPel **p; |
202 | | |
203 | | } VipsCompositeSequence; |
204 | | |
205 | | #ifdef HAVE_VECTOR_ARITH |
206 | | /* Allocate aligned memory. The return value can be released |
207 | | * by calling the vips_free_aligned() function, for example: |
208 | | * VIPS_FREEF(vips_free_aligned, ptr); |
209 | | */ |
210 | | static inline void * |
211 | | vips_alloc_aligned(size_t sz, size_t align) |
212 | 0 | { |
213 | 0 | g_assert(!(align & (align - 1))); |
214 | |
|
215 | | #ifdef HAVE__ALIGNED_MALLOC |
216 | | return _aligned_malloc(sz, align); |
217 | | #elif defined(HAVE_POSIX_MEMALIGN) |
218 | | void *ptr; |
219 | 0 | if (posix_memalign(&ptr, align, sz)) |
220 | 0 | return NULL; |
221 | 0 | return ptr; |
222 | | #elif defined(HAVE_MEMALIGN) |
223 | | return memalign(align, sz); |
224 | | #else |
225 | | #error Missing aligned alloc implementation |
226 | | #endif |
227 | 0 | } |
228 | | |
229 | | static inline void |
230 | | vips_free_aligned(void *ptr) |
231 | 0 | { |
232 | | #ifdef HAVE__ALIGNED_MALLOC |
233 | | _aligned_free(ptr); |
234 | | #else /*defined(HAVE_POSIX_MEMALIGN) || defined(HAVE_MEMALIGN)*/ |
235 | 0 | free(ptr); |
236 | 0 | #endif |
237 | 0 | } |
238 | | #endif /*HAVE_VECTOR_ARITH*/ |
239 | | |
240 | | static int |
241 | | vips_composite_stop(void *vseq, void *a, void *b) |
242 | 0 | { |
243 | 0 | VipsCompositeSequence *seq = (VipsCompositeSequence *) vseq; |
244 | |
|
245 | 0 | if (seq->input_regions) { |
246 | 0 | for (int i = 0; seq->input_regions[i]; i++) |
247 | 0 | VIPS_UNREF(seq->input_regions[i]); |
248 | 0 | VIPS_FREE(seq->input_regions); |
249 | 0 | } |
250 | |
|
251 | 0 | if (seq->composite_regions) { |
252 | 0 | for (int i = 0; seq->composite_regions[i]; i++) |
253 | 0 | VIPS_UNREF(seq->composite_regions[i]); |
254 | 0 | VIPS_FREE(seq->composite_regions); |
255 | 0 | } |
256 | |
|
257 | 0 | VIPS_FREE(seq->enabled); |
258 | 0 | VIPS_FREE(seq->p); |
259 | |
|
260 | 0 | #ifdef HAVE_VECTOR_ARITH |
261 | 0 | VIPS_FREEF(vips_free_aligned, seq); |
262 | | #else /*!defined(HAVE_VECTOR_ARITH)*/ |
263 | | VIPS_FREE(seq); |
264 | | #endif /*HAVE_VECTOR_ARITH*/ |
265 | |
|
266 | 0 | return 0; |
267 | 0 | } |
268 | | |
269 | | static void * |
270 | | vips_composite_start(VipsImage *out, void *a, void *b) |
271 | 0 | { |
272 | 0 | VipsImage **in = (VipsImage **) a; |
273 | 0 | VipsCompositeBase *composite = (VipsCompositeBase *) b; |
274 | |
|
275 | 0 | VipsCompositeSequence *seq; |
276 | 0 | int i, n; |
277 | |
|
278 | 0 | #ifdef HAVE_VECTOR_ARITH |
279 | | /* Ensure that the memory is aligned on a 16-byte boundary. |
280 | | */ |
281 | 0 | if (!(seq = ((VipsCompositeSequence *) vips_alloc_aligned( |
282 | 0 | sizeof(VipsCompositeSequence), 16)))) |
283 | | #else /*!defined(HAVE_VECTOR_ARITH)*/ |
284 | | if (!(seq = VIPS_NEW(NULL, VipsCompositeSequence))) |
285 | | #endif /*HAVE_VECTOR_ARITH*/ |
286 | 0 | return NULL; |
287 | | |
288 | 0 | seq->composite = composite; |
289 | 0 | seq->input_regions = NULL; |
290 | 0 | seq->enabled = NULL; |
291 | 0 | seq->p = NULL; |
292 | | |
293 | | /* How many images? |
294 | | */ |
295 | 0 | for (n = 0; in[n]; n++) |
296 | 0 | ; |
297 | | |
298 | | /* Allocate space for region array. |
299 | | */ |
300 | 0 | if (!(seq->input_regions = VIPS_ARRAY(NULL, n + 1, VipsRegion *))) { |
301 | 0 | vips_composite_stop(seq, NULL, NULL); |
302 | 0 | return NULL; |
303 | 0 | } |
304 | 0 | for (i = 0; i < n + 1; i++) |
305 | 0 | seq->input_regions[i] = NULL; |
306 | |
|
307 | 0 | if (!(seq->composite_regions = |
308 | 0 | VIPS_ARRAY(NULL, n + 1, VipsRegion *))) { |
309 | 0 | vips_composite_stop(seq, NULL, NULL); |
310 | 0 | return NULL; |
311 | 0 | } |
312 | 0 | for (i = 0; i < n + 1; i++) |
313 | 0 | seq->composite_regions[i] = NULL; |
314 | |
|
315 | 0 | seq->enabled = VIPS_ARRAY(NULL, n, int); |
316 | 0 | seq->p = VIPS_ARRAY(NULL, n, VipsPel *); |
317 | 0 | if (!seq->enabled || |
318 | 0 | !seq->p) { |
319 | 0 | vips_composite_stop(seq, NULL, NULL); |
320 | 0 | return NULL; |
321 | 0 | } |
322 | | |
323 | | /* Create a set of regions. |
324 | | */ |
325 | 0 | for (i = 0; i < n; i++) { |
326 | 0 | seq->input_regions[i] = vips_region_new(in[i]); |
327 | 0 | seq->composite_regions[i] = vips_region_new(in[0]); |
328 | |
|
329 | 0 | if (!seq->input_regions[i] || |
330 | 0 | !seq->composite_regions[i]) { |
331 | 0 | vips_composite_stop(seq, NULL, NULL); |
332 | 0 | return NULL; |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | 0 | #ifdef HAVE_VECTOR_ARITH |
337 | | /* We need a float version for the vector path. |
338 | | */ |
339 | 0 | if (composite->bands == 3) |
340 | 0 | seq->max_band_vec = (v4f){ |
341 | 0 | (float) composite->max_band[0], |
342 | 0 | (float) composite->max_band[1], |
343 | 0 | (float) composite->max_band[2], |
344 | 0 | (float) composite->max_band[3] |
345 | 0 | }; |
346 | 0 | #endif |
347 | |
|
348 | 0 | return seq; |
349 | 0 | } |
350 | | |
351 | | /* For each of the supported interpretations, the maximum value of each band. |
352 | | */ |
353 | | static int |
354 | | vips_composite_base_max_band(VipsCompositeBase *composite, double *max_band) |
355 | 0 | { |
356 | 0 | double max_alpha; |
357 | 0 | int b; |
358 | |
|
359 | 0 | max_alpha = vips_interpretation_max_alpha(composite->compositing_space); |
360 | |
|
361 | 0 | for (b = 0; b <= composite->bands; b++) |
362 | 0 | max_band[b] = max_alpha; |
363 | |
|
364 | 0 | switch (composite->compositing_space) { |
365 | 0 | case VIPS_INTERPRETATION_XYZ: |
366 | 0 | max_band[0] = VIPS_D65_X0; |
367 | 0 | max_band[1] = VIPS_D65_Y0; |
368 | 0 | max_band[2] = VIPS_D65_Z0; |
369 | 0 | break; |
370 | | |
371 | 0 | case VIPS_INTERPRETATION_LAB: |
372 | 0 | max_band[0] = 100; |
373 | 0 | max_band[1] = 128; |
374 | 0 | max_band[2] = 128; |
375 | 0 | break; |
376 | | |
377 | 0 | case VIPS_INTERPRETATION_LCH: |
378 | 0 | max_band[0] = 100; |
379 | 0 | max_band[1] = 128; |
380 | 0 | max_band[2] = 360; |
381 | 0 | break; |
382 | | |
383 | 0 | case VIPS_INTERPRETATION_CMC: |
384 | 0 | max_band[0] = 100; |
385 | 0 | max_band[1] = 128; |
386 | 0 | max_band[2] = 360; |
387 | 0 | break; |
388 | | |
389 | 0 | case VIPS_INTERPRETATION_scRGB: |
390 | 0 | max_band[0] = 1; |
391 | 0 | max_band[1] = 1; |
392 | 0 | max_band[2] = 1; |
393 | 0 | break; |
394 | | |
395 | 0 | case VIPS_INTERPRETATION_sRGB: |
396 | 0 | max_band[0] = 255; |
397 | 0 | max_band[1] = 255; |
398 | 0 | max_band[2] = 255; |
399 | 0 | break; |
400 | | |
401 | 0 | case VIPS_INTERPRETATION_HSV: |
402 | 0 | max_band[0] = 255; |
403 | 0 | max_band[1] = 255; |
404 | 0 | max_band[2] = 255; |
405 | 0 | break; |
406 | | |
407 | 0 | case VIPS_INTERPRETATION_CMYK: |
408 | 0 | max_band[0] = 255; |
409 | 0 | max_band[1] = 255; |
410 | 0 | max_band[2] = 255; |
411 | 0 | max_band[3] = 255; |
412 | 0 | break; |
413 | | |
414 | 0 | case VIPS_INTERPRETATION_RGB16: |
415 | 0 | max_band[0] = 65535; |
416 | 0 | max_band[1] = 65535; |
417 | 0 | max_band[2] = 65535; |
418 | 0 | break; |
419 | | |
420 | 0 | case VIPS_INTERPRETATION_GREY16: |
421 | 0 | max_band[0] = 65535; |
422 | 0 | break; |
423 | | |
424 | 0 | case VIPS_INTERPRETATION_YXY: |
425 | 0 | max_band[0] = 100; |
426 | 0 | max_band[1] = 1; |
427 | 0 | max_band[2] = 1; |
428 | 0 | break; |
429 | | |
430 | 0 | case VIPS_INTERPRETATION_B_W: |
431 | 0 | max_band[0] = 255; |
432 | 0 | break; |
433 | | |
434 | 0 | default: |
435 | 0 | return -1; |
436 | 0 | } |
437 | | |
438 | 0 | return 0; |
439 | 0 | } |
440 | | |
441 | | /* Find the subset of our input images which intersect this region. If we are |
442 | | * not in skippable mode, we must enable all layers. |
443 | | */ |
444 | | static void |
445 | | vips_composite_base_select(VipsCompositeSequence *seq, VipsRect *r) |
446 | 0 | { |
447 | 0 | VipsCompositeBase *composite = seq->composite; |
448 | 0 | int n = composite->in->area.n; |
449 | |
|
450 | 0 | seq->n = 0; |
451 | 0 | for (int i = 0; i < n; i++) |
452 | 0 | if (!composite->skippable || |
453 | 0 | vips_rect_overlapsrect(r, |
454 | 0 | &composite->subimages[i])) { |
455 | 0 | seq->enabled[seq->n] = i; |
456 | 0 | seq->n += 1; |
457 | 0 | } |
458 | 0 | } |
459 | | |
460 | | /* Cairo naming conventions: |
461 | | * |
462 | | * aR alpha of result |
463 | | * aA alpha of source A (the new pixel) |
464 | | * aB alpha of source B (the thing we accumulate) |
465 | | * xR colour band of result |
466 | | * xA colour band of source A |
467 | | * xB colour band of source B |
468 | | */ |
469 | | |
470 | | /* A is the new pixel coming in, of any non-complex type T. |
471 | | * |
472 | | * We must scale incoming pixels to 0 - 1 by dividing by the scale[] vector. |
473 | | * |
474 | | * If premultipled is not set, we premultiply incoming pixels before blending. |
475 | | * |
476 | | * B is the double pixel we are accumulating. |
477 | | */ |
478 | | template <typename T> |
479 | | static void |
480 | | vips_composite_base_blend(VipsCompositeBase *composite, |
481 | | VipsBlendMode mode, double *restrict B, T *restrict p) |
482 | 0 | { |
483 | 0 | const int bands = composite->bands; |
484 | |
|
485 | 0 | double A[MAX_BANDS + 1]; |
486 | 0 | double aA; |
487 | 0 | double aB; |
488 | 0 | double aR; |
489 | 0 | double t1; |
490 | 0 | double t2; |
491 | 0 | double t3; |
492 | 0 | double f[MAX_BANDS + 1]; |
493 | | |
494 | | /* Load and scale the pixel to 0 - 1. |
495 | | */ |
496 | 0 | for (int b = 0; b <= bands; b++) |
497 | 0 | A[b] = p[b] / composite->max_band[b]; |
498 | | /* Not necessary, but it stops a compiler warning. |
499 | | */ |
500 | 0 | for (int b = bands + 1; b < MAX_BANDS + 1; b++) |
501 | 0 | A[b] = 0.0; |
502 | |
|
503 | 0 | aA = A[bands]; |
504 | 0 | aB = B[bands]; |
505 | | |
506 | | /* We may need to premultiply A. |
507 | | */ |
508 | 0 | if (!composite->premultiplied) |
509 | 0 | for (int b = 0; b < bands; b++) |
510 | 0 | A[b] *= aA; |
511 | |
|
512 | 0 | switch (mode) { |
513 | 0 | case VIPS_BLEND_MODE_CLEAR: |
514 | 0 | aR = 0; |
515 | 0 | for (int b = 0; b < bands; b++) |
516 | 0 | B[b] = 0; |
517 | 0 | break; |
518 | | |
519 | 0 | case VIPS_BLEND_MODE_SOURCE: |
520 | 0 | aR = aA; |
521 | 0 | for (int b = 0; b < bands; b++) |
522 | 0 | B[b] = A[b]; |
523 | 0 | break; |
524 | | |
525 | 0 | case VIPS_BLEND_MODE_OVER: |
526 | 0 | aR = aA + aB * (1 - aA); |
527 | 0 | t1 = 1 - aA; |
528 | 0 | for (int b = 0; b < bands; b++) |
529 | 0 | B[b] = A[b] + t1 * B[b]; |
530 | 0 | break; |
531 | | |
532 | 0 | case VIPS_BLEND_MODE_IN: |
533 | 0 | aR = aA * aB; |
534 | | // if aA == 0, then aR == 0 and so B will already be 0 |
535 | 0 | if (aA != 0) |
536 | 0 | for (int b = 0; b < bands; b++) |
537 | 0 | B[b] = A[b] * aR / aA; |
538 | 0 | break; |
539 | | |
540 | 0 | case VIPS_BLEND_MODE_OUT: |
541 | 0 | aR = aA * (1 - aB); |
542 | | // if aA == 0, then aR == 0 and so B will already be 0 |
543 | 0 | if (aA != 0) |
544 | 0 | for (int b = 0; b < bands; b++) |
545 | 0 | B[b] = A[b] * aR / aA; |
546 | 0 | break; |
547 | | |
548 | 0 | case VIPS_BLEND_MODE_ATOP: |
549 | 0 | aR = aB; |
550 | 0 | t1 = 1 - aA; |
551 | 0 | for (int b = 0; b < bands; b++) |
552 | 0 | B[b] = A[b] + t1 * B[b]; |
553 | 0 | break; |
554 | | |
555 | 0 | case VIPS_BLEND_MODE_DEST: |
556 | 0 | aR = aB; |
557 | | // B = B |
558 | 0 | break; |
559 | | |
560 | 0 | case VIPS_BLEND_MODE_DEST_OVER: |
561 | 0 | aR = aB + aA * (1 - aB); |
562 | 0 | t1 = 1 - aB; |
563 | 0 | for (int b = 0; b < bands; b++) |
564 | 0 | B[b] = B[b] + t1 * A[b]; |
565 | 0 | break; |
566 | | |
567 | 0 | case VIPS_BLEND_MODE_DEST_IN: |
568 | 0 | aR = aA * aB; |
569 | | // B = B |
570 | 0 | if (aB != 0) |
571 | 0 | for (int b = 0; b < bands; b++) |
572 | 0 | B[b] *= aR / aB; |
573 | 0 | break; |
574 | | |
575 | 0 | case VIPS_BLEND_MODE_DEST_OUT: |
576 | 0 | aR = (1 - aA) * aB; |
577 | | // B = B |
578 | | // if aB is 0, then B is already 0 |
579 | 0 | if (aB != 0) |
580 | 0 | for (int b = 0; b < bands; b++) |
581 | 0 | B[b] *= aR / aB; |
582 | 0 | break; |
583 | | |
584 | 0 | case VIPS_BLEND_MODE_DEST_ATOP: |
585 | 0 | aR = aA; |
586 | 0 | t1 = 1 - aB; |
587 | 0 | for (int b = 0; b < bands; b++) |
588 | 0 | B[b] = t1 * A[b] + B[b]; |
589 | 0 | break; |
590 | | |
591 | 0 | case VIPS_BLEND_MODE_XOR: |
592 | 0 | aR = aA + aB - 2 * aA * aB; |
593 | 0 | t1 = 1 - aB; |
594 | 0 | t2 = 1 - aA; |
595 | 0 | for (int b = 0; b < bands; b++) |
596 | 0 | B[b] = t1 * A[b] + t2 * B[b]; |
597 | 0 | break; |
598 | | |
599 | 0 | case VIPS_BLEND_MODE_ADD: |
600 | 0 | aR = VIPS_MIN(1, aA + aB); |
601 | 0 | for (int b = 0; b < bands; b++) |
602 | 0 | B[b] = A[b] + B[b]; |
603 | 0 | break; |
604 | | |
605 | 0 | case VIPS_BLEND_MODE_SATURATE: |
606 | 0 | aR = VIPS_MIN(1, aA + aB); |
607 | 0 | t1 = VIPS_MIN(aA, 1 - aB); |
608 | 0 | for (int b = 0; b < bands; b++) |
609 | 0 | B[b] = t1 * A[b] + B[b]; |
610 | 0 | break; |
611 | | |
612 | 0 | default: |
613 | | /* The PDF modes are a bit different. |
614 | | */ |
615 | 0 | aR = aA + aB * (1 - aA); |
616 | |
|
617 | 0 | switch (mode) { |
618 | 0 | case VIPS_BLEND_MODE_MULTIPLY: |
619 | 0 | for (int b = 0; b < bands; b++) |
620 | 0 | f[b] = A[b] * B[b]; |
621 | 0 | break; |
622 | | |
623 | 0 | case VIPS_BLEND_MODE_SCREEN: |
624 | 0 | for (int b = 0; b < bands; b++) |
625 | 0 | f[b] = A[b] + B[b] - A[b] * B[b]; |
626 | 0 | break; |
627 | | |
628 | 0 | case VIPS_BLEND_MODE_OVERLAY: |
629 | 0 | for (int b = 0; b < bands; b++) |
630 | 0 | if (B[b] <= 0.5) |
631 | 0 | f[b] = 2 * A[b] * B[b]; |
632 | 0 | else |
633 | 0 | f[b] = 1 - 2 * (1 - A[b]) * (1 - B[b]); |
634 | 0 | break; |
635 | | |
636 | 0 | case VIPS_BLEND_MODE_DARKEN: |
637 | 0 | for (int b = 0; b < bands; b++) |
638 | 0 | f[b] = VIPS_MIN(A[b], B[b]); |
639 | 0 | break; |
640 | | |
641 | 0 | case VIPS_BLEND_MODE_LIGHTEN: |
642 | 0 | for (int b = 0; b < bands; b++) |
643 | 0 | f[b] = VIPS_MAX(A[b], B[b]); |
644 | 0 | break; |
645 | | |
646 | 0 | case VIPS_BLEND_MODE_COLOUR_DODGE: |
647 | 0 | for (int b = 0; b < bands; b++) |
648 | 0 | if (A[b] < 1) |
649 | 0 | f[b] = VIPS_MIN(1, B[b] / (1 - A[b])); |
650 | 0 | else |
651 | 0 | f[b] = 1; |
652 | 0 | break; |
653 | | |
654 | 0 | case VIPS_BLEND_MODE_COLOUR_BURN: |
655 | 0 | for (int b = 0; b < bands; b++) |
656 | 0 | if (A[b] > 0) |
657 | 0 | f[b] = 1 - VIPS_MIN(1, (1 - B[b]) / A[b]); |
658 | 0 | else |
659 | 0 | f[b] = 0; |
660 | 0 | break; |
661 | | |
662 | 0 | case VIPS_BLEND_MODE_HARD_LIGHT: |
663 | 0 | for (int b = 0; b < bands; b++) |
664 | 0 | if (A[b] <= 0.5) |
665 | 0 | f[b] = 2 * A[b] * B[b]; |
666 | 0 | else |
667 | 0 | f[b] = 1 - 2 * (1 - A[b]) * (1 - B[b]); |
668 | 0 | break; |
669 | | |
670 | 0 | case VIPS_BLEND_MODE_SOFT_LIGHT: |
671 | 0 | for (int b = 0; b < bands; b++) { |
672 | 0 | double g; |
673 | |
|
674 | 0 | if (B[b] <= 0.25) |
675 | 0 | g = ((16 * B[b] - 12) * B[b] + 4) * B[b]; |
676 | 0 | else |
677 | 0 | g = sqrt(B[b]); |
678 | |
|
679 | 0 | if (A[b] <= 0.5) |
680 | 0 | f[b] = B[b] - (1 - 2 * A[b]) * B[b] * (1 - B[b]); |
681 | 0 | else |
682 | 0 | f[b] = B[b] + (2 * A[b] - 1) * (g - B[b]); |
683 | 0 | } |
684 | 0 | break; |
685 | | |
686 | 0 | case VIPS_BLEND_MODE_DIFFERENCE: |
687 | 0 | for (int b = 0; b < bands; b++) |
688 | 0 | f[b] = fabs(B[b] - A[b]); |
689 | 0 | break; |
690 | | |
691 | 0 | case VIPS_BLEND_MODE_EXCLUSION: |
692 | 0 | for (int b = 0; b < bands; b++) |
693 | 0 | f[b] = A[b] + B[b] - 2 * A[b] * B[b]; |
694 | 0 | break; |
695 | | |
696 | 0 | default: |
697 | 0 | g_assert_not_reached(); |
698 | 0 | for (int b = 0; b < bands; b++) |
699 | 0 | B[b] = 0; |
700 | 0 | } |
701 | | |
702 | 0 | t1 = 1 - aB; |
703 | 0 | t2 = 1 - aA; |
704 | 0 | t3 = aA * aB; |
705 | 0 | for (int b = 0; b < bands; b++) |
706 | 0 | B[b] = t1 * A[b] + t2 * B[b] + t3 * f[b]; |
707 | 0 | break; |
708 | 0 | } |
709 | | |
710 | 0 | B[bands] = aR; |
711 | 0 | } Unexecuted instantiation: composite.cpp:void vips_composite_base_blend<unsigned char>(_VipsCompositeBase*, VipsBlendMode, double*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_composite_base_blend<signed char>(_VipsCompositeBase*, VipsBlendMode, double*, signed char*) Unexecuted instantiation: composite.cpp:void vips_composite_base_blend<unsigned short>(_VipsCompositeBase*, VipsBlendMode, double*, unsigned short*) Unexecuted instantiation: composite.cpp:void vips_composite_base_blend<short>(_VipsCompositeBase*, VipsBlendMode, double*, short*) Unexecuted instantiation: composite.cpp:void vips_composite_base_blend<unsigned int>(_VipsCompositeBase*, VipsBlendMode, double*, unsigned int*) Unexecuted instantiation: composite.cpp:void vips_composite_base_blend<int>(_VipsCompositeBase*, VipsBlendMode, double*, int*) Unexecuted instantiation: composite.cpp:void vips_composite_base_blend<float>(_VipsCompositeBase*, VipsBlendMode, double*, float*) Unexecuted instantiation: composite.cpp:void vips_composite_base_blend<double>(_VipsCompositeBase*, VipsBlendMode, double*, double*) |
712 | | |
713 | | /* We have a vector path with gcc's vector attr. |
714 | | */ |
715 | | #ifdef HAVE_VECTOR_ARITH |
716 | | /* Special path for RGBA with non-double output. This is overwhelmingly the |
717 | | * most common case, and vectorises easily. |
718 | | * |
719 | | * B is the float pixel we are accumulating, A is the new pixel coming |
720 | | * in from memory. |
721 | | */ |
722 | | template <typename T> |
723 | | static void |
724 | | vips_composite_base_blend3(VipsCompositeSequence *seq, |
725 | | VipsBlendMode mode, v4f &B, T *restrict p) |
726 | 0 | { |
727 | 0 | VipsCompositeBase *composite = seq->composite; |
728 | |
|
729 | 0 | v4f A; |
730 | 0 | float aA; |
731 | 0 | float aB; |
732 | 0 | float aR; |
733 | 0 | float t1; |
734 | 0 | float t2; |
735 | 0 | float t3; |
736 | 0 | v4f f; |
737 | 0 | v4f g; |
738 | | |
739 | | /* Load and scale the pixel to 0 - 1. |
740 | | */ |
741 | 0 | A[0] = p[0]; |
742 | 0 | A[1] = p[1]; |
743 | 0 | A[2] = p[2]; |
744 | 0 | A[3] = p[3]; |
745 | |
|
746 | 0 | A /= seq->max_band_vec; |
747 | |
|
748 | 0 | aA = A[3]; |
749 | 0 | aB = B[3]; |
750 | | |
751 | | /* We may need to premultiply A. |
752 | | */ |
753 | 0 | if (!composite->premultiplied) |
754 | 0 | A *= aA; |
755 | | |
756 | | /* See https://www.cairographics.org/operators for a nice summary of |
757 | | * the operators and their meaning. |
758 | | * |
759 | | * Some operators need the unpremultiplied values (eg. dest-in), so |
760 | | * we have to do an extra unpremultiply/premultiply. |
761 | | */ |
762 | |
|
763 | 0 | switch (mode) { |
764 | 0 | case VIPS_BLEND_MODE_CLEAR: |
765 | 0 | aR = 0; |
766 | 0 | B[0] = 0; |
767 | 0 | B[1] = 0; |
768 | 0 | B[2] = 0; |
769 | 0 | break; |
770 | | |
771 | 0 | case VIPS_BLEND_MODE_SOURCE: |
772 | 0 | aR = aA; |
773 | 0 | B = A; |
774 | 0 | break; |
775 | | |
776 | 0 | case VIPS_BLEND_MODE_OVER: |
777 | 0 | aR = aA + aB * (1 - aA); |
778 | 0 | t1 = 1 - aA; |
779 | 0 | B = A + t1 * B; |
780 | 0 | break; |
781 | | |
782 | 0 | case VIPS_BLEND_MODE_IN: |
783 | 0 | aR = aA * aB; |
784 | | // if aA == 0, then aR == 0 and so B will already be 0 |
785 | 0 | if (aA != 0) |
786 | 0 | B = A * aR / aA; |
787 | 0 | break; |
788 | | |
789 | 0 | case VIPS_BLEND_MODE_OUT: |
790 | 0 | aR = aA * (1 - aB); |
791 | | // if aA == 0, then aR == 0 and so B will already be 0 |
792 | 0 | if (aA != 0) |
793 | 0 | B = A * aR / aA; |
794 | 0 | break; |
795 | | |
796 | 0 | case VIPS_BLEND_MODE_ATOP: |
797 | 0 | aR = aB; |
798 | 0 | t1 = 1 - aA; |
799 | 0 | B = A + t1 * B; |
800 | 0 | break; |
801 | | |
802 | 0 | case VIPS_BLEND_MODE_DEST: |
803 | 0 | aR = aB; |
804 | | // B = B |
805 | 0 | break; |
806 | | |
807 | 0 | case VIPS_BLEND_MODE_DEST_OVER: |
808 | 0 | aR = aB + aA * (1 - aB); |
809 | 0 | t1 = 1 - aB; |
810 | 0 | B = B + t1 * A; |
811 | 0 | break; |
812 | | |
813 | 0 | case VIPS_BLEND_MODE_DEST_IN: |
814 | 0 | aR = aA * aB; |
815 | | // if aB is 0, then B is already 0 |
816 | 0 | if (aB != 0) |
817 | 0 | B *= aR / aB; |
818 | 0 | break; |
819 | | |
820 | 0 | case VIPS_BLEND_MODE_DEST_OUT: |
821 | 0 | aR = (1 - aA) * aB; |
822 | | // B = B |
823 | | // if aB is 0, then B is already 0 |
824 | 0 | if (aB != 0) |
825 | 0 | B *= aR / aB; |
826 | 0 | break; |
827 | | |
828 | 0 | case VIPS_BLEND_MODE_DEST_ATOP: |
829 | 0 | aR = aA; |
830 | 0 | t1 = 1 - aB; |
831 | 0 | B = t1 * A + B; |
832 | 0 | break; |
833 | | |
834 | 0 | case VIPS_BLEND_MODE_XOR: |
835 | 0 | aR = aA + aB - 2 * aA * aB; |
836 | 0 | t1 = 1 - aB; |
837 | 0 | t2 = 1 - aA; |
838 | 0 | B = t1 * A + t2 * B; |
839 | 0 | break; |
840 | | |
841 | 0 | case VIPS_BLEND_MODE_ADD: |
842 | 0 | aR = VIPS_MIN(1, aA + aB); |
843 | 0 | B = A + B; |
844 | 0 | break; |
845 | | |
846 | 0 | case VIPS_BLEND_MODE_SATURATE: |
847 | 0 | aR = VIPS_MIN(1, aA + aB); |
848 | 0 | t1 = VIPS_MIN(aA, 1 - aB); |
849 | 0 | B = t1 * A + B; |
850 | 0 | break; |
851 | | |
852 | 0 | default: |
853 | | /* The PDF modes are a bit different. |
854 | | */ |
855 | 0 | aR = aA + aB * (1 - aA); |
856 | |
|
857 | 0 | switch (mode) { |
858 | 0 | case VIPS_BLEND_MODE_MULTIPLY: |
859 | 0 | f = A * B; |
860 | 0 | break; |
861 | | |
862 | 0 | case VIPS_BLEND_MODE_SCREEN: |
863 | 0 | f = A + B - A * B; |
864 | 0 | break; |
865 | | |
866 | 0 | case VIPS_BLEND_MODE_OVERLAY: |
867 | 0 | f = B <= 0.5f |
868 | 0 | ? 2 * A * B |
869 | 0 | : 1 - 2 * (1 - A) * (1 - B); |
870 | 0 | break; |
871 | | |
872 | 0 | case VIPS_BLEND_MODE_DARKEN: |
873 | 0 | f = VIPS_MIN(A, B); |
874 | 0 | break; |
875 | | |
876 | 0 | case VIPS_BLEND_MODE_LIGHTEN: |
877 | 0 | f = VIPS_MAX(A, B); |
878 | 0 | break; |
879 | | |
880 | 0 | case VIPS_BLEND_MODE_COLOUR_DODGE: |
881 | 0 | f = A < 1 |
882 | 0 | ? VIPS_MIN(1, B / (1 - A)) |
883 | 0 | : 1; |
884 | 0 | break; |
885 | | |
886 | 0 | case VIPS_BLEND_MODE_COLOUR_BURN: |
887 | 0 | f = A > 0 |
888 | 0 | ? 1 - VIPS_MIN(1, (1 - B) / A) |
889 | 0 | : 0; |
890 | 0 | break; |
891 | | |
892 | 0 | case VIPS_BLEND_MODE_HARD_LIGHT: |
893 | 0 | f = A <= 0.5f |
894 | 0 | ? 2 * A * B |
895 | 0 | : 1 - 2 * (1 - A) * (1 - B); |
896 | 0 | break; |
897 | | |
898 | 0 | case VIPS_BLEND_MODE_SOFT_LIGHT: |
899 | | /* You can't sqrt a vector, so we must loop. |
900 | | */ |
901 | 0 | for (int b = 0; b < 3; b++) { |
902 | 0 | double g; |
903 | |
|
904 | 0 | if (B[b] <= 0.25) |
905 | 0 | g = ((16 * B[b] - 12) * B[b] + 4) * B[b]; |
906 | 0 | else |
907 | 0 | g = sqrt(B[b]); |
908 | |
|
909 | 0 | if (A[b] <= 0.5) |
910 | 0 | f[b] = B[b] - (1 - 2 * A[b]) * B[b] * (1 - B[b]); |
911 | 0 | else |
912 | 0 | f[b] = B[b] + (2 * A[b] - 1) * (g - B[b]); |
913 | 0 | } |
914 | 0 | break; |
915 | | |
916 | 0 | case VIPS_BLEND_MODE_DIFFERENCE: |
917 | 0 | g = B - A; |
918 | 0 | f = g > 0 ? g : -1 * g; |
919 | 0 | break; |
920 | | |
921 | 0 | case VIPS_BLEND_MODE_EXCLUSION: |
922 | 0 | f = A + B - 2 * A * B; |
923 | 0 | break; |
924 | | |
925 | 0 | default: |
926 | 0 | g_assert_not_reached(); |
927 | | |
928 | | /* Stop compiler warnings. |
929 | | */ |
930 | 0 | for (int b = 0; b < 3; b++) |
931 | 0 | B[b] = 0; |
932 | 0 | f = A; |
933 | 0 | } |
934 | | |
935 | 0 | t1 = 1 - aB; |
936 | 0 | t2 = 1 - aA; |
937 | 0 | t3 = aA * aB; |
938 | 0 | B = t1 * A + t2 * B + t3 * f; |
939 | 0 | break; |
940 | 0 | } |
941 | | |
942 | 0 | B[3] = aR; |
943 | 0 | } Unexecuted instantiation: composite.cpp:void vips_composite_base_blend3<unsigned char>(VipsCompositeSequence*, VipsBlendMode, float __vector(4)&, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_composite_base_blend3<unsigned short>(VipsCompositeSequence*, VipsBlendMode, float __vector(4)&, unsigned short*) Unexecuted instantiation: composite.cpp:void vips_composite_base_blend3<float>(VipsCompositeSequence*, VipsBlendMode, float __vector(4)&, float*) |
944 | | #endif /*HAVE_VECTOR_ARITH*/ |
945 | | |
946 | | /* min_T and max_T are the numeric range for this type. 0, 0 means no limit, |
947 | | * for example float. |
948 | | */ |
949 | | template <typename T, gint64 min_T, gint64 max_T> |
950 | | static void |
951 | | vips_combine_pixels(VipsCompositeSequence *seq, VipsPel *q) |
952 | 0 | { |
953 | 0 | VipsCompositeBase *composite = seq->composite; |
954 | 0 | VipsBlendMode *mode = (VipsBlendMode *) composite->mode->area.data; |
955 | 0 | int n_mode = composite->mode->area.n; |
956 | 0 | int n = seq->n; |
957 | 0 | int bands = composite->bands; |
958 | 0 | T *restrict tq = (T *restrict) q; |
959 | 0 | T **restrict tp = (T * *restrict) seq->p; |
960 | |
|
961 | 0 | double B[MAX_BANDS + 1]; |
962 | 0 | double aB; |
963 | | |
964 | | /* Load and scale the base pixel to 0 - 1. |
965 | | */ |
966 | 0 | for (int b = 0; b <= bands; b++) |
967 | 0 | B[b] = tp[0][b] / composite->max_band[b]; |
968 | |
|
969 | 0 | aB = B[bands]; |
970 | 0 | if (!composite->premultiplied) |
971 | 0 | for (int b = 0; b < bands; b++) |
972 | 0 | B[b] *= aB; |
973 | |
|
974 | 0 | for (int i = 1; i < n; i++) { |
975 | 0 | int j = seq->enabled[i]; |
976 | 0 | VipsBlendMode m = n_mode == 1 ? mode[0] : mode[j - 1]; |
977 | |
|
978 | 0 | vips_composite_base_blend<T>(composite, m, B, tp[i]); |
979 | 0 | } |
980 | | |
981 | | /* Unpremultiply, if necessary. |
982 | | */ |
983 | 0 | if (!composite->premultiplied) { |
984 | 0 | double aR = B[bands]; |
985 | |
|
986 | 0 | if (aR == 0) |
987 | 0 | for (int b = 0; b < bands; b++) |
988 | 0 | B[b] = 0; |
989 | 0 | else |
990 | 0 | for (int b = 0; b < bands; b++) |
991 | 0 | B[b] = B[b] / aR; |
992 | 0 | } |
993 | | |
994 | | /* Write back as a full range pixel, clipping to range. |
995 | | */ |
996 | 0 | for (int b = 0; b <= bands; b++) { |
997 | 0 | double v; |
998 | |
|
999 | 0 | v = B[b] * composite->max_band[b]; |
1000 | 0 | if (min_T != 0 || |
1001 | 0 | max_T != 0) { |
1002 | 0 | v = VIPS_CLIP(min_T, v, max_T); |
1003 | 0 | } |
1004 | |
|
1005 | 0 | tq[b] = v; |
1006 | 0 | } |
1007 | 0 | } Unexecuted instantiation: composite.cpp:void vips_combine_pixels<unsigned char, 0l, 255l>(VipsCompositeSequence*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_combine_pixels<signed char, -128l, 127l>(VipsCompositeSequence*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_combine_pixels<unsigned short, 0l, 65535l>(VipsCompositeSequence*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_combine_pixels<short, -32768l, 32767l>(VipsCompositeSequence*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_combine_pixels<unsigned int, 0l, 4294967295l>(VipsCompositeSequence*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_combine_pixels<int, -2147483648l, 2147483647l>(VipsCompositeSequence*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_combine_pixels<float, 0l, 0l>(VipsCompositeSequence*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_combine_pixels<double, 0l, 0l>(VipsCompositeSequence*, unsigned char*) |
1008 | | |
1009 | | #ifdef HAVE_VECTOR_ARITH |
1010 | | /* Three band (four with alpha) vector case. Non-double output. min_T and |
1011 | | * max_T are the numeric range for this type. 0, 0 means no limit, |
1012 | | * for example float. |
1013 | | */ |
1014 | | template <typename T, gint64 min_T, gint64 max_T> |
1015 | | static void |
1016 | | vips_combine_pixels3(VipsCompositeSequence *seq, VipsPel *q) |
1017 | 0 | { |
1018 | 0 | VipsCompositeBase *composite = seq->composite; |
1019 | 0 | VipsBlendMode *mode = (VipsBlendMode *) composite->mode->area.data; |
1020 | 0 | int n_mode = composite->mode->area.n; |
1021 | 0 | int n = seq->n; |
1022 | 0 | T *restrict tq = (T *restrict) q; |
1023 | 0 | T **restrict tp = (T * *restrict) seq->p; |
1024 | |
|
1025 | 0 | v4f B; |
1026 | 0 | float aB; |
1027 | |
|
1028 | 0 | B[0] = tp[0][0]; |
1029 | 0 | B[1] = tp[0][1]; |
1030 | 0 | B[2] = tp[0][2]; |
1031 | 0 | B[3] = tp[0][3]; |
1032 | | |
1033 | | /* Scale the base pixel to 0 - 1. |
1034 | | */ |
1035 | 0 | B /= seq->max_band_vec; |
1036 | 0 | aB = B[3]; |
1037 | |
|
1038 | 0 | if (!composite->premultiplied) { |
1039 | 0 | B *= aB; |
1040 | 0 | B[3] = aB; |
1041 | 0 | } |
1042 | |
|
1043 | 0 | for (int i = 1; i < n; i++) { |
1044 | 0 | int j = seq->enabled[i]; |
1045 | 0 | VipsBlendMode m = n_mode == 1 ? mode[0] : mode[j - 1]; |
1046 | |
|
1047 | 0 | vips_composite_base_blend3<T>(seq, m, B, tp[i]); |
1048 | 0 | } |
1049 | | |
1050 | | /* Unpremultiply, if necessary. |
1051 | | */ |
1052 | 0 | if (!composite->premultiplied) { |
1053 | 0 | float aR = B[3]; |
1054 | |
|
1055 | 0 | if (aR == 0) |
1056 | 0 | for (int b = 0; b < 3; b++) |
1057 | 0 | B[b] = 0; |
1058 | 0 | else { |
1059 | 0 | B /= aR; |
1060 | 0 | B[3] = aR; |
1061 | 0 | } |
1062 | 0 | } |
1063 | | |
1064 | | /* Write back as a full range pixel, clipping to range. |
1065 | | */ |
1066 | 0 | B *= seq->max_band_vec; |
1067 | 0 | if (min_T != 0 || |
1068 | 0 | max_T != 0) { |
1069 | 0 | float low = min_T; |
1070 | 0 | float high = max_T; |
1071 | |
|
1072 | 0 | B = VIPS_CLIP(low, B, high); |
1073 | 0 | } |
1074 | |
|
1075 | 0 | tq[0] = B[0]; |
1076 | 0 | tq[1] = B[1]; |
1077 | 0 | tq[2] = B[2]; |
1078 | 0 | tq[3] = B[3]; |
1079 | 0 | } Unexecuted instantiation: composite.cpp:void vips_combine_pixels3<unsigned char, 0l, 255l>(VipsCompositeSequence*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_combine_pixels3<unsigned short, 0l, 65535l>(VipsCompositeSequence*, unsigned char*) Unexecuted instantiation: composite.cpp:void vips_combine_pixels3<float, 0l, 65535l>(VipsCompositeSequence*, unsigned char*) |
1080 | | #endif /*HAVE_VECTOR_ARITH*/ |
1081 | | |
1082 | | static int |
1083 | | vips_composite_base_gen(VipsRegion *output_region, |
1084 | | void *vseq, void *a, void *b, gboolean *stop) |
1085 | 0 | { |
1086 | 0 | VipsCompositeSequence *seq = (VipsCompositeSequence *) vseq; |
1087 | 0 | VipsCompositeBase *composite = (VipsCompositeBase *) b; |
1088 | 0 | VipsRect *r = &output_region->valid; |
1089 | 0 | int ps = VIPS_IMAGE_SIZEOF_PEL(output_region->im); |
1090 | |
|
1091 | 0 | VIPS_DEBUG_MSG("vips_composite_base_gen: at %d x %d, size %d x %d\n", |
1092 | 0 | r->left, r->top, r->width, r->height); |
1093 | | |
1094 | | /* Find the subset of our input images which intersect this region. |
1095 | | */ |
1096 | 0 | vips_composite_base_select(seq, r); |
1097 | |
|
1098 | 0 | VIPS_DEBUG_MSG(" selected %d images\n", seq->n); |
1099 | | |
1100 | | /* Is there just one? We can prepare directly to output and return. |
1101 | | */ |
1102 | 0 | if (seq->n == 1) { |
1103 | | /* This can only be the background image, since it's the only |
1104 | | * image which exactly fills the whole output. |
1105 | | */ |
1106 | 0 | g_assert(seq->enabled[0] == 0); |
1107 | |
|
1108 | 0 | if (vips_region_prepare(seq->input_regions[0], r)) |
1109 | 0 | return -1; |
1110 | 0 | if (vips_region_region(output_region, seq->input_regions[0], |
1111 | 0 | r, r->left, r->top)) |
1112 | 0 | return -1; |
1113 | | |
1114 | 0 | return 0; |
1115 | 0 | } |
1116 | | |
1117 | | /* Prepare the appropriate parts into our set of composite |
1118 | | * regions. |
1119 | | */ |
1120 | 0 | for (int i = 0; i < seq->n; i++) { |
1121 | 0 | int j = seq->enabled[i]; |
1122 | |
|
1123 | 0 | VipsRect hit; |
1124 | 0 | VipsRect request; |
1125 | | |
1126 | | /* Set the composite region up to be a bit of memory at the |
1127 | | * right position. |
1128 | | */ |
1129 | 0 | if (vips_region_buffer(seq->composite_regions[j], r)) |
1130 | 0 | return -1; |
1131 | | |
1132 | | /* Clip against this subimage position and size. |
1133 | | */ |
1134 | 0 | hit = *r; |
1135 | 0 | vips_rect_intersectrect(&hit, &composite->subimages[j], &hit); |
1136 | | |
1137 | | /* Translate request to subimage coordinates. |
1138 | | */ |
1139 | 0 | request = hit; |
1140 | 0 | request.left -= composite->subimages[j].left; |
1141 | 0 | request.top -= composite->subimages[j].top; |
1142 | | |
1143 | | /* If the request is smaller than the target region, there |
1144 | | * will be some gaps. We must make sure these are zero. |
1145 | | */ |
1146 | 0 | if (request.width < r->width || |
1147 | 0 | request.height < r->height) |
1148 | 0 | vips_region_black(seq->composite_regions[j]); |
1149 | | |
1150 | | /* And render the right part of the input image to the |
1151 | | * composite region. |
1152 | | * |
1153 | | * If we are not in skippable mode, we can be completely |
1154 | | * outside the subimage area. |
1155 | | */ |
1156 | 0 | if (!vips_rect_isempty(&request)) { |
1157 | 0 | VIPS_DEBUG_MSG(" fetching pixels for input %d\n", j); |
1158 | 0 | if (vips_region_prepare_to(seq->input_regions[j], |
1159 | 0 | seq->composite_regions[j], &request, |
1160 | 0 | hit.left, hit.top)) |
1161 | 0 | return -1; |
1162 | 0 | } |
1163 | 0 | } |
1164 | | |
1165 | 0 | VIPS_GATE_START("vips_composite_base_gen: work"); |
1166 | |
|
1167 | 0 | for (int y = 0; y < r->height; y++) { |
1168 | 0 | VipsPel *q; |
1169 | |
|
1170 | 0 | for (int i = 0; i < seq->n; i++) { |
1171 | 0 | int j = seq->enabled[i]; |
1172 | |
|
1173 | 0 | seq->p[i] = VIPS_REGION_ADDR(seq->composite_regions[j], |
1174 | 0 | r->left, r->top + y); |
1175 | 0 | } |
1176 | 0 | q = VIPS_REGION_ADDR(output_region, r->left, r->top + y); |
1177 | |
|
1178 | 0 | for (int x = 0; x < r->width; x++) { |
1179 | 0 | switch (seq->input_regions[0]->im->BandFmt) { |
1180 | 0 | case VIPS_FORMAT_UCHAR: |
1181 | 0 | #ifdef HAVE_VECTOR_ARITH |
1182 | 0 | if (composite->bands == 3) |
1183 | 0 | vips_combine_pixels3<unsigned char, |
1184 | 0 | 0, UCHAR_MAX>(seq, q); |
1185 | 0 | else |
1186 | 0 | #endif |
1187 | 0 | vips_combine_pixels<unsigned char, |
1188 | 0 | 0, UCHAR_MAX>(seq, q); |
1189 | 0 | break; |
1190 | | |
1191 | 0 | case VIPS_FORMAT_CHAR: |
1192 | 0 | vips_combine_pixels<signed char, |
1193 | 0 | SCHAR_MIN, SCHAR_MAX>(seq, q); |
1194 | 0 | break; |
1195 | | |
1196 | 0 | case VIPS_FORMAT_USHORT: |
1197 | 0 | #ifdef HAVE_VECTOR_ARITH |
1198 | 0 | if (composite->bands == 3) |
1199 | 0 | vips_combine_pixels3<unsigned short, |
1200 | 0 | 0, USHRT_MAX>(seq, q); |
1201 | 0 | else |
1202 | 0 | #endif |
1203 | 0 | vips_combine_pixels<unsigned short, |
1204 | 0 | 0, USHRT_MAX>(seq, q); |
1205 | 0 | break; |
1206 | | |
1207 | 0 | case VIPS_FORMAT_SHORT: |
1208 | 0 | vips_combine_pixels<signed short, |
1209 | 0 | SHRT_MIN, SHRT_MAX>(seq, q); |
1210 | 0 | break; |
1211 | | |
1212 | 0 | case VIPS_FORMAT_UINT: |
1213 | 0 | vips_combine_pixels<unsigned int, |
1214 | 0 | 0, UINT_MAX>(seq, q); |
1215 | 0 | break; |
1216 | | |
1217 | 0 | case VIPS_FORMAT_INT: |
1218 | 0 | vips_combine_pixels<signed int, |
1219 | 0 | INT_MIN, INT_MAX>(seq, q); |
1220 | 0 | break; |
1221 | | |
1222 | 0 | case VIPS_FORMAT_FLOAT: |
1223 | 0 | #ifdef HAVE_VECTOR_ARITH |
1224 | 0 | if (composite->bands == 3) |
1225 | 0 | vips_combine_pixels3<float, |
1226 | 0 | 0, USHRT_MAX>(seq, q); |
1227 | 0 | else |
1228 | 0 | #endif |
1229 | 0 | vips_combine_pixels<float, |
1230 | 0 | 0, 0>(seq, q); |
1231 | 0 | break; |
1232 | | |
1233 | 0 | case VIPS_FORMAT_DOUBLE: |
1234 | 0 | vips_combine_pixels<double, |
1235 | 0 | 0, 0>(seq, q); |
1236 | 0 | break; |
1237 | | |
1238 | 0 | default: |
1239 | 0 | g_assert_not_reached(); |
1240 | 0 | return -1; |
1241 | 0 | } |
1242 | | |
1243 | 0 | for (int i = 0; i < seq->n; i++) |
1244 | 0 | seq->p[i] += ps; |
1245 | 0 | q += ps; |
1246 | 0 | } |
1247 | 0 | } |
1248 | | |
1249 | 0 | VIPS_GATE_STOP("vips_composite_base_gen: work"); |
1250 | |
|
1251 | 0 | return 0; |
1252 | 0 | } |
1253 | | |
1254 | | /* Is a mode "skippable"? |
1255 | | * |
1256 | | * Skippable modes are ones where a black (0, 0, 0, 0) layer placed over the |
1257 | | * base image and composited has no effect. |
1258 | | * |
1259 | | * If all the modes in our stack are skippable, we can avoid compositing the |
1260 | | * whole stack for every request. |
1261 | | */ |
1262 | | static gboolean |
1263 | | vips_composite_mode_skippable(VipsBlendMode mode) |
1264 | 0 | { |
1265 | 0 | switch (mode) { |
1266 | 0 | case VIPS_BLEND_MODE_CLEAR: |
1267 | 0 | case VIPS_BLEND_MODE_SOURCE: |
1268 | 0 | case VIPS_BLEND_MODE_IN: |
1269 | 0 | case VIPS_BLEND_MODE_OUT: |
1270 | 0 | case VIPS_BLEND_MODE_DEST_IN: |
1271 | 0 | case VIPS_BLEND_MODE_DEST_ATOP: |
1272 | 0 | return FALSE; |
1273 | | |
1274 | 0 | default: |
1275 | 0 | return TRUE; |
1276 | 0 | } |
1277 | 0 | } |
1278 | | |
1279 | | static int |
1280 | | vips_composite_base_build(VipsObject *object) |
1281 | 0 | { |
1282 | 0 | VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS(object); |
1283 | 0 | VipsConversion *conversion = VIPS_CONVERSION(object); |
1284 | 0 | VipsCompositeBase *composite = (VipsCompositeBase *) object; |
1285 | |
|
1286 | 0 | int n; |
1287 | 0 | VipsBlendMode *mode; |
1288 | 0 | VipsImage **in; |
1289 | 0 | VipsImage **decode; |
1290 | 0 | VipsImage **compositing; |
1291 | 0 | VipsImage **format; |
1292 | |
|
1293 | 0 | if (VIPS_OBJECT_CLASS(vips_composite_base_parent_class)->build(object)) |
1294 | 0 | return -1; |
1295 | | |
1296 | 0 | n = composite->in->area.n; |
1297 | |
|
1298 | 0 | if (n <= 0) { |
1299 | 0 | vips_error(klass->nickname, "%s", _("no input images")); |
1300 | 0 | return -1; |
1301 | 0 | } |
1302 | 0 | if (composite->mode->area.n != n - 1 && |
1303 | 0 | composite->mode->area.n != 1) { |
1304 | 0 | vips_error(klass->nickname, _("must be 1 or %d blend modes"), |
1305 | 0 | n - 1); |
1306 | 0 | return -1; |
1307 | 0 | } |
1308 | 0 | mode = (VipsBlendMode *) composite->mode->area.data; |
1309 | 0 | composite->skippable = TRUE; |
1310 | 0 | for (int i = 0; i < composite->mode->area.n; i++) { |
1311 | 0 | if (mode[i] < 0 || |
1312 | 0 | mode[i] >= VIPS_BLEND_MODE_LAST) { |
1313 | 0 | vips_error(klass->nickname, |
1314 | 0 | _("blend mode index %d (%d) invalid"), |
1315 | 0 | i, mode[i]); |
1316 | 0 | return -1; |
1317 | 0 | } |
1318 | | |
1319 | 0 | if (!vips_composite_mode_skippable(mode[i])) |
1320 | 0 | composite->skippable = FALSE; |
1321 | 0 | } |
1322 | | |
1323 | 0 | in = (VipsImage **) composite->in->area.data; |
1324 | | |
1325 | | /* Make a set of rects for the positions of the input images. Image 0 |
1326 | | * (the background) is always at (0, 0). |
1327 | | */ |
1328 | 0 | if (!(composite->subimages = |
1329 | 0 | VIPS_ARRAY(NULL, n, VipsRect))) |
1330 | 0 | return -1; |
1331 | 0 | for (int i = 0; i < n; i++) { |
1332 | 0 | composite->subimages[i].left = 0; |
1333 | 0 | composite->subimages[i].top = 0; |
1334 | 0 | composite->subimages[i].width = in[i]->Xsize; |
1335 | 0 | composite->subimages[i].height = in[i]->Ysize; |
1336 | 0 | } |
1337 | | |
1338 | | /* Position all images, if x/y is set. Image 0 |
1339 | | * (the background) is always at (0, 0). |
1340 | | */ |
1341 | 0 | if (composite->x_offset && |
1342 | 0 | composite->y_offset) |
1343 | 0 | for (int i = 1; i < n; i++) { |
1344 | 0 | composite->subimages[i].left = |
1345 | 0 | composite->x_offset[i - 1]; |
1346 | 0 | composite->subimages[i].top = |
1347 | 0 | composite->y_offset[i - 1]; |
1348 | 0 | } |
1349 | |
|
1350 | 0 | decode = (VipsImage **) vips_object_local_array(object, n); |
1351 | 0 | for (int i = 0; i < n; i++) |
1352 | 0 | if (vips_image_decode(in[i], &decode[i])) |
1353 | 0 | return -1; |
1354 | 0 | in = decode; |
1355 | | |
1356 | | /* Add a solid alpha to any images missing one. |
1357 | | */ |
1358 | 0 | for (int i = n - 1; i >= 0; i--) |
1359 | 0 | if (!vips_image_hasalpha(in[i])) { |
1360 | 0 | VipsImage *x; |
1361 | |
|
1362 | 0 | if (vips_addalpha(in[i], &x, nullptr)) |
1363 | 0 | return -1; |
1364 | 0 | g_object_unref(in[i]); |
1365 | 0 | in[i] = x; |
1366 | 0 | } |
1367 | | |
1368 | | /* Transform to compositing space. It defaults to sRGB or B_W, usually |
1369 | | * 8 bit, but 16 bit if any inputs are 16 bit. |
1370 | | */ |
1371 | 0 | if (!vips_object_argument_isset(object, "compositing_space")) { |
1372 | 0 | gboolean all_grey; |
1373 | 0 | gboolean any_16; |
1374 | |
|
1375 | 0 | all_grey = TRUE; |
1376 | 0 | for (int i = 0; i < n; i++) |
1377 | 0 | if (in[i]->Bands > 2) { |
1378 | 0 | all_grey = FALSE; |
1379 | 0 | break; |
1380 | 0 | } |
1381 | |
|
1382 | 0 | any_16 = FALSE; |
1383 | 0 | for (int i = 0; i < n; i++) |
1384 | 0 | if (in[i]->Type == VIPS_INTERPRETATION_GREY16 || |
1385 | 0 | in[i]->Type == VIPS_INTERPRETATION_RGB16) { |
1386 | 0 | any_16 = TRUE; |
1387 | 0 | break; |
1388 | 0 | } |
1389 | |
|
1390 | 0 | composite->compositing_space = any_16 // FIXME: Invalidates operation cache |
1391 | 0 | ? (all_grey |
1392 | 0 | ? VIPS_INTERPRETATION_GREY16 |
1393 | 0 | : VIPS_INTERPRETATION_RGB16) |
1394 | 0 | : (all_grey |
1395 | 0 | ? VIPS_INTERPRETATION_B_W |
1396 | 0 | : VIPS_INTERPRETATION_sRGB); |
1397 | 0 | } |
1398 | |
|
1399 | 0 | compositing = (VipsImage **) |
1400 | 0 | vips_object_local_array(object, n); |
1401 | 0 | for (int i = 0; i < n; i++) |
1402 | 0 | if (vips_colourspace(in[i], &compositing[i], |
1403 | 0 | composite->compositing_space, nullptr)) |
1404 | 0 | return -1; |
1405 | 0 | in = compositing; |
1406 | | |
1407 | | /* Check that they all now match in bands. This can fail for some |
1408 | | * input combinations. |
1409 | | */ |
1410 | 0 | for (int i = 1; i < n; i++) |
1411 | 0 | if (in[i]->Bands != in[0]->Bands) { |
1412 | 0 | vips_error(klass->nickname, |
1413 | 0 | "%s", _("images do not have same " |
1414 | 0 | "numbers of bands")); |
1415 | 0 | return -1; |
1416 | 0 | } |
1417 | | |
1418 | 0 | if (in[0]->Bands > MAX_BANDS) { |
1419 | 0 | vips_error(klass->nickname, |
1420 | 0 | "%s", _("too many input bands")); |
1421 | 0 | return -1; |
1422 | 0 | } |
1423 | | |
1424 | 0 | composite->bands = in[0]->Bands - 1; |
1425 | | |
1426 | | /* Set the max for each band now we know bands and compositing space. |
1427 | | */ |
1428 | 0 | if (vips_composite_base_max_band(composite, composite->max_band)) { |
1429 | 0 | vips_error(klass->nickname, |
1430 | 0 | "%s", _("unsupported compositing space")); |
1431 | 0 | return -1; |
1432 | 0 | } |
1433 | | |
1434 | | /* Transform the input images to match in format. We may have |
1435 | | * mixed float and double, for example. |
1436 | | */ |
1437 | 0 | format = (VipsImage **) vips_object_local_array(object, n); |
1438 | 0 | if (vips__formatalike_vec(in, format, n)) |
1439 | 0 | return -1; |
1440 | 0 | in = format; |
1441 | | |
1442 | | /* We want locality, so that we only prepare a few subimages each |
1443 | | * time. |
1444 | | */ |
1445 | 0 | if (vips_image_pipeline_array(conversion->out, |
1446 | 0 | VIPS_DEMAND_STYLE_SMALLTILE, in)) |
1447 | 0 | return -1; |
1448 | | |
1449 | | /* The output image is always the size of the base image. |
1450 | | */ |
1451 | 0 | conversion->out->Xsize = in[0]->Xsize; |
1452 | 0 | conversion->out->Ysize = in[0]->Ysize; |
1453 | |
|
1454 | 0 | if (vips_image_generate(conversion->out, |
1455 | 0 | vips_composite_start, |
1456 | 0 | vips_composite_base_gen, |
1457 | 0 | vips_composite_stop, |
1458 | 0 | in, composite)) |
1459 | 0 | return -1; |
1460 | | |
1461 | 0 | return 0; |
1462 | 0 | } |
1463 | | |
1464 | | static void |
1465 | | vips_composite_base_class_init(VipsCompositeBaseClass *klass) |
1466 | 1 | { |
1467 | 1 | GObjectClass *gobject_class = G_OBJECT_CLASS(klass); |
1468 | 1 | VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(klass); |
1469 | 1 | VipsOperationClass *operation_class = VIPS_OPERATION_CLASS(klass); |
1470 | | |
1471 | 1 | VIPS_DEBUG_MSG("vips_composite_base_class_init\n"); |
1472 | | |
1473 | 1 | gobject_class->dispose = vips_composite_base_dispose; |
1474 | 1 | gobject_class->set_property = vips_object_set_property; |
1475 | 1 | gobject_class->get_property = vips_object_get_property; |
1476 | | |
1477 | 1 | vobject_class->nickname = "composite_base"; |
1478 | 1 | vobject_class->description = _("blend images together"); |
1479 | 1 | vobject_class->build = vips_composite_base_build; |
1480 | | |
1481 | 1 | operation_class->flags = VIPS_OPERATION_SEQUENTIAL; |
1482 | | |
1483 | 1 | VIPS_ARG_ENUM(klass, "compositing_space", 10, |
1484 | 1 | _("Compositing space"), |
1485 | 1 | _("Composite images in this colour space"), |
1486 | 1 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
1487 | 1 | G_STRUCT_OFFSET(VipsCompositeBase, compositing_space), |
1488 | 1 | VIPS_TYPE_INTERPRETATION, VIPS_INTERPRETATION_sRGB); |
1489 | | |
1490 | 1 | VIPS_ARG_BOOL(klass, "premultiplied", 11, |
1491 | 1 | _("Premultiplied"), |
1492 | 1 | _("Images have premultiplied alpha"), |
1493 | 1 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
1494 | 1 | G_STRUCT_OFFSET(VipsCompositeBase, premultiplied), |
1495 | 1 | FALSE); |
1496 | 1 | } |
1497 | | |
1498 | | static void |
1499 | | vips_composite_base_init(VipsCompositeBase *composite) |
1500 | 0 | { |
1501 | 0 | composite->compositing_space = VIPS_INTERPRETATION_sRGB; |
1502 | 0 | } |
1503 | | |
1504 | | typedef struct _VipsComposite { |
1505 | | VipsCompositeBase parent_instance; |
1506 | | |
1507 | | /* For N input images, N - 1 x coordinates. |
1508 | | */ |
1509 | | VipsArrayInt *x; |
1510 | | |
1511 | | /* For N input images, N - 1 y coordinates. |
1512 | | */ |
1513 | | VipsArrayInt *y; |
1514 | | |
1515 | | } VipsComposite; |
1516 | | |
1517 | | typedef VipsCompositeBaseClass VipsCompositeClass; |
1518 | | |
1519 | | /* We need C linkage for this. |
1520 | | */ |
1521 | | extern "C" { |
1522 | | G_DEFINE_TYPE(VipsComposite, vips_composite, vips_composite_base_get_type()); |
1523 | | } |
1524 | | |
1525 | | static int |
1526 | | vips_composite_build(VipsObject *object) |
1527 | 0 | { |
1528 | 0 | VipsObjectClass *klass = VIPS_OBJECT_GET_CLASS(object); |
1529 | 0 | VipsCompositeBase *base = (VipsCompositeBase *) object; |
1530 | 0 | VipsComposite *composite = (VipsComposite *) object; |
1531 | |
|
1532 | 0 | int n; |
1533 | |
|
1534 | 0 | n = 0; |
1535 | 0 | if (vips_object_argument_isset(object, "in")) |
1536 | 0 | n = base->in->area.n; |
1537 | |
|
1538 | 0 | if (vips_object_argument_isset(object, "x")) { |
1539 | 0 | if (composite->x->area.n != n - 1) { |
1540 | 0 | vips_error(klass->nickname, _("must be %d x coordinates"), n - 1); |
1541 | 0 | return -1; |
1542 | 0 | } |
1543 | 0 | base->x_offset = (int *) composite->x->area.data; |
1544 | 0 | } |
1545 | | |
1546 | 0 | if (vips_object_argument_isset(object, "y")) { |
1547 | 0 | if (composite->y->area.n != n - 1) { |
1548 | 0 | vips_error(klass->nickname, _("must be %d y coordinates"), n - 1); |
1549 | 0 | return -1; |
1550 | 0 | } |
1551 | 0 | base->y_offset = (int *) composite->y->area.data; |
1552 | 0 | } |
1553 | | |
1554 | 0 | if (VIPS_OBJECT_CLASS(vips_composite_parent_class)->build(object)) |
1555 | 0 | return -1; |
1556 | | |
1557 | 0 | return 0; |
1558 | 0 | } |
1559 | | |
1560 | | static void |
1561 | | vips_composite_class_init(VipsCompositeClass *klass) |
1562 | 1 | { |
1563 | 1 | GObjectClass *gobject_class = G_OBJECT_CLASS(klass); |
1564 | 1 | VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(klass); |
1565 | 1 | VipsOperationClass *operation_class = VIPS_OPERATION_CLASS(klass); |
1566 | | |
1567 | 1 | VIPS_DEBUG_MSG("vips_composite_class_init\n"); |
1568 | | |
1569 | 1 | gobject_class->set_property = vips_object_set_property; |
1570 | 1 | gobject_class->get_property = vips_object_get_property; |
1571 | | |
1572 | 1 | vobject_class->nickname = "composite"; |
1573 | 1 | vobject_class->description = |
1574 | 1 | _("blend an array of images with an array of blend modes"); |
1575 | 1 | vobject_class->build = vips_composite_build; |
1576 | | |
1577 | 1 | operation_class->flags = VIPS_OPERATION_SEQUENTIAL; |
1578 | | |
1579 | 1 | VIPS_ARG_BOXED(klass, "in", 0, |
1580 | 1 | _("Inputs"), |
1581 | 1 | _("Array of input images"), |
1582 | 1 | VIPS_ARGUMENT_REQUIRED_INPUT, |
1583 | 1 | G_STRUCT_OFFSET(VipsCompositeBase, in), |
1584 | 1 | VIPS_TYPE_ARRAY_IMAGE); |
1585 | | |
1586 | 1 | VIPS_ARG_BOXED(klass, "mode", 3, |
1587 | 1 | _("Blend modes"), |
1588 | 1 | _("Array of VipsBlendMode to join with"), |
1589 | 1 | VIPS_ARGUMENT_REQUIRED_INPUT, |
1590 | 1 | G_STRUCT_OFFSET(VipsCompositeBase, mode), |
1591 | 1 | VIPS_TYPE_ARRAY_INT); |
1592 | | |
1593 | 1 | VIPS_ARG_BOXED(klass, "x", 4, |
1594 | 1 | _("x coordinates"), |
1595 | 1 | _("Array of x coordinates to join at"), |
1596 | 1 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
1597 | 1 | G_STRUCT_OFFSET(VipsComposite, x), |
1598 | 1 | VIPS_TYPE_ARRAY_INT); |
1599 | | |
1600 | 1 | VIPS_ARG_BOXED(klass, "y", 5, |
1601 | 1 | _("y coordinates"), |
1602 | 1 | _("Array of y coordinates to join at"), |
1603 | 1 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
1604 | 1 | G_STRUCT_OFFSET(VipsComposite, y), |
1605 | 1 | VIPS_TYPE_ARRAY_INT); |
1606 | 1 | } |
1607 | | |
1608 | | static void |
1609 | | vips_composite_init(VipsComposite *composite) |
1610 | 0 | { |
1611 | 0 | } |
1612 | | |
1613 | | static int |
1614 | | vips_compositev(VipsImage **in, VipsImage **out, int n, int *mode, va_list ap) |
1615 | 0 | { |
1616 | 0 | VipsArrayImage *image_array; |
1617 | 0 | VipsArrayInt *mode_array; |
1618 | 0 | int result; |
1619 | |
|
1620 | 0 | image_array = vips_array_image_new(in, n); |
1621 | 0 | mode_array = vips_array_int_new(mode, n - 1); |
1622 | 0 | result = vips_call_split("composite", ap, |
1623 | 0 | image_array, out, mode_array); |
1624 | 0 | vips_area_unref(VIPS_AREA(image_array)); |
1625 | 0 | vips_area_unref(VIPS_AREA(mode_array)); |
1626 | |
|
1627 | 0 | return result; |
1628 | 0 | } |
1629 | | |
1630 | | /* See conversion.c for the doc comment. |
1631 | | */ |
1632 | | |
1633 | | int |
1634 | | vips_composite(VipsImage **in, VipsImage **out, int n, int *mode, ...) |
1635 | 0 | { |
1636 | 0 | va_list ap; |
1637 | 0 | int result; |
1638 | |
|
1639 | 0 | va_start(ap, mode); |
1640 | 0 | result = vips_compositev(in, out, n, mode, ap); |
1641 | 0 | va_end(ap); |
1642 | |
|
1643 | 0 | return result; |
1644 | 0 | } |
1645 | | |
1646 | | typedef struct _VipsComposite2 { |
1647 | | VipsCompositeBase parent_instance; |
1648 | | |
1649 | | VipsImage *base; |
1650 | | VipsImage *overlay; |
1651 | | VipsBlendMode mode; |
1652 | | int x; |
1653 | | int y; |
1654 | | |
1655 | | } VipsComposite2; |
1656 | | |
1657 | | typedef VipsCompositeBaseClass VipsComposite2Class; |
1658 | | |
1659 | | /* We need C linkage for this. |
1660 | | */ |
1661 | | extern "C" { |
1662 | | G_DEFINE_TYPE(VipsComposite2, vips_composite2, vips_composite_base_get_type()); |
1663 | | } |
1664 | | |
1665 | | static int |
1666 | | vips_composite2_build(VipsObject *object) |
1667 | 0 | { |
1668 | 0 | VipsCompositeBase *base = (VipsCompositeBase *) object; |
1669 | 0 | VipsComposite2 *composite2 = (VipsComposite2 *) object; |
1670 | |
|
1671 | 0 | if (composite2->overlay && |
1672 | 0 | composite2->base) { |
1673 | 0 | VipsImage *in[3]; |
1674 | 0 | int mode[1]; |
1675 | |
|
1676 | 0 | in[0] = composite2->base; |
1677 | 0 | in[1] = composite2->overlay; |
1678 | 0 | in[2] = NULL; |
1679 | 0 | base->in = vips_array_image_new(in, 2); |
1680 | |
|
1681 | 0 | mode[0] = (int) composite2->mode; |
1682 | 0 | base->mode = vips_array_int_new(mode, 1); |
1683 | 0 | } |
1684 | |
|
1685 | 0 | base->x_offset = &composite2->x; |
1686 | 0 | base->y_offset = &composite2->y; |
1687 | |
|
1688 | 0 | if (VIPS_OBJECT_CLASS(vips_composite2_parent_class)->build(object)) |
1689 | 0 | return -1; |
1690 | | |
1691 | 0 | return 0; |
1692 | 0 | } |
1693 | | |
1694 | | static void |
1695 | | vips_composite2_class_init(VipsCompositeClass *klass) |
1696 | 1 | { |
1697 | 1 | GObjectClass *gobject_class = G_OBJECT_CLASS(klass); |
1698 | 1 | VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(klass); |
1699 | 1 | VipsOperationClass *operation_class = VIPS_OPERATION_CLASS(klass); |
1700 | | |
1701 | 1 | VIPS_DEBUG_MSG("vips_composite_class_init\n"); |
1702 | | |
1703 | 1 | gobject_class->set_property = vips_object_set_property; |
1704 | 1 | gobject_class->get_property = vips_object_get_property; |
1705 | | |
1706 | 1 | vobject_class->nickname = "composite2"; |
1707 | 1 | vobject_class->description = |
1708 | 1 | _("blend a pair of images with a blend mode"); |
1709 | 1 | vobject_class->build = vips_composite2_build; |
1710 | | |
1711 | 1 | operation_class->flags = VIPS_OPERATION_SEQUENTIAL; |
1712 | | |
1713 | 1 | VIPS_ARG_IMAGE(klass, "base", 0, |
1714 | 1 | _("Base"), |
1715 | 1 | _("Base image"), |
1716 | 1 | VIPS_ARGUMENT_REQUIRED_INPUT, |
1717 | 1 | G_STRUCT_OFFSET(VipsComposite2, base)); |
1718 | | |
1719 | 1 | VIPS_ARG_IMAGE(klass, "overlay", 1, |
1720 | 1 | _("Overlay"), |
1721 | 1 | _("Overlay image"), |
1722 | 1 | VIPS_ARGUMENT_REQUIRED_INPUT, |
1723 | 1 | G_STRUCT_OFFSET(VipsComposite2, overlay)); |
1724 | | |
1725 | 1 | VIPS_ARG_ENUM(klass, "mode", 3, |
1726 | 1 | _("Blend mode"), |
1727 | 1 | _("VipsBlendMode to join with"), |
1728 | 1 | VIPS_ARGUMENT_REQUIRED_INPUT, |
1729 | 1 | G_STRUCT_OFFSET(VipsComposite2, mode), |
1730 | 1 | VIPS_TYPE_BLEND_MODE, VIPS_BLEND_MODE_OVER); |
1731 | | |
1732 | 1 | VIPS_ARG_INT(klass, "x", 4, |
1733 | 1 | _("x"), |
1734 | 1 | _("x position of overlay"), |
1735 | 1 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
1736 | 1 | G_STRUCT_OFFSET(VipsComposite2, x), |
1737 | 1 | -VIPS_MAX_COORD, VIPS_MAX_COORD, 0); |
1738 | | |
1739 | 1 | VIPS_ARG_INT(klass, "y", 5, |
1740 | 1 | _("y"), |
1741 | 1 | _("y position of overlay"), |
1742 | 1 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
1743 | 1 | G_STRUCT_OFFSET(VipsComposite2, y), |
1744 | 1 | -VIPS_MAX_COORD, VIPS_MAX_COORD, 0); |
1745 | 1 | } |
1746 | | |
1747 | | static void |
1748 | | vips_composite2_init(VipsComposite2 *composite2) |
1749 | 0 | { |
1750 | 0 | } |
1751 | | |
1752 | | /* See conversion.c for the doc comment. |
1753 | | */ |
1754 | | |
1755 | | int |
1756 | | vips_composite2(VipsImage *base, VipsImage *overlay, VipsImage **out, |
1757 | | VipsBlendMode mode, ...) |
1758 | 0 | { |
1759 | 0 | va_list ap; |
1760 | 0 | int result; |
1761 | | |
1762 | | /* Works for gcc and clang. |
1763 | | */ |
1764 | 0 | #pragma GCC diagnostic push |
1765 | 0 | #pragma GCC diagnostic ignored "-Wvarargs" |
1766 | | |
1767 | | /* Triggers a clang compiler warning because mode might not be an int. |
1768 | | * I think the warning is harmless for all platforms we care about. |
1769 | | */ |
1770 | 0 | va_start(ap, mode); |
1771 | |
|
1772 | 0 | g_assert(sizeof(mode) == sizeof(int)); |
1773 | |
|
1774 | 0 | #pragma GCC diagnostic pop |
1775 | |
|
1776 | 0 | result = vips_call_split("composite2", ap, base, overlay, out, mode); |
1777 | 0 | va_end(ap); |
1778 | |
|
1779 | 0 | return result; |
1780 | 0 | } |