Merge branch 'master' into master
This commit is contained in:
commit
25bd6cce6f
29
ChangeLog
29
ChangeLog
@ -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]
|
||||
|
76
configure.ac
76
configure.ac
@ -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
4
cplusplus/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
html
|
||||
latex
|
||||
Doxyfile
|
||||
doxygen.stamp
|
2590
cplusplus/Doxyfile.in
Normal file
2590
cplusplus/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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
258
cplusplus/README.md
Normal 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", &out)->
|
||||
set("left", *this)->
|
||||
set("right", right));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Where `VImage::call()` is the generic call-a-vips8-operation function.
|
@ -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
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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 += '}'
|
||||
|
||||
|
@ -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; \
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
@ -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
@ -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
|
||||
|
@ -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. 19–28.
|
||||
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. 19–28.
|
||||
</para>
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<para>
|
||||
The libvips test suite is written in Python and exercises every operation in the API. It’s 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: it’ll open all the input images at the same time, and stream pixels from them as it needs them to generate the output.
|
||||
|
@ -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 process’s 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 computer’s 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 it’s 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>
|
||||
|
@ -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 it’s 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.
|
||||
|
@ -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 \
|
||||
|
@ -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. It’s 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. It’s 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 that’s 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. It’s 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.
|
||||
|
@ -21,7 +21,7 @@ $filename = "image.jpg";
|
||||
$image = Vips\Image::thumbnail($filename, 200, ["height" => 200]);
|
||||
$image->writeToFile("my-thumbnail.jpg");
|
||||
</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->writeToFile("my-thumbnail.jpg");
|
||||
<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->writeToFile("my-thumbnail.jpg");
|
||||
$ 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-r–r– 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 that’s 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>
|
||||
|
@ -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>Don’t 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 can’t 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 it’s 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>
|
||||
|
@ -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
17
doc/libvips-from-C++.md
Normal 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
21
doc/libvips-from-C++.xml
Normal 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>
|
@ -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 <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);
|
||||
}
|
||||
</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<double>` 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<double></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 < 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 ) < 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<double> 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", &out)->
|
||||
set("left", *this)->
|
||||
set("right", right));
|
||||
|
||||
return out;
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
Where VImage::call() is the generic call-a-vips8-operation function.
|
||||
</para>
|
||||
</refsect3>
|
||||
</refentry>
|
@ -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
|
||||
*/
|
||||
|
@ -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" ),
|
||||
|
@ -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,
|
||||
|
@ -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" );
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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 ) )
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 ) )
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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 )
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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" ) );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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(
|
||||
|
@ -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")
|
||||
|
BIN
test/test-suite/images/looks-like-svg.webp
Normal file
BIN
test/test-suite/images/looks-like-svg.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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 \
|
||||
|
@ -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 $*
|
Loading…
Reference in New Issue
Block a user