Merge branch 'master' into master

This commit is contained in:
John Cupitt 2020-09-24 10:31:01 +01:00 committed by GitHub
commit 25bd6cce6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 8128 additions and 3191 deletions

View File

@ -1,3 +1,32 @@
14/8/20 started 8.11
- add vips_jpegload_source() and vips_svgload_source() to public C API
- integrate doxygen in build system to generate C++ API docs
- improve C++ API doc comments
- add VipsInterpolate and guint64 support to C++ API
6/9/20 started 8.10.2
- update magicksave/load profile handling [kelilevi]
- better demand hint rules [kaas3000]
- fix tiff thumbnail from buffer and source [vansante]
- in jpegsave, don't set JFIF resolution if we set EXIF resolution
- bump minimum libheif version to 1.3 [lovell]
- dzsave in iiif mode could set info.json dimensions off by one [Linden6]
- pdfload allows dpi and scale to both be set [le0daniel]
- allow gaussblur sigma zero, meaning no blur
- better heif signature detection [lovell]
9/8/20 started 8.10.1
- fix markdown -> xml conversion in doc generation
- remove typedef redefinitions to please old gccs
- fix regression in tiff pyramid thumbnailing [tand826]
- stop 0-length buffer being passed to imagemagick [lovell]
- convert no-profile CMYK to RGB on save [augustocdias]
- ensure SVG loader skips input with chars outside x09-x7F range [lovell]
- better mask sizing in gaussmat [johntrunc]
- fix tiffsave "squash" handling [barryspearce]
- fix jpegload autorotate [chregu]
- only start the background render thread on first use
24/1/20 started 8.10.0
- more conformat IIIF output from dzsave [regisrob]
- add @id to dzsave to set IIIF id property [regisrob]

View File

