


/* refresh_source -- Refresh a portion of a mapping given the source rect
 * to be refreshed.  If the given source rect is not within the mapping,
 * this is a no-op.
 */
static int
refresh_source (GtermWidget w, Mapping mp, int x1, int y1, int nx, int ny)
                  
               	/* mapping defining refresh operation */
                       		/* region of source to be refreshed */
{
    int sx1, sx2, sy1, sy2, snx, sny;
    int dx1, dx2, dy1, dy2, dnx, dny;
    int valid;


    /* Do nothing if mapping not defined and enabled.
    */
    if (!(valid = valid_mapping (w, mp))) {
	if (DBG_TRACE)
	    fprintf (stderr, "refresh_source: valid=%d\n", valid);
	return (ERR);
    }
    if (!mp->enabled) {
	if (DBG_TRACE)
	    fprintf (stderr, "ERROR ref_src: mapping %d not enabled\n",
		mp->mapping);
	return (OK);
    }

    /* Compute the intersection of the modified region of the source raster
    ** with the rectangular region of the source raster affected by the given
    ** mapping.
    */
    sx1 = max (x1, mp->sx);
    sy1 = max (y1, mp->sy);
    sx2 = min(x1 + nx, mp->sx + mp->snx) - 1;
    sy2 = min(y1 + ny, mp->sy + mp->sny) - 1;
    snx = sx2 - sx1 + 1;
    sny = sy2 - sy1 + 1;

    /* Do nothing if the rectangles do not intersect.
    */
    if (snx <= 0 || sny <= 0)
	return (OK);

    /* Compute the destination rect affected by the mapped source rect.
    */
    dx1 = mp->x_extent[sx1 - mp->sx].lo;
    dx2 = mp->x_extent[sx2 - mp->sx].hi;
    if (dx1 > dx2) {
	dx1 = mp->x_extent[sx2 - mp->sx].lo;
	dx2 = mp->x_extent[sx1 - mp->sx].hi;
    }

    dy1 = mp->y_extent[sy1 - mp->sy].lo;
    dy2 = mp->y_extent[sy2 - mp->sy].hi;
    if (dy1 > dy2) {
	dy1 = mp->y_extent[sy2 - mp->sy].lo;
	dy2 = mp->y_extent[sy1 - mp->sy].hi;
    }

    dnx = dx2 - dx1 + 1;
    dny = dy2 - dy1 + 1;

    if (CacheXImage)						/* MF004 */
        DestroyCachedXImage();					/* MF004 */

    if (DBG_TRACE) {
	fprintf(stderr, "refresh_source -- refreshing src  (%d,%d) (%d,%d)\n",
	    sx1, sy1, snx, sny);
	fprintf(stderr, "refresh_source -- refreshing dest (%d,%d) (%d,%d)\n",
	    dx1, dy1, dnx, dny);
	dbg_printMappings (w);
    }

    /* Perform the refresh operation. */
    return (refresh_destination (w, mp, dx1, dy1, dnx, dny));
}


/* refresh_destination -- Refresh (redraw) the pixels in the given destination
 * rect.  The mapping operand defines how to generate the value of each output
 * pixel.  This is the routine which does all the real work of a mapping,
 * computing the values of the output pixels and writing to the destination
 * drawable.  Note: the destination rect must be specified in raster pixel
 * coordinates (no NDC).
 */
