Merge branch 'master' of github.com:jcupitt/libvips
Conflicts: ChangeLog TODO
This commit is contained in:
commit
c646356b4f
11
ChangeLog
11
ChangeLog
@ -1,6 +1,10 @@
|
||||
25/7/14 started 7.41.0
|
||||
- start working on --disable-deprecated
|
||||
- fix pngload with libpng >1.6.1
|
||||
- add vips_resize()
|
||||
|
||||
12/8/14 started 7.40.5
|
||||
- more doc fixes
|
||||
|
||||
25/7/14 started 7.40.5
|
||||
- fix a race in im_maxpos_avg()
|
||||
@ -10,9 +14,12 @@
|
||||
- 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
|
||||
- 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
|
||||
|
12
TODO
12
TODO
@ -1,3 +1,8 @@
|
||||
- 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
|
||||
|
||||
@ -5,10 +10,15 @@
|
||||
|
||||
finally finish --disable-deprecated option
|
||||
|
||||
- copy-paste blog post about writing vips8 operations into 'extending'
|
||||
|
||||
|
||||
|
||||
- vips_init() has a deprecation warning, bizarre
|
||||
|
||||
- vips_affine() for simple resize is slow at the command-line, since it has to
|
||||
be non-seq ... make a seq version that can only up/down size
|
||||
|
||||
|
||||
|
||||
- check and fix up docs
|
||||
|
||||
|
@ -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
|
||||
|
@ -19,6 +19,18 @@
|
||||
<para>
|
||||
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>
|
||||
|
||||
</refentry>
|
||||
|
@ -14,23 +14,354 @@
|
||||
<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. Copy-paste from the blog post about extending
|
||||
vips8.
|
||||
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>
|
||||
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.
|
||||
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 = &or->valid;
|
||||
|
||||
/* The sequence value ... the thing returned by vips_start_one().
|
||||
*/
|
||||
VipsRegion *ir = (VipsRegion *) vseq;
|
||||
|
||||
Negative *negative = (Negative *) b;
|
||||
int line_size = r->width * negative->in->Bands;
|
||||
|
||||
int x, y;
|
||||
|
||||
/* Request matching part of input region.
|
||||
*/
|
||||
if( vips_region_prepare( ir, r ) )
|
||||
return( -1 );
|
||||
|
||||
for( y = 0; y < r->height; y++ ) {
|
||||
unsigned char *p = (unsigned char *)
|
||||
VIPS_REGION_ADDR( ir, r->left, r->top + y );
|
||||
unsigned char *q = (unsigned char *)
|
||||
VIPS_REGION_ADDR( or, r->left, r->top + y );
|
||||
|
||||
for( x = 0; x < line_size; x++ )
|
||||
q[x] = negative->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, &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, &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>
|
||||
|
@ -157,8 +157,8 @@ vips_gaussmat_class_init( VipsGaussmatClass *class )
|
||||
vobject_class->build = vips_gaussmat_build;
|
||||
|
||||
VIPS_ARG_DOUBLE( class, "sigma", 2,
|
||||
_( "Radius" ),
|
||||
_( "Radius of Gaussian" ),
|
||||
_( "Sigma" ),
|
||||
_( "Sigma of Gaussian" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsGaussmat, sigma ),
|
||||
0.000001, 10000.0, 1.0 );
|
||||
|
@ -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 );
|
||||
|
@ -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().
|
||||
|
@ -43,6 +43,9 @@ int vips_shrink( VipsImage *in, VipsImage **out,
|
||||
__attribute__((sentinel));
|
||||
int vips_similarity( VipsImage *in, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_resize( VipsImage *in, VipsImage **out,
|
||||
double h_scale, double v_scale, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_affine( VipsImage *in, VipsImage **out,
|
||||
double a, double b, double c, double d, ... )
|
||||
__attribute__((sentinel));
|
||||
|
@ -9,6 +9,7 @@ libresample_la_SOURCES = \
|
||||
quadratic.c \
|
||||
resample.c \
|
||||
similarity.c \
|
||||
resize.c \
|
||||
presample.h \
|
||||
shrink.c \
|
||||
interpolate.c \
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* im_affine() ... affine transform with a supplied interpolator.
|
||||
/* affine transform with a supplied interpolator.
|
||||
*
|
||||
*
|
||||
* Copyright N. Dessipris
|
||||
|
@ -115,10 +115,12 @@ vips_resample_operation_init( void )
|
||||
extern GType vips_quadratic_get_type( void );
|
||||
extern GType vips_affine_get_type( void );
|
||||
extern GType vips_similarity_get_type( void );
|
||||
extern GType vips_resize_get_type( void );
|
||||
|
||||
vips_shrink_get_type();
|
||||
vips_quadratic_get_type();
|
||||
vips_affine_get_type();
|
||||
vips_similarity_get_type();
|
||||
vips_resize_get_type();
|
||||
}
|
||||
|
||||
|
196
libvips/resample/resize.c
Normal file
196
libvips/resample/resize.c
Normal file
@ -0,0 +1,196 @@
|
||||
/* resize an image ... a simple wrapper over affine
|
||||
*
|
||||
* 13/8/14
|
||||
* - from affine.c
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG_VERBOSE
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/debug.h>
|
||||
#include <vips/internal.h>
|
||||
#include <vips/transform.h>
|
||||
|
||||
#include "presample.h"
|
||||
|
||||
typedef struct _VipsResize {
|
||||
VipsResample parent_instance;
|
||||
|
||||
double h_scale;
|
||||
double v_scale;
|
||||
VipsInterpolate *interpolate;
|
||||
double idx;
|
||||
double idy;
|
||||
|
||||
} VipsResize;
|
||||
|
||||
typedef VipsResampleClass VipsResizeClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsResize, vips_resize, VIPS_TYPE_RESAMPLE );
|
||||
|
||||
static int
|
||||
vips_resize_build( VipsObject *object )
|
||||
{
|
||||
VipsResample *resample = VIPS_RESAMPLE( object );
|
||||
VipsResize *resize = (VipsResize *) object;
|
||||
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( object, 4 );
|
||||
|
||||
double a, b, c, d;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
a = resize->h_scale;
|
||||
b = 0.0;
|
||||
c = 0.0;
|
||||
d = resize->v_scale;
|
||||
|
||||
if( vips_affine( resample->in, &t[0], a, b, c, d,
|
||||
"interpolate", resize->interpolate,
|
||||
"idx", resize->idx,
|
||||
"idy", resize->idy,
|
||||
NULL ) ||
|
||||
vips_image_write( t[0], resample->out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_resize_class_init( VipsResizeClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
||||
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_resize_class_init\n" );
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
vobject_class->nickname = "resize";
|
||||
vobject_class->description = _( "resize an image" );
|
||||
vobject_class->build = vips_resize_build;
|
||||
|
||||
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
|
||||
|
||||
VIPS_ARG_DOUBLE( class, "h_scale", 113,
|
||||
_( "Horizontal scale factor" ),
|
||||
_( "Scale image by this factor in the horizontal axis" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsResize, h_scale ),
|
||||
0, 10000000, 0 );
|
||||
|
||||
VIPS_ARG_DOUBLE( class, "v_scale", 114,
|
||||
_( "Vertical scale factor" ),
|
||||
_( "Scale image by this factor in the vertical axis" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsResize, v_scale ),
|
||||
0, 10000000, 0 );
|
||||
|
||||
VIPS_ARG_INTERPOLATE( class, "interpolate", 2,
|
||||
_( "Interpolate" ),
|
||||
_( "Interpolate pixels with this" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsResize, interpolate ) );
|
||||
|
||||
VIPS_ARG_DOUBLE( class, "idx", 115,
|
||||
_( "Input offset" ),
|
||||
_( "Horizontal input displacement" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsResize, idx ),
|
||||
-10000000, 10000000, 0 );
|
||||
|
||||
VIPS_ARG_DOUBLE( class, "idy", 116,
|
||||
_( "Input offset" ),
|
||||
_( "Vertical input displacement" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsResize, idy ),
|
||||
-10000000, 10000000, 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_resize_init( VipsResize *resize )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_resize:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @h_scale: horizontal scale factor
|
||||
* @v_scale: vertical scale factor
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* @interpolate: interpolate pixels with this
|
||||
* @idx: input horizontal offset
|
||||
* @idy: input vertical offset
|
||||
*
|
||||
* @interpolate defaults to bilinear.
|
||||
*
|
||||
* @idx, @idy default to zero.
|
||||
*
|
||||
* See also: vips_shrink(), vips_affine(), #VipsInterpolate.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
vips_resize( VipsImage *in, VipsImage **out,
|
||||
double h_scale, double v_scale, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, v_scale );
|
||||
result = vips_call_split( "affine", ap, in, out, h_scale, v_scale );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
/* simple wrapper over vips_similarity() to make scale / rotate easy from the
|
||||
/* simple wrapper over vips_affine() to make scale / rotate easy from the
|
||||
* command-line
|
||||
*
|
||||
* 3/10/13
|
||||
* - from similarity.c
|
||||
* - from affine.c
|
||||
* 25/10/13
|
||||
* - oops, reverse rotation direction to match the convention used in the
|
||||
* rest of vips
|
||||
|
@ -52,9 +52,12 @@
|
||||
* 30/6/14
|
||||
* - fix interlaced thumbnail output, thanks lovell
|
||||
* 3/8/14
|
||||
* - box shrink less, use interpolator more, if the window_size is large
|
||||
* - box shrink less, use interpolator more, if window_size is large
|
||||
* enough
|
||||
* - default to bicubic + nosharpen if bicubic is available
|
||||
* - 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
|
||||
@ -72,7 +75,7 @@
|
||||
|
||||
#define ORIENTATION ("exif-ifd0-Orientation")
|
||||
|
||||
/* Default settings. We change the default to bicubic + nosharpen in main() if
|
||||
/* Default settings. We change the default to bicubic in main() if
|
||||
* this vips has been compiled with bicubic support.
|
||||
*/
|
||||
|
||||
@ -316,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 );
|
||||
@ -325,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 );
|
||||
}
|
||||
@ -344,7 +347,7 @@ thumbnail_interpolator( VipsObject *process, VipsImage *in )
|
||||
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 ),
|
||||
@ -397,6 +400,7 @@ thumbnail_shrink( VipsObject *process, VipsImage *in,
|
||||
int tile_width;
|
||||
int tile_height;
|
||||
int nlines;
|
||||
double sigma;
|
||||
|
||||
/* RAD needs special unpacking.
|
||||
*/
|
||||
@ -411,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) &&
|
||||
@ -474,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],
|
||||
@ -482,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",
|
||||
@ -511,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 &&
|
||||
@ -530,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
|
||||
@ -687,13 +723,11 @@ main( int argc, char **argv )
|
||||
textdomain( GETTEXT_PACKAGE );
|
||||
setlocale( LC_ALL, "" );
|
||||
|
||||
/* Does this vips have bicubic? Default to that + nosharpen if it
|
||||
/* Does this vips have bicubic? Default to that if it
|
||||
* does.
|
||||
*/
|
||||
if( vips_type_find( "VipsInterpolate", "bicubic" ) ) {
|
||||
if( vips_type_find( "VipsInterpolate", "bicubic" ) )
|
||||
interpolator = "bicubic";
|
||||
convolution_mask = "none";
|
||||
}
|
||||
|
||||
context = g_option_context_new( _( "- thumbnail generator" ) );
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user