1 /* Merge two images top-bottom. dx, dy is the offset needed to get from sec
2 * (secondary image) to ref (reference image).
3 *
4 * Usage:
5 *
6 * int
7 * vips_tbmerge( ref, sec, out, dx, dy )
8 * VipsImage *ref, *sec, *out;
9 * int dx, dy;
10 *
11 * Returns 0 on success and -1 on error
12 *
13 * Copyright: 1990, 1991 N. Dessipris
14 * Author: N. Dessipris
15 * Written on: 20/09/1990
16 * Updated on: 17/04/1991
17 * 1/6/92: J. Cupitt
18 * - check for difference bug fixed
19 * - geometry calculations improved and simplified
20 * - small speedups
21 * 30/6/93 K.Martinez : coped with IM_CODING_LABQ images
22 * 7/7/93 JC
23 * - ANSIfied
24 * - proper freeing on errors, ready for partial
25 * 8/11/93 JC
26 * - now propagates both input histories
27 * - adds magic lines for global mosaic optimisation
28 *
29 *
30 * 16/May/1994 Ahmed. Abbood
31 * - Modified to use partials on all IO
32 *
33 * June/1995 Ahmed Abbood
34 *
35 * - Modified to work with different types of images.
36 *
37 *
38 * 16/6/95 JC
39 * - added to VIPS!
40 * 7/9/95 JC
41 * - split into two parts: im_tbmerge() and im__tbmerge()
42 * - latter called by im_tbmosaic()
43 * - just the same as public im_tbmerge(), but adds no history
44 * - necessary for im_global_balance()
45 * - small bugs fixed
46 * 10/10/95 JC
47 * - better checks that parameters are sensible
48 * 11/10/95 JC
49 * - Kirk spotted what a load of rubbish Ahmed's code is
50 * - rewritten - many, many bugs fixed
51 * 28/7/97 JC
52 * - new non-rectangular im_lrmerge adapted to make this
53 * - small tidies
54 * 18/2/98 JC
55 * - im_demand_hint() call added
56 * 19/2/98 JC
57 * - now works for any dx/dy by calling im_insert() for bizarre cases
58 * 2/2/01 JC
59 * - added tunable max blend width
60 * 8/3/01 JC
61 * - switched to integer arithmetic for integer blends
62 * 23/3/01 JC
63 * - oops, iblend was broken
64 * 7/11/01 JC
65 * - more sophisticated transparency handling
66 * 15/8/02 JC
67 * - records Xoffset/Yoffset
68 * 20/6/05
69 * - now requires all bands == 0 for transparency (used to just check
70 * band 0)
71 * 24/1/11
72 * - gtk-doc
73 * - match formats and bands automatically
74 * 18/6/20 kleisauke
75 * - convert to vips8
76 */
77
78 /*
79
80 This file is part of VIPS.
81
82 VIPS is free software; you can redistribute it and/or modify
83 it under the terms of the GNU Lesser General Public License as published by
84 the Free Software Foundation; either version 2 of the License, or
85 (at your option) any later version.
86
87 This program is distributed in the hope that it will be useful,
88 but WITHOUT ANY WARRANTY; without even the implied warranty of
89 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
90 GNU Lesser General Public License for more details.
91
92 You should have received a copy of the GNU Lesser General Public License
93 along with this program; if not, write to the Free Software
94 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
95 02110-1301 USA
96
97 */
98
99 /*
100
101 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
102
103 */
104
105 #ifdef HAVE_CONFIG_H
106 #include <config.h>
107 #endif /*HAVE_CONFIG_H*/
108 #include <vips/intl.h>
109
110 #include <stdio.h>
111 #include <stdlib.h>
112 #include <math.h>
113
114 #include <vips/vips.h>
115 #include <vips/thread.h>
116 #include <vips/transform.h>
117 #include <vips/internal.h>
118
119 #include "pmosaicing.h"
120
121 /* Return the position of the first non-zero pel from the top.
122 */
123 static int
find_top(VipsRegion * ir,int * pos,int x,int y,int h)124 find_top( VipsRegion *ir, int *pos, int x, int y, int h )
125 {
126 VipsPel *pr = VIPS_REGION_ADDR( ir, x, y );
127 VipsImage *im = ir->im;
128 int ls = VIPS_REGION_LSKIP( ir ) / VIPS_IMAGE_SIZEOF_ELEMENT( im );
129 int b = im->Bands;
130 int i, j;
131
132 /* Double the number of bands in a complex.
133 */
134 if( vips_band_format_iscomplex( im->BandFmt ) )
135 b *= 2;
136
137 /* Search for the first non-zero band element from the top edge of the image.
138 */
139 #define tsearch( TYPE ) { \
140 TYPE *p = (TYPE *) pr; \
141 \
142 for( i = 0; i < h; i++ ) { \
143 for( j = 0; j < b; j++ ) \
144 if( p[j] ) \
145 break; \
146 if( j < b ) \
147 break; \
148 \
149 p += ls; \
150 } \
151 }
152
153 switch( im->BandFmt ) {
154 case VIPS_FORMAT_UCHAR: tsearch( unsigned char ); break;
155 case VIPS_FORMAT_CHAR: tsearch( signed char ); break;
156 case VIPS_FORMAT_USHORT: tsearch( unsigned short ); break;
157 case VIPS_FORMAT_SHORT: tsearch( signed short ); break;
158 case VIPS_FORMAT_UINT: tsearch( unsigned int ); break;
159 case VIPS_FORMAT_INT: tsearch( signed int ); break;
160 case VIPS_FORMAT_FLOAT: tsearch( float ); break;
161 case VIPS_FORMAT_DOUBLE: tsearch( double ); break;
162 case VIPS_FORMAT_COMPLEX: tsearch( float ); break;
163 case VIPS_FORMAT_DPCOMPLEX: tsearch( double ); break;
164
165 default:
166 vips_error( "vips_tbmerge", "%s", _( "internal error" ) );
167 return( -1 );
168 }
169
170 *pos = y + i;
171
172 return( 0 );
173 }
174
175 /* Return the position of the first non-zero pel from the bottom.
176 */
177 static int
find_bot(VipsRegion * ir,int * pos,int x,int y,int h)178 find_bot( VipsRegion *ir, int *pos, int x, int y, int h )
179 {
180 VipsPel *pr = VIPS_REGION_ADDR( ir, x, y );
181 VipsImage *im = ir->im;
182 int ls = VIPS_REGION_LSKIP( ir ) / VIPS_IMAGE_SIZEOF_ELEMENT( ir->im );
183 int b = im->Bands;
184 int i, j;
185
186 /* Double the number of bands in a complex.
187 */
188 if( vips_band_format_iscomplex( im->BandFmt ) )
189 b *= 2;
190
191 /* Search for the first non-zero band element from the top edge of the image.
192 */
193 #define rsearch( TYPE ) { \
194 TYPE *p = (TYPE *) pr + (h - 1) * ls; \
195 \
196 for( i = h - 1; i >= 0; i-- ) { \
197 for( j = 0; j < b; j++ ) \
198 if( p[j] ) \
199 break; \
200 if( j < b ) \
201 break; \
202 \
203 p -= ls; \
204 } \
205 }
206
207 switch( im->BandFmt ) {
208 case VIPS_FORMAT_UCHAR: rsearch( unsigned char ); break;
209 case VIPS_FORMAT_CHAR: rsearch( signed char ); break;
210 case VIPS_FORMAT_USHORT: rsearch( unsigned short ); break;
211 case VIPS_FORMAT_SHORT: rsearch( signed short ); break;
212 case VIPS_FORMAT_UINT: rsearch( unsigned int ); break;
213 case VIPS_FORMAT_INT: rsearch( signed int ); break;
214 case VIPS_FORMAT_FLOAT: rsearch( float ); break;
215 case VIPS_FORMAT_DOUBLE: rsearch( double ); break;
216 case VIPS_FORMAT_COMPLEX: rsearch( float ); break;
217 case VIPS_FORMAT_DPCOMPLEX: rsearch( double ); break;
218
219 default:
220 vips_error( "vips_tbmerge", "%s", _( "internal error" ) );
221 return( -1 );
222 }
223
224 *pos = y + i;
225
226 return( 0 );
227 }
228
229 /* Make first/last for oreg.
230 */
231 static int
make_firstlast(MergeInfo * inf,Overlapping * ovlap,VipsRect * oreg)232 make_firstlast( MergeInfo *inf, Overlapping *ovlap, VipsRect *oreg )
233 {
234 VipsRegion *rir = inf->rir;
235 VipsRegion *sir = inf->sir;
236 VipsRect rr, sr;
237 int x;
238 int missing;
239
240 /* We're going to build first/last ... lock it from other generate
241 * threads. In fact it's harmless if we do get two writers, but we may
242 * avoid duplicating work.
243 */
244 g_mutex_lock( ovlap->fl_lock );
245
246 /* Do we already have first/last for this area? Bail out if we do.
247 */
248 missing = 0;
249 for( x = oreg->left; x < VIPS_RECT_RIGHT( oreg ); x++ ) {
250 const int j = x - ovlap->overlap.left;
251 const int first = ovlap->first[j];
252
253 if( first < 0 ) {
254 missing = 1;
255 break;
256 }
257 }
258 if( !missing ) {
259 /* No work to do!
260 */
261 g_mutex_unlock( ovlap->fl_lock );
262 return( 0 );
263 }
264
265 /* Entire height of overlap in ref for oreg ... we know oreg is inside
266 * overlap.
267 */
268 rr.left = oreg->left;
269 rr.top = ovlap->overlap.top;
270 rr.width = oreg->width;
271 rr.height = ovlap->overlap.height;
272 rr.left -= ovlap->rarea.left;
273 rr.top -= ovlap->rarea.top;
274
275 /* Same in sec.
276 */
277 sr.left = oreg->left;
278 sr.top = ovlap->overlap.top;
279 sr.width = oreg->width;
280 sr.height = ovlap->overlap.height;
281 sr.left -= ovlap->sarea.left;
282 sr.top -= ovlap->sarea.top;
283
284 /* Make pixels.
285 */
286 if( vips_region_prepare( rir, &rr ) ||
287 vips_region_prepare( sir, &sr ) ) {
288 g_mutex_unlock( ovlap->fl_lock );
289 return( -1 );
290 }
291
292 /* Make first/last cache.
293 */
294 for( x = 0; x < oreg->width; x++ ) {
295 const int j = (x + oreg->left) - ovlap->overlap.left;
296 int *first = &ovlap->first[j];
297 int *last = &ovlap->last[j];
298
299 /* Done this line already?
300 */
301 if( *first < 0 ) {
302 /* Search for top/bottom of overlap on this scan-line.
303 */
304 if( find_top( sir, first,
305 x + sr.left, sr.top, sr.height ) ||
306 find_bot( rir, last,
307 x + rr.left, rr.top, rr.height ) ) {
308 g_mutex_unlock( ovlap->fl_lock );
309 return( -1 );
310 }
311
312 /* Translate to output space.
313 */
314 *first += ovlap->sarea.top;
315 *last += ovlap->rarea.top;
316
317 /* Clip to maximum blend width, if necessary.
318 */
319 if( ovlap->mwidth >= 0 &&
320 *last - *first > ovlap->mwidth ) {
321 int shrinkby = (*last - *first) - ovlap->mwidth;
322
323 *first += shrinkby / 2;
324 *last -= shrinkby / 2;
325 }
326 }
327 }
328
329 g_mutex_unlock( ovlap->fl_lock );
330
331 return( 0 );
332 }
333
334 /* Test pixel == 0.
335 */
336 #define TEST_ZERO( TYPE, T, RESULT ) { \
337 TYPE *tt = (T); \
338 int ii; \
339 \
340 for( ii = 0; ii < cb; ii++ ) \
341 if( tt[i + ii] ) \
342 break; \
343 if( ii == cb ) \
344 (RESULT) = 1; \
345 }
346
347 /* Blend two integer images ... one scan-line.
348 */
349 #define iblend( TYPE, B, IN1, IN2, OUT ) { \
350 TYPE *tr = (TYPE *) (IN1); \
351 TYPE *ts = (TYPE *) (IN2); \
352 TYPE *tq = (TYPE *) (OUT); \
353 const int cb = (B); \
354 int ref_zero; \
355 int sec_zero; \
356 int x, b; \
357 int i; \
358 \
359 for( i = 0, x = 0; x < oreg->width; x++ ) { \
360 ref_zero = 0; \
361 sec_zero = 0; \
362 TEST_ZERO( TYPE, tr, ref_zero ); \
363 TEST_ZERO( TYPE, ts, sec_zero ); \
364 \
365 /* Above the bottom image? \
366 */ \
367 if( y < first[x] ) { \
368 if( !ref_zero ) \
369 for( b = 0; b < cb; b++, i++ ) \
370 tq[i] = tr[i]; \
371 else \
372 for( b = 0; b < cb; b++, i++ ) \
373 tq[i] = ts[i]; \
374 } \
375 /* To the right? \
376 */ \
377 else if( y >= last[x] ) { \
378 if( !sec_zero ) \
379 for( b = 0; b < cb; b++, i++ ) \
380 tq[i] = ts[i]; \
381 else \
382 for( b = 0; b < cb; b++, i++ ) \
383 tq[i] = tr[i]; \
384 } \
385 /* In blend area. \
386 */ \
387 else { \
388 if( !ref_zero && !sec_zero ) { \
389 const int bheight = last[x] - first[x]; \
390 const int inx = ((y - first[x]) << \
391 BLEND_SHIFT) / bheight; \
392 int c1 = vips__icoef1[inx]; \
393 int c2 = vips__icoef2[inx]; \
394 \
395 for( b = 0; b < cb; b++, i++ ) \
396 tq[i] = c1 * tr[i] / BLEND_SCALE + \
397 c2 * ts[i] / BLEND_SCALE; \
398 } \
399 else if( !ref_zero ) \
400 for( b = 0; b < cb; b++, i++ ) \
401 tq[i] = tr[i]; \
402 else \
403 for( b = 0; b < cb; b++, i++ ) \
404 tq[i] = ts[i]; \
405 } \
406 } \
407 }
408
409 /* Blend two float images.
410 */
411 #define fblend( TYPE, B, IN1, IN2, OUT ) { \
412 TYPE *tr = (TYPE *) (IN1); \
413 TYPE *ts = (TYPE *) (IN2); \
414 TYPE *tq = (TYPE *) (OUT); \
415 int ref_zero; \
416 int sec_zero; \
417 const int cb = (B); \
418 int x, b; \
419 int i; \
420 \
421 for( i = 0, x = 0; x < oreg->width; x++ ) { \
422 ref_zero = 0; \
423 sec_zero = 0; \
424 TEST_ZERO( TYPE, tr, ref_zero ); \
425 TEST_ZERO( TYPE, ts, sec_zero ); \
426 \
427 /* Above the bottom image? \
428 */ \
429 if( y < first[x] ) \
430 if( !ref_zero ) \
431 for( b = 0; b < cb; b++, i++ ) \
432 tq[i] = tr[i]; \
433 else \
434 for( b = 0; b < cb; b++, i++ ) \
435 tq[i] = tr[i]; \
436 /* To the right? \
437 */ \
438 else if( y >= last[x] ) \
439 if( !sec_zero ) \
440 for( b = 0; b < cb; b++, i++ ) \
441 tq[i] = ts[i]; \
442 else \
443 for( b = 0; b < cb; b++, i++ ) \
444 tq[i] = tr[i]; \
445 /* In blend area. \
446 */ \
447 else { \
448 if( !ref_zero && !sec_zero ) { \
449 const int bheight = last[x] - first[x]; \
450 const int inx = ((y - first[x]) << \
451 BLEND_SHIFT) / bheight; \
452 double c1 = vips__coef1[inx]; \
453 double c2 = vips__coef2[inx]; \
454 \
455 for( b = 0; b < cb; b++, i++ ) \
456 tq[i] = c1 * tr[i] + c2 * ts[i]; \
457 } \
458 else if( !ref_zero ) \
459 for( b = 0; b < cb; b++, i++ ) \
460 tq[i] = tr[i]; \
461 else \
462 for( b = 0; b < cb; b++, i++ ) \
463 tq[i] = ts[i]; \
464 } \
465 } \
466 }
467
468 /* Top-bottom blend function for non-labpack images.
469 */
470 static int
tb_blend(VipsRegion * or,MergeInfo * inf,Overlapping * ovlap,VipsRect * oreg)471 tb_blend( VipsRegion *or, MergeInfo *inf, Overlapping *ovlap, VipsRect *oreg )
472 {
473 VipsRegion *rir = inf->rir;
474 VipsRegion *sir = inf->sir;
475 VipsImage *im = or->im;
476
477 VipsRect prr, psr;
478 int y, yr, ys;
479
480 /* Make sure we have a complete first/last set for this area.
481 */
482 if( make_firstlast( inf, ovlap, oreg ) )
483 return( -1 );
484
485 /* Part of rr which we will output.
486 */
487 prr = *oreg;
488 prr.left -= ovlap->rarea.left;
489 prr.top -= ovlap->rarea.top;
490
491 /* Part of sr which we will output.
492 */
493 psr = *oreg;
494 psr.left -= ovlap->sarea.left;
495 psr.top -= ovlap->sarea.top;
496
497 /* Make pixels.
498 */
499 if( vips_region_prepare( rir, &prr ) ||
500 vips_region_prepare( sir, &psr ) )
501 return( -1 );
502
503 /* Loop down overlap area.
504 */
505 for( y = oreg->top, yr = prr.top, ys = psr.top;
506 y < VIPS_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) {
507 VipsPel *pr = VIPS_REGION_ADDR( rir, prr.left, yr );
508 VipsPel *ps = VIPS_REGION_ADDR( sir, psr.left, ys );
509 VipsPel *q = VIPS_REGION_ADDR( or, oreg->left, y );
510
511 const int j = oreg->left - ovlap->overlap.left;
512 const int *first = ovlap->first + j;
513 const int *last = ovlap->last + j;
514
515 switch( im->BandFmt ) {
516 case VIPS_FORMAT_UCHAR:
517 iblend( unsigned char, im->Bands, pr, ps, q ); break;
518 case VIPS_FORMAT_CHAR:
519 iblend( signed char, im->Bands, pr, ps, q ); break;
520 case VIPS_FORMAT_USHORT:
521 iblend( unsigned short, im->Bands, pr, ps, q ); break;
522 case VIPS_FORMAT_SHORT:
523 iblend( signed short, im->Bands, pr, ps, q ); break;
524 case VIPS_FORMAT_UINT:
525 iblend( unsigned int, im->Bands, pr, ps, q ); break;
526 case VIPS_FORMAT_INT:
527 iblend( signed int, im->Bands, pr, ps, q ); break;
528 case VIPS_FORMAT_FLOAT:
529 fblend( float, im->Bands, pr, ps, q ); break;
530 case VIPS_FORMAT_DOUBLE:
531 fblend( double, im->Bands, pr, ps, q ); break;
532 case VIPS_FORMAT_COMPLEX:
533 fblend( float, im->Bands * 2, pr, ps, q ); break;
534 case VIPS_FORMAT_DPCOMPLEX:
535 fblend( double, im->Bands * 2, pr, ps, q ); break;
536
537 default:
538 vips_error( "vips_tbmerge", "%s", _( "internal error" ) );
539 return( -1 );
540 }
541 }
542
543 return( 0 );
544 }
545
546 /* Top-bottom blend function for VIPS_CODING_LABQ images.
547 */
548 static int
tb_blend_labpack(VipsRegion * or,MergeInfo * inf,Overlapping * ovlap,VipsRect * oreg)549 tb_blend_labpack( VipsRegion *or, MergeInfo *inf, Overlapping *ovlap, VipsRect *oreg )
550 {
551 VipsRegion *rir = inf->rir;
552 VipsRegion *sir = inf->sir;
553 VipsRect prr, psr;
554 int y, yr, ys;
555
556 /* Make sure we have a complete first/last set for this area. This
557 * will just look at the top 8 bits of L, not all 10, but should be OK.
558 */
559 if( make_firstlast( inf, ovlap, oreg ) )
560 return( -1 );
561
562 /* Part of rr which we will output.
563 */
564 prr = *oreg;
565 prr.left -= ovlap->rarea.left;
566 prr.top -= ovlap->rarea.top;
567
568 /* Part of sr which we will output.
569 */
570 psr = *oreg;
571 psr.left -= ovlap->sarea.left;
572 psr.top -= ovlap->sarea.top;
573
574 /* Make pixels.
575 */
576 if( vips_region_prepare( rir, &prr ) ||
577 vips_region_prepare( sir, &psr ) )
578 return( -1 );
579
580 /* Loop down overlap area.
581 */
582 for( y = oreg->top, yr = prr.top, ys = psr.top;
583 y < VIPS_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) {
584 VipsPel *pr = VIPS_REGION_ADDR( rir, prr.left, yr );
585 VipsPel *ps = VIPS_REGION_ADDR( sir, psr.left, ys );
586 VipsPel *q = VIPS_REGION_ADDR( or, oreg->left, y );
587
588 const int j = oreg->left - ovlap->overlap.left;
589 const int *first = ovlap->first + j;
590 const int *last = ovlap->last + j;
591
592 float *fq = inf->merge;
593 float *r = inf->from1;
594 float *s = inf->from2;
595
596 /* Unpack two bits we want.
597 */
598 vips__LabQ2Lab_vec( r, pr, oreg->width );
599 vips__LabQ2Lab_vec( s, ps, oreg->width );
600
601 /* Blend as floats.
602 */
603 fblend( float, 3, r, s, fq );
604
605 /* Re-pack to output buffer.
606 */
607 vips__Lab2LabQ_vec( q, inf->merge, oreg->width );
608 }
609
610 return( 0 );
611 }
612
613 /* Build per-call state.
614 */
615 static Overlapping *
build_tbstate(VipsImage * ref,VipsImage * sec,VipsImage * out,int dx,int dy,int mwidth)616 build_tbstate( VipsImage *ref, VipsImage *sec, VipsImage *out, int dx, int dy, int mwidth )
617 {
618 Overlapping *ovlap;
619
620 if( !(ovlap = vips__build_mergestate( "vips_tbmerge",
621 ref, sec, out, dx, dy, mwidth )) )
622 return( NULL );
623
624 /* Select blender.
625 */
626 switch( ovlap->ref->Coding ) {
627 case VIPS_CODING_LABQ:
628 ovlap->blend = tb_blend_labpack;
629 break;
630
631 case VIPS_CODING_NONE:
632 ovlap->blend = tb_blend;
633 break;
634
635 default:
636 vips_error( "vips_tbmerge", "%s", _( "unknown coding type" ) );
637 return( NULL );
638 }
639
640 /* Find the parts of output which come just from ref and just from sec.
641 */
642 ovlap->rpart = ovlap->rarea;
643 ovlap->spart = ovlap->sarea;
644 ovlap->rpart.height -= ovlap->overlap.height;
645 ovlap->spart.top += ovlap->overlap.height;
646 ovlap->spart.height -= ovlap->overlap.height;
647
648 /* Is there too much overlap? ie. bottom edge of ref image is greater
649 * than bottom edge of sec image, or top edge of ref > top edge of
650 * sec.
651 */
652 if( VIPS_RECT_BOTTOM( &ovlap->rarea ) > VIPS_RECT_BOTTOM( &ovlap->sarea ) ||
653 ovlap->rarea.top > ovlap->sarea.top ) {
654 vips_error( "vips_tbmerge", "%s", _( "too much overlap" ) );
655 return( NULL );
656 }
657
658 /* Max number of pixels we may have to blend together.
659 */
660 ovlap->blsize = ovlap->overlap.width;
661
662 return( ovlap );
663 }
664
665 int
vips__tbmerge(VipsImage * ref,VipsImage * sec,VipsImage * out,int dx,int dy,int mwidth)666 vips__tbmerge( VipsImage *ref, VipsImage *sec, VipsImage *out,
667 int dx, int dy, int mwidth )
668 {
669 Overlapping *ovlap;
670
671 if( dy > 0 || dy < 1 - ref->Ysize ) {
672 VipsImage *x;
673
674 #ifdef DEBUG
675 printf( "vips__tbmerge: no overlap, using insert\n" );
676 #endif
677
678 /* No overlap, use insert instead.
679 */
680 if( vips_insert( ref, sec, &x, -dx, -dy,
681 "expand", TRUE,
682 NULL ) )
683 return( -1 );
684 if( vips_image_write( x, out ) ) {
685 g_object_unref( x );
686 return( -1 );
687 }
688 g_object_unref( x );
689
690 out->Xoffset = -dx;
691 out->Yoffset = -dy;
692
693 return( 0 );
694 }
695
696 if( !(ovlap = build_tbstate( ref, sec, out, dx, dy, mwidth )) )
697 return( -1 );
698
699 if( vips_image_pipelinev( out,
700 VIPS_DEMAND_STYLE_THINSTRIP, ovlap->ref, ovlap->sec, NULL ) )
701 return( -1 );
702
703 out->Xsize = ovlap->oarea.width;
704 out->Ysize = ovlap->oarea.height;
705 out->Xoffset = -dx;
706 out->Yoffset = -dy;
707
708 if( vips_image_generate( out,
709 vips__start_merge, vips__merge_gen, vips__stop_merge, ovlap, NULL ) )
710 return( -1 );
711
712 return ( 0 );
713 }
714
715