2009-01-20 22:32:05 +01:00
|
|
|
/* nohalo interpolator
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
This file is part of VIPS.
|
2009-03-04 23:20:58 +01:00
|
|
|
|
2009-03-04 23:09:54 +01:00
|
|
|
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.
|
2009-01-20 22:32:05 +01:00
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2009-03-04 23:09:54 +01:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
2009-01-20 22:32:05 +01:00
|
|
|
|
2009-03-04 23:09:54 +01:00
|
|
|
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
|
2009-01-20 22:32:05 +01:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2009-03-04 23:20:58 +01:00
|
|
|
* 2009 (c) Nicolas Robidoux
|
2009-01-20 22:32:05 +01:00
|
|
|
*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Nicolas thanks Geert Jordaens, John Cupitt, Minglun Gong, Øyvind
|
|
|
|
* Kolås and Sven Neumann for useful comments and code.
|
2009-03-04 23:20:58 +01:00
|
|
|
*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Nicolas Robidoux's research on nohalo funded in part by an NSERC
|
|
|
|
* (National Science and Engineering Research Council of Canada)
|
|
|
|
* Discovery Grant.
|
2009-01-20 22:32:05 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Hacked for vips by J. Cupitt, 20/1/09
|
2009-03-04 23:09:54 +01:00
|
|
|
* Tweaks by N. Robidoux, 03/3/09
|
2009-01-20 22:32:05 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ================
|
|
|
|
* NOHALO RESAMPLER
|
|
|
|
* ================
|
|
|
|
*
|
|
|
|
* "Nohalo" is a family of parameterized resamplers with a mission:
|
|
|
|
* smoothly straightening oblique lines without undesirable
|
|
|
|
* side-effects.
|
|
|
|
*
|
|
|
|
* The key parameter, which may be described as a "quality" parameter,
|
|
|
|
* is an integer which specifies the number of "levels" of binary
|
2009-01-28 11:01:18 +01:00
|
|
|
* subdivision which are performed. level = 0 can be thought of as
|
|
|
|
* being plain vanilla bilinear resampling; level = 1 is then the
|
|
|
|
* first "non-classical" method of the familiy.
|
2009-01-20 22:32:05 +01:00
|
|
|
*
|
2009-01-28 11:01:18 +01:00
|
|
|
* Although it increases computational cost, additional levels
|
|
|
|
* increase the quality of the resampled pixel value unless the
|
|
|
|
* resampled location happens to be exactly where a subdivided grid
|
|
|
|
* point (for this level) is located, in which case further levels do
|
|
|
|
* not change the answer, and consequently do not increase its
|
|
|
|
* quality.
|
2009-01-20 22:32:05 +01:00
|
|
|
*
|
2009-03-04 23:09:54 +01:00
|
|
|
* ===================================================
|
|
|
|
* THIS CODE ONLY IMPLEMENTS THE LOWEST QUALITY NOHALO
|
|
|
|
* ===================================================
|
2009-03-04 23:20:58 +01:00
|
|
|
*
|
2009-01-20 22:32:05 +01:00
|
|
|
* This code implement nohalo for (quality) level = 1. Nohalo for
|
|
|
|
* higher quality levels will be implemented later.
|
|
|
|
*
|
|
|
|
* Key properties:
|
|
|
|
*
|
|
|
|
* =======================
|
|
|
|
* Nohalo is interpolatory
|
|
|
|
* =======================
|
|
|
|
*
|
|
|
|
* That is, nohalo preserves point values: If asked for the value at
|
|
|
|
* the center of an input pixel, the sampler returns the corresponding
|
|
|
|
* value, unchanged. In addition, because nohalo is continuous, if
|
|
|
|
* asked for a value at a location "very close" to the center of an
|
|
|
|
* input pixel, then the sampler returns a value "very close" to
|
|
|
|
* it. (Nohalo is not smoothing like, say, B-Spline
|
|
|
|
* pseudo-interpolation.)
|
|
|
|
*
|
|
|
|
* ========================================================
|
|
|
|
* Nohalo is co-monotone (this is why it's called "nohalo")
|
|
|
|
* ========================================================
|
|
|
|
*
|
|
|
|
* What monotonicity means here is that the resampled value is in the
|
|
|
|
* range of the four closest input values. Consequently, nohalo does
|
|
|
|
* not add haloing. It also means that clamping is unnecessary
|
|
|
|
* (provided abyss values are within the range of acceptable values,
|
|
|
|
* which is always the case). (Note: plain vanilla bilinear is also
|
|
|
|
* co-monotone.)
|
|
|
|
*
|
|
|
|
* Note: If the abyss policy is an extrapolating one---for example,
|
|
|
|
* linear or bilinear extrapolation---clamping is still unnecessary
|
|
|
|
* unless one attempts to resample outside of the convex hull of the
|
|
|
|
* input pixel positions. Consequence: the "corner" image size
|
|
|
|
* convention does not require clamping when using linear
|
|
|
|
* extrapolation abyss policy when performing image resizing, but the
|
|
|
|
* "center" one does, when upscaling, at locations very close to the
|
|
|
|
* boundary. If computing values at locations outside of the convex
|
|
|
|
* hull of the pixel locations of the input image, nearest neighbour
|
|
|
|
* abyss policy is most likely better anyway, because linear
|
|
|
|
* extrapolation produces "streaks" if positions far outside the
|
|
|
|
* original image boundary are resampled.
|
|
|
|
*
|
|
|
|
* ========================
|
|
|
|
* Nohalo is a local method
|
|
|
|
* ========================
|
|
|
|
*
|
|
|
|
* The value of the reconstructed intensity surface at any point
|
|
|
|
* depends on the values of (at most) 12 nearby input values, located
|
2009-03-04 23:09:54 +01:00
|
|
|
* in a "cross" centered at the closest four input pixel centers.
|
2009-01-20 22:32:05 +01:00
|
|
|
*
|
|
|
|
* ===========================================================
|
|
|
|
* When level = infinity, nohalo's intensity surface is smooth
|
|
|
|
* ===========================================================
|
|
|
|
*
|
|
|
|
* It is conjectured that the intensity surface is infinitely
|
|
|
|
* differentiable. Consequently, "Mach banding" (primarily caused by
|
|
|
|
* sharp "ridges" in the reconstructed intensity surface and
|
|
|
|
* particularly noticeable, for example, when using bilinear
|
|
|
|
* resampling) is (essentially) absent, even at high magnifications,
|
|
|
|
* WHEN THE LEVEL IS HIGH (more or less when 2^(level+1) is at least
|
|
|
|
* the largest local magnification factor, which means that the level
|
|
|
|
* 1 nohalo does not show much Mach banding up to a magnification of
|
|
|
|
* about 4).
|
|
|
|
*
|
|
|
|
* ===============================
|
|
|
|
* Nohalo is second order accurate
|
|
|
|
* ===============================
|
|
|
|
*
|
|
|
|
* (Except possibly near the boundary: it is easy to make this
|
|
|
|
* property carry over everywhere but this requires a tuned abyss
|
2009-03-04 23:09:54 +01:00
|
|
|
* policy---linear extrapolation, say---or building the boundary
|
|
|
|
* conditions inside the sampler.) Nohalo is exact on linear
|
|
|
|
* intensity profiles, meaning that if the input pixel values (in the
|
|
|
|
* stencil) are obtained from a function of the form f(x,y) = a + b*x
|
|
|
|
* + c*y (a, b, c constants), then the computed pixel value is exactly
|
|
|
|
* the value of f(x,y) at the asked-for sampling location. The
|
|
|
|
* boundary condition which is emulated by VIPS throught the "extend"
|
|
|
|
* extension of the input image---this corresponds to the nearest
|
|
|
|
* neighbour abyss policy---does NOT make this resampler exact on
|
|
|
|
* linears at the boundary. It does, however, guarantee that no
|
|
|
|
* clamping is required even when resampled values are computed at
|
|
|
|
* positions outside of the extent of the input image (when
|
|
|
|
* extrapolation is required).
|
2009-01-20 22:32:05 +01:00
|
|
|
*
|
|
|
|
* ===================
|
|
|
|
* Nohalo is nonlinear
|
|
|
|
* ===================
|
|
|
|
*
|
|
|
|
* In particular, resampling a sum of images may not be the same as
|
2009-03-04 23:09:54 +01:00
|
|
|
* summing the resamples. (This occurs even without taking into account
|
2009-01-20 22:32:05 +01:00
|
|
|
* over and underflow issues: images can only take values within a
|
|
|
|
* banded range, and consequently no sampler is truly linear.)
|
|
|
|
*
|
|
|
|
* ====================
|
|
|
|
* Weaknesses of nohalo
|
|
|
|
* ====================
|
|
|
|
*
|
|
|
|
* In some cases, the first level nonlinear computation is wasted:
|
|
|
|
*
|
|
|
|
* If a region is bichromatic, the nonlinear component of the level 1
|
|
|
|
* nohalo is zero in the interior of the region, and consequently
|
|
|
|
* nohalo boils down to bilinear. For such images, either stick to
|
|
|
|
* bilinear, or use a higher level (quality) setting. (There is no
|
|
|
|
* real harm in using nohalo when it boils down to bilinear if one
|
|
|
|
* does not mind wasting cycles.)
|
|
|
|
*
|
|
|
|
* Low quality levels do NOT produce a continuously differentiable
|
|
|
|
* intensity surface:
|
|
|
|
*
|
|
|
|
* With a "finite" level is used (that is, in practice), the nohalo
|
|
|
|
* intensity surface is only continuous: there are gradient
|
|
|
|
* discontinuities because the "final interpolation step" is performed
|
|
|
|
* with bilinear. (Exception: if the "corner" image size convention is
|
|
|
|
* used and the magnification factor is 2, that is, if the resampled
|
|
|
|
* points sit exactly on the binary subdivided grid, then nohalo level
|
|
|
|
* 1 gives the same result as as level=infinity, and consequently the
|
|
|
|
* intensity surface can be treated as if smooth.)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif /*HAVE_CONFIG_H*/
|
|
|
|
#include <vips/intl.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <vips/vips.h>
|
|
|
|
#include <vips/internal.h>
|
|
|
|
|
|
|
|
#include "templates.h"
|
|
|
|
|
|
|
|
#ifndef restrict
|
|
|
|
#ifdef __restrict
|
|
|
|
#define restrict __restrict
|
|
|
|
#else
|
|
|
|
#ifdef __restrict__
|
|
|
|
#define restrict __restrict__
|
|
|
|
#else
|
|
|
|
#define restrict
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FAST_PSEUDO_FLOOR is a floor and floorf replacement which has been
|
|
|
|
* found to be faster on several linux boxes than the library
|
|
|
|
* version. It returns the floor of its argument unless the argument
|
|
|
|
* is a negative integer, in which case it returns one less than the
|
|
|
|
* floor. For example:
|
|
|
|
*
|
|
|
|
* FAST_PSEUDO_FLOOR(0.5) = 0
|
|
|
|
*
|
|
|
|
* FAST_PSEUDO_FLOOR(0.f) = 0
|
|
|
|
*
|
|
|
|
* FAST_PSEUDO_FLOOR(-.5) = -1
|
|
|
|
*
|
|
|
|
* as expected, but
|
|
|
|
*
|
|
|
|
* FAST_PSEUDO_FLOOR(-1.f) = -2
|
|
|
|
*
|
|
|
|
* The locations of the discontinuities of FAST_PSEUDO_FLOOR are the
|
|
|
|
* same as floor and floorf; it is just that at negative integers the
|
|
|
|
* function is discontinuous on the right instead of the left.
|
|
|
|
*/
|
|
|
|
#define FAST_PSEUDO_FLOOR(x) ( (int)(x) - ( (x) < 0. ) )
|
|
|
|
/*
|
|
|
|
* Alternative (if conditional move is fast and correctly identified
|
|
|
|
* by the compiler):
|
|
|
|
*
|
|
|
|
* #define FAST_PSEUDO_FLOOR(x) ( (x)>=0 ? (int)(x) : (int)(x)-1 )
|
|
|
|
*/
|
|
|
|
|
2009-01-26 23:45:25 +01:00
|
|
|
#define FAST_MIN(a,b) ( (a) <= (b) ? (a) : (b) )
|
|
|
|
|
2009-01-21 00:16:30 +01:00
|
|
|
#define VIPS_TYPE_INTERPOLATE_NOHALO \
|
|
|
|
(vips_interpolate_nohalo_get_type())
|
|
|
|
#define VIPS_INTERPOLATE_NOHALO( obj ) \
|
|
|
|
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
|
|
|
|
VIPS_TYPE_INTERPOLATE_NOHALO, VipsInterpolateNohalo ))
|
|
|
|
#define VIPS_INTERPOLATE_NOHALO_CLASS( klass ) \
|
|
|
|
(G_TYPE_CHECK_CLASS_CAST( (klass), \
|
|
|
|
VIPS_TYPE_INTERPOLATE_NOHALO, VipsInterpolateNohaloClass))
|
|
|
|
#define VIPS_IS_INTERPOLATE_NOHALO( obj ) \
|
|
|
|
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_NOHALO ))
|
|
|
|
#define VIPS_IS_INTERPOLATE_NOHALO_CLASS( klass ) \
|
|
|
|
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_NOHALO ))
|
|
|
|
#define VIPS_INTERPOLATE_NOHALO_GET_CLASS( obj ) \
|
|
|
|
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
|
|
|
|
VIPS_TYPE_INTERPOLATE_NOHALO, VipsInterpolateNohaloClass ))
|
|
|
|
|
|
|
|
typedef struct _VipsInterpolateNohalo {
|
|
|
|
VipsInterpolate parent_object;
|
|
|
|
|
|
|
|
} VipsInterpolateNohalo;
|
|
|
|
|
|
|
|
typedef struct _VipsInterpolateNohaloClass {
|
|
|
|
VipsInterpolateClass parent_class;
|
|
|
|
|
|
|
|
} VipsInterpolateNohaloClass;
|
|
|
|
|
2009-01-27 16:50:19 +01:00
|
|
|
static void inline
|
2009-03-04 23:20:58 +01:00
|
|
|
nohalo_sharp_level_1(
|
2009-03-04 23:09:54 +01:00
|
|
|
const double uno_two,
|
|
|
|
const double uno_thr,
|
|
|
|
const double dos_one,
|
|
|
|
const double dos_two,
|
2009-01-21 00:16:30 +01:00
|
|
|
const double dos_thr,
|
|
|
|
const double dos_fou,
|
2009-03-04 23:09:54 +01:00
|
|
|
const double tre_one,
|
2009-01-21 00:16:30 +01:00
|
|
|
const double tre_two,
|
|
|
|
const double tre_thr,
|
|
|
|
const double tre_fou,
|
|
|
|
const double qua_two,
|
|
|
|
const double qua_thr,
|
|
|
|
double *r1,
|
|
|
|
double *r2,
|
2009-01-23 13:03:11 +01:00
|
|
|
double *r3 )
|
2009-01-20 22:32:05 +01:00
|
|
|
{
|
2009-03-04 23:20:58 +01:00
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* This function calculates the missing three double density pixel
|
|
|
|
* values. The caller does bilinear interpolation on them and
|
|
|
|
* dos_two.
|
|
|
|
*/
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* THE STENCIL OF INPUT VALUES:
|
2009-01-23 11:56:12 +01:00
|
|
|
*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Nohalo's stencil is the same as, say, Catmull-Rom, with the
|
|
|
|
* exception that the four corner values are not used:
|
2009-01-23 11:56:12 +01:00
|
|
|
*
|
2009-03-04 23:09:54 +01:00
|
|
|
* (ix-1,iy-2) (ix,iy-2)
|
|
|
|
* = uno_two = uno_thr
|
2009-01-23 11:56:12 +01:00
|
|
|
*
|
2009-03-04 23:09:54 +01:00
|
|
|
* (ix-2,iy-1) (ix-1,iy-1) (ix,iy-1) (ix+1,iy-1)
|
|
|
|
* = dos_one = dos_two = dos_thr = dos_fou
|
2009-01-23 11:56:12 +01:00
|
|
|
*
|
2009-03-04 23:09:54 +01:00
|
|
|
* (ix-2,iy) (ix-1,iy) (ix,iy) (ix+1,iy)
|
|
|
|
* = tre_one = tre_two = tre_thr = tre_fou
|
2009-01-23 11:56:12 +01:00
|
|
|
*
|
2009-03-04 23:09:54 +01:00
|
|
|
* (ix-1,iy+1) (ix,iy+1)
|
|
|
|
* = qua_two = qua_thr
|
2009-01-23 11:56:12 +01:00
|
|
|
*
|
2009-03-04 23:09:54 +01:00
|
|
|
* The indices associated with the values shown above are in the
|
|
|
|
* case that the resampling point is closer to (ix-1,iy-1) than the
|
|
|
|
* other three central positions. Pointer arithmetic is used to
|
|
|
|
* implicitly reflect the input stencil in the other three cases,
|
|
|
|
* For example, if the sampling position is closer to dos_two (that
|
|
|
|
* is, if relative_x_is_rite = 1 but relative_y_is_down = 0 below),
|
|
|
|
* then dos_two corresponds to (ix,iy-1), dos_thr corresponds to
|
|
|
|
* (ix-1,iy-1) etc. Consequently, the three missing double density
|
|
|
|
* values are halfway between dos_two and dos_thr, halfway between
|
|
|
|
* dos_two and tre_two, and at the average of the four central
|
|
|
|
* positions.
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Computation of the nonlinear slopes: If two consecutive pixel
|
|
|
|
* value differences have the same sign, the smallest one (in
|
|
|
|
* absolute value) is taken to be the corresponding slope; if the
|
|
|
|
* two consecutive pixel value differences don't have the same sign,
|
2009-03-04 23:09:54 +01:00
|
|
|
* the corresponding slope is set to 0. (In other words, apply
|
|
|
|
* minmod to comsecutive slopes.)
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
/*
|
|
|
|
* Dos(s) horizontal differences:
|
|
|
|
*/
|
|
|
|
const double prem_dos = dos_two - dos_one;
|
|
|
|
const double deux_dos = dos_thr - dos_two;
|
|
|
|
const double troi_dos = dos_fou - dos_thr;
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
|
|
|
* Tre(s) horizontal differences:
|
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double prem_tre = tre_two - tre_one;
|
2009-01-23 11:56:12 +01:00
|
|
|
const double deux_tre = tre_thr - tre_two;
|
|
|
|
const double troi_tre = tre_fou - tre_thr;
|
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Two vertical differences:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double prem_two = dos_two - uno_two;
|
|
|
|
const double deux_two = tre_two - dos_two;
|
|
|
|
const double troi_two = qua_two - tre_two;
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
|
|
|
* Thr(ee) vertical differences:
|
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double prem_thr = dos_thr - uno_thr;
|
2009-01-23 11:56:12 +01:00
|
|
|
const double deux_thr = tre_thr - dos_thr;
|
|
|
|
const double troi_thr = qua_thr - tre_thr;
|
2009-03-04 23:09:54 +01:00
|
|
|
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Useful later:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double dos_two_plus_dos_thr = dos_two + dos_thr;
|
|
|
|
const double dos_two_plus_tre_two = dos_two + tre_two;
|
2009-01-21 00:16:30 +01:00
|
|
|
|
2009-03-04 23:09:54 +01:00
|
|
|
/*
|
|
|
|
* Dos:
|
|
|
|
*/
|
|
|
|
const double half_sign_prem_dos = prem_dos >= 0. ? .5 : -.5;
|
|
|
|
const double half_sign_deux_dos = deux_dos >= 0. ? .5 : -.5;
|
|
|
|
const double half_sign_troi_dos = troi_dos >= 0. ? .5 : -.5;
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
|
|
|
* Tre:
|
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_sign_prem_tre = prem_tre >= 0. ? .5 : -.5;
|
2009-01-26 23:45:25 +01:00
|
|
|
const double half_sign_deux_tre = deux_tre >= 0. ? .5 : -.5;
|
|
|
|
const double half_sign_troi_tre = troi_tre >= 0. ? .5 : -.5;
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Two:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_sign_prem_two = prem_two >= 0. ? .5 : -.5;
|
|
|
|
const double half_sign_deux_two = deux_two >= 0. ? .5 : -.5;
|
|
|
|
const double half_sign_troi_two = troi_two >= 0. ? .5 : -.5;
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
|
|
|
* Thr:
|
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_sign_prem_thr = prem_thr >= 0. ? .5 : -.5;
|
2009-01-26 23:45:25 +01:00
|
|
|
const double half_sign_deux_thr = deux_thr >= 0. ? .5 : -.5;
|
|
|
|
const double half_sign_troi_thr = troi_thr >= 0. ? .5 : -.5;
|
2009-03-04 23:09:54 +01:00
|
|
|
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Dos:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_abs_prem_dos = half_sign_prem_dos * prem_dos;
|
|
|
|
const double sign_dos_two_horizo = half_sign_prem_dos + half_sign_deux_dos;
|
|
|
|
const double half_abs_deux_dos = half_sign_deux_dos * deux_dos;
|
|
|
|
const double sign_dos_thr_horizo = half_sign_deux_dos + half_sign_troi_dos;
|
|
|
|
const double half_abs_troi_dos = half_sign_troi_dos * troi_dos;
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Two:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_abs_prem_two = half_sign_prem_two * prem_two;
|
|
|
|
const double sign_dos_two_vertic = half_sign_prem_two + half_sign_deux_two;
|
|
|
|
const double half_abs_deux_two = half_sign_deux_two * deux_two;
|
|
|
|
const double sign_tre_two_vertic = half_sign_deux_two + half_sign_troi_two;
|
|
|
|
const double half_abs_troi_two = half_sign_troi_two * troi_two;
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-01-26 23:45:25 +01:00
|
|
|
* Tre:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_abs_prem_tre = half_sign_prem_tre * prem_tre;
|
|
|
|
const double sign_tre_two_horizo = half_sign_prem_tre + half_sign_deux_tre;
|
2009-01-26 23:45:25 +01:00
|
|
|
const double half_abs_deux_tre = half_sign_deux_tre * deux_tre;
|
|
|
|
const double sign_tre_thr_horizo = half_sign_deux_tre + half_sign_troi_tre;
|
|
|
|
const double half_abs_troi_tre = half_sign_troi_tre * troi_tre;
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
|
|
|
* Thr:
|
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_abs_prem_thr = half_sign_prem_thr * prem_thr;
|
|
|
|
const double sign_dos_thr_vertic = half_sign_prem_thr + half_sign_deux_thr;
|
2009-01-26 23:45:25 +01:00
|
|
|
const double half_abs_deux_thr = half_sign_deux_thr * deux_thr;
|
|
|
|
const double sign_tre_thr_vertic = half_sign_deux_thr + half_sign_troi_thr;
|
|
|
|
const double half_abs_troi_thr = half_sign_troi_thr * troi_thr;
|
2009-03-04 23:09:54 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Useful later:
|
|
|
|
*/
|
|
|
|
const double tre_thr_minus_dos_two = deux_thr + deux_dos;
|
|
|
|
|
2009-01-26 23:45:25 +01:00
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Dos:
|
2009-01-26 23:45:25 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_size_dos_two_horizo =
|
|
|
|
FAST_MIN( half_abs_prem_dos, half_abs_deux_dos );
|
|
|
|
const double half_size_dos_thr_horizo =
|
|
|
|
FAST_MIN( half_abs_troi_dos, half_abs_deux_dos );
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Two:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_size_dos_two_vertic =
|
|
|
|
FAST_MIN( half_abs_prem_two, half_abs_deux_two );
|
|
|
|
const double half_size_tre_two_vertic =
|
|
|
|
FAST_MIN( half_abs_troi_two, half_abs_deux_two );
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
|
|
|
* Tre:
|
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_size_tre_two_horizo =
|
|
|
|
FAST_MIN( half_abs_prem_tre, half_abs_deux_tre );
|
2009-01-26 23:45:25 +01:00
|
|
|
const double half_size_tre_thr_horizo =
|
2009-03-04 23:09:54 +01:00
|
|
|
FAST_MIN( half_abs_troi_tre, half_abs_deux_tre );
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-01-26 23:45:25 +01:00
|
|
|
* Thr:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double half_size_dos_thr_vertic =
|
|
|
|
FAST_MIN( half_abs_prem_thr, half_abs_deux_thr );
|
2009-01-26 23:45:25 +01:00
|
|
|
const double half_size_tre_thr_vertic =
|
2009-03-04 23:09:54 +01:00
|
|
|
FAST_MIN( half_abs_troi_thr, half_abs_deux_thr );
|
2009-01-26 23:45:25 +01:00
|
|
|
|
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Compute the needed "right" (at the boundary between one input
|
2009-01-26 23:45:25 +01:00
|
|
|
* pixel areas) double resolution pixel value:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double two_times_dos_twothr =
|
|
|
|
dos_two_plus_dos_thr
|
2009-01-23 11:56:12 +01:00
|
|
|
+
|
2009-03-04 23:09:54 +01:00
|
|
|
sign_dos_two_horizo * half_size_dos_two_horizo
|
2009-01-26 23:45:25 +01:00
|
|
|
-
|
2009-03-04 23:09:54 +01:00
|
|
|
sign_dos_thr_horizo * half_size_dos_thr_horizo;
|
2009-01-26 23:45:25 +01:00
|
|
|
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-01-26 23:45:25 +01:00
|
|
|
* Compute the needed "down" double resolution pixel value:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double two_times_dostre_two =
|
|
|
|
dos_two_plus_tre_two
|
2009-01-23 11:56:12 +01:00
|
|
|
+
|
2009-03-04 23:09:54 +01:00
|
|
|
sign_dos_two_vertic * half_size_dos_two_vertic
|
2009-01-26 23:45:25 +01:00
|
|
|
-
|
2009-03-04 23:09:54 +01:00
|
|
|
sign_tre_two_vertic * half_size_tre_two_vertic;
|
2009-01-21 00:16:30 +01:00
|
|
|
|
2009-01-23 11:56:12 +01:00
|
|
|
/*
|
2009-03-04 23:09:54 +01:00
|
|
|
* Compute the "diagonal" (at the boundary between thrr input
|
2009-01-26 23:45:25 +01:00
|
|
|
* pixel areas) double resolution pixel value:
|
2009-01-23 11:56:12 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double four_times_dostre_twothr =
|
|
|
|
tre_thr_minus_dos_two
|
2009-01-26 23:45:25 +01:00
|
|
|
+
|
2009-03-04 23:09:54 +01:00
|
|
|
sign_tre_two_horizo * half_size_tre_two_horizo
|
2009-01-26 23:45:25 +01:00
|
|
|
-
|
2009-03-04 23:09:54 +01:00
|
|
|
sign_tre_thr_horizo * half_size_tre_thr_horizo
|
2009-01-23 11:56:12 +01:00
|
|
|
+
|
2009-03-04 23:09:54 +01:00
|
|
|
sign_dos_thr_vertic * half_size_dos_thr_vertic
|
2009-01-26 23:45:25 +01:00
|
|
|
-
|
2009-03-04 23:09:54 +01:00
|
|
|
sign_tre_thr_vertic * half_size_tre_thr_vertic
|
2009-01-23 11:56:12 +01:00
|
|
|
+
|
2009-03-04 23:09:54 +01:00
|
|
|
two_times_dos_twothr
|
2009-01-26 23:45:25 +01:00
|
|
|
+
|
2009-03-04 23:09:54 +01:00
|
|
|
two_times_dostre_two;
|
2009-01-21 00:16:30 +01:00
|
|
|
|
2009-03-04 23:09:54 +01:00
|
|
|
/*
|
|
|
|
* Return the newly computed double density values:
|
|
|
|
*/
|
|
|
|
*r1 = two_times_dos_twothr;
|
|
|
|
*r2 = two_times_dostre_two;
|
|
|
|
*r3 = four_times_dostre_twothr;
|
2009-01-20 22:32:05 +01:00
|
|
|
}
|
|
|
|
|
2009-01-27 18:09:40 +01:00
|
|
|
/* Call nohalo_sharp_level_1 with an interpolator as a parameter.
|
2009-03-04 23:20:58 +01:00
|
|
|
* It'd be nice to do this with templates somehow :-( but I can't see a
|
2009-01-27 18:09:40 +01:00
|
|
|
* clean way to do it.
|
2009-01-27 16:03:08 +01:00
|
|
|
*/
|
2009-02-06 04:33:16 +01:00
|
|
|
#define NOHALO_SHARP_LEVEL_1_INTER( inter ) \
|
|
|
|
template <typename T> static void inline \
|
2009-03-04 23:09:54 +01:00
|
|
|
nohalo_sharp_level_1_ ## inter( PEL *pout, \
|
|
|
|
const PEL *pin, \
|
|
|
|
const int bands, \
|
|
|
|
const int lskip, \
|
|
|
|
const double relative_x, \
|
|
|
|
const double relative_y ) \
|
2009-02-06 04:33:16 +01:00
|
|
|
{ \
|
|
|
|
T* restrict out = (T *) pout; \
|
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
const int relative_x_is_rite = ( relative_x >= 0. ); \
|
|
|
|
const int relative_y_is_down = ( relative_y >= 0. ); \
|
|
|
|
\
|
|
|
|
const int sign_of_relative_x = 2 * relative_x_is_rite - 1; \
|
|
|
|
const int sign_of_relative_y = 2 * relative_y_is_down - 1; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
|
|
|
const int corner_reflection_shift = \
|
2009-03-04 23:09:54 +01:00
|
|
|
relative_x_is_rite * bands + relative_y_is_down * lskip; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
const T* restrict in = ( (T *) pin ) + corner_reflection_shift; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
const int shift_1_pixel = sign_of_relative_x * bands; \
|
|
|
|
const int shift_1_row = sign_of_relative_y * lskip; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
const double w = ( 2 * sign_of_relative_x ) * relative_x; \
|
|
|
|
const double z = ( 2 * sign_of_relative_y ) * relative_y; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
const int uno_two_shift = shift_1_row; \
|
|
|
|
const int uno_thr_shift = shift_1_row - shift_1_pixel; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
const int dos_one_shift = shift_1_pixel; \
|
|
|
|
const int dos_two_shift = 0; \
|
|
|
|
const int dos_thr_shift = -shift_1_pixel; \
|
|
|
|
const int dos_fou_shift = -2 * shift_1_pixel; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
const int tre_one_shift = dos_one_shift - shift_1_row; \
|
|
|
|
const int tre_two_shift = -shift_1_row; \
|
|
|
|
const int tre_thr_shift = dos_thr_shift - shift_1_row; \
|
|
|
|
const int tre_fou_shift = dos_fou_shift - shift_1_row; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
const int qua_two_shift = tre_two_shift - shift_1_row; \
|
|
|
|
const int qua_thr_shift = tre_thr_shift - shift_1_row; \
|
|
|
|
\
|
|
|
|
const double x = 1. - w; \
|
|
|
|
const double w_times_z = w * z; \
|
|
|
|
const double x_times_z = x * z; \
|
|
|
|
const double w_times_y_over_2 = .5 * ( w - w_times_z ); \
|
|
|
|
const double x_times_z_over_2 = .5 * x_times_z; \
|
|
|
|
const double x_times_y_over_4 = .25 * ( x - x_times_z ); \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
for( int band = 0; band < bands; band++ ) { \
|
|
|
|
double two_times_dos_twothr; \
|
|
|
|
double two_times_dostre_two; \
|
|
|
|
double four_times_dostre_twothr; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
const double dos_two = in[dos_two_shift]; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
nohalo_sharp_level_1( in[uno_two_shift], in[uno_thr_shift], \
|
|
|
|
in[dos_one_shift], dos_two, \
|
|
|
|
in[dos_thr_shift], in[dos_fou_shift], \
|
|
|
|
in[tre_one_shift], in[tre_two_shift], \
|
|
|
|
in[tre_thr_shift], in[tre_fou_shift], \
|
|
|
|
in[qua_two_shift], in[qua_thr_shift], \
|
|
|
|
&two_times_dos_twothr, \
|
|
|
|
&two_times_dostre_two, \
|
|
|
|
&four_times_dostre_twothr ); \
|
|
|
|
\
|
|
|
|
in += 1; \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
|
|
|
const T result = bilinear_ ## inter<T>( \
|
|
|
|
w_times_z, \
|
|
|
|
x_times_z_over_2, \
|
|
|
|
w_times_y_over_2, \
|
|
|
|
x_times_y_over_4, \
|
2009-03-04 23:09:54 +01:00
|
|
|
dos_two, \
|
|
|
|
two_times_dos_twothr, \
|
|
|
|
two_times_dostre_two, \
|
|
|
|
four_times_dostre_twothr ); \
|
2009-02-06 04:33:16 +01:00
|
|
|
\
|
2009-03-04 23:09:54 +01:00
|
|
|
out[band] = result; \
|
2009-02-06 04:33:16 +01:00
|
|
|
} \
|
2009-03-04 23:20:58 +01:00
|
|
|
}
|
2009-01-27 18:09:40 +01:00
|
|
|
|
|
|
|
NOHALO_SHARP_LEVEL_1_INTER( float )
|
|
|
|
NOHALO_SHARP_LEVEL_1_INTER( signed )
|
|
|
|
NOHALO_SHARP_LEVEL_1_INTER( unsigned )
|
2009-01-20 22:32:05 +01:00
|
|
|
|
2009-01-23 13:03:11 +01:00
|
|
|
/* We need C linkage for this.
|
|
|
|
*/
|
|
|
|
extern "C" {
|
2009-03-04 23:20:58 +01:00
|
|
|
G_DEFINE_TYPE( VipsInterpolateNohalo, vips_interpolate_nohalo,
|
2009-01-23 13:03:11 +01:00
|
|
|
VIPS_TYPE_INTERPOLATE );
|
|
|
|
}
|
|
|
|
|
2009-01-23 11:56:12 +01:00
|
|
|
static void
|
2009-03-04 23:20:58 +01:00
|
|
|
vips_interpolate_nohalo_interpolate( VipsInterpolate *interpolate,
|
2009-03-04 23:09:54 +01:00
|
|
|
PEL *out,
|
|
|
|
REGION *in,
|
|
|
|
double absolute_x,
|
|
|
|
double absolute_y )
|
2009-01-21 00:16:30 +01:00
|
|
|
{
|
2009-01-20 22:32:05 +01:00
|
|
|
/*
|
|
|
|
* floor's surrogate FAST_PSEUDO_FLOOR is used to make sure that the
|
|
|
|
* transition through 0 is smooth. If it is known that absolute_x
|
2009-03-04 23:09:54 +01:00
|
|
|
* and absolute_y will never be less than 0, plain cast---that is,
|
|
|
|
* const int ix = absolute_x---should be used instead. Actually,
|
|
|
|
* any function which agrees with floor for non-integer values, and
|
2009-01-20 22:32:05 +01:00
|
|
|
* picks one of the two possibilities for integer values, can be
|
2009-03-04 23:09:54 +01:00
|
|
|
* used. FAST_PSEUDO_FLOOR fits the bill.
|
2009-01-20 22:32:05 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const int ix = FAST_PSEUDO_FLOOR (absolute_x);
|
|
|
|
const int iy = FAST_PSEUDO_FLOOR (absolute_y);
|
2009-01-20 22:32:05 +01:00
|
|
|
|
2009-03-04 23:09:54 +01:00
|
|
|
/*
|
|
|
|
* Move the pointer to (the first band of) the top/left pixel
|
|
|
|
* of the 2x2 group of pixel centers which contains the
|
|
|
|
* sampling location in its convex hull:
|
|
|
|
*/
|
|
|
|
const PEL * restrict p = (PEL *) IM_REGION_ADDR( in, ix, iy );
|
2009-02-06 04:25:57 +01:00
|
|
|
|
2009-03-04 23:09:54 +01:00
|
|
|
/*
|
2009-03-05 13:18:04 +01:00
|
|
|
* VIPS versions of Nicolas's pixel addressing values. Double bands for
|
|
|
|
* complex images.
|
2009-03-04 23:09:54 +01:00
|
|
|
*/
|
2009-03-05 13:18:04 +01:00
|
|
|
const int bands = in->im->Bands * (im_iscomplex( in->im ) ? 2 : 1);
|
2009-03-04 23:09:54 +01:00
|
|
|
const int lskip = IM_REGION_LSKIP( in ) / IM_IMAGE_SIZEOF_ELEMENT( in->im );
|
2009-02-06 04:25:57 +01:00
|
|
|
|
2009-01-20 22:32:05 +01:00
|
|
|
/*
|
|
|
|
* x is the x-coordinate of the sampling point relative to the
|
2009-03-04 23:09:54 +01:00
|
|
|
* position of the center of the convex hull of the 2x2 block of
|
|
|
|
* closest pixels. Similarly for y. Range of values: [-.5,.5).
|
2009-01-20 22:32:05 +01:00
|
|
|
*/
|
2009-03-04 23:09:54 +01:00
|
|
|
const double relative_x = absolute_x - ix - .5;
|
|
|
|
const double relative_y = absolute_y - iy - .5;
|
2009-01-20 22:32:05 +01:00
|
|
|
|
2009-03-04 23:09:54 +01:00
|
|
|
#define CALL( T, inter ) \
|
2009-02-06 04:33:16 +01:00
|
|
|
nohalo_sharp_level_1_ ## inter<T>( out, \
|
|
|
|
p, \
|
|
|
|
bands, \
|
|
|
|
lskip, \
|
|
|
|
relative_x, \
|
2009-02-06 04:25:57 +01:00
|
|
|
relative_y );
|
2009-01-26 11:23:18 +01:00
|
|
|
|
2009-01-23 11:56:12 +01:00
|
|
|
switch( in->im->BandFmt ) {
|
|
|
|
case IM_BANDFMT_UCHAR:
|
2009-03-04 23:20:58 +01:00
|
|
|
CALL( unsigned char, unsigned );
|
2009-01-23 11:56:12 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IM_BANDFMT_CHAR:
|
2009-03-04 23:20:58 +01:00
|
|
|
CALL( signed char, signed );
|
2009-01-23 11:56:12 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IM_BANDFMT_USHORT:
|
2009-03-04 23:20:58 +01:00
|
|
|
CALL( unsigned short, unsigned );
|
2009-01-23 11:56:12 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IM_BANDFMT_SHORT:
|
2009-03-04 23:20:58 +01:00
|
|
|
CALL( signed short, signed );
|
2009-01-23 11:56:12 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IM_BANDFMT_UINT:
|
2009-03-04 23:20:58 +01:00
|
|
|
CALL( unsigned int, unsigned );
|
2009-01-23 11:56:12 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IM_BANDFMT_INT:
|
2009-03-04 23:20:58 +01:00
|
|
|
CALL( signed int, signed );
|
2009-01-23 11:56:12 +01:00
|
|
|
break;
|
|
|
|
|
2009-03-05 13:18:04 +01:00
|
|
|
/* Complex images handled by doubling of bands, see above.
|
|
|
|
*/
|
2009-01-23 11:56:12 +01:00
|
|
|
case IM_BANDFMT_FLOAT:
|
|
|
|
case IM_BANDFMT_COMPLEX:
|
2009-03-05 13:01:49 +01:00
|
|
|
CALL( float, float );
|
2009-01-23 11:56:12 +01:00
|
|
|
break;
|
|
|
|
|
2009-03-05 13:18:04 +01:00
|
|
|
case IM_BANDFMT_DOUBLE:
|
2009-01-23 11:56:12 +01:00
|
|
|
case IM_BANDFMT_DPCOMPLEX:
|
2009-03-05 13:01:49 +01:00
|
|
|
CALL( double, float );
|
2009-01-23 11:56:12 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2009-01-27 16:03:08 +01:00
|
|
|
g_assert( 0 );
|
2009-01-23 11:56:12 +01:00
|
|
|
break;
|
|
|
|
}
|
2009-01-21 00:16:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vips_interpolate_nohalo_class_init( VipsInterpolateNohaloClass *klass )
|
|
|
|
{
|
|
|
|
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( klass );
|
2009-03-04 23:20:58 +01:00
|
|
|
VipsInterpolateClass *interpolate_class =
|
2009-01-21 00:16:30 +01:00
|
|
|
VIPS_INTERPOLATE_CLASS( klass );
|
|
|
|
|
|
|
|
object_class->nickname = "nohalo";
|
2009-03-04 23:09:54 +01:00
|
|
|
object_class->description = _( "Edge-enhancing bilinear" );
|
2009-01-21 00:16:30 +01:00
|
|
|
|
2009-03-04 23:20:58 +01:00
|
|
|
interpolate_class->interpolate =
|
2009-01-21 00:16:30 +01:00
|
|
|
vips_interpolate_nohalo_interpolate;
|
2009-03-04 23:09:54 +01:00
|
|
|
interpolate_class->window_size = 4;
|
2009-01-20 22:32:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2009-01-21 00:16:30 +01:00
|
|
|
vips_interpolate_nohalo_init( VipsInterpolateNohalo *nohalo )
|
2009-01-20 22:32:05 +01:00
|
|
|
{
|
|
|
|
}
|