@ -2,7 +2,7 @@
# also update the version number in the m4 macros below
AC_INIT([vips], [8.10.0], [vipsip@jiscmail.ac.uk])
AC_INIT([vips], [8.11.0], [vipsip@jiscmail.ac.uk])
# required for gobject-introspection
AC_PREREQ(2.62)
@ -17,7 +17,7 @@ AC_CONFIG_MACRO_DIR([m4])
# user-visible library versioning
m4_define([vips_major_version], [8])
m4_define([vips_minor_version], [10])
m4_define([vips_minor_version], [11])
m4_define([vips_micro_version], [0])
m4_define([vips_version],
[vips_major_version.vips_minor_version.vips_micro_version])
@ -37,9 +37,9 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date -u -r $srcdir/ChangeLog`
# binary interface changes backwards compatible?: increment age
# binary interface changes not backwards compatible?: reset age to 0
LIBRARY_CURRENT=54
LIBRARY_REVISION=3
LIBRARY_AGE=12
LIBRARY_CURRENT=55
LIBRARY_REVISION=0
LIBRARY_AGE=13
# patched into include/vips/version.h
AC_SUBST(VIPS_VERSION)
@ -239,6 +239,20 @@ AC_PROG_INSTALL
AC_PROG_LN_S
AM_WITH_DMALLOC
AC_ARG_WITH([doxygen],
AS_HELP_STRING([--without-doxygen], [build without doxygen (default: test)]))
if test x"$with_doxygen" != x"no"; then
AC_CHECK_PROGS([DOXYGEN], [doxygen])
if test "$DOXYGEN"; then
with_doxygen=yes
else
AC_MSG_WARN([doxygen not found; C++ docs will not be generated])
with_doxygen=no
fi
fi
AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
# we need a fully expanded version of $libdir
# without this we get something like
# define VIPS_LIBDIR ${exec_prefix}/lib
@ -248,11 +262,11 @@ test x"$exec_prefix" = x"NONE" && exec_prefix='${prefix}'
# set $expanded_value to the fully-expanded value of the argument
expand () {
eval expanded_value=$1
eval expanded_value=$1
if test x"$expanded_value" != x"$1"; then
expand "$expanded_value"
fi
if test x"$expanded_value" != x"$1"; then
expand "$expanded_value"
fi
}
expand $libdir
@ -834,7 +848,7 @@ AC_ARG_WITH([heif],
AS_HELP_STRING([--without-heif], [build without libheif (default: test)]))
if test x"$with_heif" != x"no"; then
PKG_CHECK_MODULES(HEIF, libheif,
PKG_CHECK_MODULES(HEIF, libheif >= 1.3.0,
[with_heif=yes
have_h265_decoder=`$PKG_CONFIG libheif --variable builtin_h265_decoder`
have_avif_decoder=`$PKG_CONFIG libheif --variable builtin_avif_decoder`
@ -852,7 +866,7 @@ if test x"$with_heif" != x"no"; then
fi
PACKAGES_USED="$PACKAGES_USED libheif"
],
[AC_MSG_WARN([libheif not found; disabling HEIF support])
[AC_MSG_WARN([libheif >= 1.3.0 not found; disabling HEIF support])
with_heif=no
have_h265_decoder=
have_h265_encoder=
@ -862,42 +876,6 @@ if test x"$with_heif" != x"no"; then
)
fi
# heif_context_set_primary_image not in 1.1
if test x"$with_heif" = x"yes"; then
save_LIBS="$LIBS"
LIBS="$LIBS $HEIF_LIBS"
AC_CHECK_FUNCS(heif_context_set_primary_image,[
AC_DEFINE(HAVE_HEIF_CONTEXT_SET_PRIMARY_IMAGE,1,
[define if you have heif_context_set_primary_image.])
],[]
)
LIBS="$save_LIBS"
fi
# heif_encoding_options_alloc not in 1.1
if test x"$with_heif" = x"yes"; then
save_LIBS="$LIBS"
LIBS="$LIBS $HEIF_LIBS"
AC_CHECK_FUNCS(heif_encoding_options_alloc,[
AC_DEFINE(HAVE_HEIF_ENCODING_OPTIONS_ALLOC,1,
[define if you have heif_encoding_options_alloc.])
],[]
)
LIBS="$save_LIBS"
fi
# exif/xmp profile support not in 1.1
if test x"$with_heif" = x"yes"; then
save_LIBS="$LIBS"
LIBS="$LIBS $HEIF_LIBS"
AC_CHECK_FUNCS(heif_context_add_exif_metadata,[
AC_DEFINE(HAVE_HEIF_CONTEXT_ADD_EXIF_METADATA,1,
[define if you have heif_context_add_exif_metadata.])
],[]
)
LIBS="$save_LIBS"
fi
# color profile support added in 1.3.3
if test x"$with_heif" = x"yes"; then
save_LIBS="$LIBS"
@ -1306,7 +1284,7 @@ VIPS_LIBS="$ZLIB_LIBS $HEIF_LIBS $MAGICK_LIBS $SPNG_LIBS $PNG_LIBS $IMAGEQUANT_L
# autoconf hates multi-line AC_SUBST so we have to have another copy of this
# thing
VIPS_CONFIG="native win32: $vips_os_win32, native OS X: $vips_os_darwin, open files in binary mode: $vips_binary_open, enable debug: $enable_debug, enable deprecated library components: $enable_deprecated, enable docs with gtkdoc: $enable_gtk_doc, gobject introspection: $found_introspection, enable radiance support: $with_radiance, enable analyze support: $with_analyze, enable PPM support: $with_ppm, use fftw3 for FFT: $with_fftw, Magick package: $with_magickpackage, Magick API version: $magick_version, load with libMagick: $enable_magickload, save with libMagick: $enable_magicksave, accelerate loops with orc: $with_orc, ICC profile support with lcms: $with_lcms, file import with niftiio: $with_nifti, file import with libheif: $with_heif, file import with OpenEXR: $with_OpenEXR, file import with OpenSlide: $with_openslide, file import with matio: $with_matio, PDF import with PDFium: $with_pdfium, PDF import with poppler-glib: $with_poppler, SVG import with librsvg-2.0: $with_rsvg, zlib: $with_zlib, file import with cfitsio: $with_cfitsio, file import/export with libwebp: $with_libwebp, text rendering with pangoft2: $with_pangoft2, file import/export with libspng: $with_libspng, file import/export with libpng: $with_png, support 8bpp PNG quantisation: $with_imagequant, file import/export with libtiff: $with_tiff, file import/export with giflib: $with_giflib, file import/export with libjpeg: $with_jpeg, image pyramid export: $with_gsf, use libexif to load/save JPEG metadata: $with_libexif"
VIPS_CONFIG="native win32: $vips_os_win32, native OS X: $vips_os_darwin, open files in binary mode: $vips_binary_open, enable debug: $enable_debug, enable deprecated library components: $enable_deprecated, enable docs with gtkdoc: $enable_gtk_doc, gobject introspection: $found_introspection, enable radiance support: $with_radiance, enable analyze support: $with_analyze, enable PPM support: $with_ppm, generate C++ docs: $with_doxygen, use fftw3 for FFT: $with_fftw, Magick package: $with_magickpackage, Magick API version: $magick_version, load with libMagick: $enable_magickload, save with libMagick: $enable_magicksave, accelerate loops with orc: $with_orc, ICC profile support with lcms: $with_lcms, file import with niftiio: $with_nifti, file import with libheif: $with_heif, file import with OpenEXR: $with_OpenEXR, file import with OpenSlide: $with_openslide, file import with matio: $with_matio, PDF import with PDFium: $with_pdfium, PDF import with poppler-glib: $with_poppler, SVG import with librsvg-2.0: $with_rsvg, zlib: $with_zlib, file import with cfitsio: $with_cfitsio, file import/export with libwebp: $with_libwebp, text rendering with pangoft2: $with_pangoft2, file import/export with libspng: $with_libspng, file import/export with libpng: $with_png, support 8bpp PNG quantisation: $with_imagequant, file import/export with libtiff: $with_tiff, file import/export with giflib: $with_giflib, file import/export with libjpeg: $with_jpeg, image pyramid export: $with_gsf, use libexif to load/save JPEG metadata: $with_libexif"
AC_SUBST(VIPS_LIBDIR)
@ -1345,6 +1323,7 @@ AC_CONFIG_FILES([
libvips/mosaicing/Makefile
libvips/create/Makefile
libvips/resample/Makefile
cplusplus/Doxyfile
cplusplus/include/Makefile
cplusplus/include/vips/Makefile
cplusplus/Makefile
@ -1379,6 +1358,7 @@ gobject introspection: $found_introspection
enable radiance support: $with_radiance
enable analyze support: $with_analyze
enable PPM support: $with_ppm
generate C++ docs: $with_doxygen
* optional dependencies
use fftw3 for FFT: $with_fftw

4
cplusplus/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
html
latex
Doxyfile
doxygen.stamp

2590
cplusplus/Doxyfile.in Normal file

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,24 @@ vips-operators.cpp:
echo "// this file is generated automatically, do not edit!" >> vips-operators.cpp; \
./gen-operators.py -g cpp >> vips-operators.cpp
if HAVE_DOXYGEN
html_DATA = html
html: doxygen.stamp
doxygen.stamp: Doxyfile
$(DOXYGEN) $^
touch doxygen.stamp
install-htmlDATA:
-mkdir -p $(DESTDIR)$(htmldir)
-cp -r html $(DESTDIR)$(htmldir)
endif
EXTRA_DIST = \
README \
README.md \
vips-operators.cpp \
Doxyfile.in \
gen-operators.py

View File

@ -1,5 +0,0 @@
This is the vips8 C++ binding.
The old vips7 binding is still there, but this one is better. See the vips API
docs for documentation.

258
cplusplus/README.md Normal file
View File

@ -0,0 +1,258 @@
### Introduction
This API is a thing layer over the libvips GObject API. it adds automatic
reference counting, exceptions, operator overloads, and automatic constant
expansion.
You can drop down to the C API at any point, so all the C API docs also
work for C++.
### Example
/* compile with:
* g++ -g -Wall example.cc `pkg-config vips-cpp --cflags --libs`
*/
#include <vips/vips8>
using namespace vips;
int
main (int argc, char **argv)
{
if (VIPS_INIT (argv[0]))
vips_error_exit (NULL);
if (argc != 3)
vips_error_exit ("usage: %s input-file output-file", argv[0]);
VImage in = VImage::new_from_file (argv[1],
VImage::option ()->set ("access", VIPS_ACCESS_SEQUENTIAL));
double avg = in.avg ();
printf ("avg = %g\n", avg);
printf ("width = %d\n", in.width ());
in = VImage::new_from_file (argv[1],
VImage::option ()->set ("access", VIPS_ACCESS_SEQUENTIAL));
VImage out = in.embed (10, 10, 1000, 1000,
VImage::option ()->
set ("extend", "background")->
set ("background", 128));
out.write_to_file (argv[2]);
vips_shutdown ();
return 0;
}
Everything before `VImage in = VImage::new_from_file()` is exactly as the C
API. `vips_error_exit()` just prints the arguments plus the libvips error
log and exits with an error code.
`VImage::new_from_file()` is the C++ equivalent of
`vips_image_new_from_file()`. It works in the same way, the differences being:
- VImage lifetime is managed automatically, like a smart pointer. You don't
need to call `g_object_unref()`.
- Instead of using varargs and a `NULL`-terminated option list, this
function takes an optional `VOption` pointer. This gives a list of name /
value pairs for optional arguments to the function.
In this case we request unbuffered IO for the image, meaning, we expect
to do a single top-to-bottom scan of the image and do not need it to be
decompressed entirely. You can use the C enum name, as is done in this
case, or use a string and have the string looked up. See below.
The function will delete the `VOption` pointer for us when
it's finished with it.
- Instead of returning `NULL` on error, this constructor will raise a `VError`
exception.
There are a series of similar constructors which parallel the
other constructors in the C API, see `VImage::new_from_memory()`,
`VImage::new_from_buffer()`, and `VImage::new_matrix()`.
The convenience function `VImage::new_from_image()` makes a constant image
from an existing image. The image it returns will have the same size,
interpretation, resolution and format as the image you call it on, but with
every pixel having the constant value you specify. For example:
new_image = image.new_from_image (12);
Now `new_image` has the same size as `image`, but has one band, and every
pixel has the value 12. You can pass a `std::vector<double>` as the
argument to make a constant image with a different number of bands.
There's also `VImage::new_memory()` and `VImage::new_temp_file()`, which when
written to with `VImage::write()` will create whole images on memory or on disc.
The next line finds the average pixel value, it's the equivalent of the
`vips_avg()` function. The differences from the C API are:
- `VImage::avg()` is a member function: the `this`
parameter is the first (the only, in this case) input image.
- The function returns the first output parameter, in this case the
average pixel value. Other return values are via pointer arguments,
as in the C API.
- Like `VImage::new_from_file()`, function raises the `VError`
exception on error.
- Like `VImage::new_from_file()`, extra arguments are passed
via an optional `VOption` parameter. There are none
in this case, so the function brackets can be left empty.
All other operations follow the same pattern, for example the C API call
`vips_add(`):
int vips_add (VipsImage *left, VipsImage *right, VipsImage **out, ...);
appears in C++ as:
VImage VImage::add (VImage right, VOption *options) const
The next line uses `VImage::width()` to get the image width in pixels.
There are similar functions paralleling `vips_image_get_format()` and
friends. Use `VImage::set()` to set metadata fields, `VImage::get_int()` and
c. to fetch metadata.
Next we reload the image. The `VImage::avg()` will have scanned the image
and reached the end of the file, we need to scan again for the next
operation. If we'd selected random access mode (the default) in the
original `VImage::new_from_file()`, we would not need to reload.
The next line runs `vips_embed()` with two optional parameters. The first
sets the value to an enum (here we use a string to set the value, it'll be
looked up in the list of possible enum values, or you can use the symbols
from the C API), the second sets the value to an `int`. The `"background"`
parameter is actually a `VipsArrayDouble`: if you pass an `int` instead,
it will be automatically converted to a one-element array for you. You can
pass a `std::vector<double>` too: the utility function `VImage::to_vectorv()`
is a convenient way to make one.
Finally, `VImage::write_to_file()` will write the new image to the
filesystem. You can add a `VOption` as a final parameter and set options for
the writer if you wish. Again, the operation will throw a `VError` exception
on error. The other writers from the C API are also present: you can write
to a memory array, to a formatted image in memory, or to another image.
The API docs have a [handy table of all vips
operations](libvips/API/current/func-list.html), if you want to find out
how to do something, try searching that.
### Automatic constant expansion
The C++ API will automatically turn constants into images in some cases.
For example, you can join two images together bandwise (the
bandwise join of two RGB images would be a six-band image) with:
VImage rgb = ...;
VImage six_band = rgb.bandjoin (rgb);
You can also bandjoin a constant, for example:
VImage rgb_with_alpha = rgb.bandjoin (255);
Will add an extra band to an image, with every element in the new band having
the value 255. This is quite a general feature. You can use a constant in
most places where you can use an image and it will be converted. For example:
VImage a = (a < 128).ifthenelse (128, a);
Will set every band element of `a` less than 128 to 128.
The C++ API includes the usual range of arithmetic operator overloads.
You can mix constants, vectors and images freely.
The API overloads `[]` to be `vips_extract_band()`. You can
write:
VImage xyz = VImage::xyz (256, 256) - VImage::to_vectorv (2, 128.0, 128.0);
VImage mask = (xyz[0].pow (2) + xyz[1].pow (2)).pow (0.5) < 100;
to make a circular mask, for example.
The API overloads `()` to be `vips_getpoint()`. You can write:
VImage xyz = VImage::xyz (256, 256) - VImage::to_vectorv (2, 128.0, 128.0);
// this will have the value [0, 0]
std::vector<double> point = xyz (128, 128);
### Enum expansion
libvips operations which implement several functions with a controlling
enum, such as `vips_math()`, are expanded to a set of member functions
named after the enum. For example, the C function:
int vips_math (VipsImage *in, VipsImage **out, VipsOperationMath math, ...);
where `VipsOperationMath` has the member `VIPS_OPERATION_MATH_SIN`, has a
C convenience function `vips_sin()`:
int vips_sin (VipsImage *in, VipsImage **out, ...);
and a C++ member function `VImage::sin()`:
VImage VImage::sin (VOption *options = 0) const
### Image metadata
libvips images can have a lot of metadata attached to them, giving things like
ICC profiles, EXIF data, and so on. You can use the command-line program
`vipsheader` with the `-a` flag to list all the fields.
You can read metadata items with the member functions `get_int()`,
`get_double()`, `get_string()` and `get_blob()`. Use `get_typeof()` to call
`vips_image_get_typeof()` and read the type of an item. This will return 0
for undefined fields.
const char *VImage::get_string (const char *field);
You can use the `set()` family of overloaded members to set metadata,
for example:
void VImage::set (const char *field, const char *value);
You can use these functions to manipulate exif metadata, for example:
VImage im = VImage::new_from_file ("x.jpg")
int orientation = im.get_int (VIPS_META_ORIENTATION);
im.set (VIPS_META_ORIENTATION, 2);
im.write_to_file ("y.jpg");
### Extending the C++ interface
The C++ interface comes in two parts. First, `VImage8.h` defines a simple
layer over `GObject` for automatic reference counting, then a generic way
to call any vips8 operation with `VImage::call()`, then a few convenience
functions, then a set of overloads.
The member definition and declaration for each operation, for
example `VImage::add()`, is generated by a small Python program called
`gen-operators.py`. If you write a new libvips operator, you'll need to rerun
this program to make it visible in the C++ interface.
You can write the wrapper yourself, of course, they are very simple.
The one for `VImage::add()` looks like this:
VImage VImage::add (VImage right, VOption *options) const
{
VImage out;
call("add",
(options ? options : VImage::option())->
set("out", &amp;out)->
set("left", *this)->
set("right", right));
return out;
}
Where `VImage::call()` is the generic call-a-vips8-operation function.

View File

@ -110,19 +110,6 @@ VSource::new_from_options( const char *options )
return( out );
}
VOption *
VOption::set( const char *name, const VSource value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_SOURCE );
g_value_set_object( &pair->value, value.get_source() );
options.push_back( pair );
return( this );
}
VTarget
VTarget::new_to_descriptor( int descriptor )
{
@ -162,17 +149,4 @@ VTarget::new_to_memory()
return( out );
}
VOption *
VOption::set( const char *name, const VTarget value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_TARGET );
g_value_set_object( &pair->value, value.get_target() );
options.push_back( pair );
return( this );
}
VIPS_NAMESPACE_END

View File

@ -51,6 +51,12 @@
VIPS_NAMESPACE_START
/**
* \namespace vips
*
* General docs for the vips namespace.
*/
std::vector<double>
to_vectorv( int n, ... )
{
@ -140,6 +146,20 @@ VOption::set( const char *name, int value )
return( this );
}
// input guint64
VOption *
VOption::set( const char *name, guint64 value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, G_TYPE_UINT64 );
g_value_set_uint64( &pair->value, value );
options.push_back( pair );
return( this );
}
// input double
VOption *
VOption::set( const char *name, double value )
@ -167,39 +187,17 @@ VOption::set( const char *name, const char *value )
return( this );
}
// input image
// input vips object (image, source, target, etc. etc.)
VOption *
VOption::set( const char *name, const VImage value )
VOption::set( const char *name, const VObject value )
{
Pair *pair = new Pair( name );
VipsObject *object = value.get_object();
GType type = G_OBJECT_TYPE( object );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_IMAGE );
g_value_set_object( &pair->value, value.get_image() );
options.push_back( pair );
return( this );
}
// input double array
VOption *
VOption::set( const char *name, std::vector<double> value )
{
Pair *pair = new Pair( name );
double *array;
unsigned int i;
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_ARRAY_DOUBLE );
vips_value_set_array_double( &pair->value, NULL,
static_cast< int >( value.size() ) );
array = vips_value_get_array_double( &pair->value, NULL );
for( i = 0; i < value.size(); i++ )
array[i] = value[i];
g_value_init( &pair->value, type );
g_value_set_object( &pair->value, object );
options.push_back( pair );
return( this );
@ -229,6 +227,30 @@ VOption::set( const char *name, std::vector<int> value )
return( this );
}
// input double array
VOption *
VOption::set( const char *name, std::vector<double> value )
{
Pair *pair = new Pair( name );
double *array;
unsigned int i;
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_ARRAY_DOUBLE );
vips_value_set_array_double( &pair->value, NULL,
static_cast< int >( value.size() ) );
array = vips_value_get_array_double( &pair->value, NULL );
for( i = 0; i < value.size(); i++ )
array[i] = value[i];
options.push_back( pair );
return( this );
}
// input image array
VOption *
VOption::set( const char *name, std::vector<VImage> value )

View File

@ -60,17 +60,4 @@ VInterpolate::new_from_name( const char *name, VOption *options )
return( out );
}
VOption *
VOption::set( const char *name, const VInterpolate value )
{
Pair *pair = new Pair( name );
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_INTERPOLATE );
g_value_set_object( &pair->value, value.get_interpolate() );
options.push_back( pair );
return( this );
}
VIPS_NAMESPACE_END

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python3
# This file generates the member definitions and declarations for all vips
# operators.
@ -39,6 +39,8 @@ gtype_to_cpp = {
GValue.image_type: 'VImage',
GValue.source_type: 'VSource',
GValue.target_type: 'VTarget',
GValue.guint64_type: 'guint64',
type_from_name('VipsInterpolate'): 'VInterpolate',
GValue.array_int_type: 'std::vector<int>',
GValue.array_double_type: 'std::vector<double>',
GValue.array_image_type: 'std::vector<VImage>',
@ -60,7 +62,7 @@ _OPERATION_DEPRECATED = 8
def get_cpp_type(gtype):
"""Map a gtype to C++ type name we use to represent it.
"""Map a gtype to the C++ type name we use to represent it.
"""
if gtype in gtype_to_cpp:
return gtype_to_cpp[gtype]
@ -85,30 +87,40 @@ def cppize(name):
def generate_operation(operation_name, declaration_only=False):
intro = Introspect.get(operation_name)
required_output = [name for name in intro.required_output if name != intro.member_x]
required_output = [name
for name in intro.required_output if name != intro.member_x]
has_output = len(required_output) >= 1
# Add a C++ style comment block with some additional markings (@param,
# @return)
if declaration_only:
result = '\n/**\n * {}.'.format(intro.description.capitalize())
result = f'\n/**\n * {intro.description.capitalize()}.'
if len(intro.optional_input) > 0:
result += '\n *\n * **Optional parameters**'
for name in intro.optional_input:
details = intro.details[name]
result += f'\n * - **{cppize(name)}** -- '
result += f'{details["blurb"]}, '
result += f'{get_cpp_type(details["type"])}.'
result += '\n *'
for name in intro.method_args:
result += '\n * @param {} {}.' \
.format(cppize(name), intro.details[name]['blurb'])
details = intro.details[name]
result += f'\n * @param {cppize(name)} {details["blurb"]}.'
if has_output:
# skip the first element
for name in required_output[1:]:
result += '\n * @param {} {}.' \
.format(cppize(name), intro.details[name]['blurb'])
details = intro.details[name]
result += f'\n * @param {cppize(name)} {details["blurb"]}.'
result += '\n * @param options Optional options.'
result += '\n * @param options Set of options.'
if has_output:
result += '\n * @return {}.' \
.format(intro.details[required_output[0]]['blurb'])
details = intro.details[required_output[0]]
result += f'\n * @return {details["blurb"]}.'
result += '\n */\n'
else:
@ -120,7 +132,7 @@ def generate_operation(operation_name, declaration_only=False):
# the first output arg will be used as the result
cpp_type = get_cpp_type(intro.details[required_output[0]]['type'])
spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' '
result += '{0}{1}'.format(cpp_type, spacing)
result += f'{cpp_type}{spacing}'
else:
result += 'void '
@ -131,13 +143,13 @@ def generate_operation(operation_name, declaration_only=False):
if operation_name in cplusplus_keywords:
cplusplus_operation += '_image'
result += '{0}( '.format(cplusplus_operation)
result += f'{cplusplus_operation}( '
for name in intro.method_args:
details = intro.details[name]
gtype = details['type']
cpp_type = get_cpp_type(gtype)
spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' '
result += '{0}{1}{2}, '.format(cpp_type, spacing, cppize(name))
result += f'{cpp_type}{spacing}{cppize(name)}, '
# output params are passed by reference
if has_output:
@ -147,9 +159,9 @@ def generate_operation(operation_name, declaration_only=False):
gtype = details['type']
cpp_type = get_cpp_type(gtype)
spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' '
result += '{0}{1}*{2}, '.format(cpp_type, spacing, cppize(name))
result += f'{cpp_type}{spacing}*{cppize(name)}, '
result += 'VOption *options {0})'.format('= 0 ' if declaration_only else '')
result += f'VOption *options {"= 0 " if declaration_only else ""})'
# if no 'this' available, it's a class method and they are all const
if intro.member_x is not None:
@ -167,36 +179,35 @@ def generate_operation(operation_name, declaration_only=False):
name = required_output[0]
cpp_type = get_cpp_type(intro.details[name]['type'])
spacing = '' if cpp_type.endswith(cplusplus_suffixes) else ' '
result += ' {0}{1}{2};\n\n'.format(cpp_type, spacing, cppize(name))
result += f' {cpp_type}{spacing}{cppize(name)};\n\n'
result += ' call( "{0}",\n'.format(operation_name)
result += ' (options ? options : VImage::option())'
result += f' call( "{operation_name}",\n'
result += f' (options ? options : VImage::option())'
if intro.member_x is not None:
result += '->\n'
result += ' set( "{0}", *this )'.format(intro.member_x)
result += f'->\n'
result += f' set( "{intro.member_x}", *this )'
all_required = intro.method_args
if has_output:
# first element needs to be passed by reference
arg = cppize(required_output[0])
result += '->\n'
result += ' set( "{0}", &{1} )' \
.format(required_output[0], arg)
result += f'->\n'
result += f' set( "{required_output[0]}", &{arg} )'
# append the remaining list
all_required += required_output[1:]
for name in all_required:
arg = cppize(name)
result += '->\n'
result += ' set( "{0}", {1} )'.format(name, arg)
result += f'->\n'
result += f' set( "{name}", {arg} )'
result += ' );\n'
if has_output:
result += '\n'
result += ' return( {0} );\n'.format(required_output[0])
result += f'\n'
result += f' return( {required_output[0]} );\n'
result += '}'

View File

@ -3,8 +3,7 @@ pkginclude_HEADERS = \
VImage8.h \
VInterpolate8.h \
VConnection8.h \
vips8 \
vips-operators.h
vips8
vips-operators.h:
echo "// headers for vips operations" > vips-operators.h; \

View File

@ -34,30 +34,59 @@
VIPS_NAMESPACE_START
class VSource : VObject
/**
* A generic source object. These supply a stream of bytes that loaders can
* use to fetch image files, see VImage::new_from_source().
*
* Methods let you can connect a source up to memory, a file or
* a file descriptor. Use vips::VSourceCustom to implement custom sources
* using GObject signals.
*/
class VSource : public VObject
{
public:
/**
* Wrap a VSource around an underlying VipsSource object.
*/
VSource( VipsSource *input, VSteal steal = STEAL ) :
VObject( (VipsObject *) input, steal )
{
}
static
VSource new_from_descriptor( int descriptor );
/**
* Make a new VSource from a file descriptor.
*/
static VSource
new_from_descriptor( int descriptor );
static
VSource new_from_file( const char *filename );
/**
* Make a new VSource from a file on disc.
*/
static VSource
new_from_file( const char *filename );
static
VSource new_from_blob( VipsBlob *blob );
/**
* Make a new VSource from a binary object.
*/
static VSource
new_from_blob( VipsBlob *blob );
static
VSource new_from_memory( const void *data,
size_t size );
/**
* Make a new VSource from an area of memory.
*/
static VSource
new_from_memory( const void *data, size_t size );
static
VSource new_from_options( const char *options );
/**
* Make a new VSource from a set of options encoded as a string. See
* vips_source_new().
*/
static VSource
new_from_options( const char *options );
/**
* Get a pointer to the underlying VipsSoure object.
*/
VipsSource *
get_source() const
{
@ -66,23 +95,48 @@ public:
};
class VTarget : VObject
/**
* A generic target object. Savers can use these to write a stream of bytes
* somewhere, see VImage::write_to_target().
*
* Methods let you can connect a target up to memory, a file or
* a file descriptor. Use vips::VTargetCustom to implement custom targets
* using GObject signals.
*/
class VTarget : public VObject
{
public:
/**
* Wrap a VTarget around an underlying VipsTarget object.
*/
VTarget( VipsTarget *output, VSteal steal = STEAL ) :
VObject( (VipsObject *) output, steal )
{
}
static
VTarget new_to_descriptor( int descriptor );
/**
* Make a new VTarget which, when written to, will write to a file
* descriptor.
*/
static VTarget
new_to_descriptor( int descriptor );
/**
* Make a new VTarget which, when written to, will write to a file.
*/
static
VTarget new_to_file( const char *filename );
/**
* Make a new VTarget which, when written to, will write to a file
* descriptor.
*/
static
VTarget new_to_memory();
/**
* Get a pointer to the underlying VipsTarget object.
*/
VipsTarget *
get_target() const
{

View File

@ -39,16 +39,35 @@
VIPS_NAMESPACE_START
/**
* The libvips error class. It holds a single string containing an
* internationalized error message in utf-8 encoding.
*/
class VIPS_CPLUSPLUS_API VError : public std::exception {
std::string _what;
public:
/**
* Construct a VError, setting the error message.
*/
VError( const std::string &what ) : _what( what ) {}
/**
* Construct a VError, fetching the error message from the libvips
* error buffer.
*/
VError() : _what( vips_error_buffer() ) {}
virtual ~VError() throw() {}
// Extract string
/**
* Get a reference to the underlying C string.
*/
virtual const char *what() const throw() { return _what.c_str(); }
/**
* Print the error message to a stream.
*/
void ostream_print( std::ostream & ) const;
};

File diff suppressed because it is too large Load Diff

View File

@ -34,17 +34,36 @@
VIPS_NAMESPACE_START
class VInterpolate : VObject
/**
* An interpolation. You can pass one of these to something like
* VImage::affine for it to use to interpolate pixels.
*
* The available interpolators vary a bit with your libvips version and how it
* was built, but will include `nearest`, `bilinear` and `bicubic`. Run
* vips -l interpolate` to see them all.
*/
class VInterpolate : public VObject
{
public:
/**
* Create a VInterpolate that wraps a VipsInterpolate object. If steal
* is STEAL, then this VInterpolate takes over ownership of the libvips
* object and will automatically unref it.
*/
VInterpolate( VipsInterpolate *interpolate, VSteal steal = STEAL ) :
VObject( (VipsObject *) interpolate, steal )
{
}
/**
* Create a VInterpolate from a name, for example `"bicubic"`.
*/
static
VInterpolate new_from_name( const char *name, VOption *options = 0 );
/**
* Get a pointer to the underlying VipsInterpolate object.
*/
VipsInterpolate *
get_interpolate() const
{

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// bodies for vips operations
// Sun 5 Jul 22:36:37 BST 2020
// Mon 17 Aug 18:04:15 BST 2020
// this file is generated automatically, do not edit!
VImage VImage::CMC2LCh( VOption *options ) const

View File

@ -11,10 +11,10 @@
<refnamediv> <refname>Cite</refname> <refpurpose>References to cite for libvips</refpurpose> </refnamediv>
</para>
<para>
Martinez, K. and Cupitt, J. (2005) <ulink url="http://eprints.ecs.soton.ac.uk/12371">VIPS a highly tuned image processing software architecture</ulink>. In Proceedings of IEEE International Conference on Image Processing 2, pp. 574-577, Genova.
Martinez, K. and Cupitt, J. (2005) <link xlink:href="http://eprints.ecs.soton.ac.uk/12371">VIPS a highly tuned image processing software architecture</link>. In Proceedings of IEEE International Conference on Image Processing 2, pp. 574-577, Genova.
</para>
<para>
Cupitt, J. and Martinez, K. (1996) <ulink url="http://eprints.soton.ac.uk/252227/1/vipsspie96a.pdf">VIPS: An image processing system for large images</ulink>, Proc. SPIE, vol. 2663, pp. 1928.
Cupitt, J. and Martinez, K. (1996) <link xlink:href="http://eprints.soton.ac.uk/252227/1/vipsspie96a.pdf">VIPS: An image processing system for large images</link>, Proc. SPIE, vol. 2663, pp. 1928.
</para>

View File

@ -16,7 +16,7 @@
<para>
The libvips test suite is written in Python and exercises every operation in the API. Its also a useful source of examples.
</para>
<refsect3 id="average-a-region-of-interest-box-on-an-image">
<refsect3 xml:id="average-a-region-of-interest-box-on-an-image">
<title>Average a region of interest box on an image</title>
<programlisting language="python">
#!/usr/bin/env python
@ -34,7 +34,7 @@ roi = image.crop(left, top, width, height)
print 'average:', roi.avg()
</programlisting>
</refsect3>
<refsect3 id="libvips-and-numpy">
<refsect3 xml:id="libvips-and-numpy">
<title>libvips and numpy</title>
<para>
You can use <literal>pyvips.Image.new_from_memory()</literal> to make a vips image from an area of memory. The memory array needs to be laid out band-interleaved, as a set of scanlines, with no padding between lines.
@ -113,7 +113,7 @@ vi = pyvips.Image.new_from_memory(linear.data, width, height, bands,
vi.write_to_file(sys.argv[2])
</programlisting>
</refsect3>
<refsect3 id="build-huge-image-mosaic">
<refsect3 xml:id="build-huge-image-mosaic">
<title>Build huge image mosaic</title>
<para>
This makes a 100,000 x 100,000 black image, then inserts all the images you pass on the command-line into it at random positions. libvips is able to run this program in sequential mode: itll open all the input images at the same time, and stream pixels from them as it needs them to generate the output.

View File

@ -16,7 +16,7 @@
<para>
This page tries to explain what the different strategies are and when each is used. If you are running into unexpected memory, disc or CPU use, this might be helpful. <literal>vips_image_new_from_file()</literal> has the official documentation.
</para>
<section xml:id="direct-access">
<refsect3 xml:id="direct-access">
<title>Direct access</title>
<para>
This is the fastest and simplest one. The file is mapped directly into the processs address space and can be read with ordinary pointer access. Small files are completely mapped; large files are mapped in a series of small windows that are shared and which scroll about as pixels are read. Files which are accessed like this can be read by many threads at once, making them especially quick. They also interact well with the computers operating system: your OS will use spare memory to cache recently used chunks of the file.
@ -27,8 +27,8 @@
<para>
libvips has a special direct write mode where pixels can be written directly to the file image. This is used for the <link xlink:href="libvips-draw.html">draw operators</link>.
</para>
</section>
<section xml:id="random-access-via-load-library">
</refsect3>
<refsect3 xml:id="random-access-via-load-library">
<title>Random access via load library</title>
<para>
Some image file formats have libraries which allow true random access to image pixels. For example, libtiff lets you read any tile out of a tiled tiff image very quickly. Because the libraries allow true random access, libvips can simply hook the image load library up to the input of the operation pipeline.
@ -39,8 +39,8 @@
<para>
libvips can load tiled tiff, tiled OpenEXR, FITS and OpenSlide images in this manner.
</para>
</section>
<section xml:id="full-decompression">
</refsect3>
<refsect3 xml:id="full-decompression">
<title>Full decompression</title>
<para>
Many image load libraries do not support random access. In order to use images of this type as inputs to pipelines, libvips has to convert them to a random access format first.
@ -54,8 +54,8 @@
<para>
This is the slowest and most memory-hungry way to read files, but its unavoidable for many file formats. Unless you can use the next one!
</para>
</section>
<section xml:id="sequential-access">
</refsect3>
<refsect3 xml:id="sequential-access">
<title>Sequential access</title>
<para>
This a fairly recent addition to libvips and is a hybrid of the previous two.
@ -90,7 +90,7 @@ $ vips shrink fred.png jim.png 10 10
<para>
This is done automatically in command-line operation. In programs, you need to set <literal>access</literal> to #VIPS_ACCESS_SEQUENTIAL in calls to functions like vips_image_new_from_file().
</para>
</section>
</refsect3>
</refentry>

View File

@ -55,7 +55,7 @@ VipsPel *pixel = VIPS_REGION_ADDR( region, x, y );
g_object_unref( region );
</programlisting>
<para>
The action that <literal>vips_region_prepare()</literal> takes varies with the type of image. If the image is a file on disc, for example, then VIPS will arrange for a section of the file to be read in.
The action that <literal>vips_region_prepare()</literal> takes varies with the type of image. If the image is a file on disc, for example, then VIPS will arrange for a refsect3 of the file to be read in.
</para>
<para>
(* there is an image access mode where you can just use a pointer, but its rarely used)
@ -136,7 +136,7 @@ g_object_unref( region );
<emphasis role="strong">Data sources</emphasis>
</para>
<para>
VIPS has data sources which can supply pixels for processing from a variety of sources. VIPS can stream images from files in VIPS native format, from tiled TIFF files, from binary PPM/PGM/PBM/PFM, from Radiance (HDR) files, from FITS images and from tiled OpenEXR images. VIPS will automatically unpack other formats to temporary disc files for you but this can obviously generate a lot of disc traffic. It also has a special sequential mode for streaming operations on non-random-access formats. Another section in these docs explains <link xlink:href="How-it-opens-files.md.html">how libvips opens a file</link>. One of the sources uses the <link xlink:href="http://www.imagemagick.org">ImageMagick</link> (or optionally <link xlink:href="http://www.graphicsmagick.org">GraphicsMagick</link> library, so VIPS can read any image format that these libraries can read.
VIPS has data sources which can supply pixels for processing from a variety of sources. VIPS can stream images from files in VIPS native format, from tiled TIFF files, from binary PPM/PGM/PBM/PFM, from Radiance (HDR) files, from FITS images and from tiled OpenEXR images. VIPS will automatically unpack other formats to temporary disc files for you but this can obviously generate a lot of disc traffic. It also has a special sequential mode for streaming operations on non-random-access formats. Another refsect3 in these docs explains <link xlink:href="How-it-opens-files.md.html">how libvips opens a file</link>. One of the sources uses the <link xlink:href="http://www.imagemagick.org">ImageMagick</link> (or optionally <link xlink:href="http://www.graphicsmagick.org">GraphicsMagick</link> library, so VIPS can read any image format that these libraries can read.
</para>
<para>
VIPS images are held on disc as a 64-byte header containing basic image information like width, height, bands and format, then the image data as a single large block of pixels, left-to-right and top-to-bottom, then an XML extension block holding all the image metadata, such as ICC profiles and EXIF blocks.

View File

@ -125,14 +125,15 @@ HTML_IMAGES = \
$(top_srcdir)/doc/images/Vips-smp.png
# we have some files in markdown ... convert to docbook for gtk-doc
# pandoc makes sect1 headers, we want refsect3 for gtk-doc
# pandoc makes section headers, we want refsect3 for gtk-doc
.md.xml:
pandoc -s --template="$(realpath pandoc-docbook-template.docbook)" --wrap=none -V title="$<" -f markdown+smart -t docbook-smart -o $@ $<
sed -e s/sect1/refsect3/g < $@ > x && mv x $@
sed -e s/section/refsect3/g < $@ > x && mv x $@
# Our markdown source files
markdown_content_files = \
How-it-works.md \
libvips-from-C++.md \
Using-vipsthumbnail.md \
How-it-opens-files.md \
Examples.md \
@ -149,7 +150,6 @@ content_files = \
using-command-line.xml \
using-C.xml \
using-threads.xml \
using-cpp.xml \
extending.xml \
function-list.xml \
file-format.xml \
@ -162,7 +162,6 @@ expand_content_files = \
using-command-line.xml \
using-C.xml \
using-threads.xml \
using-cpp.xml \
extending.xml \
function-list.xml \
file-format.xml \

View File

@ -11,7 +11,7 @@
<refnamediv> <refname>Pyramids</refname> <refpurpose>How to use libvips to make image pyramids</refpurpose> </refnamediv>
</para>
<para>
libvips includes <literal>vips_dzsave()</literal>, an operation that can build image pyramids compatible with <ulink url="http://en.wikipedia.org/wiki/Deep_Zoom">DeepZoom</ulink>, Zoomify and <ulink url="https://developers.google.com/maps/">Google Maps</ulink> image viewers. Its fast and can generate pyramids for large images using only a small amount of memory.
libvips includes <literal>vips_dzsave()</literal>, an operation that can build image pyramids compatible with <link xlink:href="http://en.wikipedia.org/wiki/Deep_Zoom">DeepZoom</link>, Zoomify and <link xlink:href="https://developers.google.com/maps/">Google Maps</link> image viewers. Its fast and can generate pyramids for large images using only a small amount of memory.
</para>
<para>
The TIFF writer, <literal>vips_tiffsave()</literal> can also build tiled pyramidal TIFF images, but thats very simple to use. This page concentrates on the DeepZoom builder.
@ -64,8 +64,8 @@ operation flags: sequential nocache
<para>
You can also call <literal>vips_dzsave()</literal> from any language with a libvips binding, or by using <literal>.dz</literal> or <literal>.szi</literal> as an output file suffix.
</para>
<refsect3 id="writing-deepzoom-pyramids">
<title>Writing <ulink url="http://en.wikipedia.org/wiki/Deep_Zoom">DeepZoom</ulink> pyramids</title>
<refsect3 xml:id="writing-deepzoom-pyramids">
<title>Writing <link xlink:href="http://en.wikipedia.org/wiki/Deep_Zoom">DeepZoom</link> pyramids</title>
<para>
The <literal>--layout</literal> option sets the basic mode of operation. With no <literal>--layout</literal>, dzsave writes DeepZoom pyramids. For example:
</para>
@ -85,7 +85,7 @@ $ vips dzsave huge.tif mydz --suffix .jpg[Q=90]
will write JPEG tiles with the quality factor set to 90. You can set any format write options you like, see the API docs for <literal>vips_jpegsave()</literal> for details.
</para>
</refsect3>
<refsect3 id="writing-zoomify-pyramids">
<refsect3 xml:id="writing-zoomify-pyramids">
<title>Writing Zoomify pyramids</title>
<para>
Use <literal>--layout zoomify</literal> to put dzsave into zoomify mode. For example:
@ -100,10 +100,10 @@ $ vips dzsave huge.tif myzoom --layout zoomify
As with DeepZoom, you can use <literal>--suffix</literal> to set jpeg quality.
</para>
</refsect3>
<refsect3 id="writing-google-maps-pyramids">
<title>Writing <ulink url="https://developers.google.com/maps/">Google Maps</ulink> pyramids</title>
<refsect3 xml:id="writing-google-maps-pyramids">
<title>Writing <link xlink:href="https://developers.google.com/maps/">Google Maps</link> pyramids</title>
<para>
Use <literal>--layout google</literal> to write Google maps-style pyramids. These are compatible with <ulink url="http://leafletjs.com/">leaflet</ulink>. For example:
Use <literal>--layout google</literal> to write Google maps-style pyramids. These are compatible with <link xlink:href="http://leafletjs.com/">leaflet</link>. For example:
</para>
<programlisting>
$ vips dzsave wtc.tif gmapdir --layout google
@ -127,7 +127,7 @@ $ vips dzsave wtc.tif gmapdir --layout google
$ vips dzsave wtc.tif gmapdir --layout google --background 0 --centre
</programlisting>
</refsect3>
<refsect3 id="other-options">
<refsect3 xml:id="other-options">
<title>Other options</title>
<para>
You can use <literal>--tile-size</literal> and <literal>--overlap</literal> to control how large the tiles are and how they overlap (obviously). They default to the correct values for the selected layout.
@ -151,7 +151,7 @@ $ vips dzsave wtc.tif mypyr.zip
Use <literal>--properties</literal> to output an XML file called <literal>vips-properties.xml</literal>. This contains a dump of all the metadata vips has about the image as a set of name-value pairs. Its handy with openslide image sources.
</para>
</refsect3>
<refsect3 id="preprocessing-images">
<refsect3 xml:id="preprocessing-images">
<title>Preprocessing images</title>
<para>
You can use <literal>.dz</literal> as a filename suffix, meaning send the image to <literal>vips_dzsave()</literal>. This means you can write the output of any vips operation to a pyramid. For example:
@ -172,7 +172,7 @@ $ vips dzsave CMU-1.mrxs[level=1] x
Will pull out level 1 (the half-resolution level of an MRXS slide) and make a pyramid from that.
</para>
</refsect3>
<refsect3 id="troubleshooting">
<refsect3 xml:id="troubleshooting">
<title>Troubleshooting</title>
<para>
If you are building vips from source you do need to check the summary at the end of configure carefully. You must have the <literal>libgsf-1-dev</literal> package for <literal>vips_dzsave()</literal> to work.

View File

@ -21,7 +21,7 @@ $filename = &quot;image.jpg&quot;;
$image = Vips\Image::thumbnail($filename, 200, [&quot;height&quot; =&gt; 200]);
$image-&gt;writeToFile(&quot;my-thumbnail.jpg&quot;);
</programlisting>
<section xml:id="libvips-options">
<refsect3 xml:id="libvips-options">
<title>libvips options</title>
<para>
<literal>vipsthumbnail</literal> supports the usual range of vips command-line options. A few of them are useful:
@ -38,8 +38,8 @@ $image-&gt;writeToFile(&quot;my-thumbnail.jpg&quot;);
<para>
<literal>--vips-info</literal> shows a higher level view of the operations that <literal>vipsthumbnail</literal> is running. 
</para>
</section>
<section xml:id="looping">
</refsect3>
<refsect3 xml:id="looping">
<title>Looping</title>
<para>
<literal>vipsthumbnail</literal> can process many images in one command. For example:
@ -48,7 +48,7 @@ $image-&gt;writeToFile(&quot;my-thumbnail.jpg&quot;);
$ vipsthumbnail *.jpg
</programlisting>
<para>
will make a thumbnail for every jpeg in the current directory.  See the <link linkend="output-directory">Output directory</link> section below to see how to change where thumbnails are written.
will make a thumbnail for every jpeg in the current directory.  See the <link linkend="output-directory">Output directory</link> refsect3 below to see how to change where thumbnails are written.
</para>
<para>
<literal>vipsthumbnail</literal> will process images one after the other. You can get a good speedup by running several <literal>vipsthumbnail</literal>s in parallel, depending on how much load you want to put on your system. For example:
@ -56,8 +56,8 @@ $ vipsthumbnail *.jpg
<programlisting>
$ parallel vipsthumbnail ::: *.jpg
</programlisting>
</section>
<section xml:id="thumbnail-size">
</refsect3>
<refsect3 xml:id="thumbnail-size">
<title>Thumbnail size</title>
<para>
You can set the bounding box of the generated thumbnail with the <literal>--size</literal> option. For example:
@ -80,8 +80,8 @@ $ vipsthumbnail shark.jpg --size 200x
<para>
You can append <literal>!</literal> to force a resize to the exact target size, breaking the aspect ratio.
</para>
</section>
<section xml:id="cropping">
</refsect3>
<refsect3 xml:id="cropping">
<title>Cropping</title>
<para>
<literal>vipsthumbnail</literal> normally shrinks images to fit within the box set by <literal>--size</literal>. You can use the <literal>--smartcrop</literal> option to crop to fill the box instead. Excess pixels are trimmed away using the strategy you set. For example:
@ -112,8 +112,8 @@ $ vipsthumbnail owl.jpg --smartcrop attention -s 128
<para>
First it shrinks the image to get the vertical axis to 128 pixels, then crops down to 128 pixels across using the <literal>attention</literal> strategy. This one searches the image for features which might catch a human eye, see <literal>vips_smartcrop()</literal> for details.
</para>
</section>
<section xml:id="linear-light">
</refsect3>
<refsect3 xml:id="linear-light">
<title>Linear light</title>
<para>
Shrinking images involves combining many pixels into one. Arithmetic averaging really ought to be in terms of the number of photons, but (for historical reasons) the values stored in image files are usually related to the voltage that should be applied to the electron gun in a CRT display.
@ -140,8 +140,8 @@ real 0m4.660s
user 0m4.640s
sys 0m0.016s
</programlisting>
</section>
<section xml:id="output-directory">
</refsect3>
<refsect3 xml:id="output-directory">
<title>Output directory</title>
<para>
You set the thumbnail write parameters with the <literal>-o</literal> option. This is a pattern which the input filename is pasted into to produce the output filename. For example:
@ -170,8 +170,8 @@ $ vipsthumbnail fred.jpg ../jim.tif -o mythumbs/tn_%s.jpg
<para>
Now both input files will have thumbnails written to a subdirectory of their current directory.
</para>
</section>
<section xml:id="output-format-and-options">
</refsect3>
<refsect3 xml:id="output-format-and-options">
<title>Output format and options</title>
<para>
You can use <literal>-o</literal> to specify the thumbnail image format too. For example: 
@ -245,8 +245,8 @@ $ vipsthumbnail 42-32157534.jpg -o x.jpg[optimize_coding,strip]
$ ls -l x.jpg
-rw-rr 1 john john 3600 Nov 12 21:27 x.jpg
</programlisting>
</section>
<section xml:id="colour-management">
</refsect3>
<refsect3 xml:id="colour-management">
<title>Colour management</title>
<para>
<literal>vipsthumbnail</literal> will optionally put images through LittleCMS for you. You can use this to move all thumbnails to the same colour space. All web browsers assume that images without an ICC profile are in sRGB colourspace, so if you move your thumbnails to sRGB, you can strip all the embedded profiles. This can save several kb per thumbnail.
@ -290,8 +290,8 @@ $ vipsthumbnail kgdev.jpg --iprofile cmyk
<para>
(As before, the magic string <literal>cmyk</literal> selects a high-quality CMYK profile thats built into libvips, but you can use any CMYK profile you like.)
</para>
</section>
<section xml:id="final-suggestion">
</refsect3>
<refsect3 xml:id="final-suggestion">
<title>Final suggestion</title>
<para>
Putting all this together, I suggest this as a sensible set of options:
@ -303,7 +303,7 @@ $ vipsthumbnail fred.jpg \
-o tn_%s.jpg[optimize_coding,strip] \
--eprofile srgb
</programlisting>
</section>
</refsect3>
</refentry>

View File

@ -16,7 +16,7 @@
<para>
This chapter runs through the four main styles that have been found to work well. If you want to write a new binding, one of these should be close to what you need.
</para>
<section xml:id="dont-bind-the-top-level-c-api">
<refsect3 xml:id="dont-bind-the-top-level-c-api">
<title>Dont bind the top-level C API</title>
<para>
The libvips C API (vips_add() and so on) is very inconvenient and dangerous to use from other languages due to its heavy use of varargs.
@ -117,8 +117,8 @@ main( int argc, char **argv )
<para>
Use vips_operation_get_flags() to get general information about an operator.
</para>
</section>
<section xml:id="compiled-language-which-can-call-c">
</refsect3>
<refsect3 xml:id="compiled-language-which-can-call-c">
<title>Compiled language which can call C</title>
<para>
The C++ binding uses this lower layer to define a function called <literal>VImage::call()</literal> which can call any libvips operator with a not-varargs set of variable arguments.
@ -144,8 +144,8 @@ VImage VImage::invert( VOption *options )
<para>
The <literal>VImage</literal> class also adds automatic reference counting, constant expansion, operator overloads, and various other useful features.
</para>
</section>
<section xml:id="dynamic-language-with-ffi">
</refsect3>
<refsect3 xml:id="dynamic-language-with-ffi">
<title>Dynamic language with FFI</title>
<para>
Languages like Ruby, Python, JavaScript and LuaJIT cant call C directly, but they do support FFI. The bindings for these languages work rather like C++, but use FFI to call into libvips and run operations.
@ -153,14 +153,14 @@ VImage VImage::invert( VOption *options )
<para>
Since these languages are dynamic, they can add another trick: they intercept the method-missing hook and attempt to run any method calls not implemented by the <literal>Image</literal> class as libvips operators. This makes these bindings self-writing: they only contain a small amount of code and just expose everything they find in the libvips class hierarchy.
</para>
</section>
<section xml:id="dynamic-langauge-without-ffi">
</refsect3>
<refsect3 xml:id="dynamic-langauge-without-ffi">
<title>Dynamic langauge without FFI</title>
<para>
PHP does not have FFI, unfortunately, so for this language a small native module implements the general <literal>vips_call()</literal> function for PHP language types, and a larger pure PHP layer makes it convenient to use.
</para>
</section>
<section xml:id="gobject-introspection">
</refsect3>
<refsect3 xml:id="gobject-introspection">
<title>gobject-introspection</title>
<para>
The C source code to libvips has been marked up with special comments describing the interface in a standard way. These comments are read by the <literal>gobject-introspection</literal> package when libvips is compiled and used to generate a typelib, a description of how to call the library. Many languages have gobject-introspection packages: all you need to do to call libvips from your favorite language is to start g-o-i, load the libvips typelib, and you should have the whole library available. For example, from Python its as simple as:
@ -177,8 +177,8 @@ from gi.repository import Vips
<para>
If you have a choice, I would recommend simply using FFI.
</para>
</section>
<section xml:id="documentation">
</refsect3>
<refsect3 xml:id="documentation">
<title>Documentation</title>
<para>
You can generate searchable docs from a <code>.gir</code> (the thing that is built from scanning libvips and which in turn turn the typelib is made from) with <command>g-ir-doc-tool</command>, for example:
@ -202,7 +202,7 @@ $ yelp-build html .
<para>
To make HTML docs. This is an easy way to see what you can call in the library.
</para>
</section>
</refsect3>
</refentry>

View File

@ -7,32 +7,32 @@
]>
<book id="index">
<bookinfo>
<title>VIPS Reference Manual</title>
<title>libvips Reference Manual</title>
<releaseinfo>
For VIPS @VIPS_VERSION@.
For libvips @VIPS_VERSION@.
The latest version of this documentation can be found on the
<ulink role="online-location"
url="http://libvips.github.io/libvips/API/current/">VIPS website</ulink>.
url="http://libvips.github.io/libvips/API/current/">libvips website</ulink>.
</releaseinfo>
</bookinfo>
<chapter>
<title>VIPS Overview</title>
<title>libvips Overview</title>
<para>
VIPS is a free image processing system. It is good with large
libvips is a free image processing system. It is good with large
images (images larger than the amount of RAM you have available), with
many CPUs (speed scales linearly to at least 32 threads), for working
with colour, for scientific analysis and for general research
and development. As well as JPEG, TIFF and PNG images, it also
supports scientific formats like FITS, Matlab, Analyze, PFM,
Radiance and OpenSlide. It works on many UNIX-like platforms,
as well as Windows and OS X. VIPS is released under the GNU Library
as well as Windows and OS X. libvips is released under the GNU Library
General Public License (GNU LGPL).
</para>
<xi:include href="xml/using-command-line.xml"/>
<xi:include href="xml/using-C.xml"/>
<xi:include href="xml/using-cpp.xml"/>
<xi:include href="xml/libvips-from-C++.xml"/>
<xi:include href="xml/binding.xml"/>
<xi:include href="xml/extending.xml"/>
<xi:include href="xml/function-list.xml"/>
@ -47,7 +47,7 @@
</chapter>
<chapter>
<title>Core VIPS API</title>
<title>Core libvips API</title>
<xi:include href="xml/vips.xml"/>
<xi:include href="xml/image.xml"/>
<xi:include href="xml/header.xml"/>
@ -67,7 +67,7 @@
</chapter>
<chapter>
<title>VIPS operation API by section</title>
<title>libvips operation API by section</title>
<xi:include href="xml/arithmetic.xml"/>
<xi:include href="xml/colour.xml"/>
<xi:include href="xml/conversion.xml"/>

17
doc/libvips-from-C++.md Normal file
View File

@ -0,0 +1,17 @@
<refmeta>
<refentrytitle>libvips from C++</refentrytitle>
<manvolnum>3</manvolnum>
<refmiscinfo>libvips</refmiscinfo>
</refmeta>
<refnamediv>
<refname>C++</refname>
<refpurpose>Using libvips from C++</refpurpose>
</refnamediv>
libvips comes with a convenient C++ API. It is a very thin wrapper over the
C API and adds automatic reference counting, exceptions, operator
overloads, and automatic constant expansion.
The documentation for this API is in a [different part of the
website](https://libvips.github.io/libvips/API/current/cpp).

21
doc/libvips-from-C++.xml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<refentry id="libvips-from-C++.md">
<para>
<refmeta> <refentrytitle>libvips from C++</refentrytitle> <manvolnum>3</manvolnum> <refmiscinfo>libvips</refmiscinfo> </refmeta>
</para>
<para>
<refnamediv> <refname>C++</refname> <refpurpose>Using libvips from C++</refpurpose> </refnamediv>
</para>
<para>
libvips comes with a convenient C++ API. It is a very thin wrapper over the C API and adds automatic reference counting, exceptions, operator overloads, and automatic constant expansion.
</para>
<para>
The documentation for this API is in a <link xlink:href="https://libvips.github.io/libvips/API/current/cpp">different part of the website</link>.
</para>
</refentry>

View File

@ -1,403 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<!-- vim: set ts=2 sw=2 expandtab: -->
<refentry id="using-from-cpp">
<refmeta>
<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 from C++</refpurpose>
</refnamediv>
<refsect3 id="using-cpp">
<title>Introduction</title>
<para>
VIPS comes with a convenient C++ API. It is a very thin wrapper over the
C API and adds automatic reference counting, exceptions, operator
overloads, and automatic constant expansion. You can drop down to the C
API at any point, so all the C API docs also work for C++.
</para>
<programlisting language="cpp">
/* compile with:
* g++ -g -Wall example.cc `pkg-config vips-cpp --cflags --libs`
*/
#include &lt;vips/vips8&gt;
using namespace vips;
int
main (int argc, char **argv)
{
if (VIPS_INIT (argv[0]))
vips_error_exit (NULL);
if (argc != 3)
vips_error_exit ("usage: %s input-file output-file", argv[0]);
VImage in = VImage::new_from_file (argv[1],
VImage::option ()-&gt;set ("access", VIPS_ACCESS_SEQUENTIAL));
double avg = in.avg ();
printf ("avg = %g\n", avg);
printf ("width = %d\n", in.width ());
in = VImage::new_from_file (argv[1],
VImage::option ()-&gt;set ("access", VIPS_ACCESS_SEQUENTIAL));
VImage out = in.embed (10, 10, 1000, 1000,
VImage::option ()-&gt;
set ("extend", "background")-&gt;
set ("background", 128));
out.write_to_file (argv[2]);
vips_shutdown ();
return (0);
}
</programlisting>
<para>
Everything before <code>VImage in = VImage::..</code> is exactly
as the C API. vips_error_exit() just prints the arguments plus the
libvips error log and exits with an error code.
</para>
<para>
<code>VImage in = VImage::..</code> is the C++ equivalent of
vips_image_new_from_file(). It works
in the same way, the differences being:
<itemizedlist>
<listitem>
<para>
<code>VImage</code> lifetime is managed automatically, like a smart
pointer. You don't need to call g_object_unref().
</para>
</listitem>
<listitem>
<para>
Instead of using varargs and a %NULL-terminated option list, this
function takes an optional <code>VOption</code> pointer. This
gives a list of name / value pairs for optional arguments to the
function.
</para>
<para>
In this case we request unbuffered IO for the image, meaning, we
expect to do a single top-to-bottom scan of the image and do not
need it to be decompressed entirely. You can use the C enum name,
as is done in this case, or use a string and have the string
looked up. See below.
</para>
<para>
The function will delete the <code>VOption</code> pointer for
us when it's finished with it.
</para>
</listitem>
<listitem>
<para>
Instead of returning %NULL on error, this constructor will
raise a <code>VError</code> exception.
</para>
</listitem>
</itemizedlist>
There are a series of similar constructors which parallel the other
constructors in the C API, see VImage::new_from_memory(),
VImage::new_from_buffer(), and VImage::new_matrix().
</para>
<para>
The convenience function `VImage::new_from_image()` makes a constant
image from an existing image. The image it returns will have the same
size, interpretation, resolution and format as the image you call it on,
but with every pixel having the constant value you specify. For example:
<programlisting language="cpp">
new_image = image.new_from_image(12);
</programlisting>
Now `new_image` has the same size as `image`, but has one band, and every
pixel has the value 12. You can pass a `std::vector&lt;double&gt;` as the
argument to make a constant image with a different number of bands.
</para>
<para>
There's also
VImage::new_memory() and VImage::new_temp_file(), which when written to
with VImage::write() will create whole images on memory or on disc.
</para>
<para>
The next line finds the average pixel value, it's the equivalent of the
vips_avg() function. The differences from the C API are:
<itemizedlist>
<listitem>
<para>
VImage::avg() is a member function: the <code>this</code>
parameter is the first (the only, in this case) input image.
</para>
<para>
The function returns the first output parameter, in this case the
average pixel value. Other return values are via pointer arguments,
as in the C API.
</para>
<para>
Like VImage::new_from_file(), function raises the
<code>VError</code> exception on error.
</para>
<para>
Like VImage::new_from_file(), extra arguments are passed
via an optional <code>VOption</code> parameter. There are none
in this case, so the function brackets can be left empty.
</para>
</listitem>
</itemizedlist>
All other operations follow the same pattern, for example the C API call
vips_add():
<programlisting language="cpp">
int vips_add( VipsImage *left, VipsImage *right, VipsImage **out, ... );
</programlisting>
appears in C++ as:
<programlisting language="cpp">
VImage VImage::add( VImage right, VOption *options ) const
</programlisting>
</para>
<para>
The next line uses VImage::width() to get the image width in pixels.
There are similar functions paralleling vips_image_get_format() and
friends. Use VImage::set() to set metadata fields, VImage::get_int() and
c. to fetch metadata.
</para>
<para>
Next we reload the image. The VImage::avg() will have scanned the image
and reached the end of the file, we need to scan again for the next
operation. If we'd selected random access mode (the default) in the
original VImage::new_from_file(), we would not need to reload.
</para>
<para>
The next line runs vips_embed() with two optional parameters. The first
sets the value to an enum (here we use a string to set the value, it'll
be looked up in the list of possible enum values, or you can use the
symbols from the C API), the
second sets the value to an <code>int</code>. The
<code>"background"</code>
parameter is actually a #VipsArrayDouble: if you pass an
<code>int</code> instead, it will be automatically converted to a
one-element array for you. You can pass a
<code>std::vector&lt;double&gt;</code> too: the utility function
VImage::to_vectorv() is a convenient way to make one.
</para>
<para>
Finally, VImage::write_to_file() will write the new image to the
filesystem. You can add a #VOption as a final parameter and set options
for the writer if you wish. Again, the operation will throw a #VError
exception on error. The other writers from the C API are also present:
you can write to a memory array, to a formatted image in memory, or to
another image.
</para>
<para>
The API docs have a <link linkend="function-list">handy table of all vips
operations</link>, if you want to find out how to do something, try
searching that.
</para>
</refsect3>
<refsect3 id="cpp-expansion">
<title>Automatic constant expansion</title>
<para>
The C++ API will automatically turn constants into images in some cases.
For example, you can join two images together bandwise (the
bandwise join of two RGB images would be a six-band image) with:
<programlisting language="cpp">
VImage rgb = ...;
VImage six_band = rgb.bandjoin( rgb );
</programlisting>
You can also bandjoin a constant, for example:
<programlisting language="cpp">
VImage rgb_with_alpha = rgb.bandjoin( 255 );
</programlisting>
Will add an extra band to an image, with every element in the new band
having the value 255. This is quite a general feature. You can use a
constant in most places where you can use an image and it will be
converted. For example:
<programlisting language="cpp">
VImage a = (a &lt; 128).ifthenelse( 128, a );
</programlisting>
Will set every band element of <code>a</code> less than 128 to 128.
</para>
<para>
The C++ API includes the usual range of arithmetic operator overloads.
You can mix constants, vectors and images freely.
</para>
<para>
The API overloads <code>[]</code> to be vips_extract_band(). You can
write:
<programlisting language="cpp">
VImage xyz = VImage::xyz( 256, 256 ) - VImage::to_vectorv( 2, 128.0, 128.0 );
VImage mask = (xyz[0].pow( 2 ) + xyz[1].pow( 2 )).pow( 0.5 ) &lt; 100;
</programlisting>
to make a circular mask, for example.
</para>
<para>
The API overloads <code>()</code> to be vips_getpoint(). You can
write:
<programlisting language="cpp">
VImage xyz = VImage::xyz( 256, 256 ) - VImage::to_vectorv( 2, 128.0, 128.0 );
// this will have the value [0, 0]
std::vector&lt;double&gt; point = xyz(128, 128);
</programlisting>
</para>
</refsect3>
<refsect3 id="cpp-enum">
<title>Enum expansion</title>
<para>
VIPS operations which implement several functions with a controlling
enum, such as vips_math(), are expanded to a set of member functions
named after the enum. For example, the C function:
<programlisting language="cpp">
int vips_math( VipsImage *in, VipsImage **out, VipsOperationMath math, ... );
</programlisting>
where #VipsOperationMath has the member #VIPS_OPERATION_MATH_SIN, has a
C convenience function vips_sin():
<programlisting language="cpp">
int vips_sin( VipsImage *in, VipsImage **out, ... );
</programlisting>
and a C++ member function VImage::sin():
<programlisting language="cpp">
VImage VImage::sin( VOption *options = 0 ) const
</programlisting>
</para>
</refsect3>
<refsect3 id="cpp-metadata">
<title>Image metadata</title>
<para>
VIPS images can have a lot of metadata attached to them, giving things
like ICC profiles, EXIF data, and so on. You can use the command-line
program <code>vipsheader</code> with the <code>-a</code> flag to list
all the fields.
</para>
<para>
You can read metadata items with the member functions
<code>get_int()</code>, <code>get_double()</code>,
<code>get_string()</code> and <code>get_blob()</code>. Use
<code>get_typeof()</code> to call vips_image_get_typeof() and read the
type of an item. This will return 0 for undefined fields.
<programlisting language="cpp">
const char *VImage::get_string( const char *field ) throw( VError );
</programlisting>
</para>
<para>
You can use the <code>set()</code> family of overloaded members to set
metadata, for example:
<programlisting language="cpp">
void VImage::set( const char *field, const char *value );
</programlisting>
</para>
<para>
You can use these functions to manipulate exif metadata, for example:
<programlisting language="cpp">
VImage im = VImage::new_from_file( "x.jpg" )
int orientation = im.get_int( VIPS_META_ORIENTATION );
im.set( VIPS_META_ORIENTATION, 2 );
im.write_to_file( "y.jpg" );
</programlisting>
</para>
</refsect3>
<refsect3 id="cpp-extend">
<title>Extending the C++ interface</title>
<para>
The C++ interface comes in two parts. First, <code>VImage8.h</code>
defines a simple layer over #GObject for automatic reference counting,
then a generic way to call any vips8 operation with VImage::call(),
then a few convenience functions, then a set of overloads.
</para>
<para>
The member definition and declaration for each operation, for example
VImage::add(), is generated by a small Python program called
<code>gen-operators.py</code>. If you write a new VIPS operator, you'll
need to rerun this program to make it visible in the C++ interface.
</para>
<para>
You can write the wrapper yourself, of course, they are very simple.
The one for VImage::add() looks like this:
<programlisting language="cpp">
VImage VImage::add( VImage right, VOption *options ) const
{
VImage out;
call("add",
(options ? options : VImage::option())->
set("out", &amp;out)->
set("left", *this)->
set("right", right));
return out;
}
</programlisting>
Where VImage::call() is the generic call-a-vips8-operation function.
</para>
</refsect3>
</refentry>

View File

@ -424,7 +424,10 @@ vips_arrayjoinv( VipsImage **in, VipsImage **out, int n, va_list ap )
* Smallest common format in
* <link linkend="libvips-arithmetic">arithmetic</link>).
*
* See also: vips_join(), vips_insert().
* vips_colourspace() can be useful for moving the images to a common
* colourspace for compositing.
*
* See also: vips_join(), vips_insert(), vips_colourspace().
*
* Returns: 0 on success, -1 on error
*/

View File

@ -4,6 +4,9 @@
* - from vips_sharpen()
* 19/11/14
* - change parameters to be more imagemagick-like
* 21/9/20
* - allow sigma zero, meaning no blur
* - sigma < 0.2 is just copy
*/
/*
@ -73,23 +76,33 @@ vips_gaussblur_build( VipsObject *object )
if( VIPS_OBJECT_CLASS( vips_gaussblur_parent_class )->build( object ) )
return( -1 );
if( vips_gaussmat( &t[0], gaussblur->sigma, gaussblur->min_ampl,
"separable", TRUE,
"precision", gaussblur->precision,
NULL ) )
return( -1 );
/* vips_gaussmat() will make a 1x1 pixel mask for anything smaller than
* this.
*/
if( gaussblur->sigma < 0.2 ) {
if( vips_copy( gaussblur->in, &t[1], NULL ) )
return( -1 );
}
else {
if( vips_gaussmat( &t[0],
gaussblur->sigma, gaussblur->min_ampl,
"separable", TRUE,
"precision", gaussblur->precision,
NULL ) )
return( -1 );
#ifdef DEBUG
printf( "gaussblur: blurring with:\n" );
vips_matrixprint( t[0], NULL );
printf( "gaussblur: blurring with:\n" );
vips_matrixprint( t[0], NULL );
#endif /*DEBUG*/
g_info( "gaussblur mask width %d", t[0]->Xsize );
g_info( "gaussblur mask width %d", t[0]->Xsize );
if( vips_convsep( gaussblur->in, &t[1], t[0],
"precision", gaussblur->precision,
NULL ) )
return( -1 );
if( vips_convsep( gaussblur->in, &t[1], t[0],
"precision", gaussblur->precision,
NULL ) )
return( -1 );
}
g_object_set( object, "out", vips_image_new(), NULL );
@ -132,7 +145,7 @@ vips_gaussblur_class_init( VipsGaussblurClass *class )
_( "Sigma of Gaussian" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsGaussblur, sigma ),
0.01, 1000, 1.5 );
0.0, 1000, 1.5 );
VIPS_ARG_DOUBLE( class, "min_ampl", 3,
_( "Minimum amplitude" ),

View File

@ -129,7 +129,7 @@ vips_gaussmat_build( VipsObject *object )
vips_error( class->nickname, "%s", _( "mask too large" ) );
return( -1 );
}
width = 2 * x - 1;
width = 2 * VIPS_MAX( x - 1, 0 ) + 1;
height = gaussmat->separable ? 1 : width;
vips_image_init_fields( create->out,

View File

@ -1049,8 +1049,8 @@ write_json( VipsForeignSaveDz *dz )
gsf_output_printf( out,
" \"width\": %d,\n"
" \"height\": %d\n",
dz->layer->image->Xsize,
dz->layer->image->Ysize );
dz->layer->width,
dz->layer->height );
gsf_output_printf( out,
"}\n" );

View File

@ -1336,8 +1336,7 @@ vips__foreign_convert_saveable( VipsImage *in, VipsImage **ready,
}
/* If this image is CMYK and the saver is RGB-only, use lcms to try to
* import to XYZ. This will only work if the image has an embedded
* profile.
* import to XYZ.
*/
if( in->Type == VIPS_INTERPRETATION_CMYK &&
in->Bands >= 4 &&
@ -1348,6 +1347,8 @@ vips__foreign_convert_saveable( VipsImage *in, VipsImage **ready,
if( vips_icc_import( in, &out,
"pcs", VIPS_PCS_XYZ,
"embedded", TRUE,
"input_profile", "cmyk",
NULL ) ) {
g_object_unref( in );
return( -1 );

View File

@ -18,6 +18,8 @@
* - revise for new VipsSource API
* 10/5/20
* - deprecate autorotate -- it's too difficult to support properly
* 31/7/20
* - block broken thumbnails, if we can
*/
/*
@ -252,15 +254,21 @@ static const char *heif_magic[] = {
*
* enum heif_filetype_result result = heif_check_filetype( buf, 12 );
*
* but it's very conservative and seems to be missing some of the Noka hief
* but it's very conservative and seems to be missing some of the Nokia hief
* types.
*/
static int
vips_foreign_load_heif_is_a( const char *buf, int len )
{
if( len >= 12 ) {
const guint chunk_len = GUINT_FROM_BE( *((guint32 *) buf) );
int i;
if( chunk_len > 32 ||
chunk_len % 4 != 0 )
return( 0 );
for( i = 0; i < VIPS_NUMBER( heif_magic ); i++ )
if( strncmp( buf + 4, heif_magic[i], 8 ) == 0 )
return( 1 );
@ -277,6 +285,71 @@ vips_foreign_load_heif_get_flags( VipsForeignLoad *load )
return( VIPS_FOREIGN_SEQUENTIAL );
}
/* We've selcted the page. Try to select the associated thumbnail instead,
* if we can.
*/
static int
vips_foreign_load_heif_set_thumbnail( VipsForeignLoadHeif *heif )
{
heif_item_id thumb_ids[1];
int n_thumbs;
struct heif_image_handle *thumb_handle;
struct heif_image *thumb_img;
struct heif_error error;
double main_aspect;
double thumb_aspect;
n_thumbs = heif_image_handle_get_list_of_thumbnail_IDs(
heif->handle, thumb_ids, 1 );
if( n_thumbs == 0 )
return( 0 );
error = heif_image_handle_get_thumbnail( heif->handle,
thumb_ids[0], &thumb_handle );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
/* Just checking the width and height of the handle isn't
* enough -- we have to experimentally decode it and test the
* decoded dimensions.
*/
error = heif_decode_image( thumb_handle, &thumb_img,
heif_colorspace_RGB,
heif_chroma_interleaved_RGB,
NULL );
if( error.code ) {
VIPS_FREEF( heif_image_handle_release, thumb_handle );
vips__heif_error( &error );
return( -1 );
}
thumb_aspect = (double)
heif_image_get_width( thumb_img, heif_channel_interleaved ) /
heif_image_get_height( thumb_img, heif_channel_interleaved );
VIPS_FREEF( heif_image_release, thumb_img );
main_aspect = (double)
heif_image_handle_get_width( heif->handle ) /
heif_image_handle_get_height( heif->handle );
/* The bug we are working around has decoded thumbs as 512x512
* with the main image as 6kx4k, so a 0.1 threshold is more
* than tight enough to spot the error.
*/
if( fabs( main_aspect - thumb_aspect ) > 0.1 ) {
VIPS_FREEF( heif_image_handle_release, thumb_handle );
return( 0 );
}
VIPS_FREEF( heif_image_handle_release, heif->handle );
heif->handle = thumb_handle;
return( 0 );
}
/* Select a page. If thumbnail is set, select the thumbnail for that page, if
* there is one.
*/
@ -307,26 +380,8 @@ vips_foreign_load_heif_set_page( VipsForeignLoadHeif *heif,
}
if( thumbnail ) {
heif_item_id thumb_ids[1];
int n_thumbs;
struct heif_image_handle *thumb_handle;
n_thumbs = heif_image_handle_get_list_of_thumbnail_IDs(
heif->handle, thumb_ids, 1 );
if( n_thumbs > 0 ) {
error = heif_image_handle_get_thumbnail(
heif->handle,
thumb_ids[0], &thumb_handle );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
VIPS_FREEF( heif_image_handle_release,
heif->handle );
heif->handle = thumb_handle;
}
if( vips_foreign_load_heif_set_thumbnail( heif ) )
return( -1 );
/* If we were asked to select the thumbnail, say we
* did, even if there are no thumbnails and we just

View File

@ -79,6 +79,10 @@ typedef struct _VipsForeignSaveHeif {
*/
VipsForeignHeifCompression compression;
/* CPU effort (0-8).
*/
int speed;
/* The image we save. This is a copy of save->ready since we need to
* be able to update the metadata.
*/
@ -129,7 +133,6 @@ vips_foreign_save_heif_dispose( GObject *gobject )
dispose( gobject );
}
#ifdef HAVE_HEIF_CONTEXT_ADD_EXIF_METADATA
typedef struct heif_error (*libheif_metadata_fn)( struct heif_context *,
const struct heif_image_handle *,
const void *, int );
@ -141,12 +144,10 @@ struct _VipsForeignSaveHeifMetadata {
{ VIPS_META_EXIF_NAME, heif_context_add_exif_metadata },
{ VIPS_META_XMP_NAME, heif_context_add_XMP_metadata }
};
#endif /*HAVE_HEIF_CONTEXT_ADD_EXIF_METADATA*/
static int
vips_foreign_save_heif_write_metadata( VipsForeignSaveHeif *heif )
{
#ifdef HAVE_HEIF_CONTEXT_ADD_EXIF_METADATA
int i;
struct heif_error error;
@ -178,7 +179,6 @@ vips_foreign_save_heif_write_metadata( VipsForeignSaveHeif *heif )
return( -1 );
}
}
#endif /*HAVE_HEIF_CONTEXT_ADD_EXIF_METADATA*/
return( 0 );
}
@ -216,13 +216,9 @@ vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
}
#endif /*HAVE_HEIF_COLOR_PROFILE*/
#ifdef HAVE_HEIF_ENCODING_OPTIONS_ALLOC
options = heif_encoding_options_alloc();
if( vips_image_hasalpha( heif->image ) )
options->save_alpha_channel = 1;
#else /*!HAVE_HEIF_ENCODING_OPTIONS_ALLOC*/
options = NULL;
#endif /*HAVE_HEIF_ENCODING_OPTIONS_ALLOC*/
#ifdef DEBUG
printf( "encoding ..\n" );
@ -230,16 +226,13 @@ vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
error = heif_context_encode_image( heif->ctx,
heif->img, heif->encoder, options, &heif->handle );
#ifdef HAVE_HEIF_ENCODING_OPTIONS_ALLOC
heif_encoding_options_free( options );
#endif /*HAVE_HEIF_ENCODING_OPTIONS_ALLOC*/
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
#ifdef HAVE_HEIF_CONTEXT_SET_PRIMARY_IMAGE
if( vips_image_get_typeof( heif->image, "heif-primary" ) ) {
int primary;
@ -256,7 +249,6 @@ vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
}
}
}
#endif /*HAVE_HEIF_CONTEXT_SET_PRIMARY_IMAGE*/
if( !save->strip &&
vips_foreign_save_heif_write_metadata( heif ) )
@ -363,6 +355,14 @@ vips_foreign_save_heif_build( VipsObject *object )
return( -1 );
}
error = heif_encoder_set_parameter_integer( heif->encoder,
"speed", heif->speed );
if( error.code &&
error.subcode != heif_suberror_Unsupported_parameter ) {
vips__heif_error( &error );
return( -1 );
}
/* TODO .. support extra per-encoder params with
* heif_encoder_list_parameters().
*/
@ -470,6 +470,13 @@ vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class )
VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
VIPS_FOREIGN_HEIF_COMPRESSION_HEVC );
VIPS_ARG_INT( class, "speed", 15,
_( "speed" ),
_( "CPU effort" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, speed ),
0, 8, 5 );
}
static void
@ -478,6 +485,7 @@ vips_foreign_save_heif_init( VipsForeignSaveHeif *heif )
heif->ctx = heif_context_alloc();
heif->Q = 50;
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_HEVC;
heif->speed = 5;
}
typedef struct _VipsForeignSaveHeifFile {
@ -670,6 +678,7 @@ vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target )
* * @Q: %gint, quality factor
* * @lossless: %gboolean, enable lossless encoding
* * @compression: #VipsForeignHeifCompression, write with this compression
* * @speed: %gint, CPU effort, 0 slowest - 8 fastest, AV1 compression only
*
* Write a VIPS image to a file in HEIF format.
*
@ -680,6 +689,9 @@ vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target )
*
* Use @compression to set the encoder e.g. HEVC, AVC, AV1
*
* Use @speed to control the CPU effort spent improving compression.
* This is currently only applicable to AV1 encoders, defaults to 5.
*
* See also: vips_image_write_to_file(), vips_heifload().
*
* Returns: 0 on success, -1 on error.
@ -709,6 +721,7 @@ vips_heifsave( VipsImage *in, const char *filename, ... )
* * @Q: %gint, quality factor
* * @lossless: %gboolean, enable lossless encoding
* * @compression: #VipsForeignHeifCompression, write with this compression
* * @speed: %gint, CPU effort, 0 slowest - 8 fastest, AV1 compression only
*
* As vips_heifsave(), but save to a memory buffer.
*
@ -759,6 +772,7 @@ vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
* * @Q: %gint, quality factor
* * @lossless: %gboolean, enable lossless encoding
* * @compression: #VipsForeignHeifCompression, write with this compression
* * @speed: %gint, CPU effort, 0 slowest - 8 fastest, AV1 compression only
*
* As vips_heifsave(), but save to a target.
*

