/* Set output area of trn so that it just holds all of our input pels. */ void im__transform_set_area( Transformation *trn ) { double xA, xB, xC, xD; double yA, yB, yC, yD; int xmin, xmax, ymin, ymax; im__transform_forward( trn, trn->iarea.left, trn->iarea.top, &xA, &yA ); im__transform_forward( trn, IM_RECT_RIGHT( &trn->iarea ) - 1, trn->iarea.top, &xB, &yB ); im__transform_forward( trn, trn->iarea.left, IM_RECT_BOTTOM( &trn->iarea ) - 1, &xC, &yC ); im__transform_forward( trn, IM_RECT_RIGHT( &trn->iarea ) - 1, IM_RECT_BOTTOM( &trn->iarea ) - 1, &xD, &yD ); xmin = IM_MIN( xA, IM_MIN( xB, IM_MIN( xC, xD ) ) ); ymin = IM_MIN( yA, IM_MIN( yB, IM_MIN( yC, yD ) ) ); xmax = IM_MAX( xA, IM_MAX( xB, IM_MAX( xC, xD ) ) ); ymax = IM_MAX( yA, IM_MAX( yB, IM_MAX( yC, yD ) ) ); trn->oarea.left = xmin; trn->oarea.top = ymin; trn->oarea.width = xmax - xmin + 1; trn->oarea.height = ymax - ymin + 1; }
/* Transform a rect using a point transformer. */ static void transform_rect( const Transformation *trn, transform_fn transform, const Rect *in, /* In input space */ Rect *out ) /* In output space */ { double x1, y1; /* Map corners */ double x2, y2; double x3, y3; double x4, y4; double left, right, top, bottom; /* Map input Rect. */ transform( trn, in->left, in->top, &x1, &y1 ); transform( trn, in->left, IM_RECT_BOTTOM( in ), &x3, &y3 ); transform( trn, IM_RECT_RIGHT( in ), in->top, &x2, &y2 ); transform( trn, IM_RECT_RIGHT( in ), IM_RECT_BOTTOM( in ), &x4, &y4 ); /* Find bounding box for these four corners. Round-to-nearest to try * to stop rounding errors growing images. */ left = IM_MIN( x1, IM_MIN( x2, IM_MIN( x3, x4 ) ) ); right = IM_MAX( x1, IM_MAX( x2, IM_MAX( x3, x4 ) ) ); top = IM_MIN( y1, IM_MIN( y2, IM_MIN( y3, y4 ) ) ); bottom = IM_MAX( y1, IM_MAX( y2, IM_MAX( y3, y4 ) ) ); out->left = IM_RINT( left ); out->top = IM_RINT( top ); out->width = IM_RINT( right - left ); out->height = IM_RINT( bottom - top ); }
/* Build per-call state. */ static Overlapping * build_tbstate( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) { Overlapping *ovlap; if( !(ovlap = im__build_mergestate( "im_tbmerge", ref, sec, out, dx, dy, mwidth )) ) return( NULL ); /* Select blender. */ switch( ovlap->ref->Coding ) { case IM_CODING_LABQ: ovlap->blend = tb_blend_labpack; break; case IM_CODING_NONE: ovlap->blend = tb_blend; break; default: im_error( "im_tbmerge", "%s", _( "unknown coding type" ) ); return( NULL ); } /* Find the parts of output which come just from ref and just from sec. */ ovlap->rpart = ovlap->rarea; ovlap->spart = ovlap->sarea; ovlap->rpart.height -= ovlap->overlap.height; ovlap->spart.top += ovlap->overlap.height; ovlap->spart.height -= ovlap->overlap.height; /* Is there too much overlap? ie. bottom edge of ref image is greater * than bottom edge of sec image, or top edge of ref > top edge of * sec. */ if( IM_RECT_BOTTOM( &ovlap->rarea ) > IM_RECT_BOTTOM( &ovlap->sarea ) || ovlap->rarea.top > ovlap->sarea.top ) { im_error( "im_tbmerge", "%s", _( "too much overlap" ) ); return( NULL ); } /* Max number of pixels we may have to blend together. */ ovlap->blsize = ovlap->overlap.width; return( ovlap ); }
/* Generate function. */ static int make_vert_gen( REGION *or, void *seq, void *a, void *b ) { IMAGE *in = (IMAGE *) a; Rect *r = &or->valid; int le = r->left; int to = r->top; int ri = IM_RECT_RIGHT( r ); int bo = IM_RECT_BOTTOM( r ); int nb = in->Bands; int x, y, z; for( y = to; y < bo; y++ ) { VipsPel *q = IM_REGION_ADDR( or, le, y ); VipsPel *p = IM_IMAGE_ADDR( in, 0, y ); switch( in->BandFmt ) { case IM_BANDFMT_UCHAR: VERT( unsigned char ); break; case IM_BANDFMT_CHAR: VERT( signed char ); break; case IM_BANDFMT_USHORT: VERT( unsigned short ); break; case IM_BANDFMT_SHORT: VERT( signed short ); break; case IM_BANDFMT_UINT: VERT( unsigned int ); break; case IM_BANDFMT_INT: VERT( signed int ); break; case IM_BANDFMT_FLOAT: VERT( float ); break; case IM_BANDFMT_DOUBLE: VERT( double ); break; default: g_assert( 0 ); } } return( 0 ); }
/* Scroll to make an xywh area visible. If the area is larger than the * viewport, position the view at the bottom left if the xywh area ... * this is usually right for workspaces. */ void workspaceview_scroll( Workspaceview *wview, int x, int y, int w, int h ) { GtkAdjustment *hadj = gtk_scrolled_window_get_hadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW( wview->window ) ); Rect *vp = &wview->vp; int nx, ny; nx = hadj->value; if( x + w > IM_RECT_RIGHT( vp ) ) nx = IM_MAX( 0, (x + w) - vp->width ); if( x < nx ) nx = x; ny = vadj->value; if( y + h > IM_RECT_BOTTOM( vp ) ) ny = IM_MAX( 0, (y + h) - vp->height ); if( y < ny ) ny = y; #ifdef DEBUG printf( "workspaceview_scroll: x=%d, y=%d, w=%d, h=%d, " "nx = %d, ny = %d\n", x, y, w, h, nx, ny ); #endif /*DEBUG*/ adjustments_set_value( hadj, vadj, nx, ny ); }
/* Rotate a small piece. */ static int rot270_gen( REGION *or, void *seq, void *a, void *b ) { REGION *ir = (REGION *) seq; IMAGE *in = (IMAGE *) a; /* Output area. */ Rect *r = &or->valid; int le = r->left; int ri = IM_RECT_RIGHT(r); int to = r->top; int bo = IM_RECT_BOTTOM(r); int x, y, i; /* Pixel geometry. */ int ps, ls; /* Find the area of the input image we need. */ Rect need; need.left = in->Xsize - bo; need.top = le; need.width = r->height; need.height = r->width; if( im_prepare( ir, &need ) ) return( -1 ); /* Find PEL size and line skip for ir. */ ps = IM_IMAGE_SIZEOF_PEL( in ); ls = IM_REGION_LSKIP( ir ); /* Rotate the bit we now have. */ for( y = to; y < bo; y++ ) { /* Start of this output line. */ PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); /* Corresponding position in ir. */ PEL *p = (PEL *) IM_REGION_ADDR( ir, need.left + need.width - (y - to) - 1, need.top ); for( x = le; x < ri; x++ ) { for( i = 0; i < ps; i++ ) q[i] = p[i]; q += ps; p += ls; } } return( 0 ); }
static int vips_rot180_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsImage *in = (VipsImage *) a; /* Output area. */ VipsRect *r = &or->valid; int le = r->left; int ri = IM_RECT_RIGHT(r); int to = r->top; int bo = IM_RECT_BOTTOM(r); int x, y; /* Pixel geometry. */ int ps; /* Find the area of the input image we need. */ Rect need; need.left = in->Xsize - ri; need.top = in->Ysize - bo; need.width = r->width; need.height = r->height; if( vips_region_prepare( ir, &need ) ) return( -1 ); /* Find PEL size and line skip for ir. */ ps = VIPS_IMAGE_SIZEOF_PEL( in ); /* Rotate the bit we now have. */ for( y = to; y < bo; y++ ) { /* Start of this output line. */ VipsPel *q = VIPS_REGION_ADDR( or, le, y ); /* Corresponding position in ir. */ VipsPel *p = VIPS_REGION_ADDR( ir, need.left + need.width - 1, need.top + need.height - (y - to) - 1 ); /* Blap across! */ for( x = le; x < ri; x++ ) { memcpy( q, p, ps ); q += ps; p -= ps; } } return( 0 ); }
/* Fastcor generate function. */ static int fastcor_gen( REGION *or, void *seq, void *a, void *b ) { REGION *ir = (REGION *) seq; IMAGE *ref = (IMAGE *) b; Rect irect; Rect *r = &or->valid; int le = r->left; int to = r->top; int bo = IM_RECT_BOTTOM(r); int ri = IM_RECT_RIGHT(r); int x, y, i, j; int lsk; /* What part of ir do we need? */ irect.left = or->valid.left; irect.top = or->valid.top; irect.width = or->valid.width + ref->Xsize - 1; irect.height = or->valid.height + ref->Ysize - 1; if( im_prepare( ir, &irect ) ) return( -1 ); lsk = IM_REGION_LSKIP( ir ); /* Loop over or. */ for( y = to; y < bo; y++ ) { PEL *a = (PEL *) IM_REGION_ADDR( ir, le, y ); unsigned int *q = (unsigned int *) IM_REGION_ADDR( or, le, y ); for( x = le; x < ri; x++ ) { int sum = 0; PEL *b = (PEL *) ref->data; PEL *a1 = a; for( j = 0; j < ref->Ysize; j++ ) { PEL *a2 = a1; for( i = 0; i < ref->Xsize; i++ ) { int t = *b++ - *a2++; sum += t * t; } a1 += lsk; } *q++ = sum; a += 1; } } return( 0 ); }
/* Flip a small area. */ static int flip_gen( REGION *or, void *seq, void *a, void *b ) { REGION *ir = (REGION *) seq; Rect *r = &or->valid; Rect in; char *p, *q; int x, y, z; int le = r->left; int ri = IM_RECT_RIGHT(r); int to = r->top; int bo = IM_RECT_BOTTOM(r); int ps = IM_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 = IM_RECT_RIGHT( &in ) - 1; /* Ask for input we need. */ if( im_prepare( ir, &in ) ) return( -1 ); /* Loop, copying and reversing lines. */ for( y = to; y < bo; y++ ) { p = IM_REGION_ADDR( ir, lastx, y ); q = IM_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 ); }
static void workspaceview_child_size_cb( Columnview *cview, GtkAllocation *allocation, Workspaceview *wview ) { Workspace *ws = WORKSPACE( VOBJECT( wview )->iobject ); Workspacegroup *wsg = workspace_get_workspacegroup( ws ); int right, bottom; g_assert( IS_WORKSPACEVIEW( wview ) ); /* Compute a new bounding box for our children. */ wview->bounding.left = 0; wview->bounding.top = 0; wview->bounding.width = 0; wview->bounding.height = 0; (void) view_map( VIEW( wview ), (view_map_fn) workspaceview_child_size_sub, &wview->bounding, NULL ); wview->bounding.width += 1000; wview->bounding.height += 1000; #ifdef DEBUG { Column *col = COLUMN( VOBJECT( cview )->iobject ); printf( "workspaceview_child_size_cb: cview %s " "bb left=%d, top=%d, width=%d, height=%d\n", IOBJECT( col )->name, wview->bounding.left, wview->bounding.top, wview->bounding.width, wview->bounding.height ); } #endif /*DEBUG*/ /* Resize our fixed if necessary. */ right = IM_RECT_RIGHT( &wview->bounding ); bottom = IM_RECT_BOTTOM( &wview->bounding ); if( right != wview->width || bottom != wview->height ) { gtk_widget_set_size_request( GTK_WIDGET( wview->fixed ), right, bottom ); /* Update the model hints ... it uses bounding to position * loads and saves. */ ws->area = wview->bounding; filemodel_set_offset( FILEMODEL( wsg ), ws->area.left, ws->area.top ); } }
/* Generate function. */ static int make_horz_gen( REGION *or, void *seq, void *a, void *b ) { IMAGE *in = (IMAGE *) a; Rect *r = &or->valid; int le = r->left; int to = r->top; int ri = IM_RECT_RIGHT( r ); int bo = IM_RECT_BOTTOM( r ); int nb = in->Bands; int lsk = IM_REGION_LSKIP( or ); int ht = or->im->Ysize; int x, y, z; for( x = le; x < ri; x++ ) { VipsPel *q = IM_REGION_ADDR( or, x, to ); VipsPel *p = IM_IMAGE_ADDR( in, x, 0 ); switch( in->BandFmt ) { case IM_BANDFMT_UCHAR: HORZ( unsigned char ); break; case IM_BANDFMT_CHAR: HORZ( signed char ); break; case IM_BANDFMT_USHORT: HORZ( unsigned short ); break; case IM_BANDFMT_SHORT: HORZ( signed short ); break; case IM_BANDFMT_UINT: HORZ( unsigned int ); break; case IM_BANDFMT_INT: HORZ( signed int ); break; case IM_BANDFMT_FLOAT: HORZ( float ); break; case IM_BANDFMT_DOUBLE: HORZ( double ); break; default: g_assert( 0 ); } } return( 0 ); }
/* Fetch one pixel at a time ... good for very large shrinks. */ static int point_shrink_gen( REGION *or, void *seq, void *a, void *b ) { REGION *ir = (REGION *) seq; IMAGE *in = (IMAGE *) a; SubsampleInfo *st = (SubsampleInfo *) b; Rect *r = &or->valid; int le = r->left; int ri = IM_RECT_RIGHT( r ); int to = r->top; int bo = IM_RECT_BOTTOM(r); int ps = IM_IMAGE_SIZEOF_PEL( in ); Rect s; int x, y; int k; /* Loop down the region. */ for( y = to; y < bo; y++ ) { char *q = IM_REGION_ADDR( or, le, y ); char *p; /* Loop across the region, in owidth sized pieces. */ for( x = le; x < ri; x++ ) { /* Ask for input. */ s.left = x * st->xshrink; s.top = y * st->yshrink; s.width = 1; s.height = 1; if( im_prepare( ir, &s ) ) return( -1 ); /* Append new pels to output. */ p = IM_REGION_ADDR( ir, s.left, s.top ); for( k = 0; k < ps; k++ ) q[k] = p[k]; q += ps; } } return( 0 ); }
/* Loop over the output region, filling with data from cache. */ static int tile_cache_fill( REGION *out, void *seq, void *a, void *b ) { REGION *in = (REGION *) seq; Read *read = (Read *) b; const int tw = read->tile_width; const int th = read->tile_height; Rect *r = &out->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( read->lock ); for( y = ys; y < IM_RECT_BOTTOM( r ); y += th ) for( x = xs; x < IM_RECT_RIGHT( r ); x += tw ) { Tile *tile; Rect tarea; Rect hit; if( !(tile = tile_find( read, in, x, y )) ) { g_mutex_unlock( read->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. */ im_rect_intersectrect( &tarea, r, &hit ); copy_region( tile->region, out, &hit ); } g_mutex_unlock( read->lock ); return( 0 ); }
/* Zerox generate function. */ static int zerox_gen( REGION *or, void *seq, void *a, void *b ) { REGION *ir = (REGION *) seq; IMAGE *in = (IMAGE *) a; int flag = GPOINTER_TO_INT( b ); Rect irect; Rect *r = &or->valid; /* Range of pixels we loop over. */ int le = r->left; int to = r->top; int bo = IM_RECT_BOTTOM( r ); int ba = in->Bands; int ne = ba * r->width; int i, y; /* We need to be able to see one pixel to the right. */ irect.top = r->top; irect.left = r->left; irect.width = r->width + 1; irect.height = r->height; if( im_prepare( ir, &irect ) ) return( -1 ); for( y = to; y < bo; y++ ) { PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); switch( in->BandFmt ) { case IM_BANDFMT_CHAR: LOOP( signed char ); break; case IM_BANDFMT_SHORT: LOOP( signed short ); break; case IM_BANDFMT_INT: LOOP( signed int ); break; case IM_BANDFMT_FLOAT: LOOP( float ); break; case IM_BANDFMT_DOUBLE: LOOP( double ); break; default: g_assert( 0 ); } } return( 0 ); }
/* Copy rect from from to to. */ static void copy_region( REGION *from, REGION *to, Rect *area ) { int y; /* Area should be inside both from and to. */ g_assert( im_rect_includesrect( &from->valid, area ) ); g_assert( im_rect_includesrect( &to->valid, area ) ); /* Loop down common area, copying. */ for( y = area->top; y < IM_RECT_BOTTOM( area ); y++ ) { PEL *p = (PEL *) IM_REGION_ADDR( from, area->left, y ); PEL *q = (PEL *) IM_REGION_ADDR( to, area->left, y ); memcpy( q, p, IM_IMAGE_SIZEOF_PEL( from->im ) * area->width ); } }
/* Bandjoin generate function. */ static int bandjoin_gen( REGION *or, void *seq, void *a, void *b ) { REGION **ir = (REGION **) seq; Rect *r = &or->valid; int le = r->left; int ri = IM_RECT_RIGHT(r); int to = r->top; int bo = IM_RECT_BOTTOM(r); int x, y, z; int i1s = IM_IMAGE_SIZEOF_PEL( ir[0]->im ); int i2s = IM_IMAGE_SIZEOF_PEL( ir[1]->im ); /* Ask for input we need. */ if( im_prepare( ir[0], r ) ) return( -1 ); if( im_prepare( ir[1], r ) ) return( -1 ); /* Perform join. */ for( y = to; y < bo; y++ ) { PEL *i1 = (PEL *) IM_REGION_ADDR( ir[0], le, y ); PEL *i2 = (PEL *) IM_REGION_ADDR( ir[1], le, y ); PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); for( x = le; x < ri; x++ ) { /* Copy bytes from first file. */ for( z = 0; z < i1s; z++ ) *q++ = *i1++; /* Copy bytes from in2. */ for( z = 0; z < i2s; z++ ) *q++ = *i2++; } } return( 0 ); }
static int find_hist( REGION *reg, void *seq, void *a, void *b ) { Histogram *hist = (Histogram *) seq; Rect *r = ®->valid; IMAGE *im = reg->im; int le = r->left; int to = r->top; int bo = IM_RECT_BOTTOM(r); int nb = im->Bands; int max_val = im->BandFmt == IM_BANDFMT_UCHAR ? 256 : 65536; int scale = max_val / hist->bins; int x, y, z, i; int index[3]; /* Fill these with dimensions, backwards. */ index[0] = index[1] = index[2] = 0; /* Accumulate! */ for( y = to; y < bo; y++ ) { char *line = IM_REGION_ADDR( reg, le, y ); switch( im->BandFmt ) { case IM_BANDFMT_UCHAR: LOOP( unsigned char ); break; case IM_BANDFMT_USHORT: LOOP( unsigned char ); break; default: error_exit( "panic #34847563245" ); } } return( 0 ); }
/* Do a map. */ static int maplut_gen( REGION *or, void *vseq, void *a, void *b ) { Seq *seq = (Seq *) vseq; IMAGE *in = (IMAGE *) a; LutInfo *st = (LutInfo *) b; REGION *ir = seq->ir; Rect *r = &or->valid; int le = r->left; int to = r->top; int bo = IM_RECT_BOTTOM(r); int np = r->width; /* Pels across region */ int ne = IM_REGION_N_ELEMENTS( or ); /* Number of elements */ int x, y, z, i; /* Get input ready. */ if( im_prepare( ir, r ) ) return( -1 ); /* Process! */ if( st->nb == 1 ) /* One band lut. */ outer_switch( loop1, loop1c, loop1g, loop1cg ) else /* Many band lut. */ if( ir->im->Bands == 1 ) /* ... but 1 band input. */ outer_switch( loop1m, loop1cm, loop1gm, loop1cgm ) else outer_switch( loop, loopc, loopg, loopcg ) return( 0 ); }
/* Generate function. */ static int make_xy_gen( REGION *or, void *seq, void *a, void *b ) { Rect *r = &or->valid; int le = r->left; int to = r->top; int ri = IM_RECT_RIGHT( r ); int bo = IM_RECT_BOTTOM( r ); int x, y; for( y = to; y < bo; y++ ) { unsigned int *q = (unsigned int *) IM_REGION_ADDR( or, le, y ); for( x = le; x < ri; x++ ) { q[0] = x; q[1] = y; q += 2; } } return( 0 ); }
/* Make sure we have first/last for this area. */ static int make_firstlast( MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) { REGION *rir = inf->rir; REGION *sir = inf->sir; Rect rr, sr; int y, yr, ys; int missing; /* We're going to build first/last ... lock it from other generate * threads. In fact it's harmless if we do get two writers, but we may * avoid duplicating work. */ g_mutex_lock( ovlap->fl_lock ); /* Do we already have first/last for this area? Bail out if we do. */ missing = 0; for( y = oreg->top; y < IM_RECT_BOTTOM( oreg ); y++ ) { const int j = y - ovlap->overlap.top; const int first = ovlap->first[j]; if( first < 0 ) { missing = 1; break; } } if( !missing ) { /* No work to do! */ g_mutex_unlock( ovlap->fl_lock ); return( 0 ); } /* Entire width of overlap in ref for scan-lines we want. */ rr.left = ovlap->overlap.left; rr.top = oreg->top; rr.width = ovlap->overlap.width; rr.height = oreg->height; rr.left -= ovlap->rarea.left; rr.top -= ovlap->rarea.top; /* Entire width of overlap in sec for scan-lines we want. */ sr.left = ovlap->overlap.left; sr.top = oreg->top; sr.width = ovlap->overlap.width; sr.height = oreg->height; sr.left -= ovlap->sarea.left; sr.top -= ovlap->sarea.top; #ifdef DEBUG printf( "im__lrmerge: making first/last for areas:\n" ); printf( "ref: left = %d, top = %d, width = %d, height = %d\n", rr.left, rr.top, rr.width, rr.height ); printf( "sec: left = %d, top = %d, width = %d, height = %d\n", sr.left, sr.top, sr.width, sr.height ); #endif /* Make pixels. */ if( im_prepare( rir, &rr ) || im_prepare( sir, &sr ) ) { g_mutex_unlock( ovlap->fl_lock ); return( -1 ); } /* Make first/last cache. */ for( y = oreg->top, yr = rr.top, ys = sr.top; y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { const int j = y - ovlap->overlap.top; int *first = &ovlap->first[j]; int *last = &ovlap->last[j]; /* Done this line already? */ if( *first < 0 ) { /* Search for start/end of overlap on this scan-line. */ if( find_first( sir, first, sr.left, ys, sr.width ) || find_last( rir, last, rr.left, yr, rr.width ) ) { g_mutex_unlock( ovlap->fl_lock ); return( -1 ); } /* Translate to output space. */ *first += ovlap->sarea.left; *last += ovlap->rarea.left; /* Clip to maximum blend width, if necessary. */ if( ovlap->mwidth >= 0 && *last - *first > ovlap->mwidth ) { int shrinkby = (*last - *first) - ovlap->mwidth; *first += shrinkby / 2; *last -= shrinkby / 2; } } } g_mutex_unlock( ovlap->fl_lock ); return( 0 ); }
/* Dilate! */ static int dilate_gen( REGION *or, void *vseq, void *a, void *b ) { SeqInfo *seq = (SeqInfo *) vseq; INTMASK *msk = (INTMASK *) b; REGION *ir = seq->ir; int *soff = seq->soff; int *coff = seq->coff; Rect *r = &or->valid; Rect s; int le = r->left; int to = r->top; int bo = IM_RECT_BOTTOM(r); int sz = IM_REGION_N_ELEMENTS( or ); int *t; int x, y; int result, i; /* Prepare the section of the input image we need. A little larger * than the section of the output image we are producing. */ s = *r; s.width += msk->xsize - 1; s.height += msk->ysize - 1; if( im_prepare( ir, &s ) ) return( -1 ); #ifdef DEBUG printf( "erode_gen: preparing %dx%d pixels\n", s.width, s.height ); #endif /*DEBUG*/ /* Scan mask, building offsets we check when processing. Only do this * if the bpl has changed since the previous im_prepare(). */ if( seq->last_bpl != IM_REGION_LSKIP( ir ) ) { seq->last_bpl = IM_REGION_LSKIP( ir ); seq->ss = 0; seq->cs = 0; for( t = msk->coeff, y = 0; y < msk->ysize; y++ ) for( x = 0; x < msk->xsize; x++, t++ ) switch( *t ) { case 255: soff[seq->ss++] = IM_REGION_ADDR( ir, x + le, y + to ) - IM_REGION_ADDR( ir, le, to ); break; case 128: break; case 0: coff[seq->cs++] = IM_REGION_ADDR( ir, x + le, y + to ) - IM_REGION_ADDR( ir, le, to ); break; default: im_error( "im_dilate", _( "bad mask element (%d " "should be 0, 128 or 255)" ), *t ); return( -1 ); } } /* Dilate! */ for( y = to; y < bo; y++ ) { PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); /* Loop along line. */ for( x = 0; x < sz; x++, q++, p++ ) { /* Search for a hit on the set list. */ result = 0; for( i = 0; i < seq->ss; i++ ) if( p[soff[i]] ) { /* Found a match! */ result = 255; break; } /* No set pixels ... search for a hit in the clear * pixels. */ if( !result ) for( i = 0; i < seq->cs; i++ ) if( !p[coff[i]] ) { /* Found a match! */ result = 255; break; } *q = result; } } return( 0 ); }
/* Loop over a big region, filling it in many small pieces with threads. */ static int eval_to_region( REGION *or, im_threadgroup_t *tg ) { Rect *r = &or->valid; Rect image; int x, y; image.left = 0; image.top = 0; image.width = or->im->Xsize; image.height = or->im->Ysize; /* Note we'll be working to fill a contigious area. */ tg->inplace = 1; /* Loop over or, attaching to all sub-parts in turn. */ for( y = r->top; y < IM_RECT_BOTTOM( r ); y += tg->ph ) for( x = r->left; x < IM_RECT_RIGHT( r ); x += tg->pw ) { im_thread_t *thr; Rect pos; Rect clipped; /* thrs appear on idle when the child thread does * threadgroup_idle_add and hits the 'go' semaphore. */ thr = im_threadgroup_get( tg ); /* Set the position we want to generate with this * thread. Clip against the size of the image and the * space available in or. */ pos.left = x; pos.top = y; pos.width = tg->pw; pos.height = tg->ph; im_rect_intersectrect( &pos, &image, &clipped ); im_rect_intersectrect( &clipped, r, &clipped ); /* Note params and start work. */ thr->oreg = or; thr->pos = clipped; thr->x = clipped.left; thr->y = clipped.top; im_threadgroup_trigger( thr ); /* Check for errors. */ if( im_threadgroup_iserror( tg ) ) { /* Don't kill threads yet ... we may want to * get some error stuff out of them. */ im_threadgroup_wait( tg ); return( -1 ); } } /* Wait for all threads to hit 'go' again. */ im_threadgroup_wait( tg ); if( im_threadgroup_iserror( tg ) ) return( -1 ); return( 0 ); }
/* Erode! */ static int erode_gen( REGION *or, void *vseq, void *a, void *b ) { MorphSequence *seq = (MorphSequence *) vseq; Morph *morph = (Morph *) b; INTMASK *mask = morph->mask; REGION *ir = seq->ir; int *soff = seq->soff; int *coff = seq->coff; Rect *r = &or->valid; Rect s; int le = r->left; int to = r->top; int bo = IM_RECT_BOTTOM(r); int sz = IM_REGION_N_ELEMENTS( or ); int *t; int x, y; int result, i; /* Prepare the section of the input image we need. A little larger * than the section of the output image we are producing. */ s = *r; s.width += mask->xsize - 1; s.height += mask->ysize - 1; if( im_prepare( ir, &s ) ) return( -1 ); #ifdef DEBUG printf( "erode_gen: preparing %dx%d@%dx%d pixels\n", s.width, s.height, s.left, s.top ); #endif /*DEBUG*/ /* Scan mask, building offsets we check when processing. Only do this * if the bpl has changed since the previous im_prepare(). */ if( seq->last_bpl != IM_REGION_LSKIP( ir ) ) { seq->last_bpl = IM_REGION_LSKIP( ir ); seq->ss = 0; seq->cs = 0; for( t = mask->coeff, y = 0; y < mask->ysize; y++ ) for( x = 0; x < mask->xsize; x++, t++ ) switch( *t ) { case 255: soff[seq->ss++] = IM_REGION_ADDR( ir, x + le, y + to ) - IM_REGION_ADDR( ir, le, to ); break; case 128: break; case 0: coff[seq->cs++] = IM_REGION_ADDR( ir, x + le, y + to ) - IM_REGION_ADDR( ir, le, to ); break; default: g_assert( 0 ); } } /* Erode! */ for( y = to; y < bo; y++ ) { VipsPel *p = IM_REGION_ADDR( ir, le, y ); VipsPel *q = IM_REGION_ADDR( or, le, y ); /* Loop along line. */ for( x = 0; x < sz; x++, q++, p++ ) { /* Check all set pixels are set. */ result = 255; for( i = 0; i < seq->ss; i++ ) if( !p[soff[i]] ) { /* Found a mismatch! */ result = 0; break; } /* Check all clear pixels are clear. */ if( result ) for( i = 0; i < seq->cs; i++ ) if( p[coff[i]] ) { result = 0; break; } *q = result; } } return( 0 ); }
/* Loop over region, adding information to the appropriate fields of tmp. */ static int scan_fn( REGION *reg, void *seq, void *a, void *b ) { double *tmp = (double *) seq; Rect *r = ®->valid; IMAGE *im = reg->im; int le = r->left; int to = r->top; int bo = IM_RECT_BOTTOM(r); int sz = IM_REGION_N_ELEMENTS( reg ); double s = 0.0; double s2 = 0.0; int x, y; /* Sum pels in this section. */ #define loop(TYPE) \ { TYPE *p; \ \ for( y = to; y < bo; y++ ) { \ p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ \ for( x = 0; x < sz; x++ ) { \ TYPE v = p[x]; \ \ s += v; \ s2 += (double) v * (double) v; \ }\ }\ } /* Now generate code for all types. */ switch( im->BandFmt ) { case IM_BANDFMT_UCHAR: loop(unsigned char); break; case IM_BANDFMT_CHAR: loop(signed char); break; case IM_BANDFMT_USHORT: loop(unsigned short); break; case IM_BANDFMT_SHORT: loop(signed short); break; case IM_BANDFMT_UINT: loop(unsigned int); break; case IM_BANDFMT_INT: loop(signed int); break; case IM_BANDFMT_FLOAT: loop(float); break; case IM_BANDFMT_DOUBLE: #ifdef HAVE_LIBOIL for( y = to; y < bo; y++ ) { double *p = (double *) IM_REGION_ADDR( reg, le, y ); double t; double t2; oil_sum_f64( &t, p, sizeof( double ), sz ); oil_squaresum_f64( &t2, p, sz ); s += t; s2 += t2; } #else /*!HAVE_LIBOIL*/ loop(double); #endif /*HAVE_LIBOIL*/ break; default: assert( 0 ); } /* Add to sum for this sequence. */ tmp[0] += s; tmp[1] += s2; return( 0 ); }
/* Shrink a REGION. */ static int shrink_gen( REGION *or, void *vseq, void *a, void *b ) { SeqInfo *seq = (SeqInfo *) vseq; ShrinkInfo *st = (ShrinkInfo *) b; REGION *ir = seq->ir; Rect *r = &or->valid; Rect s; int le = r->left; int ri = IM_RECT_RIGHT( r ); int to = r->top; int bo = IM_RECT_BOTTOM(r); int x, y, z, k; /* What part of the input image do we need? Very careful: round left * down, round right up. */ s.left = r->left * st->xshrink; s.top = r->top * st->yshrink; s.width = ceil( IM_RECT_RIGHT( r ) * st->xshrink ) - s.left; s.height = ceil( IM_RECT_BOTTOM( r ) * st->yshrink ) - s.top; if( im_prepare( ir, &s ) ) return( -1 ); /* Init offsets for pel addressing. Note that offsets must be for the * type we will address the memory array with. */ for( z = 0, y = 0; y < st->mh; y++ ) for( x = 0; x < st->mw; x++ ) seq->off[z++] = (IM_REGION_ADDR( ir, x, y ) - IM_REGION_ADDR( ir, 0, 0 )) / IM_IMAGE_SIZEOF_ELEMENT( ir->im ); switch( ir->im->BandFmt ) { case IM_BANDFMT_UCHAR: ishrink(unsigned char); break; case IM_BANDFMT_CHAR: ishrink(char); break; case IM_BANDFMT_USHORT: ishrink(unsigned short); break; case IM_BANDFMT_SHORT: ishrink(short); break; case IM_BANDFMT_UINT: ishrink(unsigned int); break; case IM_BANDFMT_INT: ishrink(int); break; case IM_BANDFMT_FLOAT: fshrink(float); break; case IM_BANDFMT_DOUBLE: fshrink(double); break; default: im_error( "im_shrink", "%s", _( "unsupported input format" ) ); return( -1 ); } return( 0 ); }
/* Subsample a REGION. We fetch in IM_MAX_WIDTH pixel-wide strips, left-to-right * across the input. */ static int line_shrink_gen( REGION *or, void *seq, void *a, void *b ) { REGION *ir = (REGION *) seq; IMAGE *in = (IMAGE *) a; SubsampleInfo *st = (SubsampleInfo *) b; Rect *r = &or->valid; int le = r->left; int ri = IM_RECT_RIGHT( r ); int to = r->top; int bo = IM_RECT_BOTTOM(r); int ps = IM_IMAGE_SIZEOF_PEL( in ); int owidth = IM_MAX_WIDTH / st->xshrink; Rect s; int x, y; int z, k; /* Loop down the region. */ for( y = to; y < bo; y++ ) { char *q = IM_REGION_ADDR( or, le, y ); char *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 = IM_MIN( owidth, ri - x ); /* Ask for this many from input ... can save a * little here! */ int iw = ow * st->xshrink - (st->xshrink - 1); /* Ask for input. */ s.left = x * st->xshrink; s.top = y * st->yshrink; s.width = iw; s.height = 1; if( im_prepare( ir, &s ) ) return( -1 ); /* Append new pels to output. */ p = IM_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 * st->xshrink; } } } return( 0 ); }
/* Top-bottom blend function for IM_CODING_LABQ images. */ static int tb_blend_labpack( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) { REGION *rir = inf->rir; REGION *sir = inf->sir; Rect prr, psr; int y, yr, ys; /* Make sure we have a complete first/last set for this area. This * will just look at the top 8 bits of L, not all 10, but should be OK. */ if( make_firstlast( inf, ovlap, oreg ) ) return( -1 ); /* Part of rr which we will output. */ prr = *oreg; prr.left -= ovlap->rarea.left; prr.top -= ovlap->rarea.top; /* Part of sr which we will output. */ psr = *oreg; psr.left -= ovlap->sarea.left; psr.top -= ovlap->sarea.top; /* Make pixels. */ if( im_prepare( rir, &prr ) ) return( -1 ); if( im_prepare( sir, &psr ) ) return( -1 ); /* Loop down overlap area. */ for( y = oreg->top, yr = prr.top, ys = psr.top; y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { VipsPel *pr = IM_REGION_ADDR( rir, prr.left, yr ); VipsPel *ps = IM_REGION_ADDR( sir, psr.left, ys ); VipsPel *q = IM_REGION_ADDR( or, oreg->left, y ); const int j = oreg->left - ovlap->overlap.left; const int *first = ovlap->first + j; const int *last = ovlap->last + j; float *fq = inf->merge; float *r = inf->from1; float *s = inf->from2; /* Unpack two bits we want. */ vips__LabQ2Lab_vec( r, pr, oreg->width ); vips__LabQ2Lab_vec( s, ps, oreg->width ); /* Blend as floats. */ fblend( float, 3, r, s, fq ); /* Re-pack to output buffer. */ vips__Lab2LabQ_vec( q, inf->merge, oreg->width ); } 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 ); }
static int affinei( IMAGE *in, IMAGE *out, VipsInterpolate *interpolate, Transformation *trn ) { Affine *affine; double edge; /* Make output image. */ if( im_piocheck( in, out ) ) return( -1 ); if( im_cp_desc( out, in ) ) return( -1 ); /* Need a copy of the params for the lifetime of out. */ if( !(affine = IM_NEW( out, Affine )) ) return( -1 ); affine->interpolate = NULL; if( im_add_close_callback( out, (im_callback_fn) affine_free, affine, NULL ) ) return( -1 ); affine->in = in; affine->out = out; affine->interpolate = interpolate; g_object_ref( interpolate ); affine->trn = *trn; if( im__transform_calc_inverse( &affine->trn ) ) return( -1 ); out->Xsize = affine->trn.oarea.width; out->Ysize = affine->trn.oarea.height; /* Normally SMALLTILE ... except if this is a size up/down affine. */ if( affine->trn.b == 0.0 && affine->trn.c == 0.0 ) { if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) return( -1 ); } else { if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) return( -1 ); } /* Check for coordinate overflow ... we want to be able to hold the * output space inside INT_MAX / TRANSFORM_SCALE. */ edge = INT_MAX / VIPS_TRANSFORM_SCALE; if( affine->trn.oarea.left < -edge || affine->trn.oarea.top < -edge || IM_RECT_RIGHT( &affine->trn.oarea ) > edge || IM_RECT_BOTTOM( &affine->trn.oarea ) > edge ) { im_error( "im_affinei", "%s", _( "output coordinates out of range" ) ); return( -1 ); } /* Generate! */ if( im_generate( out, im_start_one, affinei_gen, im_stop_one, in, affine ) ) return( -1 ); return( 0 ); }
/* Top-bottom blend function for non-labpack images. */ static int tb_blend( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) { REGION *rir = inf->rir; REGION *sir = inf->sir; IMAGE *im = or->im; Rect prr, psr; int y, yr, ys; /* Make sure we have a complete first/last set for this area. */ if( make_firstlast( inf, ovlap, oreg ) ) return( -1 ); /* Part of rr which we will output. */ prr = *oreg; prr.left -= ovlap->rarea.left; prr.top -= ovlap->rarea.top; /* Part of sr which we will output. */ psr = *oreg; psr.left -= ovlap->sarea.left; psr.top -= ovlap->sarea.top; /* Make pixels. */ if( im_prepare( rir, &prr ) ) return( -1 ); if( im_prepare( sir, &psr ) ) return( -1 ); /* Loop down overlap area. */ for( y = oreg->top, yr = prr.top, ys = psr.top; y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { VipsPel *pr = IM_REGION_ADDR( rir, prr.left, yr ); VipsPel *ps = IM_REGION_ADDR( sir, psr.left, ys ); VipsPel *q = IM_REGION_ADDR( or, oreg->left, y ); const int j = oreg->left - ovlap->overlap.left; const int *first = ovlap->first + j; const int *last = ovlap->last + j; switch( im->BandFmt ) { case IM_BANDFMT_UCHAR: iblend( unsigned char, im->Bands, pr, ps, q ); break; case IM_BANDFMT_CHAR: iblend( signed char, im->Bands, pr, ps, q ); break; case IM_BANDFMT_USHORT: iblend( unsigned short, im->Bands, pr, ps, q ); break; case IM_BANDFMT_SHORT: iblend( signed short, im->Bands, pr, ps, q ); break; case IM_BANDFMT_UINT: iblend( unsigned int, im->Bands, pr, ps, q ); break; case IM_BANDFMT_INT: iblend( signed int, im->Bands, pr, ps, q ); break; case IM_BANDFMT_FLOAT: fblend( float, im->Bands, pr, ps, q ); break; case IM_BANDFMT_DOUBLE: fblend( double, im->Bands, pr, ps, q ); break; case IM_BANDFMT_COMPLEX: fblend( float, im->Bands*2, pr, ps, q ); break; case IM_BANDFMT_DPCOMPLEX: fblend( double, im->Bands*2, pr, ps, q ); break; default: im_error( "im_tbmerge", "%s", _( "internal error" ) ); return( -1 ); } } return( 0 ); }