Last active
April 18, 2017 04:49
-
-
Save jcupitt/abacc012e2991f332e8b to your computer and use it in GitHub Desktop.
use libvips to compose two images with alpha channels
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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