Exemplo n.º 1
0
/*!
* \return	The filtered object, or NULL on error.
* \ingroup	WlzValueFilters
* \brief	Applies a recursive filter to the given object.
* \param	srcObj			Given object.
* \param	ftr			Recursive filter.
* \param	actionMsk		Action mask.
* \param	dstErr			Destination error pointer, may
*                                       be null.
*/
WlzObject	*WlzRsvFilterObj(WlzObject *srcObj, WlzRsvFilter *ftr,
			         int actionMsk, WlzErrorNum *dstErr)
{
  WlzValues	tVal;
  WlzObject	*xObj = NULL,
		*yObj = NULL,
		*xyObj = NULL,
		*zObj = NULL,
  		*dstObj = NULL;
  WlzErrorNum	errNum = WLZ_ERR_NONE;

  if((srcObj == NULL) || (ftr == NULL))
  {
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else if(srcObj->domain.core == NULL)
  {
    errNum = WLZ_ERR_DOMAIN_NULL;
  }
  else if(srcObj->values.core == NULL)
  {
    errNum = WLZ_ERR_VALUES_NULL;
  }
  else if(WlzGreyTableIsTiled(srcObj->values.core->type))
  {
    errNum = WLZ_ERR_VALUES_TYPE;
  }
  else
  {
    switch(srcObj->type)
    {
      case WLZ_EMPTY_OBJ:
        dstObj = WlzMakeEmpty(&errNum);
	break;
      case WLZ_2D_DOMAINOBJ:
	if((actionMsk & (WLZ_RSVFILTER_ACTION_X |
			 WLZ_RSVFILTER_ACTION_Y)) != 0)
	{
	  /* Filter in each required direction. */
	  if((actionMsk & WLZ_RSVFILTER_ACTION_X) != 0)
	  {
	    xObj = WlzRsvFilterObj2DX(srcObj, ftr, &errNum);
	  }
	  if((errNum == WLZ_ERR_NONE) &&
	     ((actionMsk & WLZ_RSVFILTER_ACTION_Y) != 0))
	  {
	    yObj = WlzRsvFilterObj2DY((xObj)? xObj: srcObj,
	    		              ftr, &errNum);
	  }
	  if(errNum == WLZ_ERR_NONE)
	  {
	    if(yObj)
	    {
	      dstObj = yObj;
	      yObj = NULL;
	    }
	    else if(xObj)
	    {
	      dstObj = xObj;
	      xObj = NULL;
	    }
	  }
	  if(xObj)
	  {
	    WlzFreeObj(xObj);
	  }
	  if(yObj)
	  {
	    WlzFreeObj(yObj);
	  }
	}
	else
	{
	  /* No filtering required. */
	  dstObj = WlzNewGrey(srcObj, &errNum);
	}
        break;
      case WLZ_3D_DOMAINOBJ:
	if((actionMsk & (WLZ_RSVFILTER_ACTION_X | WLZ_RSVFILTER_ACTION_Y |
			  WLZ_RSVFILTER_ACTION_Z)) != 0)
	{
	  if((actionMsk & (WLZ_RSVFILTER_ACTION_X | 
	  		   WLZ_RSVFILTER_ACTION_Y)) != 0)
	  {
	    xyObj = WlzRsvFilterObj3DXY(srcObj, ftr, actionMsk,
	    				&errNum);
	  }
	  if((errNum == WLZ_ERR_NONE) && 
	     ((actionMsk & WLZ_RSVFILTER_ACTION_Z) != 0))
	  {
	    zObj = WlzRsvFilterObj3DZ((xyObj)? xyObj: srcObj, ftr, &errNum);
	  }
	  if(errNum == WLZ_ERR_NONE)
	  {
	    if(zObj)
	    {
	      dstObj = zObj;
	      zObj = NULL;
	    }
	    else if(xyObj)
	    {
	      dstObj = xyObj;
	      xyObj = NULL;
	    }
	  }
	  if(xyObj)
	  {
	    WlzFreeObj(xyObj);
	  }
	  if(zObj)
	  {
	    WlzFreeObj(zObj);
	  }
	}
	else
	{
	  /* No filtering required. */
	  tVal = WlzCopyValues(srcObj->type, srcObj->values, srcObj->domain,
	  		       &errNum);
	  if(errNum == WLZ_ERR_NONE)
	  {
	    dstObj= WlzMakeMain(srcObj->type, srcObj->domain, tVal,
	    			NULL, NULL, &errNum);
	  }
	}
        break;
      default:
        errNum = WLZ_ERR_OBJECT_TYPE;
	break;
    }
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  return(dstObj);
}
Exemplo n.º 2
0
/*! 
* \ingroup      WlzValuesUtils
* \brief        Set the value maskVal within the domain given by the	
*		mask object. The mask object can be a 2D, 3D, polygon	
*		or boundary object. A 3D mask with a 2D object is an	
*		error. A 2D mask with a 3D object will be applied to	
*		each plane in turn.			
*
* \return       New object with the same domain as the input object but
 with values in the intersection with the mask domain set to the mask
 value. NULL on error.
 * \param    obj	Input object
 * \param    mask	Mask object.
 * \param    maskVal	mask value.
 * \param    dstErr	Error return.
* \par      Source:
*                WlzGreyMask.c
*/
WlzObject *WlzGreyMask(
  WlzObject	*obj,
  WlzObject	*mask,
  WlzPixelV	maskVal,
  WlzErrorNum	*dstErr)
{
  WlzObject	*rtnObj=NULL;
  WlzObject	*tmpMask, *obj1;
  WlzValues	values;
  WlzPixelV	tmpMaskval;
  WlzIntervalWSpace	iwsp;
  WlzGreyWSpace		gwsp;
  WlzGreyP		gptr;
  int			i;
  WlzErrorNum	errNum=WLZ_ERR_NONE;

  /* check obj */
  if( obj == NULL ){
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else {
    switch( obj->type ){
    case WLZ_2D_DOMAINOBJ:
      if( obj->values.core == NULL ){
	errNum = WLZ_ERR_VALUES_NULL;
      }
      break;

    case WLZ_3D_DOMAINOBJ:
      return WlzGreyMask3d(obj, mask, maskVal, dstErr);

    case WLZ_TRANS_OBJ:
      if((values.obj = WlzGreyMask(obj->values.obj, mask, maskVal,
      				   &errNum)) != NULL){
	return WlzMakeMain(WLZ_TRANS_OBJ, obj->domain, values,
			   NULL, NULL, dstErr);
      }
      break;

    case WLZ_EMPTY_OBJ:
      return WlzMakeEmpty(dstErr);

    default:
      errNum = WLZ_ERR_OBJECT_TYPE;
      break;
    }
  }

  /* check the mask */
  if( errNum == WLZ_ERR_NONE ){
    if( mask == NULL ){
      errNum = WLZ_ERR_OBJECT_NULL;
    }
    else {
      values.core = NULL;
      switch( mask->type ){
      case WLZ_2D_DOMAINOBJ:
	tmpMask = WlzMakeMain(WLZ_2D_DOMAINOBJ, mask->domain, values,
			      NULL, NULL, &errNum);
	break;

      case WLZ_TRANS_OBJ:
	tmpMask = WlzMakeMain(WLZ_2D_DOMAINOBJ, mask->values.obj->domain,
			      values, NULL, NULL, &errNum);
	break;

      case WLZ_EMPTY_OBJ:
	return WlzMakeMain(WLZ_2D_DOMAINOBJ, obj->domain, obj->values,
			   NULL, NULL, dstErr);

      case WLZ_2D_POLYGON:
	tmpMask = WlzPolyToObj(mask->domain.poly, WLZ_SIMPLE_FILL, &errNum);
	break;

      case WLZ_BOUNDLIST:
	tmpMask = WlzBoundToObj(mask->domain.b, WLZ_SIMPLE_FILL, &errNum);
	break;

      default:
	errNum = WLZ_ERR_OBJECT_TYPE;
	break;
      }
      if( errNum == WLZ_ERR_NONE ){
	tmpMask = WlzAssignObject(tmpMask, NULL);
      }
    }
  }

  /* copy input obj and setvalues in the intersection */
  if(errNum == WLZ_ERR_NONE){
    if((rtnObj = WlzNewGrey(obj, &errNum)) != NULL){
      if((obj1 = WlzIntersect2(obj, tmpMask, &errNum)) != NULL){
	obj1->values = WlzAssignValues(rtnObj->values, NULL);
	errNum = WlzInitGreyScan(obj1, &iwsp, &gwsp);
	WlzValueConvertPixel(&tmpMaskval, maskVal, gwsp.pixeltype);
	while((errNum == WLZ_ERR_NONE) &&
	      ((errNum = WlzNextGreyInterval(&iwsp)) == WLZ_ERR_NONE)){
	  gptr = gwsp.u_grintptr;
	  switch( gwsp.pixeltype ){
	  case WLZ_GREY_INT:
	    for(i=0; i<iwsp.colrmn; i++, gptr.inp++){
	      *gptr.inp = tmpMaskval.v.inv;
	    }
	    break;
	  case WLZ_GREY_SHORT:
	    for(i=0; i<iwsp.colrmn; i++, gptr.shp++){
	      *gptr.shp = tmpMaskval.v.shv;
	    }
	    break;
	  case WLZ_GREY_UBYTE:
	    for(i=0; i<iwsp.colrmn; i++, gptr.ubp++){
	      *gptr.ubp = tmpMaskval.v.ubv;
	    }
	    break;
	  case WLZ_GREY_FLOAT:
	    for(i=0; i<iwsp.colrmn; i++, gptr.flp++){
	      *gptr.flp = tmpMaskval.v.flv;
	    }
	    break;
	  case WLZ_GREY_DOUBLE:
	    for(i=0; i<iwsp.colrmn; i++, gptr.dbp++){
	      *gptr.dbp = tmpMaskval.v.dbv;
	    }
	    break;
	  case WLZ_GREY_RGBA:
	    for(i=0; i<iwsp.colrmn; i++, gptr.rgbp++){
	      *gptr.rgbp = tmpMaskval.v.rgbv;
	    }
	    break;
	  default:
	    errNum = WLZ_ERR_GREY_TYPE;
	    break;
	  }
	}
	if( errNum == WLZ_ERR_EOO ){
	  errNum = WLZ_ERR_NONE;
	}
	WlzFreeObj(obj1);
      }
      else {
	WlzFreeObj(rtnObj);
	rtnObj = NULL;
      }
    }
    WlzFreeObj(tmpMask);
  }

  if( dstErr ){
    *dstErr = errNum;
  }
  return rtnObj;
}
Exemplo n.º 3
0
/*! 
* \ingroup      WlzValuesUtils
* \brief        Makes a dithered object from the given grey-level Woolz
 object. The destination bits are determined by the number of shades in
 the dithered image (usually 1 bit) and the bit planes to use (typically
 to match the bit-planes of a display mask).
*
* \return       Object with dithered grey values.
* \param    o	Input object.
* \param    destBits	Destination bit planes for dithered values.
* \param    dstErr	Error return.
* \par      Source:
*                WlzGreyDitherObj.c
*/
WlzObject *WlzGreyDitherObj(
  WlzObject	*o,
  unsigned int  destBits,
  WlzErrorNum	*dstErr)
{
  WlzObject	*obj=NULL;
  WlzIntervalWSpace	iwsp1, iwsp2;
  WlzGreyWSpace		gwsp1, gwsp2;
  int		i, j, m, g, G;
  int		n_to;
  int		bit_val[8], permute_val[256];
  int		factor_from, factor_to, mask_start;
  WlzErrorNum	errNum=WLZ_ERR_NONE;

  /* check object type - 1 only */
  if( o == NULL ){
    errNum = WLZ_ERR_OBJECT_NULL;
  }
  else {
    switch( o->type ){
    case WLZ_2D_DOMAINOBJ:
      /* check it has a valuetable */
      if( o->values.core == NULL ){
	errNum = WLZ_ERR_VALUES_NULL;
      }
      else if( WlzGreyTableIsTiled(o->values.core->type) ){
	errNum = WLZ_ERR_VALUES_TYPE;
      }
      break;

    case WLZ_3D_DOMAINOBJ:
    case WLZ_TRANS_OBJ:
      errNum = WLZ_ERR_OBJECT_TYPE;
      break;

    case WLZ_EMPTY_OBJ:
      obj = WlzMakeEmpty(&errNum);
      break;

    default:
      errNum = WLZ_ERR_OBJECT_TYPE;
      break;
    }
  }

  if( !obj && (errNum == WLZ_ERR_NONE) ){
    /* count the bits in the mask */
    for(i=1, n_to=0; i <= destBits; i <<= 1 )
    {
      if( i & destBits )
	n_to++;
    }
    
    /* check numbers of bits */
    if( n_to == 0 ){
      errNum = WLZ_ERR_PARAM_DATA;
    }
    else {
      /* make a new object with the same domain */
      if((obj = WlzNewGrey(o, &errNum)) != NULL){
	if( n_to < 8 ){

	  /* set up some factors */
	  factor_from = 255;
	  factor_to = (1<<n_to) - 1;
	  mask_start = (1 << (8-n_to)) - 1;

	  /* set up bit permute lut */
	  for(i=0, j=1, m=destBits; i < n_to; i++)
	  {
	    while( !(m&1) )
	    {
	      m = m >> 1;
	      j = j << 1;
	    }
	    m = m >> 1;
	    bit_val[i] = j;
	  }

	  for(i=0; i < (1<<n_to); i++)
	  {
	    permute_val[i] = 0;
	    for(j=0, m=1; j < n_to; j++, m = m<<1 ){
	      permute_val[i] += (i&m) * bit_val[j];
	    }
	  }

	  for(;i < 256; i++){
	    permute_val[i] = 0;
	  }

	  /* now scan the objects */
	  WlzInitGreyScan(o,&iwsp1,&gwsp1);
	  WlzInitGreyScan(obj,&iwsp2,&gwsp2);
	  while((WlzNextGreyInterval(&iwsp1) == WLZ_ERR_NONE) && 
		(WlzNextGreyInterval(&iwsp2) == WLZ_ERR_NONE ) )
	  {
	    j = iwsp1.linpos;
	    G = rand() & mask_start;
	    switch(  gwsp1.pixeltype )
	    {
	    case WLZ_GREY_INT:
	      for(i=iwsp1.lftpos; i<=iwsp1.rgtpos; i++,
		    gwsp1.u_grintptr.inp++,
		    gwsp2.u_grintptr.inp++ )
	      {
		G += (*gwsp1.u_grintptr.inp) & 255;
		g = (G * factor_to) / factor_from;
		(*gwsp2.u_grintptr.inp) = permute_val[g];
		G -= (g * factor_from) / factor_to;
	      }
	      break;

	    case WLZ_GREY_SHORT:
	      for(i=iwsp1.lftpos; i<=iwsp1.rgtpos; i++,
		    gwsp1.u_grintptr.shp++,
		    gwsp2.u_grintptr.shp++ )
	      {
		G += (*gwsp1.u_grintptr.shp) & 255;
		g = (G * factor_to) / factor_from;
		(*gwsp2.u_grintptr.shp) = (short )(permute_val[g]);
		G -= (g * factor_from) / factor_to;
	      }
	      break;

	    case WLZ_GREY_UBYTE:
	      for(i=iwsp1.lftpos; i<=iwsp1.rgtpos; i++,
		    gwsp1.u_grintptr.ubp++,
		    gwsp2.u_grintptr.ubp++ )
	      {
		G += (*gwsp1.u_grintptr.ubp);
		g = (G * factor_to) / factor_from;
		(*gwsp2.u_grintptr.ubp) = (WlzUByte )(permute_val[g]);
		G -= (g * factor_from) / factor_to;
	      }
	      break;

	    case WLZ_GREY_RGBA: /* RGBA to be done RAB */
	    default:
	      errNum = WLZ_ERR_GREY_TYPE;
	      break;
	    }
	  }
	}
      }
    }
  }
Exemplo n.º 4
0
/*!
* \return	The filtered object, maybe NULL on error.
* \ingroup	WlzValueFilters
* \brief	WlzSeqPar performs a sequential or parallel local
*               transform. A distance transform is an example of a
*               sequential transform and a laplacian is an example of
*               a parallel transform.
*               Only WLZ_2D_DOMAINOBJ objects with values may be passed
*               to WlzSeqPar().
* \note		If the point to be transformed is at line l and col k,
*               there is an array of pointers spWSpace->adrptr[-7:7],
*               whose i'th entry gives the address of the point
*               (l + (i * spWSpace->ldelta), k), but which is only
*               meaningful for lines within bdrSz of the point.
*               For example: spWSpace->adrptr[-3] and
*               spWSpace->adrptr[3] are undefined if bdrSz < 3.
* \param	srcObj			The given WLZ_2D_DOMAINOBJ object.
* \param	newObjFlag		If zero then the given object is
*					overwritten otherwise a new object is
*					created.
* \param	sequentialFlag		If non zero the transform is
*                                       seqential and transformed
*                                       values are used in calculating
*                                       the neighbouring values.
*                                       If zero the transform always
*                                       works on the original grey
*                                       values.
* \param	rasterDir		Direction of raster scan.
* \param	bdrSz			Local transform kernel half-size,
*					must be in range 0 - 7.
*                                       The usual 8 immediate neighbors
*                                       correspond to bdrSz == 1.
* \param	bkgVal			Background grey value.
* \param	transformData		Data supplied to the transform
*                                       function.
* \param	transformFn		Supplied transform function.
* \param	dstErr			Destination error pointer, may
*                                       be NULL.
*/
WlzObject	*WlzSeqPar(WlzObject *srcObj,
			   int newObjFlag, int sequentialFlag,
			   WlzRasterDir rasterDir, int bdrSz, int bkgVal,
			   void *transformData,
			   int (*transformFn)(WlzSeqParWSpace *, void *),
			   WlzErrorNum *dstErr)
{
  int 		tI0,
  		curLine,
  		howManyLn,
		bdrP1,
		numBufs,
		kol,
		kol1,
		lineSz, 		/* lineSz is the maximally sized line,
					   including borders */
		lineOffset, 		/* lineOffset is difference in line
					   numbers between that being
					   transformed and that needed in the
					   input buffer for use in the
					   transform */
		nextLine,
		inLine,
		needed,
		idx,
		itop,
		minLine;
  WlzErrorNum	errNum = WLZ_ERR_NONE;
  int 		*lineBufSpace = NULL, 	/* lineBufSpace is the current line
  					   buffer */
  		*firstColP,
  		*lastColP,
		*dstBuf,
		*lastLnP,
		*tIP0;
  WlzIntervalDomain *jdp;
  WlzObject 	*dstObj = NULL;
  WlzSeqParWSpace spWSpace;		/* Work space which is passed on to
  					   the transform function. */
  WlzIntervalWSpace srcIWsp,
  		dstIWsp;
  WlzGreyWSpace srcGWsp,
  		dstGWsp;
  int		*lineBufs[15],      	/* lineBufs is the array of line
  					   buffers needed to obtain
					   neighbouring points. */
		*adrbase[15];

  WLZ_DBG((WLZ_DBG_LVL_FN|WLZ_DBG_LVL_1),
  	  ("WlzSeqPar FE %p %d %d %d %d %d %p %p %p\n",
	   srcObj, newObjFlag, sequentialFlag, (int )rasterDir, bdrSz, bkgVal,
	   transformData, transformFn, dstErr));
  if(srcObj == NULL)
  {
    errNum = WLZ_ERR_OBJECT_NULL;
    if(dstErr)
    {
      *dstErr = errNum;
    }
    return(NULL);
  }
  if((srcObj->type != WLZ_2D_DOMAINOBJ) || (srcObj->values.core == NULL) ||
     (srcObj->domain.core == NULL))
  {
    errNum = WLZ_ERR_OBJECT_TYPE;
    if(dstErr)
    {
      *dstErr = errNum;
    }
    return(NULL);
  }
  /*
   * Make an array spWSpace.adrptr of pointers with range [-7,7]
   */
  spWSpace.adrptr = adrbase + 7;
  spWSpace.brdrsz = bdrSz;
  jdp = srcObj->domain.i;
  bdrP1 = -bdrSz - 1;
  /*
   * The buffer must contain both the current line and bdrSz lines each side.
   */
  numBufs = (bdrSz * 2) + 1;
  kol1 = kol = jdp->kol1;
  lineSz = jdp->lastkl - kol + numBufs;
  /*
   * Some pointers to first/last line and column positions
   * to accomodate different rasters
   */
  firstColP = &dstIWsp.lftpos;
  lastColP = &dstIWsp.rgtpos;
  lastLnP = &jdp->lastln;
  /*
   * spWSpace.kdelta is increment to adrs when processing points
   * if positive, procede left to right in lines
   * if negative procede right to left
   * spWSpace.ldelta is increment to line number
   * if positive, the raster is proceeding in increasing line numbers
   * if negative the raster is procceding with decreasing line numbers
   */
  spWSpace.kdelta = 1;
  spWSpace.ldelta = 1;
  /*
   * Alter parameters for right to left rasters
   */
  if(!sequentialFlag)
  {
    rasterDir = WLZ_RASTERDIR_ILIC;
  }
  if((rasterDir == WLZ_RASTERDIR_ILDC) || (rasterDir == WLZ_RASTERDIR_DLDC))
  {
    tIP0 = firstColP;
    firstColP = lastColP;
    lastColP = tIP0;
    spWSpace.kdelta = -1;
  }
  /*
   * Alter parameters for bottom to top rasters
   */
  if((rasterDir == WLZ_RASTERDIR_DLIC) || (rasterDir == WLZ_RASTERDIR_DLDC))
  {
    spWSpace.ldelta = -1;
    lastLnP = &jdp->line1;
  }
  lineOffset = bdrSz * spWSpace.ldelta;
  /* 
   * Allocate space to buffers 
   */
  lineBufSpace = (int *)AlcCalloc(lineSz * (numBufs + 1), sizeof(int));
  for(idx = 0; idx < numBufs; ++idx)
  {
    lineBufs[idx] = lineBufSpace + (idx + 1) * lineSz;
  }
  /*
   * Either use existing object or create a new grey table object
   * according to the newObjFlag.
   * If a new object is required then it is created with the SAME interval
   * list and property list and a DIFFERENT but IDENTICAL grey table.
   */
  if(newObjFlag)
  {
    dstObj = WlzNewGrey(srcObj, &errNum);
  }
  else
  {
    dstObj = srcObj;
  }
  /*
   * Use nxxiv in transplant mode, placing and taking grey values
   * from the line buffers
   * srcIWsp is the workspace for entering values into the line buffers
   * dstIWsp is the workspace for processing the intervals and replacing
   * the transformed values into the grey table
   */
  srcGWsp.gvio = 1;		  /* output (since we are specifying tranpl) */
  dstGWsp.gvio = 0;						    /* input */
  errNum = WlzInitGreyRasterScan(srcObj, &srcIWsp, &srcGWsp, rasterDir, 1);
  if(errNum == WLZ_ERR_NONE)
  {
    errNum = WlzInitGreyRasterScan(dstObj, &dstIWsp, &dstGWsp, rasterDir, 1);
  }
  if(errNum != WLZ_ERR_NONE)
  {
    if(dstErr)
    {
      *dstErr = errNum;
    }
    return(NULL);
  }
  /*
   * curLine is the line currently being processed
   * inLine is the last line entered into the line buffers
   * initialize it to an imaginary line just before the first border line
   */
  (void )WlzNextInterval(&srcIWsp);
  nextLine = srcIWsp.linpos;
  inLine = srcIWsp.linpos - (spWSpace.ldelta * (bdrSz + 1));
  /*
   * Process the next interval
   */
  while((errNum == WLZ_ERR_NONE) &&
        ((errNum = WlzNextGreyInterval(&dstIWsp)) == WLZ_ERR_NONE))
  {
    /*
     * When processing curLine, the line buffer must contain
     * lines upto needed=curLine+border width
     */
    curLine = dstIWsp.linpos;
    needed = curLine + lineOffset;
    /*
     * Next action depends which lines are aleady in the line buffers
     */
    howManyLn = (needed - inLine) * spWSpace.ldelta;
    WLZ_DBG((WLZ_DBG_LVL_3),
  	    ("WlzSeqPar 01 %d %d\n",
	     inLine, curLine));
    if(howManyLn < 0)
    {
      /*
       * This is an error it is impossible to have input a line before needed
       */
      errNum = WLZ_ERR_DOMAIN_DATA;
      if(dstErr)
      {
        *dstErr = errNum;
      }
      return(NULL);
    }
    else if(howManyLn > 0)
    {
      /*
       * If howManyLn > 0 we need to input some lines to input buffer
       * routine to fill the input buffers used to determine the value
       * of the transform needed = maximal(minimal)line needed to be filled
       * into the buffer
       * if backwards raster, needed = curLine - bdrSz
       * if forwards raster, needed = curLine + bdrSz
       * nextLine = line number of the next interval to be filed by nxxiv
       */
      while(1)
      {
	if((needed - nextLine) * spWSpace.ldelta < 0)
	{
	  /*
	   * Next line needed less far away than next line available
	   * from nxxint fill required buffer lines with bkgVal and return
	   * minLine = WlzSeqParPosMod(inLine, numBufs), i.e. the remainder
	   * of inLine when divided by numBufs.
	   * It thus can be used as the offset into the array of
	   * logical line addresses to determine which line in the
	   * circular buffer inLine is being stored
	   */
	  while(inLine != needed)
	  {
	    inLine += spWSpace.ldelta;
	    minLine = WlzSeqParPosMod(inLine, numBufs);
	    WlzValueSetInt(lineBufs[minLine], bkgVal, lineSz);
	  }
	  break;
	}
	/*
	 * Line(s) needed include some real lines, i.e. obtained from nxxiv
	 * first fill buffer with bkgVal
	 */
	while(inLine != nextLine)
	{
	  inLine += spWSpace.ldelta;
	  minLine = WlzSeqParPosMod(inLine, numBufs);
	  WlzValueSetInt(lineBufs[minLine], bkgVal, lineSz);
	}
	/*
	 * nxxint interval to be put into buffer calculate adrs in
	 * buffer by adding leftcol to logical adrs stored in
	 * lineBufs[minLine]. then insert with nxxiv
	 */
	do
	{
	  srcGWsp.u_grintptr.inp = lineBufs[minLine] +
	  			   srcIWsp.lftpos + bdrSz - kol1;
	  WlzGreyInterval(&srcIWsp);
	  /*
	   * Call nxxint to get next interval if in same line store in buffer
	   */
	  if(WlzNextInterval(&srcIWsp) != 0)
	  {
	    /*
	     * When nxxint has finished all interval set a high value into
	     * nextLine, so all future lines input will be dummy lines
	     */
	    nextLine = (*lastLnP) - (bdrP1 * spWSpace.ldelta);
	    goto filledLABEL;
	  }
	} while(srcIWsp.nwlpos == 0);
	/*
	 * If next interval in new line,store new line number and
	 * jump to start of routine to see what sort of line buffer
	 * processing is required next
	 */
	nextLine = srcIWsp.linpos;
      }
    }
    /*
     * Line buffers filled for processing of this line either from above call
     * or buffer filled for previous intervals processed on this line
     * kol is the first point in the interval to be processed
     * itop is the last point to be processed
     */
filledLABEL: 					    /* LABEL! see goto above */
    kol = *firstColP;
    itop = *lastColP;
    /*
     * First load spWSpace.adrptr with addresses of required neighbors
     */
    idx = bdrP1;
    while(idx++ < bdrSz)
    {
      tI0 = idx * spWSpace.ldelta;
      spWSpace.adrptr[idx] = lineBufs[WlzSeqParPosMod(curLine + tI0,
      						      numBufs)] +
			     kol + bdrSz - kol1;
    }
    /*
     * Then set the pointer where the transformed value will be placed
     * if parallel mode, adrs is in current line buffer
     * if sequential mode, adrs is in input buffer
     * dstGWsp.grintptr is the location in the nxxiv workspace
     * giving the location from which nxxiv will transplant
     * values into the grey table
     */
    if(sequentialFlag)
    {
      dstBuf = spWSpace.adrptr[0];
    }
    else
    {
      dstBuf = lineBufSpace + bdrSz;
    }
    dstGWsp.u_grintptr.inp = dstBuf + dstIWsp.lftpos - kol;
    /*
     * Procede thru interval,calling the transforming function
     * at each point and storing the result where required
     */
    kol -= spWSpace.kdelta;
    while(kol != itop)
    {
      kol += spWSpace.kdelta;
      *dstBuf = (*transformFn)(&spWSpace, transformData);
      /*
       * after each point, update the neighbor adrs and the output adrs
       */
      idx = bdrP1;
      while(idx++ < bdrSz)
      {
	spWSpace.adrptr[idx] += spWSpace.kdelta;
      }
      dstBuf += spWSpace.kdelta;
    }
    /*
     * Having finished this interval proceed to the next
     */
  }
  if(errNum == WLZ_ERR_EOO)
  {
    errNum = WLZ_ERR_NONE;
  }
  if(lineBufSpace)
  {
    AlcFree((void *)lineBufSpace);
  }
  if(dstErr)
  {
    *dstErr = errNum;
  }
  WLZ_DBG((WLZ_DBG_LVL_FN|WLZ_DBG_LVL_1),
  	  ("WlzSeqPar FX %p\n",
	   dstObj));
  return(dstObj);
}