static int
refresh_destination (GtermWidget w, Mapping mp, int x1, int y1, int nx, int ny)
                  
               			/* mapping defining refresh operation */
                       		/* region of destination to be refreshed */
{
    Raster sr, dr, pr;
    Display *display = w->gterm.display;
    int scaling, xflip, yflip, delxin=0, delxout=0;
    int ox, oy, rop, clip, mapping, i, j;
    int src, st, sx, sy, snx, sny;
    int dst, dt, dx, dy, dnx, dny;
    int xoff, yoff, p1, p2, q1, q2;
    float xscale, yscale;
    struct mapping *np, p_mp;
    XImage *xin, *xout;
    int status = OK;
    Pixmap pixmap;						/* MF004 */

    Region clip_region, mask_region;
    uchar *old_xin_lp, *old_xout_lp;
    uchar *xin_lp, *xout_lp, *op;
    int xin_bpl, xout_bpl;
    int *xmap, *ymap, valid;
    XRectangle r;
    XImage *img1 = (XImage *) NULL, 
	   *img2 = (XImage *) NULL, 
	   *img3 = (XImage *) NULL, 
	   *img4 = (XImage *) NULL, 
	   *img5 = (XImage *) NULL;



    if (DBG_TRACE)
	fprintf(stderr, "refresh_destination ENTER[0x%x]: pos=%d,%d sz=%d,%d\n",
	    w, x1,y1,nx,ny);

    if (!w || !XtIsRealized ((Widget)w))
	return (OK);

    /* Do nothing if mapping not defined and enabled. */
    if (!(valid = valid_mapping (w, mp))) {
	if (DBG_TRACE)
	    fprintf (stderr, "refresh_destination: valid=%d\n", valid);
	return (ERR);
    }
    if (!mp->enabled) {
	if (DBG_TRACE)
	    fprintf (stderr, "ERROR ref_dst: mapping %d not enabled\n",
		mp->mapping);
	return (OK);
    }


    if (DBG_CMAPS && DBG_CM_VERB)
	dbg_printCmaps (w);
    if (DBG_TRACE)
	dbg_printRasters (w);


    /* Offsets into the x_*,y_* mapping lookup tables. */
    xoff = x1 - mp->dx;
    yoff = y1 - mp->dy;

    /* Get source and destination rects. */
    dst = mp->dst;
    dx  = x1;	dy  = y1;
    dnx = nx;	dny = ny;

    src = mp->src;
    p1  = mp->x_srcpix[xoff];
    q1  = mp->y_srcpix[yoff];
    p2  = mp->x_srcpix[xoff + nx - 1];
    q2  = mp->y_srcpix[yoff + ny - 1];
    sx  = min (p1, p2);
    sy  = min (q1, q2);
    snx = abs (p2 - p1) + 1;
    sny = abs (q2 - q1) + 1;

    /* Define some local variables. */
    sr = &w->gterm.rasters[src];
    dr = &w->gterm.rasters[dst];
    pr = &w->gterm.rasters[0];		/* display pixmap raster	*/
    mapping  = mp->mapping;
    scaling  = mp->scaling;
    xscale   = mp->xscale;
    yscale   = mp->yscale;
    rop = mp->rop;

    if (DBG_TRACE) {
	 fprintf (stderr, 
	    "refresh_destination: src,dst = %d,%d st,dt = %s[%d],%s[%d]\n",
	        src, dst, 
	        (sr->type == 1 ? "image" : "pixmap"),  sr->depth,
	        (dr->type == 1 ? "image" : "pixmap"),  dr->depth);
	fprintf (stderr, 
	    "refresh: gterm.pixmap=0x%x src.pixmap=0x%x  dest.pixmap=0x%x\n",
	    w->gterm.pixmap, sr->r.pixmap, dr->r.pixmap);
	fprintf (stderr, 
	    "refresh: gterm.pixmap=0x%x src.ximage=0x%x  dest.ximage=0x%x\n",
	    w->gterm.pixmap, sr->r.ximage, dr->r.ximage);
	fprintf (stderr, 
	    "refresh_destination: src=>(%d,%d : %d,%d)  dst=>(%d,%d : %d,%d)\n",
	    sx, sy, snx, sny, dx, dy, dnx, dny);
    }


    /* Basic error checking.
    */
    if (!sr->type || !dr->type) {
	if (DBG_TRACE)
	    fprintf (stderr, "ERROR: invalid src or dest type\n");
	return (ERR);

    } else if (snx <= 0 || sny <= 0 || dnx == 0 || dny == 0) {
	if (DBG_TRACE)
	    fprintf (stderr, "ERROR: negative src position or zero size\n");
	return (ERR);

    } else if (src < 0 || src > w->gterm.maxRasters ||
	dst < 0 || dst > w->gterm.maxRasters) {
	    if (DBG_TRACE)
	        fprintf (stderr, "ERROR:  invalid src or dest value\n");
	    return (ERR);
    }


    /* Do we have a flip in X or Y? */
    xflip = mp->dnx < 0;
    yflip = mp->dny < 0;

    /* Any higher numbered mappings which map to the same destination as the
     * mapping MP will obscure the current mapping.  Construct a clip mask
     * defining the region of the destination we can write to.  This will be
     * the region not obscured by any higher numbered, active mappings.
     */
    clip = False;
    clip_region = XCreateRegion();
    r.x      = dx;  
    r.y      = dy;
    r.width  = dnx;  
    r.height = dny;
#ifdef sun
    /* As far as I can tell the Sun (probably in the OW X server) has an
     * off by one bug affecting clipping in the server.  A clip region is
     * too small by one pixel at the right and bottom, causing these pixels
     * to not be written when refreshing the screen (usually this shows up
     * as black lines on the screen when a region is refreshed).  So far
     * I haven't seen this on any other platform.  The problem is imperfectly
     * understood and the following workaround may not completely workaround
     * the problem.
     */
    r.width++;  r.height++;
#endif
    XUnionRectWithRegion (&r, clip_region, clip_region);

    for (np = mp->next;  np;  np = np->next) {
	struct mapping p_np;

	if (!np->enabled || np->dst != dst)
	    continue;
	get_pixel_mapping (w, np, &p_np, 0);

	r.x = p_np.dx;  r.y = p_np.dy;
	r.width = abs(p_np.dnx);
	r.height = abs(p_np.dny);

	if (XRectInRegion (clip_region,
	    r.x, r.y, r.width, r.height) != RectangleOut) {

	    mask_region = XCreateRegion();
	    XUnionRectWithRegion (&r, mask_region, mask_region);
	    XSubtractRegion (clip_region, mask_region, clip_region);
	    XDestroyRegion (mask_region);
	    clip++;
	}
    }
    if (DBG_TRACE)
	fprintf (stderr,
	    "refresh_destination: xflip=%d  yflip=%d  clip=%d  scaling=%d\n",
	     xflip, yflip, clip, scaling);

    if (clip && dr->type == PixmapRaster)
	XSetRegion (w->gterm.display, w->gterm.exposeGC, clip_region);


    /* Handle the special case of a pixmap to pixmap (or window) copy in
     * the server with no scaling.
     */
    if (!scaling && sr->type == PixmapRaster && dr->type == PixmapRaster) {

        if (DBG_TRACE)
	    fprintf (stderr,
	        "refresh_destination: pixmap-to-pixmap copy  %d <-> %d\n",
	         src, dst);
	    
	if (src == 0 && dst == 0 && w->gterm.pixmap && !(rop & R_Transient)) {

	    if (DBG_TRACE) 
		fprintf (stderr, 
		    "refresh_destination: pixmap copy src=dst=0\n");

	    XCopyArea (display, 
		w->gterm.pixmap, w->gterm.pixmap,
		w->gterm.exposeGC, sx, sy, snx, sny, dx, dy);

	    XCopyArea (display, 
		GPMtoRPM(w, dr), dr->r.pixmap,
		w->gterm.exposeGC, dx, dy, dnx, dny, dx, dy);

	    /* Update the root display shadow pixmap.
	    */
	    XCopyArea (display, 
		pr->shadow_pixmap, pr->shadow_pixmap,
		w->gterm.expose8GC, sx, sy, snx, sny, dx, dy);

	} else {
	    Raster rp = &w->gterm.rasters[src-1];

	    if (DBG_TRACE) {
		fprintf (stderr, 
		    "refresh_destination: pixmap copy src || dst != 0\n");
		fprintf (stderr, "src=%d  dest=%d  shadow=0x%x  ximage=0x%x\n",
		    src, dst, dr->shadow_pixmap, rp->r.ximage);
		dbg_printRasters (w);
		fprintf (stderr,
		    "src=%d,%d %d %d   dest=%d,%d  %d %d  (src=%d  dst=%d)\n",
		    sx, sy, snx, sny, dx, dy, dnx, dny, src, dst);
	    }

	    XCopyArea (display, 
		RPMtoRPM(sr, dr), dr->r.pixmap, 
		w->gterm.exposeGC, sx, sy, snx, sny, dx, dy);


	    /*  Update the shadow pixmap.
	    */
	    XCopyArea (display, 
		sr->shadow_pixmap, dr->shadow_pixmap, 
		w->gterm.expose8GC, sx, sy, snx, sny, dx, dy);

	    if (dst == 0 && w->gterm.pixmap && !(rop & R_Transient))
		XCopyArea (display,
		    RPMtoGPM (sr, w), w->gterm.pixmap,
		    w->gterm.exposeGC, sx, sy, snx, sny, dx, dy);
	}
		

	if (DBG_TRACE)
	    fprintf (stderr,
		"refresh_destination: spec_case: pixmap copy no scaling\n");

	goto done;
    }

    /* Any other case requires working on ximages in the client.  The first
     * step is to get the source ximage.
     */
    if (sr->type == PixmapRaster) {

	if (DBG_TRACE)
	    fprintf (stderr,
		"refresh_destination: source type is PixmapRaster[%d]\n", src);
	    
	/* Source is a pixmap but we need a local copy as either the
	 * destination is not a pixmap, or we need to do some scaling.
	 */
	if (CacheXImage) {					/* MF004 */
            pixmap = (src || !w->gterm.pixmap) ? sr->r.pixmap : w->gterm.pixmap;

	    if (DBG_TRACE) {
		fprintf (stderr, "refresh_destination: xin is cached XImage ");
		fprintf (stderr, " src=%d pix=0x%x  dims=%d,%d\n",
		    src, w->gterm.pixmap, sr->width, sr->height);
	    }

            xin = GetCachedXImage (w, pixmap, sr->width, sr->height);
            if (xin == NULL) {
                xin = XGetImage (display, pixmap,
                    0, 0, sr->width, sr->height, 0xff, ZPixmap);
                if (xin == NULL) {
                    status = ERR;
                    goto done;
                } else
                    NewCachedXImage (w, xin, pixmap, sr->width, sr->height);
            }

	} else {						/* MF004 */

	    /*
	    */
	    if (w->gterm.w_depth > ColormapDepth) {
		if (DBG_TRACE) {
		    fprintf (stderr,
			"src=%d  dst=%d  d_ras=%d  %d x %d  pr=0x%x  sr=0x%x\n",
		        src, dst, w->gterm.d_raster, sr->width, sr->height, 
		        pr->shadow_pixmap, sr->shadow_pixmap);
		    dbg_printRasters (w);
		}
		    
	        xin = XGetImage (display, sr->shadow_pixmap,
	            0, 0, sr->width, sr->height, 0xff, ZPixmap);

	    } else {
	        xin = XGetImage (display,
	            (src || !w->gterm.pixmap) ? sr->r.pixmap : w->gterm.pixmap,
	            0, 0, sr->width, sr->height, 0xff, ZPixmap);
	    }

	    if (DBG_TRACE) {
		fprintf (stderr, "refresh_destination: xin is drawable '%s'\n",
    		    (src || !w->gterm.pixmap) ? "sr raster" : "gterm pixmap");
		fprintf (stderr,
		    "refresh_destination: xin size=%d,%d  bpp=%d bpl=%d\n",
		    xin->width, xin->height, xin->bits_per_pixel,
		    xin->bytes_per_line);
	    }

	    if (xin == NULL) {
	        status = ERR;
	        goto done;
	    }
   	    delxin++;
	}							/* MF004 */

    } else {
	/* Source is an ximage. */
	if (DBG_TRACE)
	    fprintf (stderr,
		"refresh_destination: src type is XImage (sr->r.ximage)\n");

	xin = sr->r.ximage;
    }

    /* Handle the special case of a copy of an ximage to an output pixmap
     * with no scaling.
     */
    if (!scaling && dr->type == PixmapRaster) {

	if (DBG_TRACE)
	    fprintf (stderr, "refresh_destination: NO scaling, dest=Pixmap\n");

	if (dst == 0 && w->gterm.pixmap && !(rop & R_Transient)) {

	    if (DBG_TRACE)
		fprintf (stderr, "refresh_destination: dest == 0\n");

	    XPutImage (display, 
		w->gterm.pixmap, w->gterm.exposeGC, 
		(img1=IMGtoGPM(w,xin,sx,sy,dnx,dny)),
		sx, sy, dx, dy, dnx, dny);


	    if (DBG_TRACE)
		fprintf (stderr,
		    "\n\nrefresh: gterm.pixmap=0x%x dest.pixmap=0x%x\n\n",
		    w->gterm.pixmap, dr->r.pixmap);

	    XCopyArea (display, 
		GPMtoRPM (w, dr), dr->r.pixmap,
		w->gterm.exposeGC, dx, dy, dnx, dny, dx, dy);

	    if (w->gterm.w_depth > ColormapDepth) {
	        XPutImage (display, pr->shadow_pixmap, w->gterm.expose8GC, xin,
		    sx, sy, dx, dy, dnx, dny);
	    }

	} else {

	    if (DBG_TRACE) {
		fprintf (stderr, "refresh_destination: xin dst > 0\n");
		fprintf (stderr,
		    "refresh_destination: src=(%d,%d) => %d,%d : %d,%d\n",
		    sx, sy, dx, dy, dnx, dny);
		fprintf (stderr, 
		    "refresh_destination: dr->shadow_pixmap = 0x%x\n",
		    dr->shadow_pixmap);
		fprintf (stderr, "refresh_destination: xin => %d x %d x %d\n",
		    xin->width, xin->height, xin->depth);
	    }

	    /* Copy the 'xin' to the 'dr->r.pixmap'
	    */
	    XPutImage (display, 
		dr->r.pixmap, w->gterm.exposeGC, 
		(img2=IMGtoRPM(w,xin,dr,sx,sy,dnx,dny)),
		sx, sy, dx, dy, dnx, dny);


	    if (w->gterm.w_depth > ColormapDepth) {
	        XPutImage (display, dr->r.pixmap, w->gterm.exposeGC, 
		    (img3=IMGtoRPM(w,xin,dr,sx,sy,dnx,dny)),
		    sx, sy, dx, dy, dnx, dny);

		/*  Shadow the pixmap with an 8-bit version for readback.
		*/
		if (DBG_TRACE)
		    fprintf (stderr,
			"updating shadow_pixmap........................0x%x\n",
			dr->shadow_pixmap);
	        XPutImage (display, dr->shadow_pixmap,
		    w->gterm.expose8GC, xin,
		    sx, sy, dx, dy, dnx, dny);
	    }
	}

	if (DBG_TRACE)
	    fprintf (stderr, 
		"refresh_destination: NO scaling, dest=Pixmap -- DONE\n");

	goto done;
    }

    /* Get output ximage. 
    */
    if (dr->type == ImageRaster) {

	if (DBG_TRACE)
	    fprintf (stderr,
		"refresh_destination: dest type is XImage (dr->r.ximage)\n");

	xout = dr->r.ximage;
	ox = dx;  
	oy = dy;

    } else {

	uchar *data = (uchar *) XtCalloc (dnx * dny, sizeof(uchar));


	if (DBG_TRACE)
	    fprintf (stderr,
		"refresh_destination: dest type is Pixmap, create XImage\n");
	    
	if (data == NULL) {
	    status = ERR;
	    goto done;
	}

	if (DBG_TRACE)
	    fprintf (stderr,
		"refresh_destination: creating 8-bit ximage....%d x %d\n",
		dnx, dny);

	xout = XCreateImage (w->gterm.display, NULL, RasterDepth,
	    ZPixmap, 0, (char *)data, dnx, dny, 8, 0);

	if (xout == NULL) {
	    XtFree ((char *)data);
	    status = ERR;
	    goto done;
	}
	ox = 0;  
	oy = 0;
	delxout++;
    }

    xin_lp = (uchar *)xin->data;
    xout_lp = (uchar *)xout->data;
    xin_bpl = xin->bytes_per_line;
    xout_bpl = xout->bytes_per_line;

    /* Map a region of the input ximage XIN to the output ximage XOUT.  Various
     * approaches are used to generate the output data, depending upon what
     * type of scaling we are doing.
     */
    if (DBG_TRACE)
	fprintf (stderr, "refresh: scaling=%d  xin=0x%x  xout=0x%x\n",
	    scaling, xin, xout);

    if (!scaling) {
	/* No scaling.  Copy a region of the ximage xin to xout without
	 * spatially scaling the image data.
	 */
	if (clip && dr->type == ImageRaster)
	    goto zoom;

	xin_lp = (uchar *)xin->data + sy * xin_bpl + sx;
	xout_lp = (uchar *)xout->data + oy * xout_bpl + ox;

	for (j=0;  j < dny;  j++) {
	    memmove (xout_lp, xin_lp, dnx);
	    xin_lp += xin_bpl;
	    xout_lp += xout_bpl;
	}

    } else if (scaling == M_INTZOOM) {
	/* Integer zoom.  The zoom factor is an integer, allowing the zoomed
	 * image to be calculated without using the xmap,ymap lookup tables.
	 */
	if (clip && dr->type == ImageRaster)
	    goto zoom;

	scale_intzoom (xin_lp,xin_bpl, xout_lp,xout_bpl, sx,sy, ox,oy,dnx,dny,
	    xflip,yflip, (int)(xscale + 0.5), (int)(yscale + 0.5));

    } else if (scaling == M_ZOOM) {
	/* We have a zoom, or one-to-many, scaling.  Zoom scaling is always
	 * done with pixel replication.  The [xy]_srcpix arrays in the mapping
	 * descriptor give the source pixel corresponding to each mapped pixel
	 * in the destination raster.
	 */
zoom:
	xmap = &mp->x_srcpix[xoff];
	ymap = &mp->y_srcpix[yoff];

	scale_zoom (xin_lp, xin_bpl, xout_lp, xout_bpl,
	    xmap, ymap, ox, oy, dnx, dny,
	    (clip && dr->type == ImageRaster) ? clip_region : (Region)NULL);

    } else if (scaling == M_DEZOOM) {
	/* We have a dezoom, or many-to-one, scaling.  A block of pixels in
	 * the input raster are combined to generate the value of each output
	 * pixel, using one of several antialias algorithms to compute the
	 * output pixel value.
	 */
	float *x_src = &mp->x_src[xoff];
	float *y_src = &mp->y_src[yoff];
	int near_unitary = (xscale > 0.5 && yscale > 0.5);
	int function;

	/* Get the antialising function to be applied. */
	if (!(function = (mp->rop & R_MFMask)))
	    function = MF_NEAREST;

	/* If the dezoom factor is small and either MF_BILINEAR or
	 * MF_NEAREST is enabled, use the indicated method to sample the
	 * input data.  This uses all the data but minimizes smoothing.
	 */
	if ((function & (MF_BILINEAR|MF_NEAREST)) && near_unitary)
	    function = (function & MF_BILINEAR) ? MF_BILINEAR : MF_NEAREST;
	else if (function != (function & (MF_BILINEAR|MF_NEAREST)))
	    function &= ~(MF_BILINEAR|MF_NEAREST);

filter:
	/* This can take a while so update the display. */
	XFlush (w->gterm.display);

	/* If the filtering operation involves any arithmetic combinations
	 * of pixels we must convert pixel numbers to pixel intensity values
	 * before performing the filtering operation.  This is a case where
	 * we would be better off if frame buffers were maintained using
	 * pixel intensities rather than hardware pixel numbers.
	 */
	if (function != MF_NEAREST) {
            uchar *data = (uchar *) XtMalloc (xin->width * xin->height);
            if (data == NULL) {
                status = ERR;
                goto done;
            }

            mf_getinten (w,
		xin_lp, xin->width, xin->height, xin_bpl, sx,sy,
	        data, xin->width, xin->height, xin_bpl, sx,sy, snx,sny);

            if (!delxin) {
                xin = XCreateImage (w->gterm.display, NULL, RasterDepth,
                    ZPixmap, 0, (char *)data, xin->width, xin->height, 8, 0);
                if (xin == NULL) {
                    XtFree ((char *)data);
                    status = ERR;
                    goto done;
                }
                delxin++;
            } else {
                XtFree ((char *)xin->data);
                xin->data = (char *) data;
            }
            xin_lp = (uchar *)xin->data;
	}

	/* Filter the source rect to the destination. 
	*/
	switch (function) {
	case MF_NEAREST:
	    scale_nearest (
		xin_lp, xin->width, xin->height, xin_bpl,
		xout_lp, xout->width, xout->height, xout_bpl,
		x_src, y_src, ox, oy, dnx, dny,
		(clip && dr->type == ImageRaster) ? clip_region : (Region)NULL
	    );
	    break;
	case MF_BILINEAR:
	    scale_bilinear (
		xin_lp, xin->width, xin->height, xin_bpl,
		xout_lp, xout->width, xout->height, xout_bpl,
		x_src, y_src, ox, oy, dnx, dny,
		(clip && dr->type == ImageRaster) ? clip_region : (Region)NULL
	    );
	    break;
	case MF_BLKAVG:
	    scale_boxcar (
		xin_lp, xin->width, xin->height, xin_bpl,
		xout_lp, xout->width, xout->height, xout_bpl,
		x_src,y_src, sx,sy,snx,sny, ox,oy,dnx,dny,
		xscale,yscale, 0, clip ? clip_region : (Region)NULL
	    );
	    break;
	case MF_BOXCAR:
	    scale_boxcar (
		xin_lp, xin->width, xin->height, xin_bpl,
		xout_lp, xout->width, xout->height, xout_bpl,
		x_src,y_src, sx,sy,snx,sny, ox,oy,dnx,dny,
		xscale,yscale, 1, clip ? clip_region : (Region)NULL
	    );
	    break;
	case MF_LOWPASS:
	    scale_lowpass (
		xin_lp, xin->width, xin->height, xin_bpl,
		xout_lp, xout->width, xout->height, xout_bpl,
		x_src,y_src, sx,sy,snx,sny, ox,oy,dnx,dny,
		xscale,yscale, clip ? clip_region : (Region)NULL
	    );
	    break;
	default:
	    function = MF_BILINEAR;
	    goto filter;
	}

	/* If the operation was performed in intensity space convert back
	 * to pixel number.
	 */
	if (function != MF_NEAREST)
            mf_getpixel (w,
		xout_lp, xout->width, xout->height, xout_bpl,  ox,oy,
	        xout_lp, xout->width, xout->height, xout_bpl,  ox,oy, dnx,dny);

    } else {
	status = ERR;
	goto done;
    }

    /* Copy the scaled ximage to the output pixmap, if any.
     */
    if (dr->type == PixmapRaster) {
	if (DBG_TRACE)
	    fprintf (stderr, "refresh_destination: COPY TO OUTPUT PIXMAP\n");

	if (dst == 0 && w->gterm.pixmap && !(rop & R_Transient)) {

	    if (DBG_TRACE) {
		fprintf (stderr, "refresh_destination: dst=0\n");
		fprintf (stderr,
		    "refresh_destination: src=(%d,%d) => %d,%d : %d,%d\n",
		    ox, oy, dx, dy, dnx, dny);
	    }

	    XPutImage (display, 
		w->gterm.pixmap, w->gterm.exposeGC, 
		(img4=IMGtoGPM(w,xout,ox,oy,dnx,dny)),
		ox, oy, dx, dy, dnx, dny);

	    XCopyArea (display, 
		GPMtoRPM (w, dr), dr->r.pixmap,
		w->gterm.exposeGC, dx, dy, dnx, dny, dx, dy);

	    if (w->gterm.w_depth > ColormapDepth) {
		if (DBG_TRACE) {
		    fprintf (stderr,
			"updating shadow_pixmap........................0x%x\n",
			dr->shadow_pixmap);
		    fprintf (stderr,
			"updating %d,%d  to %d,%d  %d %d\n",
			ox, oy, dx, dy, dnx, dny);
		}
	        XPutImage (display, dr->shadow_pixmap,
		    w->gterm.expose8GC, xout, ox, oy, dx, dy, dnx, dny);
	    }

	} else {

	    if (DBG_TRACE) {
		fprintf (stderr, "refresh_destination: xout dst > 0\n");
		fprintf (stderr,
		    "refresh_destination: src=(%d,%d) => %d,%d : %d,%d\n",
		    ox, oy, dx, dy, dnx, dny);
	    }

	    XPutImage (display, 
		dr->r.pixmap, w->gterm.exposeGC,
		(img5=IMGtoRPM(w,xout,dr,ox,oy,dnx,dny)),
		ox, oy, dx, dy, dnx, dny);

	    if (w->gterm.w_depth > ColormapDepth) {
		/*
	        XPutImage (display, pr->r.pixmap, w->gterm.exposeGC, xout,
		    ox, oy, dx, dy, dnx, dny);
		*/

		/*  Shadow the pixmap with an 8-bit version for readback.
		*/
		if (DBG_TRACE) {
		    fprintf (stderr,
			"updating shadow_pixmap........................0x%x\n",
		        dr->shadow_pixmap);
		    fprintf (stderr,
			"updating %d,%d  to %d,%d  %d %d\n", 
			ox, oy, dx, dy, dnx, dny);
		}
	        XPutImage (display, dr->shadow_pixmap, 
		    w->gterm.expose8GC, xout,
		    ox, oy, dx, dy, dnx, dny);
	    }
	}
    }
    
done:
    /* Clean up.
     */
    if (delxin)  XDestroyImage (xin);
    if (delxout) XDestroyImage (xout);

    if (img1) XDestroyImage (img1);
    if (img2) XDestroyImage (img2);
    if (img3) XDestroyImage (img3);
    if (img4) XDestroyImage (img4);
    if (img5) XDestroyImage (img5);

    XDestroyRegion (clip_region);
    if (clip && dr->type == PixmapRaster)
	XSetClipMask (w->gterm.display, w->gterm.exposeGC, None);

    if (DBG_TRACE)
	fprintf (stderr, "refresh_destination: doing mappings  status=%d\n",
	    status);

    /* Execute any mappings defined on the raster just updated. */
    if (status == OK) {

	GtRefreshPixels (w, dst, GtPixel, dx, dy, dnx, dny);

	if (dst == 0) {
	    Region clip_region = (Region)NULL;
	    XRectangle r;

	    clip_region = XCreateRegion();
	    r.x = dx;  r.y = dy;
	    r.width = dnx;  r.height = dny;
	    XUnionRectWithRegion (&r, clip_region, clip_region);

	    update_transients (w, clip_region);
	    XDestroyRegion (clip_region);
	}
    }

    if (DBG_TRACE)
	fprintf(stderr, "refresh_destination:  LEAVING  status=%d\n", status);

    return (status);
}


