im_zoom() -> class
This commit is contained in:
parent
236b8b99ad
commit
0bbc1b20d6
|
@ -4,7 +4,7 @@
|
|||
it's cheap so caching doesn't help anyway
|
||||
- auto rshift down to 8 bits on save, if necessary
|
||||
- im_gaussnoise(), im_copy_file(), im_grid(), im_scale(), im_scaleps(),
|
||||
im_wrap(), im_rotquad() redone as classes
|
||||
im_wrap(), im_rotquad(), im_zoom() redone as classes
|
||||
- add --angle option to dzsave
|
||||
|
||||
14/5/13 started 7.32.4
|
||||
|
|
|
@ -35,6 +35,6 @@ libconversion_la_SOURCES = \
|
|||
im_system.c \
|
||||
im_system_image.c \
|
||||
im_text.c \
|
||||
im_zoom.c
|
||||
zoom.c
|
||||
|
||||
AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@
|
||||
|
|
|
@ -129,6 +129,7 @@ vips_conversion_operation_init( void )
|
|||
extern GType vips_grid_get_type( void );
|
||||
extern GType vips_scale_get_type( void );
|
||||
extern GType vips_wrap_get_type( void );
|
||||
extern GType vips_zoom_get_type( void );
|
||||
|
||||
vips_copy_get_type();
|
||||
vips_tile_cache_get_type();
|
||||
|
@ -155,6 +156,7 @@ vips_conversion_operation_init( void )
|
|||
vips_grid_get_type();
|
||||
vips_scale_get_type();
|
||||
vips_wrap_get_type();
|
||||
vips_zoom_get_type();
|
||||
}
|
||||
|
||||
/* The common part of most binary conversion
|
||||
|
|
|
@ -1,375 +0,0 @@
|
|||
/* im_zoom
|
||||
*
|
||||
* Author: N. Martinez 1991
|
||||
* 6/6/94 JC
|
||||
* - rewritten to ANSI-C
|
||||
* - now works for any type, including IM_CODING_LABQ
|
||||
* 7/10/94 JC
|
||||
* - new IM_ARRAY() macro
|
||||
* 26/1/96 JC
|
||||
* - separate x and y zoom factors
|
||||
* 21/8/96 JC
|
||||
* - partial, yuk! this is so complicated ...
|
||||
* 30/8/96 JC
|
||||
* - sets demand_hint
|
||||
* 10/2/00 JC
|
||||
* - check for integer overflow in zoom facs ... was happening with ip's
|
||||
* zoom on large images
|
||||
* 3/8/02 JC
|
||||
* - fall back to im_copy() for x & y factors == 1
|
||||
* 24/3/09
|
||||
* - added IM_CODING_RAD support
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Test for pixel size and use memcpy() on individual pixels once they reach
|
||||
* sizes of the order of tens of bytes. char-wise copy is quicker than
|
||||
* memcpy() for smaller pixels.
|
||||
*
|
||||
* Also, I haven't tested it but int-wise copying may be faster still, as
|
||||
* long as alignment permits it.
|
||||
*
|
||||
* tcv. 2006-09-01
|
||||
*/
|
||||
|
||||
/* Turn on IM_REGION_ADDR() range checks.
|
||||
#define DEBUG 1
|
||||
*/
|
||||
|
||||
#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 <limits.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
/* Round N down to P boundary.
|
||||
*/
|
||||
#define ROUND_DOWN(N,P) ((N) - ((N) % P))
|
||||
|
||||
/* Round N up to P boundary.
|
||||
*/
|
||||
#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) ))
|
||||
|
||||
/* Our main parameter struct.
|
||||
*/
|
||||
typedef struct {
|
||||
int xfac; /* Scale factors */
|
||||
int yfac;
|
||||
} ZoomInfo;
|
||||
|
||||
/* Paint the part of the region containing only whole pels.
|
||||
*/
|
||||
static void
|
||||
paint_whole( REGION *or, REGION *ir, ZoomInfo *zm,
|
||||
const int left, const int right, const int top, const int bottom )
|
||||
{
|
||||
const int ps = IM_IMAGE_SIZEOF_PEL( ir->im );
|
||||
const int ls = IM_REGION_LSKIP( or );
|
||||
const int rs = ps * (right - left);
|
||||
|
||||
/* Transform to ir coordinates.
|
||||
*/
|
||||
const int ileft = left / zm->xfac;
|
||||
const int iright = right / zm->xfac;
|
||||
const int itop = top / zm->yfac;
|
||||
const int ibottom = bottom / zm->yfac;
|
||||
|
||||
int x, y, z, i;
|
||||
|
||||
/* We know this!
|
||||
*/
|
||||
g_assert( right > left && bottom > top &&
|
||||
right % zm->xfac == 0 &&
|
||||
left % zm->xfac == 0 &&
|
||||
top % zm->yfac == 0 &&
|
||||
bottom % zm->yfac == 0 );
|
||||
|
||||
/* Loop over input, as we know we are all whole.
|
||||
*/
|
||||
for( y = itop; y < ibottom; y++ ) {
|
||||
VipsPel *p = IM_REGION_ADDR( ir, ileft, y );
|
||||
VipsPel *q = IM_REGION_ADDR( or, left, y * zm->yfac );
|
||||
VipsPel *r;
|
||||
|
||||
/* Expand the first line of pels.
|
||||
*/
|
||||
r = q;
|
||||
for( x = ileft; x < iright; x++ ) {
|
||||
/* Copy each pel xfac times.
|
||||
*/
|
||||
for( z = 0; z < zm->xfac; z++ ) {
|
||||
for( i = 0; i < ps; i++ )
|
||||
r[i] = p[i];
|
||||
|
||||
r += ps;
|
||||
}
|
||||
|
||||
p += ps;
|
||||
}
|
||||
|
||||
/* Copy the expanded line yfac-1 times.
|
||||
*/
|
||||
r = q + ls;
|
||||
for( z = 1; z < zm->yfac; z++ ) {
|
||||
memcpy( r, q, rs );
|
||||
r += ls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Paint the part of the region containing only part-pels.
|
||||
*/
|
||||
static void
|
||||
paint_part( REGION *or, REGION *ir, const ZoomInfo *zm,
|
||||
const int left, const int right, const int top, const int bottom )
|
||||
{
|
||||
const int ps = IM_IMAGE_SIZEOF_PEL( ir->im );
|
||||
const int ls = IM_REGION_LSKIP( or );
|
||||
const int rs = ps * (right - left);
|
||||
|
||||
/* Start position in input.
|
||||
*/
|
||||
const int ix = left / zm->xfac;
|
||||
const int iy = top / zm->yfac;
|
||||
|
||||
/* Pels down to yfac boundary, pels down to bottom. Do the smallest of
|
||||
* these for first y loop.
|
||||
*/
|
||||
const int ptbound = (iy + 1) * zm->yfac - top;
|
||||
const int ptbot = bottom - top;
|
||||
|
||||
int yt = IM_MIN( ptbound, ptbot );
|
||||
|
||||
int x, y, z, i;
|
||||
|
||||
/* Only know this.
|
||||
*/
|
||||
g_assert( right - left >= 0 && bottom - top >= 0 );
|
||||
|
||||
/* Have to loop over output.
|
||||
*/
|
||||
for( y = top; y < bottom; ) {
|
||||
VipsPel *p = IM_REGION_ADDR( ir, ix, y / zm->yfac );
|
||||
VipsPel *q = IM_REGION_ADDR( or, left, y );
|
||||
VipsPel *r;
|
||||
|
||||
/* Output pels until we jump the input pointer.
|
||||
*/
|
||||
int xt = (ix + 1) * zm->xfac - left;
|
||||
|
||||
/* Loop for this output line.
|
||||
*/
|
||||
r = q;
|
||||
for( x = left; x < right; x++ ) {
|
||||
/* Copy 1 pel.
|
||||
*/
|
||||
for( i = 0; i < ps; i++ )
|
||||
r[i] = p[i];
|
||||
r += ps;
|
||||
|
||||
/* Move input if on boundary.
|
||||
*/
|
||||
--xt;
|
||||
if( xt == 0 ) {
|
||||
xt = zm->xfac;
|
||||
p += ps;
|
||||
}
|
||||
}
|
||||
|
||||
/* Repeat that output line until the bottom of this pixel
|
||||
* boundary, or we hit bottom.
|
||||
*/
|
||||
r = q + ls;
|
||||
for( z = 1; z < yt; z++ ) {
|
||||
memcpy( r, q, rs );
|
||||
r += ls;
|
||||
}
|
||||
|
||||
/* Move y on by the number of lines we wrote.
|
||||
*/
|
||||
y += yt;
|
||||
|
||||
/* Reset yt for next iteration.
|
||||
*/
|
||||
yt = zm->yfac;
|
||||
}
|
||||
}
|
||||
|
||||
/* Zoom a REGION.
|
||||
*/
|
||||
static int
|
||||
zoom_gen( REGION *or, void *seq, void *a, void *b )
|
||||
{
|
||||
REGION *ir = (REGION *) seq;
|
||||
ZoomInfo *zm = (ZoomInfo *) b;
|
||||
|
||||
/* Output area we are building.
|
||||
*/
|
||||
const Rect *r = &or->valid;
|
||||
const int ri = IM_RECT_RIGHT( r );
|
||||
const int bo = IM_RECT_BOTTOM(r);
|
||||
|
||||
Rect s;
|
||||
int left, right, top, bottom;
|
||||
int width, height;
|
||||
|
||||
/* Area of input we need. We have to round out, as we may have
|
||||
* part-pixels all around the edges.
|
||||
*/
|
||||
left = ROUND_DOWN( r->left, zm->xfac );
|
||||
right = ROUND_UP( ri, zm->xfac );
|
||||
top = ROUND_DOWN( r->top, zm->yfac );
|
||||
bottom = ROUND_UP( bo, zm->yfac );
|
||||
width = right - left;
|
||||
height = bottom - top;
|
||||
s.left = left / zm->xfac;
|
||||
s.top = top / zm->yfac;
|
||||
s.width = width / zm->xfac;
|
||||
s.height = height / zm->yfac;
|
||||
if( im_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
/* Find the part of the output (if any) which uses only whole pels.
|
||||
*/
|
||||
left = ROUND_UP( r->left, zm->xfac );
|
||||
right = ROUND_DOWN( ri, zm->xfac );
|
||||
top = ROUND_UP( r->top, zm->yfac );
|
||||
bottom = ROUND_DOWN( bo, zm->yfac );
|
||||
width = right - left;
|
||||
height = bottom - top;
|
||||
|
||||
/* Stage 1: we just paint the whole pels in the centre of the region.
|
||||
* As we know they are not clipped, we can do it quickly.
|
||||
*/
|
||||
if( width > 0 && height > 0 )
|
||||
paint_whole( or, ir, zm, left, right, top, bottom );
|
||||
|
||||
/* Just fractional pixels left. Paint in the top, left, right and
|
||||
* bottom parts.
|
||||
*/
|
||||
if( top - r->top > 0 )
|
||||
/* Some top pixels.
|
||||
*/
|
||||
paint_part( or, ir, zm,
|
||||
r->left, ri, r->top, IM_MIN( top, bo ) );
|
||||
if( left - r->left > 0 && height > 0 )
|
||||
/* Left pixels.
|
||||
*/
|
||||
paint_part( or, ir, zm,
|
||||
r->left, IM_MIN( left, ri ), top, bottom );
|
||||
if( ri - right > 0 && height > 0 )
|
||||
/* Right pixels.
|
||||
*/
|
||||
paint_part( or, ir, zm,
|
||||
IM_MAX( right, r->left ), ri, top, bottom );
|
||||
if( bo - bottom > 0 && height >= 0 )
|
||||
/* Bottom pixels.
|
||||
*/
|
||||
paint_part( or, ir, zm,
|
||||
r->left, ri, IM_MAX( bottom, r->top ), bo );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* im_zoom:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @xfac: horizontal scale factor
|
||||
* @yfac: vertical scale factor
|
||||
*
|
||||
* Zoom an image by repeating pixels. This is fast nearest-neighbour
|
||||
* zoom.
|
||||
*
|
||||
* See also: im_affinei(), im_subsample().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
im_zoom( IMAGE *in, IMAGE *out, int xfac, int yfac )
|
||||
{
|
||||
ZoomInfo *zm;
|
||||
|
||||
/* Check arguments.
|
||||
*/
|
||||
if( xfac <= 0 || yfac <= 0 ) {
|
||||
im_error( "im_zoom", "%s", _( "zoom factors should be >= 0" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( (double) in->Xsize * xfac > (double) INT_MAX / 2 ||
|
||||
(double) in->Ysize * yfac > (double) INT_MAX / 2 ) {
|
||||
/* Make sure we won't get integer overflow.
|
||||
*/
|
||||
im_error( "im_zoom", "%s", _( "zoom factors too large" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( xfac == 1 && yfac == 1 )
|
||||
return( im_copy( in, out ) );
|
||||
if( im_piocheck( in, out ) ||
|
||||
im_check_coding_known( "im_zoom", in ) )
|
||||
return( -1 );
|
||||
|
||||
/* Make output.
|
||||
*/
|
||||
if( im_cp_desc( out, in ) )
|
||||
return( -1 );
|
||||
out->Xsize = in->Xsize * xfac;
|
||||
out->Ysize = in->Ysize * yfac;
|
||||
|
||||
/* Save parameters.
|
||||
*/
|
||||
if( !(zm = IM_NEW( out, ZoomInfo )) )
|
||||
return( -1 );
|
||||
zm->xfac = xfac;
|
||||
zm->yfac = yfac;
|
||||
|
||||
/* Set demand hints. THINSTRIP will prevent us from using
|
||||
* paint_whole() much ... so go for FATSTRIP.
|
||||
*/
|
||||
if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
/* Generate!
|
||||
*/
|
||||
if( im_generate( out,
|
||||
im_start_one, zoom_gen, im_stop_one, in, zm ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
|
@ -0,0 +1,444 @@
|
|||
/* im_zoom
|
||||
*
|
||||
* Author: N. Martinez 1991
|
||||
* 6/6/94 JC
|
||||
* - rewritten to ANSI-C
|
||||
* - now works for any type, including IM_CODING_LABQ
|
||||
* 7/10/94 JC
|
||||
* - new IM_ARRAY() macro
|
||||
* 26/1/96 JC
|
||||
* - separate x and y zoom factors
|
||||
* 21/8/96 JC
|
||||
* - partial, yuk! this is so complicated ...
|
||||
* 30/8/96 JC
|
||||
* - sets demand_hint
|
||||
* 10/2/00 JC
|
||||
* - check for integer overflow in zoom facs ... was happening with ip's
|
||||
* zoom on large images
|
||||
* 3/8/02 JC
|
||||
* - fall back to im_copy() for x & y factors == 1
|
||||
* 24/3/09
|
||||
* - added IM_CODING_RAD support
|
||||
* 1/2/10
|
||||
* - gtkdoc
|
||||
* 1/6/13
|
||||
* - redo 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Test for pixel size and use memcpy() on individual pixels once they reach
|
||||
* sizes of the order of tens of bytes. char-wise copy is quicker than
|
||||
* memcpy() for smaller pixels.
|
||||
*
|
||||
* Also, I haven't tested it but int-wise copying may be faster still, as
|
||||
* long as alignment permits it.
|
||||
*
|
||||
* tcv. 2006-09-01
|
||||
*/
|
||||
|
||||
/* Turn on ADDR() range checks.
|
||||
#define DEBUG 1
|
||||
*/
|
||||
|
||||
#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 <limits.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "conversion.h"
|
||||
|
||||
typedef struct _VipsZoom {
|
||||
VipsConversion parent_instance;
|
||||
|
||||
/* The input image.
|
||||
*/
|
||||
VipsImage *in;
|
||||
|
||||
int xfac; /* Scale factors */
|
||||
int yfac;
|
||||
|
||||
} VipsZoom;
|
||||
|
||||
typedef VipsConversionClass VipsZoomClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsZoom, vips_zoom, VIPS_TYPE_CONVERSION );
|
||||
|
||||
/* Round N down to P boundary.
|
||||
*/
|
||||
#define ROUND_DOWN( N, P ) ((N) - ((N) % P))
|
||||
|
||||
/* Round N up to P boundary.
|
||||
*/
|
||||
#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) ))
|
||||
|
||||
/* Paint the part of the region containing only whole pels.
|
||||
*/
|
||||
static void
|
||||
vips_zoom_paint_whole( VipsRegion *or, VipsRegion *ir, VipsZoom *zoom,
|
||||
const int left, const int right, const int top, const int bottom )
|
||||
{
|
||||
const int ps = VIPS_IMAGE_SIZEOF_PEL( ir->im );
|
||||
const int ls = VIPS_REGION_LSKIP( or );
|
||||
const int rs = ps * (right - left);
|
||||
|
||||
/* Transform to ir coordinates.
|
||||
*/
|
||||
const int ileft = left / zoom->xfac;
|
||||
const int iright = right / zoom->xfac;
|
||||
const int itop = top / zoom->yfac;
|
||||
const int ibottom = bottom / zoom->yfac;
|
||||
|
||||
int x, y, z, i;
|
||||
|
||||
/* We know this!
|
||||
*/
|
||||
g_assert( right > left && bottom > top &&
|
||||
right % zoom->xfac == 0 &&
|
||||
left % zoom->xfac == 0 &&
|
||||
top % zoom->yfac == 0 &&
|
||||
bottom % zoom->yfac == 0 );
|
||||
|
||||
/* Loop over input, as we know we are all whole.
|
||||
*/
|
||||
for( y = itop; y < ibottom; y++ ) {
|
||||
VipsPel *p = VIPS_REGION_ADDR( ir, ileft, y );
|
||||
VipsPel *q = VIPS_REGION_ADDR( or, left, y * zoom->yfac );
|
||||
VipsPel *r;
|
||||
|
||||
/* Expand the first line of pels.
|
||||
*/
|
||||
r = q;
|
||||
for( x = ileft; x < iright; x++ ) {
|
||||
/* Copy each pel xfac times.
|
||||
*/
|
||||
for( z = 0; z < zoom->xfac; z++ ) {
|
||||
for( i = 0; i < ps; i++ )
|
||||
r[i] = p[i];
|
||||
|
||||
r += ps;
|
||||
}
|
||||
|
||||
p += ps;
|
||||
}
|
||||
|
||||
/* Copy the expanded line yfac-1 times.
|
||||
*/
|
||||
r = q + ls;
|
||||
for( z = 1; z < zoom->yfac; z++ ) {
|
||||
memcpy( r, q, rs );
|
||||
r += ls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Paint the part of the region containing only part-pels.
|
||||
*/
|
||||
static void
|
||||
vips_zoom_paint_part( VipsRegion *or, VipsRegion *ir, VipsZoom *zoom,
|
||||
const int left, const int right, const int top, const int bottom )
|
||||
{
|
||||
const int ps = VIPS_IMAGE_SIZEOF_PEL( ir->im );
|
||||
const int ls = VIPS_REGION_LSKIP( or );
|
||||
const int rs = ps * (right - left);
|
||||
|
||||
/* Start position in input.
|
||||
*/
|
||||
const int ix = left / zoom->xfac;
|
||||
const int iy = top / zoom->yfac;
|
||||
|
||||
/* Pels down to yfac boundary, pels down to bottom. Do the smallest of
|
||||
* these for first y loop.
|
||||
*/
|
||||
const int ptbound = (iy + 1) * zoom->yfac - top;
|
||||
const int ptbot = bottom - top;
|
||||
|
||||
int yt = VIPS_MIN( ptbound, ptbot );
|
||||
|
||||
int x, y, z, i;
|
||||
|
||||
/* Only know this.
|
||||
*/
|
||||
g_assert( right - left >= 0 && bottom - top >= 0 );
|
||||
|
||||
/* Have to loop over output.
|
||||
*/
|
||||
for( y = top; y < bottom; ) {
|
||||
VipsPel *p = VIPS_REGION_ADDR( ir, ix, y / zoom->yfac );
|
||||
VipsPel *q = VIPS_REGION_ADDR( or, left, y );
|
||||
VipsPel *r;
|
||||
|
||||
/* Output pels until we jump the input pointer.
|
||||
*/
|
||||
int xt = (ix + 1) * zoom->xfac - left;
|
||||
|
||||
/* Loop for this output line.
|
||||
*/
|
||||
r = q;
|
||||
for( x = left; x < right; x++ ) {
|
||||
/* Copy 1 pel.
|
||||
*/
|
||||
for( i = 0; i < ps; i++ )
|
||||
r[i] = p[i];
|
||||
r += ps;
|
||||
|
||||
/* Move input if on boundary.
|
||||
*/
|
||||
--xt;
|
||||
if( xt == 0 ) {
|
||||
xt = zoom->xfac;
|
||||
p += ps;
|
||||
}
|
||||
}
|
||||
|
||||
/* Repeat that output line until the bottom of this pixel
|
||||
* boundary, or we hit bottom.
|
||||
*/
|
||||
r = q + ls;
|
||||
for( z = 1; z < yt; z++ ) {
|
||||
memcpy( r, q, rs );
|
||||
r += ls;
|
||||
}
|
||||
|
||||
/* Move y on by the number of lines we wrote.
|
||||
*/
|
||||
y += yt;
|
||||
|
||||
/* Reset yt for next iteration.
|
||||
*/
|
||||
yt = zoom->yfac;
|
||||
}
|
||||
}
|
||||
|
||||
/* Zoom a VipsRegion.
|
||||
*/
|
||||
static int
|
||||
vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
||||
{
|
||||
VipsRegion *ir = (VipsRegion *) seq;
|
||||
VipsZoom *zoom = (VipsZoom *) b;
|
||||
|
||||
/* Output area we are building.
|
||||
*/
|
||||
const VipsRect *r = &or->valid;
|
||||
const int ri = VIPS_RECT_RIGHT( r );
|
||||
const int bo = VIPS_RECT_BOTTOM(r);
|
||||
|
||||
VipsRect s;
|
||||
int left, right, top, bottom;
|
||||
int width, height;
|
||||
|
||||
/* Area of input we need. We have to round out, as we may have
|
||||
* part-pixels all around the edges.
|
||||
*/
|
||||
left = ROUND_DOWN( r->left, zoom->xfac );
|
||||
right = ROUND_UP( ri, zoom->xfac );
|
||||
top = ROUND_DOWN( r->top, zoom->yfac );
|
||||
bottom = ROUND_UP( bo, zoom->yfac );
|
||||
width = right - left;
|
||||
height = bottom - top;
|
||||
s.left = left / zoom->xfac;
|
||||
s.top = top / zoom->yfac;
|
||||
s.width = width / zoom->xfac;
|
||||
s.height = height / zoom->yfac;
|
||||
if( vips_region_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
/* Find the part of the output (if any) which uses only whole pels.
|
||||
*/
|
||||
left = ROUND_UP( r->left, zoom->xfac );
|
||||
right = ROUND_DOWN( ri, zoom->xfac );
|
||||
top = ROUND_UP( r->top, zoom->yfac );
|
||||
bottom = ROUND_DOWN( bo, zoom->yfac );
|
||||
width = right - left;
|
||||
height = bottom - top;
|
||||
|
||||
/* Stage 1: we just paint the whole pels in the centre of the region.
|
||||
* As we know they are not clipped, we can do it quickly.
|
||||
*/
|
||||
if( width > 0 && height > 0 )
|
||||
vips_zoom_paint_whole( or, ir, zoom, left, right, top, bottom );
|
||||
|
||||
/* Just fractional pixels left. Paint in the top, left, right and
|
||||
* bottom parts.
|
||||
*/
|
||||
if( top - r->top > 0 )
|
||||
/* Some top pixels.
|
||||
*/
|
||||
vips_zoom_paint_part( or, ir, zoom,
|
||||
r->left, ri, r->top, VIPS_MIN( top, bo ) );
|
||||
if( left - r->left > 0 && height > 0 )
|
||||
/* Left pixels.
|
||||
*/
|
||||
vips_zoom_paint_part( or, ir, zoom,
|
||||
r->left, VIPS_MIN( left, ri ), top, bottom );
|
||||
if( ri - right > 0 && height > 0 )
|
||||
/* Right pixels.
|
||||
*/
|
||||
vips_zoom_paint_part( or, ir, zoom,
|
||||
VIPS_MAX( right, r->left ), ri, top, bottom );
|
||||
if( bo - bottom > 0 && height >= 0 )
|
||||
/* Bottom pixels.
|
||||
*/
|
||||
vips_zoom_paint_part( or, ir, zoom,
|
||||
r->left, ri, VIPS_MAX( bottom, r->top ), bo );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_zoom_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsConversion *conversion = VIPS_CONVERSION( object );
|
||||
VipsZoom *zoom = (VipsZoom *) object;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_zoom_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
g_assert( zoom->xfac > 0 );
|
||||
g_assert( zoom->yfac > 0 );
|
||||
|
||||
/* Make sure we won't get integer overflow.
|
||||
*/
|
||||
if( (double) zoom->in->Xsize * zoom->xfac > (double) INT_MAX / 2 ||
|
||||
(double) zoom->in->Ysize * zoom->yfac > (double) INT_MAX / 2 ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "zoom factors too large" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( zoom->xfac == 1 &&
|
||||
zoom->yfac == 1 )
|
||||
return( vips_image_write( zoom->in, conversion->out ) );
|
||||
|
||||
if( vips_image_pio_input( zoom->in ) ||
|
||||
vips_check_coding_known( class->nickname, zoom->in ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_image_copy_fields( conversion->out, zoom->in ) )
|
||||
return( -1 );
|
||||
/* Set demand hints. THINSTRIP will prevent us from using
|
||||
* vips_zoom_paint_whole() much ... so go for FATSTRIP.
|
||||
*/
|
||||
vips_demand_hint( conversion->out,
|
||||
VIPS_DEMAND_STYLE_FATSTRIP, zoom->in, NULL );
|
||||
conversion->out->Xsize = zoom->in->Xsize * zoom->xfac;
|
||||
conversion->out->Ysize = zoom->in->Ysize * zoom->yfac;
|
||||
|
||||
if( vips_image_generate( conversion->out,
|
||||
vips_start_one, vips_zoom_gen, vips_stop_one,
|
||||
zoom->in, zoom ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* xy range we sanity check on ... just to stop crazy numbers from divide by 0
|
||||
* etc. causing g_assert() failures later.
|
||||
*/
|
||||
#define RANGE (100000000)
|
||||
|
||||
static void
|
||||
vips_zoom_class_init( VipsZoomClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
||||
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
vobject_class->nickname = "zoom";
|
||||
vobject_class->description = _( "zoom an image" );
|
||||
vobject_class->build = vips_zoom_build;
|
||||
|
||||
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
|
||||
|
||||
VIPS_ARG_IMAGE( class, "input", 0,
|
||||
_( "Input" ),
|
||||
_( "Input image" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsZoom, in ) );
|
||||
|
||||
VIPS_ARG_INT( class, "xfac", 2,
|
||||
_( "Xfac" ),
|
||||
_( "Horizontal zoom factor" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsZoom, xfac ),
|
||||
1, RANGE, 1 );
|
||||
|
||||
VIPS_ARG_INT( class, "yfac", 3,
|
||||
_( "Yfac" ),
|
||||
_( "Vertical zoom factor" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsZoom, yfac ),
|
||||
1, RANGE, 1 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_zoom_init( VipsZoom *zoom )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_zoom:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @xfac: horizontal scale factor
|
||||
* @yfac: vertical scale factor
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Zoom an image by repeating pixels. This is fast nearest-neighbour
|
||||
* zoom.
|
||||
*
|
||||
* See also: vips_affine(), vips_subsample().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_zoom( VipsImage *in, VipsImage **out, int xfac, int yfac, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, yfac );
|
||||
result = vips_call_split( "zoom", ap, in, out, xfac, yfac );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
|
@ -1565,6 +1565,22 @@ im_scaleps( VipsImage *in, VipsImage *out )
|
|||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_zoom( VipsImage *in, VipsImage *out, int xfac, int yfac )
|
||||
{
|
||||
VipsImage *t;
|
||||
|
||||
if( vips_zoom( in, &t, xfac, yfac, NULL ) )
|
||||
return( -1 );
|
||||
if( vips_image_write( t, out ) ) {
|
||||
g_object_unref( t );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips__math( VipsImage *in, VipsImage *out, VipsOperationMath math )
|
||||
{
|
||||
|
|
|
@ -194,6 +194,8 @@ int vips_grid( VipsImage *in, VipsImage **out,
|
|||
__attribute__((sentinel));
|
||||
int vips_wrap( VipsImage *in, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_zoom( VipsImage *in, VipsImage **out, int xfac, int yfac, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
int vips_cast( VipsImage *in, VipsImage **out, VipsBandFormat format, ... )
|
||||
__attribute__((sentinel));
|
||||
|
@ -268,7 +270,6 @@ int im_text( VipsImage *out, const char *text, const char *font,
|
|||
int im_insertset( VipsImage *main, VipsImage *sub, VipsImage *out, int n, int *x, int *y );
|
||||
|
||||
int im_subsample( VipsImage *in, VipsImage *out, int xshrink, int yshrink );
|
||||
int im_zoom( VipsImage *in, VipsImage *out, int xfac, int yfac );
|
||||
|
||||
int im_system( VipsImage *im, const char *cmd, char **out );
|
||||
VipsImage *im_system_image( VipsImage *im,
|
||||
|
|
|
@ -715,6 +715,7 @@ int im_gaussnoise( VipsImage *out, int x, int y, double mean, double sigma );
|
|||
int im_grid( VipsImage *in, VipsImage *out, int tile_height, int across, int down );
|
||||
int im_scale( VipsImage *in, VipsImage *out );
|
||||
int im_scaleps( VipsImage *in, VipsImage *out );
|
||||
int im_zoom( VipsImage *in, VipsImage *out, int xfac, int yfac );
|
||||
|
||||
int im_c2amph( VipsImage *in, VipsImage *out );
|
||||
int im_c2rect( VipsImage *in, VipsImage *out );
|
||||
|
|
Loading…
Reference in New Issue