Merge remote-tracking branch 'origin/7.40'

Conflicts:
	ChangeLog
	TODO
	configure.ac
	libvips/foreign/vipspng.c
This commit is contained in:
John Cupitt 2014-08-12 13:41:52 +01:00
commit b24cbb492c
24 changed files with 957 additions and 168 deletions

View File

@ -1,12 +1,23 @@
25/7/14 started 7.41.0
- start working on --disable-deprecated
12/8/14 started 7.40.5
- more doc fixes
25/7/14 started 7.40.5
- fix a race in im_maxpos_avg()
- 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
- vipsthumbnail has an anti-alias filter between shrink and affine
- vipsthumbnail defaults to bicubic
- better rounding behaviour for fixed-point bicubic reduces noise
- fix pngload with libpng >=1.6.11
- fix colour for openslide read associated
4/7/14 started 7.40.4
- fix vips_rawsave_fd(), thanks aferrero2707

16
TODO
View File

@ -1,14 +1,8 @@
- 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
- can we pick the vipsthumbnail int shrink factor more intelligently?
- did we include the exif patch in the latest windows build? check on laptop
we don't seem to have this fix in the repository!
- vips_object_unref_outputs() needs docs ... bindings will need it
@ -16,6 +10,8 @@
finally finish --disable-deprecated option
- vips_init() has a deprecation warning, bizarre
- check and fix up docs
@ -27,7 +23,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