/* scale_zoom -- Compute the given destination rect from the input image,
 * using pixel replication and the given x and y dst->scr pixels maps.
 */
static void
scale_zoom (uchar *idata, int ibpl, uchar *odata, int obpl, int *xmap, int *ymap, int dx, int dy, int dnx, int dny, Region clip_region)

                         		/* input, output data */
                   			/* bytes per line */
              			/* src coords of each dst pixel */
              				/* src coords of each dst pixel */
                         		/* destination rect */
                       			/* clip Region or null */
{
    int i, j;
    uchar *ip, *op;
    uchar *last_ip = NULL;
    uchar *last_op = NULL;

    for (j=0;  j < dny;  j++) {
	ip = idata + ymap[j] * ibpl;
	op = odata + (j+dy) * obpl + dx;

	if (!clip_region) {
	    if (ip == last_ip)
		memmove (op, last_op, dnx);
	    else {
		for (i=0;  i < dnx;  i++) {
		    op[i] = ip[xmap[i]];
		}
	    }
	    last_ip = ip;
	    last_op = op;
	} else {
	    for (i=0;  i < dnx;  i++)
		if (XPointInRegion (clip_region, i + dx, j + dy))
		    op[i] = ip[xmap[i]];
	}
    }
}


/* scale_intzoom -- Compute the given destination rect from the input image,
 * using pixel replication and integer scaling.  This is functionally
 * equivalent to scale_zoom using the lookup tables, but optimized for the
 * case of integer scaling.
 */
