works!
but only OVER mode at the moment, and it's not especially quick
This commit is contained in:
parent
e44921f481
commit
7c7c30a601
@ -1,6 +1,7 @@
|
||||
noinst_LTLIBRARIES = libconversion.la
|
||||
|
||||
libconversion_la_SOURCES = \
|
||||
composite.c \
|
||||
smartcrop.c \
|
||||
conversion.c \
|
||||
pconversion.h \
|
||||
|
@ -49,6 +49,16 @@
|
||||
#include <vips/internal.h>
|
||||
#include <vips/debug.h>
|
||||
|
||||
#include "pconversion.h"
|
||||
|
||||
/* Maximum number of input images -- why not?
|
||||
*/
|
||||
#define MAX_INPUT_IMAGES (64)
|
||||
|
||||
/* Maximum number of image bands.
|
||||
*/
|
||||
#define MAX_BANDS (64)
|
||||
|
||||
/**
|
||||
* VipsBlendMode:
|
||||
* VIPS_BLEND_MODE_OVER:
|
||||
@ -61,6 +71,13 @@
|
||||
* @gasi's composite example https://gist.github.com/jcupitt/abacc012e2991f332e8b
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Alpha_compositing
|
||||
*
|
||||
* composite -compose over PNG_transparency_demonstration_1.png \
|
||||
* Opera-icon-high-res.png x.png
|
||||
*
|
||||
* vips composite "PNG_transparency_demonstration_1.png \
|
||||
* Opera-icon-high-res.png" x.png 0
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct _VipsComposite {
|
||||
@ -79,44 +96,112 @@ typedef struct _VipsComposite {
|
||||
*/
|
||||
VipsInterpretation compositing_space;
|
||||
|
||||
/* Set if the input images have already been premultiplied.
|
||||
*/
|
||||
gboolean premultiplied;
|
||||
|
||||
/* The number of inputs. This can be less than the number of images in
|
||||
* @in.
|
||||
*/
|
||||
int n;
|
||||
|
||||
/* The number of bands we are blending.
|
||||
*/
|
||||
int bands;
|
||||
|
||||
} VipsComposite;
|
||||
|
||||
typedef VipsConversionClass VipsCompositeClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsComposite, vips_composite, VIPS_TYPE_CONVERSION );
|
||||
|
||||
#define COMPOSE_INT( TYPE ) { \
|
||||
TYPE
|
||||
#define BLEND( OUT, AOUT, SRC1, A1, SRC2, A2 ) { \
|
||||
if( AOUT == 0 ) \
|
||||
OUT = 0; \
|
||||
else \
|
||||
switch( mode ) { \
|
||||
case VIPS_BLEND_MODE_OVER: \
|
||||
OUT = (SRC1 + SRC2 * (1 - A1)) / AOUT; \
|
||||
break; \
|
||||
\
|
||||
default: \
|
||||
g_assert_not_reached(); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define COMBINE( TYPE ) { \
|
||||
TYPE **tp = (TYPE **) p; \
|
||||
TYPE *tq = (TYPE *) q; \
|
||||
\
|
||||
for( x = 0; x < width; x++ ) { \
|
||||
int o = x * (bands + 1); \
|
||||
\
|
||||
for( b = 0; b < bands; b++ ) \
|
||||
pixel[b] = tp[n - 1][o + b]; \
|
||||
alpha = tp[n - 1][o + bands] / 255.0; \
|
||||
\
|
||||
for( i = n - 2; i >= 0; i-- ) { \
|
||||
TYPE *src1 = tp[i] + o; \
|
||||
double a1 = src1[bands] / 255.0; \
|
||||
double aout = a1 + alpha * (1 - a1); \
|
||||
VipsBlendMode mode = ((VipsBlendMode *) \
|
||||
composite->mode->area.data)[i]; \
|
||||
\
|
||||
for( b = 0; b < bands; b++ ) \
|
||||
BLEND( pixel[b], aout, \
|
||||
src1[b], a1, pixel[b], alpha ); \
|
||||
\
|
||||
alpha = aout; \
|
||||
} \
|
||||
\
|
||||
for( b = 0; b < bands; b++ ) \
|
||||
tq[o + b] = pixel[b]; \
|
||||
tq[o + bands] = alpha * 255; \
|
||||
} \
|
||||
}
|
||||
|
||||
static void
|
||||
vips_composite_process_line( VipsComposite *composite, VipsBandFormat format,
|
||||
VipsPel *q, VipsPel **p, int width )
|
||||
{
|
||||
int n = composite->n;
|
||||
int i;
|
||||
int bands = composite->bands;
|
||||
|
||||
double pixel[MAX_BANDS];
|
||||
double alpha;
|
||||
int x, i, b;
|
||||
|
||||
switch( format ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
COMPOSE_INT( unsigned char );
|
||||
COMBINE( unsigned char );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_CHAR:
|
||||
COMPOSE_INT( unsigned char );
|
||||
COMBINE( signed char );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
COMBINE( unsigned short );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_SHORT:
|
||||
COMBINE( signed short );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_UINT:
|
||||
COMBINE( unsigned int );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_INT:
|
||||
COMBINE( signed int );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
COMBINE( float );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
g_assert_not_reached();
|
||||
COMBINE( double );
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_COMPLEX:
|
||||
@ -138,7 +223,7 @@ vips_composite_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop
|
||||
|
||||
if( vips_reorder_prepare_many( or->im, ir, r ) )
|
||||
return( -1 );
|
||||
for( i = 0; i < bandary->n; i++ )
|
||||
for( i = 0; i < composite->n; i++ )
|
||||
p[i] = VIPS_REGION_ADDR( ir[i], r->left, r->top );
|
||||
p[i] = NULL;
|
||||
q = VIPS_REGION_ADDR( or, r->left, r->top );
|
||||
@ -149,7 +234,7 @@ vips_composite_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop
|
||||
vips_composite_process_line( composite, ir[0]->im->BandFmt,
|
||||
q, p, r->width );
|
||||
|
||||
for( i = 0; i < bandary->n; i++ )
|
||||
for( i = 0; i < composite->n; i++ )
|
||||
p[i] += VIPS_REGION_LSKIP( ir[i] );
|
||||
q += VIPS_REGION_LSKIP( or );
|
||||
}
|
||||
@ -164,8 +249,8 @@ vips_composite_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsConversion *conversion = VIPS_CONVERSION( object );
|
||||
VipsBandary *bandary = (VipsBandary *) object;
|
||||
VipsComposite *composite = (VipsComposite *) object;
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 5 );
|
||||
|
||||
int i;
|
||||
VipsImage **in;
|
||||
@ -177,13 +262,49 @@ vips_composite_build( VipsObject *object )
|
||||
VipsInterpretation compositing_space;
|
||||
int max_bands;
|
||||
VipsInterpretation max_interpretation;
|
||||
VipsBlendMode *mode;
|
||||
VipsImage *out;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_composite_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
composite->n = composite->in->area->n;
|
||||
composite->n = composite->in->area.n;
|
||||
|
||||
if( composite->n <= 0 ) {
|
||||
vips_error( class->nickname, "%s", _( "no input images" ) );
|
||||
return( -1 );
|
||||
}
|
||||
if( composite->mode->area.n != composite->n - 1 ) {
|
||||
vips_error( class->nickname,
|
||||
_( "for %d input images there must be %d blend modes" ),
|
||||
composite->n, composite->n - 1 );
|
||||
return( -1 );
|
||||
}
|
||||
mode = (VipsBlendMode *) composite->mode->area.data;
|
||||
for( i = 0; i < composite->n - 1; i++ ) {
|
||||
if( mode[i] < 0 ||
|
||||
mode[i] >= VIPS_BLEND_MODE_LAST ) {
|
||||
vips_error( class->nickname,
|
||||
_( "blend mode index %d (%d) invalid" ),
|
||||
i, mode[i] );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
in = (VipsImage **) composite->in->area.data;
|
||||
|
||||
/* Premultiply alpha, if it hasn't been.
|
||||
*/
|
||||
if( !composite->premultiplied ) {
|
||||
VipsImage **premultiply = (VipsImage **)
|
||||
vips_object_local_array( object, composite->n );
|
||||
|
||||
for( i = 0; i < composite->n; i++ )
|
||||
if( vips_premultiply( in[i], &premultiply[i], NULL ) )
|
||||
return( -1 );
|
||||
in = premultiply;
|
||||
}
|
||||
|
||||
in = (VipsImage **) composite->in->data;
|
||||
decode = (VipsImage **) vips_object_local_array( object, composite->n );
|
||||
for( i = 0; i < composite->n; i++ )
|
||||
if( vips_image_decode( in[i], &decode[i] ) )
|
||||
@ -197,7 +318,7 @@ vips_composite_build( VipsObject *object )
|
||||
if( !vips_image_hasalpha( in[i] ) ) {
|
||||
VipsImage *x;
|
||||
|
||||
if( vips_bandjoin( in[i], &x, 255 ) )
|
||||
if( vips_bandjoin_const1( in[i], &x, 255, NULL ) )
|
||||
return( -1 );
|
||||
g_object_unref( in[i] );
|
||||
in[i] = x;
|
||||
@ -205,6 +326,12 @@ vips_composite_build( VipsObject *object )
|
||||
break;
|
||||
}
|
||||
|
||||
if( composite->n > MAX_INPUT_IMAGES ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "too many input images" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Transform to compositing space. It defaults to sRGB or B_W.
|
||||
*/
|
||||
if( !vips_object_argument_isset( object, "compositing_space" ) ) {
|
||||
@ -249,15 +376,37 @@ vips_composite_build( VipsObject *object )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( vips_image_pipeline_array( conversion->out,
|
||||
if( in[0]->Bands > MAX_BANDS ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "too many input bands" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
composite->bands = in[0]->Bands - 1;
|
||||
|
||||
t[0] = vips_image_new();
|
||||
out = t[0];
|
||||
|
||||
if( vips_image_pipeline_array( out,
|
||||
VIPS_DEMAND_STYLE_THINSTRIP, in ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_image_generate( conversion->out,
|
||||
if( vips_image_generate( out,
|
||||
vips_start_many, vips_composite_gen, vips_stop_many,
|
||||
in, composite ) )
|
||||
return( -1 );
|
||||
|
||||
/* And unpremultiply alpha, if we need to.
|
||||
*/
|
||||
if( !composite->premultiplied ) {
|
||||
if( vips_unpremultiply( out, &t[1], NULL ) )
|
||||
return( -1 );
|
||||
out = t[1];
|
||||
}
|
||||
|
||||
if( vips_image_write( out, conversion->out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -286,7 +435,7 @@ vips_composite_class_init( VipsCompositeClass *class )
|
||||
G_STRUCT_OFFSET( VipsComposite, in ),
|
||||
VIPS_TYPE_ARRAY_IMAGE );
|
||||
|
||||
VIPS_ARG_BOXED( class, "mode", 1,
|
||||
VIPS_ARG_BOXED( class, "mode", 3,
|
||||
_( "Blend modes" ),
|
||||
_( "Array of VipsBlendMode to join with" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
@ -294,12 +443,19 @@ vips_composite_class_init( VipsCompositeClass *class )
|
||||
VIPS_TYPE_ARRAY_INT );
|
||||
|
||||
VIPS_ARG_ENUM( class, "compositing_space", 10,
|
||||
_( "Interpretation" ),
|
||||
_( "Pixel interpretation" ),
|
||||
_( "Compositing space" ),
|
||||
_( "Composite images in this colour space" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsComposite, compositing_space ),
|
||||
VIPS_TYPE_INTERPRETATION, VIPS_INTERPRETATION_sRGB );
|
||||
|
||||
VIPS_ARG_BOOL( class, "premultiplied", 11,
|
||||
_( "Premultiplied" ),
|
||||
_( "Images have premultiplied alpha" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsComposite, premultiplied ),
|
||||
FALSE );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@ -309,14 +465,18 @@ vips_composite_init( VipsComposite *composite )
|
||||
}
|
||||
|
||||
static int
|
||||
vips_compositev( VipsImage **in, VipsImage **out, int n, va_list ap )
|
||||
vips_compositev( VipsImage **in, VipsImage **out, int n, int *mode, va_list ap )
|
||||
{
|
||||
VipsArrayImage *array;
|
||||
VipsArrayImage *image_array;
|
||||
VipsArrayInt *mode_array;
|
||||
int result;
|
||||
|
||||
array = vips_array_image_new( in, n );
|
||||
result = vips_call_split( "composite", ap, array, out );
|
||||
vips_area_unref( VIPS_AREA( array ) );
|
||||
image_array = vips_array_image_new( in, n );
|
||||
mode_array = vips_array_int_new( mode, n - 1 );
|
||||
result = vips_call_split( "composite", ap,
|
||||
image_array, out, mode_array );
|
||||
vips_area_unref( VIPS_AREA( image_array ) );
|
||||
vips_area_unref( VIPS_AREA( mode_array ) );
|
||||
|
||||
return( result );
|
||||
}
|
||||
@ -326,34 +486,23 @@ vips_compositev( VipsImage **in, VipsImage **out, int n, va_list ap )
|
||||
* @in: (array length=n) (transfer none): array of input images
|
||||
* @out: output image
|
||||
* @n: number of input images
|
||||
* @mode: array of (@n - 1) #VipsBlendMode
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Join a set of images together, bandwise.
|
||||
*
|
||||
* If the images
|
||||
* have n and m bands, then the output image will have n + m
|
||||
* bands, with the first n coming from the first image and the last m
|
||||
* from the second.
|
||||
*
|
||||
* If the images differ in size, the smaller images are enlarged to match the
|
||||
* larger by adding zero pixels along the bottom and right.
|
||||
*
|
||||
* The input images are cast up to the smallest common type (see table
|
||||
* Smallest common format in
|
||||
* <link linkend="libvips-arithmetic">arithmetic</link>).
|
||||
* Composite an array of images together.
|
||||
*
|
||||
* See also: vips_insert().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
vips_composite( VipsImage **in, VipsImage **out, int n, ... )
|
||||
vips_composite( VipsImage **in, VipsImage **out, int n, int *mode, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, n );
|
||||
result = vips_compositev( in, out, n, ap );
|
||||
va_start( ap, mode );
|
||||
result = vips_compositev( in, out, n, mode, ap );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
@ -364,24 +513,28 @@ vips_composite( VipsImage **in, VipsImage **out, int n, ... )
|
||||
* @in1: first input image
|
||||
* @in2: second input image
|
||||
* @out: output image
|
||||
* @mode: composite with this blend mode
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Join a pair of images together, bandwise. See vips_composite().
|
||||
* Composite a pair of images together. See vips_composite().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
vips_composite2( VipsImage *in1, VipsImage *in2, VipsImage **out, ... )
|
||||
vips_composite2( VipsImage *in1, VipsImage *in2, VipsImage **out,
|
||||
VipsBlendMode mode1, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
VipsImage *in[2];
|
||||
int mode[1];
|
||||
|
||||
in[0] = in1;
|
||||
in[1] = in2;
|
||||
mode[0] = mode1;
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_compositev( in, out, 2, ap );
|
||||
va_start( ap, mode1 );
|
||||
result = vips_compositev( in, out, 2, mode, ap );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
|
@ -274,6 +274,7 @@ vips_conversion_operation_init( void )
|
||||
extern GType vips_xyz_get_type( void );
|
||||
extern GType vips_falsecolour_get_type( void );
|
||||
extern GType vips_gamma_get_type( void );
|
||||
extern GType vips_composite_get_type( void );
|
||||
|
||||
vips_copy_get_type();
|
||||
vips_tile_cache_get_type();
|
||||
@ -321,4 +322,5 @@ vips_conversion_operation_init( void )
|
||||
vips_xyz_get_type();
|
||||
vips_falsecolour_get_type();
|
||||
vips_gamma_get_type();
|
||||
vips_composite_get_type();
|
||||
}
|
||||
|
@ -89,6 +89,11 @@ typedef enum {
|
||||
VIPS_INTERESTING_LAST
|
||||
} VipsInteresting;
|
||||
|
||||
typedef enum {
|
||||
VIPS_BLEND_MODE_OVER,
|
||||
VIPS_BLEND_MODE_LAST
|
||||
} VipsBlendMode;
|
||||
|
||||
int vips_copy( VipsImage *in, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_tilecache( VipsImage *in, VipsImage **out, ... )
|
||||
|
@ -44,6 +44,8 @@ GType vips_angle45_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_ANGLE45 (vips_angle45_get_type())
|
||||
GType vips_interesting_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_INTERESTING (vips_interesting_get_type())
|
||||
GType vips_blend_mode_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_BLEND_MODE (vips_blend_mode_get_type())
|
||||
/* enumerations from "../../../libvips/include/vips/convolution.h" */
|
||||
GType vips_combine_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_COMBINE (vips_combine_get_type())
|
||||
|
@ -348,6 +348,23 @@ vips_interesting_get_type( void )
|
||||
|
||||
return( etype );
|
||||
}
|
||||
GType
|
||||
vips_blend_mode_get_type( void )
|
||||
{
|
||||
static GType etype = 0;
|
||||
|
||||
if( etype == 0 ) {
|
||||
static const GEnumValue values[] = {
|
||||
{VIPS_BLEND_MODE_OVER, "VIPS_BLEND_MODE_OVER", "over"},
|
||||
{VIPS_BLEND_MODE_LAST, "VIPS_BLEND_MODE_LAST", "last"},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
||||
etype = g_enum_register_static( "VipsBlendMode", values );
|
||||
}
|
||||
|
||||
return( etype );
|
||||
}
|
||||
/* enumerations from "../../libvips/include/vips/convolution.h" */
|
||||
GType
|
||||
vips_combine_get_type( void )
|
||||
|
Loading…
x
Reference in New Issue
Block a user