Merge remote-tracking branch 'origin/7.40'

Conflicts:
	TODO
This commit is contained in:
John Cupitt 2014-08-09 17:03:43 +01:00
commit b25dcc9ebe
22 changed files with 503 additions and 117 deletions

View File

@ -7,7 +7,12 @@
- limit n_thr on tiny images
- don't exit() on memleak detected, just warn
- add "autocrop" option to openslide load
- argh fix affine, again
- argh fix affine, again, there were sometimes black bars with nohalo and the
vips8 interface
- pngsave in interlaced mode makes a copy of the image, so it's always seq
- vipsthumbnail shrinks to 1/2 window_size: faster and better quality
- vipsthumbnail defaults to bicubic + nosharpen
- better rounding behaviour for fixed-point bicubic reduces noise
4/7/14 started 7.40.4
- fix vips_rawsave_fd(), thanks aferrero2707

16
TODO
View File

@ -1,14 +1,3 @@
- affine makes black lines with interp window_offset > 1, see nohalo
- experiment with size down to two sizes above in vipsthumbnail
how does this affect speed and sharpness?
- vipsthumbnail -t plus large -s seems to fail?
- bicubic adds noise to 255/255/255, why? try babe.jpg background
- vips_object_unref_outputs() needs docs ... bindings will need it
@ -16,6 +5,9 @@
finally finish --disable-deprecated option
- copy-paste blog post about writing vips8 operations into 'extending'
- vips_init() has a deprecation warning, bizarre
- check and fix up docs
@ -27,7 +19,7 @@
need better vipsobject docs
gtk-doc now used markdown, phew, use this to expand some sections
gtk-doc now uses markdown, phew, use this to expand some sections

View File

@ -17,7 +17,7 @@
<refsect1 id="binding-goi">
<title>Binding and gobject-introspection</title>
<para>
Stuff about goi.
Write this section once the vips8 Python binding is done.
</para>
</refsect1>

View File

@ -17,8 +17,22 @@
<refsect1 id="extending-subclassing">
<title>Adding operations to VIPS</title>
<para>
all about subclassing.
All about subclassing. Copy-paste from the blog post about extending
vips8.
</para>
<para>
If you are writing a language binding, you won't need these. Instead, make
a new operation with vips_operation_new() (all it does is look up the
operation by name with vips_type_find(), then call g_object_new() for you),
then use vips_argument_map() and friends to loop over the operation's
arguments setting them. Once you have set all arguments, use
vips_cache_operation_build() to look up the operation in the cache and
either build or dup it. If something goes wrong, you'll need to use
vips_object_unref_outputs() and g_object_unref() to free the
partially-built object.
</para>
</refsect1>
</refentry>

View File

@ -4,14 +4,14 @@
]>
<refentry id="using-from-c">
<refmeta>
<refentrytitle>Using VIPS from C</refentrytitle>
<refentrytitle>VIPS from C</refentrytitle>
<manvolnum>3</manvolnum>
<refmiscinfo>VIPS Library</refmiscinfo>
</refmeta>
<refnamediv>
<refname>Using VIPS</refname>
<refpurpose>How to use the VIPS library</refpurpose>
<refpurpose>How to use the VIPS library from C</refpurpose>
</refnamediv>
<refsect1 id="using-C">

View File

@ -11,7 +11,7 @@
<refnamediv>
<refname>Using VIPS</refname>
<refpurpose>How to use the VIPS library</refpurpose>
<refpurpose>How to use the VIPS library from the command-line</refpurpose>
</refnamediv>
<refsect1 id="using-command-line">

View File

@ -262,6 +262,7 @@ vips_spcor_correlation( VipsCorrelation *correlation,
default:
g_assert( 0 );
return;
}
c2 = sqrt( sum2 );

View File

