/* VipsBandjoin -- bandwise join of a set of images * * Copyright: 1991, N. Dessipris, modification of im_bandjoin() * * Author: N. Dessipris * Written on: 17/04/1991 * Modified on : * 16/3/94 JC * - rewritten for partials * - now in ANSI C * - now works for any number of input images, except zero * 7/10/94 JC * - new IM_NEW() * 16/4/07 * - fall back to im_copy() for 1 input image * 17/1/09 * - cleanups * - gtk-doc * - im_bandjoin() just calls this * - works for RAD coding too * 27/1/10 * - formatalike inputs * 17/5/11 * - sizealike inputs * 27/10/11 * - rewrite as a class * 7/11/15 * - added bandjoin_const */ /* This file is part of VIPS. VIPS is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define VIPS_DEBUG */ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include #include #include #include "bandary.h" typedef struct _VipsBandjoin { VipsBandary parent_instance; /* The input images. */ VipsArrayImage *in; } VipsBandjoin; typedef VipsBandaryClass VipsBandjoinClass; G_DEFINE_TYPE( VipsBandjoin, vips_bandjoin, VIPS_TYPE_BANDARY ); static void vips_bandjoin_buffer( VipsBandarySequence *seq, VipsPel *q, VipsPel **p, int width ) { VipsBandary *bandary = seq->bandary; VipsConversion *conversion = (VipsConversion *) bandary; VipsImage **in = bandary->ready; /* Output pel size. */ const int ops = VIPS_IMAGE_SIZEOF_PEL( conversion->out ); int i; /* Loop for each input image. Scattered write is faster than * scattered read. */ for( i = 0; i < bandary->n; i++ ) { /* Input pel size. */ int ips = VIPS_IMAGE_SIZEOF_PEL( in[i] ); VipsPel * restrict p1; VipsPel * restrict q1; int x, z; q1 = q; p1 = p[i]; for( x = 0; x < width; x++ ) { for( z = 0; z < ips; z++ ) q1[z] = p1[z]; p1 += ips; q1 += ops; } q += ips; } } static int vips_bandjoin_build( VipsObject *object ) { VipsBandary *bandary = (VipsBandary *) object; VipsBandjoin *bandjoin = (VipsBandjoin *) object; if( bandjoin->in ) { bandary->in = vips_array_image_get( bandjoin->in, &bandary->n ); if( bandary->n == 1 ) return( vips_bandary_copy( bandary ) ); else { int i; bandary->out_bands = 0; for( i = 0; i < bandary->n; i++ ) if( bandary->in[i] ) bandary->out_bands += bandary->in[i]->Bands; } } if( VIPS_OBJECT_CLASS( vips_bandjoin_parent_class )->build( object ) ) return( -1 ); return( 0 ); } static void vips_bandjoin_class_init( VipsBandjoinClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); VipsBandaryClass *bandary_class = VIPS_BANDARY_CLASS( class ); VIPS_DEBUG_MSG( "vips_bandjoin_class_init\n" ); gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; vobject_class->nickname = "bandjoin"; vobject_class->description = _( "bandwise join a set of images" ); vobject_class->build = vips_bandjoin_build; bandary_class->process_line = vips_bandjoin_buffer; VIPS_ARG_BOXED( class, "in", 0, _( "Input" ), _( "Array of input images" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsBandjoin, in ), VIPS_TYPE_ARRAY_IMAGE ); } static void vips_bandjoin_init( VipsBandjoin *bandjoin ) { /* Init our instance fields. */ } static int vips_bandjoinv( VipsImage **in, VipsImage **out, int n, va_list ap ) { VipsArrayImage *array; int result; array = vips_array_image_new( in, n ); result = vips_call_split( "bandjoin", ap, array, out ); vips_area_unref( VIPS_AREA( array ) ); return( result ); } /** * vips_bandjoin: * @in: (array length=n) (transfer none): array of input images * @out: (out): output image * @n: number of input images * @...: %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 * arithmetic). * * See also: vips_insert(). * * Returns: 0 on success, -1 on error */ int vips_bandjoin( VipsImage **in, VipsImage **out, int n, ... ) { va_list ap; int result; va_start( ap, n ); result = vips_bandjoinv( in, out, n, ap ); va_end( ap ); return( result ); } /** * vips_bandjoin2: * @in1: first input image * @in2: second input image * @out: (out): output image * @...: %NULL-terminated list of optional named arguments * * Join a pair of images together, bandwise. See vips_bandjoin(). * * Returns: 0 on success, -1 on error */ int vips_bandjoin2( VipsImage *in1, VipsImage *in2, VipsImage **out, ... ) { va_list ap; int result; VipsImage *in[2]; in[0] = in1; in[1] = in2; va_start( ap, out ); result = vips_bandjoinv( in, out, 2, ap ); va_end( ap ); return( result ); } typedef struct _VipsBandjoinConst { VipsBandary parent_instance; VipsImage *in; VipsArrayDouble *c; /* The constant expanded to in's format, ready to be appended to each * pixel. */ int n; VipsPel *c_ready; } VipsBandjoinConst; typedef VipsBandaryClass VipsBandjoinConstClass; G_DEFINE_TYPE( VipsBandjoinConst, vips_bandjoin_const, VIPS_TYPE_BANDARY ); static void vips_bandjoin_const_finalize( GObject *object ) { VipsBandjoinConst *bandjoin = (VipsBandjoinConst *) object; VIPS_FREE( bandjoin->c_ready ); G_OBJECT_CLASS( vips_bandjoin_const_parent_class )->finalize( object ); } static void vips_bandjoin_const_buffer( VipsBandarySequence *seq, VipsPel *q, VipsPel **p, int width ) { VipsBandary *bandary = seq->bandary; VipsConversion *conversion = (VipsConversion *) bandary; VipsBandjoinConst *bandjoin = (VipsBandjoinConst *) bandary; VipsImage *in = bandary->ready[0]; /* Output pel size. */ const int ops = VIPS_IMAGE_SIZEOF_PEL( conversion->out ); /* Input pel size. */ const int ips = VIPS_IMAGE_SIZEOF_PEL( in ); /* Extra bands size. */ const int ebs = ops - ips; VipsPel * restrict p1; VipsPel * restrict q1; int x, z; q1 = q; p1 = p[0]; /* Special path for 8-bit RGB -> RGBA ... it's a common case. */ if( ips == 3 && ebs == 1 ) { int c = bandjoin->c_ready[0]; for( x = 0; x < width; x++ ) { q1[0] = p1[0]; q1[1] = p1[1]; q1[2] = p1[2]; q1[3] = c; p1 += 3; q1 += 4; } } else { for( x = 0; x < width; x++ ) { for( z = 0; z < ips; z++ ) q1[z] = p1[z]; p1 += ips; q1 += ips; for( z = 0; z < ebs; z++ ) q1[z] = bandjoin->c_ready[z]; q1 += ebs; } } } static int vips_bandjoin_const_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsBandary *bandary = (VipsBandary *) object; VipsBandjoinConst *bandjoin = (VipsBandjoinConst *) object; if( bandjoin->c && bandjoin->in ) { double *c; int n; c = vips_array_double_get( bandjoin->c, &n ); if( n == 0 ) return( vips_bandary_copy( bandary ) ); else bandary->out_bands = bandjoin->in->Bands + n; bandary->n = 1; bandary->in = &bandjoin->in; if( !(bandjoin->c_ready = vips__vector_to_pels( class->nickname, n, bandjoin->in->BandFmt, bandjoin->in->Coding, c, NULL, n )) ) return( -1 ); } if( VIPS_OBJECT_CLASS( vips_bandjoin_const_parent_class )-> build( object ) ) return( -1 ); return( 0 ); } static void vips_bandjoin_const_class_init( VipsBandjoinConstClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); VipsBandaryClass *bandary_class = VIPS_BANDARY_CLASS( class ); VIPS_DEBUG_MSG( "vips_bandjoin_const_class_init\n" ); gobject_class->finalize = vips_bandjoin_const_finalize; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; vobject_class->nickname = "bandjoin_const"; vobject_class->description = _( "append a constant band to an image" ); vobject_class->build = vips_bandjoin_const_build; bandary_class->process_line = vips_bandjoin_const_buffer; VIPS_ARG_IMAGE( class, "in", 0, _( "Input" ), _( "Input image" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsBandjoinConst, in ) ); VIPS_ARG_BOXED( class, "c", 12, _( "Constants" ), _( "Array of constants to add" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsBandjoinConst, c ), VIPS_TYPE_ARRAY_DOUBLE ); } static void vips_bandjoin_const_init( VipsBandjoinConst *bandjoin ) { /* Init our instance fields. */ } static int vips_bandjoin_constv( VipsImage *in, VipsImage **out, double *c, int n, va_list ap ) { VipsArrayDouble *array; int result; array = vips_array_double_new( c, n ); result = vips_call_split( "bandjoin_const", ap, in, out, array ); vips_area_unref( VIPS_AREA( array ) ); return( result ); } /** * vips_bandjoin_const: (method) * @in: input image * @out: (out): output image * @c: (array length=n): array of constants to append * @n: number of constants * @...: %NULL-terminated list of optional named arguments * * Append a set of constant bands to an image. * * See also: vips_bandjoin(). * * Returns: 0 on success, -1 on error */ int vips_bandjoin_const( VipsImage *in, VipsImage **out, double *c, int n, ... ) { va_list ap; int result; va_start( ap, n ); result = vips_bandjoin_constv( in, out, c, n, ap ); va_end( ap ); return( result ); } /** * vips_bandjoin_const1: (method) * @in: input image * @out: (out): output image * @c: constant to append * @...: %NULL-terminated list of optional named arguments * * Append a single constant band to an image. * * Returns: 0 on success, -1 on error */ int vips_bandjoin_const1( VipsImage *in, VipsImage **out, double c, ... ) { va_list ap; int result; va_start( ap, c ); result = vips_bandjoin_constv( in, out, &c, 1, ap ); va_end( ap ); return( result ); } /** * vips_addalpha: (method) * @in: input image * @out: (out): output image * @...: %NULL-terminated list of optional named arguments * * Append an alpha channel. * * See also: vips_image_hasalpha(). * * Returns: 0 on success, -1 on error */ int vips_addalpha( VipsImage *in, VipsImage **out, ... ) { double max_alpha; max_alpha = 255.0; if( in->Type == VIPS_INTERPRETATION_GREY16 || in->Type == VIPS_INTERPRETATION_RGB16 ) max_alpha = 65535; if( vips_bandjoin_const1( in, out, max_alpha, NULL ) ) return( -1 ); return( 0 ); }