View File

@ -108,6 +108,8 @@
* - revise for source IO
* 5/5/20 angelmixu
* - better handling of JFIF res unit 0
* 13/9/20
* - set resolution unit from JFIF
*/
/*
@ -506,6 +508,13 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
break;
}
#ifdef DEBUG
if( cinfo->saw_JFIF_marker )
printf( "read_jpeg_header: jfif _density %d, %d, unit %d\n",
cinfo->X_density, cinfo->Y_density,
cinfo->density_unit );
#endif /*DEBUG*/
/* Get the jfif resolution. exif may overwrite this later. Default to
* 72dpi (as EXIF does).
*/
@ -514,12 +523,6 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
if( cinfo->saw_JFIF_marker &&
cinfo->X_density != 1U &&
cinfo->Y_density != 1U ) {
#ifdef DEBUG
printf( "read_jpeg_header: jfif _density %d, %d, unit %d\n",
cinfo->X_density, cinfo->Y_density,
cinfo->density_unit );
#endif /*DEBUG*/
switch( cinfo->density_unit ) {
case 0:
/* X_density / Y_density gives the pixel aspect ratio.
@ -535,6 +538,8 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
*/
xres = cinfo->X_density / 25.4;
yres = cinfo->Y_density / 25.4;
vips_image_set_string( out,
VIPS_META_RESOLUTION_UNIT, "in" );
break;
case 2:
@ -542,6 +547,8 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
*/
xres = cinfo->X_density / 10.0;
yres = cinfo->Y_density / 10.0;
vips_image_set_string( out,
VIPS_META_RESOLUTION_UNIT, "cm" );
break;
default:
@ -837,7 +844,7 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
{
struct jpeg_decompress_struct *cinfo = &jpeg->cinfo;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 4 );
vips_object_local_array( VIPS_OBJECT( out ), 5 );
VipsImage *im;
@ -872,11 +879,13 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
if( jpeg->autorotate &&
vips_image_get_orientation( im ) != 1 ) {
/* This will go via a huge memory buffer :-(
/* We have to copy to memory before calling autorot, since it
* needs random access.
*/
if( vips_autorot( im, &t[3], NULL ) )
if( !(t[3] = vips_image_copy_memory( im )) ||
vips_autorot( t[3], &t[4], NULL ) )
return( -1 );
im = t[3];
im = t[4];
}
if( vips_image_write( im, out ) )

View File

@ -550,3 +550,36 @@ vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... )
return( result );
}
/**
* vips_jpegload_source:
* @source: source to load
* @out: (out): image to write
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* * @shrink: %gint, shrink by this much on load
* * @fail: %gboolean, fail on errors
* * @autorotate: %gboolean, use exif Orientation tag to rotate the image
* during load
*
* Read a JPEG-formatted memory block into a VIPS image. Exactly as
* vips_jpegload(), but read from a source.
*
* See also: vips_jpegload().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_jpegload_source( VipsSource *source, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_call_split( "jpegload_source", ap, source, out );
va_end( ap );
return( result );
}

View File

@ -686,13 +686,13 @@ magick_set_vips_profile_cb( Image *image,
char name_text[256];
VipsBuf vips_name = VIPS_BUF_STATIC( name_text );
if( strcmp( name, "XMP" ) == 0 )
if( strcasecmp( name, "XMP" ) == 0 )
vips_buf_appendf( &vips_name, VIPS_META_XMP_NAME );
else if( strcmp( name, "IPTC" ) == 0 )
else if( strcasecmp( name, "IPTC" ) == 0 )
vips_buf_appendf( &vips_name, VIPS_META_IPTC_NAME );
else if( strcmp( name, "ICM" ) == 0 )
else if( strcasecmp( name, "ICC" ) == 0 )
vips_buf_appendf( &vips_name, VIPS_META_ICC_NAME );
else if( strcmp( name, "EXIF" ) == 0 )
else if( strcasecmp( name, "EXIF" ) == 0 )
vips_buf_appendf( &vips_name, VIPS_META_EXIF_NAME );
else
vips_buf_appendf( &vips_name, "magickprofile-%s", name );
@ -736,7 +736,7 @@ magick_set_magick_profile_cb( VipsImage *im,
else if( strcmp( name, VIPS_META_IPTC_NAME ) == 0 )
vips_buf_appendf( &buf, "IPTC" );
else if( strcmp( name, VIPS_META_ICC_NAME ) == 0 )
vips_buf_appendf( &buf, "ICM" );
vips_buf_appendf( &buf, "ICC" );
else if( strcmp( name, VIPS_META_EXIF_NAME ) == 0 )
vips_buf_appendf( &buf, "EXIF" );
else if( vips_isprefix( "magickprofile-", name ) )

View File

@ -256,7 +256,7 @@ G_DEFINE_TYPE( VipsForeignLoadMagickBuffer, vips_foreign_load_magick_buffer,
static gboolean
vips_foreign_load_magick_buffer_is_a_buffer( const void *buf, size_t len )
{
return( magick_ismagick( (const unsigned char *) buf, len ) );
return( len > 10 && magick_ismagick( (const unsigned char *) buf, len ) );
}
/* Unfortunately, libMagick does not support header-only reads very well. See

View File

@ -16,6 +16,8 @@
* - reopen the input if we minimised too early
* 11/3/20
* - move on top of VipsSource
* 21/9/20
* - allow dpi and scale to both be set [le0daniel]
*/
/*
@ -65,9 +67,6 @@
#include "pforeign.h"
/* TODO ... put minimise support back in.
*/
#if defined(HAVE_POPPLER)
#include <cairo.h>
@ -109,10 +108,14 @@ typedef struct _VipsForeignLoadPdf {
*/
double dpi;
/* Calculate this from DPI. At 72 DPI, we render 1:1 with cairo.
/* Scale by this factor.
*/
double scale;
/* The total scale factor we render with.
*/
double total_scale;
/* Background colour.
*/
VipsArrayDouble *background;
@ -170,8 +173,7 @@ vips_foreign_load_pdf_build( VipsObject *object )
GError *error = NULL;
if( !vips_object_argument_isset( object, "scale" ) )
pdf->scale = pdf->dpi / 72.0;
pdf->total_scale = pdf->scale * pdf->dpi / 72.0;
pdf->stream = vips_g_input_stream_new_from_source( pdf->source );
if( !(pdf->doc = poppler_document_new_from_stream( pdf->stream,
@ -338,8 +340,8 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load )
* does round to nearest. Without this, things like
* shrink-on-load will break.
*/
pdf->pages[i].width = VIPS_RINT( width * pdf->scale );
pdf->pages[i].height = VIPS_RINT( height * pdf->scale );
pdf->pages[i].width = VIPS_RINT( width * pdf->total_scale );
pdf->pages[i].height = VIPS_RINT( height * pdf->total_scale );
if( pdf->pages[i].width > pdf->image.width )
pdf->image.width = pdf->pages[i].width;
@ -376,6 +378,12 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load )
return( 0 );
}
static void
vips_foreign_load_pdf_minimise( VipsImage *image, VipsForeignLoadPdf *pdf )
{
vips_source_minimise( pdf->source );
}
static int
vips_foreign_load_pdf_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
@ -421,10 +429,10 @@ vips_foreign_load_pdf_generate( VipsRegion *or,
cr = cairo_create( surface );
cairo_surface_destroy( surface );
cairo_scale( cr, pdf->scale, pdf->scale );
cairo_scale( cr, pdf->total_scale, pdf->total_scale );
cairo_translate( cr,
(pdf->pages[i].left - rect.left) / pdf->scale,
(pdf->pages[i].top - rect.top) / pdf->scale );
(pdf->pages[i].left - rect.left) / pdf->total_scale,
(pdf->pages[i].top - rect.top) / pdf->total_scale );
/* poppler is single-threaded, but we don't need to lock since
* we're running inside a non-threaded tilecache.
@ -464,25 +472,20 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load )
*/
t[0] = vips_image_new();
/* Don't minimise on ::minimise (end of computation): we support
* threaded read, and minimise will happen outside the cache lock.
/* Close input immediately at end of read.
*/
g_signal_connect( t[0], "minimise",
G_CALLBACK( vips_foreign_load_pdf_minimise ), pdf );
/* Very large strips to limit render calls per page.
*/
vips_foreign_load_pdf_set_image( pdf, t[0] );
if( vips_image_generate( t[0],
NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) )
return( -1 );
/* Don't use tilecache to keep the number of calls to
* pdf_page_render() low. Don't thread the cache, we rely on
* locking to keep pdf single-threaded. Use a large strip size to
* (again) keep the number of calls to page_render low.
*/
if( vips_linecache( t[0], &t[1],
"tile_height", VIPS_MIN( 5000, pdf->pages[0].height ),
NULL ) )
return( -1 );
if( vips_image_write( t[1], load->real ) )
NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) ||
vips_sequential( t[0], &t[1],
"tile_height", VIPS_MIN( 5000, pdf->pages[0].height ),
NULL ) ||
vips_image_write( t[1], load->real ) )
return( -1 );
return( 0 );
@ -862,9 +865,8 @@ vips_foreign_load_pdf_is_a( const char *filename )
* left. Set to -1 to mean "until the end of the document". Use vips_grid()
* to change page layout.
*
* Use @dpi to set the rendering resolution. The default is 72. Alternatively,
* you can scale the rendering from the default 1 point == 1 pixel by
* setting @scale.
* Use @dpi to set the rendering resolution. The default is 72. Additionally,
* you can scale by setting @scale. If you set both, they combine.
*
* Use @background to set the background RGBA colour. The default is 255
* (solid white), use eg. 0 for a transparent background.

