rewrite im_replicate() as a class

This commit is contained in:
John Cupitt 2011-10-26 16:51:27 +01:00
parent 2bfe021088
commit 014763a845
11 changed files with 371 additions and 255 deletions

View File

@ -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

18
TODO
View File

@ -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

View File

@ -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 \

View File

@ -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();
}

View File

@ -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

View File

@ -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,

View File

@ -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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vips/vips.h>
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 );
}

View File

@ -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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vips/vips.h>
#include <vips/internal.h>
#include <vips/debug.h>
#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 );
}

View File

@ -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 );
}

View File

@ -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 );

View File

@ -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