/* 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 ); }
/* 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 ); }
/* r is the bit we are trying to paint, guaranteed to be entirely within * border area i. Set out to be the edge of the image we need to paint the * pixels in r. */ static void vips_embed_find_edge( VipsEmbed *embed, VipsRect *r, int i, VipsRect *out ) { /* Expand the border by 1 pixel, intersect with the image area, and we * get the edge. Usually too much though: eg. we could make the entire * right edge. */ *out = embed->border[i]; vips_rect_marginadjust( out, 1 ); vips_rect_intersectrect( out, &embed->rsub, out ); /* Usually too much though: eg. we could make the entire * right edge. If we're strictly up/down/left/right of the image, we * can trim. */ if( i == 0 || i == 2 ) { VipsRect extend; /* Above or below. */ extend = *r; extend.top = 0; extend.height = embed->height; vips_rect_intersectrect( out, &extend, out ); } if( i == 1 || i == 3 ) { VipsRect extend; /* Left or right. */ extend = *r; extend.left = 0; extend.width = embed->width; vips_rect_intersectrect( out, &extend, out ); } }
/* Insert generate function. */ static int vips_insert_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion **ir = (VipsRegion **) seq; VipsRect *r = &or->valid; VipsInsert *insert = (VipsInsert *) b; VipsRect ovl; /* Does the rect we have been asked for fall entirely inside the * sub-image? */ if( vips_rect_includesrect( &insert->rsub, &or->valid ) ) return( vips_insert_just_one( or, ir[1], insert->rsub.left, insert->rsub.top ) ); /* Does it fall entirely inside the main, and not at all inside the * sub? */ vips_rect_intersectrect( &or->valid, &insert->rsub, &ovl ); if( vips_rect_includesrect( &insert->rmain, &or->valid ) && vips_rect_isempty( &ovl ) ) return( vips_insert_just_one( or, ir[0], insert->rmain.left, insert->rmain.top ) ); /* Output requires both (or neither) input. If it is not entirely * inside both the main and the sub, then there is going to be some * background. */ if( !(vips_rect_includesrect( &insert->rsub, &or->valid ) && vips_rect_includesrect( &insert->rmain, &or->valid )) ) vips_region_paint_pel( or, r, insert->ink ); /* Paste from main. */ if( vips_insert_paste_region( or, ir[0], &insert->rmain ) ) return( -1 ); /* Paste from sub. */ if( vips_insert_paste_region( or, ir[1], &insert->rsub ) ) return( -1 ); return( 0 ); }
/* Write a set of tiles across the strip. */ static int layer_write_tile( Write *write, Layer *layer, VipsRegion *strip ) { VipsImage *im = layer->image; VipsRect *area = &strip->valid; VipsRect image; int x; image.left = 0; image.top = 0; image.width = im->Xsize; image.height = im->Ysize; for( x = 0; x < im->Xsize; x += write->tilew ) { VipsRect tile; tile.left = x; tile.top = area->top; tile.width = write->tilew; tile.height = write->tileh; vips_rect_intersectrect( &tile, &image, &tile ); /* Have to repack pixels. */ pack2tiff( write, layer, strip, &tile, write->tbuf ); #ifdef DEBUG_VERBOSE printf( "Writing %dx%d tile at position %dx%d to image %s\n", tile.width, tile.height, tile.left, tile.top, TIFFFileName( layer->tif ) ); #endif /*DEBUG_VERBOSE*/ if( TIFFWriteTile( layer->tif, write->tbuf, tile.left, tile.top, 0, 0 ) < 0 ) { vips_error( "vips2tiff", "%s", _( "TIFF write tile failed" ) ); return( -1 ); } } return( 0 ); }
/* Paste in parts of ir that fall within or --- ir is an input REGION for an * image positioned at pos within or. */ static int vips_insert_paste_region( VipsRegion *or, VipsRegion *ir, VipsRect *pos ) { VipsRect ovl; /* Does any of the sub-image appear in the area we have been asked * to make? */ vips_rect_intersectrect( &or->valid, pos, &ovl ); if( !vips_rect_isempty( &ovl ) ) { /* Find the part of in we need. */ ovl.left -= pos->left; ovl.top -= pos->top; /* Paint this area of pixels into or. */ if( vips_region_prepare_to( ir, or, &ovl, ovl.left + pos->left, ovl.top + pos->top ) ) return( -1 ); } 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_embed_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsEmbed *embed = (VipsEmbed *) b; VipsRect *r = &or->valid; Rect ovl; int i; VipsPel *p; int plsk; /* Entirely within the input image? Generate the subimage and copy * pointers. */ if( vips_rect_includesrect( &embed->rsub, r ) ) { VipsRect need; need = *r; need.left -= embed->x; need.top -= embed->y; if( vips_region_prepare( ir, &need ) || vips_region_region( or, ir, r, need.left, need.top ) ) return( -1 ); return( 0 ); } /* Does any of the input image appear in the area we have been asked * to make? Paste it in. */ vips_rect_intersectrect( r, &embed->rsub, &ovl ); if( !vips_rect_isempty( &ovl ) ) { /* Paint the bits coming from the input image. */ ovl.left -= embed->x; ovl.top -= embed->y; if( vips_region_prepare_to( ir, or, &ovl, ovl.left + embed->x, ovl.top + embed->y ) ) return( -1 ); ovl.left += embed->x; ovl.top += embed->y; } switch( embed->extend ) { case VIPS_EXTEND_BLACK: case VIPS_EXTEND_WHITE: VIPS_GATE_START( "vips_embed_gen: work1" ); /* Paint the borders a solid value. */ for( i = 0; i < 8; i++ ) vips_region_paint( or, &embed->border[i], embed->extend == 0 ? 0 : 255 ); VIPS_GATE_STOP( "vips_embed_gen: work1" ); break; case VIPS_EXTEND_BACKGROUND: VIPS_GATE_START( "vips_embed_gen: work2" ); /* Paint the borders a solid value. */ for( i = 0; i < 8; i++ ) vips_region_paint_pel( or, &embed->border[i], embed->ink ); VIPS_GATE_STOP( "vips_embed_gen: work2" ); break; case VIPS_EXTEND_COPY: /* Extend the borders. */ for( i = 0; i < 8; i++ ) { VipsRect todo; VipsRect edge; vips_rect_intersectrect( r, &embed->border[i], &todo ); if( !vips_rect_isempty( &todo ) ) { vips_embed_find_edge( embed, &todo, i, &edge ); /* Did we paint any of the input image? If we * did, we can fetch the edge pixels from * that. */ if( !vips_rect_isempty( &ovl ) ) { p = VIPS_REGION_ADDR( or, edge.left, edge.top ); plsk = VIPS_REGION_LSKIP( or ); } else { /* No pixels painted ... fetch * directly from the input image. */ edge.left -= embed->x; edge.top -= embed->y; if( vips_region_prepare( ir, &edge ) ) return( -1 ); p = VIPS_REGION_ADDR( ir, edge.left, edge.top ); plsk = VIPS_REGION_LSKIP( ir ); } vips_embed_paint_edge( embed, or, i, &todo, p, plsk ); } } break; default: g_assert( 0 ); } 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 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 ); }
/* A new strip has arrived! The strip has at least enough pixels in to * write a line of tiles or a set of scanlines. * * - write a line of tiles / set of scanlines * - shrink what we can to the layer below * - move our strip down by the tile height * - copy the overlap with the previous strip */ static int layer_strip_arrived( Layer *layer ) { Write *write = layer->write; int result; VipsRect new_strip; VipsRect overlap; VipsRect image_area; if( write->tile ) result = layer_write_tile( write, layer, layer->strip ); else result = layer_write_strip( write, layer, layer->strip ); if( result ) return( -1 ); if( layer->below && layer_strip_shrink( layer ) ) return( -1 ); /* Position our strip down the image. * * Expand the strip if necessary to make sure we have an even * number of lines. */ layer->y += write->tileh; new_strip.left = 0; new_strip.top = layer->y; new_strip.width = layer->image->Xsize; new_strip.height = write->tileh; image_area.left = 0; image_area.top = 0; image_area.width = layer->image->Xsize; image_area.height = layer->image->Ysize; vips_rect_intersectrect( &new_strip, &image_area, &new_strip ); if( (new_strip.height & 1) == 1 ) new_strip.height += 1; /* What pixels that we will need do we already have? Save them in * overlap. */ vips_rect_intersectrect( &new_strip, &layer->strip->valid, &overlap ); if( !vips_rect_isempty( &overlap ) ) { if( vips_region_buffer( layer->copy, &overlap ) ) return( -1 ); vips_region_copy( layer->strip, layer->copy, &overlap, overlap.left, overlap.top ); } if( !vips_rect_isempty( &new_strip ) ) { if( vips_region_buffer( layer->strip, &new_strip ) ) return( -1 ); /* And copy back again. */ if( !vips_rect_isempty( &overlap ) ) vips_region_copy( layer->copy, layer->strip, &overlap, overlap.left, overlap.top ); } 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 ReadSlide * readslide_new( const char *filename, VipsImage *out, int level, gboolean autocrop, const char *associated ) { ReadSlide *rslide; int64_t w, h; const char *error; const char *background; const char * const *properties; char *associated_names; if( level && associated ) { vips_error( "openslide2vips", "%s", _( "specify only one of level or associated " "image" ) ); return( NULL ); } rslide = VIPS_NEW( out, ReadSlide ); memset( rslide, 0, sizeof( *rslide ) ); g_signal_connect( out, "close", G_CALLBACK( readslide_destroy_cb ), rslide ); rslide->level = level; rslide->autocrop = autocrop; rslide->associated = g_strdup( associated ); /* Non-crazy defaults, override below if we can. */ rslide->tile_width = 256; rslide->tile_height = 256; rslide->osr = openslide_open( filename ); if( rslide->osr == NULL ) { vips_error( "openslide2vips", "%s", _( "unsupported slide format" ) ); return( NULL ); } error = openslide_get_error( rslide->osr ); if( error ) { vips_error( "openslide2vips", _( "opening slide: %s" ), error ); return( NULL ); } if( level < 0 || level >= openslide_get_level_count( rslide->osr ) ) { vips_error( "openslide2vips", "%s", _( "invalid slide level" ) ); return( NULL ); } if( associated && check_associated_image( rslide->osr, associated ) ) return( NULL ); if( associated ) { openslide_get_associated_image_dimensions( rslide->osr, associated, &w, &h ); vips_image_set_string( out, "slide-associated-image", associated ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); } else { char buf[256]; const char *value; openslide_get_level_dimensions( rslide->osr, level, &w, &h ); rslide->downsample = openslide_get_level_downsample( rslide->osr, level ); vips_image_set_int( out, "slide-level", level ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Try to get tile width/height. An undocumented, experimental * feature. */ vips_snprintf( buf, 256, "openslide.level[%d].tile-width", level ); if( (value = openslide_get_property_value( rslide->osr, buf )) ) rslide->tile_width = atoi( value ); vips_snprintf( buf, 256, "openslide.level[%d].tile-height", level ); if( (value = openslide_get_property_value( rslide->osr, buf )) ) rslide->tile_height = atoi( value ); if( value ) VIPS_DEBUG_MSG( "readslide_new: found tile-size\n" ); /* Some images have a bounds in the header. Crop to * that if autocrop is set. */ if( rslide->autocrop ) if( !get_bounds( rslide->osr, &rslide->bounds ) ) rslide->autocrop = FALSE; if( rslide->autocrop ) { VipsRect image; rslide->bounds.left /= rslide->downsample; rslide->bounds.top /= rslide->downsample; rslide->bounds.width /= rslide->downsample; rslide->bounds.height /= rslide->downsample; /* Clip against image size. */ image.left = 0; image.top = 0; image.width = w; image.height = h; vips_rect_intersectrect( &rslide->bounds, &image, &rslide->bounds ); /* If we've clipped to nothing, ignore bounds. */ if( vips_rect_isempty( &rslide->bounds ) ) rslide->autocrop = FALSE; } if( rslide->autocrop ) { w = rslide->bounds.width; h = rslide->bounds.height; } } rslide->bg = 0xffffff; if( (background = openslide_get_property_value( rslide->osr, OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR )) ) rslide->bg = strtoul( background, NULL, 16 ); if( w <= 0 || h <= 0 || rslide->downsample < 0 ) { vips_error( "openslide2vips", _( "getting dimensions: %s" ), openslide_get_error( rslide->osr ) ); return( NULL ); } if( w > INT_MAX || h > INT_MAX ) { vips_error( "openslide2vips", "%s", _( "image dimensions overflow int" ) ); return( NULL ); } if( !rslide->autocrop ) { rslide->bounds.left = 0; rslide->bounds.top = 0; rslide->bounds.width = w; rslide->bounds.height = h; } vips_image_init_fields( out, w, h, 4, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 ); for( properties = openslide_get_property_names( rslide->osr ); *properties != NULL; properties++ ) vips_image_set_string( out, *properties, openslide_get_property_value( rslide->osr, *properties ) ); associated_names = g_strjoinv( ", ", (char **) openslide_get_associated_image_names( rslide->osr ) ); vips_image_set_string( out, "slide-associated-images", associated_names ); VIPS_FREE( associated_names ); return( rslide ); }
static int vips_draw_rect_build( VipsObject *object ) { VipsDraw *draw = VIPS_DRAW( object ); VipsDrawink *drawink = VIPS_DRAWINK( object ); VipsArea *ink = VIPS_AREA( drawink->ink ); VipsDrawRect *draw_rect = (VipsDrawRect *) object; int left = draw_rect->left; int top = draw_rect->top; int width = draw_rect->width; int height = draw_rect->height; VipsRect image; VipsRect rect; VipsRect clip; if( VIPS_OBJECT_CLASS( vips_draw_rect_parent_class )->build( object ) ) return( -1 ); /* Also use a solid fill for very narrow unfilled rects. */ if( !draw_rect->fill && width > 2 && height > 2 ) return( vips_draw_rect( draw->image, ink->data, ink->n, left, top, width, 1, NULL ) || vips_draw_rect( draw->image, ink->data, ink->n, left + width - 1, top, 1, height, NULL ) || vips_draw_rect( draw->image, ink->data, ink->n, left, top + height - 1, width, 1, NULL ) || vips_draw_rect( draw->image, ink->data, ink->n, left, top, 1, height, NULL ) ); image.left = 0; image.top = 0; image.width = draw->image->Xsize; image.height = draw->image->Ysize; rect.left = left; rect.top = top; rect.width = width; rect.height = height; vips_rect_intersectrect( &rect, &image, &clip ); if( !vips_rect_isempty( &clip ) ) { VipsPel *to = VIPS_IMAGE_ADDR( draw->image, clip.left, clip.top ); VipsPel *q; int x, y; /* We plot the first line pointwise, then memcpy() it for the * subsequent lines. */ q = to; for( x = 0; x < clip.width; x++ ) { vips__drawink_pel( drawink, q ); q += draw->psize; } q = to + draw->lsize; for( y = 1; y < clip.height; y++ ) { memcpy( q, to, clip.width * draw->psize ); q += draw->lsize; } } 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 ); }