@ -38,7 +38,7 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date`
# binary interface changes not backwards compatible?: reset age to 0
LIBRARY_CURRENT=38
LIBRARY_REVISION=3
LIBRARY_REVISION=4
LIBRARY_AGE=0
# patched into include/vips/version.h

View File

@ -17,7 +17,19 @@
<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>
<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>

View File

@ -14,11 +14,356 @@
<refpurpose>How to add operations to VIPS</refpurpose>
</refnamediv>
<refsect1 id="extending-subclassing">
<title>Adding operations to VIPS</title>
<refsect1 id="extending-pointtopoint">
<title>A simple point-to-point operation</title>
<para>
all about subclassing.
All operations are subclasses of #VipsOperation, which in turn
subclasses #VipsObject and then %GObject. You need to define a new
instance struct and a new class struct.
<programlisting language="C">
typedef struct _Negative {
VipsOperation parent_instance;
VipsImage *in;
VipsImage *out;
int image_max;
} Negative;
typedef struct _NegativeClass {
VipsOperationClass parent_class;
/* No new class members needed for this op.
*/
} NegativeClass;
</programlisting>
</para>
<para>
This operation will find the photographic negative of an unsigned
8-bit image, optionally letting you specify the value which the pixels
"pivot" about. It doesn't need any class members (ie. values common
to all operations of this type), so the second struct is empty. See
vips_invert() for a more complete version of this operation that's
actually in the library.
</para>
<para>
GObject has a handy macro to write some of the boilerplate for you.
<programlisting language="C">
G_DEFINE_TYPE( Negative, negative, VIPS_TYPE_OPERATION );
</programlisting>
This defines a function called negative_get_type(),
which registers this new class and returns its #GType (a
pointer-sized integer). negative_get_type() in turn needs two
functions, negative_init(), to initialise a new instance, and
negative_class_init(), to initialise a new class.
</para>
<para>
negative_init() is very simple, it just sets the default value for
our optional class parameter.
<programlisting language="C">
static void
negative_init( Negative *negative )
{
negative->image_max = 255;
}
</programlisting>
</para>
<para>
negative_class_init() is more complicated: it has to set various
fields in various superclasses.
<programlisting language="C">
static void
negative_class_init( NegativeClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "negative";
object_class->description = "photographic negative";
object_class->build = negative_build;
VIPS_ARG_IMAGE( class, "in", 1,
"Input",
"Input image",
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( Negative, in ) );
VIPS_ARG_IMAGE( class, "out", 2,
"Output",
"Output image",
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( Negative, out ) );
VIPS_ARG_INT( class, "image_max", 4,
"Image maximum",
"Maximum value in image: pivot about this",
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( Negative, image_max ),
0, 255, 255 );
}
</programlisting>
</para>
<para>
In %GObject, it needs to set the getters and setters for this class. vips
has a generic get/set system, so any subclass of #VipsObject needs to
use the vips ones.
</para>
<para>
In #VipsObject, it needs to set the operation @nickname and @description,
and set a build function (see below). @nickname is used to refer to
this operation in the API, @description is used to explain this
operation to users and will be translated into their language.
</para>
<para>
Finally, it needs to set the arguments this class constructor
takes. There are a set of handy macros for doing this. The first few
parameters are always the same and mean: class pointer for argument,
argument name, argument priority (bindings expect required arguments in
order of priority), long argument name (this one is internationalised
and displayed to users), description (again, users can see this),
some flags describing the argument, and finally the position of the
member in the struct.
</para>
<para>
Integer arguments take three more values: the minimum, maximum and
default value for the argument.
</para>
<para>
The build function is the thing VipsObject calls after supplying
arguments. It checks that all required arguments have been set and are
valid and constructs the object. After build, the object is expected
to be ready for use.
<programlisting language="C">
static int
negative_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
Negative *negative = (Negative *) object;
if( VIPS_OBJECT_CLASS( negative_parent_class )->build( object ) )
return( -1 );
if( vips_check_uncoded( class->nickname, negative->in ) ||
vips_check_format( class->nickname, negative->in, VIPS_FORMAT_UCHAR ) )
return( -1 );
g_object_set( object, "out", vips_image_new(), NULL );
if( vips_image_pipelinev( negative->out,
VIPS_DEMAND_STYLE_THINSTRIP, negative->in, NULL ) )
return( -1 );
if( vips_image_generate( negative->out,
vips_start_one,
negative_generate,
vips_stop_one,
negative->in, negative ) )
return( -1 );
return( 0 );
}
</programlisting>
</para>
<para>
negative_build() first chains up to the superclass: this will check
that all input arguments have been supplied and are sane.
</para>
<para>
Next, it adds its own checks. This is a demo operation, so we just
work for uncoded, unsigned 8-bit images.
</para>
<para>
Next, it creates the output image. This needs to be set with
g_object_set() so that vips can see that it has been assigned. vips
will also handle the reference counting for you.
</para>
<para>
vips_image_pipelinev() links our new image onto the input image and
notes that this operation prefers to work in lines.
</para>
<para>
Finally, vips_image_generate() attaches a set of callbacks to the
output image to generate chunks of it on request. vips_start_one()
and vips_stop_one() are convenience functions that make the input
region for you.
</para>
<para>
And then the actual image processing.
<programlisting language="C">
static int
negative_generate( VipsRegion *or,
void *vseq, void *a, void *b, gboolean *stop )
{
/* The area of the output region we have been asked to make.
*/
VipsRect *r = &amp;or-&gt;valid;
/* The sequence value ... the thing returned by vips_start_one().
*/
VipsRegion *ir = (VipsRegion *) vseq;
Negative *negative = (Negative *) b;
int line_size = r-&gt;width * negative-&gt;in-&gt;Bands;
int x, y;
/* Request matching part of input region.
*/
if( vips_region_prepare( ir, r ) )
return( -1 );
for( y = 0; y &lt; r-&gt;height; y++ ) {
unsigned char *p = (unsigned char *)
VIPS_REGION_ADDR( ir, r-&gt;left, r-&gt;top + y );
unsigned char *q = (unsigned char *)
VIPS_REGION_ADDR( or, r-&gt;left, r-&gt;top + y );
for( x = 0; x &lt; line_size; x++ )
q[x] = negative-&gt;image_max - p[x];
}
return( 0 );
}
</programlisting>
</para>
<para>
This has to calculate a section of the output image. The output
#VipsRegion, @or, contains a #VipsRect called @valid which is the
area needing calculation. negative_generate() asks for the
corresponding pixels from the input region, then loops over the
area. VIPS_REGION_ADDR() is a simple macro that does pointer arithmetic
for you: you need to stay within the valid area.
</para>
<para>
To add the operation to vips, just call negative_get_type(). You
can then use @negative from any of the vips interfaces. For example,
in Python you'd use it like this:
<programlisting language="python">
out = in.negative(image_max = 128)
</programlisting>
</para>
<para>
From the command-line it'd look like this:
<programlisting language="bash">
$ vips negative in.png out.tif --image-max 128
</programlisting>
</para>
<para>
And from C like this:
<programlisting language="C">
VipsImage *in;
VipsImage *out;
if( vips_call( "negative", in, &amp;out, "image_max", 128, NULL ) )
... error
</programlisting>
</para>
<para>
Unfortunately that will do almost no compile-time type checking,
so all vips operations have a tiny extra wrapper to add a bit of
safety. For example:
<programlisting language="C">
static int
negative( VipsImage *in, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_call_split( "negative", ap, in, out );
va_end( ap );
return( result );
}
</programlisting>
</para>
<para>
And now you can write:
<programlisting language="C">
if( negative( in, &amp;out, "image_max", 128, NULL ) )
... error
</programlisting>
and it's at least a bit safer.
</para>
</refsect1>
<refsect1 id="extending-othertypes">
<title>Other types of operation</title>
<para>
Change the _build() function to make other types of operation.
</para>
<para>
Use vips_image_generate() with vips_start_many() to make operations
which demand pixels from more than one image at once, such as image
plus image.
</para>
<para>
Use vips_sink() instead of vips_image_generate() to loop over an image
and calculate a value. vips uses this for the statistics operations,
like vips_avg().
</para>
<para>
Use vips_image_wio_input() to get an entire image into memory so you
can read it with a pointer. This will obviously not scale well to
very large images, but some operations, like FFTs or flood-fill, need
the whole image to be available at once.
</para>
<para>
Make area operations, like filters, by enlarging the #VipsRect that
_generate() is given before calling vips_image_prepare(). You can
enlarge the input image, so that the output image is the same size as
the original input, by using vips_embed() within the _build() function.
</para>
<para>
Make things like flips and rotates by making larger changes to the
#VipsRect in _generate().
</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

@ -43,6 +43,8 @@
* - use openslide_detect_vendor() on >= 3.4.0
* 30/7/14
* - add autocrop toggle
* 9/8/14
* - do argb -> rgba for associated as well
*/
/*
@ -390,6 +392,45 @@ vips__openslide_read_header( const char *filename, VipsImage *out,
return( 0 );
}
/* Convert from ARGB to RGBA and undo premultiplication.
*
* We throw away transparency. Formats like Mirax use transparent + bg
* colour for areas with no useful pixels. But if we output
* transparent pixels and then convert to RGB for jpeg write later, we
* would have to pass the bg colour down the pipe somehow. The
* structure of dzsave makes this tricky.
*
* We could output plain RGB instead, but that would break
* compatibility with older vipses.
*/
static void
argb2rgba( uint32_t *buf, int n, uint32_t bg )
{
int i;
for( i = 0; i < n; i++ ) {
uint32_t *p = buf + i;
uint32_t x = *p;
uint8_t a = x >> 24;
VipsPel *out = (VipsPel *) p;
if( a != 0 ) {
out[0] = 255 * ((x >> 16) & 255) / a;
out[1] = 255 * ((x >> 8) & 255) / a;
out[2] = 255 * (x & 255) / a;
out[3] = 255;
}
else {
/* Use background color.
*/
out[0] = (bg >> 16) & 255;
out[1] = (bg >> 8) & 255;
out[2] = bg & 255;
out[3] = 255;
}
}
}
static int
vips__openslide_generate( VipsRegion *out,
void *_seq, void *_rslide, void *unused, gboolean *stop )
@ -401,7 +442,6 @@ vips__openslide_generate( VipsRegion *out,
uint32_t *buf = (uint32_t *) VIPS_REGION_ADDR( out, r->left, r->top );
const char *error;
int i;
VIPS_DEBUG_MSG( "vips__openslide_generate: %dx%d @ %dx%d\n",
r->width, r->height, r->left, r->top );
@ -434,39 +474,9 @@ vips__openslide_generate( VipsRegion *out,
return( -1 );
}
/* Convert from ARGB to RGBA and undo premultiplication. Since we are
* inside a cache, we know buf must be continuous.
*
* We throw away transparency. Formats like Mirax use transparent + bg
* colour for areas with no useful pixels. But if we output
* transparent pixels and then convert to RGB for jpeg write later, we
* would have to pass the bg colour down the pipe somehow. The
* structure of dzsave makes this tricky.
*
* We could output plain RGB instead, but that would break
* compatibility with older vipses.
/* Since we are inside a cache, we know buf must be continuous.
*/
for( i = 0; i < n; i++ ) {
uint32_t *p = buf + i;
uint32_t x = *p;
uint8_t a = x >> 24;
VipsPel *out = (VipsPel *) p;
if( a != 0 ) {
out[0] = 255 * ((x >> 16) & 255) / a;
out[1] = 255 * ((x >> 8) & 255) / a;
out[2] = 255 * (x & 255) / a;
out[3] = 255;
}
else {
/* Use background color.
*/
out[0] = (bg >> 16) & 255;
out[1] = (bg >> 8) & 255;
out[2] = bg & 255;
out[3] = 255;
}
}
argb2rgba( buf, n, bg );
return( 0 );
}
@ -519,6 +529,7 @@ vips__openslide_read_associated( const char *filename, VipsImage *out,
{
ReadSlide *rslide;
VipsImage *raw;
uint32_t *buf;
const char *error;
VIPS_DEBUG_MSG( "vips__openslide_read_associated: %s %s\n",
@ -532,14 +543,15 @@ vips__openslide_read_associated( const char *filename, VipsImage *out,
if( !(rslide = readslide_new( filename, raw, 0, FALSE, associated )) ||
vips_image_write_prepare( raw ) )
return( -1 );
openslide_read_associated_image( rslide->osr, rslide->associated,
(uint32_t *) VIPS_IMAGE_ADDR( raw, 0, 0 ) );
buf = (uint32_t *) VIPS_IMAGE_ADDR( raw, 0, 0 );
openslide_read_associated_image( rslide->osr, rslide->associated, buf );
error = openslide_get_error( rslide->osr );
if( error ) {
vips_error( "openslide2vips",
_( "reading associated image: %s" ), error );
return( -1 );
}
argb2rgba( buf, raw->Xsize * raw->Ysize, rslide->bg );
if( vips_image_write( raw, out ) )
return( -1 );

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

@ -48,6 +48,8 @@
* still set color_type to alpha
* 16/7/13
* - more robust error handling from libpng
* 9/8/14
* - don't check profiles, helps with libpng >=1.6.11
*/
/*
@ -191,7 +193,8 @@ read_new( VipsImage *out, gboolean readbehind )
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE
/* Prevent libpng (>=1.6.11) verifying sRGB profiles.
*/
png_set_option( read->pPng, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
png_set_option( read->pPng,
PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/
/* Catch PNG errors from png_create_info_struct().
@ -726,7 +729,8 @@ write_new( VipsImage *in )
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE
/* Prevent libpng (>=1.6.11) verifying sRGB profiles.
*/
png_set_option( write->pPng, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
png_set_option( write->pPng,
PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/
/* Catch PNG errors from png_create_info_struct().
@ -789,10 +793,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 +837,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 +878,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,13 @@
* 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 window_size is large
* enough
* - default to bicubic if available
* - add an anti-alias filter between shrink and affine
* - support CMYK
* - use SEQ_UNBUF for a memory saving
*/
#ifdef HAVE_CONFIG_H
@ -68,6 +75,10 @@
#define ORIENTATION ("exif-ifd0-Orientation")
/* Default settings. We change the default to bicubic 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 +181,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 +228,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 +263,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.
@ -299,7 +319,7 @@ thumbnail_open( VipsObject *process, const char *filename )
jpegshrink );
if( !(im = vips_image_new_from_file( filename,
"access", VIPS_ACCESS_SEQUENTIAL,
"access", VIPS_ACCESS_SEQUENTIAL_UNBUFFERED,
"shrink", jpegshrink,
NULL )) )
return( NULL );
@ -308,7 +328,7 @@ thumbnail_open( VipsObject *process, const char *filename )
/* All other formats.
*/
if( !(im = vips_image_new_from_file( filename,
"access", VIPS_ACCESS_SEQUENTIAL,
"access", VIPS_ACCESS_SEQUENTIAL_UNBUFFERED,
NULL )) )
return( NULL );
}
@ -324,10 +344,10 @@ 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.
* neighbor. Otherwise we make thumbnails that look fuzzy and awful.
*/
if( !(interp = VIPS_INTERPOLATE( vips_object_new_from_string(
g_type_class_ref( VIPS_TYPE_INTERPOLATE ),
@ -380,6 +400,7 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
int tile_width;
int tile_height;
int nlines;
double sigma;
/* RAD needs special unpacking.
*/
@ -394,12 +415,16 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
}
/* In linear mode, we import right at the start.
*
* We also have to import the whole image if it's CMYK, since
* vips_colourspace() (see below) doesn't know about CMYK.
*
* This is only going to work for images in device space. If you have
* an image in PCS which also has an attached profile, strange things
* will happen.
*/
if( linear_processing &&
if( (linear_processing ||
in->Type == VIPS_INTERPRETATION_CMYK) &&
in->Coding == VIPS_CODING_NONE &&
(in->BandFmt == VIPS_FORMAT_UCHAR ||
in->BandFmt == VIPS_FORMAT_USHORT) &&
@ -430,7 +455,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 );
@ -457,6 +482,7 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
* has been used ... but it never will, since thread1 will block on
* this cache lock.
*/
vips_get_tile_size( in,
&tile_width, &tile_height, &nlines );
if( vips_tilecache( in, &t[4],
@ -465,12 +491,39 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
"max_tiles", (nlines * 2) / 10,
"access", VIPS_ACCESS_SEQUENTIAL,
"threaded", TRUE,
NULL ) ||
vips_affine( t[4], &t[5], residual, 0, 0, residual,
"interpolate", interp,
NULL ) )
NULL ) )
return( NULL );
in = t[5];
in = t[4];
/* If the final affine will be doing a large downsample, we can get
* nasty aliasing on hard edges. Blur before affine to smooth this out.
*
* Don't blur for very small shrinks, blur with radius 1 for x1.5
* shrinks, blur radius 2 for x2.5 shrinks and above, etc.
*/
sigma = ((1.0 / residual) - 0.5) / 1.5;
if( residual < 1.0 &&
sigma > 0.1 ) {
if( vips_gaussmat( &t[9], sigma, 0.2,
"separable", TRUE,
"integer", TRUE,
NULL ) ||
vips_convsep( in, &t[5], t[9], NULL ) )
return( NULL );
vips_info( "vipsthumbnail", "anti-alias, sigma %g",
sigma );
#ifdef DEBUG
printf( "anti-alias blur matrix is:\n" );
vips_matrixprint( t[9], NULL );
#endif /*DEBUG*/
in = t[5];
}
if( vips_affine( in, &t[6], residual, 0, 0, residual,
"interpolate", interp,
NULL ) )
return( NULL );
in = t[6];
vips_info( "vipsthumbnail", "residual scale by %g", residual );
vips_info( "vipsthumbnail", "%s interpolation",
@ -494,10 +547,10 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
}
else {
vips_info( "vipsthumbnail", "converting to sRGB" );
if( vips_colourspace( in, &t[6],
if( vips_colourspace( in, &t[7],
VIPS_INTERPRETATION_sRGB, NULL ) )
return( NULL );
in = t[6];
in = t[7];
}
}
else if( export_profile &&
@ -513,13 +566,13 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
vips_info( "vipsthumbnail",
"exporting with profile %s", export_profile );
if( vips_icc_transform( in, &t[6], export_profile,
if( vips_icc_transform( in, &t[7], export_profile,
"input_profile", import_profile,
"embedded", TRUE,
NULL ) )
return( NULL );
in = t[6];
in = t[7];
}
/* If we are upsampling, don't sharpen, since nearest looks dumb
@ -570,12 +623,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 +648,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 +679,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 +723,12 @@ main( int argc, char **argv )
textdomain( GETTEXT_PACKAGE );
setlocale( LC_ALL, "" );
/* Does this vips have bicubic? Default to that if it
* does.
*/
if( vips_type_find( "VipsInterpolate", "bicubic" ) )
interpolator = "bicubic";
context = g_option_context_new( _( "- thumbnail generator" ) );
g_option_context_add_main_entries( context, options, GETTEXT_PACKAGE );