libvips/doc/binding.xml
Kleis Auke Wolthuizen 9c22d74cff
doc: make pandoc optional (#2866)
Since it's currently unavailable on RHEL 9.
2022-06-15 10:19:49 +01:00

228 lines
8.9 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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="binding">
<para>
<refmeta> <refentrytitle>How to write bindings</refentrytitle> <manvolnum>3</manvolnum> <refmiscinfo>libvips</refmiscinfo> </refmeta>
</para>
<para>
<refnamediv> <refname>Binding</refname> <refpurpose>Writing bindings for libvips</refpurpose> </refnamediv>
</para>
<para>
There are full libvips bindings for quite a few environments now: C, C++, command-line, Ruby, PHP, Lua, Python and JavaScript (node).
</para>
<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>
<refsect3 xml:id="dont-bind-the-top-level-c-api">
<title>Dont bind the top-level C API</title>
<para>
The libvips C API (vips_add() and so on) was designed to be easy for humans to write. It is inconvenient and dangerous to use from other languages due to its heavy use of varargs.
</para>
<para>
Its much better to use the layer below. This lower layer is structured as:
</para>
<itemizedlist>
<listitem>
<para>
Create operator. You can use vips_operation_new() to make a new <literal>VipsOperation</literal> object from an operator nickname, like <literal>&quot;add&quot;</literal>.
</para>
</listitem>
<listitem>
<para>
Set parameters. You can loop over the operation with vips_argument_map() to get the name and type of each input argument. For each argument, you need to get the value from your language, convert to a <literal>GValue</literal>, then use g_object_set_property() to set that value on the operator.
</para>
</listitem>
<listitem>
<para>
Execute with vips_cache_operation_build().
</para>
</listitem>
<listitem>
<para>
Extract results. Again, you loop over the operator arguments with vips_argument_map(), but instead of inputs, this time you look for output arguments. You extract their value with g_object_get_property(), and pass the value back to your language.
</para>
</listitem>
</itemizedlist>
<para>
For example, you can execute vips_invert() like this:
</para>
<programlisting language="c">
/* compile with
*
* gcc -g -Wall callvips.c `pkg-config vips --cflags --libs`
*
*/
#include &lt;vips/vips.h&gt;
int
main( int argc, char **argv )
{
VipsImage *in;
VipsImage *out;
VipsOperation *op;
VipsOperation *new_op;
GValue gvalue = { 0 };
if( VIPS_INIT( argv[0] ) )
/* This shows the vips error buffer and quits with a fail exit
* code.
*/
vips_error_exit( NULL );
/* This will print a table of any ref leaks on exit, very handy for
* development.
*/
vips_leak_set( TRUE );
if( argc != 3 )
vips_error_exit( &quot;usage: %s input-filename output-filename&quot;,
argv[0] );
if( !(in = vips_image_new_from_file( argv[1], NULL )) )
vips_error_exit( NULL );
/* Create a new operator from a nickname. NULL for unknown operator.
*/
op = vips_operation_new( &quot;invert&quot; );
/* Init a gvalue as an image, set it to in, use the gvalue to set the
* operator property.
*/
g_value_init( &amp;gvalue, VIPS_TYPE_IMAGE );
g_value_set_object( &amp;gvalue, in );
g_object_set_property( G_OBJECT( op ), &quot;in&quot;, &amp;gvalue );
g_value_unset( &amp;gvalue );
/* We no longer need in: op will hold a ref to it as long as it needs
* it.
*/
g_object_unref( in );
/* Call the operation. This will look up the operation+args in the vips
* operation cache and either return a previous operation, or build
* this one. In either case, we have a new ref we must release.
*/
if( !(new_op = vips_cache_operation_build( op )) ) {
g_object_unref( op );
vips_error_exit( NULL );
}
g_object_unref( op );
op = new_op;
/* Now get the result from op. g_value_get_object() does not ref the
* object, so we need to make a ref for out to hold.
*/
g_value_init( &amp;gvalue, VIPS_TYPE_IMAGE );
g_object_get_property( G_OBJECT( op ), &quot;out&quot;, &amp;gvalue );
out = VIPS_IMAGE( g_value_get_object( &amp;gvalue ) );
g_object_ref( out );
g_value_unset( &amp;gvalue );
/* All done: we can unref op. The output objects from op actually hold
* refs back to it, so before we can unref op, we must unref them.
*/
vips_object_unref_outputs( VIPS_OBJECT( op ) );
g_object_unref( op );
if( vips_image_write_to_file( out, argv[2], NULL ) )
vips_error_exit( NULL );
g_object_unref( out );
return( 0 );
}
</programlisting>
</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 set of variable arguments.
</para>
<para>
A small Python program walks the set of all libvips operators and generates a set of static bindings. For example:
</para>
<programlisting language="c++">
VImage VImage::invert( VOption *options )
{
VImage out;
call( &quot;invert&quot;, (options ? options : VImage::option()) -&gt;
set( &quot;in&quot;, *this ) -&gt;
set( &quot;out&quot;, &amp;out ) );
return( out );
}
</programlisting>
<para>
So from C++ you can call any libvips operator (though without static typechecking) with <literal>VImage::call()</literal>, or use the member functions on <literal>VImage</literal> to get type-checked calls for at least the required operator arguments.
</para>
<para>
The <literal>VImage</literal> class also adds automatic reference counting, constant expansion, operator overloads, and various other useful features.
</para>
</refsect3>
<refsect3 xml:id="dynamic-language-with-ffi">
<title>Dynamic language with FFI</title>
<para>
Languages like Ruby, Python, JavaScript and LuaJIT cant call C directly, but they do support FFI. The bindings for these languages work rather like C++, but use FFI to call into libvips and run operations.
</para>
<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. In effect, the binding is generated at runtime.
</para>
</refsect3>
<refsect3 xml:id="dynamic-language-without-ffi">
<title>Dynamic language without FFI</title>
<para>
PHP does not have a useful FFI, unfortunately, so for this language a small C 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>
</refsect3>
<refsect3 xml:id="gobject-introspection">
<title>gobject-introspection</title>
<para>
The C source code to libvips has been marked up with special comments describing the interface in a standard way. These comments are read by the <literal>gobject-introspection</literal> package when libvips is compiled and used to generate a typelib, a description of how to call the library. Many languages have gobject-introspection packages: all you need to do to call libvips from your favorite language is to start g-o-i, load the libvips typelib, and you should have the whole library available. For example, from Python its as simple as:
</para>
<programlisting language="python">
from gi.repository import Vips
</programlisting>
<para>
You can now use all of the libvips introspection machinery, as noted above.
</para>
<para>
Unfortunately g-o-i has some strong disadvantages. It is not very portable, since you will need a g-o-i layer for whatever platform you are targetting; it does not cross-compile well, since typelibs include a lot of very-low level data (such as exact structure layouts); and installation for your users is likely to be tricky.
</para>
<para>
If you have a choice, I would recommend simply using FFI.
</para>
</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:
</para>
<programlisting>
$ g-ir-doc-tool --language=Python -o ~/mydocs Vips-8.0.gir
</programlisting>
<para>
Then to view them, either:
</para>
<programlisting>
$ yelp ~/mydocs
</programlisting>
<para>
Or perhaps:
</para>
<programlisting>
$ cd ~/mydocs
$ yelp-build html .
</programlisting>
<para>
To make HTML docs. This is an easy way to see what you can call in the library.
</para>
</refsect3>
</refentry>