diff --git a/libvips/inplace/flood.c b/libvips/inplace/flood.c index d068b9ba..8bdee4ef 100644 --- a/libvips/inplace/flood.c +++ b/libvips/inplace/flood.c @@ -187,27 +187,12 @@ buffer_add( Buffer *buf, Flood *flood, int x1, int x2, int y, int dir ) /* Is p "connected"? ie. is equal to or not equal to flood->edge, depending on * whether we are flooding to the edge boundary or flooding edge-coloured * pixels. - * - * If test and mark are different images, we also need to check ink to make - * sure we haven't painted this pixel before. Otherwise we can get stuck in - * loops. */ static inline gboolean -flood_connected( Flood *flood, PEL *tp, PEL *mp ) +flood_connected( Flood *flood, PEL *tp ) { int j; - if( flood->test != flood->mark ) { - for( j = 0; j < flood->msize; j++ ) - if( mp[j] != flood->ink[j] ) - break; - - if( j == flood->msize ) - /* We've painted this before, can't be connected now. - */ - return( FALSE ); - } - for( j = 0; j < flood->tsize; j++ ) if( tp[j] != flood->edge[j] ) break; @@ -217,6 +202,20 @@ flood_connected( Flood *flood, PEL *tp, PEL *mp ) return( flood->equal ^ (j < flood->tsize) ); } +/* Is p painted? + */ +static inline gboolean +flood_painted( Flood *flood, PEL *mp ) +{ + int j; + + for( j = 0; j < flood->msize; j++ ) + if( mp[j] != flood->ink[j] ) + break; + + return( j == flood->msize ); +} + /* Faster than memcpy for n < about 20. */ static inline void @@ -229,7 +228,7 @@ flood_paint( Flood *flood, PEL *q ) } /* Fill left and right, return the endpoints. The start point (x, y) must be - * connected. + * connected and unpainted. */ static void flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 ) @@ -239,33 +238,43 @@ flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 ) PEL *tp; PEL *mp; int i; + int j; g_assert( flood_connected( flood, - (PEL *) IM_IMAGE_ADDR( flood->test, x, y ), + (PEL *) IM_IMAGE_ADDR( flood->test, x, y ) ) ); + g_assert( !flood_painted( flood, (PEL *) IM_IMAGE_ADDR( flood->mark, x, y ) ) ); - /* Fill this pixel and to the right. + /* Search to the right for the first non-connected pixel. If the start + * pixel is unpainted, we know all the intervening pixels must be + * unpainted too. */ tp = (PEL *) IM_IMAGE_ADDR( flood->test, x, y ); - mp = (PEL *) IM_IMAGE_ADDR( flood->mark, x, y ); - for( i = x; i < width && flood_connected( flood, tp, mp ); i++ ) { - flood_paint( flood, mp ); + for( i = x; i < width; i++ ) { + if( !flood_connected( flood, tp ) ) + break; tp += flood->tsize; - mp += flood->msize; } *x2 = i - 1; - /* Fill to the left. + /* Search left. */ tp = (PEL *) IM_IMAGE_ADDR( flood->test, x - 1, y ); - mp = (PEL *) IM_IMAGE_ADDR( flood->mark, x - 1, y ); - for( i = x - 1; i > 0 && flood_connected( flood, tp, mp ); i-- ) { - flood_paint( flood, mp ); + for( i = x - 1; i > 0; i-- ) { + if( !flood_connected( flood, tp ) ) + break; tp -= flood->tsize; - mp -= flood->msize; } *x1 = i + 1; + /* Paint the range we discovered. + */ + mp = (PEL *) IM_IMAGE_ADDR( flood->mark, x, y ); + for( i = *x1; i <= *x2; i++ ) { + flood_paint( flood, mp ); + mp += flood->msize; + } + if( flood->dout ) { flood->left = IM_MIN( flood->left, *x1 ); flood->right = IM_MAX( flood->right, *x2 ); @@ -285,18 +294,29 @@ flood_scanline( Flood *flood, int x, int y, int *x1, int *x2 ) static void flood_around( Flood *flood, Scan *scan ) { + PEL *tp; int x; g_assert( scan->dir == 1 || scan->dir == -1 ); + tp = (PEL *) IM_IMAGE_ADDR( flood->test, scan->x1, scan->y ); for( x = scan->x1; x <= scan->x2; x++ ) { - PEL *tp = (PEL *) IM_IMAGE_ADDR( flood->test, x, scan->y ); - PEL *mp = (PEL *) IM_IMAGE_ADDR( flood->mark, x, scan->y ); - - if( flood_connected( flood, tp, mp ) ) { + if( flood_connected( flood, tp ) ) { int x1a; int x2a; + /* If mark and test are different images, we also need + * to check for painted. Otherwise we can get stuck in + * connected loops. + */ + if( flood->mark != flood->test ) { + PEL *mp = (PEL *) IM_IMAGE_ADDR( + flood->mark, x, scan->y ); + + if( flood_painted( flood, mp ) ) + continue; + } + flood_scanline( flood, x, scan->y, &x1a, &x2a ); /* Our new scanline can have up to three more @@ -316,7 +336,10 @@ flood_around( Flood *flood, Scan *scan ) scan->dir ); x = x2a; + tp = (PEL *) IM_IMAGE_ADDR( flood->test, x, scan->y ); } + + tp += flood->tsize; } } @@ -341,8 +364,7 @@ flood_all( Flood *flood, int x, int y ) /* Test start pixel ... nothing to do? */ if( flood_connected( flood, - (PEL *) IM_IMAGE_ADDR( flood->test, x, y ), - (PEL *) IM_IMAGE_ADDR( flood->mark, x, y ) ) ) { + (PEL *) IM_IMAGE_ADDR( flood->test, x, y ) ) ) { int x1, x2; flood_scanline( flood, x, y, &x1, &x2 );