static void
scale_intzoom (uchar *idata, int ibpl, uchar *odata, int obpl, int sx, int sy, int dx, int dy, int dnx, int dny, int xflip, int yflip, int nx, int ny)

                         		/* input, output data */
                   			/* bytes per line */
               				/* start coords of src rect */
                         		/* destination rect */
                     			/* set if x or y is flipped */
               				/* replication factors */
{
    int n;
    int pix;
    uchar *ip, *op;
    uchar *otop, *olast, *lp;
    int i, j, k;

    olast = odata + (dy + dny) * obpl - dnx + dx;

    if (xflip) {
	for (j=0, k=0;  j < dny;  j += ny, k++) {
	    ip = idata + (sy + k) * ibpl + sx;

	    op = odata + (dy + (yflip ? (dny-ny-j) : j)) * obpl + dx + dnx;
	    otop = lp = op - dnx;


	    /* Why are the case statements below necessary, doesn't the
	     * default case do the same thing regardless of what nx is?  MJF
	     */

	    /* Replicate a block of pixels. */
	    switch (nx) {
	    case 2:
		for (n = (dnx/2);  --n >= 0;  ) {
		    pix = *ip++;
		    *--op = pix;  *--op = pix;
		}
		break;
	    case 3:
		for (n = (dnx/3);  --n >= 0;  ) {
		    pix = *ip++;
		    *--op = pix;  *--op = pix;  *--op = pix;
		}
		break;
	    case 4:
		for (n = (dnx/4);  --n >= 0;  ) {
		    pix = *ip++;
		    *--op = pix;  *--op = pix;
		    *--op = pix;  *--op = pix;
		}
		break;
	    case 5:
		for (n = (dnx/5);  --n >= 0;  ) {
		    pix = *ip++;
		    *--op = pix;  *--op = pix;  *--op = pix;
		    *--op = pix;  *--op = pix;
		}
		break;
	    case 6:
		for (n = (dnx/6);  --n >= 0;  ) {
		    pix = *ip++;
		    *--op = pix;  *--op = pix;  *--op = pix;
		    *--op = pix;  *--op = pix;  *--op = pix;
		}
		break;
	    case 7:
		for (n = (dnx/7);  --n >= 0;  ) {
		    pix = *ip++;
		    *--op = pix;  *--op = pix;  *--op = pix;
		    *--op = pix;  *--op = pix;
		    *--op = pix;  *--op = pix;
		}
		break;
	    case 8:
		for (n = (dnx/8);  --n >= 0;  ) {
		    pix = *ip++;
		    *--op = pix;  *--op = pix;
		    *--op = pix;  *--op = pix;
		    *--op = pix;  *--op = pix;
		    *--op = pix;  *--op = pix;
		}
		break;
	    default:
		for (n = (dnx/nx);  --n >= 0;  ) {
		    pix = *ip++;
		    for (i=nx;  --i >= 0;  )
			*--op = pix;
		}
		break;
	    }

	    /* Fill the last partial pixel. */
	    pix = *ip++;
	    while (op > otop)
		*--op = pix;

	    /* Replicate in Y. */
	    for (n=ny, op=lp;  --n > 0;  ) {
		op += obpl;
		if (op <= olast)
		    memmove (op, lp, dnx);
	    }
	}
    } else {
	for (j=0, k=0;  j < dny;  j += ny, k++) {
	    ip = idata + (sy + k) * ibpl + sx;
	    op = lp = odata + (dy + (yflip ? (dny-ny-j) : j)) * obpl + dx;
	    otop = op + dnx;

	    /* Replicate a block of pixels. */
	    switch (nx) {
	    case 2:
		for (n = (dnx/2);  --n >= 0;  ) {
		    pix = *ip++;
		    *op++ = pix;  *op++ = pix;
		}
		break;
	    case 3:
		for (n = (dnx/3);  --n >= 0;  ) {
		    pix = *ip++;
		    *op++ = pix;  *op++ = pix;  *op++ = pix;
		}
		break;
	    case 4:
		for (n = (dnx/4);  --n >= 0;  ) {
		    pix = *ip++;
		    *op++ = pix;  *op++ = pix;
		    *op++ = pix;  *op++ = pix;
		}
		break;
	    case 5:
		for (n = (dnx/5);  --n >= 0;  ) {
		    pix = *ip++;
		    *op++ = pix;  *op++ = pix;  *op++ = pix;
		    *op++ = pix;  *op++ = pix;
		}
		break;
	    case 6:
		for (n = (dnx/6);  --n >= 0;  ) {
		    pix = *ip++;
		    *op++ = pix;  *op++ = pix;  *op++ = pix;
		    *op++ = pix;  *op++ = pix;  *op++ = pix;
		}
		break;
	    case 7:
		for (n = (dnx/7);  --n >= 0;  ) {
		    pix = *ip++;
		    *op++ = pix;  *op++ = pix;  *op++ = pix;
		    *op++ = pix;  *op++ = pix;
		    *op++ = pix;  *op++ = pix;
		}
		break;
	    case 8:
		for (n = (dnx/8);  --n >= 0;  ) {
		    pix = *ip++;
		    *op++ = pix;  *op++ = pix;
		    *op++ = pix;  *op++ = pix;
		    *op++ = pix;  *op++ = pix;
		    *op++ = pix;  *op++ = pix;
		}
		break;
	    default:
		for (n = (dnx/nx);  --n >= 0;  ) {
		    pix = *ip++;
		    for (i=nx;  --i >= 0;  )
			*op++ = pix;
		}
		break;
	    }

	    /* Fill the last partial pixel. */
	    pix = *ip++;
	    while (op < otop)
		*op++ = pix;

	    /* Replicate in Y. */
	    for (n=ny, op=lp;  --n > 0;  ) {
		op += obpl;
		if (op <= olast)
		    memmove (op, lp, dnx);
	    }
	}
    }
}


/* scale_nearest -- Compute the given destination rect from the input image,
 * using the nearest neighbor technique.
 */
