Skip to content

Commit 793f90b

Browse files
committed
All pixel shifts are gone!
We had to move the offset calculation from resize to reduce{h,v}.
1 parent b90542e commit 793f90b

File tree

3 files changed

+88
-93
lines changed

3 files changed

+88
-93
lines changed

libvips/resample/reduceh.cpp

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,8 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
309309

310310
s.left = r->left * reduceh->hshrink;
311311
s.top = r->top;
312-
s.width = r->width * reduceh->hshrink + reduceh->n_point
313-
+ reduceh->hoffset;
312+
s.width = r->width * reduceh->hshrink + reduceh->n_point -
313+
reduceh->hoffset;
314314
s.height = r->height;
315315
if( vips_region_prepare( ir, &s ) )
316316
return( -1 );
@@ -325,8 +325,8 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
325325

326326
q = VIPS_REGION_ADDR( out_region, r->left, r->top + y );
327327

328-
X = (r->left + 0.5) * reduceh->hshrink - 0.5
329-
+ reduceh->hoffset;
328+
X = (r->left + 0.5) * reduceh->hshrink - 0.5 -
329+
reduceh->hoffset;
330330

331331
/* We want p0 to be the start (ie. x == 0) of the input
332332
* scanline we are reading from. We can then calculate the p we
@@ -430,7 +430,7 @@ vips_reduceh_build( VipsObject *object )
430430
vips_object_local_array( object, 2 );
431431

432432
VipsImage *in;
433-
double left;
433+
double width, extra_pixels;
434434

435435
if( VIPS_OBJECT_CLASS( vips_reduceh_parent_class )->build( object ) )
436436
return( -1 );
@@ -446,8 +446,6 @@ vips_reduceh_build( VipsObject *object )
446446
if( reduceh->hshrink == 1 )
447447
return( vips_image_write( in, resample->out ) );
448448

449-
/* Build the tables of pre-computed coefficients.
450-
*/
451449
reduceh->n_point =
452450
vips_reduce_get_points( reduceh->kernel, reduceh->hshrink );
453451
g_info( "reduceh: %d point mask", reduceh->n_point );
@@ -456,6 +454,28 @@ vips_reduceh_build( VipsObject *object )
456454
"%s", _( "reduce factor too large" ) );
457455
return( -1 );
458456
}
457+
458+
/* Output size. We need to always round to nearest, so round(), not
459+
* rint().
460+
*/
461+
width = VIPS_ROUND_UINT(
462+
(double) resample->in->Xsize / reduceh->hshrink );
463+
464+
/* How many pixels we are inventing in the input, -ve for
465+
* discarding.
466+
*/
467+
extra_pixels =
468+
width * reduceh->hshrink - resample->in->Xsize;
469+
470+
/* If we are rounding down, we are not using some input
471+
* pixels. We need to move the origin *inside* the input image
472+
* by half that distance so that we discard pixels equally
473+
* from left and right.
474+
*/
475+
reduceh->hoffset = (1 + extra_pixels) / 2.0;
476+
477+
/* Build the tables of pre-computed coefficients.
478+
*/
459479
for( int x = 0; x < VIPS_TRANSFORM_SCALE; x++ ) {
460480
reduceh->matrixf[x] =
461481
VIPS_ARRAY( object, reduceh->n_point, double );
@@ -487,15 +507,11 @@ vips_reduceh_build( VipsObject *object )
487507
return( -1 );
488508
in = t[0];
489509

490-
left = reduceh->n_point / 2.0 - 1;
491-
reduceh->hoffset -= modf( left, &left );
492-
printf( "hoffset: %f\n", reduceh->hoffset );
493-
494510
/* Add new pixels around the input so we can interpolate at the edges.
495511
*/
496512
if( vips_embed( in, &t[1],
497-
left, 0,
498-
in->Xsize + reduceh->n_point + reduceh->hoffset, in->Ysize,
513+
reduceh->n_point / 2 - 1, 0,
514+
in->Xsize + reduceh->n_point, in->Ysize,
499515
"extend", VIPS_EXTEND_COPY,
500516
(void *) NULL ) )
501517
return( -1 );
@@ -512,8 +528,7 @@ vips_reduceh_build( VipsObject *object )
512528
* example, vipsthumbnail knows the true reduce factor (including the
513529
* fractional part), we just see the integer part here.
514530
*/
515-
resample->out->Xsize = VIPS_ROUND_UINT(
516-
resample->in->Xsize / reduceh->hshrink );
531+
resample->out->Xsize = width;
517532
if( resample->out->Xsize <= 0 ) {
518533
vips_error( object_class->nickname,
519534
"%s", _( "image has shrunk to nothing" ) );
@@ -569,13 +584,6 @@ vips_reduceh_class_init( VipsReducehClass *reduceh_class )
569584
G_STRUCT_OFFSET( VipsReduceh, kernel ),
570585
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
571586

572-
VIPS_ARG_DOUBLE( reduceh_class, "hoffset", 5,
573-
_( "Offset" ),
574-
_( "Horizontal displacement" ),
575-
VIPS_ARGUMENT_OPTIONAL_INPUT,
576-
G_STRUCT_OFFSET( VipsReduceh, hoffset ),
577-
-10000000, 10000000, 0 );
578-
579587
/* Old name.
580588
*/
581589
VIPS_ARG_DOUBLE( reduceh_class, "xshrink", 3,
@@ -600,7 +608,6 @@ static void
600608
vips_reduceh_init( VipsReduceh *reduceh )
601609
{
602610
reduceh->kernel = VIPS_KERNEL_LANCZOS3;
603-
reduceh->hoffset = 0.0;
604611
}
605612

606613
/* See reduce.c for the doc comment.

libvips/resample/reducev.cpp

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -536,15 +536,15 @@ vips_reducev_gen( VipsRegion *out_region, void *vseq,
536536
s.left = r->left;
537537
s.top = r->top * reducev->vshrink;
538538
s.width = r->width;
539-
s.height = r->height * reducev->vshrink + reducev->n_point
540-
+ reducev->voffset;
539+
s.height = r->height * reducev->vshrink + reducev->n_point -
540+
reducev->voffset;
541541
if( vips_region_prepare( ir, &s ) )
542542
return( -1 );
543543

544544
VIPS_GATE_START( "vips_reducev_gen: work" );
545545

546-
double Y = (r->top + 0.5) * reducev->vshrink - 0.5
547-
+ reducev->voffset;
546+
double Y = (r->top + 0.5) * reducev->vshrink - 0.5 -
547+
reducev->voffset;
548548

549549
for( int y = 0; y < r->height; y++ ) {
550550
VipsPel *q =
@@ -651,8 +651,8 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
651651
s.left = r->left;
652652
s.top = r->top * reducev->vshrink;
653653
s.width = r->width;
654-
s.height = r->height * reducev->vshrink + reducev->n_point
655-
+ reducev->voffset;
654+
s.height = r->height * reducev->vshrink + reducev->n_point -
655+
reducev->voffset;
656656
if( vips_region_prepare( ir, &s ) )
657657
return( -1 );
658658

@@ -667,8 +667,8 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
667667

668668
VIPS_GATE_START( "vips_reducev_vector_gen: work" );
669669

670-
double Y = (r->top + 0.5) * reducev->vshrink - 0.5
671-
+ reducev->voffset;
670+
double Y = (r->top + 0.5) * reducev->vshrink - 0.5 -
671+
reducev->voffset;
672672

673673
for( int y = 0; y < r->height; y++ ) {
674674
VipsPel *q =
@@ -732,41 +732,7 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in, VipsImage **out )
732732

733733
VipsGenerateFn generate;
734734

735-
/* Build masks.
736-
*/
737-
for( int y = 0; y < VIPS_TRANSFORM_SCALE; y++ ) {
738-
reducev->matrixf[y] =
739-
VIPS_ARRAY( NULL, reducev->n_point, double );
740-
if( !reducev->matrixf[y] )
741-
return( -1 );
742-
743-
vips_reduce_make_mask( reducev->matrixf[y],
744-
reducev->kernel, reducev->vshrink,
745-
(float) y / VIPS_TRANSFORM_SCALE );
746-
747-
#ifdef DEBUG
748-
printf( "%6.2g", (double) y / VIPS_TRANSFORM_SCALE );
749-
for( int i = 0; i < reducev->n_point; i++ )
750-
printf( ", %6.2g", reducev->matrixf[y][i] );
751-
printf( "\n" );
752-
#endif /*DEBUG*/
753-
}
754-
755-
/* uchar and ushort need an int version of the masks.
756-
*/
757-
if( VIPS_IMAGE_SIZEOF_ELEMENT( in ) <= 2 )
758-
for( int y = 0; y < VIPS_TRANSFORM_SCALE; y++ ) {
759-
reducev->matrixi[y] =
760-
VIPS_ARRAY( NULL, reducev->n_point, int );
761-
if( !reducev->matrixi[y] )
762-
return( -1 );
763-
764-
vips_vector_to_fixed_point(
765-
reducev->matrixf[y], reducev->matrixi[y],
766-
reducev->n_point, VIPS_INTERPOLATE_SCALE );
767-
}
768-
769-
/* And we need an 2.6 version if we will use the vector path.
735+
/* We need an 2.6 version if we will use the vector path.
770736
*/
771737
if( in->BandFmt == VIPS_FORMAT_UCHAR &&
772738
vips_vector_isenabled() )
@@ -836,7 +802,7 @@ vips_reducev_build( VipsObject *object )
836802
VipsImage **t = (VipsImage **) vips_object_local_array( object, 4 );
837803

838804
VipsImage *in;
839-
double top;
805+
double height, extra_pixels;
840806

841807
if( VIPS_OBJECT_CLASS( vips_reducev_parent_class )->build( object ) )
842808
return( -1 );
@@ -861,21 +827,63 @@ vips_reducev_build( VipsObject *object )
861827
return( -1 );
862828
}
863829

830+
/* Output size. We need to always round to nearest, so round(), not
831+
* rint().
832+
*/
833+
height = VIPS_ROUND_UINT(
834+
(double) resample->in->Ysize / reducev->vshrink );
835+
836+
/* How many pixels we are inventing in the input, -ve for
837+
* discarding.
838+
*/
839+
extra_pixels =
840+
height * reducev->vshrink - resample->in->Ysize;
841+
842+
/* If we are rounding down, we are not using some input
843+
* pixels. We need to move the origin *inside* the input image
844+
* by half that distance so that we discard pixels equally
845+
* from left and right.
846+
*/
847+
reducev->voffset = (1 + extra_pixels) / 2.0;
848+
849+
/* Build the tables of pre-computed coefficients.
850+
*/
851+
for( int y = 0; y < VIPS_TRANSFORM_SCALE; y++ ) {
852+
reducev->matrixf[y] =
853+
VIPS_ARRAY( NULL, reducev->n_point, double );
854+
reducev->matrixi[y] =
855+
VIPS_ARRAY( NULL, reducev->n_point, int );
856+
if( !reducev->matrixf[y] ||
857+
!reducev->matrixi[y] )
858+
return( -1 );
859+
860+
vips_reduce_make_mask( reducev->matrixf[y],
861+
reducev->kernel, reducev->vshrink,
862+
(float) y / VIPS_TRANSFORM_SCALE );
863+
864+
for( int i = 0; i < reducev->n_point; i++ )
865+
reducev->matrixi[y][i] = reducev->matrixf[y][i] *
866+
VIPS_INTERPOLATE_SCALE;
867+
868+
#ifdef DEBUG
869+
printf( "vips_reducev_raw: mask %d\n ", y );
870+
for( int i = 0; i < reducev->n_point; i++ )
871+
printf( "%d ", reducev->matrixi[y][i] );
872+
printf( "\n" );
873+
#endif /*DEBUG*/
874+
}
875+
864876
/* Unpack for processing.
865877
*/
866878
if( vips_image_decode( in, &t[0] ) )
867879
return( -1 );
868880
in = t[0];
869881

870-
top = reducev->n_point / 2.0 - 1;
871-
reducev->voffset -= modf( top, &top );
872-
printf( "voffset: %f\n", reducev->voffset );
873-
874882
/* Add new pixels around the input so we can interpolate at the edges.
875883
*/
876884
if( vips_embed( in, &t[1],
877-
0, top,
878-
in->Xsize, in->Ysize + reducev->n_point + reducev->voffset,
885+
0, reducev->n_point / 2 - 1,
886+
in->Xsize, in->Ysize + reducev->n_point,
879887
"extend", VIPS_EXTEND_COPY,
880888
(void *) NULL ) )
881889
return( -1 );
@@ -946,13 +954,6 @@ vips_reducev_class_init( VipsReducevClass *reducev_class )
946954
G_STRUCT_OFFSET( VipsReducev, kernel ),
947955
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
948956

949-
VIPS_ARG_DOUBLE( reducev_class, "voffset", 5,
950-
_( "Offset" ),
951-
_( "Vertical displacement" ),
952-
VIPS_ARGUMENT_OPTIONAL_INPUT,
953-
G_STRUCT_OFFSET( VipsReducev, voffset ),
954-
-10000000, 10000000, 0 );
955-
956957
/* Old name.
957958
*/
958959
VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3,
@@ -977,7 +978,6 @@ static void
977978
vips_reducev_init( VipsReducev *reducev )
978979
{
979980
reducev->kernel = VIPS_KERNEL_LANCZOS3;
980-
reducev->voffset = 0.0;
981981
}
982982

983983
/* See reduce.c for the doc comment.

libvips/resample/resize.c

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,8 @@ vips_resize_build( VipsObject *object )
176176
VipsImage *in;
177177
double hscale;
178178
double vscale;
179-
double hoffset;
180-
double voffset;
181179
int int_hshrink;
182180
int int_vshrink;
183-
int extra_pixels;
184181

185182
if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) )
186183
return( -1 );
@@ -195,9 +192,6 @@ vips_resize_build( VipsObject *object )
195192
else
196193
vscale = resize->scale;
197194

198-
hoffset = 0.0;
199-
voffset = 0.0;
200-
201195
/* The int part of our scale.
202196
*/
203197
int_hshrink = vips_resize_int_shrink( resize, hscale );
@@ -215,8 +209,6 @@ vips_resize_build( VipsObject *object )
215209
return( -1 );
216210
in = t[0];
217211

218-
extra_pixels = in->Ysize * int_vshrink - resample->in->Ysize;
219-
voffset = (1 + extra_pixels) / 2.0;
220212
vscale *= int_vshrink;
221213
}
222214

@@ -226,8 +218,6 @@ vips_resize_build( VipsObject *object )
226218
return( -1 );
227219
in = t[1];
228220

229-
extra_pixels = in->Xsize * int_hshrink - resample->in->Xsize;
230-
hoffset = (1 + extra_pixels) / 2.0;
231221
hscale *= int_hshrink;
232222
}
233223

@@ -242,7 +232,6 @@ vips_resize_build( VipsObject *object )
242232
g_info( "residual reducev by %g", vscale );
243233
if( vips_reducev( in, &t[2], 1.0 / vscale,
244234
"kernel", resize->kernel,
245-
"voffset", voffset,
246235
NULL ) )
247236
return( -1 );
248237
in = t[2];
@@ -253,7 +242,6 @@ vips_resize_build( VipsObject *object )
253242
hscale );
254243
if( vips_reduceh( in, &t[3], 1.0 / hscale,
255244
"kernel", resize->kernel,
256-
"hoffset", hoffset,
257245
NULL ) )
258246
return( -1 );
259247
in = t[3];

0 commit comments

Comments
 (0)