Skip to content

Instantly share code, notes, and snippets.

@jcupitt
Last active April 18, 2017 04:49
Show Gist options
  • Save jcupitt/abacc012e2991f332e8b to your computer and use it in GitHub Desktop.
Save jcupitt/abacc012e2991f332e8b to your computer and use it in GitHub Desktop.
use libvips to compose two images with alpha channels
/* compile with:
*
* gcc -g -Wall composite2.c `pkg-config vips --cflags --libs`
*/
#include <stdio.h>
#include <vips/vips.h>
/* Composite images a and b.
*/
int
composite( VipsObject *context, VipsImage *a, VipsImage *b, VipsImage **out )
{
/* These images will all be unreffed when context is unreffed.
*/
VipsImage **t = (VipsImage **) vips_object_local_array( context, 16 );
/* Extract the alpha channels.
*/
if( vips_extract_band( a, &t[0], 3, NULL ) ||
vips_extract_band( b, &t[1], 3, NULL ) )
return( -1 );
/* Extract the RGB bands.
*/
if( vips_extract_band( a, &t[2], 0, "n", 3, NULL ) ||
vips_extract_band( b, &t[3], 0, "n", 3, NULL ) )
return( -1 );
/* Go to scrgb. This is a simple linear colourspace.
*/
if( vips_colourspace( t[2], &t[4], VIPS_INTERPRETATION_scRGB, NULL ) ||
vips_colourspace( t[3], &t[5], VIPS_INTERPRETATION_scRGB, NULL ) )
return( -1 );
/* Compute normalized input alpha channels.
*/
if( vips_linear1( t[0], &t[6], 1.0 / 255.0, 0.0, NULL ) ||
vips_linear1( t[1], &t[7], 1.0 / 255.0, 0.0, NULL ) )
return( -1 );
// Compute normalized output alpha channel:
//
// References:
// - http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
// - https://github.com/jcupitt/ruby-vips/issues/28#issuecomment-9014826
//
// out_a = src_a + dst_a * (1 - src_a)
if( vips_linear1( t[6], &t[8], -1.0, 1.0, NULL ) ||
vips_multiply( t[7], t[8], &t[9], NULL ) ||
vips_add( t[6], t[9], &t[10], NULL ) )
return( -1 );
// Compute output RGB channels:
//
// Wikipedia:
// out_rgb = (src_rgb * src_a + dst_rgb * dst_a * (1 - src_a)) / out_a
//
// `vips_ifthenelse` with `blend=TRUE`: http://bit.ly/1KoSsga
// out = (cond / 255) * in1 + (1 - cond / 255) * in2
//
// Substitutions:
//
// cond --> src_a
// in1 --> src_rgb
// in2 --> dst_rgb * dst_a (premultiplied destination RGB)
//
// Division by `out_a` is not required as `vips_ifthenelse` doesn’t require
// normalized inputs.
if( vips_multiply( t[5], t[7], &t[11], NULL ) ||
vips_ifthenelse( t[0], t[4], t[11], &t[12],
"blend", TRUE,
NULL ) )
return( -1 );
// back to the start colourspace, unnormalise the alpha, reattach
if( vips_colourspace( t[12], &t[13], t[1]->Type, NULL ) ||
vips_linear1( t[10], &t[14], 255.0, 0.0,
"uchar", TRUE,
NULL ) ||
vips_bandjoin2( t[13], t[14], &t[15], NULL ) )
return( -1 );
/* Return a reference to the output image.
*/
g_object_ref( t[15] );
*out = t[15];
return( 0 );
}
int
main( int argc, char **argv )
{
GOptionContext *context;
GOptionGroup *main_group;
GError *error = NULL;
// Main
char* programName = argv[0];
char* srcPath = argv[1];
char* dstPath = argv[2];
char* outPath = argv[3];
if( VIPS_INIT( programName ) )
vips_error_exit( NULL );
context = g_option_context_new( "src dst out" );
main_group = g_option_group_new( NULL, NULL, NULL, NULL, NULL );
g_option_context_set_main_group( context, main_group );
vips_add_option_entries( main_group );
if( !g_option_context_parse( context, &argc, &argv, &error ) ) {
if( error ) {
fprintf( stderr, "%s\n", error->message );
g_error_free( error );
}
vips_error_exit( NULL );
}
if( argc != 4 )
vips_error_exit( "usage: %s src dst out", programName );
// Load input images:
VipsImage *srcInput;
VipsImage *dstInput;
if( !(srcInput = vips_image_new_from_file( srcPath, NULL )) ||
!(dstInput = vips_image_new_from_file( dstPath, NULL )) )
vips_error_exit( NULL );
// the controlling object for this processing: all temp objects get unreffed
// when this is unreffed
VipsObject *handle = VIPS_OBJECT( vips_image_new() );
VipsImage *out;
if( composite( handle, srcInput, dstInput, &out ) ) {
g_object_unref( handle );
vips_error_exit( NULL );
}
g_object_unref( handle );
// Output
if( vips_image_write_to_file( out, outPath, NULL ) )
vips_error_exit( NULL );
g_object_unref( out );
g_object_unref( srcInput );
g_object_unref( dstInput );
return( 0 );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment