Merge remote-tracking branch 'origin/7.40'
Conflicts: ChangeLog TODO configure.ac libvips/foreign/vipspng.c
This commit is contained in:
commit
b24cbb492c
13
ChangeLog
13
ChangeLog
@ -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
16
TODO
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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 = &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>
|
||||
|
||||
</refentry>
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -262,6 +262,7 @@ vips_spcor_correlation( VipsCorrelation *correlation,
|
||||
|
||||
default:
|
||||
g_assert( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
c2 = sqrt( sum2 );
|
||||
|
@ -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 );
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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().
|
||||
*/
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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 );
|
||||
|
Loading…
Reference in New Issue
Block a user