/** * im_region_buffer: * @reg: region to operate upon * @r: #Rect of pixels you need to be able to address * * The region is transformed so that at least @r pixels are available as a * memory buffer. * * Returns: 0 on success, or -1 for error. */ int im_region_buffer( REGION *reg, Rect *r ) { IMAGE *im = reg->im; Rect image; Rect clipped; im__region_check_ownership( reg ); /* Clip against image. */ image.top = 0; image.left = 0; image.width = im->Xsize; image.height = im->Ysize; im_rect_intersectrect( r, &image, &clipped ); /* Test for empty. */ if( im_rect_isempty( &clipped ) ) { im_error( "im_region_buffer", "%s", _( "valid clipped to nothing" ) ); return( -1 ); } /* Have we been asked to drop caches? We want to throw everything * away. * * If not, try to reuse the current buffer. */ if( reg->invalid ) { im_region_reset( reg ); if( !(reg->buffer = im_buffer_new( im, &clipped )) ) return( -1 ); } else { /* Don't call im_region_reset() ... we combine buffer unref * and new buffer ref in one call to reduce malloc/free * cycling. */ IM_FREEF( im_window_unref, reg->window ); if( !(reg->buffer = im_buffer_unref_ref( reg->buffer, im, &clipped )) ) return( -1 ); } /* Init new stuff. */ reg->valid = reg->buffer->area; reg->bpl = IM_IMAGE_SIZEOF_PEL( im ) * reg->buffer->area.width; reg->type = IM_REGION_BUFFER; reg->data = reg->buffer->buf; return( 0 ); }
/* Region should be a pixel buffer. On return, check * reg->buffer->done to see if there are pixels there already. Otherwise, you * need to calculate. */ int im_region_buffer( REGION *reg, Rect *r ) { IMAGE *im = reg->im; Rect image; Rect clipped; im__region_check_ownership( reg ); /* Clip against image. */ image.top = 0; image.left = 0; image.width = im->Xsize; image.height = im->Ysize; im_rect_intersectrect( r, &image, &clipped ); /* Test for empty. */ if( im_rect_isempty( &clipped ) ) { im_error( "im_region_buffer", _( "valid clipped to nothing" ) ); return( -1 ); } /* Already have stuff? */ if( reg->type == IM_REGION_BUFFER && im_rect_includesrect( ®->valid, &clipped ) && reg->buffer && !reg->buffer->invalid ) return( 0 ); /* Don't call im_region_reset() ... we combine buffer unref and new * buffer ref in one call to reduce malloc/free cycling. */ IM_FREEF( im_window_unref, reg->window ); if( !(reg->buffer = im_buffer_unref_ref( reg->buffer, im, &clipped )) ) return( -1 ); /* Init new stuff. */ reg->valid = reg->buffer->area; reg->bpl = IM_IMAGE_SIZEOF_PEL( im ) * reg->buffer->area.width; reg->type = IM_REGION_BUFFER; reg->data = reg->buffer->buf; return( 0 ); }
/** * im_draw_mask: * @image: image to draw on * @x: draw mask here * @y: draw mask here * @ink: value to draw * @mask_im: mask of 0/255 values showing where to plot * * Draw a mask on the image. @mask_im is a monochrome 8-bit image with 0/255 * for transparent or @ink coloured points. Intermediate values blend the ink * with the pixel. Use with im_text() to draw text on an image. * * @ink is an array of bytes * containing a valid pixel for the image's format. * It must have at least IM_IMAGE_SIZEOF_PEL( @image ) bytes. * * See also: im_draw_circle(), im_text(), im_draw_line_user(). * * Returns: 0 on success, or -1 on error. */ int im_draw_mask( VipsImage *image, VipsImage *mask_im, int x, int y, PEL *ink ) { Mask *mask; if( !(mask = mask_new( image, x, y, ink, mask_im )) ) return( -1 ); /* Any points to plot? */ if( im_rect_isempty( &mask->image_clip ) ) { mask_free( mask ); return( 0 ); } /* Loop through image plotting where required. */ switch( image->Coding ) { case IM_CODING_LABQ: if( mask_draw_labq( mask ) ) { mask_free( mask ); return( 0 ); } break; case IM_CODING_NONE: if( mask_draw( mask ) ) { mask_free( mask ); return( 0 ); } break; default: g_assert( 0 ); } mask_free( mask ); return( 0 ); }
/** * im_draw_image: * @image: image to draw on * @sub: image to draw * @x: position to insert * @y: position to insert * * Draw @sub on top of @image at position @x, @y. The two images must have the * same * Coding. If @sub has 1 band, the bands will be duplicated to match the * number of bands in @image. @sub will be converted to @image's format, see * im_clip2fmt(). * * See also: im_insert(). * * Returns: 0 on success, or -1 on error. */ int im_draw_image( VipsImage *image, VipsImage *sub, int x, int y ) { Rect br, sr, clip; PEL *p, *q; int z; /* Make rects for main and sub and clip. */ br.left = 0; br.top = 0; br.width = image->Xsize; br.height = image->Ysize; sr.left = x; sr.top = y; sr.width = sub->Xsize; sr.height = sub->Ysize; im_rect_intersectrect( &br, &sr, &clip ); if( im_rect_isempty( &clip ) ) return( 0 ); if( !(sub = im__inplace_base( "im_draw_image", image, sub, image )) || im_rwcheck( image ) || im_incheck( sub ) ) return( -1 ); /* Loop, memcpying sub to main. */ p = (PEL *) IM_IMAGE_ADDR( sub, clip.left - x, clip.top - y ); q = (PEL *) IM_IMAGE_ADDR( image, clip.left, clip.top ); for( z = 0; z < clip.height; z++ ) { memcpy( (char *) q, (char *) p, clip.width * IM_IMAGE_SIZEOF_PEL( sub ) ); p += IM_IMAGE_SIZEOF_LINE( sub ); q += IM_IMAGE_SIZEOF_LINE( image ); } return( 0 ); }
/** * im_draw_smudge: * @image: image to smudge * @left: area to smudge * @top: area to smudge * @width: area to smudge * @height: area to smudge * * Smudge a section of @image. Each pixel in the area @left, @top, @width, * @height is replaced by the average of the surrounding 3x3 pixels. * * This an inplace operation, so @image is changed. It does not thread and will * not work well as part of a pipeline. On 32-bit machines it will be limited * to 2GB images. * * See also: im_draw_line(). * * Returns: 0 on success, or -1 on error. */ int im_draw_smudge( VipsImage *im, int left, int top, int width, int height ) { Rect area, image, clipped; IMAGE *t[2]; area.left = left; area.top = top; area.width = width; area.height = height; image.left = 0; image.top = 0; image.width = im->Xsize; image.height = im->Ysize; im_rect_intersectrect( &area, &image, &clipped ); if( im_rect_isempty( &clipped ) ) return( 0 ); if( !blur ) { blur = im_create_imaskv( "im_draw_smudge", 3, 1, 1, 4, 1 ); blur->scale = 6; } if( !(t[0] = im_open( "im_draw_smudge", "p" )) ) return( -1 ); if( !(t[1] = im_open_local( t[0], "im_draw_smudge", "p" )) || im_convsep( im, t[0], blur ) || im_extract_area( t[0], t[1], clipped.left, clipped.top, clipped.width, clipped.height ) || im_draw_image( im, t[1], clipped.left, clipped.top ) ) { im_close( t[0] ); return( -1 ); } im_close( t[0] ); return( 0 ); }
static int affinei_gen( REGION *or, void *seq, void *a, void *b ) { REGION *ir = (REGION *) seq; const IMAGE *in = (IMAGE *) a; const Affine *affine = (Affine *) b; 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 Rect *r = &or->valid; const int le = r->left; const int ri = IM_RECT_RIGHT( r ); const int to = r->top; const int bo = IM_RECT_BOTTOM( r ); const Rect *iarea = &affine->trn.iarea; const Rect *oarea = &affine->trn.oarea; int ps = IM_IMAGE_SIZEOF_PEL( in ); int x, y, z; Rect 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. */ im__transform_invert_rect( &affine->trn, &want, &need ); /* Now go to space (2) above. */ need.left += iarea->left; need.top += iarea->top; /* Add a border for interpolation. Plus one for rounding errors. */ im_rect_marginadjust( &need, window_offset + 1 ); /* Clip against the size of (2). */ image.left = 0; image.top = 0; image.width = in->Xsize; image.height = in->Ysize; im_rect_intersectrect( &need, &image, &clipped ); /* Outside input image? All black. */ if( im_rect_isempty( &clipped ) ) { im_region_black( or ); return( 0 ); } /* We do need some pixels from the input image to make our output - * ask for them. */ if( im_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.dx; const double oy = y + oarea->top - affine->trn.dy; /* Continuous cods in input space. */ double ix, iy; PEL *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; q = (PEL *) IM_REGION_ADDR( or, le, y ); for( x = le; x < ri; x++ ) { int fx, fy; fx = FLOOR( ix ); fy = 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 ); }
/** * im_draw_rect: * @image: image to draw on * @left: area to paint * @top: area to paint * @width: area to paint * @height: area to paint * @fill: fill the rect * @ink: paint with this colour * * Paint pixels within @left, @top, @width, @height in @image with @ink. If * @fill is zero, just paint a 1-pixel-wide outline. * * See also: im_draw_circle(). * * Returns: 0 on success, or -1 on error. */ int im_draw_rect( IMAGE *image, int left, int top, int width, int height, int fill, VipsPel *ink ) { Rect im, rect, clipped; Draw draw; if( !fill ) return( im_draw_rect( image, left, top, width, 1, 1, ink ) || im_draw_rect( image, left + width - 1, top, 1, height, 1, ink ) || im_draw_rect( image, left, top + height - 1, width, 1, 1, ink ) || im_draw_rect( image, left, top, 1, height, 1, ink ) ); int x, y; VipsPel *to; VipsPel *q; /* Find area we plot. */ im.left = 0; im.top = 0; im.width = image->Xsize; im.height = image->Ysize; rect.left = left; rect.top = top; rect.width = width; rect.height = height; im_rect_intersectrect( &rect, &im, &clipped ); /* Any points left to plot? */ if( im_rect_isempty( &clipped ) ) return( 0 ); if( im_check_coding_known( "im_draw_rect", image ) || !im__draw_init( &draw, image, ink ) ) return( -1 ); /* We plot the first line pointwise, then memcpy() it for the * subsequent lines. */ to = IM_IMAGE_ADDR( image, clipped.left, clipped.top ); q = to; for( x = 0; x < clipped.width; x++ ) { im__draw_pel( &draw, q ); q += draw.psize; } q = to + draw.lsize; for( y = 1; y < clipped.height; y++ ) { memcpy( q, to, clipped.width * draw.psize ); q += draw.lsize; } im__draw_free( &draw ); return( 0 ); }
/** * im_region_image: * @reg: region to operate upon * @r: #Rect of pixels you need to be able to address * * The region is transformed so that at least @r pixels are available directly * from the image. The image needs to be a memory buffer or represent a file * on disc that has been mapped or can be mapped. * * Returns: 0 on success, or -1 for error. */ int im_region_image( REGION *reg, Rect *r ) { Rect image; Rect clipped; /* Sanity check. */ im__region_check_ownership( reg ); /* Clip against image. */ image.top = 0; image.left = 0; image.width = reg->im->Xsize; image.height = reg->im->Ysize; im_rect_intersectrect( r, &image, &clipped ); /* Test for empty. */ if( im_rect_isempty( &clipped ) ) { im_error( "im_region_image", "%s", _( "valid clipped to nothing" ) ); return( -1 ); } if( reg->im->data ) { /* We have the whole image available ... easy! */ im_region_reset( reg ); /* We can't just set valid = clipped, since this may be an * incompletely calculated memory buffer. Just set valid to r. */ reg->valid = clipped; reg->bpl = IM_IMAGE_SIZEOF_LINE( reg->im ); reg->data = reg->im->data + (gint64) clipped.top * IM_IMAGE_SIZEOF_LINE( reg->im ) + clipped.left * IM_IMAGE_SIZEOF_PEL( reg->im ); reg->type = IM_REGION_OTHER_IMAGE; } else if( reg->im->dtype == IM_OPENIN ) { /* No complete image data ... but we can use a rolling window. */ if( reg->type != IM_REGION_WINDOW || !reg->window || reg->window->top > clipped.top || reg->window->top + reg->window->height < clipped.top + clipped.height ) { im_region_reset( reg ); if( !(reg->window = im_window_ref( reg->im, clipped.top, clipped.height )) ) return( -1 ); reg->type = IM_REGION_WINDOW; } /* Note the area the window actually represents. */ reg->valid.left = 0; reg->valid.top = reg->window->top; reg->valid.width = reg->im->Xsize; reg->valid.height = reg->window->height; reg->bpl = IM_IMAGE_SIZEOF_LINE( reg->im ); reg->data = reg->window->data; } else { im_error( "im_region_image", "%s", _( "bad image type" ) ); return( -1 ); } return( 0 ); }
/* Smear a section of an IMAGE. As above, but shift it left a bit. */ int im_smear( IMAGE *im, int ix, int iy, Rect *r ) { int x, y, a, b, c; int ba = im->Bands; int el = ba * im->Xsize; Rect area, image, clipped; double total[ 256 ]; if( im_rwcheck( im ) ) return( -1 ); /* Don't do the margins. */ area = *r; area.left += ix; area.top += iy; image.left = 0; image.top = 0; image.width = im->Xsize; image.height = im->Ysize; im_rect_marginadjust( &image, -1 ); image.left--; im_rect_intersectrect( &area, &image, &clipped ); /* Any left? */ if( im_rect_isempty( &clipped ) ) return( 0 ); /* What we do for each type. */ #define SMEAR(TYPE) \ for( y = clipped.top; y < clipped.top + clipped.height; y++ ) \ for( x = clipped.left; \ x < clipped.left + clipped.width; x++ ) { \ TYPE *to = (TYPE *) im->data + x * ba + y * el; \ TYPE *from = to - el; \ TYPE *f; \ \ for( a = 0; a < ba; a++ ) \ total[a] = 0.0; \ \ for( a = 0; a < 3; a++ ) { \ f = from; \ for( b = 0; b < 3; b++ ) \ for( c = 0; c < ba; c++ ) \ total[c] += *f++; \ from += el; \ } \ \ for( a = 0; a < ba; a++ ) \ to[a] = (40 * (double) to[a+ba] + total[a]) \ / 49.0; \ } /* Loop through the remaining pixels. */ switch( im->BandFmt ) { case IM_BANDFMT_UCHAR: SMEAR(unsigned char); break; case IM_BANDFMT_CHAR: SMEAR(char); break; case IM_BANDFMT_USHORT: SMEAR(unsigned short); break; case IM_BANDFMT_SHORT: SMEAR(short); break; case IM_BANDFMT_UINT: SMEAR(unsigned int); break; case IM_BANDFMT_INT: SMEAR(int); break; case IM_BANDFMT_FLOAT: SMEAR(float); break; case IM_BANDFMT_DOUBLE: SMEAR(double); break; /* Do complex types too. Just treat as float and double, but with * twice the number of bands. */ case IM_BANDFMT_COMPLEX: /* Twice number of bands: double size and bands. */ ba *= 2; el *= 2; SMEAR(float); break; case IM_BANDFMT_DPCOMPLEX: /* Twice number of bands: double size and bands. */ ba *= 2; el *= 2; SMEAR(double); break; default: im_error( "im_smear", "%s", _( "unknown band format" ) ); return( -1 ); } return( 0 ); }
static int replicate_gen( REGION *or, void *seq, void *a, void *b ) { REGION *ir = (REGION *) seq; IMAGE *in = (IMAGE *) a; Rect *r = &or->valid; int twidth = in->Xsize; int theight = in->Ysize; int x, y; Rect 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( im_rect_includesrect( &tile, r ) ) { Rect irect; /* Translate request to input space. */ irect = *r; irect.left -= xs; irect.top -= ys; if( im_prepare( ir, &irect ) ) return( -1 ); if( im_region_region( or, ir, r, irect.left, irect.top ) ) return( -1 ); return( 0 ); } for( y = ys; y < IM_RECT_BOTTOM( r ); y += theight ) for( x = xs; x < IM_RECT_RIGHT( r ); x += twidth ) { Rect 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. */ im_rect_intersectrect( &tile, r, &paint ); /* Translate back to ir coordinates. */ paint.left -= x; paint.top -= y; g_assert( !im_rect_isempty( &paint ) ); /* Render into or. */ if( im_prepare_to( ir, or, &paint, paint.left + x, paint.top + y ) ) return( -1 ); } return( 0 ); }