View File

@ -62,7 +62,7 @@ typedef struct _VipsForeignSavePpm VipsForeignSavePpm;
typedef int (*VipsSavePpmFn)( VipsForeignSavePpm *, VipsImage *, VipsPel * );
typedef struct _VipsForeignSavePpm {
struct _VipsForeignSavePpm {
VipsForeignSave parent_object;
VipsTarget *target;
@ -74,7 +74,7 @@ typedef struct _VipsForeignSavePpm {
/* Deprecated.
*/
gboolean squash;
} VipsForeignSavePpm;
};
typedef VipsForeignSaveClass VipsForeignSavePpmClass;

View File

@ -303,7 +303,8 @@ vips_foreign_load_png_header( VipsForeignLoad *load )
return( -1 );
}
/*
#ifdef DEBUG
printf( "width: %d\nheight: %d\nbit depth: %d\ncolor type: %d\n",
png->ihdr.width, png->ihdr.height,
png->ihdr.bit_depth, png->ihdr.color_type );
@ -311,7 +312,7 @@ vips_foreign_load_png_header( VipsForeignLoad *load )
"interlace method: %d\n",
png->ihdr.compression_method, png->ihdr.filter_method,
png->ihdr.interlace_method );
*/
#endif /*DEBUG*/
/* Just convert to host-endian if nothing else applies.
*/