@ -827,6 +827,11 @@ scanline_write( COLR *scanline, int width, FILE *fp )
for( i = 0; i < 4; i++ ) {
for( j = 0; j < width; ) {
/* Not needed, but keeps gcc used-before-set wsrning
* quiet.
*/
cnt = 1;
/* Set beg / cnt to the start and length of the next
* run longer than MINRUN.
*/

View File

@ -1053,7 +1053,8 @@ parse_header( ReadTiff *rtiff, VipsImage *out )
if( tfexists( rtiff->tiff, TIFFTAG_PLANARCONFIG ) ) {
int v;
tfget16( rtiff->tiff, TIFFTAG_PLANARCONFIG, &v );
if( !tfget16( rtiff->tiff, TIFFTAG_PLANARCONFIG, &v ) )
return( -1 );
if( v == PLANARCONFIG_SEPARATE )
rtiff->separate = TRUE;
}

View File

@ -58,6 +58,7 @@
* - attach IPCT data (app13), thanks Gary
* 2/10/13 Lovell Fuller
* - add optimize_coding parameter
* - add progressive mode
* 12/11/13
* - add "strip" option to remove all metadata
* 13/11/13

View File

@ -789,10 +789,18 @@ write_vips( Write *write, int compress, int interlace )
if( setjmp( png_jmpbuf( write->pPng ) ) )
return( -1 );
/* Check input image.
/* Check input image. If we are writing interlaced, we need to make 7
* passes over the image. We advertise ourselves as seq, so to ensure
* we only suck once from upstream, switch to WIO.
*/
if( vips_image_pio_input( in ) )
return( -1 );
if( interlace ) {
if( vips_image_wio_input( in ) )
return( -1 );
}
else {
if( vips_image_pio_input( in ) )
return( -1 );
}
if( compress < 0 || compress > 9 ) {
vips_error( "vips2png",
"%s", _( "compress should be in [0,9]" ) );
@ -825,7 +833,7 @@ write_vips( Write *write, int compress, int interlace )
in->Xsize, in->Ysize, bit_depth, color_type, interlace_type,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT );
/* Set resolution. libpnbg uses pixels per meter.
/* Set resolution. libpng uses pixels per meter.
*/
png_set_pHYs( write->pPng, write->pInfo,
VIPS_RINT( in->Xres * 1000 ), VIPS_RINT( in->Yres * 1000 ),
@ -866,7 +874,7 @@ write_vips( Write *write, int compress, int interlace )
/* Write data.
*/
for( i = 0; i < nb_passes; i++ )
if( vips_sink_disc( write->in, write_png_block, write ) )
if( vips_sink_disc( in, write_png_block, write ) )
return( -1 );
/* The setjmp() was held by our background writer: reset it.

View File

@ -584,9 +584,22 @@ vips__buffer_init_cb( VipsBufferThread *buffer_thread )
{
/* This is a mem leak, not catastrophic.
*/
/* Threads (including the main thread) must call
* vips_thread_shutdown() before exiting. Check that they have.
*
* We can't save automatically, because the shutdown order is
* important. We must free all memory before saving the thread
* profile, for example.
*
* We can't do the freeing in this callback since GPrivate has already
* stopped working.
*/
vips_warn( "VipsBuffer",
_( "vips_thread_shutdown() not called for thread %p" ),
g_thread_self() );
_( "vips_thread_shutdown() not called for thread %p, see %s" ),
g_thread_self(),
"https://github.com/jcupitt/ruby-vips/issues/55" );
}
/* Init the buffer cache system.

View File

@ -2781,6 +2781,24 @@ vips_image_wio_input( VipsImage *image )
*/
g_object_unref( t1 );
/* We need to zap any start/gen/stop callbacks. If we don't,
* calling vips_region_prepare_to() later to read from this
* image will fail, since it will think it need to create the
* image, not read from it.
*/
image->start_fn = NULL;
image->generate_fn = NULL;
image->stop_fn = NULL;
image->client1 = NULL;
image->client2 = NULL;
/* ... and that may confuse any regions which are trying to
* generate from this image.
*/
if( image->regions )
vips_warn( "vips_image_wio_input", "%s",
"rewinding image with active regions" );
break;
case VIPS_IMAGE_OPENIN:

View File

@ -127,6 +127,9 @@ vips_get_argv0( void )
* it may not be able to get hold of @argv0 and VIPS may therefore be unable
* to find its data files. It is much better to call this function yourself.
*
* vips_init() is a macro, since it tries to check binary compatibility
* between the caller and the library.
*
* vips_init() does approximately the following:
*
* <itemizedlist>
@ -157,14 +160,14 @@ vips_get_argv0( void )
* Example:
*
* |[
* int main( int argc, char **argv )
* int main (int argc, char **argv)
* {
* if( vips_init( argv[0] ) )
* vips_error_exit( "unable to start VIPS" );
* if (vips_init (argv[0]))
* vips_error_exit ("unable to start VIPS");
*
* vips_shutdown();
* vips_shutdown ();
*
* return( 0 );
* return 0;
* }
* ]|
*
@ -174,7 +177,7 @@ vips_get_argv0( void )
* Returns: 0 on success, -1 otherwise
*/
/* vips_init() is actually a macro which checks library and application
/* vips_init() is a macro which checks library and application
* compatibility before calling vips__init().
*/

View File

@ -67,16 +67,93 @@
* following major features:
*
* <emphasis>Functional class creation</emphasis> Vips objects have a very
* regular
* lifecycle: initialise, build, use, destroy. They behave rather like
* regular lifecycle: initialise, build, use, destroy. They behave rather like
* function calls and are free of side-effects.
*
* <emphasis>Run-time introspection</emphasis> Vips objects can be fully
* introspected at
* run-time. There is not need for separate source-code analysis.
* introspected at run-time. There is not need for separate source-code
* analysis.
*
* <emphasis>Command-line interface</emphasis> Any vips object can be run from
* the command-line with the `vips` driver program.
*
* ## The #VipsObject lifecycle
*
* #VipsObject s have a strictly defined lifecycle, split broadly as construct
* and then use. In detail, the stages are:
*
* 1. g_object_new(). The #VipsObject is created with g_object_new(). Objects
* in this state are blank slates and need to have their various parameters
* set.
*
* 2. g_object_set(). You loop over the #VipsArgument that the object has
* defined with vips_argument_map(). Arguments have a set of flags attached to
* them for required, optional, input, output, type, and so on. You must set
* all required arguments.
*
* 3. vips_object_build(). Call this to construct the object and get it ready
* for use. Building an object happens in four stages, see below.
*
* 4. g_object_get(). The object has now been built. You can read out any
* computed values.
*
* 5. g_object_unref(). When you are done with an object, you can unref it.
* See the section on reference counting for an explanation of the convention
* that #VipsObject uses. When the last ref to an object is released, the
* object is closed. Objects close in three stages, see below.
*
* The stages inside vips_object_build() are:
*
* 1. Chain up through the object's @build class methods. At each stage,
* each object does any initial setup and checking, then chains up to its
* superclass.
*
* 2. The innermost @build method in #VipsObject itself checks that all input
* arguments have been set and then returns.
*
* 3. All object @build methods now finish executing, from innermost to
* outermost. They know all input arguments have been checked and supplied, so
* now they set all output arguments.
*
* 4. vips_object_build() finishes the process by checking that all output
* objects have been set, and then triggering the #VipsObject::postbuild
* signal. #VipsObject::postbuild only runs if the object has constructed
* successfuly.
*
* And the stages inside close are:
*
* 1. #VipsObject::preclose. This is emitted at the start of
* the #VipsObject dispose. The object is still functioning.
*
* 2. #VipsObject::close. This runs just after all #VipsArgument held by
* the object have been released.
*
* 3. #VipsObject::postclose. This runs right at the end. The object
* pointer is still valid, but nothing else is.
*
* ## #VipsArgument
*
* libvips has a simple mechanism for automating at least some aspects of
* %GObject properties. You add a set of macros to your _class_init() which
* describe the arguments, and set the get and set functions to the vips ones.
*
* See <link linkend="extending">extending</link> for a complete example.
*
* ## The #VipsObject reference counting convention
*
* #VipsObject has a set of conventions to simplify reference counting.
*
* 1. All input %GObject have a ref added to them, owned by the object. When a
* #VipsObject is unreffed, all of these refs to input objects are
* automatically dropped.
*
* 2. All output %GObject hold a ref to the object. When a %GObject which is an
* output of a #VipsObject is disposed, it must drop this reference.
* #VipsObject which are outputs of other #VipsObject will do this
* automatically.
*
* See #VipsOperation for an example of #VipsObject reference counting.
*
*/
/**
@ -1502,6 +1579,13 @@ vips_object_class_init( VipsObjectClass *class )
G_STRUCT_OFFSET( VipsObject, description ),
"" );
/**
* VipsObject::postbuild:
* @object: the object that has been built
*
* The ::postbuild signal is emitted once just after successful object
* construction. Return non-zero to cause object construction to fail.
*/
vips_object_signals[SIG_POSTBUILD] = g_signal_new( "postbuild",
G_TYPE_FROM_CLASS( class ),
G_SIGNAL_RUN_LAST,
@ -1509,6 +1593,14 @@ vips_object_class_init( VipsObjectClass *class )
NULL, NULL,
vips_INT__VOID,
G_TYPE_INT, 0 );
/**
* VipsObject::preclose:
* @object: the object that is to close
*
* The ::preclose signal is emitted once just before object close
* starts. The oject is still alive.
*/
vips_object_signals[SIG_PRECLOSE] = g_signal_new( "preclose",
G_TYPE_FROM_CLASS( class ),
G_SIGNAL_RUN_LAST,
@ -1516,6 +1608,14 @@ vips_object_class_init( VipsObjectClass *class )
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0 );
/**
* VipsObject::close:
* @object: the object that is closing
*
* The ::close signal is emitted once during object close. The object
* is dying and may not work.
*/
vips_object_signals[SIG_CLOSE] = g_signal_new( "close",
G_TYPE_FROM_CLASS( class ),
G_SIGNAL_RUN_LAST,
@ -1523,6 +1623,14 @@ vips_object_class_init( VipsObjectClass *class )
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0 );
/**
* VipsObject::postclose:
* @object: the object that has closed
*
* The ::postclose signal is emitted once after object close. The
* object pointer is still valid, but nothing else.
*/
vips_object_signals[SIG_POSTCLOSE] = g_signal_new( "postclose",
G_TYPE_FROM_CLASS( class ),
G_SIGNAL_RUN_LAST,

View File

@ -60,8 +60,7 @@
* on #VipsObject to provide the introspection and command-line interface to
* libvips.
*
* It also maintains a cache of recent operations. You can tune the cache
* behaviour in various ways, see vips_cache_set_max() and friends.
* It also maintains a cache of recent operations. See below.
*
* vips_call(), vips_call_split() and vips_call_split_option_string() are used
* by vips to implement the C API. They can execute any #VipsOperation,
@ -86,31 +85,75 @@
* }
* ]|
*
* If you are writing a language binding, you won't need these. Instead, make
* a new operation with vips_operation_new() (all it does is look up the
* operation by name with vips_type_find(), then call g_object_new() for you),
* then use vips_argument_map() and friends to loop over the operation's
* arguments setting them. Once you have set all arguments, use
* vips_cache_operation_build() to look up the operation in the cache and
* either build or dup it. If something goes wrong, you'll need to use
* vips_object_unref_outputs() and g_object_unref(). :wq
*
*
* Use vips_call() to call any vips operation from C. If you want to search
* for operations, see what arguments they need, and test argument
* properties, see
* <link linkend="libvips-object">object</link>. Each operation also has a
* wrapper function, of course, to give type safety for required arguments.
*
* vips_call_split() lets you run an operation with the optional and required
* arguments split into separate lists. vips_call_split_option_string() lets
* you set options from strings as well.
*
* Use vips_call_argv() to run any vips operation from a command-line style
* argc/argv array.
* argc/argv array. This is the thing used by the vips main program to
* implement the vips command-line interface.
*
* ## #VipsOperation and reference counting
*
* After calling a #VipsOperation you are responsible for unreffing any output
* objects. For example, consider:
*
* |[
* VipsImage *im = ...;
* VipsImage *t1;
*
* if (vips_invert (im, &t1, NULL))
* error ..
* ]|
*
* This will invert @im and return it as a new #VipsImage, @t1. As the caller
* of vips_invert(), you are responsible for @t1 and must unref it when you no
* longer need it. If vips_invert() fails, no @t1 is returned and you don't
* need to do anything.
*
* Consider running two operations, one after the other. You could write:
*
* |[
* VipsImage *im = ...;
* VipsImage *t1, *t2;
*
* if (vips_invert (im, &t1, NULL)) {
* g_object_unref (im);
* return -1;
* }
* g_object_unref (im);
*
* if (vips_flip (t1, &t2, VIPS_DIRECTION_HORIZONTAL, NULL)) {
* g_object_unref (t1);
* return -1;
* }
* g_object_unref (t1);
* ]|
*
* This is correct, but rather long-winded. libvips provides a handy thing to
* make a vector of auto-freeing object references. You can write this as:
*
* |[
* VipsObject *parent = ...;
* VipsImage *im = ...;
* VipsImage *t = (VipsImage **) vips_object_local_array (parent, 2);
*
* if (vips_invert (im, &t[0], NULL) ||
* vips_flip (t[0], &t[1], VIPS_DIRECTION_HORIZONTAL, NULL))
* return -1;
* ]|
*
* where @parent is some enclosing object which will be unreffed when this
* task is complete. vips_object_local_array() makes an array of #VipsObject
* (or #VipsImage, in this case) where when @parent is freed, all non-NULL
* #VipsObject in the array are also unreffed.
*
* ## The #VipsOperation cache
*
* Because all #VipsObject are immutable, they can be cached. The cache is
* very simple to use: instead of calling vips_object_build(), instead call
* vips_cache_operation_build(). This function calculates a hash from the
* operations's input arguments and looks it up in table of all recent
* operations. If there's a hit, the new operation is unreffed, the old
* operation reffed, and the old operation returned in place of the new one.
*
* The cache size is controlled with vips_cache_set_max() and friends.
*/
/**

View File

@ -55,6 +55,16 @@
#include <vips/internal.h>
#include <vips/debug.h>
/**
* SECTION: type
* @short_description: basic types
* @stability: Stable
* @see_also: <link linkend="libvips-operation">operation</link>
* @include: vips/vips.h
*
* A selection of GType defintions used by VIPS.
*/
/* A very simple boxed type for testing. Just an int.
*/

View File

@ -1599,8 +1599,7 @@ vips_enum_from_nick( const char *domain, GType type, const char *nick )
#define BIGBUF (10000)
/* Scan @buf for the first "%ns" (eg. "%12s") and substitute the
* lowest-numbered one for @sub. @buf is @len bytes
* in size.
* lowest-numbered one for @sub. @buf is @len bytes in size.
*
* If there are no %ns, use the first %s.
*/
@ -1620,6 +1619,7 @@ vips__substitute( const char *domain, char *buf, size_t len, char *sub )
lowest_n = -1;
sub_start = NULL;
sub_end = NULL;
for( p = buf; (p = strchr( p, '%' )); p++ )
if( isdigit( p[1] ) ) {
char *q;

View File

@ -88,6 +88,21 @@
#include <vips/internal.h>
#include <vips/debug.h>
/**
* SECTION: vips
* @short_description: startup, shutdown, version
* @stability: Stable
* @see_also: <link linkend="libvips-operation">operation</link>
* @include: vips/vips.h
*
* Start VIPS up, shut VIPS down, get version information, relocation.
*
* VIPS is a relocatable package, meaning you can move the directory tree you
* compiled it to at runtime and it will still be able to find all data files.
* This is required for OS X and Windows, but slightly unusual in the Unix
* world. See vips_init() and vips_guess_prefix().
*/
/* Try to make an O_BINARY ... sometimes need the leading '_'.
*/
#ifdef BINARY_OPEN

View File

@ -104,9 +104,10 @@ G_DEFINE_TYPE( VipsInterpolateBicubic, vips_interpolate_bicubic,
/* Fixed-point version, for 8 and 16-bit types.
*/
template <typename T, int min_value, int max_value>
template <typename T, int max_value>
static void inline
bicubic_int_tab( void *pout, const VipsPel *pin,
bicubic_unsigned_int_tab( void *pout, const VipsPel *pin,
const int bands, const int lskip,
const int *cx, const int *cy )
{
@ -152,7 +153,73 @@ bicubic_int_tab( void *pout, const VipsPel *pin,
const T qua_thr = in[l3_plus_b2];
const T qua_fou = in[l3_plus_b3];
int bicubic = bicubic_int<T>(
int bicubic = bicubic_unsigned_int<T>(
uno_one, uno_two, uno_thr, uno_fou,
dos_one, dos_two, dos_thr, dos_fou,
tre_one, tre_two, tre_thr, tre_fou,
qua_one, qua_two, qua_thr, qua_fou,
cx, cy );
if( bicubic < 0 )
bicubic = 0;
else if( bicubic > max_value )
bicubic = max_value;
out[z] = bicubic;
in += 1;
}
}
template <typename T, int min_value, int max_value>
static void inline
bicubic_signed_int_tab( void *pout, const VipsPel *pin,
const int bands, const int lskip,
const int *cx, const int *cy )
{
T* restrict out = (T *) pout;
const T* restrict in = (T *) pin;
const int b1 = bands;
const int b2 = b1 + b1;
const int b3 = b1 + b2;
const int l1 = lskip / sizeof( T );
const int l2 = l1 + l1;
const int l3 = l1 + l2;
const int l1_plus_b1 = l1 + b1;
const int l1_plus_b2 = l1 + b2;
const int l1_plus_b3 = l1 + b3;
const int l2_plus_b1 = l2 + b1;
const int l2_plus_b2 = l2 + b2;
const int l2_plus_b3 = l2 + b3;
const int l3_plus_b1 = l3 + b1;
const int l3_plus_b2 = l3 + b2;
const int l3_plus_b3 = l3 + b3;
for( int z = 0; z < bands; z++ ) {
const T uno_one = in[0];
const T uno_two = in[b1];
const T uno_thr = in[b2];
const T uno_fou = in[b3];
const T dos_one = in[l1];
const T dos_two = in[l1_plus_b1];
const T dos_thr = in[l1_plus_b2];
const T dos_fou = in[l1_plus_b3];
const T tre_one = in[l2];
const T tre_two = in[l2_plus_b1];
const T tre_thr = in[l2_plus_b2];
const T tre_fou = in[l2_plus_b3];
const T qua_one = in[l3];
const T qua_two = in[l3_plus_b1];
const T qua_thr = in[l3_plus_b2];
const T qua_fou = in[l3_plus_b3];
int bicubic = bicubic_signed_int<T>(
uno_one, uno_two, uno_thr, uno_fou,
dos_one, dos_two, dos_thr, dos_fou,
tre_one, tre_two, tre_thr, tre_fou,
@ -350,9 +417,10 @@ vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate,
switch( in->im->BandFmt ) {
case VIPS_FORMAT_UCHAR:
bicubic_int_tab<unsigned char, 0, UCHAR_MAX>(
bicubic_unsigned_int_tab<unsigned char, UCHAR_MAX>(
out, p, bands, lskip,
cxi, cyi );
/*
Handy for benchmarking
@ -360,27 +428,29 @@ vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate,
bicubic_float_tab<unsigned char>(
out, p, bands, lskip,
cxf, cyf );
bicubic_notab<unsigned char>(
out, p, bands, lskip,
x - ix, y - iy );
*/
break;
case VIPS_FORMAT_CHAR:
bicubic_int_tab<signed char, SCHAR_MIN, SCHAR_MAX>(
bicubic_signed_int_tab<signed char, SCHAR_MIN, SCHAR_MAX>(
out, p, bands, lskip,
cxi, cyi );
break;
case VIPS_FORMAT_USHORT:
bicubic_int_tab<unsigned short, 0, USHRT_MAX>(
bicubic_unsigned_int_tab<unsigned short, USHRT_MAX>(
out, p, bands, lskip,
cxi, cyi );
break;
case VIPS_FORMAT_SHORT:
bicubic_int_tab<signed short, SHRT_MIN, SHRT_MAX>(
bicubic_signed_int_tab<signed short, SHRT_MIN, SHRT_MAX>(
out, p, bands, lskip,
cxi, cyi );
break;

View File

@ -169,44 +169,103 @@ bilinear_nosign(
* Bicubic (Catmull-Rom) interpolation templates:
*/
static int inline
unsigned_fixed_round( int v )
{
const int round_by = VIPS_INTERPOLATE_SCALE >> 1;
return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT );
}
/* Fixed-point integer bicubic, used for 8 and 16-bit types.
*/
template <typename T> static int inline
bicubic_int(
bicubic_unsigned_int(
const T uno_one, const T uno_two, const T uno_thr, const T uno_fou,
const T dos_one, const T dos_two, const T dos_thr, const T dos_fou,
const T tre_one, const T tre_two, const T tre_thr, const T tre_fou,
const T qua_one, const T qua_two, const T qua_thr, const T qua_fou,
const int* restrict cx, const int* restrict cy )
{
const int r0 =
(cx[0] * uno_one +
cx[1] * uno_two +
cx[2] * uno_thr +
cx[3] * uno_fou) >> VIPS_INTERPOLATE_SHIFT;
const int r0 = unsigned_fixed_round(
cx[0] * uno_one +
cx[1] * uno_two +
cx[2] * uno_thr +
cx[3] * uno_fou );
const int r1 =
(cx[0] * dos_one +
cx[1] * dos_two +
cx[2] * dos_thr +
cx[3] * dos_fou) >> VIPS_INTERPOLATE_SHIFT;
const int r1 = unsigned_fixed_round(
cx[0] * dos_one +
cx[1] * dos_two +
cx[2] * dos_thr +
cx[3] * dos_fou );
const int r2 =
(cx[0] * tre_one +
cx[1] * tre_two +
cx[2] * tre_thr +
cx[3] * tre_fou) >> VIPS_INTERPOLATE_SHIFT;
const int r2 = unsigned_fixed_round(
cx[0] * tre_one +
cx[1] * tre_two +
cx[2] * tre_thr +
cx[3] * tre_fou );
const int r3 =
(cx[0] * qua_one +
cx[1] * qua_two +
cx[2] * qua_thr +
cx[3] * qua_fou) >> VIPS_INTERPOLATE_SHIFT;
const int r3 = unsigned_fixed_round(
cx[0] * qua_one +
cx[1] * qua_two +
cx[2] * qua_thr +
cx[3] * qua_fou );
return( (cy[0] * r0 +
cy[1] * r1 +
cy[2] * r2 +
cy[3] * r3) >> VIPS_INTERPOLATE_SHIFT );
return( unsigned_fixed_round(
cy[0] * r0 +
cy[1] * r1 +
cy[2] * r2 +
cy[3] * r3 ) );
}
static int inline
signed_fixed_round( int v )
{
const int sign_of_v = 2 * (v > 0) - 1;
const int round_by = sign_of_v * (VIPS_INTERPOLATE_SCALE >> 1);
return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT );
}
/* Fixed-point integer bicubic, used for 8 and 16-bit types.
*/
template <typename T> static int inline
bicubic_signed_int(
const T uno_one, const T uno_two, const T uno_thr, const T uno_fou,
const T dos_one, const T dos_two, const T dos_thr, const T dos_fou,
const T tre_one, const T tre_two, const T tre_thr, const T tre_fou,
const T qua_one, const T qua_two, const T qua_thr, const T qua_fou,
const int* restrict cx, const int* restrict cy )
{
const int r0 = signed_fixed_round(
cx[0] * uno_one +
cx[1] * uno_two +
cx[2] * uno_thr +
cx[3] * uno_fou );
const int r1 = signed_fixed_round(
cx[0] * dos_one +
cx[1] * dos_two +
cx[2] * dos_thr +
cx[3] * dos_fou );
const int r2 = signed_fixed_round(
cx[0] * tre_one +
cx[1] * tre_two +
cx[2] * tre_thr +
cx[3] * tre_fou );
const int r3 = signed_fixed_round(
cx[0] * qua_one +
cx[1] * qua_two +
cx[2] * qua_thr +
cx[3] * qua_fou );
return( signed_fixed_round(
cy[0] * r0 +
cy[1] * r1 +
cy[2] * r2 +
cy[3] * r3 ) );
}
/* Floating-point bicubic, used for int/float/double types.

View File

@ -51,6 +51,10 @@
* unlike the main image wrt. rotation / colour / etc.
* 30/6/14
* - fix interlaced thumbnail output, thanks lovell
* 3/8/14
* - box shrink less, use interpolator more, if the window_size is large
* enough
* - default to bicubic + nosharpen if bicubic is available
*/
#ifdef HAVE_CONFIG_H
@ -68,6 +72,10 @@
#define ORIENTATION ("exif-ifd0-Orientation")
/* Default settings. We change the default to bicubic + nosharpen in main() if
* this vips has been compiled with bicubic support.
*/
static char *thumbnail_size = "128";
static int thumbnail_width = 128;
static int thumbnail_height = 128;
@ -170,14 +178,20 @@ get_angle( VipsImage *im )
* We shrink in two stages: first, a shrink with a block average. This can
* only accurately shrink by integer factors. We then do a second shrink with
* a supplied interpolator to get the exact size we want.
*
* We aim to do the second shrink by roughly half the interpolator's
* window_size.
*/
static int
calculate_shrink( VipsImage *im, double *residual )
calculate_shrink( VipsImage *im, double *residual,
VipsInterpolate *interp )
{
VipsAngle angle = get_angle( im );
gboolean rotate = angle == VIPS_ANGLE_90 || angle == VIPS_ANGLE_270;
int width = rotate_image && rotate ? im->Ysize : im->Xsize;
int height = rotate_image && rotate ? im->Xsize : im->Ysize;
const int window_size =
interp ? vips_interpolate_get_window_size( interp ) : 2;
VipsDirection direction;
@ -211,9 +225,12 @@ calculate_shrink( VipsImage *im, double *residual )
*/
double factor2 = factor < 1.0 ? 1.0 : factor;
/* Int component of shrink.
/* Int component of factor2.
*
* We want to shrink by less for interpolators with larger windows.
*/
int shrink = floor( factor2 );
int shrink = VIPS_MAX( 1,
floor( factor2 ) / VIPS_MAX( 1, window_size / 2 ) );
if( residual &&
direction == VIPS_DIRECTION_HORIZONTAL ) {
@ -243,7 +260,7 @@ calculate_shrink( VipsImage *im, double *residual )
static int
thumbnail_find_jpegshrink( VipsImage *im )
{
int shrink = calculate_shrink( im, NULL );
int shrink = calculate_shrink( im, NULL, NULL );
/* We can't use pre-shrunk images in linear mode. libjpeg shrinks in Y
* (of YCbCR), not linear space.
@ -324,7 +341,7 @@ thumbnail_interpolator( VipsObject *process, VipsImage *in )
double residual;
VipsInterpolate *interp;
calculate_shrink( in, &residual );
calculate_shrink( in, &residual, NULL );
/* For images smaller than the thumbnail, we upscale with nearest
* neighbor. Otherwise we makes thumbnails that look fuzzy and awful.
@ -430,7 +447,7 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
return( NULL );
in = t[2];
shrink = calculate_shrink( in, &residual );
shrink = calculate_shrink( in, &residual, interp );
vips_info( "vipsthumbnail", "integer shrink by %d", shrink );
@ -570,12 +587,18 @@ thumbnail_crop( VipsObject *process, VipsImage *im )
static VipsImage *
thumbnail_rotate( VipsObject *process, VipsImage *im )
{
VipsImage **t = (VipsImage **) vips_object_local_array( process, 1 );
VipsImage **t = (VipsImage **) vips_object_local_array( process, 2 );
VipsAngle angle = get_angle( im );
if( rotate_image ) {
if( vips_rot( im, &t[0], get_angle( im ), NULL ) )
if( rotate_image &&
angle != VIPS_ANGLE_0 ) {
/* Need to copy to memory, we have to stay seq.
*/
t[0] = vips_image_new_memory();
if( vips_image_write( im, t[0] ) ||
vips_rot( t[0], &t[1], angle, NULL ) )
return( NULL );
im = t[0];
im = t[1];
(void) vips_image_remove( im, ORIENTATION );
}
@ -589,8 +612,6 @@ thumbnail_rotate( VipsObject *process, VipsImage *im )
static int
thumbnail_write( VipsObject *process, VipsImage *im, const char *filename )
{
VipsImage **t = (VipsImage **) vips_object_local_array( process, 1 );
char *file;
char *p;
char buf[FILENAME_MAX];
@ -622,16 +643,7 @@ thumbnail_write( VipsObject *process, VipsImage *im, const char *filename )
g_free( file );
/* We need to cache the whole of the thumbnail before we write it
* in case we are writing an interlaced image. Interlaced png (for
* example) will make 7 passes over the image during write.
*/
if( vips_tilecache( im, &t[0],
"threaded", TRUE,
"persistent", TRUE,
"max_tiles", -1,
NULL ) ||
vips_image_write_to_file( t[0], output_name, NULL ) ) {
if( vips_image_write_to_file( im, output_name, NULL ) ) {
g_free( output_name );
return( -1 );
}
@ -675,6 +687,14 @@ main( int argc, char **argv )
textdomain( GETTEXT_PACKAGE );
setlocale( LC_ALL, "" );
/* Does this vips have bicubic? Default to that + nosharpen if it
* does.
*/
if( vips_type_find( "VipsInterpolate", "bicubic" ) ) {
interpolator = "bicubic";
convolution_mask = "none";
}
context = g_option_context_new( _( "- thumbnail generator" ) );
g_option_context_add_main_entries( context, options, GETTEXT_PACKAGE );