/* 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 ); }
/* Make a new buffer. */ VipsBuffer * vips_buffer_new( VipsImage *im, VipsRect *area ) { VipsBuffer *buffer; buffer = g_new( VipsBuffer, 1 ); buffer->ref_count = 1; buffer->im = im; buffer->area = *area; buffer->done = FALSE; buffer->cache = NULL; buffer->bsize = (size_t) VIPS_IMAGE_SIZEOF_PEL( im ) * area->width * area->height; if( !(buffer->buf = vips_tracked_malloc( buffer->bsize )) ) { vips_buffer_unref( buffer ); return( NULL ); } #ifdef DEBUG printf( "** vips_buffer_new: left = %d, top = %d, " "width = %d, height = %d (%p)\n", buffer->area.left, buffer->area.top, buffer->area.width, buffer->area.height, buffer ); #endif /*DEBUG*/ #ifdef DEBUG g_mutex_lock( vips__global_lock ); vips__buffers_all = g_slist_prepend( vips__buffers_all, buffer ); printf( "%d buffers in vips\n", g_slist_length( vips__buffers_all ) ); g_mutex_unlock( vips__global_lock ); #endif /*DEBUG*/ return( buffer ); }
/* Read a ppm/pgm file using mmap(). */ static int read_mmap( FILE *fp, const char *filename, int msb_first, VipsImage *out ) { const guint64 header_offset = ftell( fp ); VipsImage *x = vips_image_new(); VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( x ), 3 ); if( vips_rawload( filename, &t[0], out->Xsize, out->Ysize, VIPS_IMAGE_SIZEOF_PEL( out ), "offset", header_offset, NULL ) || vips_copy( t[0], &t[1], "bands", out->Bands, "format", out->BandFmt, "coding", out->Coding, NULL ) || vips_copy( t[1], &t[2], "swap", !vips_amiMSBfirst(), NULL ) || vips_image_write( t[2], out ) ) { g_object_unref( x ); return( -1 ); } g_object_unref( x ); return( 0 ); }
static int vips_rot180_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsImage *in = (VipsImage *) a; /* Output area. */ VipsRect *r = &or->valid; int le = r->left; int ri = IM_RECT_RIGHT(r); int to = r->top; int bo = IM_RECT_BOTTOM(r); int x, y; /* Pixel geometry. */ int ps; /* Find the area of the input image we need. */ Rect need; need.left = in->Xsize - ri; need.top = in->Ysize - bo; need.width = r->width; need.height = r->height; if( vips_region_prepare( ir, &need ) ) return( -1 ); /* Find PEL size and line skip for ir. */ ps = VIPS_IMAGE_SIZEOF_PEL( in ); /* Rotate the bit we now have. */ for( y = to; y < bo; y++ ) { /* Start of this output line. */ VipsPel *q = VIPS_REGION_ADDR( or, le, y ); /* Corresponding position in ir. */ VipsPel *p = VIPS_REGION_ADDR( ir, need.left + need.width - 1, need.top + need.height - (y - to) - 1 ); /* Blap across! */ for( x = le; x < ri; x++ ) { memcpy( q, p, ps ); q += ps; p -= ps; } } return( 0 ); }
static int buffer_move( VipsBuffer *buffer, VipsRect *area ) { VipsImage *im = buffer->im; size_t new_bsize; g_assert( buffer->ref_count == 1 ); vips_buffer_undone( buffer ); g_assert( !buffer->done ); buffer->area = *area; new_bsize = (size_t) VIPS_IMAGE_SIZEOF_PEL( im ) * area->width * area->height; if( buffer->bsize < new_bsize || !buffer->buf ) { buffer->bsize = new_bsize; VIPS_FREEF( vips_tracked_free, buffer->buf ); if( !(buffer->buf = vips_tracked_malloc( buffer->bsize )) ) return( -1 ); } return( 0 ); }
static int vips_fits_write( VipsRegion *region, VipsRect *area, void *a ) { VipsFits *fits = (VipsFits *) a; VipsImage *image = fits->image; int es = VIPS_IMAGE_SIZEOF_ELEMENT( image ); int ps = VIPS_IMAGE_SIZEOF_PEL( image ); int status; int y, b, x, k; status = 0; VIPS_DEBUG_MSG( "vips_fits_write: " "writing left = %d, top = %d, width = %d, height = %d\n", area->left, area->top, area->width, area->height ); /* We need to write a band at a time. We can't bandsplit in vips, * since vips_sink_disc() can't loop over many images at once, sadly. */ for( y = 0; y < area->height; y++ ) { VipsPel *p = VIPS_REGION_ADDR( region, area->left, area->top + y ); for( b = 0; b < image->Bands; b++ ) { VipsPel *p1, *q; long fpixel[3]; p1 = p + b * es; q = fits->buffer; for( x = 0; x < area->width; x++ ) { for( k = 0; k < es; k++ ) q[k] = p1[k]; q += es; p1 += ps; } fpixel[0] = area->left + 1; fpixel[1] = area->top + y + 1; fpixel[2] = b + 1; /* No need to lock, write functions are single-threaded. */ if( fits_write_pix( fits->fptr, fits->datatype, fpixel, area->width, fits->buffer, &status ) ) { vips_fits_error( status ); return( -1 ); } } } return( 0 ); }
/* Paint the part of the region containing only whole pels. */ static void vips_zoom_paint_whole( VipsRegion *or, VipsRegion *ir, VipsZoom *zoom, const int left, const int right, const int top, const int bottom ) { const int ps = VIPS_IMAGE_SIZEOF_PEL( ir->im ); const int ls = VIPS_REGION_LSKIP( or ); const int rs = ps * (right - left); /* Transform to ir coordinates. */ const int ileft = left / zoom->xfac; const int iright = right / zoom->xfac; const int itop = top / zoom->yfac; const int ibottom = bottom / zoom->yfac; int x, y, z, i; /* We know this! */ g_assert( right > left && bottom > top && right % zoom->xfac == 0 && left % zoom->xfac == 0 && top % zoom->yfac == 0 && bottom % zoom->yfac == 0 ); /* Loop over input, as we know we are all whole. */ for( y = itop; y < ibottom; y++ ) { VipsPel *p = VIPS_REGION_ADDR( ir, ileft, y ); VipsPel *q = VIPS_REGION_ADDR( or, left, y * zoom->yfac ); VipsPel *r; /* Expand the first line of pels. */ r = q; for( x = ileft; x < iright; x++ ) { /* Copy each pel xfac times. */ for( z = 0; z < zoom->xfac; z++ ) { for( i = 0; i < ps; i++ ) r[i] = p[i]; r += ps; } p += ps; } /* Copy the expanded line yfac-1 times. */ r = q + ls; for( z = 1; z < zoom->yfac; z++ ) { memcpy( r, q, rs ); r += ls; } } }
static int vips_flip_horizontal_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsRect *r = &or->valid; VipsRect in; VipsPel *p, *q; int x, y, z; int le = r->left; int ri = VIPS_RECT_RIGHT(r); int to = r->top; int bo = VIPS_RECT_BOTTOM(r); int ps = VIPS_IMAGE_SIZEOF_PEL( ir->im ); /* sizeof pel */ int hgt = ir->im->Xsize - r->width; int lastx; /* Transform to input coordinates. */ in = *r; in.left = hgt - r->left; /* Find x of final pixel in input area. */ lastx = VIPS_RECT_RIGHT( &in ) - 1; /* Ask for input we need. */ if( vips_region_prepare( ir, &in ) ) return( -1 ); /* Loop, copying and reversing lines. */ for( y = to; y < bo; y++ ) { p = VIPS_REGION_ADDR( ir, lastx, y ); q = VIPS_REGION_ADDR( or, le, y ); for( x = le; x < ri; x++ ) { /* Copy the pel. */ for( z = 0; z < ps; z++ ) q[z] = p[z]; /* Skip forwards in out, back in in. */ q += ps; p -= ps; } } return( 0 ); }
/* Generate an area of @or. @ir is large enough. */ static void vips_shrink2_gen2( VipsShrink2 *shrink, VipsShrink2Sequence *seq, VipsRegion *or, VipsRegion *ir, int left, int top, int width, int height ) { VipsResample *resample = VIPS_RESAMPLE( shrink ); const int bands = resample->in->Bands; const int sizeof_pixel = VIPS_IMAGE_SIZEOF_PEL( resample->in ); const int ls = VIPS_REGION_LSKIP( ir ) / VIPS_IMAGE_SIZEOF_ELEMENT( resample->in ); int x, y, i; int x1, y1, b; for( y = 0; y < height; y++ ) { VipsPel *out = VIPS_REGION_ADDR( or, left, top + y ); for( x = 0; x < width; x++ ) { int ix = (left + x) * shrink->xshrink; int iy = (top + y) * shrink->yshrink; VipsPel *in = VIPS_REGION_ADDR( ir, ix, iy ); switch( resample->in->BandFmt ) { case VIPS_FORMAT_UCHAR: ISHRINK( unsigned char ); break; case VIPS_FORMAT_CHAR: ISHRINK( char ); break; case VIPS_FORMAT_USHORT: ISHRINK( unsigned short ); break; case VIPS_FORMAT_SHORT: ISHRINK( short ); break; case VIPS_FORMAT_UINT: ISHRINK( unsigned int ); break; case VIPS_FORMAT_INT: ISHRINK( int ); break; case VIPS_FORMAT_FLOAT: FSHRINK( float ); break; case VIPS_FORMAT_DOUBLE: FSHRINK( double ); break; default: g_assert( 0 ); } out += sizeof_pixel; } } }
/* Copy a single pixel sideways into a line of pixels. */ static void vips_embed_copy_pixel( VipsEmbed *embed, VipsPel *q, VipsPel *p, int n ) { const int bs = VIPS_IMAGE_SIZEOF_PEL( embed->in ); int x, b; for( x = 0; x < n; x++ ) for( b = 0; b < bs; b++ ) *q++ = p[b]; }
/* Swap 4- of bytes. */ static void vips_byteswap_swap4( VipsPel *in, VipsPel *out, int width, VipsImage *im ) { guint32 *p = (guint32 *) in; guint32 *q = (guint32 *) out; int sz = (VIPS_IMAGE_SIZEOF_PEL( im ) * width) / 4; int x; for( x = 0; x < sz; x++ ) q[x] = GUINT32_SWAP_LE_BE( p[x] ); }
/* Swap 8- of bytes. */ static void vips_copy_swap8( VipsPel *in, VipsPel *out, int width, VipsImage *im ) { guint64 *p = (guint64 *) in; guint64 *q = (guint64 *) out; int sz = (VIPS_IMAGE_SIZEOF_PEL( im ) * width) / 8; int x; for( x = 0; x < sz; x++ ) q[x] = GUINT64_SWAP_LE_BE( p[x] ); }
static void vips_bandjoin_buffer( VipsBandary *bandary, VipsPel *q, VipsPel **p, int width ) { 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; } }
/* Paint r of region or. It's a border area, lying entirely within * embed->border[i]. p points to the top-left source pixel to fill with. * plsk is the line stride. */ static void vips_embed_paint_edge( VipsEmbed *embed, VipsRegion *or, int i, VipsRect *r, VipsPel *p, int plsk ) { const int bs = VIPS_IMAGE_SIZEOF_PEL( embed->in ); VipsRect todo; VipsPel *q; int y; VIPS_GATE_START( "vips_embed_paint_edge: work" ); /* Pixels left to paint. */ todo = *r; /* Corner pieces ... copy the single pixel to paint the top line of * todo, then use the line copier below to paint the rest of it. */ if( i > 3 ) { q = VIPS_REGION_ADDR( or, todo.left, todo.top ); vips_embed_copy_pixel( embed, q, p, todo.width ); p = q; todo.top += 1; todo.height -= 1; } if( i == 1 || i == 3 ) { /* Vertical line of pixels to copy. */ for( y = 0; y < todo.height; y++ ) { q = VIPS_REGION_ADDR( or, todo.left, todo.top + y ); vips_embed_copy_pixel( embed, q, p, todo.width ); p += plsk; } } else { /* Horizontal line of pixels to copy. */ for( y = 0; y < todo.height; y++ ) { q = VIPS_REGION_ADDR( or, todo.left, todo.top + y ); memcpy( q, p, bs * todo.width ); } } VIPS_GATE_STOP( "vips_embed_paint_edge: work" ); }
/* Fetch one pixel at a time ... good for very large shrinks. */ static int vips_subsample_point_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsSubsample *subsample = (VipsSubsample *) b; VipsImage *in = (VipsImage *) a; VipsRect *r = &or->valid; int le = r->left; int ri = VIPS_RECT_RIGHT( r ); int to = r->top; int bo = VIPS_RECT_BOTTOM(r); int ps = VIPS_IMAGE_SIZEOF_PEL( in ); VipsRect s; int x, y; int k; /* Loop down the region. */ for( y = to; y < bo; y++ ) { VipsPel *q = VIPS_REGION_ADDR( or, le, y ); VipsPel *p; /* Loop across the region, in owidth sized pieces. */ for( x = le; x < ri; x++ ) { /* Ask for input. */ s.left = x * subsample->xfac; s.top = y * subsample->yfac; s.width = 1; s.height = 1; if( vips_region_prepare( ir, &s ) ) return( -1 ); /* Append new pels to output. */ p = VIPS_REGION_ADDR( ir, s.left, s.top ); for( k = 0; k < ps; k++ ) q[k] = p[k]; q += ps; } } return( 0 ); }
/* Copy rect from @from to @to. */ static void copy_region( VipsRegion *from, VipsRegion *to, VipsRect *area ) { int y; /* Area should be inside both from and to. */ g_assert( vips_rect_includesrect( &from->valid, area ) ); g_assert( vips_rect_includesrect( &to->valid, area ) ); /* Loop down common area, copying. */ for( y = area->top; y < VIPS_RECT_BOTTOM( area ); y++ ) { VipsPel *p = VIPS_REGION_ADDR( from, area->left, y ); VipsPel *q = VIPS_REGION_ADDR( to, area->left, y ); memcpy( q, p, VIPS_IMAGE_SIZEOF_PEL( from->im ) * area->width ); } }
/* Pack the pixels in @area from @in into a TIFF tile buffer. */ static void pack2tiff( Write *write, Layer *layer, VipsRegion *in, VipsRect *area, VipsPel *q ) { int y; /* JPEG compression can read outside the pixel area for edge tiles. It * always compresses 8x8 blocks, so if the image width or height is * not a multiple of 8, it can look beyond the pixels we will write. * * Black out the tile first to make sure these edge pixels are always * zero. */ if( write->compression == COMPRESSION_JPEG && (area->width < write->tilew || area->height < write->tileh) ) memset( q, 0, TIFFTileSize( layer->tif ) ); for( y = area->top; y < VIPS_RECT_BOTTOM( area ); y++ ) { VipsPel *p = (VipsPel *) VIPS_REGION_ADDR( in, area->left, y ); if( write->im->Coding == VIPS_CODING_LABQ ) LabQ2LabC( q, p, area->width ); else if( write->onebit ) eightbit2onebit( write, q, p, area->width ); else if( (in->im->Bands == 1 || in->im->Bands == 2) && write->miniswhite ) invert_band0( write, q, p, area->width ); else if( write->im->BandFmt == VIPS_FORMAT_SHORT && write->im->Type == VIPS_INTERPRETATION_LABS ) LabS2Lab16( q, p, area->width ); else memcpy( q, p, area->width * VIPS_IMAGE_SIZEOF_PEL( write->im ) ); q += write->tls; } }
static int write_ppm_line_ascii( VipsImage *in, FILE *fp, VipsPel *p ) { const int sk = VIPS_IMAGE_SIZEOF_PEL( in ); int x, k; for( x = 0; x < in->Xsize; x++ ) { for( k = 0; k < in->Bands; k++ ) { switch( in->BandFmt ) { case VIPS_FORMAT_UCHAR: fprintf( fp, "%d ", p[k] ); break; case VIPS_FORMAT_USHORT: fprintf( fp, "%d ", ((unsigned short *) p)[k] ); break; case VIPS_FORMAT_UINT: fprintf( fp, "%d ", ((unsigned int *) p)[k] ); break; default: g_assert( 0 ); } } fprintf( fp, " " ); p += sk; } if( !fprintf( fp, "\n" ) ) { vips_error( "vips2ppm", "%s", _( "write error ... disc full?" ) ); return( -1 ); } return( 0 ); }
static int vips_bandunfold_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsBandunfold *bandunfold = (VipsBandunfold *) b; VipsRegion *ir = (VipsRegion *) seq; VipsImage *in = ir->im; VipsImage *out = or->im; VipsRect *r = &or->valid; int esize = VIPS_IMAGE_SIZEOF_ELEMENT( in ); int psize = VIPS_IMAGE_SIZEOF_PEL( out ); VipsRect need; int y; need.left = r->left / bandunfold->factor; need.top = r->top; need.width = (1 + r->width) / bandunfold->factor; need.height = r->height; if( vips_region_prepare( ir, &need ) ) return( -1 ); for( y = 0; y < r->height; y++ ) { VipsPel *p = VIPS_REGION_ADDR( ir, r->left / bandunfold->factor, r->top + y ) + (r->left % bandunfold->factor) * esize; VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top + y ); /* We can't use vips_region_region() since we change pixel * coordinates. */ memcpy( q, p, r->width * psize ); } return( 0 ); }
/* Subsample a VipsRegion. We fetch in VIPS_MAX_WIDTH pixel-wide strips, * left-to-right across the input. */ static int vips_subsample_line_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsSubsample *subsample = (VipsSubsample *) b; VipsImage *in = (VipsImage *) a; VipsRect *r = &or->valid; int le = r->left; int ri = VIPS_RECT_RIGHT( r ); int to = r->top; int bo = VIPS_RECT_BOTTOM( r ); int ps = VIPS_IMAGE_SIZEOF_PEL( in ); int owidth = VIPS_MAX_WIDTH / subsample->xfac; VipsRect s; int x, y; int z, k; /* Loop down the region. */ for( y = to; y < bo; y++ ) { VipsPel *q = VIPS_REGION_ADDR( or, le, y ); VipsPel *p; /* Loop across the region, in owidth sized pieces. */ for( x = le; x < ri; x += owidth ) { /* How many pixels do we make this time? */ int ow = VIPS_MIN( owidth, ri - x ); /* Ask for this many from input ... can save a * little here! */ int iw = ow * subsample->xfac - (subsample->xfac - 1); /* Ask for input. */ s.left = x * subsample->xfac; s.top = y * subsample->yfac; s.width = iw; s.height = 1; if( vips_region_prepare( ir, &s ) ) return( -1 ); /* Append new pels to output. */ p = VIPS_REGION_ADDR( ir, s.left, s.top ); for( z = 0; z < ow; z++ ) { for( k = 0; k < ps; k++ ) q[k] = p[k]; q += ps; p += ps * subsample->xfac; } } } return( 0 ); }
static int vips_affine_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; const VipsAffine *affine = (VipsAffine *) b; const VipsImage *in = (VipsImage *) a; const int window_size = vips_interpolate_get_window_size( affine->interpolate ); const int window_offset = vips_interpolate_get_window_offset( affine->interpolate ); const VipsInterpolateMethod interpolate = vips_interpolate_get_method( affine->interpolate ); /* Area we generate in the output image. */ const VipsRect *r = &or->valid; const int le = r->left; const int ri = VIPS_RECT_RIGHT( r ); const int to = r->top; const int bo = VIPS_RECT_BOTTOM( r ); const VipsRect *iarea = &affine->trn.iarea; const VipsRect *oarea = &affine->trn.oarea; int ps = VIPS_IMAGE_SIZEOF_PEL( in ); int x, y, z; VipsRect image, want, need, clipped; #ifdef DEBUG printf( "affine: generating left=%d, top=%d, width=%d, height=%d\n", r->left, r->top, r->width, r->height ); #endif /*DEBUG*/ /* We are generating this chunk of the transformed image. */ want = *r; want.left += oarea->left; want.top += oarea->top; /* Find the area of the input image we need. */ vips__transform_invert_rect( &affine->trn, &want, &need ); /* That does round-to-nearest, because it has to stop rounding errors * growing images unexpectedly. We need round-down, so we must * add half a pixel along the left and top. But we are int :( so add 1 * pixel. * * Add an extra line along the right and bottom as well, for rounding. */ vips_rect_marginadjust( &need, 1 ); /* Now go to space (2) above. */ need.left += iarea->left; need.top += iarea->top; /* Add a border for interpolation. */ need.width += window_size - 1; need.height += window_size - 1; need.left -= window_offset; need.top -= window_offset; /* Clip against the size of (2). */ image.left = 0; image.top = 0; image.width = in->Xsize; image.height = in->Ysize; vips_rect_intersectrect( &need, &image, &clipped ); /* Outside input image? All black. */ if( vips_rect_isempty( &clipped ) ) { vips_region_black( or ); return( 0 ); } /* We do need some pixels from the input image to make our output - * ask for them. */ if( vips_region_prepare( ir, &clipped ) ) return( -1 ); #ifdef DEBUG printf( "affine: preparing left=%d, top=%d, width=%d, height=%d\n", clipped.left, clipped.top, clipped.width, clipped.height ); #endif /*DEBUG*/ /* Resample! x/y loop over pixels in the output image (5). */ for( y = to; y < bo; y++ ) { /* Input clipping rectangle. */ const int ile = iarea->left; const int ito = iarea->top; const int iri = iarea->left + iarea->width; const int ibo = iarea->top + iarea->height; /* Derivative of matrix. */ const double ddx = affine->trn.ia; const double ddy = affine->trn.ic; /* Continuous cods in transformed space. */ const double ox = le + oarea->left - affine->trn.odx; const double oy = y + oarea->top - affine->trn.ody; /* Continuous cods in input space. */ double ix, iy; VipsPel *q; /* To (3). */ ix = affine->trn.ia * ox + affine->trn.ib * oy; iy = affine->trn.ic * ox + affine->trn.id * oy; /* Now move to (2). */ ix += iarea->left; iy += iarea->top; /* And the input offset. */ ix -= affine->trn.idx; iy -= affine->trn.idy; q = VIPS_REGION_ADDR( or, le, y ); for( x = le; x < ri; x++ ) { int fx, fy; fx = FAST_PSEUDO_FLOOR( ix ); fy = FAST_PSEUDO_FLOOR( iy ); /* Clipping! */ if( fx < ile || fx >= iri || fy < ito || fy >= ibo ) { for( z = 0; z < ps; z++ ) q[z] = 0; } else { interpolate( affine->interpolate, q, ir, ix, iy ); } ix += ddx; iy += ddy; q += ps; } } return( 0 ); }
static int vips_affine_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; const VipsAffine *affine = (VipsAffine *) b; const VipsImage *in = (VipsImage *) a; const int window_size = vips_interpolate_get_window_size( affine->interpolate ); const int window_offset = vips_interpolate_get_window_offset( affine->interpolate ); const VipsInterpolateMethod interpolate = vips_interpolate_get_method( affine->interpolate ); /* Area we generate in the output image. */ const VipsRect *r = &or->valid; const int le = r->left; const int ri = VIPS_RECT_RIGHT( r ); const int to = r->top; const int bo = VIPS_RECT_BOTTOM( r ); const VipsRect *iarea = &affine->trn.iarea; const VipsRect *oarea = &affine->trn.oarea; int ps = VIPS_IMAGE_SIZEOF_PEL( in ); int x, y, z; VipsRect image, want, need, clipped; #ifdef DEBUG_VERBOSE printf( "vips_affine_gen: " "generating left=%d, top=%d, width=%d, height=%d\n", r->left, r->top, r->width, r->height ); #endif /*DEBUG_VERBOSE*/ /* We are generating this chunk of the transformed image. This takes * us to space 4. */ want = *r; want.left += oarea->left; want.top += oarea->top; /* Find the area of the input image we need. This takes us to space 3. */ vips__transform_invert_rect( &affine->trn, &want, &need ); /* That does round-to-nearest, because it has to stop rounding errors * growing images unexpectedly. We need round-down, so we must * add half a pixel along the left and top. But we are int :( so add 1 * pixel. * * Add an extra line along the right and bottom as well, for rounding. */ vips_rect_marginadjust( &need, 1 ); /* We need to fetch a larger area for the interpolator. */ need.left -= window_offset; need.top -= window_offset; need.width += window_size - 1; need.height += window_size - 1; /* Now go to space 2, the expanded input image. This is the one we * read pixels from. */ need.left += window_offset; need.top += window_offset; /* Clip against the size of (2). */ image.left = 0; image.top = 0; image.width = in->Xsize; image.height = in->Ysize; vips_rect_intersectrect( &need, &image, &clipped ); #ifdef DEBUG_VERBOSE printf( "vips_affine_gen: " "preparing left=%d, top=%d, width=%d, height=%d\n", clipped.left, clipped.top, clipped.width, clipped.height ); #endif /*DEBUG_VERBOSE*/ if( vips_rect_isempty( &clipped ) ) { vips_region_black( or ); return( 0 ); } if( vips_region_prepare( ir, &clipped ) ) return( -1 ); VIPS_GATE_START( "vips_affine_gen: work" ); /* Resample! x/y loop over pixels in the output image (5). */ for( y = to; y < bo; y++ ) { /* Input clipping rectangle. We offset this so we can clip in * space 2. */ const int ile = iarea->left + window_offset; const int ito = iarea->top + window_offset; const int iri = ile + iarea->width; const int ibo = ito + iarea->height; /* Derivative of matrix. */ const double ddx = affine->trn.ia; const double ddy = affine->trn.ic; /* Continuous cods in transformed space. */ const double ox = le + oarea->left - affine->trn.odx; const double oy = y + oarea->top - affine->trn.ody; /* Continuous cods in input space. */ double ix, iy; VipsPel *q; /* To (3). */ ix = affine->trn.ia * ox + affine->trn.ib * oy; iy = affine->trn.ic * ox + affine->trn.id * oy; /* And the input offset in (3). */ ix -= affine->trn.idx; iy -= affine->trn.idy; /* Finally to 2. */ ix += window_offset; iy += window_offset; q = VIPS_REGION_ADDR( or, le, y ); for( x = le; x < ri; x++ ) { int fx, fy; fx = FAST_PSEUDO_FLOOR( ix ); fy = FAST_PSEUDO_FLOOR( iy ); /* Clip against iarea. */ if( fx >= ile && fx < iri && fy >= ito && fy < ibo ) { /* Verify that we can read the whole stencil. * With DEBUG on this will range-check. */ g_assert( VIPS_REGION_ADDR( ir, (int) ix - window_offset, (int) iy - window_offset ) ); g_assert( VIPS_REGION_ADDR( ir, (int) ix - window_offset + window_size - 1, (int) iy - window_offset + window_size - 1 ) ); interpolate( affine->interpolate, q, ir, ix, iy ); } else { for( z = 0; z < ps; z++ ) q[z] = 0; } ix += ddx; iy += ddy; q += ps; } } VIPS_GATE_STOP( "vips_affine_gen: work" ); return( 0 ); }
/* 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_ifthenelse_gen( VipsRegion *or, void *seq, void *client1, void *client2, gboolean *stop ) { VipsRegion **ir = (VipsRegion **) seq; VipsIfthenelse *ifthenelse = (VipsIfthenelse *) client2; VipsRect *r = &or->valid; int le = r->left; int to = r->top; int bo = VIPS_RECT_BOTTOM( r ); VipsImage *c = ir[2]->im; VipsImage *a = ir[0]->im; int size, width; int i, x, y, z; int all0, alln0; if( c->Bands == 1 ) { /* Copying PEL-sized units with a one-band conditional. */ size = VIPS_IMAGE_SIZEOF_PEL( a ); width = r->width; } else { /* Copying ELEMENT sized-units with an n-band conditional. */ size = VIPS_IMAGE_SIZEOF_ELEMENT( a ); width = r->width * a->Bands; } if( vips_region_prepare( ir[2], r ) ) return( -1 ); /* Is the conditional all zero or all non-zero? We can avoid asking * for one of the inputs to be calculated. */ all0 = *((PEL *) VIPS_REGION_ADDR( ir[2], le, to )) == 0; alln0 = *((PEL *) VIPS_REGION_ADDR( ir[2], le, to )) != 0; for( y = to; y < bo; y++ ) { PEL *p = (PEL *) VIPS_REGION_ADDR( ir[2], le, y ); for( x = 0; x < width; x++ ) { all0 &= p[x] == 0; alln0 &= p[x] != 0; } if( !all0 && !alln0 ) break; } if( alln0 ) { /* All non-zero. Point or at the then image. */ if( vips_region_prepare( ir[0], r ) || vips_region_region( or, ir[0], r, r->left, r->top ) ) return( -1 ); } else if( all0 ) { /* All zero. Point or at the else image. */ if( vips_region_prepare( ir[1], r ) || vips_region_region( or, ir[1], r, r->left, r->top ) ) return( -1 ); } else { /* Mix of set and clear ... ask for both then and else parts * and interleave. */ if( vips_region_prepare( ir[0], r ) || vips_region_prepare( ir[1], r ) ) return( -1 ); for( y = to; y < bo; y++ ) { PEL *ap = (PEL *) VIPS_REGION_ADDR( ir[0], le, y ); PEL *bp = (PEL *) VIPS_REGION_ADDR( ir[1], le, y ); PEL *cp = (PEL *) VIPS_REGION_ADDR( ir[2], le, y ); PEL *q = (PEL *) VIPS_REGION_ADDR( or, le, y ); if( ifthenelse->blend ) { if( c->Bands == 1 ) vips_blend1_buffer( q, cp, ap, bp, r->width, a ); else vips_blendn_buffer( q, cp, ap, bp, r->width, a ); } else { for( x = 0, i = 0; i < width; i++, x += size ) if( cp[i] ) for( z = x; z < x + size; z++ ) q[z] = ap[z]; else for( z = x; z < x + size; z++ ) q[z] = bp[z]; } } } return( 0 ); }
/* This can work inplace, ie. in == out is allowed. */ static void vips_rot45_rot45( VipsImage *out, VipsImage *in ) { size_t ps = VIPS_IMAGE_SIZEOF_PEL( in ); VipsPel *temp = VIPS_ARRAY( in, ps, VipsPel ); int size = in->Xsize; int size_2 = size / 2; int x, y; g_assert( in->Xsize == in->Ysize ); g_assert( out->Xsize == out->Ysize ); g_assert( in->Xsize == out->Xsize ); g_assert( in->Xsize % 2 == 1 ); /* Split the square into 8 triangles. Loop over the top-left one, * reflect to index the others. * * 1 1 2 2 3 * 8 1 2 3 3 * 8 8 x 4 4 * 7 7 6 5 4 * 7 6 6 5 5 * * do the centre separately. */ for( y = 0; y < size_2; y++ ) for( x = y; x < size_2; x++ ) { /* Save 1, it goes into 2 at the end. */ POINT_TO_TEMP( temp, x, y ); /* Fill 1 from 8. */ ASSIGN( x, y, y, size_2 - (x - y) ); /* 8 from 7. */ ASSIGN( y, size_2 - (x - y), y, (size - 1) - x ); /* 7 from 6. */ ASSIGN( y, (size - 1) - x, size_2 - (x - y), (size - 1) - y ); /* 6 from 5. */ ASSIGN( size_2 - (x - y), (size - 1) - y, (size - 1) - x, (size - 1) - y ); /* 5 from 4. */ ASSIGN( (size - 1) - x, (size - 1) - y, (size - 1) - y, (x - y) + size_2 ); /* 4 from 3. */ ASSIGN( (size - 1) - y, (x - y) + size_2, (size - 1) - y, x ); /* 3 from 2. */ ASSIGN( (size - 1) - y, x, (x - y) + size_2, y ); /* 2 from saved 1. */ TEMP_TO_POINT( (x - y) + size_2, y, temp ); } /* Centre. */ ASSIGN( size_2, size_2, size_2, size_2 ); }
static int vips_quadratic_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) vseq; VipsQuadratic *quadratic = (VipsQuadratic *) b; VipsResample *resample = VIPS_RESAMPLE( quadratic ); VipsInterpolateMethod interpolate_fn = vips_interpolate_get_method( quadratic->interpolate ); /* @in is the enlarged image (borders on, after vips_embed()). Use * @resample->in for the original, not-expanded image. */ const VipsImage *in = (VipsImage *) a; const int ps = VIPS_IMAGE_SIZEOF_PEL( in ); double *vec = (double *) VIPS_IMAGE_ADDR( quadratic->mat, 0, 0 ); int clip_width = resample->in->Xsize; int clip_height = resample->in->Ysize; int xlow = or->valid.left; int ylow = or->valid.top; int xhigh = VIPS_RECT_RIGHT( &or->valid ); int yhigh = VIPS_RECT_BOTTOM( &or->valid ); VipsPel *q; int xo, yo; /* output coordinates, dstimage */ int z; double fxi, fyi; /* input coordinates */ double dx, dy; /* xo derivative of input coord. */ double ddx, ddy; /* 2nd xo derivative of input coord. */ VipsRect image; image.left = 0; image.top = 0; image.width = in->Xsize; image.height = in->Ysize; if( vips_region_image( ir, &image ) ) return( -1 ); for( yo = ylow; yo < yhigh; yo++ ) { fxi = xlow + vec[0]; /* order 0 */ fyi = yo + vec[1]; dx = 1.0; dy = 0.0; switch( quadratic->order ) { case 3: fxi += vec[10] * yo * yo + vec[8] * xlow * xlow; fyi += vec[11] * yo * yo + vec[9] * xlow * xlow; dx += vec[8]; ddx = vec[8] * 2.0; dy += vec[9]; ddy = vec[9] * 2.0; case 2: fxi += vec[6] * xlow * yo; fyi += vec[7] * xlow * yo; dx += vec[6] * yo; dy += vec[7] * yo; case 1: fxi += vec[4] * yo + vec[2] * xlow; fyi += vec[5] * yo + vec[3] * xlow; dx += vec[2]; dy += vec[3]; case 0: /* See above for order 0. */ break; default: g_assert( 0 ); return(-7); } q = VIPS_REGION_ADDR( or, xlow, yo ); for( xo = xlow; xo < xhigh; xo++ ) { int xi, yi; xi = fxi; yi = fyi; /* Clipping! */ if( xi < 0 || yi < 0 || xi >= clip_width || yi >= clip_height ) { for( z = 0; z < ps; z++ ) q[z] = 0; } else interpolate_fn( quadratic->interpolate, q, ir, fxi, fyi ); q += ps; fxi += dx; fyi += dy; if( quadratic->order > 2 ) { dx += ddx; dy += ddy; } } } return( 0 ); }
/* Paint the part of the region containing only part-pels. */ static void vips_zoom_paint_part( VipsRegion *or, VipsRegion *ir, VipsZoom *zoom, const int left, const int right, const int top, const int bottom ) { const int ps = VIPS_IMAGE_SIZEOF_PEL( ir->im ); const int ls = VIPS_REGION_LSKIP( or ); const int rs = ps * (right - left); /* Start position in input. */ const int ix = left / zoom->xfac; const int iy = top / zoom->yfac; /* Pels down to yfac boundary, pels down to bottom. Do the smallest of * these for first y loop. */ const int ptbound = (iy + 1) * zoom->yfac - top; const int ptbot = bottom - top; int yt = VIPS_MIN( ptbound, ptbot ); int x, y, z, i; /* Only know this. */ g_assert( right - left >= 0 && bottom - top >= 0 ); /* Have to loop over output. */ for( y = top; y < bottom; ) { VipsPel *p = VIPS_REGION_ADDR( ir, ix, y / zoom->yfac ); VipsPel *q = VIPS_REGION_ADDR( or, left, y ); VipsPel *r; /* Output pels until we jump the input pointer. */ int xt = (ix + 1) * zoom->xfac - left; /* Loop for this output line. */ r = q; for( x = left; x < right; x++ ) { /* Copy 1 pel. */ for( i = 0; i < ps; i++ ) r[i] = p[i]; r += ps; /* Move input if on boundary. */ --xt; if( xt == 0 ) { xt = zoom->xfac; p += ps; } } /* Repeat that output line until the bottom of this pixel * boundary, or we hit bottom. */ r = q + ls; for( z = 1; z < yt; z++ ) { memcpy( r, q, rs ); r += ls; } /* Move y on by the number of lines we wrote. */ y += yt; /* Reset yt for next iteration. */ yt = zoom->yfac; } }
/* Make and init a Write. */ static Write * write_new( VipsImage *im, const char *filename, VipsForeignTiffCompression compression, int Q, VipsForeignTiffPredictor predictor, char *profile, gboolean tile, int tile_width, int tile_height, gboolean pyramid, gboolean squash, gboolean miniswhite, VipsForeignTiffResunit resunit, double xres, double yres, gboolean bigtiff, gboolean rgbjpeg, gboolean properties, gboolean strip ) { Write *write; if( !(write = VIPS_NEW( im, Write )) ) return( NULL ); write->im = im; write->filename = vips_strdup( VIPS_OBJECT( im ), filename ); write->layer = NULL; write->tbuf = NULL; write->compression = get_compression( compression ); write->jpqual = Q; write->predictor = predictor; write->tile = tile; write->tilew = tile_width; write->tileh = tile_height; write->pyramid = pyramid; write->onebit = squash; write->miniswhite = miniswhite; write->icc_profile = vips_strdup( NULL, profile ); write->bigtiff = bigtiff; write->rgbjpeg = rgbjpeg; write->properties = properties; write->strip = strip; write->resunit = get_resunit( resunit ); write->xres = xres; write->yres = yres; /* In strip mode we use tileh to set rowsperstrip, and that does not * have the multiple-of-16 restriction. */ if( tile ) { if( (write->tilew & 0xf) != 0 || (write->tileh & 0xf) != 0 ) { vips_error( "vips2tiff", "%s", _( "tile size not a multiple of 16" ) ); return( NULL ); } } /* We can only pyramid LABQ and non-complex images. */ if( write->pyramid ) { if( im->Coding == VIPS_CODING_NONE && vips_band_format_iscomplex( im->BandFmt ) ) { vips_error( "vips2tiff", "%s", _( "can only pyramid LABQ and " "non-complex images" ) ); return( NULL ); } } /* Only 1-bit-ize 8 bit mono images. */ if( write->onebit && (im->Coding != VIPS_CODING_NONE || im->BandFmt != VIPS_FORMAT_UCHAR || im->Bands != 1) ) { vips_warn( "vips2tiff", "%s", _( "can only squash 1 band uchar images -- " "disabling squash" ) ); write->onebit = 0; } if( write->onebit && write->compression == COMPRESSION_JPEG ) { vips_warn( "vips2tiff", "%s", _( "can't have 1-bit JPEG -- disabling JPEG" ) ); write->compression = COMPRESSION_NONE; } /* We can only MINISWHITE non-complex images of 1 or 2 bands. */ if( write->miniswhite && (im->Coding != VIPS_CODING_NONE || vips_band_format_iscomplex( im->BandFmt ) || im->Bands > 2) ) { vips_warn( "vips2tiff", "%s", _( "can only save non-complex greyscale images " "as miniswhite -- disabling miniswhite" ) ); write->miniswhite = FALSE; } /* Sizeof a line of bytes in the TIFF tile. */ if( im->Coding == VIPS_CODING_LABQ ) write->tls = write->tilew * 3; else if( write->onebit ) write->tls = VIPS_ROUND_UP( write->tilew, 8 ) / 8; else write->tls = VIPS_IMAGE_SIZEOF_PEL( im ) * write->tilew; /* Build the pyramid framework. */ write->layer = pyramid_new( write, NULL, im->Xsize, im->Ysize ); /* Fill all the layers. */ if( pyramid_fill( write ) ) { write_free( write ); return( NULL ); } if( tile ) write->tbuf = vips_malloc( NULL, TIFFTileSize( write->layer->tif ) ); else write->tbuf = vips_malloc( NULL, TIFFScanlineSize( write->layer->tif ) ); if( !write->tbuf ) { write_free( write ); return( NULL ); } return( write ); }
static int vips_mapim_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; VipsRegion **ir = (VipsRegion **) seq; const VipsImage **in_array = (const VipsImage **) a; const VipsMapim *mapim = (VipsMapim *) b; const VipsResample *resample = VIPS_RESAMPLE( mapim ); const VipsImage *in = in_array[0]; const int window_size = vips_interpolate_get_window_size( mapim->interpolate ); const int window_offset = vips_interpolate_get_window_offset( mapim->interpolate ); const VipsInterpolateMethod interpolate = vips_interpolate_get_method( mapim->interpolate ); const int ps = VIPS_IMAGE_SIZEOF_PEL( in ); const int clip_width = resample->in->Xsize; const int clip_height = resample->in->Ysize; VipsRect bounds, image, clipped; int x, y, z; #ifdef DEBUG_VERBOSE printf( "vips_mapim_gen: " "generating left=%d, top=%d, width=%d, height=%d\n", r->left, r->top, r->width, r->height ); #endif /*DEBUG_VERBOSE*/ /* Fetch the chunk of the mapim image we need, and find the max/min in * x and y. */ if( vips_region_prepare( ir[1], r ) ) return( -1 ); VIPS_GATE_START( "vips_mapim_gen: work" ); vips_mapim_region_minmax( ir[1], r, &bounds ); VIPS_GATE_STOP( "vips_mapim_gen: work" ); /* The bounding box of that area is what we will need from @in. Add * enough for the interpolation stencil as well. * */ bounds.width += window_size - 1; bounds.height += window_size - 1; /* Clip against the expanded image. */ image.left = 0; image.top = 0; image.width = in->Xsize; image.height = in->Ysize; vips_rect_intersectrect( &bounds, &image, &clipped ); #ifdef DEBUG_VERBOSE printf( "vips_mapim_gen: " "preparing left=%d, top=%d, width=%d, height=%d\n", clipped.left, clipped.top, clipped.width, clipped.height ); #endif /*DEBUG_VERBOSE*/ if( vips_rect_isempty( &clipped ) ) { vips_region_black( or ); return( 0 ); } if( vips_region_prepare( ir[0], &clipped ) ) return( -1 ); VIPS_GATE_START( "vips_mapim_gen: work" ); /* Resample! x/y loop over pixels in the output image (5). */ for( y = 0; y < r->height; y++ ) { VipsPel * restrict p = VIPS_REGION_ADDR( ir[1], r->left, y + r->top ); VipsPel * restrict q = VIPS_REGION_ADDR( or, r->left, y + r->top ); switch( ir[1]->im->BandFmt ) { case VIPS_FORMAT_UCHAR: ULOOKUP( unsigned char ); break; case VIPS_FORMAT_CHAR: LOOKUP( signed char ); break; case VIPS_FORMAT_USHORT: ULOOKUP( unsigned short ); break; case VIPS_FORMAT_SHORT: LOOKUP( signed short ); break; case VIPS_FORMAT_UINT: ULOOKUP( unsigned int ); break; case VIPS_FORMAT_INT: LOOKUP( signed int ); break; case VIPS_FORMAT_FLOAT: case VIPS_FORMAT_COMPLEX: LOOKUP( float ); break; break; case VIPS_FORMAT_DOUBLE: case VIPS_FORMAT_DPCOMPLEX: LOOKUP( double ); break; default: g_assert_not_reached(); } } VIPS_GATE_STOP( "vips_mapim_gen: work" ); return( 0 ); }
/* 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 ); }