static int vips_msb_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsMsb *msb = (VipsMsb *) b; VipsConversion *conversion = (VipsConversion *) msb; VipsRect *r = &or->valid; int le = r->left; int to = r->top; int bo = VIPS_RECT_BOTTOM( r ); int sz = r->width * conversion->out->Bands; int x, y, i; if( vips_region_prepare( ir, r ) ) return( -1 ); for( y = to; y < bo; y++ ) { VipsPel *p = VIPS_REGION_ADDR( ir, le, y ); VipsPel *q = VIPS_REGION_ADDR( or, le, y ); if( msb->in->Coding == VIPS_CODING_LABQ && msb->band == -1 ) { /* LABQ, no sub-band select. */ for( x = 0; x < r->width; x++ ) { q[0] = p[0]; q[1] = 0x80 ^ p[1]; q[2] = 0x80 ^ p[2]; q += 4; p += 3; } } else if( msb->sign ) { /* Copy, converting signed to unsigned. */ p += msb->offset; for( i = 0; i < sz; i++ ) { q[i] = 0x80 ^ *p; p += msb->instep; } } else { /* Just pick out bytes. */ p += msb->offset; for( i = 0; i < sz; i++ ) { q[i] = *p; p += msb->instep; } } } return( 0 ); }
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 ); }
/* Another strip of image pixels from vips_sink_disc(). Write into the top * pyramid layer. */ static int write_strip( VipsRegion *region, VipsRect *area, void *a ) { Write *write = (Write *) a; Layer *layer = write->layer; #ifdef DEBUG printf( "write_strip: strip at %d, height %d\n", area->top, area->height ); #endif/*DEBUG*/ for(;;) { VipsRect *to = &layer->strip->valid; VipsRect target; /* The bit of strip that needs filling. */ target.left = 0; target.top = layer->write_y; target.width = layer->image->Xsize; target.height = to->height; vips_rect_intersectrect( &target, to, &target ); /* Clip against what we have available. */ vips_rect_intersectrect( &target, area, &target ); /* Are we empty? All done. */ if( vips_rect_isempty( &target ) ) break; /* And copy those pixels in. * * FIXME: If the strip fits inside the region we've just * received, we could skip the copy. Will this happen very * often? Unclear. */ vips_region_copy( region, layer->strip, &target, target.left, target.top ); layer->write_y += target.height; /* We can either fill the strip, if it's somewhere half-way * down the image, or, if it's at the bottom, get to the last * real line of pixels. */ if( layer->write_y == VIPS_RECT_BOTTOM( to ) || layer->write_y == layer->height ) { if( layer_strip_arrived( layer ) ) return( -1 ); } } return( 0 ); }
static int read_jpeg_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; ReadJpeg *jpeg = (ReadJpeg *) a; struct jpeg_decompress_struct *cinfo = &jpeg->cinfo; int sz = cinfo->output_width * cinfo->output_components; int y; #ifdef DEBUG printf( "read_jpeg_generate: line %d, %d rows\n", r->top, r->height ); #endif /*DEBUG*/ /* We're inside a tilecache where tiles are the full image width, so * this should always be true. */ g_assert( r->left == 0 ); g_assert( r->width == or->im->Xsize ); g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); /* Tiles should always be on a 8-pixel boundary. */ g_assert( r->top % 8 == 0 ); /* Tiles should always be a strip in height, unless it's the final * strip. */ g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) ); /* Here for longjmp() from vips__new_error_exit(). */ if( setjmp( jpeg->eman.jmp ) ) return( -1 ); for( y = 0; y < r->height; y++ ) { JSAMPROW row_pointer[1]; row_pointer[0] = (JSAMPLE *) VIPS_REGION_ADDR( or, 0, r->top + y ); jpeg_read_scanlines( cinfo, &row_pointer[0], 1 ); if( jpeg->invert_pels ) { int x; for( x = 0; x < sz; x++ ) row_pointer[0][x] = 255 - row_pointer[0][x]; } } return( 0 ); }
static int png2vips_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; Read *read = (Read *) a; int y; #ifdef DEBUG printf( "png2vips_generate: line %d, %d rows\n", r->top, r->height ); #endif /*DEBUG*/ /* We're inside a tilecache where tiles are the full image width, so * this should always be true. */ g_assert( r->left == 0 ); g_assert( r->width == or->im->Xsize ); g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); /* Tiles should always be a strip in height, unless it's the final * strip. */ g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) ); /* And check that y_pos is correct. It should be, since we are inside * a vips_sequential(). */ g_assert( r->top == read->y_pos ); for( y = 0; y < r->height; y++ ) { png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y ); /* We need to catch and ignore errors from read_row(). */ if( !setjmp( png_jmpbuf( read->pPng ) ) ) png_read_row( read->pPng, q, NULL ); else { #ifdef DEBUG printf( "png2vips_generate: png_read_row() failed, " "line %d\n", r->top + y ); printf( "png2vips_generate: file %s\n", read->name ); printf( "png2vips_generate: thread %p\n", g_thread_self() ); #endif /*DEBUG*/ } read->y_pos += 1; } return( 0 ); }
/* Generate func. */ static int vips_tile_cache_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *in = (VipsRegion *) seq; VipsTileCache *cache = (VipsTileCache *) b; const int tw = cache->tile_width; const int th = cache->tile_height; VipsRect *r = &or->valid; /* Find top left of tiles we need. */ int xs = (r->left / tw) * tw; int ys = (r->top / th) * th; int x, y; g_mutex_lock( cache->lock ); VIPS_DEBUG_MSG( "vips_tile_cache_gen: " "left = %d, top = %d, width = %d, height = %d\n", r->left, r->top, r->width, r->height ); for( y = ys; y < VIPS_RECT_BOTTOM( r ); y += th ) for( x = xs; x < VIPS_RECT_RIGHT( r ); x += tw ) { Tile *tile; VipsRect tarea; VipsRect hit; if( !(tile = tile_find( cache, in, x, y )) ) { g_mutex_unlock( cache->lock ); return( -1 ); } /* The area of the tile. */ tarea.left = x; tarea.top = y; tarea.width = tw; tarea.height = th; /* The part of the tile that we need. */ vips_rect_intersectrect( &tarea, r, &hit ); copy_region( tile->region, or, &hit ); } g_mutex_unlock( cache->lock ); return( 0 ); }
/* 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 ); }
static int vips_xyz_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsXyz *xyz = (VipsXyz *) a; VipsRect *r = &or->valid; int le = r->left; int to = r->top; int ri = VIPS_RECT_RIGHT( r ); int bo = VIPS_RECT_BOTTOM( r ); int x, y, i; for( y = to; y < bo; y++ ) { unsigned int *q = (unsigned int *) VIPS_REGION_ADDR( or, le, y ); unsigned int dims[5]; int r; int h; h = xyz->height * xyz->csize * xyz->dsize; dims[4] = y / h; r = y % h; h /= xyz->dsize; dims[3] = r / h; r %= h; h /= xyz->csize; dims[2] = r / h; r %= h; dims[1] = r; for( x = le; x < ri; x++ ) { dims[0] = x; for( i = 0; i < xyz->dimensions; i++ ) q[i] = dims[i]; q += xyz->dimensions; } } return( 0 ); }
static int vips_flip_vertical_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 y; int le = r->left; int to = r->top; int bo = VIPS_RECT_BOTTOM( r ); int ls; int psk, qsk; /* Transform to input coordinates. */ in = *r; in.top = ir->im->Ysize - bo; /* Ask for input we need. */ if( vips_region_prepare( ir, &in ) ) return( -1 ); /* Loop, copying and reversing lines. */ p = VIPS_REGION_ADDR( ir, le, in.top + in.height - 1 ); q = VIPS_REGION_ADDR( or, le, to ); psk = VIPS_REGION_LSKIP( ir ); qsk = VIPS_REGION_LSKIP( or ); ls = VIPS_REGION_SIZEOF_LINE( or ); for( y = to; y < bo; y++ ) { memcpy( q, p, ls ); p -= psk; q += qsk; } 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 ); } }
static int vips_sequential_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsSequential *sequential = (VipsSequential *) b; VipsRect *r = &or->valid; VipsRegion *ir = (VipsRegion *) seq; VIPS_DEBUG_MSG( "vips_sequential_generate %d\n", r->top ); /* The y pos of the request must be the same as our current file * position. */ if( r->top != sequential->y_pos ) { printf( "vips_sequential_generate: error -- " "at position %d in file, but position %d requested", sequential->y_pos, r->top ); vips_error( "VipsSequential", _( "non-sequential read --- " "at position %d in file, but position %d requested" ), sequential->y_pos, r->top ); return( -1 ); } /* We're inside a tilecache where tiles are the full image width, so * this should always be true. */ g_assert( r->left == 0 ); g_assert( r->width == or->im->Xsize ); g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); /* Pointer copy. */ if( vips_region_prepare( ir, r ) || vips_region_region( or, ir, r, r->left, r->top ) ) return( -1 ); sequential->y_pos += r->height; return( 0 ); }
/* 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; } }
/* Do a map. */ static int vips_maplut_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop ) { VipsMaplutSequence *seq = (VipsMaplutSequence *) vseq; VipsImage *in = (VipsImage *) a; VipsMaplut *maplut = (VipsMaplut *) b; VipsRegion *ir = seq->ir; VipsRect *r = &or->valid; int le = r->left; int to = r->top; int bo = VIPS_RECT_BOTTOM( r ); int np = r->width; /* Pels across region */ int ne = VIPS_REGION_N_ELEMENTS( or ); /* Number of elements */ int x, y, z, i; if( vips_region_prepare( ir, r ) ) return( -1 ); if( maplut->nb == 1 ) /* One band lut. */ outer_switch( loop1, loop1c, loop1g, loop1cg ) else /* Many band lut. */ if( in->Bands == 1 ) /* ... but 1 band input. */ outer_switch( loop1m, loop1cm, loop1gm, loop1cgm ) else outer_switch( loop, loopc, loopg, loopcg ) 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 ); }
static int fits2vips_generate( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop ) { VipsFits *fits = (VipsFits *) a; Rect *r = &out->valid; VipsPel *q; int z; int status; long fpixel[MAX_DIMENSIONS]; long lpixel[MAX_DIMENSIONS]; long inc[MAX_DIMENSIONS]; status = 0; VIPS_DEBUG_MSG( "fits2vips_generate: " "generating left = %d, top = %d, width = %d, height = %d\n", r->left, r->top, r->width, r->height ); /* Special case: the region we are writing to is exactly the width we * need, ie. we can read a rectangular area into it. */ if( VIPS_REGION_LSKIP( out ) == VIPS_REGION_SIZEOF_LINE( out ) ) { VIPS_DEBUG_MSG( "fits2vips_generate: block read\n" ); for( z = 0; z < MAX_DIMENSIONS; z++ ) fpixel[z] = 1; fpixel[0] = r->left + 1; fpixel[1] = r->top + 1; fpixel[2] = fits->band_select + 1; for( z = 0; z < MAX_DIMENSIONS; z++ ) lpixel[z] = 1; lpixel[0] = VIPS_RECT_RIGHT( r ); lpixel[1] = VIPS_RECT_BOTTOM( r ); lpixel[2] = fits->band_select + 1; for( z = 0; z < MAX_DIMENSIONS; z++ ) inc[z] = 1; q = VIPS_REGION_ADDR( out, r->left, r->top ); /* Break on ffgsv() for this call. */ g_mutex_lock( fits->lock ); if( fits_read_subset( fits->fptr, fits->datatype, fpixel, lpixel, inc, NULL, q, NULL, &status ) ) { vips_fits_error( status ); g_mutex_unlock( fits->lock ); return( -1 ); } g_mutex_unlock( fits->lock ); } else { int y; for( y = r->top; y < VIPS_RECT_BOTTOM( r ); y ++ ) { for( z = 0; z < MAX_DIMENSIONS; z++ ) fpixel[z] = 1; fpixel[0] = r->left + 1; fpixel[1] = y + 1; fpixel[2] = fits->band_select + 1; for( z = 0; z < MAX_DIMENSIONS; z++ ) lpixel[z] = 1; lpixel[0] = VIPS_RECT_RIGHT( r ); lpixel[1] = y + 1; lpixel[2] = fits->band_select + 1; for( z = 0; z < MAX_DIMENSIONS; z++ ) inc[z] = 1; q = VIPS_REGION_ADDR( out, r->left, y ); /* Break on ffgsv() for this call. */ g_mutex_lock( fits->lock ); if( fits_read_subset( fits->fptr, fits->datatype, fpixel, lpixel, inc, NULL, q, NULL, &status ) ) { vips_fits_error( status ); g_mutex_unlock( fits->lock ); return( -1 ); } g_mutex_unlock( fits->lock ); } } return( 0 ); }
/* Build a pyramid. * * width/height is the size of this layer, real_* the subsection of the layer * which is real pixels (as opposed to background). */ static Layer * pyramid_build( VipsForeignSaveDz *dz, Layer *above, int width, int height, VipsRect *real_pixels ) { VipsForeignSave *save = VIPS_FOREIGN_SAVE( dz ); Layer *layer = VIPS_NEW( dz, Layer ); VipsRect strip; int limit; layer->dz = dz; layer->width = width; layer->height = height; layer->tiles_across = ROUND_UP( width, dz->tile_size ) / dz->tile_size; layer->tiles_down = ROUND_UP( height, dz->tile_size ) / dz->tile_size; layer->real_pixels = *real_pixels; layer->image = NULL; layer->strip = NULL; layer->copy = NULL; if( !above ) /* Top of pyramid. */ layer->sub = 1; else layer->sub = above->sub * 2; layer->below = NULL; layer->above = above; /* We round the image size up to an even number to make x2 shrink * easy. */ layer->image = vips_image_new(); if( vips_image_pipelinev( layer->image, VIPS_DEMAND_STYLE_ANY, save->ready, NULL ) ) { layer_free( layer ); return( NULL ); } layer->image->Xsize = width + (width & 1); layer->image->Ysize = height + (height & 1); layer->strip = vips_region_new( layer->image ); layer->copy = vips_region_new( layer->image ); /* The regions will get used in the bg thread callback, so make sure * we don't own them. */ vips__region_no_ownership( layer->strip ); vips__region_no_ownership( layer->copy ); /* Build a line of tiles here. Normally strips are height + 2 * * overlap, but the first row is missing the top edge. * * Expand the strip if necessary to make sure we have an even * number of lines. */ layer->y = 0; layer->write_y = 0; strip.left = 0; strip.top = 0; strip.width = layer->image->Xsize; strip.height = dz->tile_size + dz->overlap; if( (strip.height & 1) == 1 ) strip.height += 1; if( vips_region_buffer( layer->strip, &strip ) ) { layer_free( layer ); return( NULL ); } switch( dz->depth ) { case VIPS_FOREIGN_DZ_DEPTH_ONEPIXEL: limit = 1; break; case VIPS_FOREIGN_DZ_DEPTH_ONETILE: limit = dz->tile_size; break; case VIPS_FOREIGN_DZ_DEPTH_ONE: limit = VIPS_MAX( width, height ); break; default: g_assert( 0 ); limit = dz->tile_size; break; } if( width > limit || height > limit ) { /* Round up, so eg. a 5 pixel wide image becomes 3 a layer * down. * * For the rect, round left/top down, round bottom/right up, * so we get all possible pixels. */ VipsRect halfrect; halfrect.left = real_pixels->left / 2; halfrect.top = real_pixels->top / 2; halfrect.width = (VIPS_RECT_RIGHT( real_pixels ) + 1) / 2 - halfrect.left; halfrect.height = (VIPS_RECT_BOTTOM( real_pixels ) + 1) / 2 - halfrect.top; if( !(layer->below = pyramid_build( dz, layer, (width + 1) / 2, (height + 1) / 2, &halfrect )) ) { layer_free( layer ); return( NULL ); } layer->n = layer->below->n + 1; } else layer->n = 0; #ifdef DEBUG printf( "pyramid_build:\n" ); printf( "\tn = %d\n", layer->n ); printf( "\twidth = %d, height = %d\n", width, height ); printf( "\tXsize = %d, Ysize = %d\n", layer->image->Xsize, layer->image->Ysize ); printf( "\treal_pixels.left = %d, real_pixels.top = %d\n", real_pixels->left, real_pixels->top ); printf( "\treal_pixels.width = %d, real_pixels.height = %d\n", real_pixels->width, real_pixels->height ); #endif return( layer ); }
static int vips_sequential_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsSequential *sequential = (VipsSequential *) b; VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( sequential ); VipsRect *r = &or->valid; VipsRegion *ir = (VipsRegion *) seq; VIPS_DEBUG_MSG_GREEN( "thread %p request for line %d, height %d\n", g_thread_self(), r->top, r->height ); if( sequential->trace ) vips_info( class->nickname, "request for line %d, height %d", r->top, r->height ); VIPS_GATE_START( "vips_sequential_generate: wait" ); g_mutex_lock( sequential->lock ); VIPS_GATE_STOP( "vips_sequential_generate: wait" ); VIPS_DEBUG_MSG_GREEN( "thread %p has lock ...\n", g_thread_self() ); /* If we've seen an error, everything must stop. */ if( sequential->error ) { g_mutex_unlock( sequential->lock ); return( -1 ); } if( r->top > sequential->y_pos && sequential->y_pos > 0 ) { /* This request is for stuff beyond the current read position, * and this is not the first request. We * stall for a while to give other threads time to catch up. * * The stall can be cancelled by a signal on @ready. * * We don't stall forever, since an error would be better than * deadlock, and we don't fail on timeout, since the timeout * may be harmless. */ #ifdef HAVE_COND_INIT gint64 time; time = g_get_monotonic_time() + STALL_TIME * G_TIME_SPAN_SECOND; #else GTimeVal time; g_get_current_time( &time ); g_time_val_add( &time, STALL_TIME * 1000000 ); #endif VIPS_DEBUG_MSG_GREEN( "thread %p stalling for up to %gs ...\n", g_thread_self(), STALL_TIME ); VIPS_GATE_START( "vips_sequential_generate: wait" ); /* Exit the loop on timeout or condition passes. We have to * be wary of spurious wakeups. */ while( r->top > sequential->y_pos ) { #ifdef HAVE_COND_INIT if( !g_cond_wait_until( sequential->ready, sequential->lock, time ) ) break; #else if( !g_cond_timed_wait( sequential->ready, sequential->lock, &time ) ) break; #endif /* We may have woken up because of an eval error. */ if( sequential->error ) { g_mutex_unlock( sequential->lock ); return( -1 ); } } VIPS_GATE_STOP( "vips_sequential_generate: wait" ); VIPS_DEBUG_MSG_GREEN( "thread %p awake again ...\n", g_thread_self() ); } if( r->top > sequential->y_pos ) { /* This is a request for something some way down the image, * and we've fallen through from the stall above. * * Probably the operation is something like extract_area and * we should skip the initial part of the image. In fact, * we read to cache, since it may be useful. */ VipsRect area; VIPS_DEBUG_MSG_GREEN( "thread %p skipping to line %d ...\n", g_thread_self(), r->top ); area.left = 0; area.top = sequential->y_pos; area.width = 1; area.height = r->top - sequential->y_pos; if( vips_region_prepare( ir, &area ) ) { VIPS_DEBUG_MSG( "thread %p error, unlocking #1 ...\n", g_thread_self() ); sequential->error = -1; g_cond_broadcast( sequential->ready ); g_mutex_unlock( sequential->lock ); return( -1 ); } sequential->y_pos = VIPS_RECT_BOTTOM( &area ); } /* This is a request for old or present pixels -- serve from cache. * This may trigger further, sequential reads. */ VIPS_DEBUG_MSG_GREEN( "thread %p reading ...\n", g_thread_self() ); if( vips_region_prepare( ir, r ) || vips_region_region( or, ir, r, r->left, r->top ) ) { VIPS_DEBUG_MSG( "thread %p error, unlocking #2 ...\n", g_thread_self() ); sequential->error = -1; g_cond_broadcast( sequential->ready ); g_mutex_unlock( sequential->lock ); return( -1 ); } if( VIPS_RECT_BOTTOM( r ) > sequential->y_pos ) { /* This request has moved the read point. Update it, and wake * up all stalled threads for a retry. */ sequential->y_pos = VIPS_RECT_BOTTOM( r ); VIPS_DEBUG_MSG_GREEN( "thread %p updating y_pos to %d and " "waking stalled\n", g_thread_self(), sequential->y_pos ); g_cond_broadcast( sequential->ready ); } VIPS_DEBUG_MSG_GREEN( "thread %p unlocking ...\n", g_thread_self() ); g_mutex_unlock( sequential->lock ); return( 0 ); }
/* Zoom a VipsRegion. */ static int vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsZoom *zoom = (VipsZoom *) b; /* Output area we are building. */ const VipsRect *r = &or->valid; const int ri = VIPS_RECT_RIGHT( r ); const int bo = VIPS_RECT_BOTTOM(r); VipsRect s; int left, right, top, bottom; int width, height; /* Area of input we need. We have to round out, as we may have * part-pixels all around the edges. */ left = ROUND_DOWN( r->left, zoom->xfac ); right = ROUND_UP( ri, zoom->xfac ); top = ROUND_DOWN( r->top, zoom->yfac ); bottom = ROUND_UP( bo, zoom->yfac ); width = right - left; height = bottom - top; s.left = left / zoom->xfac; s.top = top / zoom->yfac; s.width = width / zoom->xfac; s.height = height / zoom->yfac; if( vips_region_prepare( ir, &s ) ) return( -1 ); /* Find the part of the output (if any) which uses only whole pels. */ left = ROUND_UP( r->left, zoom->xfac ); right = ROUND_DOWN( ri, zoom->xfac ); top = ROUND_UP( r->top, zoom->yfac ); bottom = ROUND_DOWN( bo, zoom->yfac ); width = right - left; height = bottom - top; /* Stage 1: we just paint the whole pels in the centre of the region. * As we know they are not clipped, we can do it quickly. */ if( width > 0 && height > 0 ) vips_zoom_paint_whole( or, ir, zoom, left, right, top, bottom ); /* Just fractional pixels left. Paint in the top, left, right and * bottom parts. */ if( top - r->top > 0 ) /* Some top pixels. */ vips_zoom_paint_part( or, ir, zoom, r->left, ri, r->top, VIPS_MIN( top, bo ) ); if( left - r->left > 0 && height > 0 ) /* Left pixels. */ vips_zoom_paint_part( or, ir, zoom, r->left, VIPS_MIN( left, ri ), top, bottom ); if( ri - right > 0 && height > 0 ) /* Right pixels. */ vips_zoom_paint_part( or, ir, zoom, VIPS_MAX( right, r->left ), ri, top, bottom ); if( bo - bottom > 0 && height >= 0 ) /* Bottom pixels. */ vips_zoom_paint_part( or, ir, zoom, r->left, ri, VIPS_MAX( bottom, r->top ), bo ); return( 0 ); }
static void vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate, void *out, VipsRegion *in, double x, double y ) { /* Find the mask index. We round-to-nearest, so we need to generate * indexes in 0 to VIPS_TRANSFORM_SCALE, 2^n + 1 values. We multiply * by 2 more than we need to, add one, mask, then shift down again to * get the extra range. */ const int sx = x * VIPS_TRANSFORM_SCALE * 2; const int sy = y * VIPS_TRANSFORM_SCALE * 2; const int six = sx & (VIPS_TRANSFORM_SCALE * 2 - 1); const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1); const int tx = (six + 1) >> 1; const int ty = (siy + 1) >> 1; /* We know x/y are always positive, so we can just (int) them. */ const int ix = (int) x; const int iy = (int) y; /* Back and up one to get the top-left of the 4x4. */ const VipsPel *p = VIPS_REGION_ADDR( in, ix - 1, iy - 1 ); /* Look up the tables we need. */ const int *cxi = vips_bicubic_matrixi[tx]; const int *cyi = vips_bicubic_matrixi[ty]; const double *cxf = vips_bicubic_matrixf[tx]; const double *cyf = vips_bicubic_matrixf[ty]; /* Pel size and line size. */ const int bands = in->im->Bands; const int lskip = VIPS_REGION_LSKIP( in ); g_assert( ix - 1 >= in->valid.left ); g_assert( iy - 1 >= in->valid.top ); g_assert( ix + 2 < VIPS_RECT_RIGHT( &in->valid ) ); g_assert( iy + 2 < VIPS_RECT_BOTTOM( &in->valid ) ); /* Confirm that absolute_x and absolute_y are >= 1, because of * window_offset. */ g_assert( x >= 1.0 ); g_assert( y >= 1.0 ); #ifdef DEBUG printf( "vips_interpolate_bicubic_interpolate: %g %g\n", x, y ); printf( "\tleft=%d, top=%d, width=%d, height=%d\n", ix - 1, iy - 1, 4, 4 ); printf( "\tmaskx=%d, masky=%d\n", tx, ty ); #endif /*DEBUG*/ switch( in->im->BandFmt ) { case VIPS_FORMAT_UCHAR: bicubic_unsigned_int_tab<unsigned char, UCHAR_MAX>( out, p, bands, lskip, cxi, cyi ); /* Handy for benchmarking bicubic_float_tab<unsigned char>( out, p, bands, lskip, cxf, cyf ); bicubic_notab<unsigned char>( out, p, bands, lskip, x - ix, y - iy ); */ break; case VIPS_FORMAT_CHAR: bicubic_signed_int_tab<signed char, SCHAR_MIN, SCHAR_MAX>( out, p, bands, lskip, cxi, cyi ); break; case VIPS_FORMAT_USHORT: bicubic_unsigned_int_tab<unsigned short, USHRT_MAX>( out, p, bands, lskip, cxi, cyi ); break; case VIPS_FORMAT_SHORT: bicubic_signed_int_tab<signed short, SHRT_MIN, SHRT_MAX>( out, p, bands, lskip, cxi, cyi ); break; case VIPS_FORMAT_UINT: bicubic_float_tab<unsigned int>( out, p, bands, lskip, cxf, cyf ); break; case VIPS_FORMAT_INT: bicubic_float_tab<signed int>( out, p, bands, lskip, cxf, cyf ); break; case VIPS_FORMAT_FLOAT: bicubic_float_tab<float>( out, p, bands, lskip, cxf, cyf ); break; case VIPS_FORMAT_DOUBLE: bicubic_notab<double>( out, p, bands, lskip, x - ix, y - iy ); break; case VIPS_FORMAT_COMPLEX: bicubic_float_tab<float>( out, p, bands * 2, lskip, cxf, cyf ); break; case VIPS_FORMAT_DPCOMPLEX: bicubic_notab<double>( out, p, bands * 2, lskip, x - ix, y - iy ); break; default: break; } }
static int png2vips_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; Read *read = (Read *) a; int y; #ifdef DEBUG printf( "png2vips_generate: line %d, %d rows\n", r->top, r->height ); printf( "png2vips_generate: y_top = %d\n", read->y_pos ); #endif /*DEBUG*/ /* We're inside a tilecache where tiles are the full image width, so * this should always be true. */ g_assert( r->left == 0 ); g_assert( r->width == or->im->Xsize ); g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); /* Tiles should always be a strip in height, unless it's the final * strip. */ g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) ); /* And check that y_pos is correct. It should be, since we are inside * a vips_sequential(). */ if( r->top != read->y_pos ) { vips_error( "vipspng", _( "out of order read at line %d" ), read->y_pos ); return( -1 ); } for( y = 0; y < r->height; y++ ) { png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y ); /* We need to catch and ignore errors from read_row(). */ if( !setjmp( png_jmpbuf( read->pPng ) ) ) png_read_row( read->pPng, q, NULL ); else { #ifdef DEBUG printf( "png2vips_generate: png_read_row() failed, " "line %d\n", r->top + y ); printf( "png2vips_generate: file %s\n", read->name ); printf( "png2vips_generate: thread %p\n", g_thread_self() ); #endif /*DEBUG*/ } read->y_pos += 1; } /* Turn errors back on. png_read_end() can trigger them too. */ if( setjmp( png_jmpbuf( read->pPng ) ) ) return( -1 ); /* We need to shut down the reader immediately at the end of read or * we won't detach ready for the next image. */ if( read->y_pos >= read->out->Ysize ) { png_read_end( read->pPng, NULL ); read_destroy( read ); } return( 0 ); }
/* Shrink what pixels we can from this strip into the layer below. If the * strip below fills, recurse. */ static int layer_strip_shrink( Layer *layer ) { Layer *below = layer->below; VipsRegion *from = layer->strip; VipsRegion *to = below->strip; VipsRect target; VipsRect source; /* Our pixels might cross a strip boundary in the layer below, so we * have to write repeatedly until we run out of pixels. */ for(;;) { /* The pixels the layer below needs. */ target.left = 0; target.top = below->write_y; target.width = below->image->Xsize; target.height = to->valid.height; vips_rect_intersectrect( &target, &to->valid, &target ); /* Those pixels need this area of this layer. */ source.left = target.left * 2; source.top = target.top * 2; source.width = target.width * 2; source.height = target.height * 2; /* Of which we have these available. */ vips_rect_intersectrect( &source, &from->valid, &source ); /* So these are the pixels in the layer below we can provide. */ target.left = source.left / 2; target.top = source.top / 2; target.width = source.width / 2; target.height = source.height / 2; /* None? All done. */ if( vips_rect_isempty( &target ) ) break; (void) vips_region_shrink( from, to, &target ); below->write_y += target.height; /* If we've filled the strip below, let it know. * We can either fill the region, if it's somewhere half-way * down the image, or, if it's at the bottom, get to the last * real line of pixels. */ if( below->write_y == VIPS_RECT_BOTTOM( &to->valid ) || below->write_y == below->height ) { if( layer_strip_arrived( below ) ) return( -1 ); } } return( 0 ); }
static int vips_sequential_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsSequential *sequential = (VipsSequential *) b; VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( sequential ); VipsRect *r = &or->valid; VipsRegion *ir = (VipsRegion *) seq; VIPS_DEBUG_MSG( "thread %p request for %d lines, start line %d\n", g_thread_self(), r->height, r->top ); if( sequential->trace ) vips_diag( class->nickname, "request for %d lines, starting at line %d", r->height, r->top ); g_mutex_lock( sequential->lock ); VIPS_DEBUG_MSG( "thread %p has lock ...\n", g_thread_self() ); /* If we've seen an error, everything must stop. */ if( sequential->error ) { g_mutex_unlock( sequential->lock ); return( -1 ); } if( r->top > sequential->y_pos && sequential->y_pos > 0 ) { /* We have started reading (y_pos > 0) and this request is for * stuff beyond that, stall for a short while to give other * threads time to catch up. * * The stall can be cancelled by a signal on @ready. */ VIPS_DEBUG_MSG( "thread %p stalling for up to %gs ...\n", g_thread_self(), STALL_TIME ); vips_g_cond_timed_wait( sequential->ready, sequential->lock, STALL_TIME * 1000000 ); VIPS_DEBUG_MSG( "thread %p awake again ...\n", g_thread_self() ); } /* This is a request for something some way down the image, and we've * either not read anything yet or fallen through from the stall * above. * * Probably the operation is something like extract_area and we should * skip the initial part of the image. In fact, we read to cache, * since it may be useful. */ if( r->top > sequential->y_pos ) { VipsRect area; VIPS_DEBUG_MSG( "thread %p skipping to line %d ...\n", g_thread_self(), r->top ); area.left = 0; area.top = sequential->y_pos; area.width = 1; area.height = r->top - sequential->y_pos; if( vips_region_prepare( ir, &area ) ) { VIPS_DEBUG_MSG( "thread %p error, unlocking ...\n", g_thread_self() ); sequential->error = -1; g_cond_broadcast( sequential->ready ); g_mutex_unlock( sequential->lock ); return( -1 ); } sequential->y_pos = VIPS_RECT_BOTTOM( &area ); } /* This is a request for old or present pixels -- serve from cache. * This may trigger further, sequential reads. */ VIPS_DEBUG_MSG( "thread %p reading ...\n", g_thread_self() ); if( vips_region_prepare( ir, r ) || vips_region_region( or, ir, r, r->left, r->top ) ) { VIPS_DEBUG_MSG( "thread %p error, unlocking ...\n", g_thread_self() ); sequential->error = -1; g_cond_broadcast( sequential->ready ); g_mutex_unlock( sequential->lock ); return( -1 ); } if( VIPS_RECT_BOTTOM( r ) > sequential->y_pos ) { /* This request has moved the read point. Update it, and wake * up all stalled threads for a retry. */ sequential->y_pos = VIPS_RECT_BOTTOM( r ); VIPS_DEBUG_MSG( "thread %p updating y_pos to %d and " "waking stalled\n", g_thread_self(), sequential->y_pos ); g_cond_broadcast( sequential->ready ); } VIPS_DEBUG_MSG( "thread %p unlocking ...\n", g_thread_self() ); g_mutex_unlock( sequential->lock ); 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 ); }
/* 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_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 ); }
static int png2vips_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRect *r = &or->valid; Read *read = (Read *) a; int y; #ifdef DEBUG printf( "png2vips_generate: line %d, %d rows\n", r->top, r->height ); printf( "png2vips_generate: y_top = %d\n", read->y_pos ); #endif /*DEBUG*/ /* We're inside a tilecache where tiles are the full image width, so * this should always be true. */ g_assert( r->left == 0 ); g_assert( r->width == or->im->Xsize ); g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize ); /* Tiles should always be a strip in height, unless it's the final * strip. */ g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) ); /* And check that y_pos is correct. It should be, since we are inside * a vips_sequential(). */ if( r->top != read->y_pos ) { vips_error( "vipspng", _( "out of order read at line %d" ), read->y_pos ); return( -1 ); } for( y = 0; y < r->height; y++ ) { png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y ); /* We need to catch errors from read_row(). */ if( !setjmp( png_jmpbuf( read->pPng ) ) ) png_read_row( read->pPng, q, NULL ); else { /* We've failed to read some pixels. Knock this * operation out of cache. */ vips_foreign_load_invalidate( read->out ); #ifdef DEBUG printf( "png2vips_generate: png_read_row() failed, " "line %d\n", r->top + y ); printf( "png2vips_generate: file %s\n", read->name ); printf( "png2vips_generate: thread %p\n", g_thread_self() ); #endif /*DEBUG*/ /* And bail if fail is on. We have to add an error * message, since the handler we install just does * g_warning(). */ if( read->fail ) { vips_error( "vipspng", "%s", _( "libpng read error" ) ); return( -1 ); } } read->y_pos += 1; } /* Catch errors from png_read_end(). This can fail on a truncated * file. */ if( setjmp( png_jmpbuf( read->pPng ) ) ) { if( read->fail ) { vips_error( "vipspng", "%s", _( "libpng read error" ) ); return( -1 ); } return( 0 ); } /* We need to shut down the reader immediately at the end of read or * we won't detach ready for the next image. */ if( read->y_pos >= read->out->Ysize ) { png_read_end( read->pPng, NULL ); read_destroy( read ); } return( 0 ); }
static int vips_replicate_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsImage *in = (VipsImage *) a; VipsRect *r = &or->valid; int twidth = in->Xsize; int theight = in->Ysize; int x, y; VipsRect tile; /* Find top left of tiles we need. */ int xs = (r->left / twidth) * twidth; int ys = (r->top / theight) * theight; /* The tile enclosing the top-left corner of the requested area. */ tile.left = xs; tile.top = ys; tile.width = twidth; tile.height = theight; /* If the request fits inside a single tile, we can just pointer-copy. */ if( vips_rect_includesrect( &tile, r ) ) { VipsRect irect; /* Translate request to input space. */ irect = *r; irect.left -= xs; irect.top -= ys; if( vips_region_prepare( ir, &irect ) ) return( -1 ); if( vips_region_region( or, ir, r, irect.left, irect.top ) ) return( -1 ); return( 0 ); } for( y = ys; y < VIPS_RECT_BOTTOM( r ); y += theight ) for( x = xs; x < VIPS_RECT_RIGHT( r ); x += twidth ) { VipsRect paint; /* Whole tile at x, y */ tile.left = x; tile.top = y; tile.width = twidth; tile.height = theight; /* Which parts touch the area of the output we are * building. */ vips_rect_intersectrect( &tile, r, &paint ); /* Translate back to ir coordinates. */ paint.left -= x; paint.top -= y; g_assert( !vips_rect_isempty( &paint ) ); /* Render into or. */ if( vips_region_prepare_to( ir, or, &paint, paint.left + x, paint.top + y ) ) return( -1 ); } return( 0 ); }
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 ); }
static int vips_blend_gen( VipsRegion *or, void *seq, void *client1, void *client2, gboolean *stop ) { VipsRegion **ir = (VipsRegion **) seq; 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 x, y; int all0, all255; if( vips_region_prepare( ir[2], r ) ) return( -1 ); /* Is the conditional all zero or all 255? We can avoid asking * for one of the inputs to be calculated. */ all0 = *VIPS_REGION_ADDR( ir[2], le, to ) == 0; all255 = *VIPS_REGION_ADDR( ir[2], le, to ) == 255; for( y = to; y < bo; y++ ) { VipsPel *p = VIPS_REGION_ADDR( ir[2], le, y ); int width = r->width * c->Bands; for( x = 0; x < width; x++ ) { all0 &= p[x] == 0; all255 &= p[x] == 255; } if( !all0 && !all255 ) break; } if( all255 ) { /* All 255. 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++ ) { VipsPel *ap = VIPS_REGION_ADDR( ir[0], le, y ); VipsPel *bp = VIPS_REGION_ADDR( ir[1], le, y ); VipsPel *cp = VIPS_REGION_ADDR( ir[2], le, y ); VipsPel *q = VIPS_REGION_ADDR( or, le, y ); 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 ); } } return( 0 ); }