static void
scale_nearest (

    uchar *idata,			/* input data */
    int inx, int iny, int ibpl,		/* dimensions of input array */
    uchar *odata,			/* output data */
    int onx, int ony, int obpl,		/* dimensions of output array */
    float *x_src, float *y_src,		/* src coords of each dst pixel */
    int dx, int dy, int dnx, int dny,	/* destination rect */
    Region clip_region)			/* clip Region or null */
{
    int m, n, i, j;
    uchar *op;

    for (j=0;  j < dny;  j++) {
	op = odata + (j+dy) * obpl + dx;
	n = y_src[j];

	if (!clip_region) {
	    for (i=0;  i < dnx;  i++) {
		m = x_src[i];
		op[i] = idata[n * ibpl + m];
	    }
	} else {
	    for (i=0;  i < dnx;  i++)
		if (XPointInRegion (clip_region, i + dx, j + dy)) {
		    m = x_src[i];
		    op[i] = idata[n * ibpl + m];
		}
	}
    }
}


/* scale_bilinear -- Compute the given destination rect from the input image,
 * using bilinear interpolation.
 */
static void
scale_bilinear (

    uchar *idata,			/* input data */
    int inx, int iny, int ibpl,		/* dimensions of input array */
    uchar *odata,			/* output data */
    int onx, int ony, int obpl,		/* dimensions of output array */
    float *x_src, float *y_src,		/* src coords of each dst pixel */
    int dx, int dy, int dnx, int dny,	/* destination rect */
    Region clip_region)			/* clip Region or null */
{
    int i;
    uchar *op;
    float *lp, *w1, *w2;
    int buflen, line, *px, pixel, j;
    float lo_w, hi_w, x, y;
    uchar *lo, *hi;
    float *buf;

    buflen = (3 * dnx + 2) * sizeof(float) + dnx * sizeof(int);
    if ((buf = (float *) XtMalloc (buflen)) == NULL)
	return;

    lp = buf + 1;
    w1 = lp + dnx + 1;
    w2 = w1 + dnx;
    px = (int *)(w2 + dnx);

    /* Compute the weight vectors at each point in X. */
    for (i=0;  i < dnx;  i++) {
	x = x_src[i] - 0.5;
	px[i] = (int) x;
	w1[i] = 1.0 - (x - (int)x);
	w2[i] = 1.0 - w1[i];
    }

    /* For each line of the destination rect first interpolate in Y to the
     * y_src coordinate of the output line, then interpolate in X to compute
     * the final output pixels.
     */
    for (j=0;  j < dny;  j++) {
	op = odata + (j+dy) * obpl + dx;
	y = y_src[j] - 0.5;
	line = (int) y;
	lo = idata + line * ibpl;
	hi = idata + min (iny - 1, line + 1) * ibpl;
	lo_w = 1.0 - (y - line);
	hi_w = 1.0 - lo_w;

	/* Interpolate in Y to the line at y_src[j]. */
	for (i=0;  i < dnx;  i++) {
	    pixel = px[i];
	    lp[i] = (float)lo[pixel] * lo_w + (float)hi[pixel] * hi_w;
	}
	lp[-1]  = lp[0];
	lp[dnx] = lp[dnx-1];

	/* Interpolate in X to the final output pixels. */
	if (!clip_region) {
	    for (i=0;  i < dnx;  i++)
		op[i] = lp[i] * w1[i] + lp[i+1] * w2[i];
	} else {
	    for (i=0;  i < dnx;  i++)
		if (XPointInRegion (clip_region, i + dx, j + dy))
		    op[i] = lp[i] * w1[i] + lp[i+1] * w2[i];
	}
    }

    XtFree ((char *)buf);
}


/* scale_lowpass -- Apply a lowpass filter to a region of a 2D data array.
 * The region ox,oy,nx,ny of the output data array is calculated by running
 * a convolution kernel over the region of the input data array at ix,iy.
 * The size of the convolution kernel is adjusted to match the scale factors
 * xscale, yscale.
 */
static void
scale_lowpass (

    uchar *idata,			/* input data */
    int inx, int iny, int ibpl,		/* full input array */
    uchar *odata,			/* output data */
    int onx, int ony, int obpl,		/* full input array */
    float *x_src, float *y_src,		/* src coords of each dst pixel */
    int sx, int sy, int snx, int sny,	/* source rect */
    int dx, int dy, int dnx, int dny,	/* destination rect */
    float xscale, float yscale,		/* scale factors */
    Region clip_region)			/* clip Region or null */
{
    uchar *data;
    
    if ((data = (uchar *) XtMalloc (inx * iny)) == NULL)
	return;

    /* Run a lowpass filter over the input rect. */
    lw_convolve (idata,inx,iny,ibpl, sx,sy, data,inx,iny,ibpl, sx,sy,
	snx,sny, xscale,yscale);

    /* Sample the filtered data to generate the output rect. */
    scale_nearest (data,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src,
	dx,dy,dnx,dny, clip_region);

    XtFree ((char *)data);
}


/* lw_convolve -- Convolution primitive for scale_lowpass.
 */
static void
lw_convolve (
    uchar *idata, 			/* input data */
    int inx, int iny, int ix, int iy,	/* size of input array, start pos */
    int ibpl,				/* bytes per line */
    uchar *odata,			/* output data */
    int onx, int ony, int ox, int oy,	/* size of output array, start pos */
    int obpl,				/* bytes per line */
    int nx, int ny,				/* size of output region */
    float xscale, float yscale)		/* determines amount of smoothing */
{
    uchar *ip;
    int l, m, x, hx, pixval;
    int kx, ky, hy, i, j, y;
    uchar *lp[11], *op;

    /* Determine kernel size (min 3x3, max 7x7). */
    if (xscale < 0.1)
	hx = 3;
    else if (xscale >= 0.5)
	hx = 1;
    else
	hx = ((int)(1.0 / xscale)) / 2;

    if (yscale < 0.1)
	hy = 3;
    else if (yscale >= 0.5)
	hy = 1;
    else
	hy = ((int)(1.0 / yscale)) / 2;

    kx = hx * 2 + 1;
    ky = hy * 2 + 1;

    /* Compute the output data.
     */
    for (j=0;  j < ny;  j++) {
	/* Get line pointers. */
	op = odata + (j+oy) * obpl + ox;
	for (i=0;  i < ky;  i++) {
	    y = iy + j - hy + i;
	    if (y < 0)
		y = 0;
	    else if (y >= iny)
		y = iny - 1;
	    lp[i] = y * ibpl + idata;
	}

	/* Compute a line of output pixels */
	for (i=0;  i < nx;  i++) {
	    x = ix + i;
	    pixval = 0;

	    if (x < hx) {
		/* Near left edge. */
		for (m=0;  m < ky;  m++)
		    for (l=0;  l < kx;  l++)
			pixval += lp[m][max(0,x-hx+l)];
	    } else if (x >= inx - hx) {
		/* Near right edge. */
		for (m=0;  m < ky;  m++)
		    for (l=0;  l < kx;  l++)
			pixval += lp[m][min(inx-1,x-hx+l)];
	    } else {
		/* In central region. */
		for (m=0;  m < ky;  m++) {
		    ip = lp[m] + x - hx;
		    for (l=0;  l < kx;  l++)
			pixval += ip[l];
		}
	    }

	    op[i] = (float)pixval / (float)(kx * ky) + 0.5;
	}
    }
}


/* scale_boxcar -- Apply a boxcar filter to a region of a 2D data array
 * and interpolate the result to the output grid.  The region ox,oy,nx,ny of
 * the output data array is calculated by block averaging the corresponding
 * source rect and then sampling the reduced image using bilinear interpolation
 * to compute the output pixel raster.  This antialiasing technique aims to
 * be as fast as possible but still does a reasonable job of reducing the
 * image.
 */
static void
scale_boxcar (
    uchar *idata, 		/* input data */
    int inx, int iny, int ibpl,	/* full input array */
    uchar  *odata,		/* output data */
    int onx, int ony, int obpl,	/* full input array */
    float *x_src, float *y_src,	/* src coords of each dst pixel */
    int sx, int sy,
    int snx, int sny,		/* source rect */
    int dx, int dy,
    int dnx, int dny,		/* destination rect */
    float xscale, float yscale,	/* scale factors */
    int interp,			/* set if interpolation is desired */
    Region clip_region)		/* clip Region or null */
{
    int xblock, yblock;
    int x1, x2, y1, y2, nx, ny;
    float xstep, ystep;
    int xoff, yoff;
    uchar *bp;

    /* Determine blocking factors.   If interpolation is disabled we need
     * to block one step more than for the linear interpolate case in order
     * to ensure that all the data is used.
     */
    xblock = max(1, (int) (1.0 / xscale));
    if (!interp && (1.0 / xscale) - xblock > ZOOM_TOL)
	xblock++;
    yblock = max(1, (int) (1.0 / yscale));
    if (!interp && (1.0 / yscale) - yblock > ZOOM_TOL)
	yblock++;

    /* Align the input region for the given blocking factors. */
    x1 = sx / xblock * xblock;  x2 = (sx + snx - 1) / xblock * xblock;
    y1 = sy / yblock * yblock;  y2 = (sy + sny - 1) / yblock * yblock;
    nx = (x2 - x1) / xblock + 1;  ny = (y2 - y1) / yblock + 1;

    /* Compute the block averaged input rect.  */
    if (xblock > 1 || yblock > 1) {
	if ((bp = (uchar *) XtMalloc (nx * ny)) == NULL)
	    return;
	bx_boxcar (idata,inx,iny,ibpl, x1,y1,x2,y2, bp, xblock, yblock);
	idata = bp;
	inx = ibpl = nx;
	iny = ny;
	xoff = x1;  yoff = y1;
	xstep = 1.0 / xblock;  ystep = 1.0 / yblock;
    } else {
	bp = NULL;
	xoff = yoff = 0;
	xstep = ystep = 1.0;
    }

    /* Interpolate the input rect to the output grid. */
    if (interp &&
	((1.0 / xscale) - xblock) > ZOOM_TOL ||
	((1.0 / yscale) - yblock) > ZOOM_TOL) {

	/* Use bilinear interpolation to compute the output grid. */
	bx_interp (idata,inx,iny,ibpl, odata,onx,ony,obpl,
	    x_src,y_src, xoff,yoff,xstep,ystep, dx,dy,dnx,dny, clip_region);

    } else {
	/* Extract pixels from block averaged input data. */
	bx_extract (idata,inx,iny,ibpl, odata,onx,ony,obpl,
	    x_src,y_src, xoff,yoff,xstep,ystep, dx,dy,dnx,dny, clip_region);
    }

    if (bp)
	XtFree ((char *)bp);
}


