add howitworks.md
This commit is contained in:
parent
60e661614f
commit
30829ef003
@ -1,5 +1,6 @@
|
|||||||
25/3/17 started 8.5.2
|
25/3/17 started 8.5.2
|
||||||
- better behaviour for truncated PNG files, thanks Yury
|
- better behaviour for truncated PNG files, thanks Yury
|
||||||
|
- move some docs from the wiki and blog into core libvips docs
|
||||||
|
|
||||||
25/3/17 started 8.5.1
|
25/3/17 started 8.5.1
|
||||||
- init more classes earlier, thanks David
|
- init more classes earlier, thanks David
|
||||||
|
1
doc/.gitignore
vendored
Normal file
1
doc/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
How-it-works.xml
|
316
doc/How-it-works.md
Normal file
316
doc/How-it-works.md
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
Compared to most image processing libraries, VIPS needs little RAM and runs
|
||||||
|
quickly, especially on machines with more than one CPU. VIPS achieves this
|
||||||
|
improvement by only keeping the pixels currently being processed in RAM
|
||||||
|
and by having an efficient, threaded image IO system. This page explains
|
||||||
|
how these features are implemented.
|
||||||
|
|
||||||
|
### Images
|
||||||
|
|
||||||
|
VIPS images have three dimensions: width, height and bands. Bands usually
|
||||||
|
(though not always) represent colour. These three dimensions can be any
|
||||||
|
size up to 2 ** 31 elements. Every band element in an image has to have the
|
||||||
|
same format. A format is an 8-, 16- or 32-bit int, signed or unsigned, 32-
|
||||||
|
or 64-bit float, and 64- or 128-bit complex.
|
||||||
|
|
||||||
|
### Regions
|
||||||
|
|
||||||
|
An image can be very large, much larger than the available memory, so you
|
||||||
|
can't just access pixels with a pointer \*.
|
||||||
|
|
||||||
|
Instead, you read pixels from an image with a region. This is a rectangular
|
||||||
|
sub-area of an image. In C, the API looks like:
|
||||||
|
|
||||||
|
```c
|
||||||
|
VipsImage *image = vips_image_new_from_file( filename, NULL );
|
||||||
|
VipsRegion *region = vips_region_new( image );
|
||||||
|
|
||||||
|
// ask for a 100x100 pixel region at 0x0 (top left)
|
||||||
|
VipsRect r = { left: 0, top: 0, width: 100, height: 100 };
|
||||||
|
if( vips_region_prepare( region, &r ) )
|
||||||
|
vips_error( ... );
|
||||||
|
|
||||||
|
// get a pointer to the pixel at x, y, where x, y must
|
||||||
|
// be within the region
|
||||||
|
|
||||||
|
// as long as you stay within the valid area for the region,
|
||||||
|
// you can address pixels with regular pointer arithmetic
|
||||||
|
|
||||||
|
// compile with -DDEBUG and the macro will check bounds for you
|
||||||
|
|
||||||
|
// add VIPS_REGION_LSKIP() to move down a line
|
||||||
|
VipsPel *pixel = VIPS_REGION_ADDR( region, x, y );
|
||||||
|
|
||||||
|
// you can call vips_region_prepare() many times
|
||||||
|
|
||||||
|
// everything in libvips is a GObject ... when you're done,
|
||||||
|
// just free with
|
||||||
|
g_object_unref( region );
|
||||||
|
```
|
||||||
|
|
||||||
|
The action that `vips_region_prepare()` 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.
|
||||||
|
|
||||||
|
(\* there is an image access mode where you can just use a pointer, but
|
||||||
|
it's rarely used)
|
||||||
|
|
||||||
|
### Partial images
|
||||||
|
|
||||||
|
A partial image is one where, instead of storing a value for each pixel, VIPS
|
||||||
|
stores a function which can make any rectangular area of pixels on demand.
|
||||||
|
|
||||||
|
If you use `vips_region_prepare()` on a region created on a partial image,
|
||||||
|
VIPS will allocate enough memory to hold the pixels you asked for and use
|
||||||
|
the stored function to calculate values for just those pixels \*.
|
||||||
|
|
||||||
|
The stored function comes in three parts: a start function, a generate
|
||||||
|
function and a stop function. The start function creates a state, the
|
||||||
|
generate function uses the state plus a requested area to calculate pixel
|
||||||
|
values and the stop function frees the state again. Breaking the stored
|
||||||
|
function into three parts is good for SMP scaling: resource allocation and
|
||||||
|
synchronisation mostly happens in start functions, so generate functions
|
||||||
|
can run without having to talk to each other.
|
||||||
|
|
||||||
|
VIPS makes a set of guarantees about parallelism that make this simple to
|
||||||
|
program. Start and stop functions are mutually exclusive and a state is
|
||||||
|
never used by more than one generate. In other words, a start / generate /
|
||||||
|
generate / stop sequence works like a thread.
|
||||||
|
|
||||||
|
![](Sequence.png)
|
||||||
|
|
||||||
|
(\* in fact VIPS keeps a cache of calculated pixel buffers and will return
|
||||||
|
a pointer to a previously-calculated buffer if it can)
|
||||||
|
|
||||||
|
### Operations
|
||||||
|
|
||||||
|
VIPS operations read input images and write output images, performing some
|
||||||
|
transformation on the pixels. When an operation writes to an image the
|
||||||
|
action it takes depends upon the image type. For example, if the image is a
|
||||||
|
file on disc then VIPS will start a data sink to stream pixels to the file,
|
||||||
|
or if the image is a partial one then it will just attach start / generate /
|
||||||
|
stop functions.
|
||||||
|
|
||||||
|
Like most threaded image processing systems, all VIPS operations have to
|
||||||
|
be free of side-effects. In other words, operations cannot modify images,
|
||||||
|
they can only create new images. This could result in a lot of copying if
|
||||||
|
an operation is only making a small change to a large image so VIPS has a
|
||||||
|
set of mechanisms to copy image areas by just adjusting pointers. Most of
|
||||||
|
the time no actual copying is necessary and you can perform operations on
|
||||||
|
large images at low cost.
|
||||||
|
|
||||||
|
### Run-time code generation
|
||||||
|
|
||||||
|
VIPS uses [Orc](http://code.entropywave.com/orc/), a run-time compiler, to
|
||||||
|
generate code for some operations. For example, to compute a convolution
|
||||||
|
on an 8-bit image, VIPS will examine the convolution matrix and the source
|
||||||
|
image and generate a tiny program to calculate the convolution. This program
|
||||||
|
is then "compiled" to the vector instruction set for your CPU, for example
|
||||||
|
SSE3 on most x86 processors.
|
||||||
|
|
||||||
|
Run-time vector code generation typically speeds operations up by a factor
|
||||||
|
of three or four.
|
||||||
|
|
||||||
|
### Joining operations together
|
||||||
|
|
||||||
|
The region create / prepare / prepare / free calls you use to get pixels
|
||||||
|
from an image are an exact parallel to the start / generate / generate /
|
||||||
|
stop calls that images use to create pixels. In fact, they are the same:
|
||||||
|
a region on a partial image holds the state created by that image for the
|
||||||
|
generate function that will fill the region with pixels.
|
||||||
|
|
||||||
|
![](Combine.png)
|
||||||
|
|
||||||
|
VIPS joins image processing operations together by linking the output of one
|
||||||
|
operation (the start / generate / stop sequence) to the input of the next
|
||||||
|
(the region it uses to get pixels for processing). This link is a single
|
||||||
|
function call, and very fast. Additionally, because of the the split between
|
||||||
|
allocation and processing, once a pipeline of operations has been set up,
|
||||||
|
VIPS is able to run without allocating and freeing memory.
|
||||||
|
|
||||||
|
This graph (generated by `vipsprofile`, the vips profiler) shows memory use
|
||||||
|
over time for a vips pipeline running on a large image. The bottom trace
|
||||||
|
shows total memory, the upper traces show threads calculating useful results
|
||||||
|
(green), threads blocked on synchronisation (red) and memory allocations
|
||||||
|
(white ticks).
|
||||||
|
|
||||||
|
![](Memtrace.png)
|
||||||
|
|
||||||
|
Because the intermediate image is just a small region in memory, a pipeline
|
||||||
|
of operations running together needs very little RAM. In fact, intermediates
|
||||||
|
are small enough that they can fit in L2 cache on most machines, so an
|
||||||
|
entire pipeline can run without touching main memory. And finally, because
|
||||||
|
each thread runs a very cheap copy of just the writeable state of the
|
||||||
|
entire pipeline, threads can run with few locks. VIPS needs just four lock
|
||||||
|
operations per output tile, regardless of the pipeline length or complexity.
|
||||||
|
|
||||||
|
### Data sources
|
||||||
|
|
||||||
|
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. A post on the libvips blog [explains how libvips opens a
|
||||||
|
file](http://libvips.blogspot.co.uk/2012/06/how-libvips-opens-file.html). One
|
||||||
|
of the sources uses the [ImageMagick](http://www.imagemagick.org) (or
|
||||||
|
optionally [GraphicsMagick](http://www.graphicsmagick.org)) library, so
|
||||||
|
VIPS can read any image format that these libraries can read.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
When reading from a large VIPS image (or any other format with the same
|
||||||
|
structure on disc, such as binary PPM), VIPS keeps a set of small rolling
|
||||||
|
windows into the file, some small number of scanlines in size. As pixels
|
||||||
|
are demanded by different threads VIPS will move these windows up and down
|
||||||
|
the file. As a result, VIPS can process images much larger than RAM, even
|
||||||
|
on 32-bit machines.
|
||||||
|
|
||||||
|
### Data sinks
|
||||||
|
|
||||||
|
In a demand-driven system, something has to do the demanding. VIPS has a
|
||||||
|
variety of data sinks that you can use to pull image data though a pipeline
|
||||||
|
in various situations. There are sinks that will build a complete image
|
||||||
|
in memory, sinks to draw to a display, sinks to loop over an image (useful
|
||||||
|
for statistical operations, for example) and sinks to stream an image to disc.
|
||||||
|
|
||||||
|
The disc sink looks something like this:
|
||||||
|
|
||||||
|
![](Sink.png)
|
||||||
|
|
||||||
|
The sink keeps two buffers\*, each as wide as the image. It starts threads
|
||||||
|
as rapidly as it can up to the concurrency limit, filling each buffer with
|
||||||
|
tiles\*\* of calculated pixels, each thread calculating one tile at once. A
|
||||||
|
separate background thread watches each buffer and, as soon as the last tile
|
||||||
|
in a buffer finishes, writes that complete set of scanlines to disc using
|
||||||
|
whatever image write library is appropriate. VIPS can write with libjpeg,
|
||||||
|
libtiff, libpng and others. It then wipes the buffer and repositions it
|
||||||
|
further down the image, ready for the next set of tiles to stream in.
|
||||||
|
|
||||||
|
These features in combination mean that, once a pipeline of image processing
|
||||||
|
operations has been built, VIPS can run almost lock-free. This is very
|
||||||
|
important for SMP scaling: you don't want the synchronization overhead to
|
||||||
|
scale with either the number of threads or the complexity of the pipeline
|
||||||
|
of operations being performed. As a result, VIPS scales almost linearly
|
||||||
|
with increasing numbers of threads:
|
||||||
|
|
||||||
|
![](Vips-smp.png)
|
||||||
|
|
||||||
|
Number of CPUs is on the horizontal axis, speedup is on the vertical
|
||||||
|
axis. Taken from the [[Benchmarks]] page.
|
||||||
|
|
||||||
|
(\* there can actually be more than one, it allocate enough buffers to
|
||||||
|
ensure that there are at least two tiles for every thread)
|
||||||
|
|
||||||
|
(\*\* tiles can be any shape and size, VIPS has a tile hint system that
|
||||||
|
operations use to tell sinks what tile geometry they prefer)
|
||||||
|
|
||||||
|
### Operation cache
|
||||||
|
|
||||||
|
Because VIPS operations are free of side-effects\*, you can cache them. Every
|
||||||
|
time you call an operation, VIPS searches the cache for a previous call to
|
||||||
|
the same operation with the same arguments. If it finds a match, you get
|
||||||
|
the previous result again. This can give a huge speedup.
|
||||||
|
|
||||||
|
By default, VIPS caches the last 1,000 operation calls. You can also control
|
||||||
|
the cache size by memory use or by files opened.
|
||||||
|
|
||||||
|
(\* Some vips operations DO have side effects, for example,
|
||||||
|
`vips_draw_circle()` will draw a circle on an image. These operations emit an
|
||||||
|
"invalidate" signal on the image they are called on and this signal makes
|
||||||
|
all downstream operations and caches drop their contents.)
|
||||||
|
|
||||||
|
### Operation database and APIs
|
||||||
|
|
||||||
|
VIPS has around 300 image processing operations written in this style. Each
|
||||||
|
operation is a GObject class. You can use the standard GObject calls to walk
|
||||||
|
the class hierarchy and discover operations, and libvips adds a small amount
|
||||||
|
of extra introspection metadata to handle things like optional arguments.
|
||||||
|
|
||||||
|
The [C API](using-from-c.html) is a set of simple wrappers which create
|
||||||
|
class instances for you. The [C++ API](using-from-cpp.html) is a little
|
||||||
|
fancier and adds things like automatic object lifetime management. The
|
||||||
|
[command-line interface](using-cli.html) uses introspection to run any vips
|
||||||
|
operation in the class hierarchy.
|
||||||
|
|
||||||
|
The [Python API](using-from-python.html) is built on top of
|
||||||
|
gobject-introspection. It is written in Python, so as long as you can get
|
||||||
|
gobject-introspection working, you should be able to use vips. It supports
|
||||||
|
python2 and python3 and works on Linux, OS X and Windows.
|
||||||
|
|
||||||
|
### Snip
|
||||||
|
|
||||||
|
The VIPS GUI, nip2, has its own scripting language called Snip. Snip is a
|
||||||
|
lazy, higher-order, purely functional, object oriented language. Almost all
|
||||||
|
of nip2's menus are implemented in it, and nip2 workspaces are Snip programs.
|
||||||
|
|
||||||
|
VIPS operations listed in the operation database appear as Snip functions. For
|
||||||
|
example, `abs` can be used from Snip as:
|
||||||
|
|
||||||
|
```
|
||||||
|
// absolute value of image b
|
||||||
|
a = vips_call "abs" [b] [];
|
||||||
|
```
|
||||||
|
|
||||||
|
However, `abs` won't work on anything except the primitive vips image type. It
|
||||||
|
can't be used on any class, or list or number. Definitions in `_stdenv.dev`
|
||||||
|
wrap each VIPS operation as a higher level Snip operation. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
abs x
|
||||||
|
= oo_unary_function abs_op x, is_class x
|
||||||
|
= vips_call "abs" [x] [], is_image x
|
||||||
|
= abs_cmplx x, is_complex x
|
||||||
|
= abs_num x, is_real x
|
||||||
|
= abs_list x, is_real_list x
|
||||||
|
= abs_list (map abs_list x), is_matrix x
|
||||||
|
= error (_ "bad arguments to " ++ "abs")
|
||||||
|
{
|
||||||
|
abs_op = Operator "abs" abs Operator_type.COMPOUND false;
|
||||||
|
|
||||||
|
abs_list l = (sum (map square l)) ** 0.5;
|
||||||
|
|
||||||
|
abs_num n
|
||||||
|
= n, n >= 0
|
||||||
|
= -n;
|
||||||
|
|
||||||
|
abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This defines the behaviour of `abs` for the base Snip types (number, list,
|
||||||
|
matrix, image and so on), then classes will use that to define operator
|
||||||
|
behaviour on higher-level objects.
|
||||||
|
|
||||||
|
Now you can use:
|
||||||
|
|
||||||
|
```
|
||||||
|
// absolute value of anything
|
||||||
|
a = abs b;
|
||||||
|
```
|
||||||
|
|
||||||
|
and you ought to get sane behaviour for any object, including things like
|
||||||
|
the `Matrix` class.
|
||||||
|
|
||||||
|
You can write Snip classes which present functions to the user as menu
|
||||||
|
items. For example, `Math.def` has this:
|
||||||
|
|
||||||
|
```
|
||||||
|
Math_arithmetic_item = class
|
||||||
|
Menupullright "_Arithmetic" "basic arithmetic for objects" {
|
||||||
|
|
||||||
|
Absolute_value_item = class
|
||||||
|
Menuaction "A_bsolute Value" "absolute value of x" {
|
||||||
|
action x = map_unary abs x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the user can select an object and click `Math / Abs` to find the absolute
|
||||||
|
value of that object.
|
||||||
|
|
@ -126,13 +126,26 @@ IGNORE_HFILES = $(IGNORE_VIPS_INCLUDE) $(IGNORE_VIPS_C)
|
|||||||
# Images to copy into HTML directory.
|
# Images to copy into HTML directory.
|
||||||
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
|
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
|
||||||
HTML_IMAGES = \
|
HTML_IMAGES = \
|
||||||
$(top_srcdir)/doc/images/interconvert.png
|
$(top_srcdir)/doc/images/interconvert.png \
|
||||||
|
$(top_srcdir)/doc/images/Combine.png \
|
||||||
|
$(top_srcdir)/doc/images/Memtrace.png \
|
||||||
|
$(top_srcdir)/doc/images/Sequence.png \
|
||||||
|
$(top_srcdir)/doc/images/Sink.png \
|
||||||
|
$(top_srcdir)/doc/images/Vips-smp.png
|
||||||
|
|
||||||
# we have some files in markdown ... convert to docbook for gtk-doc
|
# we have some files in markdown ... convert to docbook for gtk-doc
|
||||||
|
# pandoc makes sect1 headers, we want refsect3 for gtk-doc
|
||||||
.md.xml:
|
.md.xml:
|
||||||
pandoc -s -S --template="$(realpath pandoc-docbook-template.docbook)" --wrap=none -V title="$<" -f markdown -t docbook -o $@ $<
|
pandoc -s -S --template="$(realpath pandoc-docbook-template.docbook)" --wrap=none -V title="$<" -f markdown -t docbook -o $@ $<
|
||||||
sed -e s/sect1/refsect3/g < $@ > x && mv x $@
|
sed -e s/sect1/refsect3/g < $@ > x && mv x $@
|
||||||
|
|
||||||
|
# Our markdown source files
|
||||||
|
markdown_content_files = \
|
||||||
|
How-it-works.md
|
||||||
|
|
||||||
|
# converted to xml in this dir by pandoc
|
||||||
|
markdown_content_files_docbook = $(markdown_content_files:.md=.xml)
|
||||||
|
|
||||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
||||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
||||||
content_files = \
|
content_files = \
|
||||||
@ -144,7 +157,7 @@ content_files = \
|
|||||||
extending.xml \
|
extending.xml \
|
||||||
function-list.xml \
|
function-list.xml \
|
||||||
file-format.xml \
|
file-format.xml \
|
||||||
whatsnew-8.5.xml \
|
${markdown_content_files_docbook} \
|
||||||
binding.xml
|
binding.xml
|
||||||
|
|
||||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
||||||
@ -159,7 +172,7 @@ expand_content_files = \
|
|||||||
extending.xml \
|
extending.xml \
|
||||||
function-list.xml \
|
function-list.xml \
|
||||||
file-format.xml \
|
file-format.xml \
|
||||||
whatsnew-8.5.xml \
|
${markdown_content_files_docbook} \
|
||||||
binding.xml
|
binding.xml
|
||||||
|
|
||||||
# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
|
# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
|
||||||
@ -176,13 +189,14 @@ include gtk-doc.make
|
|||||||
# Other files to distribute
|
# Other files to distribute
|
||||||
# e.g. EXTRA_DIST += version.xml.in
|
# e.g. EXTRA_DIST += version.xml.in
|
||||||
EXTRA_DIST += \
|
EXTRA_DIST += \
|
||||||
|
${markdown_content_files} \
|
||||||
images \
|
images \
|
||||||
gen-function-list.py
|
gen-function-list.py
|
||||||
|
|
||||||
# Files not to distribute
|
# Files not to distribute
|
||||||
# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
|
# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
|
||||||
# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
|
# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
|
||||||
DISTCLEANFILES = libvips.types
|
DISTCLEANFILES = libvips.types
|
||||||
|
|
||||||
# Comment this out if you want 'make check' to test you doc status
|
# Comment this out if you want 'make check' to test you doc status
|
||||||
# and run some sanity checks
|
# and run some sanity checks
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
cat <<EOF
|
|
||||||
gtkdoc-mkhtml version 1.25 - generate documentation in html format
|
|
||||||
|
|
||||||
--verbose Print extra output while processing
|
|
||||||
--path=SEARCH_PATH Extra source directories
|
|
||||||
MODULE Name of the doc module being parsed
|
|
||||||
DRIVER_FILE File containing the toplevel DocBook file.
|
|
||||||
--version Print the version of this program
|
|
||||||
--help Print this help
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# echo "args $*\n";
|
|
||||||
|
|
||||||
# parse options, ignore unknown options for future extensions
|
|
||||||
|
|
||||||
verbose="0"
|
|
||||||
searchpath=
|
|
||||||
uninstalled=no
|
|
||||||
while true; do
|
|
||||||
case "X$1" in
|
|
||||||
X--version) echo "1.25"; exit 0;;
|
|
||||||
X--help) usage; exit 0;;
|
|
||||||
X--uninstalled) uninstalled=yes; shift;;
|
|
||||||
X--verbose) verbose="1"; shift;;
|
|
||||||
X--path=*) searchpath=`echo "$1" | sed s/.*=//`; shift;;
|
|
||||||
X--*) shift;;
|
|
||||||
X*) break;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if test $# -lt 2; then
|
|
||||||
usage 1>&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
module="$1"
|
|
||||||
shift
|
|
||||||
document="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
quiet="1"
|
|
||||||
if test $verbose = "1"; then
|
|
||||||
quiet="0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test $uninstalled = yes; then
|
|
||||||
# this does not work from buiddir!=srcdir
|
|
||||||
gtkdocdir=`dirname $0`
|
|
||||||
# traditional Bourne shells may not support -e here, use -f
|
|
||||||
if test ! -f $gtkdocdir/gtk-doc.xsl; then
|
|
||||||
# try to src dir (set from makefiles) too
|
|
||||||
if test -f $ABS_TOP_SRCDIR/gtk-doc.xsl; then
|
|
||||||
gtkdocdir=$ABS_TOP_SRCDIR
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
styledir=$gtkdocdir/style
|
|
||||||
#echo "uninstalled, gtkdocdir=$gtkdocdir, cwd=$PWD"
|
|
||||||
else
|
|
||||||
# the first two are needed to resolve datadir
|
|
||||||
prefix=/usr
|
|
||||||
datarootdir=${prefix}/share
|
|
||||||
gtkdocdir=${datarootdir}/gtk-doc/data
|
|
||||||
styledir=$gtkdocdir
|
|
||||||
fi
|
|
||||||
|
|
||||||
# we could do "$path_option $PWD "
|
|
||||||
# to avoid needing rewriting entities that are copied from the header
|
|
||||||
# into docs under xml
|
|
||||||
if test "X$searchpath" = "X"; then
|
|
||||||
path_arg=
|
|
||||||
else
|
|
||||||
path_arg="--path $searchpath"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# profiling
|
|
||||||
profile_args=""
|
|
||||||
if test "$GTKDOC_PROFILE" != ""; then
|
|
||||||
profile_args="--profile"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#echo /usr/bin/xsltproc $path_arg --nonet --xinclude \
|
|
||||||
# --stringparam gtkdoc.bookname $module \
|
|
||||||
# --stringparam gtkdoc.version "1.25" \
|
|
||||||
# "$@" $gtkdocdir/gtk-doc.xsl "$document"
|
|
||||||
/usr/bin/xsltproc 2>profile.txt $profile_args $path_arg --nonet --xinclude \
|
|
||||||
--stringparam gtkdoc.bookname $module \
|
|
||||||
--stringparam gtkdoc.version "1.25" \
|
|
||||||
--stringparam chunk.quietly $quiet \
|
|
||||||
--stringparam chunker.output.quiet $quiet \
|
|
||||||
"$@" $gtkdocdir/gtk-doc.xsl "$document" || exit $?
|
|
||||||
|
|
||||||
# profiling
|
|
||||||
if test "$GTKDOC_PROFILE" != ""; then
|
|
||||||
cat profile.txt | gprof2dot.py -e 0.01 -n 0.01 | dot -Tpng -o profile.png
|
|
||||||
else
|
|
||||||
rm profile.txt
|
|
||||||
fi
|
|
||||||
|
|
||||||
# copy navigation images and stylesheets to html directory ...
|
|
||||||
cp -f $styledir/*.png $styledir/*.css ./
|
|
||||||
|
|
||||||
|
|
||||||
echo "timestamp" > ../html.stamp
|
|
||||||
|
|
BIN
doc/images/Combine.png
Normal file
BIN
doc/images/Combine.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
doc/images/Memtrace.png
Normal file
BIN
doc/images/Memtrace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
BIN
doc/images/Sequence.png
Normal file
BIN
doc/images/Sequence.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
BIN
doc/images/Sink.png
Normal file
BIN
doc/images/Sink.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
doc/images/Vips-smp.png
Normal file
BIN
doc/images/Vips-smp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -39,7 +39,7 @@
|
|||||||
<xi:include href="xml/function-list.xml"/>
|
<xi:include href="xml/function-list.xml"/>
|
||||||
<xi:include href="xml/file-format.xml"/>
|
<xi:include href="xml/file-format.xml"/>
|
||||||
<xi:include href="xml/using-threads.xml"/>
|
<xi:include href="xml/using-threads.xml"/>
|
||||||
<xi:include href="xml/whatsnew-8.5.xml"/>
|
<xi:include href="xml/How-it-works.xml"/>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
||||||
<chapter>
|
<chapter>
|
||||||
|
@ -11,11 +11,11 @@ $endif$
|
|||||||
<refmeta>
|
<refmeta>
|
||||||
<refentrytitle>$title$</refentrytitle>
|
<refentrytitle>$title$</refentrytitle>
|
||||||
<manvolnum>3</manvolnum>
|
<manvolnum>3</manvolnum>
|
||||||
<refmiscinfo>VIPS Library</refmiscinfo>
|
<refmiscinfo>libvips</refmiscinfo>
|
||||||
</refmeta>
|
</refmeta>
|
||||||
|
|
||||||
<refnamediv>
|
<refnamediv>
|
||||||
<refname>VIPS</refname>
|
<refname>libvips</refname>
|
||||||
<refpurpose>$title$</refpurpose>
|
<refpurpose>$title$</refpurpose>
|
||||||
</refnamediv>
|
</refnamediv>
|
||||||
|
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
libvips 8.5 should be out by the end of March 2017. This page introduces the
|
|
||||||
main features.
|
|
||||||
|
|
||||||
## New operators
|
|
||||||
|
|
||||||
Almost all of the logic from the `vipsthumbnail` program is now in a pair of
|
|
||||||
new operators, `vips_thumbnail()` and `vips_thumbnail_buffer()`. These are very
|
|
||||||
handy for the various scripting languages with vips bindings: you can now make
|
|
||||||
a high-quality, high-speed thumbnail in PHP (for example) with just:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$filename = ...;
|
|
||||||
$image = Vips\Image::thumbnail($filename, 200, ["height" => 200]);
|
|
||||||
$image.writeToFile("my-thumbnail.jpg");
|
|
||||||
```
|
|
||||||
|
|
||||||
The new thumbnail operator has also picked up some useful features:
|
|
||||||
|
|
||||||
* **Smart crop** A new cropping mode called `attention` searches the image for
|
|
||||||
edges, skin tones and areas of saturated colour, and attempts to position the
|
|
||||||
crop box over the most significant feature. There's a `vips_smartcrop()`
|
|
||||||
operator as well.
|
|
||||||
|
|
||||||
* **Crop constraints** Thanks to tomasc, libvips has crop constraints. You
|
|
||||||
can set it to only thumbnail if the image is larger or smaller than the target
|
|
||||||
(the `<` and `>` modifiers in imagemagick), and to crop to a width or height.
|
|
||||||
|
|
||||||
* **Buffer sources** `vips_thumbnail_buffer()` will thumbnail an image held as
|
|
||||||
a formatted block of data in memory. This is useful for cloud services, where
|
|
||||||
the filesystem is often rather slow.
|
|
||||||
|
|
||||||
CLAHE, or Contrast-Limited Adaptive Histogram Equalisation, is a simple way to
|
|
||||||
make local histogram equalisation more useful.
|
|
||||||
|
|
||||||
Plain local equalization removes
|
|
||||||
all global brightness variation and can make images hard to understand.
|
|
||||||
The `hist_local` operator now has a `max-slope` parameter you can use to limit
|
|
||||||
how much equalisation can alter your image. A value of 3 generally works well.
|
|
||||||
|
|
||||||
## Toilet roll images
|
|
||||||
|
|
||||||
libvips used to let you pick single pages out of multi-page images, such
|
|
||||||
as PDFs, but had little support for processing entire documents.
|
|
||||||
|
|
||||||
libvips 8.5 now has good support for toilet roll images. You can load a
|
|
||||||
multipage image as a very tall, thin strip, process the whole thing, and write
|
|
||||||
back to another multi-page file. The extra feature is an `n` parameter which
|
|
||||||
gives the number of pages to load, or -1 to load all pages.
|
|
||||||
|
|
||||||
For example, (OME-
|
|
||||||
TIFF)[https://www.openmicroscopy.org/site/support/ome-model/ome-tiff]
|
|
||||||
is a standard for microscopy data that stores volumetric images as multi-page
|
|
||||||
TIFFs. They have some (sample
|
|
||||||
data)[https://www.openmicroscopy.org/site/support/ome-model/ome-tiff/data.html]
|
|
||||||
including a 4D image of an embryo.
|
|
||||||
|
|
||||||
Each TIFF contains 10 slices. Normally you just see page 0:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ vipsheader tubhiswt_C0_TP13.ome.tif
|
|
||||||
tubhiswt_C0_TP13.ome.tif: 512x512 uchar, 1 band, b-w, tiffload
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `n=-1` and you see all the pages as a very tall strip:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ vipsheader tubhiswt_C0_TP13.ome.tif[n=-1]
|
|
||||||
tubhiswt_C0_TP13.ome.tif: 512x5120 uchar, 1 band, b-w, tiffload
|
|
||||||
```
|
|
||||||
|
|
||||||
You can work with PDF, TIFF, GIF and all imagemagick-supported formats in
|
|
||||||
this way.
|
|
||||||
|
|
||||||
You can write this tall strip to another file, and it will be broken up into
|
|
||||||
pages:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ vips copy tubhiswt_C0_TP13.ome.tif[n=-1] x.tif
|
|
||||||
$ vipsheader x.tif
|
|
||||||
x.tif: 512x512 uchar, 1 band, b-w, tiffload
|
|
||||||
$ vipsheader x.tif[n=-1]
|
|
||||||
x.tif: 512x5120 uchar, 1 band, b-w, tiffload
|
|
||||||
```
|
|
||||||
|
|
||||||
The extra magic is a `page-height` property that images carry around that says
|
|
||||||
how long each sheet of toilet paper is.
|
|
||||||
|
|
||||||
There are clearly some restrictions with this style of multi-page document
|
|
||||||
handling: all pages must have identical width, height and colour depth; and image
|
|
||||||
processing operators have no idea they are dealing with a multi-page document,
|
|
||||||
so if you do something like `resize`, you'll need to update `page-height`.
|
|
||||||
You'll also need to be careful about edge effects if you're using spatial
|
|
||||||
filters.
|
|
||||||
|
|
||||||
## Computation reordering
|
|
||||||
|
|
||||||
Thanks to the developer of
|
|
||||||
(PhotoFlow)[https://github.com/aferrero2707/PhotoFlow], a non-destructive image
|
|
||||||
editor with a libvips backend, libvips can now reorder computations to reduce
|
|
||||||
recalculation. This can (sometimes) produce a dramatic speedup.
|
|
||||||
|
|
||||||
This has been (discussed on the libvips
|
|
||||||
blog)[http://libvips.blogspot.co.uk/2017/01/automatic-computation-reordering.html],
|
|
||||||
but briefly, the order in which operator arguments are evaluated can have a
|
|
||||||
big effect on runtime due to the way libvips tries to cache and reuse results
|
|
||||||
behind the scenes.
|
|
||||||
|
|
||||||
The blog post has some examples and some graphs.
|
|
||||||
|
|
||||||
## New sequential mode
|
|
||||||
|
|
||||||
libvips sequential mode has been around for a while. This is the thing libvips
|
|
||||||
uses to stream pixels through your computer, from input file to output file,
|
|
||||||
without having to have the whole image in memory all at the same time. When it
|
|
||||||
works, it give a nice performance boost and a large drop in memory use.
|
|
||||||
|
|
||||||
There are some more complex cases where it didn't work. Consider this Python
|
|
||||||
program:
|
|
||||||
|
|
||||||
```python
|
|
||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import random
|
|
||||||
|
|
||||||
import gi
|
|
||||||
gi.require_version('Vips', '8.0')
|
|
||||||
from gi.repository import Vips
|
|
||||||
|
|
||||||
composite = Vips.Image.black(10000, 10000)
|
|
||||||
|
|
||||||
for filename in sys.argv[2:]:
|
|
||||||
tile = Vips.Image.new_from_file(filename, access = Vips.Access.SEQUENTIAL)
|
|
||||||
x = random.randint(0, composite.width - tile.width)
|
|
||||||
y = random.randint(0, composite.height - tile.height)
|
|
||||||
composite = composite.insert(tile, x, y)
|
|
||||||
|
|
||||||
composite.write_to_file(sys.argv[1])
|
|
||||||
```
|
|
||||||
|
|
||||||
It makes a large 10,000 x 10,000 pixel image, then inserts all of the images
|
|
||||||
you list at random positions, then writes the result.
|
|
||||||
|
|
||||||
You'd think this could work with sequential mode, but sadly with earlier
|
|
||||||
libvipses it will sometimes fail. The problem is that images can cover each
|
|
||||||
other, so while writing, libvips can discover that it only needs the bottom few
|
|
||||||
pixels of one of the input images. The image loaders used to track the current
|
|
||||||
read position, and if a request came in for some pixels way down the image,
|
|
||||||
they'd assume one of the evaluation threads had run ahead of the rest and
|
|
||||||
needed to be stalled. Once stalled, it was only restarted on a long timeout,
|
|
||||||
causing performance to drop through the floor.
|
|
||||||
|
|
||||||
libvips 8.5 has a new implementation of sequential mode that changes the way
|
|
||||||
threads are kept together as images are processed. Rather than trying to add
|
|
||||||
constraints to load operations, instead it puts the constraints into operations
|
|
||||||
that can cause threads to become spread out, such as vertical shrink.
|
|
||||||
|
|
||||||
As a result of this change, many more things can run in sequential mode, and
|
|
||||||
out of order reads should be impossible.
|
|
||||||
|
|
||||||
## `libxml2` swapped out for `expat`
|
|
||||||
|
|
||||||
libvips has used libxml2 as its XML parser since dinosaurs roamed the Earth.
|
|
||||||
Now libvips is based on gobject, the XML parser selected by glib, expat, makes
|
|
||||||
more sense, since it will already be linked.
|
|
||||||
|
|
||||||
It's nice to be able to remove a required dependency for a change.
|
|
||||||
|
|
||||||
## File format support
|
|
||||||
|
|
||||||
As usual, there are a range of improvements to file format read and write.
|
|
||||||
|
|
||||||
* Thanks to a push from Felix Bünemann, TIFF now supports load and save to and
|
|
||||||
from memory buffers.
|
|
||||||
* `dzsave` can write to memory (as a zip file) as well.
|
|
||||||
* Again, thanks to pushing from Felix, libvips now supports ICC, XMP and IPCT
|
|
||||||
metadata for WebP images.
|
|
||||||
* FITS images support `bzero` and `bscale`.
|
|
||||||
* `tiffload` memory use is now much lower for images with large strips.
|
|
||||||
|
|
||||||
## Other
|
|
||||||
|
|
||||||
Many small bug fixes, improvements to the C++ binding. As usual, the
|
|
||||||
ChangeLog has more detail, if you're interested.
|
|
Loading…
Reference in New Issue
Block a user