| /* Manage pipelines of partial images. |
| * |
| * J.Cupitt, 17/4/93. |
| * 1/7/93 JC |
| * - adapted for partial v2 |
| * - ANSIfied |
| * 6/7/93 JC |
| * - im_setupout() conventions clarified - see autorewind in |
| * im_iocheck(). |
| * 20/7/93 JC |
| * - eval callbacks added |
| * 7/9/93 JC |
| * - demand hint mechanism added |
| * 25/10/93 |
| * - asynchronous output mechanisms removed, as no observable speed-up |
| * 9/5/94 |
| * - new thread stuff added, with a define to turn it off |
| * 15/8/94 |
| * - start & stop functions can now be NULL for no-op |
| * 7/10/94 JC |
| * - evalend callback system added |
| * 23/12/94 JC |
| * - IM_ARRAY uses added |
| * 22/2/95 JC |
| * - im_fill_copy() added |
| * - im_region_region() uses modified |
| * 24/4/95 JC & KM |
| * - im_fill_lines() bug removed |
| * 30/8/96 JC |
| * - revised and simplified ... some code shared with im_iterate() |
| * - new im_generate_region() added |
| * 2/3/98 JC |
| * - IM_ANY added |
| * 20/7/99 JC |
| * - tile geometry made into ints for easy tuning |
| * 30/7/99 RP JC |
| * - threads reorganised for POSIX |
| * 29/9/99 JC |
| * - threadgroup stuff added |
| * 15/4/04 |
| * - better how-many-pixels-calculated |
| * 27/11/06 |
| * - merge background write stuff |
| * 7/11/07 |
| * - new start/end eval callbacks |
| * 7/10/09 |
| * - gtkdoc comments |
| * 16/4/10 |
| * - remove threadgroup stuff |
| */ |
| |
| /* |
| |
| 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
| */ |
| |
| /* |
| |
| These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
| |
| */ |
| |
| /* |
| #define DEBUG_IO |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif /*HAVE_CONFIG_H*/ |
| #include <vips/intl.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif /*HAVE_UNISTD_H*/ |
| |
| #include <vips/vips.h> |
| #include <vips/internal.h> |
| #include <vips/thread.h> |
| #include <vips/debug.h> |
| |
| #ifdef WITH_DMALLOC |
| #include <dmalloc.h> |
| #endif /*WITH_DMALLOC*/ |
| |
| /** |
| * SECTION: generate |
| * @short_description: calculate pixels and pixel buffers |
| * @stability: Stable |
| * @see_also: <link linkend="libvips-image">image</link>, |
| * <link linkend="libvips-region">region</link> |
| * @include: vips/vips.h |
| * |
| * These functions let you generate regions of pixels in an image |
| * processing operation, and ask for regions of image to be calculated. |
| */ |
| |
| /** |
| * im_start_one: |
| * |
| * Start function for one image in. Input image is first user data. |
| * |
| * See also: im_generate(). |
| */ |
| void * |
| im_start_one( IMAGE *out, void *client, void *dummy ) |
| { |
| IMAGE *in = (IMAGE *) client; |
| |
| return( im_region_create( in ) ); |
| } |
| |
| /** |
| * im_stop_one: |
| * |
| * Stop function for one image in. Input image is first user data. |
| * |
| * See also: im_generate(). |
| */ |
| int |
| im_stop_one( void *seq, void *dummy1, void *dummy2 ) |
| { |
| REGION *reg = (REGION *) seq; |
| |
| im_region_free( reg ); |
| |
| return( 0 ); |
| } |
| |
| /** |
| * im_stop_many: |
| * |
| * Stop function for many images in. First client is a pointer to |
| * a %NULL-terminated array of input images. |
| * |
| * See also: im_generate(). |
| */ |
| int |
| im_stop_many( void *seq, void *dummy1, void *dummy2 ) |
| { |
| REGION **ar = (REGION **) seq; |
| |
| if( ar ) { |
| int i; |
| |
| for( i = 0; ar[i]; i++ ) |
| im_region_free( ar[i] ); |
| im_free( (char *) ar ); |
| } |
| |
| return( 0 ); |
| } |
| |
| /** |
| * im_start_many: |
| * |
| * Start function for many images in. First client is a pointer to |
| * a %NULL-terminated array of input images. |
| * |
| * See also: im_generate(), im_allocate_input_array() |
| */ |
| void * |
| im_start_many( IMAGE *out, void *client, void *dummy ) |
| { |
| IMAGE **in = (IMAGE **) client; |
| |
| int i, n; |
| REGION **ar; |
| |
| /* How many images? |
| */ |
| for( n = 0; in[n]; n++ ) |
| ; |
| |
| /* Alocate space for region array. |
| */ |
| if( !(ar = IM_ARRAY( NULL, n + 1, REGION * )) ) |
| return( NULL ); |
| |
| /* Create a set of regions. |
| */ |
| for( i = 0; i < n; i++ ) |
| if( !(ar[i] = im_region_create( in[i] )) ) { |
| im_stop_many( ar, NULL, NULL ); |
| return( NULL ); |
| } |
| ar[n] = NULL; |
| |
| return( ar ); |
| } |
| |
| /** |
| * im_allocate_input_array: |
| * @out: free array when this image closes |
| * @Varargs: %NULL-terminated list of input images |
| * |
| * Convenience function --- make a %NULL-terminated array of input images. |
| * Use with im_start_many(). |
| * |
| * See also: im_generate(), im_start_many(). |
| * |
| * Returns: %NULL-terminated array of images. Do not free the result. |
| */ |
| IMAGE ** |
| im_allocate_input_array( IMAGE *out, ... ) |
| { |
| va_list ap; |
| IMAGE **ar; |
| IMAGE *im; |
| int i, n; |
| |
| /* Count input images. |
| */ |
| va_start( ap, out ); |
| for( n = 0; (im = va_arg( ap, IMAGE * )); n++ ) |
| ; |
| va_end( ap ); |
| |
| /* Allocate array. |
| */ |
| if( !(ar = IM_ARRAY( out, n + 1, IMAGE * )) ) |
| return( NULL ); |
| |
| /* Fill array. |
| */ |
| va_start( ap, out ); |
| for( i = 0; i < n; i++ ) |
| ar[i] = va_arg( ap, IMAGE * ); |
| va_end( ap ); |
| ar[n] = NULL; |
| |
| return( ar ); |
| } |
| |
| /** |
| * im_start_fn: |
| * @out: image being calculated |
| * @a: user data |
| * @b: user data |
| * |
| * Start a new processing sequence for this generate function. This allocates |
| * per-thread state, such as an input region. |
| * |
| * See also: im_start_one(), im_start_many(). |
| * |
| * Returns: a new sequence value |
| */ |
| |
| /** |
| * im_generate_fn: |
| * @out: #REGION to fill |
| * @seq: sequence value |
| * @a: user data |
| * @b: user data |
| * |
| * Fill @out->valid with pixels. @seq contains per-thread state, such as the |
| * input regions. |
| * |
| * See also: im_generate(), im_stop_many(). |
| * |
| * Returns: 0 on success, -1 on error. |
| */ |
| |
| /** |
| * im_stop_fn: |
| * @seq: sequence value |
| * @a: user data |
| * @b: user data |
| * |
| * Stop a processing sequence. This frees |
| * per-thread state, such as an input region. |
| * |
| * See also: im_stop_one(), im_stop_many(). |
| * |
| * Returns: 0 on success, -1 on error. |
| */ |
| |
| /* A write function for VIPS images. Just write() the pixel data. |
| */ |
| static int |
| write_vips( REGION *region, Rect *area, void *a, void *b ) |
| { |
| size_t nwritten, count; |
| void *buf; |
| |
| count = region->bpl * area->height; |
| buf = IM_REGION_ADDR( region, 0, area->top ); |
| do { |
| nwritten = write( region->im->fd, buf, count ); |
| if( nwritten == (size_t) -1 ) |
| return( errno ); |
| |
| buf = (void *) ((char *) buf + nwritten); |
| count -= nwritten; |
| } while( count > 0 ); |
| |
| return( 0 ); |
| } |
| |
| /** |
| * im_generate: |
| * @im: generate this image |
| * @start: start sequences with this function |
| * @generate: generate pixels with this function |
| * @stop: stop sequences with this function |
| * @a: user data |
| * @b: user data |
| * |
| * Generates an image. The action depends on the image type. |
| * |
| * For images opened with "p", im_generate() just attaches the |
| * start/generate/stop callbacks and returns. |
| * |
| * For "t" images, memory is allocated for the whole image and it is entirely |
| * generated using vips_sink(). |
| * |
| * For "w" images, memory for a few scanlines is allocated and |
| * vips_sink_disc() used to generate the image in small chunks. As each |
| * chunk is generated, it is written to disc. |
| * |
| * See also: vips_sink(), im_open(), im_prepare(), im_wrapone(). |
| * |
| * Returns: 0 on success, or -1 on error. |
| */ |
| int |
| im_generate( IMAGE *im, |
| im_start_fn start, im_generate_fn generate, im_stop_fn stop, |
| void *a, void *b ) |
| { |
| int res; |
| |
| g_assert( !im_image_sanity( im ) ); |
| |
| if( !im->hint_set ) { |
| im_error( "im_generate", |
| "%s", _( "im_demand_hint() not set" ) ); |
| return( -1 ); |
| } |
| |
| if( im->Xsize <= 0 || im->Ysize <= 0 || im->Bands <= 0 ) { |
| im_error( "im_generate", |
| "%s", _( "bad dimensions" ) ); |
| return( -1 ); |
| } |
| |
| /* We don't use this, but make sure it's set in case any old binaries |
| * are expectiing it. |
| */ |
| im->Bbits = im_bits_of_fmt( im->BandFmt ); |
| |
| /* Look at output type to decide our action. |
| */ |
| switch( im->dtype ) { |
| case IM_PARTIAL: |
| /* Output to partial image. Just attach functions and return. |
| */ |
| if( im->generate || im->start || im->stop ) { |
| im_error( "im_generate", |
| "%s", _( "func already attached" ) ); |
| return( -1 ); |
| } |
| |
| im->start = start; |
| im->generate = generate; |
| im->stop = stop; |
| im->client1 = a; |
| im->client2 = b; |
| |
| #ifdef DEBUG_IO |
| printf( "im_generate: attaching partial callbacks\n" ); |
| #endif /*DEBUG_IO*/ |
| |
| break; |
| |
| case IM_SETBUF: |
| case IM_SETBUF_FOREIGN: |
| case IM_MMAPINRW: |
| case IM_OPENOUT: |
| /* Eval now .. sanity check. |
| */ |
| if( im->generate || im->start || im->stop ) { |
| im_error( "im_generate", |
| "%s", _( "func already attached" ) ); |
| return( -1 ); |
| } |
| |
| /* Get output ready. |
| */ |
| if( im_setupout( im ) ) |
| return( -1 ); |
| |
| /* Attach callbacks. |
| */ |
| im->start = start; |
| im->generate = generate; |
| im->stop = stop; |
| im->client1 = a; |
| im->client2 = b; |
| |
| if( im->dtype == IM_OPENOUT ) |
| res = vips_sink_disc( im, |
| (VipsRegionWrite) write_vips, NULL ); |
| else |
| res = vips_sink_memory( im ); |
| |
| /* Error? |
| */ |
| if( res ) |
| return( -1 ); |
| |
| break; |
| |
| default: |
| /* Not a known output style. |
| */ |
| im_error( "im_generate", _( "unable to output to a %s image" ), |
| im_dtype2char( im->dtype ) ); |
| return( -1 ); |
| } |
| |
| /* Successful write: trigger "written". |
| */ |
| if( im__trigger_callbacks( im->writtenfns ) ) |
| return( -1 ); |
| |
| return( 0 ); |
| } |
| |