/* bx_boxcar -- Block average primitive for scale_boxcar.
 */
static void
bx_boxcar (uchar *idata, int inx, int iny, int ibpl, int x1, int y1, int x2, int y2, uchar *obuf, int xblock, int yblock)
                 			/* input data array */
                       			/* array dimensions */
                    			/* region to be block averaged */
                			/* output array */
                       			/* blocking factors */
{
    uchar *ip, *op;
    int count, i, *sp;
    int obpl, block, nxblocks, nyblocks, j, k;
    uchar *lp, *bp;
    int *sb;

    nxblocks = obpl = (x2 - x1) / xblock + 1;
    nyblocks = (y2 - y1) / yblock + 1;
    count = xblock * yblock;

    if ((sb = (int *) XtMalloc (obpl * sizeof(int))) == NULL)
	return;

    /* I don't think the following works for pixel values allocated from the
     * default colormap, as the pixel values are not sequentially allocated.
     */
    for (block = 0;  block < nyblocks;  block++) {
	lp = idata + ibpl * (block * yblock + y1) + x1;
	bp = obuf + block * obpl;

	memset (sb, 0, obpl * sizeof(int));
	for (k=yblock;  --k >= 0;  lp += ibpl)
	    for (j=nxblocks, ip=lp, sp=sb;  --j >= 0;  sp++)
		for (i=xblock;  --i >= 0;  )
		    *sp += *ip++;

	for (i=obpl, sp=sb, op=bp;  --i >= 0;  )
	    *op++ = *sp++ / count;
    }

    XtFree ((char *)sb);
}


/* bx_extract -- Block extract primitive for scale_boxcar.
 */
static void
bx_extract (

    uchar *idata,			/* input, output data */
    int inx, int iny, int ibpl,		/* full input array */
    uchar *odata,			/* input, output data */
    int onx, int ony, int obpl,		/* full input array */
    float *x_src, float *y_src,		/* src coords of each dst pixel */
    int dx, int dy, int dnx, int dny,	/* destination rect */
    int xoff, int yoff,			/* offset of input region */
    float xstep, float ystep,		/* scale of input region */
    Region clip_region)			/* clip Region or null */
{
    int m, n, i;
    uchar *op;
    int j;

    for (j=0;  j < dny;  j++) {
	op = odata + (j+dy) * obpl + dx;
	n = (y_src[j] - yoff) * ystep;

	if (!clip_region) {
	    for (i=0;  i < dnx;  i++) {
		m = (x_src[i] - xoff) * xstep;
		op[i] = idata[n * ibpl + m];
	    }
	} else {
	    for (i=0;  i < dnx;  i++)
		if (XPointInRegion (clip_region, i + dx, j + dy)) {
		    m = (x_src[i] - xoff) * xstep;
		    op[i] = idata[n * ibpl + m];
		}
	}
    }
}


/* bx_interp -- Bilinear interpolation primitive for scale_boxcar.
 */
static void
bx_interp (

    uchar *idata,			/* input, output data */
    int inx, int iny, int ibpl,		/* dimensions of input array */
    uchar *odata,			/* input, output data */
    int onx, int ony, int obpl,		/* dimensions of output array */
    float *x_src, float *y_src,		/* src coords of each dst pixel */
    int xoff, int yoff,			/* offset of input region */
    float xstep, float ystep,		/* scale of input region */
    int dx, int dy, int dnx, int dny,	/* destination rect */
    Region clip_region)			/* clip Region or null */
{
    int i;
    uchar *op;
    float *lp, *w1, *w2;
    int buflen, line, *px, pixel, j;
    float lo_w, hi_w, x, y;
    uchar *lo, *hi;
    float *buf;

    buflen = (3 * dnx + 2) * sizeof(float) + dnx * sizeof(int);
    if ((buf = (float *) XtMalloc (buflen)) == NULL)
	return;

    lp = buf + 1;
    w1 = lp + dnx + 1;
    w2 = w1 + dnx;
    px = (int *)(w2 + dnx);

    /* Compute the weight vectors at each point in X. */
    for (i=0;  i < dnx;  i++) {
	x = ((x_src[i] - xoff) * xstep) - 0.5;
	px[i] = (int) x;
	w1[i] = 1.0 - (x - (int)x);
	w2[i] = 1.0 - w1[i];
    }

    /* For each line of the destination rect first interpolate in Y to the
     * y_src coordinate of the output line, then interpolate in X to compute
     * the final output pixels.
     */
    for (j=0;  j < dny;  j++) {
	op = odata + (j+dy) * obpl + dx;
	y = ((y_src[j] - yoff) * ystep) - 0.5;
	line = (int) y;
	lo = idata + line * ibpl;
	hi = idata + min (iny - 1, line + 1) * ibpl;
	lo_w = 1.0 - (y - line);
	hi_w = 1.0 - lo_w;

	/* Interpolate in Y to the line at y_src[j]. */
	for (i=0;  i < dnx;  i++) {
	    pixel = px[i];
	    lp[i] = (float)lo[pixel] * lo_w + (float)hi[pixel] * hi_w;
	}
	lp[-1]  = lp[0];
	lp[dnx] = lp[dnx-1];

	/* Interpolate in X to the final output pixels. */
	if (!clip_region) {
	    for (i=0;  i < dnx;  i++)
		op[i] = lp[i] * w1[i] + lp[i+1] * w2[i];
	} else {
	    for (i=0;  i < dnx;  i++)
		if (XPointInRegion (clip_region, i + dx, j + dy))
		    op[i] = lp[i] * w1[i] + lp[i+1] * w2[i];
	}
    }

    XtFree ((char *)buf);
}


/* mf_getinten -- Copy the source rect to the destination rect, converting
 * pixel numbers to pixel intensities.
 */
static void
mf_getinten (GtermWidget w, uchar *idata, int inx, int iny, int ibpl, int sx, int sy, uchar *odata, int onx, int ony, int obpl, int dx, int dy, int nx, int ny)

                  
                         		/* input, output data */
                       			/* dimensions of input array */
                       			/* dimensions of output array */
               				/* source offset */
               				/* destination offset */
               				/* size of region */
{
    Pixel *cmap;
    uchar *ip, *op;
    int n;
    int j;

    cmap = get_cmap_out (w);
    for (j=0;  j < ny;  j++) {
	ip = idata + ibpl * (sy + j) + sx;
	op = odata + obpl * (dy + j) + dx;
	for (n = nx;  --n >= 0;  )
	    *op++ = (cmap[*ip++]);
    }
}


/* mf_getpixel -- Copy the source rect to the destination rect, converting
 * pixel intensities to pixel numbers.
 */
static void
mf_getpixel (GtermWidget w, uchar *idata, int inx, int iny, int ibpl, int sx, int sy, uchar *odata, int onx, int ony, int obpl, int dx, int dy, int nx, int ny)

                  
                         		/* input, output data */
                       			/* dimensions of input array */
                       			/* dimensions of output array */
               				/* source offset */
               				/* destination offset */
               				/* size of region */
{
    Pixel *cmap;
    uchar *ip, *op;
    int n;
    int j;

    cmap = get_cmap_in (w);
    for (j=0;  j < ny;  j++) {
	ip = idata + ibpl * (sy + j) + sx;
	op = odata + obpl * (dy + j) + dx;
	for (n = nx;  --n >= 0;  )
	    *op++ = (cmap[*ip++]);
    }
}


/* get_regions -- For each pixel I in the sequence of DNX pixels starting at DX
 * there is an associated value XMAP[I].  Compare this sequence to an alternate
 * sequence and return a list of subregions {XS,XE,XV} for which the XMAP
 * values are equal (XV=0), not equal (XV=1), or not common to (XV=2) the two
 * regions.  The number of regions output is returned as the function value.
 */
static int
get_regions (int *xs, int *xe, int *xv, int max_regions, int dx, int dnx, int *xmap, int alt_dx, int alt_dnx, int *alt_xmap)
{
    int state, current;
    int nx, i;
    int offset, old_i;

    offset = dx - alt_dx;
    nx = 0;

    for (i=0;  i < dnx;  i++) {
	if (nx >= max_regions-1)
	    return (0);

	/* Determine whether or not this pixel is mapped the same in both
	 * sequences.
	 */
	old_i = i + offset;
	if (alt_dnx <= 0 || old_i < 0 || old_i >= alt_dnx)
	    state = 2;
	else
	    state = (xmap[i] != alt_xmap[old_i]);

	/* When the state boundary is crossed add a range. */
	if (i == 0) {
	    xs[nx] = dx;
	    xv[nx] = current = state;
	}
	if (state != current) {
	    xe[nx] = dx + i - 1;
	    xs[++nx] = dx + i;
	    xv[nx] = current = state;
	}
	if (i == dnx-1)
	    xe[nx++] = dx + i;
    }

    return (nx);
}