View File

@ -211,7 +211,7 @@ vips_foreign_load_svg_is_a( const void *buf, size_t len )
* before the <svg line.
*
* Simple rules:
* - first 24 chars are plain ascii
* - first 24 chars are plain ascii (x09-x7F)
* - first SVG_HEADER_SIZE chars contain "<svg", upper or lower case.
*
* We could rsvg_handle_new_from_data() on the buffer, but that can be
@ -220,7 +220,7 @@ vips_foreign_load_svg_is_a( const void *buf, size_t len )
if( len < 24 )
return( 0 );
for( i = 0; i < 24; i++ )
if( !isascii( str[i] ) )
if( !isascii( str[i] ) || str[i] < 9 )
return( FALSE );
for( i = 0; i < SVG_HEADER_SIZE && i < len - 5; i++ )
if( g_ascii_strncasecmp( str + i, "<svg", 4 ) == 0 )

View File

@ -406,7 +406,7 @@ vips_foreign_save_tiff_file_build( VipsObject *object )
/* Handle the deprecated squash parameter.
*/
if( vips_object_argument_isset( object, "squash" ) )
if( tiff->squash )
/* We set that even in the case of LAB to LABQ.
*/
tiff->bitdepth = 1;

View File

@ -94,6 +94,8 @@
* - revise for target IO
* 18/2/20 Elad-Laufer
* - add subsample_mode, deprecate no_subsample
* 13/9/20
* - only write JFIF resolution if we don't have EXIF
*/
/*
@ -412,6 +414,59 @@ write_profile_data (j_compress_ptr cinfo,
}
}
#ifndef HAVE_EXIF
/* Set the JFIF resolution from the vips xres/yres tags.
*/
static void
vips_jfif_resolution_from_image( struct jpeg_compress_struct *cinfo,
VipsImage *image )
{
int xres, yres;
const char *p;
int unit;
/* Default to inches, more progs support it.
*/
unit = 1;
if( vips_image_get_typeof( image, VIPS_META_RESOLUTION_UNIT ) &&
!vips_image_get_string( image,
VIPS_META_RESOLUTION_UNIT, &p ) ) {
if( vips_isprefix( "cm", p ) )
unit = 2;
else if( vips_isprefix( "none", p ) )
unit = 0;
}
switch( unit ) {
case 0:
xres = VIPS_RINT( image->Xres );
yres = VIPS_RINT( image->Yres );
break;
case 1:
xres = VIPS_RINT( image->Xres * 25.4 );
yres = VIPS_RINT( image->Yres * 25.4 );
break;
case 2:
xres = VIPS_RINT( image->Xres * 10.0 );
yres = VIPS_RINT( image->Yres * 10.0 );
break;
default:
g_assert_not_reached();
break;
}
VIPS_DEBUG_MSG( "vips_jfif_resolution_from_image: "
"setting xres = %d, yres = %d, unit = %d\n", xres, yres, unit );
cinfo->density_unit = unit;
cinfo->X_density = xres;
cinfo->Y_density = yres;
}
#endif /*HAVE_EXIF*/
/* Write an ICC Profile from a file into the JPEG stream.
*/
static int
@ -643,18 +698,24 @@ write_vips( Write *write, int qfac, const char *profile,
}
}
/* Don't write the APP0 JFIF headers if we are stripping.
/* Only write the JFIF headers if we are not stripping and we have no
* EXIF. Some readers get confused if you set both.
*/
if( strip )
write->cinfo.write_JFIF_header = FALSE;
write->cinfo.write_JFIF_header = FALSE;
#ifndef HAVE_EXIF
if( !strip ) {
vips_jfif_resolution_from_image( &write->cinfo, write->in );
write->cinfo.write_JFIF_header = TRUE;
}
#endif /*HAVE_EXIF*/
/* Build compress tables.
/* Write app0 and build compress tables.
*/
jpeg_start_compress( &write->cinfo, TRUE );
/* Write any APP markers we need.
/* All the other APP chunks come next.
*/
if( !strip ) {
if( !strip ) {
/* We need to rebuild the exif data block from any exif tags
* on the image.
*/

View File

@ -460,9 +460,6 @@ vips_webp_add_chunks( VipsWebPWrite *write )
const char *vips_name = vips__webp_names[i].vips;
const char *webp_name = vips__webp_names[i].webp;
if( strcmp( vips_name, VIPS_META_ICC_NAME ) == 0 &&
write->profile )
if( vips_image_get_typeof( write->image, vips_name ) ) {
const void *data;
size_t length;

View File

@ -383,6 +383,8 @@ int vips_jpegload( const char *filename, VipsImage **out, ... )
__attribute__((sentinel));
int vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... )
__attribute__((sentinel));
int vips_jpegload_source( VipsSource *source, VipsImage **out, ... )
__attribute__((sentinel));
int vips_jpegsave_target( VipsImage *in, VipsTarget *target, ... )
__attribute__((sentinel));
@ -621,6 +623,8 @@ int vips_svgload( const char *filename, VipsImage **out, ... )
__attribute__((sentinel));
int vips_svgload_buffer( void *buf, size_t len, VipsImage **out, ... )
__attribute__((sentinel));
int vips_svgload_source( VipsSource *source, VipsImage **out, ... )
__attribute__((sentinel));
int vips_gifload( const char *filename, VipsImage **out, ... )
__attribute__((sentinel));

View File

@ -133,8 +133,8 @@ typedef enum {
VIPS_ACCESS_LAST
} VipsAccess;
typedef void *(*VipsStartFn)( struct _VipsImage *out, void *a, void *b );
typedef int (*VipsGenerateFn)( struct _VipsRegion *out,
typedef void *(*VipsStartFn)( VipsImage *out, void *a, void *b );
typedef int (*VipsGenerateFn)( VipsRegion *out,
void *seq, void *a, void *b, gboolean *stop );
typedef int (*VipsStopFn)( void *seq, void *a, void *b );
@ -143,7 +143,7 @@ typedef int (*VipsStopFn)( void *seq, void *a, void *b );
*/
typedef struct _VipsProgress {
/*< private >*/
struct _VipsImage *im; /* Image we are part of */
VipsImage *im; /* Image we are part of */
/*< public >*/
int run; /* Time we have been running */
@ -169,7 +169,9 @@ typedef struct _VipsProgress {
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_IMAGE, VipsImageClass ))
typedef struct _VipsImage {
/* Matching typedef in basic.h.
*/
struct _VipsImage {
VipsObject parent_instance;
/*< private >*/
@ -281,7 +283,7 @@ typedef struct _VipsImage {
/* The VipsImage (if any) we should signal eval progress on.
*/
struct _VipsImage *progress_signal;
VipsImage *progress_signal;
/* Record the file length here. We use this to stop ourselves mapping
* things beyond the end of the file in the case that the file has
@ -309,7 +311,7 @@ typedef struct _VipsImage {
*/
gboolean delete_on_close;
char *delete_on_close_filename;
} VipsImage;
};
typedef struct _VipsImageClass {
VipsObjectClass parent_class;
@ -519,7 +521,7 @@ void vips_value_set_array_image( GValue *value, int n );
/* Defined in reorder.c, but really a function on image.
*/
int vips_reorder_prepare_many( VipsImage *image,
struct _VipsRegion **regions, VipsRect *r );
VipsRegion **regions, VipsRect *r );
void vips_reorder_margin_hint( VipsImage *image, int margin );
void vips_image_free_buffer( VipsImage *image, void *buffer );

View File

@ -114,7 +114,6 @@ void vips__threadpool_init( void );
void vips__cache_init( void );
void vips__sink_screen_init( void );
void vips__print_renders( void );
void vips__type_leak( void );

View File

@ -77,8 +77,10 @@ typedef enum {
} VipsRegionShrink;
/* Sub-area of image.
*
* Matching typedef in basic.h.
*/
typedef struct _VipsRegion {
struct _VipsRegion {
VipsObject parent_object;
/*< public >*/
@ -112,7 +114,7 @@ typedef struct _VipsRegion {
* dropped.
*/
gboolean invalid;
} VipsRegion;
};
typedef struct _VipsRegionClass {
VipsObjectClass parent_class;

View File

@ -297,25 +297,18 @@ vips__demand_hint_array( VipsImage *image,
if( in[i]->dhint == VIPS_DEMAND_STYLE_ANY )
nany++;
/* Find the most restrictive of all the hints available to us.
*
* We have tried to be smarter about this in the past -- for example,
* detecting all ANY inputs and ignoring the hint in this case, but
* there are inevitably odd cases which cause problems. For example,
* new_from_memory, resize, affine, write_to_memory would run with
* FATSTRIP.
*/
set_hint = hint;
if( len == 0 )
/* No input images? Just set the requested hint. We don't
* force ANY, since the operation might be something like
* tiled read of an EXR image, where we certainly don't want
* ANY.
*/
;
else if( nany == len )
/* Special case: if all the inputs are ANY, then output can
* be ANY regardless of what this function wants.
*/
set_hint = VIPS_DEMAND_STYLE_ANY;
else
/* Find the most restrictive of all the hints available to us.
*/
for( i = 0; i < len; i++ )
set_hint = (VipsDemandStyle) VIPS_MIN(
(int) set_hint, (int) in[i]->dhint );
for( i = 0; i < len; i++ )
set_hint = (VipsDemandStyle) VIPS_MIN(
(int) set_hint, (int) in[i]->dhint );
image->dhint = set_hint;

View File

@ -413,7 +413,6 @@ vips_init( const char *argv0 )
#endif /*HAVE_THREAD_NEW*/
vips__threadpool_init();
vips__sink_screen_init();
vips__buffer_init();
vips__meta_init();

View File

@ -450,6 +450,9 @@ vips__render_shutdown( void )
}
else
g_mutex_unlock( render_dirty_lock );
VIPS_FREEF( vips_g_mutex_free, render_dirty_lock );
vips_semaphore_destroy( &n_render_dirty_sem );
}
}
@ -1027,8 +1030,8 @@ render_thread_main( void *client )
return( NULL );
}
void
vips__sink_screen_init( void )
static void *
vips__sink_screen_init( void *data )
{
g_assert( !render_thread );
g_assert( !render_dirty_lock );
@ -1037,6 +1040,8 @@ vips__sink_screen_init( void )
vips_semaphore_init( &n_render_dirty_sem, 0, "n_render_dirty" );
render_thread = vips_g_thread_new( "sink_screen",
render_thread_main, NULL );
return( NULL );
}
/**
@ -1096,8 +1101,12 @@ vips_sink_screen( VipsImage *in, VipsImage *out, VipsImage *mask,
int priority,
VipsSinkNotify notify_fn, void *a )
{
static GOnce once = G_ONCE_INIT;
Render *render;
VIPS_ONCE( &once, vips__sink_screen_init, NULL );
if( tile_width <= 0 || tile_height <= 0 ||
max_tiles < -1 ) {
vips_error( "vips_sink_screen", "%s", _( "bad parameters" ) );

View File

@ -137,7 +137,7 @@ vips_merge_class_init( VipsMergeClass *class )
VIPS_ARG_ENUM( class, "direction", 4,
_( "Direction" ),
_( "Horizontal or vertcial merge" ),
_( "Horizontal or vertical merge" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsMerge, direction ),
VIPS_TYPE_DIRECTION, VIPS_DIRECTION_HORIZONTAL );

View File

@ -195,7 +195,7 @@ vips_mosaic_class_init( VipsMosaicClass *class )
VIPS_ARG_ENUM( class, "direction", 4,
_( "Direction" ),
_( "Horizontal or vertcial mosaic" ),
_( "Horizontal or vertical mosaic" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsMosaic, direction ),
VIPS_TYPE_DIRECTION, VIPS_DIRECTION_HORIZONTAL );

View File

@ -504,7 +504,7 @@ vips_mosaic1_class_init( VipsMosaic1Class *class )
VIPS_ARG_ENUM( class, "direction", 4,
_( "Direction" ),
_( "Horizontal or vertcial mosaic" ),
_( "Horizontal or vertical mosaic" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsMosaic1, direction ),
VIPS_TYPE_DIRECTION, VIPS_DIRECTION_HORIZONTAL );

View File

@ -135,10 +135,14 @@ typedef struct _VipsThumbnail {
int heif_thumbnail_width;
int heif_thumbnail_height;
/* For TIFF sources, open subifds rather than pages to get pyr layers.
/* For TIFF sources, open subifds to get pyr layers.
*/
gboolean subifd_pyramid;
/* For TIFF sources, open pages to get pyr layers.
*/
gboolean page_pyramid;
} VipsThumbnail;
typedef struct _VipsThumbnailClass {
@ -261,13 +265,10 @@ vips_thumbnail_get_tiff_pyramid_page( VipsThumbnail *thumbnail )
/* Single-page docs can't be pyramids.
*/
if( thumbnail->n_loaded_pages < 2 )
if( thumbnail->n_pages < 2 )
return;
/* Use n_loaded_pages not n_pages since we support thumbnailing a page
* or range of pages from a many-page tiff.
*/
for( i = 0; i < thumbnail->n_loaded_pages; i++ ) {
for( i = 0; i < thumbnail->n_pages; i++ ) {
VipsImage *page;
int level_width;
int level_height;
@ -301,9 +302,9 @@ vips_thumbnail_get_tiff_pyramid_page( VipsThumbnail *thumbnail )
#ifdef DEBUG
printf( "vips_thumbnail_get_tiff_pyramid_page: "
"%d layer pyramid detected\n",
thumbnail->n_loaded_pages );
thumbnail->n_pages );
#endif /*DEBUG*/
thumbnail->level_count = thumbnail->n_loaded_pages;
thumbnail->level_count = thumbnail->n_pages;
}
/* Detect a TIFF pyramid made of subifds following a roughly /2 shrink.
@ -549,7 +550,12 @@ vips_thumbnail_open( VipsThumbnail *thumbnail )
if( thumbnail->level_count == 0 ) {
thumbnail->subifd_pyramid = FALSE;
thumbnail->page_pyramid = TRUE;
vips_thumbnail_get_tiff_pyramid_page( thumbnail );
if( thumbnail->level_count == 0 )
thumbnail->page_pyramid = FALSE;
}
}
@ -730,24 +736,6 @@ vips_thumbnail_build( VipsObject *object )
return( -1 );
in = t[2];
/* If there's an alpha, we have to premultiply before shrinking. See
* https://github.com/libvips/libvips/issues/291
*/
have_premultiplied = FALSE;
if( vips_image_hasalpha( in ) ) {
g_info( "premultiplying alpha" );
if( vips_premultiply( in, &t[3], NULL ) )
return( -1 );
have_premultiplied = TRUE;
/* vips_premultiply() makes a float image. When we
* vips_unpremultiply() below, we need to cast back to the
* pre-premultiply format.
*/
unpremultiplied_format = in->BandFmt;
in = t[3];
}
/* Shrink to preshrunk_page_height, so we work for multi-page images.
*/
vips_thumbnail_calculate_shrink( thumbnail,
@ -765,6 +753,26 @@ vips_thumbnail_build( VipsObject *object )
vshrink = (double) in->Ysize / target_image_height;
}
/* If there's an alpha, we have to premultiply before shrinking. See
* https://github.com/libvips/libvips/issues/291
*/
have_premultiplied = FALSE;
if( vips_image_hasalpha( in ) &&
hshrink != 1.0 &&
vshrink != 1.0 ) {
g_info( "premultiplying alpha" );
if( vips_premultiply( in, &t[3], NULL ) )
return( -1 );
have_premultiplied = TRUE;
/* vips_premultiply() makes a float image. When we
* vips_unpremultiply() below, we need to cast back to the
* pre-premultiply format.
*/
unpremultiplied_format = in->BandFmt;
in = t[3];
}
if( vips_resize( in, &t[4], 1.0 / hshrink,
"vscale", 1.0 / vshrink,
NULL ) )
@ -1051,6 +1059,7 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor )
"scale", 1.0 / factor,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) {
/* We support three modes: subifd pyramids, page-based
* pyramids, and simple multi-page TIFFs (no pyramid).
@ -1060,7 +1069,7 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor )
"access", VIPS_ACCESS_SEQUENTIAL,
"subifd", (int) factor,
NULL ) );
else if( thumbnail->level_count > 0 )
else if( thumbnail->page_pyramid )
return( vips_image_new_from_file( file->filename,
"access", VIPS_ACCESS_SEQUENTIAL,
"page", (int) factor,
@ -1069,8 +1078,8 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor )
return( vips_image_new_from_file( file->filename,
"access", VIPS_ACCESS_SEQUENTIAL,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) {
return( vips_image_new_from_file( file->filename,
"access", VIPS_ACCESS_SEQUENTIAL,
@ -1264,12 +1273,29 @@ vips_thumbnail_buffer_open( VipsThumbnail *thumbnail, double factor )
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) {
return( vips_image_new_from_buffer(
buffer->buf->data, buffer->buf->length,
buffer->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"page", (int) factor,
NULL ) );
/* We support three modes: subifd pyramids, page-based
* pyramids, and simple multi-page TIFFs (no pyramid).
*/
if( thumbnail->subifd_pyramid )
return( vips_image_new_from_buffer(
buffer->buf->data, buffer->buf->length,
buffer->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"subifd", (int) factor,
NULL ) );
else if( thumbnail->page_pyramid )
return( vips_image_new_from_buffer(
buffer->buf->data, buffer->buf->length,
buffer->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"page", (int) factor,
NULL ) );
else
return( vips_image_new_from_buffer(
buffer->buf->data, buffer->buf->length,
buffer->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) {
return( vips_image_new_from_buffer(
@ -1443,12 +1469,29 @@ vips_thumbnail_source_open( VipsThumbnail *thumbnail, double factor )
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) {
return( vips_image_new_from_source(
source->source,
source->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"page", (int) factor,
NULL ) );
/* We support three modes: subifd pyramids, page-based
* pyramids, and simple multi-page TIFFs (no pyramid).
*/
if( thumbnail->subifd_pyramid )
return( vips_image_new_from_source(
source->source,
source->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"subifd", (int) factor,
NULL ) );
else if( thumbnail->page_pyramid )
return( vips_image_new_from_source(
source->source,
source->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"page", (int) factor,
NULL ) );
else
return( vips_image_new_from_source(
source->source,
source->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) {
return( vips_image_new_from_source(

View File

@ -21,6 +21,7 @@ OME_FILE = os.path.join(IMAGES, "multi-channel-z-series.ome.tif")
ANALYZE_FILE = os.path.join(IMAGES, "t00740_tr1_segm.hdr")
GIF_FILE = os.path.join(IMAGES, "cramps.gif")
WEBP_FILE = os.path.join(IMAGES, "1.webp")
WEBP_LOOKS_LIKE_SVG_FILE = os.path.join(IMAGES, "looks-like-svg.webp")
EXR_FILE = os.path.join(IMAGES, "sample.exr")
FITS_FILE = os.path.join(IMAGES, "WFPC2u5780205r_c0fx.fits")
OPENSLIDE_FILE = os.path.join(IMAGES, "CMU-1-Small-Region.svs")

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -17,7 +17,7 @@ from helpers import \
GIF_ANIM_DISPOSE_PREVIOUS_FILE, \
GIF_ANIM_DISPOSE_PREVIOUS_EXPECTED_PNG_FILE, \
temp_filename, assert_almost_equal_objects, have, skip_if_no, \
TIF1_FILE, TIF2_FILE, TIF4_FILE
TIF1_FILE, TIF2_FILE, TIF4_FILE, WEBP_LOOKS_LIKE_SVG_FILE
class TestForeign:
@ -580,6 +580,10 @@ class TestForeign:
assert im.width == 16
assert im.height == 16
# load should see metadata like eg. icc profiles
im = pyvips.Image.magickload(JPEG_FILE)
assert len(im.get("icc-profile-data")) == 564
# added in 8.7
@skip_if_no("magicksave")
def test_magicksave(self):
@ -597,6 +601,7 @@ class TestForeign:
assert self.colour.bands == x.bands
max_diff = (self.colour - x).abs().max()
assert max_diff < 60
assert len(x.get("icc-profile-data")) == 564
self.save_load_buffer("magicksave_buffer", "magickload_buffer",
self.colour, 60, format="JPG")
@ -676,6 +681,11 @@ class TestForeign:
assert x1.get("page-height") == x2.get("page-height")
assert x1.get("gif-loop") == x2.get("gif-loop")
# WebP image that happens to contain the string "<svg"
if have("svgload"):
x = pyvips.Image.new_from_file(WEBP_LOOKS_LIKE_SVG_FILE)
assert x.get("vips-loader") == "webpload"
@skip_if_no("analyzeload")
def test_analyzeload(self):
def analyze_valid(im):

View File

@ -2,7 +2,7 @@
import pytest
import pyvips
from helpers import JPEG_FILE, OME_FILE, HEIC_FILE, all_formats, have
from helpers import JPEG_FILE, OME_FILE, HEIC_FILE, TIF_FILE, all_formats, have
# Run a function expecting a complex image on a two-band image
@ -186,6 +186,13 @@ class TestResample:
assert im.width == 100
assert im.height == 570
# should be able to thumbnail a single-page tiff in a buffer
im1 = pyvips.Image.thumbnail(TIF_FILE, 100)
with open(TIF_FILE, 'rb') as f:
buf = f.read()
im2 = pyvips.Image.thumbnail_buffer(buf, 100)
assert abs(im1.avg() - im2.avg()) < 1
if have("heifload"):
# this image is orientation 6 ... thumbnail should flip it
im = pyvips.Image.new_from_file(HEIC_FILE)

View File

@ -20,11 +20,11 @@ bin_SCRIPTS = \
batch_rubber_sheet \
batch_crop \
vipsprofile \
vips-8.10
vips-8.11
EXTRA_DIST = \
vipsprofile \
vips-8.10 \
vips-8.11 \
light_correct.in \
shrink_width.in \
batch_image_convert.in \

View File

@ -124,7 +124,6 @@ prepend_var $libvar $VIPSHOME/lib
prepend_var PATH $VIPSHOME/bin
prepend_var PKG_CONFIG_PATH $VIPSHOME/lib/pkgconfig
prepend_var GI_TYPELIB_PATH $VIPSHOME/lib/girepository-1.0
prepend_var PYTHONPATH $VIPSHOME/lib/python2.7/site-packages
# run, passing in args we were passed
exec $*