/* The inverse: take ink to a vec of double. Used in the vips7 compat * wrappers. Result valid while im is valid. */ double * vips__ink_to_vector( const char *domain, VipsImage *im, VipsPel *ink, int *n ) { VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( im ), 6 ); double *result; #ifdef VIPS_DEBUG printf( "vips__ink_to_vector: starting\n" ); #endif /*VIPS_DEBUG*/ /* Wrap a VipsImage around ink. */ t[0] = vips_image_new_from_memory( ink, 1, 1, VIPS_IMAGE_SIZEOF_PEL( im ), VIPS_FORMAT_UCHAR ); if( vips_copy( t[0], &t[1], "bands", im->Bands, "format", im->BandFmt, "coding", im->Coding, "interpretation", im->Type, NULL ) ) return( NULL ); /* The image may be coded .. unpack to double. */ if( vips_image_decode( t[1], &t[2] ) || vips_cast( t[2], &t[3], VIPS_FORMAT_DOUBLE, NULL ) ) return( NULL ); /* To a mem buffer, then copy to out. */ if( !(t[4] = vips_image_new_memory()) || vips_image_write( t[3], t[4] ) ) return( NULL ); if( !(result = VIPS_ARRAY( im, t[4]->Bands, double )) ) return( NULL ); memcpy( result, t[4]->data, VIPS_IMAGE_SIZEOF_PEL( t[4] ) ); *n = t[4]->Bands; #ifdef VIPS_DEBUG { int i; printf( "vips__ink_to_vector:\n" ); printf( "\tink = " ); for( i = 0; i < n; i++ ) printf( "%d ", ink[i] ); printf( "\n" ); printf( "\tvec = " ); for( i = 0; i < *n; i++ ) printf( "%d ", result[i] ); printf( "\n" ); } #endif /*VIPS_DEBUG*/ return( result ); }
static int vips_RGB162sRGB( VipsImage *in, VipsImage **out, ... ) { if( vips_cast( in, out, VIPS_FORMAT_UCHAR, "shift", TRUE, NULL ) ) return( -1 ); (*out)->Type = VIPS_INTERPRETATION_sRGB; return( 0 ); }
static int vips_ifthenelse_build( VipsObject *object ) { VipsConversion *conversion = VIPS_CONVERSION( object ); VipsIfthenelse *ifthenelse = (VipsIfthenelse *) object; VipsImage *all[3]; VipsImage **band = (VipsImage **) vips_object_local_array( object, 3 ); VipsImage **size = (VipsImage **) vips_object_local_array( object, 3 ); VipsImage **format = (VipsImage **) vips_object_local_array( object, 3 ); if( VIPS_OBJECT_CLASS( vips_ifthenelse_parent_class )->build( object ) ) return( -1 ); /* We have to have the condition image last since we want the output * image to inherit its properties from the then/else parts. */ all[0] = ifthenelse->in1; all[1] = ifthenelse->in2; all[2] = ifthenelse->cond; /* No need to check input images, sizealike and friends will do this * for us. */ /* Cast our input images up to a common bands and size. */ if( vips__bandalike_vec( "VipsIfthenelse", all, band, 3, 0 ) || vips__sizealike_vec( band, size, 3 ) ) return( -1 ); /* Condition is cast to uchar, then/else to a common type. */ if( vips_cast( size[2], &format[2], VIPS_FORMAT_UCHAR, NULL ) ) return( -1 ); if( vips__formatalike_vec( size, format, 2 ) ) return( -1 ); if( vips_image_copy_fields_array( conversion->out, format ) ) return( -1 ); vips_demand_hint_array( conversion->out, VIPS_DEMAND_STYLE_SMALLTILE, format ); if( vips_image_generate( conversion->out, vips_start_many, vips_ifthenelse_gen, vips_stop_many, format, ifthenelse ) ) return( -1 ); return( 0 ); }
static int vips_labelregions_build( VipsObject *object ) { VipsMorphology *morphology = VIPS_MORPHOLOGY( object ); VipsImage *in = morphology->in; VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); VipsImage *mask; int segments; int *m; int x, y; if( VIPS_OBJECT_CLASS( vips_labelregions_parent_class )-> build( object ) ) return( -1 ); /* Create the zero mask image in memory. */ mask = vips_image_new_memory(); g_object_set( object, "mask", mask, NULL ); if( vips_black( &t[0], in->Xsize, in->Ysize, NULL ) || vips_cast( t[0], &t[1], VIPS_FORMAT_INT, NULL ) || vips_image_write( t[1], mask ) ) return( -1 ); segments = 1; m = (int *) mask->data; for( y = 0; y < mask->Ysize; y++ ) { for( x = 0; x < mask->Xsize; x++ ) { if( !m[x] ) { /* Use a direct path for speed. */ if( vips__draw_flood_direct( mask, in, segments, x, y ) ) return( -1 ); segments += 1; } } m += mask->Xsize; } g_object_set( object, "segments", segments, NULL ); return( 0 ); }
static int vips_freqmult_build( VipsObject *object ) { VipsFreqfilt *freqfilt = VIPS_FREQFILT( object ); VipsFreqmult *freqmult = (VipsFreqmult *) object; VipsImage **t = (VipsImage **) vips_object_local_array( object, 5 ); VipsImage *in; if( VIPS_OBJECT_CLASS( vips_freqmult_parent_class )-> build( object ) ) return( -1 ); in = freqfilt->in; if( vips_band_format_iscomplex( in->BandFmt ) ) { if( vips_multiply( in, freqmult->mask, &t[0], NULL ) || vips_invfft( t[0], &t[1], "real", TRUE, NULL ) ) return( -1 ); in = t[1]; } else { /* Optimisation: output of vips_invfft() is double, we * will usually cast to char, so rather than keeping a * large double buffer and partial to char from that, * cast to a memory buffer and copy to out from that. * * FIXME does this actually work now we're a class? test * perhaps we need a temporary object */ t[4] = vips_image_new_memory(); if( vips_fwfft( in, &t[0], NULL ) || vips_multiply( t[0], freqmult->mask, &t[1], NULL ) || vips_invfft( t[1], &t[2], "real", TRUE, NULL ) || vips_cast( t[2], &t[3], in->BandFmt, NULL ) || vips_image_write( t[3], t[4] ) ) return( -1 ); in = t[4]; } if( vips_image_write( in, freqfilt->out ) ) return( -1 ); return( 0 ); }
static int vips_recomb_build( VipsObject *object ) { VipsConversion *conversion = (VipsConversion *) object; VipsRecomb *recomb = (VipsRecomb *) object; VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); if( VIPS_OBJECT_CLASS( vips_recomb_parent_class )->build( object ) ) return( -1 ); if( vips_image_pio_input( recomb->in ) || vips_check_uncoded( "VipsRecomb", recomb->in ) || vips_check_noncomplex( "VipsRecomb", recomb->in ) ) return( -1 ); if( vips_image_pio_input( recomb->m ) || vips_check_uncoded( "VipsRecomb", recomb->m ) || vips_check_noncomplex( "VipsRecomb", recomb->m ) || vips_check_mono( "VipsRecomb", recomb->m ) ) return( -1 ); if( recomb->in->Bands != recomb->m->Xsize ) { vips_error( "VipsRecomb", "%s", _( "bands in must equal matrix width" ) ); return( -1 ); } if( vips_cast( recomb->m, &t[0], VIPS_FORMAT_DOUBLE, NULL ) || vips_image_wio_input( t[0] ) ) return( -1 ); recomb->coeff = (double *) VIPS_IMAGE_ADDR( t[0], 0, 0 ); if( vips_image_copy_fields( conversion->out, recomb->in ) ) return( -1 ); vips_demand_hint( conversion->out, VIPS_DEMAND_STYLE_THINSTRIP, recomb->in, NULL ); conversion->out->Bands = recomb->m->Ysize; if( vips_bandfmt_isint( recomb->in->BandFmt ) ) conversion->out->BandFmt = VIPS_FORMAT_FLOAT; if( vips_image_generate( conversion->out, vips_start_one, vips_recomb_gen, vips_stop_one, recomb->in, recomb ) ) return( -1 ); return( 0 ); }
int vips__formatalike_vec( VipsImage **in, VipsImage **out, int n ) { int i; VipsBandFormat format; g_assert( n >= 1 ); format = in[0]->BandFmt; for( i = 1; i < n; i++ ) format = vips_format_common( format, in[i]->BandFmt ); for( i = 0; i < n; i++ ) if( vips_cast( in[i], &out[i], format, NULL ) ) return( -1 ); return( 0 ); }
/** * vips_check_matrix: * @domain: the originating domain for the error message * @im: image to check * @out: put image as in-memory doubles here * * Matrix images must have width and height less than 1000 and have 1 band. * * Return 0 if the image will pass as a matrix, or -1 and set an error * message otherwise. * * @out is set to be @im cast to double and stored in memory. Use * VIPS_IMAGE_ADDR() to address values in @out. @out is unreffed for you * when @im is unreffed. * * See also: vips_error(). * * Returns: 0 if OK, -1 otherwise. */ int vips_check_matrix( const char *domain, VipsImage *im, VipsImage **out ) { if( im->Xsize > 1000 || im->Ysize > 1000 ) { vips_error( domain, "%s", _( "matrix image too large" ) ); return( -1 ); } if( im->Bands != 1 ) { vips_error( domain, "%s", _( "matrix image must have one band" ) ); return( -1 ); } if( vips_cast( im, out, VIPS_FORMAT_DOUBLE, NULL ) ) return( -1 ); vips_object_local( im, *out ); if( vips_image_wio_input( *out ) ) return( -1 ); return( 0 ); }
static int vips_statistic_build( VipsObject *object ) { VipsStatistic *statistic = VIPS_STATISTIC( object ); VipsStatisticClass *sclass = VIPS_STATISTIC_GET_CLASS( statistic ); VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); #ifdef DEBUG printf( "vips_statistic_build: " ); vips_object_print_name( object ); printf( "\n" ); #endif /*DEBUG*/ if( VIPS_OBJECT_CLASS( vips_statistic_parent_class )->build( object ) ) return( -1 ); statistic->ready = statistic->in; if( vips_image_decode( statistic->ready, &t[0] ) ) return( -1 ); statistic->ready = t[0]; /* If there's a format table, cast the input. */ if( sclass->format_table ) { if( vips_cast( statistic->ready, &t[1], sclass->format_table[statistic->in->BandFmt], NULL ) ) return( -1 ); statistic->ready = t[1]; } if( vips_sink( statistic->ready, vips_statistic_scan_start, vips_statistic_scan, vips_statistic_scan_stop, statistic, NULL ) ) return( -1 ); return( 0 ); }
/* Process the first @n bands with @fn, detach and reattach remaining bands. */ static int vips_process_n( const char *domain, VipsImage *in, VipsImage **out, int n, VipsColourTransformFn fn ) { if( in->Bands > n ) { VipsImage *scope = vips_image_new(); VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( scope ), 4 ); if( vips_extract_band( in, &t[0], 0, "n", n, NULL ) || vips_extract_band( in, &t[1], n, "n", in->Bands - n, NULL ) || fn( t[0], &t[2], NULL ) || vips_cast( t[1], &t[3], t[2]->BandFmt, NULL ) || vips_bandjoin2( t[2], t[3], out, NULL ) ) { g_object_unref( scope ); return( -1 ); } g_object_unref( scope ); } else if( in->Bands == n ) { if( fn( in, out, NULL ) ) return( -1 ); } else { vips_error( domain, "%s", _( "too few bands for operation" ) ); return( -1 ); } return( 0 ); }
static VipsImage * thumbnail_shrink( VipsObject *process, VipsImage *in ) { VipsImage **t = (VipsImage **) vips_object_local_array( process, 10 ); VipsInterpretation interpretation = linear_processing ? VIPS_INTERPRETATION_XYZ : VIPS_INTERPRETATION_sRGB; /* TRUE if we've done the import of an ICC transform and still need to * export. */ gboolean have_imported; /* TRUE if we've premultiplied and need to unpremultiply. */ gboolean have_premultiplied; VipsBandFormat unpremultiplied_format; /* Sniff the incoming image and try to guess what the alpha max is. */ double max_alpha; double shrink; /* RAD needs special unpacking. */ if( in->Coding == VIPS_CODING_RAD ) { vips_info( "vipsthumbnail", "unpacking Rad to float" ); /* rad is scrgb. */ if( vips_rad2float( in, &t[0], NULL ) ) return( NULL ); in = t[0]; } /* Try to guess what the maximum alpha might be. */ max_alpha = 255; if( in->BandFmt == VIPS_FORMAT_USHORT ) max_alpha = 65535; /* In linear mode, we import right at the start. * * We also have to import the whole image if it's CMYK, since * vips_colourspace() (see below) doesn't know about CMYK. * * This is only going to work for images in device space. If you have * an image in PCS which also has an attached profile, strange things * will happen. */ have_imported = FALSE; if( (linear_processing || in->Type == VIPS_INTERPRETATION_CMYK) && in->Coding == VIPS_CODING_NONE && (in->BandFmt == VIPS_FORMAT_UCHAR || in->BandFmt == VIPS_FORMAT_USHORT) && (vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || import_profile) ) { if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) vips_info( "vipsthumbnail", "importing with embedded profile" ); else vips_info( "vipsthumbnail", "importing with profile %s", import_profile ); if( vips_icc_import( in, &t[1], "input_profile", import_profile, "embedded", TRUE, "pcs", VIPS_PCS_XYZ, NULL ) ) return( NULL ); in = t[1]; have_imported = TRUE; } /* To the processing colourspace. This will unpack LABQ as well. */ vips_info( "vipsthumbnail", "converting to processing space %s", vips_enum_nick( VIPS_TYPE_INTERPRETATION, interpretation ) ); if( vips_colourspace( in, &t[2], interpretation, NULL ) ) return( NULL ); in = t[2]; /* If there's an alpha, we have to premultiply before shrinking. See * https://github.com/jcupitt/libvips/issues/291 */ have_premultiplied = FALSE; if( in->Bands == 2 || (in->Bands == 4 && in->Type != VIPS_INTERPRETATION_CMYK) || in->Bands == 5 ) { vips_info( "vipsthumbnail", "premultiplying alpha" ); if( vips_premultiply( in, &t[3], "max_alpha", max_alpha, NULL ) ) return( NULL ); have_premultiplied = TRUE; /* vips_premultiply() makes a float image. When we * vips_unpremultiply() below, we need to cast back to the * pre-premultiply format. */ unpremultiplied_format = in->BandFmt; in = t[3]; } shrink = calculate_shrink( in ); if( vips_resize( in, &t[4], 1.0 / shrink, NULL ) ) return( NULL ); in = t[4]; if( have_premultiplied ) { vips_info( "vipsthumbnail", "unpremultiplying alpha" ); if( vips_unpremultiply( in, &t[5], "max_alpha", max_alpha, NULL ) || vips_cast( t[5], &t[6], unpremultiplied_format, NULL ) ) return( NULL ); in = t[6]; } /* Colour management. * * If we've already imported, just export. Otherwise, we're in * device space and we need a combined import/export to transform to * the target space. */ if( have_imported ) { if( export_profile || vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { vips_info( "vipsthumbnail", "exporting to device space with a profile" ); if( vips_icc_export( in, &t[7], "output_profile", export_profile, NULL ) ) return( NULL ); in = t[7]; } else { vips_info( "vipsthumbnail", "converting to sRGB" ); if( vips_colourspace( in, &t[7], VIPS_INTERPRETATION_sRGB, NULL ) ) return( NULL ); in = t[7]; } } else if( export_profile && (vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || import_profile) ) { VipsImage *out; vips_info( "vipsthumbnail", "exporting with profile %s", export_profile ); /* We first try with the embedded profile, if any, then if * that fails try again with the supplied fallback profile. */ out = NULL; if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { vips_info( "vipsthumbnail", "importing with embedded profile" ); if( vips_icc_transform( in, &t[7], export_profile, "embedded", TRUE, NULL ) ) { vips_warn( "vipsthumbnail", _( "unable to import with " "embedded profile: %s" ), vips_error_buffer() ); vips_error_clear(); } else out = t[7]; } if( !out && import_profile ) { vips_info( "vipsthumbnail", "importing with fallback profile" ); if( vips_icc_transform( in, &t[7], export_profile, "input_profile", import_profile, "embedded", FALSE, NULL ) ) return( NULL ); out = t[7]; } /* If the embedded profile failed and there's no fallback or * the fallback failed, out will still be NULL. */ if( out ) in = out; } if( delete_profile && vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { vips_info( "vipsthumbnail", "deleting profile from output image" ); if( !vips_image_remove( in, VIPS_META_ICC_NAME ) ) return( NULL ); } return( in ); }
/* Calculate a pixel for an image from a vec of double. Valid while im is * valid. imag can be NULL, meaning all zero for the imaginary component. */ VipsPel * vips__vector_to_ink( const char *domain, VipsImage *im, double *real, double *imag, int n ) { /* Run our pipeline relative to this. */ VipsImage *context = vips_image_new(); VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( context ), 6 ); VipsBandFormat format; int bands; double *ones; VipsPel *result; int i; #ifdef VIPS_DEBUG printf( "vips__vector_to_ink: starting\n" ); #endif /*VIPS_DEBUG*/ vips_image_decode_predict( im, &bands, &format ); ones = VIPS_ARRAY( im, n, double ); for( i = 0; i < n; i++ ) ones[i] = 1.0; /* Cast vec to match the decoded image. */ if( vips_black( &t[1], 1, 1, "bands", bands, NULL ) || vips_linear( t[1], &t[2], ones, real, n, NULL ) || vips_cast( t[2], &t[3], format, NULL ) ) { g_object_unref( context ); return( NULL ); } /* And now recode the vec to match the original im. */ if( vips_image_encode( t[3], &t[4], im->Coding ) || !(t[5] = vips_image_new_memory()) || vips_image_write( t[4], t[5] ) ) { g_object_unref( context ); return( NULL ); } if( !(result = VIPS_ARRAY( im, VIPS_IMAGE_SIZEOF_PEL( t[5] ), VipsPel )) ) { g_object_unref( context ); return( NULL ); } g_assert( VIPS_IMAGE_SIZEOF_PEL( t[5] ) == VIPS_IMAGE_SIZEOF_PEL( im ) ); memcpy( result, t[5]->data, VIPS_IMAGE_SIZEOF_PEL( im ) ); g_object_unref( context ); #ifdef VIPS_DEBUG { int i; printf( "vips__vector_to_ink:\n" ); printf( "\t(real, imag) = " ); for( i = 0; i < n; i++ ) printf( "(%g, %g) ", real[i], imag ? imag[i] : 0 ); printf( "\n" ); printf( "\tink = " ); for( i = 0; i < VIPS_IMAGE_SIZEOF_PEL( im ); i++ ) printf( "%d ", result[i] ); printf( "\n" ); } #endif /*VIPS_DEBUG*/ return( result ); }
/* Make a pair of vector constants into a set of formatted pixels. bands can * be 3 while n is 1, meaning expand the constant to the number of bands. * imag can be NULL, meaning all zero for the imaginary component. */ VipsPel * vips__vector_to_pels( const char *domain, int bands, VipsBandFormat format, VipsCoding coding, double *real, double *imag, int n ) { /* Run our pipeline relative to this. */ VipsImage *context = vips_image_new(); VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( context ), 8 ); VipsImage *in; double *ones; VipsPel *result; int i; #ifdef VIPS_DEBUG printf( "vips__vector_to_pels: starting\n" ); #endif /*VIPS_DEBUG*/ ones = VIPS_ARRAY( context, n, double ); for( i = 0; i < n; i++ ) ones[i] = 1.0; /* Make the real and imaginary parts. */ if( vips_black( &t[0], 1, 1, "bands", bands, NULL ) || vips_linear( t[0], &t[1], ones, real, n, NULL ) ) { g_object_unref( context ); return( NULL ); } in = t[1]; if( imag ) { if( vips_black( &t[2], 1, 1, "bands", bands, NULL ) || vips_linear( t[2], &t[3], ones, imag, n, NULL ) || vips_complexform( in, t[3], &t[4], NULL ) ) { g_object_unref( context ); return( NULL ); } in = t[4]; } /* Cast to the output type and coding. */ if( vips_cast( in, &t[5], format, NULL ) || vips_image_encode( t[5], &t[6], coding ) ) { g_object_unref( context ); return( NULL ); } in = t[6]; /* Write to memory, copy to output buffer. */ if( !(t[7] = vips_image_new_memory()) || vips_image_write( in, t[7] ) ) { g_object_unref( context ); return( NULL ); } in = t[7]; if( !(result = VIPS_ARRAY( NULL, VIPS_IMAGE_SIZEOF_PEL( in ), VipsPel )) ) { g_object_unref( context ); return( NULL ); } memcpy( result, in->data, VIPS_IMAGE_SIZEOF_PEL( in ) ); #ifdef VIPS_DEBUG { int i; printf( "vips__vector_to_ink:\n" ); printf( "\t(real, imag) = " ); for( i = 0; i < n; i++ ) printf( "(%g, %g) ", real[i], imag ? imag[i] : 0 ); printf( "\n" ); printf( "\tink = " ); for( i = 0; i < VIPS_IMAGE_SIZEOF_PEL( in ); i++ ) printf( "%d ", result[i] ); printf( "\n" ); } #endif /*VIPS_DEBUG*/ g_object_unref( context ); return( result ); }
static int vips_histogram_build( VipsObject *object ) { VipsHistogram *histogram = VIPS_HISTOGRAM( object ); VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsHistogramClass *hclass = VIPS_HISTOGRAM_GET_CLASS( histogram ); VipsImage **decode; VipsImage **format; VipsImage **band; VipsImage **size; VipsImage **memory; VipsPel *outbuf; VipsPel **inbuf; int i; #ifdef DEBUG printf( "vips_histogram_build: " ); vips_object_print_name( object ); printf( "\n" ); #endif /*DEBUG*/ if( VIPS_OBJECT_CLASS( vips_histogram_parent_class )->build( object ) ) return( -1 ); g_assert( histogram->n > 0 ); /* Must be NULL-terminated. */ g_assert( !histogram->in[histogram->n] ); decode = (VipsImage **) vips_object_local_array( object, histogram->n ); format = (VipsImage **) vips_object_local_array( object, histogram->n ); band = (VipsImage **) vips_object_local_array( object, histogram->n ); size = (VipsImage **) vips_object_local_array( object, histogram->n ); memory = (VipsImage **) vips_object_local_array( object, histogram->n ); g_object_set( histogram, "out", vips_image_new(), NULL ); for( i = 0; i < histogram->n; i++ ) if( vips_image_decode( histogram->in[i], &decode[i] ) || vips_check_hist( class->nickname, decode[i] ) ) return( -1 ); /* Cast our input images up to a common format, bands and size. If * input_format is set, cast to a fixed input type. */ if( hclass->input_format != VIPS_FORMAT_NOTSET ) { for( i = 0; i < histogram->n; i++ ) if( vips_cast( decode[i], &format[i], hclass->input_format, NULL ) ) return( -1 ); } else { if( vips__formatalike_vec( decode, format, histogram->n ) ) return( -1 ); } if( vips__bandalike_vec( class->nickname, format, band, histogram->n, 1 ) || vips__hist_sizealike_vec( band, size, histogram->n ) ) return( -1 ); if( vips_image_pipeline_array( histogram->out, VIPS_DEMAND_STYLE_THINSTRIP, size ) ) return( -1 ); /* Need a copy of the inputs in memory. */ if( !(inbuf = VIPS_ARRAY( object, histogram->n + 1, VipsPel * )) ) return( -1 ); for( i = 0; i < histogram->n; i++ ) { if( !(memory[i] = vips_image_copy_memory( size[i] )) ) return( -1 ); inbuf[i] = VIPS_IMAGE_ADDR( memory[i], 0, 0 ); } inbuf[i] = NULL; /* Keep a copy of the memory images here for subclasses. */ histogram->ready = memory; histogram->out->Xsize = VIPS_IMAGE_N_PELS( histogram->ready[0] ); histogram->out->Ysize = 1; if( hclass->format_table ) histogram->out->BandFmt = hclass->format_table[histogram->ready[0]->BandFmt]; histogram->out->Type = VIPS_INTERPRETATION_HISTOGRAM; if( !(outbuf = vips_malloc( object, VIPS_IMAGE_SIZEOF_LINE( histogram->out ))) ) return( -1 ); hclass->process( histogram, outbuf, inbuf, histogram->ready[0]->Xsize ); if( vips_image_write_line( histogram->out, 0, outbuf ) ) return( -1 ); return( 0 ); }