/* get_rects -- Combine two lists of regions, one in X and the other in Y,
 * to produce a list of 2D rectangles defining the regions outlined by the
 * region lists.  Only rects for which the given condition is true in either
 * X or Y are selected.  Adjacent rects are combined.
 */
static int
get_rects (XRectangle *o_rl, int max_rects, int *xs, int *xe, int *xv, int nx, int *ys, int *ye, int *yv, int ny, int xcond, int ycond)
                     		/* receives list of rectangles */
                  		/* max rectangles out */
                          	/* X list of regions */
                          	/* Y list of regions */
                     		/* X,Y condition bitflags */
{
    int i, j;
    XRectangle rl[MAX_REGIONS];
    int limit = min (max_rects, MAX_REGIONS);
    int o_nrects=0, nrects=0;
    int i1, i2, j1, j2;

    /* Get initial list of rects matching the given X and Y conditions.
     * Rects which are adjacent in X are combined to form one larger rect.
     */
    for (j=0;  j < ny;  j++) {
	for (i=0;  i < nx;  i++) {
	    if ((xv[i] & xcond) || (yv[j] & ycond)) {
		/* Collapse rects adjacent in X. */
		for (i1 = i2 = i++;  i < nx;  i++) {
		    if ((xv[i] & xcond) || (yv[j] & ycond))
			i2 = i;
		    else
			break;
		}

		rl[nrects].x = xs[i1];
		rl[nrects].y = ys[j];
		rl[nrects].width = xe[i2] - xs[i1] + 1;
		rl[nrects].height = ye[j] - ys[j] + 1;

		if (++nrects >= limit)
		    return (nrects);
	    }
	}
    }

    /* Now scan the rect list and collapse rects which are adjacent in Y
     * into one larger rect.  Find all the rects that are at the same X
     * and write them to the output list, collapsing rects that are adjacent
     * in Y in the process.
     */
    for (i=0;  i < nx;  i++)
	for (j=0;  j < nrects;  ) {
	    /* Find first rect if any. */
	    for (j1=0, j2 = -1;  j < nrects;  j++)
		if (rl[j].x == xs[i]) {
		    j1 = j2 = j++;
		    break;
		}
	    
	    /* Collapse rects adjacent in Y. */
	    for (   ;  j < nrects;  j++)
		if (rl[j].x == xs[i])
		    if (rl[j].y == rl[j2].y + rl[j2].height &&
			    rl[j].width == rl[j1].width)
			j2 = j;
		    else
			break;
	    
	    /* Output the rect. */
	    if (j2 >= j1) {
		o_rl[o_nrects].x = rl[j1].x;
		o_rl[o_nrects].y = rl[j1].y;
		o_rl[o_nrects].width = rl[j1].width;
		o_rl[o_nrects].height = rl[j2].y + rl[j2].height - rl[j1].y;

		if (++o_nrects >= max_rects)
		    return (o_nrects);
	    }
	}

    return (o_nrects);
}


/* rect_intersect -- Compute the intersection of two rects.  The area of
 * the intersection is returned as the function value, i.e., zero is
 * returned if the rects do not intersect.
 */
static int
rect_intersect (XRectangle *in, XRectangle *r1, XRectangle *r2)
{
    int x1, y1, x2, y2;

    x1 = max (r1->x, r2->x);
    y1 = max (r1->y, r2->y);
    x2 = min ((int)r1->x + (int)r1->width, (int)r2->x + (int)r2->width) - 1;
    y2 = min ((int)r1->y + (int)r1->height, (int)r2->y + (int)r2->height) - 1;

    in->x = x1;
    in->y = y1;
    in->width = max (0, x2 - x1 + 1);
    in->height = max (0, y2 - y1 + 1);

    return (in->width * in->height);
}


/* save_mapping -- Store a mapping in a mapping descriptor.
 */
static void 
save_mapping (Mapping mp, int mapping, int rop, int src, int st, int sx, int sy, int sw, int sh, int dst, int dt, int dx, int dy, int dw, int dh)
{
    mp->src = src;  mp->st = st;
	mp->sx = sx; mp->sy = sy; mp->snx = sw; mp->sny = sh;
    mp->dst = dst;  mp->dt = dt;
	mp->dx = dx; mp->dy = dy; mp->dnx = dw; mp->dny = dh;
    mp->defined = mp->enabled = mp->refresh = 1;
    mp->mapping = mapping;
    mp->rop = rop;
}

/* load_mapping -- Load a mapping from a mapping descriptor.
 */
static void
load_mapping (Mapping mp, int *mapping, int *rop, int *src, int *st, int *sx, int *sy, int *sw, int *sh, int *dst, int *dt, int *dx, int *dy, int *dw, int *dh)
{
    *src = mp->src;  *st = mp->st;
	*sx = mp->sx; *sy = mp->sy; *sw = mp->snx; *sh = mp->sny;
    *dst = mp->dst;  *dt = mp->dt;
	*dx = mp->dx; *dy = mp->dy; *dw = mp->dnx; *dh = mp->dny;
    *mapping = mp->mapping;
    *rop = mp->rop;
}


/* get_pixel_mapping -- Copy a mapping, converting to pixel coordinates in
 * the process if the mapping is not already in pixel coordinates.
 */
static void
get_pixel_mapping (GtermWidget w, Mapping mp1, Mapping mp2, int update)
                  
                		/* input mapping */
                		/* output mapping */
               				/* update mapping */
{
    float maxndc = (float)MAXNDC;

    mp2->mapping = mp1->mapping;
    mp2->refresh = mp1->refresh;
    mp2->enabled = mp1->enabled;
    mp2->rop = mp1->rop;

    if (!(mp2->defined = mp1->defined))
	return;

    mp2->src = mp1->src;
    if (mp1->st == GtPixel) {
	mp2->st = mp1->st;
	mp2->sx = mp1->sx;	mp2->sy = mp1->sy;
	mp2->snx = mp1->snx;	mp2->sny = mp1->sny;
    } else {
	float width  = w->gterm.rasters[mp1->src].width;
	float height = w->gterm.rasters[mp1->src].height;
	mp2->sx = mp1->sx * width / maxndc;
	mp2->sy = (MAXNDC - (mp1->sy + abs(mp1->sny))) * height / maxndc;
	mp2->snx = mp1->snx * width / maxndc;
	mp2->sny = mp1->sny * height / maxndc;		/* NDC flipped in Y */
	mp2->st = GtPixel;
    }

    mp2->dst = mp1->dst;
    if (mp1->dt == GtPixel) {
	mp2->dt = mp1->dt;
	mp2->dx = mp1->dx;	mp2->dy = mp1->dy;
	mp2->dnx = mp1->dnx;	mp2->dny = mp1->dny;
    } else {
	float width  = w->gterm.rasters[mp1->dst].width;
	float height = w->gterm.rasters[mp1->dst].height;
	mp2->dx = mp1->dx * width / maxndc;
	mp2->dy = (MAXNDC - (mp1->dy + abs(mp1->dny))) * height / maxndc;
	mp2->dnx = mp1->dnx * width / maxndc;
	mp2->dny = mp1->dny * -height / maxndc;		/* NDC flipped in Y */
	mp2->dt = GtPixel;
    }

    /* The lookup tables are already in pixel space, so we can propagate
     * these to the new mapping if the old mapping was updated.
     */
    if (update && mp1->updated) {
	if ((mp2->mapdata = (uchar *) XtMalloc (mp1->datalen))) {

	    memmove (mp2->mapdata, mp1->mapdata, mp1->datalen);
	    mp2->datalen = mp1->datalen;
	    mp2->scaling = mp1->scaling;
	    mp2->xscale = mp1->xscale;
	    mp2->yscale = mp1->yscale;

	    mp2->x_extent = (mapExtent *)
		((uchar *)mp1->x_extent - mp1->mapdata + mp2->mapdata);
	    mp2->y_extent = (mapExtent *)
		((uchar *)mp1->y_extent - mp1->mapdata + mp2->mapdata);
	    mp2->x_srcpix = (int *)
		((uchar *)mp1->x_srcpix - mp1->mapdata + mp2->mapdata);
	    mp2->y_srcpix = (int *)
		((uchar *)mp1->y_srcpix - mp1->mapdata + mp2->mapdata);
	    mp2->x_src = (float *)
		((uchar *)mp1->x_src - mp1->mapdata + mp2->mapdata);
	    mp2->y_src = (float *)
		((uchar *)mp1->y_src - mp1->mapdata + mp2->mapdata);

	    mp2->updated = 1;
	}
    } else
	mp2->updated = 0;
}

/* valid_mapping -- Perform some sanity checks on a mapping to verify that
 * it contains something meaningful.
 */
