diff --git a/ChangeLog b/ChangeLog index 37371551..1783f3be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,7 +2,8 @@ - version bump for new dev cycle - im_subtract(), im_avg(), im_min(), im_minpos(), im_copy(), im_embed(), im_flophor(), im_flipver(), im_insert(), im_insert_noexpand(), im_lrjoin(), - im_tbjoin(), im_extract_area(), im_extract_bands(), im_extract_areabands() + im_tbjoin(), im_extract_area(), im_extract_bands(), im_extract_areabands(), + im_replicate() redone as classes - added VIPS_ARGUMENT_APPEND to help control arg ordering - generate has a 'stop' param to signal successful early termination diff --git a/TODO b/TODO index 8f02af61..bc9cf26e 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,12 @@ + +- do clip etc. in new style, good to get everything that VipsAdd uses in + the new stack + + insert* needs clip, lintra_vec + + + + - bandalike: consider RGB + RGBA ... we should bandup by adding a black band (or white?? unclear) @@ -76,15 +85,6 @@ -- do clip etc. in new style, good to get everything that VipsAdd uses in - the new stack - - embed needs flip* *join extract_area replicate insert - - *join needs insert too - - insert* needs clip, lintra_vec - diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am index 1215727b..5297a3d8 100644 --- a/libvips/conversion/Makefile.am +++ b/libvips/conversion/Makefile.am @@ -9,6 +9,7 @@ libconversion_la_SOURCES = \ insert.c \ join.c \ extract.c \ + replicate.c \ conver_dispatch.c \ im_black.c \ im_c2amph.c \ @@ -21,7 +22,6 @@ libconversion_la_SOURCES = \ im_gbandjoin.c \ im_mask2vips.c \ im_msb.c \ - im_replicate.c \ im_grid.c \ im_ri2c.c \ im_rot180.c \ diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index 78b887e5..70bfb032 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -109,6 +109,7 @@ vips_conversion_operation_init( void ) extern GType vips_join_get_type( void ); extern GType vips_extract_area_get_type( void ); extern GType vips_extract_band_get_type( void ); + extern GType vips_replicate_get_type( void ); vips_copy_get_type(); vips_embed_get_type(); @@ -117,5 +118,6 @@ vips_conversion_operation_init( void ) vips_join_get_type(); vips_extract_area_get_type(); vips_extract_band_get_type(); + vips_replicate_get_type(); } diff --git a/libvips/conversion/embed.c b/libvips/conversion/embed.c index fb42dd86..3736728b 100644 --- a/libvips/conversion/embed.c +++ b/libvips/conversion/embed.c @@ -81,11 +81,11 @@ * @x: place @in at this x position in @out * @y: place @in at this y position in @out * - * The opposite of im_extract(): embed @in within an image of size @width by - * @height at position @x, @y. @extend + * The opposite of #VipsExtractArea: embed @in within an image of size @width + * by @height at position @x, @y. @extend * controls what appears in the new pels, see #VipsExtend. * - * See also: im_extract_area(), im_insert(). + * See also: #VipsExtractArea, #VipsInsert. * * Returns: 0 on success, -1 on error. */ @@ -325,6 +325,76 @@ vips_embed_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) return( 0 ); } +static int +vips_embed_repeat( VipsPool *pool, VipsImage *input, VipsImage **output, + int x, int y, int width, int height ) +{ + VipsPoolContext *context = vips_pool_context_new( pool ); + + /* Clock arithmetic: we want negative x/y to wrap around + * nicely. + */ + const int nx = x < 0 ? + -x % input->Xsize : input->Xsize - x % input->Xsize; + const int ny = y < 0 ? + -y % input->Ysize : input->Ysize - y % input->Ysize; + + if( + vips_replicate( input, &VIPS_VI( 1 ), + width / input->Xsize + 2, + height / input->Ysize + 2, NULL ) || + vips_extract_area( VIPS_VI( 1 ), output, + nx, ny, width, height, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_embed_mirror( VipsPool *pool, VipsImage *input, VipsImage **output, + int x, int y, int width, int height ) +{ + VipsPoolContext *context = vips_pool_context_new( pool ); + + /* As repeat, but the tiles are twice the size because of + * mirroring. + */ + const int w2 = input->Xsize * 2; + const int h2 = input->Ysize * 2; + + const int nx = x < 0 ? -x % w2 : w2 - x % w2; + const int ny = y < 0 ? -y % h2 : h2 - y % h2; + + if( + /* Make a 2x2 mirror tile. + */ + vips_flip( input, &VIPS_VI( 1 ), + VIPS_DIRECTION_HORIZONTAL, NULL ) || + vips_join( input, VIPS_VI( 1 ), &VIPS_VI( 2 ), + VIPS_DIRECTION_HORIZONTAL, NULL ) || + vips_flip( VIPS_VI( 2 ), &VIPS_VI( 3 ), + VIPS_DIRECTION_VERTICAL, NULL ) || + vips_join( VIPS_VI( 2 ), VIPS_VI( 3 ), &VIPS_VI( 4 ), + VIPS_DIRECTION_VERTICAL, NULL ) || + + /* Repeat, then cut out the centre. + */ + vips_replicate( VIPS_VI( 4 ), &VIPS_VI( 5 ), + width / VIPS_VI( 4 )->Xsize + 2, + height / VIPS_VI( 4 )->Ysize + 2, NULL ) || + vips_extract_area( VIPS_VI( 5 ), &VIPS_VI( 6 ), + nx, ny, width, height, NULL ) || + + /* Overwrite the centre with the input, much faster + * for centre pixels. + */ + vips_insert( VIPS_VI( 6 ), input, output, + x, y, NULL ) ) + return( -1 ); + + return( 0 ); +} + static int vips_embed_build( VipsObject *object ) { @@ -332,6 +402,7 @@ vips_embed_build( VipsObject *object ) VipsEmbed *embed = (VipsEmbed *) object; VipsRect want; + VipsPool *pool; if( VIPS_OBJECT_CLASS( vips_embed_parent_class )->build( object ) ) return( -1 ); @@ -352,87 +423,30 @@ vips_embed_build( VipsObject *object ) embed->height == embed->input->Ysize ) return( vips_image_write( embed->input, conversion->output ) ); + pool = vips_pool_new( "VipsEmbed" ); + vips_object_local( object, pool ); + switch( embed->extend ) { case VIPS_EXTEND_REPEAT: { - /* Clock arithmetic: we want negative x/y to wrap around - * nicely. - */ - const int nx = embed->x < 0 ? - -embed->x % embed->input->Xsize : - embed->input->Xsize - embed->x % embed->input->Xsize; - const int ny = embed->y < 0 ? - -embed->y % embed->input->Ysize : - embed->input->Ysize - embed->y % embed->input->Ysize; + VipsPoolContext *context = vips_pool_context_new( pool ); - VipsImage *t[1]; - - if( im_open_local_array( conversion->output, - t, 1, "embed-type2", "p" ) || - im_replicate( embed->input, t[0], - embed->width / embed->input->Xsize + 2, - embed->height / embed->input->Ysize + 2 ) || - im_extract_area( t[0], conversion->output, - nx, ny, embed->width, embed->height ) ) + if( vips_embed_repeat( pool, embed->input, &VIPS_VI( 1 ), + embed->x, embed->y, embed->width, embed->height ) || + vips_image_write( VIPS_VI( 1 ), conversion->output ) ) return( -1 ); } + break; case VIPS_EXTEND_MIRROR: { - /* As case 2, but the tiles are twice the size because of - * mirroring. - */ - const int w2 = embed->input->Xsize * 2; - const int h2 = embed->input->Ysize * 2; + VipsPoolContext *context = vips_pool_context_new( pool ); - const int nx = embed->x < 0 ? - -embed->x % w2 : w2 - embed->x % w2; - const int ny = embed->y < 0 ? - -embed->y % h2 : h2 - embed->y % h2; - - VipsImage *t[7]; - - if( im_open_local_array( conversion->output, - t, 7, "embed-type3", "p" ) || - /* Cache the edges of in, since we may well be reusing - * them repeatedly. Will only help for tiny borders - * (up to 20 pixels?), but that's our typical case - * with im_conv() etc. - im_cache( in, t[0], IM__TILE_WIDTH, IM__TILE_HEIGHT, - 3 * (in->Xsize / IM__TILE_WIDTH + 1) + - 3 * (in->Ysize / IM__TILE_HEIGHT + 1) ) || - */ - - /* - - FIXME ... alternatively, don't cache, hmm, - need to time this for typical cases - - */ - im_copy( embed->input, t[0] ) || - - /* Make a 2x2 mirror tile. - */ - im_fliphor( t[0], t[1] ) || - im_lrjoin( t[0], t[1], t[2] ) || - im_flipver( t[2], t[3] ) || - im_tbjoin( t[2], t[3], t[4] ) || - - /* Repeat, then cut out the centre. - */ - im_replicate( t[4], t[5], - embed->width / t[4]->Xsize + 2, - embed->height / t[4]->Ysize + 2 ) || - im_extract_area( t[5], t[6], nx, ny, - embed->width, embed->height ) || - - /* Overwrite the centre with the input, much faster - * for centre pixels. - */ - im_insert_noexpand( t[6], embed->input, - conversion->output, embed->x, embed->y ) ) - return( -1 ); + if( vips_embed_mirror( pool, embed->input, &VIPS_VI( 1 ), + embed->x, embed->y, embed->width, embed->height ) || + vips_image_write( VIPS_VI( 1 ), conversion->output ) ) + return( -1 ); } break; @@ -461,7 +475,7 @@ vips_embed_build( VipsObject *object ) want.top = embed->y; want.width = embed->input->Xsize; want.height = embed->input->Ysize; - im_rect_intersectrect( &want, &embed->rout, &embed->rsub ); + vips_rect_intersectrect( &want, &embed->rout, &embed->rsub ); /* FIXME ... actually, it can't. embed_find_edge() will fail * if rsub is empty. Make this more general at some point diff --git a/libvips/conversion/flip.c b/libvips/conversion/flip.c index 40f771da..a11b5e1c 100644 --- a/libvips/conversion/flip.c +++ b/libvips/conversion/flip.c @@ -66,7 +66,7 @@ /** * VipsFlip: - * @in: input image + * @input: input image * @output: output image * @direction: flip horizontally or vertically * @@ -250,7 +250,7 @@ vips_flip_class_init( VipsFlipClass *class ) vobject_class->description = _( "flip an image" ); vobject_class->build = vips_flip_build; - VIPS_ARG_IMAGE( class, "in", 1, + VIPS_ARG_IMAGE( class, "input", 1, _( "Input" ), _( "Input image" ), VIPS_ARGUMENT_REQUIRED_INPUT, diff --git a/libvips/conversion/im_replicate.c b/libvips/conversion/im_replicate.c deleted file mode 100644 index a247e2ee..00000000 --- a/libvips/conversion/im_replicate.c +++ /dev/null @@ -1,167 +0,0 @@ -/* replicate an image x times horizontally and vertically - * - * JC, 30 sep 03 - * - * 15/4/04 - * - some optimisations for some cases - * 1/2/10 - * - gtkdoc - */ - -/* - - This file is part of VIPS. - - VIPS is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -/* Turn on debugging output. -#define DEBUG -#define DEBUG_PAINT -#define DEBUG_MAKE - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include - -#include - -static int -replicate_gen( REGION *or, void *seq, void *a, void *b ) -{ - REGION *ir = (REGION *) seq; - IMAGE *in = (IMAGE *) a; - Rect *r = &or->valid; - int twidth = in->Xsize; - int theight = in->Ysize; - int x, y; - Rect tile; - - /* Find top left of tiles we need. - */ - int xs = (r->left / twidth) * twidth; - int ys = (r->top / theight) * theight; - - /* The tile enclosing the top-left corner of the requested area. - */ - tile.left = xs; - tile.top = ys; - tile.width = twidth; - tile.height = theight; - - /* If the request fits inside a single tile, we can just pointer-copy. - */ - if( im_rect_includesrect( &tile, r ) ) { - Rect irect; - - /* Translate request to input space. - */ - irect = *r; - irect.left -= xs; - irect.top -= ys; - if( im_prepare( ir, &irect ) ) - return( -1 ); - - if( im_region_region( or, ir, r, irect.left, irect.top ) ) - return( -1 ); - - return( 0 ); - } - - for( y = ys; y < IM_RECT_BOTTOM( r ); y += theight ) - for( x = xs; x < IM_RECT_RIGHT( r ); x += twidth ) { - Rect paint; - - /* Whole tile at x, y - */ - tile.left = x; - tile.top = y; - tile.width = twidth; - tile.height = theight; - - /* Which parts touch the area of the output we are - * building. - */ - im_rect_intersectrect( &tile, r, &paint ); - - /* Translate back to ir coordinates. - */ - paint.left -= x; - paint.top -= y; - - g_assert( !im_rect_isempty( &paint ) ); - - /* Render into or. - */ - if( im_prepare_to( ir, or, &paint, - paint.left + x, - paint.top + y ) ) - return( -1 ); - } - - return( 0 ); -} - -/** - * im_replicate: - * @in: input image - * @out: output image - * @across: repeat @in this many times across - * @down: repeat @in this many times down - * - * Replicate an image x times horizontally and vertically. - * - * See also: im_embed(), im_copy(). - * - * Returns: 0 on success, -1 on error. - */ -int -im_replicate( IMAGE *in, IMAGE *out, int across, int down ) -{ - if( across <= 0 || down <= 0 ) { - im_error( "im_replicate", "%s", _( "bad parameters" ) ); - return( -1 ); - } - if( im_piocheck( in, out ) || - im_cp_desc( out, in ) ) - return( -1 ); - out->Xsize *= across; - out->Ysize *= down; - - /* We can render small tiles with pointer copies. - */ - if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) - return( -1 ); - - if( im_generate( out, - im_start_one, replicate_gen, im_stop_one, in, NULL ) ) - return( -1 ); - - return( 0 ); -} - diff --git a/libvips/conversion/replicate.c b/libvips/conversion/replicate.c new file mode 100644 index 00000000..e6b086ab --- /dev/null +++ b/libvips/conversion/replicate.c @@ -0,0 +1,246 @@ +/* replicate an image x times horizontally and vertically + * + * JC, 30 sep 03 + * + * 15/4/04 + * - some optimisations for some cases + * 1/2/10 + * - gtkdoc + * 26/10/11 + * - redone as a class + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include "conversion.h" + +/** + * VipsReplicate: + * @in: input image + * @output: output image + * @across: repeat input this many times across + * @down: repeat input this many times down + * + * Repeats an image many times. + * + * See also: #VipsExtract. + */ + +typedef struct _VipsReplicate { + VipsConversion parent_instance; + + /* The input image. + */ + VipsImage *input; + + int across; + int down; + +} VipsReplicate; + +typedef VipsConversionClass VipsReplicateClass; + +G_DEFINE_TYPE( VipsReplicate, vips_replicate, VIPS_TYPE_CONVERSION ); + +static int +vips_replicate_gen( VipsRegion *or, void *seq, void *a, void *b, + gboolean *stop ) +{ + VipsRegion *ir = (VipsRegion *) seq; + VipsImage *in = (VipsImage *) a; + VipsRect *r = &or->valid; + int twidth = in->Xsize; + int theight = in->Ysize; + + int x, y; + VipsRect tile; + + /* Find top left of tiles we need. + */ + int xs = (r->left / twidth) * twidth; + int ys = (r->top / theight) * theight; + + /* The tile enclosing the top-left corner of the requested area. + */ + tile.left = xs; + tile.top = ys; + tile.width = twidth; + tile.height = theight; + + /* If the request fits inside a single tile, we can just pointer-copy. + */ + if( vips_rect_includesrect( &tile, r ) ) { + VipsRect irect; + + /* Translate request to input space. + */ + irect = *r; + irect.left -= xs; + irect.top -= ys; + if( vips_region_prepare( ir, &irect ) ) + return( -1 ); + + if( vips_region_region( or, ir, r, irect.left, irect.top ) ) + return( -1 ); + + return( 0 ); + } + + for( y = ys; y < VIPS_RECT_BOTTOM( r ); y += theight ) + for( x = xs; x < VIPS_RECT_RIGHT( r ); x += twidth ) { + VipsRect paint; + + /* Whole tile at x, y + */ + tile.left = x; + tile.top = y; + tile.width = twidth; + tile.height = theight; + + /* Which parts touch the area of the output we are + * building. + */ + vips_rect_intersectrect( &tile, r, &paint ); + + /* Translate back to ir coordinates. + */ + paint.left -= x; + paint.top -= y; + + g_assert( !im_rect_isempty( &paint ) ); + + /* Render into or. + */ + if( vips_region_prepare_to( ir, or, &paint, + paint.left + x, + paint.top + y ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +vips_replicate_build( VipsObject *object ) +{ + VipsConversion *conversion = VIPS_CONVERSION( object ); + VipsReplicate *replicate = (VipsReplicate *) object; + + if( VIPS_OBJECT_CLASS( vips_replicate_parent_class )->build( object ) ) + return( -1 ); + + if( vips_image_pio_input( replicate->input ) || + vips_image_pio_output( conversion->output ) ) + return( -1 ); + + if( vips_image_copy_fields( conversion->output, replicate->input ) ) + return( -1 ); + vips_demand_hint( conversion->output, + VIPS_DEMAND_STYLE_SMALLTILE, replicate->input, NULL ); + + conversion->output->Xsize *= replicate->across; + conversion->output->Ysize *= replicate->down; + + if( vips_image_generate( conversion->output, + vips_start_one, vips_replicate_gen, vips_stop_one, + replicate->input, replicate ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_replicate_class_init( VipsReplicateClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + VIPS_DEBUG_MSG( "vips_replicate_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "replicate"; + vobject_class->description = _( "replicate an image" ); + vobject_class->build = vips_replicate_build; + + VIPS_ARG_IMAGE( class, "input", 0, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReplicate, input ) ); + + VIPS_ARG_INT( class, "across", 4, + _( "Across" ), + _( "Repeat this many times horizontally" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReplicate, across ), + 1, 1000000, 1 ); + + VIPS_ARG_INT( class, "down", 5, + _( "Down" ), + _( "Repeat this many times vertically" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReplicate, down ), + 1, 1000000, 1 ); + +} + +static void +vips_replicate_init( VipsReplicate *replicate ) +{ +} + +int +vips_replicate( VipsImage *in, VipsImage **out, int across, int down, ... ) +{ + va_list ap; + int result; + + va_start( ap, down ); + result = vips_call_split( "replicate", ap, in, out, across, down ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 512f643b..f573dc29 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -1249,3 +1249,20 @@ im_extract_areabands( IMAGE *in, IMAGE *out, return( 0 ); } +int +im_replicate( IMAGE *in, IMAGE *out, int across, int down ) +{ + VipsImage *t; + + if( vips_replicate( in, &t, across, down, + NULL ) ) + return( -1 ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); + return( -1 ); + } + g_object_unref( t ); + + return( 0 ); +} + diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index 0e50ec11..190755fd 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -132,6 +132,8 @@ int vips_extract_area( VipsImage *input, VipsImage **output, __attribute__((sentinel)); int vips_extract_band( VipsImage *input, VipsImage **output, int band, ... ) __attribute__((sentinel)); +int vips_replicate( VipsImage *in, VipsImage **out, int across, int down, ... ) + __attribute__((sentinel)); @@ -163,7 +165,6 @@ int im_text( VipsImage *out, const char *text, const char *font, int im_bandjoin( VipsImage *in1, VipsImage *in2, VipsImage *out ); int im_gbandjoin( VipsImage **in, VipsImage *out, int n ); int im_insertset( VipsImage *main, VipsImage *sub, VipsImage *out, int n, int *x, int *y ); -int im_replicate( VipsImage *in, VipsImage *out, int across, int down ); int im_grid( VipsImage *in, VipsImage *out, int tile_height, int across, int down ); int im_wrap( VipsImage *in, VipsImage *out, int x, int y ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 958030da..b3cb31d8 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -556,6 +556,8 @@ int im_extract_bands( VipsImage *in, VipsImage *out, int band, int nbands ); int im_extract_areabands( VipsImage *in, VipsImage *out, int left, int top, int width, int height, int band, int nbands ); +int im_replicate( VipsImage *in, VipsImage *out, int across, int down ); + /* ruby-vips uses this */ #define vips_class_map_concrete_all vips_class_map_all