static int
valid_mapping (GtermWidget w, Mapping mp)
{
    int x, y;
    int snx, sny, dnx, dny;
    int s_width, s_height, d_width, d_height;
    Raster sr, dr;

    if (!mp || !mp->defined)
	return (False);

    if (mp->src < 0 || mp->src >= w->gterm.maxRasters)
	return (False);
    if (mp->dst < 0 || mp->dst >= w->gterm.maxRasters)
	return (False);

    sr = &w->gterm.rasters[mp->src];
    dr = &w->gterm.rasters[mp->dst];
    if (!sr->type || !dr->type)
	return (False);

    switch (mp->st) {
    case GtPixel:
	s_width = sr->width;  s_height = sr->height;
	break;
    case GtNDC:
	s_width = MAXNDC + 1;  s_height = MAXNDC + 1;
	break;
    default:
	return (False);
    }

    switch (mp->dt) {
    case GtPixel:
	d_width = dr->width;  d_height = dr->height;
	break;
    case GtNDC:
	d_width = MAXNDC + 1;  d_height = MAXNDC + 1;
	break;
    default:
	return (False);
    }

    snx = mp->snx;  dnx = abs(mp->dnx);
    sny = mp->sny;  dny = abs(mp->dny);
    if (snx <= 0 || dnx <= 0 || sny <= 0 || dny <= 0)
	return (False);

    x = mp->sx;  y = mp->sy;
    if (x < 0 || x >= s_width || y < 0 || y >= s_height)
	return (False);
    x = mp->sx + snx - 1;  y = mp->sy + sny - 1;
    if (x < 0 || x >= s_width || y < 0 || y >= s_height)
	return (False);

    x = mp->dx;  y = mp->dy;
    if (x < 0 || x >= d_width || y < 0 || y >= d_height)
	return (False);
    x = mp->dx + dnx - 1;  y = mp->dy + dny - 1;
    if (x < 0 || x >= d_width || y < 0 || y >= d_height)
	return (False);

    return (True);
}


/* initialize_mapping -- Initialize the contents of a mapping descriptor.
 */
static void
initialize_mapping (Mapping mp)
{
    memset ((char *)mp, 0, sizeof(struct mapping));
}


/* update_mapping -- Update the portion of a mapping descriptor used at
 * runtime to execute the mapping.  This information consists of several
 * lookup tables and other parameters describing how a destination pixel
 * maps back to a source pixel and vice versa.
 */
static void
update_mapping (GtermWidget w, Mapping mp)
{
    uchar *op;
    int i, j, k;
    int snx, sny, dnx, dny, sx, sy, dx, dy;
    int xmax, ymax, lo, hi, edge1, edge2;
    int temp, xflip=0, yflip=0;
    struct mapping p_mp;
    float pixwidth, *fp;
    int *ip;

    if (mp->updated)
	return;

    /* The internal lookup tables are in pixel units. */
    initialize_mapping (&p_mp);
    get_pixel_mapping (w, mp, &p_mp, 0);

    if ((snx = p_mp.snx) <= 0 || (sny = p_mp.sny) <= 0)
	return;

    if ((dnx = p_mp.dnx) < 0) {
	dnx = -dnx;
	xflip++;
    }
    if ((dny = p_mp.dny) < 0) {
	dny = -dny;
	yflip++;
    }

    sx   = p_mp.sx;
    sy   = p_mp.sy;
    dx   = p_mp.dx;
    dy   = p_mp.dy;
    xmax = dnx - 1;
    ymax = dny - 1;

    /* Discard the temporary mapping.
    free_mapping (w, &p_mp);
     */

    /* Get scale factors. */
    mp->xscale = (float)dnx / (float)snx;
    mp->yscale = (float)dny / (float)sny;

    /* Determine type of scaling to be used. */
    if (mp->xscale < 1.0 || mp->yscale < 1.0) {
	mp->scaling = M_DEZOOM;
    } else if (mp->xscale > 1.0 || mp->yscale > 1.0) {
	mp->scaling = M_ZOOM;
	if (abs(mp->xscale - (int)(mp->xscale+0.5)) < ZOOM_TOL &&
	    abs(mp->yscale - (int)(mp->yscale+0.5)) < ZOOM_TOL)
	    mp->scaling = M_INTZOOM;
    } else
	mp->scaling = (xflip || yflip) ? M_ZOOM : M_NOSCALING;

    /* Get a data buffer for the lookup tables. */
    mp->datalen =
	snx * sizeof(mapExtent) +		/* xy, extent */
	sny * sizeof(mapExtent) +
	dnx * sizeof(int) +			/* xy, srcpix */
	dny * sizeof(int) +
	dnx * sizeof(float) +			/* xy, src */
	dny * sizeof(float);

    if (mp->mapdata)
	mp->mapdata = (uchar *) XtRealloc ((char *)mp->mapdata, mp->datalen);
    else
	mp->mapdata = (uchar *) XtMalloc (mp->datalen);
    if (mp->mapdata == NULL)
	return;

    /* Set the table pointers. */
    op = mp->mapdata;
    mp->x_extent = (mapExtent *) op;	op += snx * sizeof(mapExtent);
    mp->y_extent = (mapExtent *) op;	op += sny * sizeof(mapExtent);
    mp->x_srcpix = (int *) op;		op += dnx * sizeof(int);
    mp->y_srcpix = (int *) op;		op += dny * sizeof(int);
    mp->x_src    = (float *) op;	op += dnx * sizeof(float);
    mp->y_src    = (float *) op;	op += dny * sizeof(float);

    /* Compute the backpointers to the source raster for each destination
     * pixel center.
     */
    for (i=0, ip = mp->x_srcpix, fp = mp->x_src;  i < dnx;  i++) {
	fp[i] = ((xflip ? xmax - i : i) + 0.5) / mp->xscale + sx;
	ip[i] = fp[i];
    }
    for (i=0, ip = mp->y_srcpix, fp = mp->y_src;  i < dny;  i++) {
	fp[i] = ((yflip ? ymax - i : i) + 0.5) / mp->yscale + sy;
	ip[i] = fp[i];
    }

    /* Compute the extent arrays.  These define the range of destination
     * pixels affected by each source pixel.
     */
    lo = dnx - 1 + dx;
    hi = dx;
    for (i=0;  i < snx;  i++) {
	mp->x_extent[i].lo = lo;
	mp->x_extent[i].hi = hi;
    }
    lo = dny - 1 + dy;
    hi = dy;
    for (i=0;  i < sny;  i++) {
	mp->y_extent[i].lo = lo;
	mp->y_extent[i].hi = hi;
    }

    /* Map the left and right or top and bottom edges of each destination
     * pixel back into the source rect and update the corresponding extent
     * entries to indicate that these source pixels are used to compute the
     * destination pixel.
     */
    pixwidth = 1.0 - ZOOM_TOL;

    for (i=0;  i < dnx;  i++) {
	edge1 = (xflip ? xmax - i : i) / mp->xscale;
	edge2 = (xflip ? xmax - (i-pixwidth) : (i+pixwidth)) / mp->xscale;
	if (edge1 > edge2) {
	    temp = edge1;  edge1 = edge2;  edge2 = temp;
	}
	edge1 = max (0, edge1);
	edge2 = min (snx - 1, edge2);

	for (j=edge1, k = dx + i;  j <= edge2;  j++) {
	    if (mp->x_extent[j].lo > k)
		mp->x_extent[j].lo = k;
	    if (mp->x_extent[j].hi < k)
		mp->x_extent[j].hi = k;
	}
    }

    for (i=0;  i < dny;  i++) {
	edge1 = (yflip ? ymax - i : i) / mp->yscale;
	edge2 = (yflip ? ymax - (i-pixwidth) : (i+pixwidth)) / mp->yscale;
	if (edge1 > edge2) {
	    temp = edge1;  edge1 = edge2;  edge2 = temp;
	}
	edge1 = max (0, edge1);
	edge2 = min (sny - 1, edge2);

	for (j=edge1, k = dy + i;  j <= edge2;  j++) {
	    if (mp->y_extent[j].lo > k)
		mp->y_extent[j].lo = k;
	    if (mp->y_extent[j].hi < k)
		mp->y_extent[j].hi = k;
	}
    }

    mp->updated = 1;
}


/* free_mapping -- Free any storage used internally by a mapping descriptor,
 * and deactivate the mapping.
 */
static void
free_mapping (GtermWidget w, Mapping mp)
{
    mp_unlink (w, mp);
    mp->defined = mp->enabled = mp->updated = 0;
    if (mp->mapdata) {
	XtFree ((char *) mp->mapdata);
	mp->mapdata = NULL;
	mp->datalen = 0;
	mp->x_extent = mp->y_extent = NULL;
	mp->x_srcpix = mp->y_srcpix = NULL;
	mp->x_src = mp->y_src = NULL;
	mp->updated = 0;
    }
}

static void
mp_linkafter (GtermWidget w, Mapping mp, Mapping ref_mp)
{
    Mapping map;

    /* Don't use the reference mapping unless it is already linked or
     * the list is empty.
     */
    if (w->gterm.mp_head) {
	for (map = w->gterm.mp_head;  map && map != ref_mp;  map = map->next)
	    ;
	if (map != ref_mp)
	    ref_mp = NULL;
    }

    mp->prev = ref_mp;
    mp->next = ref_mp ? ref_mp->next : NULL;
    if (ref_mp && ref_mp->next)
	ref_mp->next->prev = mp;
    if (ref_mp)
	ref_mp->next = mp;

    if (!w->gterm.mp_tail || ref_mp == w->gterm.mp_tail)
	w->gterm.mp_tail = mp;
    if (!w->gterm.mp_head)
	w->gterm.mp_head = mp;
}


static void
mp_unlink (GtermWidget w, Mapping mp)
{
    if (mp->prev)
	mp->prev->next = mp->next;
    if (mp->next)
	mp->next->prev = mp->prev;
    if (w->gterm.mp_head == mp)
	w->gterm.mp_head = mp->next;
    if (w->gterm.mp_tail == mp)
	w->gterm.mp_tail = mp->prev;

    mp->prev = mp->next = NULL;
}



