/* * IMAGING routines. * ----------------------- * Our strategy here is to support a range of visuals with pseudocolor being * preferred if available. All imaging is done internally using 8 bit images * and a max 256 element colormap. If the display hardware has a depth less * than 8 bits, e.g. for a monochrome display, the image is reduced to the * screen depth by some technique before being output to the display. * * Images (rasters) are implemented internally in Gterm using either ximages or * off screen pixmaps. Which format is used is decided at raster create time * and is controlled by a Gterm resource. This is transparent to the client * application. Currently only 8 bit rasters are supported. * * GtRasterInit (gt) * GtAssignRaster (gt, raster, drawable) * GtCreateRaster (gt, raster, type, width, height, depth) * GtDestroyRaster (gt, raster) * exists = GtQueryRaster (gt, raster, &type, &width, &height, &depth) * raster = GtNextRaster (gt) * GtSetRaster (gt, raster) * raster = GtGetRaster (gt) * n = GtNRasters (gt) * * GtWritePixels (gt, raster, pixels, nbits, x1, y1, nx, ny) * GtReadPixels (gt, raster, pixels, nbits, x1, y1, nx, ny) * GtSetPixels (gt, raster, ct, x1, y1, nx, ny, color, rop) * GtRefreshPixels (gt, raster, ct, x1, y1, nx, ny) * pixmap = GtExtractPixmap (gt, src, ct, x, y, width, height) * GtInsertPixmap (gt, pixmap, dst, ct, x, y, width, height) * * colormap = GtNextColormap (gt) * GtFreeColormap (gt, colormap) * GtWriteColormap (gt, colormap, first, nelem, r, g, b) * GtReadColormap (gt, colormap, first, nelem, r, g, b) * GtLoadColormap (gt, colormap, offset, scale) * exists = GtQueryColormap (gt, colormap, &first, &nelem, &maxelem) * GtWriteIomap (gt, iomap, first, nelem) * GtReadIomap (gt, iomap, first, nelem) * GtReadLUT (gt, lut, first, nelem) * pixel = GtGetClientPixel (gt, gterm_pixel) * * GtInitMappings (gt) * mapping = GtNextMapping (gt) * GtFreeMapping (gt, mapping) * GtRaiseMapping (gt, mapping, ref|NULL) * GtLowerMapping (gt, mapping, ref|NULL) * int = GtCompareMappings (gt, map1, map2) * GtEnableMapping (gt, mapping, refresh) * GtDisableMapping (gt, mapping, erase) * active = GtActiveMapping (gt, mapping) * GtRefreshMapping (gt, mapping) * raster = GtSelectRaster (gt, dras, dt, dx, dy, rt, &rx, &ry, &mapping) * GtSetDisplayRaster (gt, mapping) * * GtCopyRaster (gt, rop, * src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) * GtSetMapping (gt, mapping, rop, * src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) * GtGetMapping (gt, mapping, rop, * src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) * * GtMapVector (gt, mapping, dir, pv1, pv2, npts) * GtPixelToNDC (gt, raster, pv1, pv2, npts) * GtNDCToPixel (gt, raster, pv1, pv2, npts) * * GtDebug (gt, fp, what) * * In the case of CopyRaster or {Set|Get}Mapping, raster coordinates may be * specified in either raster pixel (GtPixel) units or in normalized device * coordinates (GtNDC) in the range 0-32767. * --------------------------------------------------------------------------- */ GtRasterInit (w) GtermWidget w; { register int i; register Raster rp; register struct colormap *cm; struct colormap *next_cm; invalidate_draw_context (w); /* Destroy any existing rasters. */ if (w->gterm.rasters) { for (i=1; i < w->gterm.maxRasters; i++) { if (w->gterm.rasters[i].type) GtDestroyRaster (w, i); } } /* Allocate the initially empty raster descriptors. */ XtFree ((char *)w->gterm.rasters); w->gterm.rasters = rp = (Raster) XtCalloc (w->gterm.maxRasters, sizeof (struct raster)); w->gterm.nrasters = 0; w->gterm.raster = 0; /* Raster 0 is the display window. */ if (DBG_TRACE) fprintf (stderr, "GtRasterInit: Init display PixmapRaster (%dx%d) depth=%d\n", w->core.width, w->core.height, w->core.depth); rp->type = PixmapRaster; rp->width = w->core.width; rp->height = w->core.height; rp->depth = w->core.depth; rp->r.pixmap = w->gterm.window; rp->delete = 0; rp->shadow_pixmap = XCreatePixmap (w->gterm.display, w->gterm.window, w->core.width, w->core.height, RasterDepth); /* Free any previously allocated colormap cells. */ if (! w->gterm.useGlobalCmap) { if (w->gterm.ncolors > SZ_STATIC_CMAP && w->gterm.useDefaultCM) { XFreeColors (w->gterm.display, w->core.colormap, &w->gterm.cmap[SZ_STATIC_CMAP], w->gterm.ncolors-SZ_STATIC_CMAP, 0); w->gterm.ncolors = SZ_STATIC_CMAP; invalidate_cmap (w); } } /* Free any client defined colormaps. */ for (cm = w->gterm.colormaps; cm; cm = next_cm) { next_cm = cm->next; XtFree ((char *)cm); } w->gterm.colormaps = NULL; /* Initialize the mappings. */ GtInitMappings (w); if (DBG_TRACE) fprintf (stderr, "GtRasterInit: After Init Mappings...Returning\n"); } initialize_shadow_pixmap (GtermWidget w, int dst) { Raster rp = (Raster) NULL; if (w->gterm.rasters) { rp = &w->gterm.rasters[dst]; if (dst == 0 || rp->type == PixmapRaster) { XFillRectangle (w->gterm.display, rp->shadow_pixmap, w->gterm.clear8GC, 0, 0, rp->width, rp->height); } } } /* GtNextRaster -- Return the index of the next unused raster. */ GtNextRaster (w) register GtermWidget w; { register int i; if (w->gterm.rasters) for (i=1; i < w->gterm.maxRasters; i++) if (!w->gterm.rasters[i].type) return (i); return (-1); } /* GtNRasters -- Return the number of currently defined rasters. */ GtNRasters (w) GtermWidget w; { return (w->gterm.nrasters); } /* GtAssignRaster -- Assign a raster descriptor to an externally created * drawable (window or pixmap). The raster thus created may be mapped or * drawn into like the rasters created privately by the imaging code, but * this allows use of this code to access other windows, or shared pixmaps. */ GtAssignRaster (w, raster, drawable, type) GtermWidget w; int raster; /* one-indexed */ XtPointer drawable; /* object containing pixel array */ int type; /* type of drawable [not used] */ { register Raster rp; XWindowAttributes wa; if (raster <= 0 || raster >= w->gterm.maxRasters) return (ERR); else rp = &w->gterm.rasters[raster]; if (!w->gterm.wa_defined) { if (!XGetWindowAttributes (w->gterm.display, (Window)drawable, &wa)) return (ERR); } else wa = w->gterm.wa; rp->type = PixmapRaster; rp->width = wa.width; rp->height = wa.height; rp->depth = wa.depth; rp->r.pixmap = (Pixmap) drawable; rp->delete = 0; return (OK); } /* GtCreateRaster -- Create a new raster of the given size. A server pixmap * (GtServer) or ximage (GtClient) raster will be created depending upon the * current value of the cacheRasters resource. */ GtCreateRaster (w, raster, type, width, height, depth) GtermWidget w; int raster; /* one-indexed */ int type; int width, height; int depth; { register uchar *op; register int npix, pixel; uchar *data; XImage *xp; Raster rp; int cache; if (!XtIsRealized ((Widget)w)) return (ERR); if (DBG_TRACE) { fprintf (stderr, "GtCreateRaster: raster=%d type=%s (%dx%dx%d)\n", raster, ((type == GtClient) ? "GtClient" : "GtServer"), width, height, depth); } /* Only rasters of depth 8 bits are currently supported. Eventually ** we may want to allow arbitrarily deep frame buffers (e.g. for RGB ** composites, overlay planes, etc). if (depth && depth != 8) { if (DBG_TRACE) fprintf (stderr, "GtCreateRaster ERROR: attempt to create raster depth=%d\n", depth); return (ERR); } */ /* Check for a raster number in bounds. */ if (raster < 0 || raster >= w->gterm.maxRasters) { if (DBG_TRACE) fprintf (stderr, "GtCreateRaster ERROR: invalid raster = %d\n", raster); return (ERR); } else rp = &w->gterm.rasters[raster]; /* A create on raster 0 (the display window) is treated as an attempt * to resize the window. */ if (raster == 0) { XWindowAttributes wa; invalidate_draw_context (w); /* Issue the resize request. */ XtVaSetValues ((Widget)w, XtNwidth, (XtArgVal)width, XtNheight, (XtArgVal)height, NULL); XFlush (w->gterm.display); /* The following generates a round trip request to the server and * is an attempt to allow the window system time to process the * resize request before the client can issue a GtQueryRaster to * see if the request has succeeded (hence causing a race condition). * If the window is not the requested size the delay flag is set * to cause graphics input processing to be suspended until the * window is resized or redisplayed. A dummy expose event is * generated to clear the delay condition in case the resize request * is not granted. */ if (XGetWindowAttributes (w->gterm.display, w->gterm.window, &wa)) { rp->width = wa.width; rp->height = wa.height; if (rp->width != width || rp->height != height) { XExposeEvent ev; ev.type = Expose; ev.send_event = True; ev.display = w->gterm.display; ev.window = w->gterm.window; ev.x = ev.y = 0; ev.width = ev.height = 1; ev.count = 0; XSendEvent (w->gterm.display, w->gterm.window, False, NoEventMask, (XEvent *)&ev); w->gterm.delay = 1; } } return (OK); } /* Get rid of any old raster. */ GtDestroyRaster (w, raster); rp->width = width; rp->height = height; rp->depth = depth; rp->delete = 1; /* Cache the raster? */ if (strcmp (w->gterm.cacheRasters, "always") == 0) cache = 1; else if (strcmp (w->gterm.cacheRasters, "never") == 0) cache = 0; else cache = (type == GtServer); if (DBG_TRACE) { fprintf (stderr, "GtCreateRaster: cacheRasters = '%s' type=%s cache=%d\n", w->gterm.cacheRasters, (type == GtServer ? "GtServer" : "GtClient"), cache); fprintf (stderr,"GtCreateRaster: cache=%d (%dx%dx%d)\n", cache, width, height, depth); fprintf (stderr,"GtCreateRaster: Creating %s: %d\n", (cache ? "PIXMAPRASTER" : "IMAGERASTER"), raster); } /* Create new raster. */ if (cache) { /* Create a pixmap. */ rp->type = PixmapRaster; rp->depth = depth; rp->r.pixmap = XCreatePixmap (w->gterm.display, w->gterm.window, width, height, depth); if (DBG_TRACE) fprintf (stderr, "GtCreateRaster: [pixmap] creating shadow pixmap %dx%d\n", w->core.width+1, w->core.height+1); rp->shadow_pixmap = XCreatePixmap (w->gterm.display, w->gterm.window, w->core.width+1, w->core.height+1, RasterDepth); if (rp->r.pixmap == (Pixmap) NULL) goto ximage; XFillRectangle (w->gterm.display, rp->r.pixmap, (depth == RasterDepth ? w->gterm.clear8GC : w->gterm.clearGC), 0, 0, width, height); } else { /* Create an XImage for the raster. */ ximage: rp->type = ImageRaster; rp->depth = depth; /* Get pixel storage. */ if ((data = (uchar *) XtMalloc((npix=width*height))) == NULL) return (ERR); else { for (op=data, pixel=w->gterm.color0; --npix >= 0; ) *op++ = pixel; } /* The following doesn't yet deal properly with byte and bit ordering * differences between the server and client. */ rp->r.ximage = xp = XCreateImage (w->gterm.display, NULL, RasterDepth, ZPixmap, 0, (char *)data, width, height, 8, 0); if (xp == NULL) { rp->type = 0; return (ERR); } if (DBG_TRACE) fprintf (stderr, "GtCreateRaster: [ximage] creating shadow pixmap %dx%d\n", w->core.width+1, w->core.height+1); rp->shadow_pixmap = XCreatePixmap (w->gterm.display, w->gterm.window, w->core.width+1, w->core.height+1, RasterDepth); } w->gterm.nrasters++; if (DBG_TRACE) { int i; fprintf (stderr, "GtCreateRaster: LEAVING nraster=%d\n", w->gterm.nrasters); for (i=0; i < w->gterm.nrasters; i++) fprintf (stderr, "GtCreateRaster[%d]: type=%8s %dx%d [%d]\n", i, (w->gterm.rasters[i].type==GtClient)?"GtClient":"GtServer", w->gterm.rasters[i].width, w->gterm.rasters[i].height, w->gterm.rasters[i].depth); } return (OK); } /* GtDestroyRaster -- Destroy a raster. Any mappings which reference the * raster are deactivated, and all storage associated with the raster is freed. */ GtDestroyRaster (w, raster) GtermWidget w; int raster; { register Raster rp; register Mapping mp, next; if (raster <= 0) return; invalidate_draw_context (w); /* Disable any mappings that reference this raster. */ for (mp = w->gterm.mp_head; mp; mp = next) { next = mp->next; if (mp->src == raster || mp->dst == raster) free_mapping (w, mp); } /* Destroy the raster. */ rp = &w->gterm.rasters[raster]; if (rp->type) { if (rp->delete) { if (rp->type == ImageRaster) XDestroyImage (rp->r.ximage); else if (rp->type == PixmapRaster) XFreePixmap (w->gterm.display, rp->r.pixmap); } w->gterm.nrasters--; rp->type = 0; rp->delete = 0; } } /* GtQueryRaster -- Determine whether a raster exists and if so return its * size. */ GtQueryRaster (w, raster, type, width, height, depth) GtermWidget w; int raster; /* one-indexed */ int *type; int *width, *height; int *depth; { register Raster rp; if (DBG_TRACE && DBG_VERBOSE) fprintf (stderr, "GtQueryRaster: raster=%d\n", raster); if (raster < 0 || raster > w->gterm.maxRasters) return (0); rp = &w->gterm.rasters[raster]; if (rp->type) { if (type) { if (rp->type == PixmapRaster) *type = GtServer; else *type = GtClient; } if (width) *width = rp->width; if (height) *height = rp->height; if (depth) *depth = rp->depth; if (DBG_TRACE && DBG_VERBOSE) fprintf (stderr, "GtQueryRaster: raster=%d (%s) w=%d h=%d d=%d\n", raster, ((*type == PixmapRaster) ? "GtServer" : "GtClient"), *width, *height, *depth); return (1); } else return (0); } /* GtWritePixels -- Write to a rectangular region of a raster. If any * mappings are currently defined which reference this raster as the source, * and a mapped region is being rewritten, the affected pixels will be * refreshed by the mapping. */ GtWritePixels (w, raster, pixels, nbits, x1, y1, nx, ny) GtermWidget w; int raster; uchar *pixels; int nbits; /* not used */ int x1, y1; int nx, ny; { register uchar *ip, *op; register Pixel *cmap; register int i, n, bytes_per_line; Mapping mp; Raster rp; uchar *lp; XWindowAttributes wa; unsigned int *ras = NULL; rp = &w->gterm.rasters[raster]; if (DBG_TRACE) fprintf(stderr, "GtWritePixels[%s] ENTER: nbits=%d raster=%d type='%s' wa=0x%x\n", dbg_wSize(w), nbits, raster, (rp->type == PixmapRaster) ? "PixmapRaster" : "ImageRaster", w->gterm.wa); if (rp->type == 0) return (ERR); /* Perform some range checks. */ if (x1 < 0 || x1 > rp->width || nx <= 0 || x1+nx > rp->width) return (ERR); if (y1 < 0 || y1 > rp->height || ny <= 0 || y1+ny > rp->height) return (ERR); if (!w->gterm.wa_defined) { if (!XGetWindowAttributes (w->gterm.display, w->gterm.window, &wa)) { fprintf (stderr, "GtWritePixels: Error getting window attrs\n"); return (ERR); } } else wa = w->gterm.wa; if (DBG_TRACE) fprintf (stderr, "GtWritePixels: window depth=%d RasterDepth= %d class=%s\n", wa.depth, RasterDepth, dbg_visStr(wa.visual->class)); if (rp->type == PixmapRaster) { Display *display = w->gterm.display; XImage *ximage; uchar *data; int npix; if (DBG_TRACE) fprintf(stderr, "GtWritePix: Doing PixmapRaster[%s]....depth=%d\n", dbg_wSize(w), wa.depth); /* Get a data buffer. */ if ((data = (uchar *)XtMalloc (npix = nx * ny)) == NULL) return (ERR); /* Convert the pixel values to colormap indices. */ cmap = get_cmap_in (w); for (ip=pixels, op=data, n=npix; --n >= 0; ) { /* In TrueColor mode, we preserve the index values in the ** XImage and apply the colormap when rendering. */ *op++ = (wa.depth == ColormapDepth ? (cmap[*ip++] & 0377) : ((*ip++) & 0377)); } if (DBG_TRACE) fprintf(stderr, "GtWritePix: Creating 8-bit ximage\n"); ximage = XCreateImage (w->gterm.display, NULL, RasterDepth, ZPixmap, 0, (char *)data, nx, ny, 8, 0); if (raster == 0 && w->gterm.pixmap) { if (DBG_TRACE) fprintf(stderr, "GtWritePix: type = pixmap, raster=0\n"); XPutImage (display, w->gterm.pixmap, w->gterm.exposeGC, IMGtoGPM(w,ximage,0,0,nx,ny), 0, 0, x1, y1, nx, ny); XCopyArea (display, GPMtoRPM(w, rp), rp->r.pixmap, w->gterm.exposeGC, x1, y1, nx, ny, x1, y1); } else { XPutImage (display, rp->r.pixmap, w->gterm.exposeGC, IMGtoRPM (w,ximage,rp,0,0,nx,ny), 0, 0, x1, y1, nx, ny); } XtFree ((char *)data); ximage->data = NULL; XDestroyImage (ximage); } else if (rp->type == ImageRaster) { int min=256, max=0; int min1=256, max1=0; if (DBG_TRACE) fprintf (stderr, "GtWritePix: ImageRaster....bytes_per_line=%d ras depth=%d\n", rp->r.ximage->bytes_per_line, rp->r.ximage->depth); cmap = get_cmap_in (w); bytes_per_line = rp->r.ximage->bytes_per_line; lp = (uchar *)rp->r.ximage->data + y1 * bytes_per_line + x1; ip = pixels; if (DBG_TRACE) fprintf(stderr, "GtWritePix: Doing ColormapDepth....writing to ximage\n"); /* Copy the data into the ximage data raster, converting input * pixels to Xlib pixel values in the process. * * Possibly this should be done at Pixmap write time rather than * during raster i/o so that the image pixel values are preserved. * Otherwise reading back pixels is difficult and if the color map * is dynamically modified the original pixel values may be lost. * Postponing display pixel value generation would also make it * easy to add support later for image depths other than 8 bit. * Doing the conversion to display pixels here is however simpler * and more efficient so that is how we do it for now. */ for (i=0; i < ny; i++) { for (n=nx, op=lp; --n >= 0; ) { if (wa.depth == ColormapDepth) *op++ = (cmap[*ip++] & 0xFF); else *op++ = (*ip++ & 0xFF); if (DBG_TRACE) { if (*ip < min) min = *ip; if (*ip > max) max = *ip; if (cmap[*ip] < min1) min1 = cmap[*ip]; if (cmap[*ip] > max1) max1 = cmap[*ip]; } } lp += bytes_per_line; } if (DBG_TRACE) fprintf (stderr, "CMAP MINMAX: %d %d -- %d %d\n", min, max, min1, max1); } /* Execute any mappings that reference this raster. */ for (mp = w->gterm.mp_head; mp; mp = mp->next) { if (wa.depth != ColormapDepth) { /* FIXME */ update_mapping (w, mp); refresh_source (w, mp, x1, y1, nx, ny); /* break; */ } else if (mp->enabled && mp->src == raster) { struct mapping *map=mp, p_mp; if (map->st != GtPixel || map->dt != GtPixel) { initialize_mapping (&p_mp); get_pixel_mapping (w, map, &p_mp, 1); update_mapping (w, map = &p_mp); } else update_mapping (w, map); refresh_source (w, map, x1, y1, nx, ny); if (map == &p_mp) free_mapping (w, map); } } if (DBG_TRACE) fprintf(stderr, "GtWritePixels[%s] LEAVING....\n", dbg_wSize(w)); return (OK); } /* GtReadPixels -- Read a rectangular region of a raster. */ GtReadPixels (w, raster, pixels, nbits, x1, y1, nx, ny) GtermWidget w; int raster; uchar *pixels; int nbits; /* not used */ int x1, y1; int nx, ny; { register uchar *ip, *op; register Pixel *cmap; register int n; int bytes_per_line, i, nskip = 1; int x, y, delxin = 0; XImage *xin; Raster rp; uchar *lp; if (DBG_TRACE) fprintf (stderr, "GtReadPixels: ras=%d %d %d %d %d w->depth=%d\n", raster, x1, y1, nx, ny, w->gterm.w_depth); rp = &w->gterm.rasters[raster]; if (rp->type == 0) return (ERR); /* Perform some range checks. */ if (x1 < 0 || x1 > rp->width || nx <= 0 || x1+nx > rp->width) return (ERR); if (y1 < 0 || y1 > rp->height || ny <= 0 || y1+ny > rp->height) return (ERR); /* Get the input ximage. if (rp->type == PixmapRaster) { */ if (rp->type == PixmapRaster || (raster == 0 && w->gterm.w_depth > 8)) { Display *display = w->gterm.display; if (DBG_TRACE) fprintf (stderr, "GtReadPixels: rp->type == PixmapRaster [%d]\n", raster); /* Read the pixmap subraster into an ximage. If we are reading the * screen (raster == 0) and we have an off-screen backing store pixmap, * use that instead of the screen. */ if (w->gterm.w_depth > ColormapDepth) { Raster ras = (Raster) NULL; if (raster) ras = &w->gterm.rasters[w->gterm.d_raster]; else ras = &w->gterm.rasters[0]; xin = XGetImage (display, (raster == 0 && w->gterm.pixmap) ? ras->shadow_pixmap : rp->r.pixmap, x1, y1, nx, ny, 0xff, ZPixmap); } else { xin = XGetImage (display, (raster == 0 && w->gterm.pixmap) ? w->gterm.pixmap : rp->r.pixmap, x1, y1, nx, ny, 0xff, ZPixmap); } if (xin == NULL) return (ERR); delxin++; x = y = 0; } else { xin = rp->r.ximage; x = x1; y = y1; } nskip = xin->bits_per_pixel / 8; if (DBG_TRACE) fprintf (stderr, "GtReadPixels: xin->bpp=%d bpl=%d nskip=%d %d,%d %dx%d\n", xin->bits_per_pixel, xin->bytes_per_line, nskip, x1, y1, nx, ny); if (w->gterm.w_depth == ColormapDepth) cmap = get_cmap_out (w); bytes_per_line = xin->bytes_per_line; lp = (uchar *)xin->data + (y * bytes_per_line + (nskip * x)); op = pixels; /* Copy the data to the output buffer, converting display pixels to * client pixels in the process. */ for (i=0; i < ny; i++) { for (n=nx, ip=lp; --n >= 0; ) { if (w->gterm.w_depth == ColormapDepth) { *op++ = cmap[*ip]; } else { *op++ = *ip; } ip += nskip; } lp += bytes_per_line; } if (delxin) XDestroyImage (xin); return (OK); } /* GtSetPixels -- Set all the raster pixels in a region to a single color. * If nx=ny=0 the entire raster will be written. */ GtSetPixels (w, raster, ct, x1, y1, nx, ny, color, rop) GtermWidget w; int raster; int ct; int x1, y1; int nx, ny; int color; int rop; { register Raster rp; Mapping mp; /* Get raster pointer. */ rp = &w->gterm.rasters[raster]; if (rp->type == 0) return (ERR); /* Get pixel coordinates. */ if (ct != GtPixel) { struct mapping sv_mp, p_mp; initialize_mapping (&sv_mp); /* MF035 */ save_mapping (&sv_mp, 0, 0, 0, GtPixel, 0,0,0,0, raster, ct, x1,y1,nx,ny); get_pixel_mapping (w, &sv_mp, &p_mp, 0); x1 = p_mp.dx; y1 = p_mp.dy; nx = p_mp.dnx; ny = p_mp.dny; } /* Perform some range checks. */ if (x1 == 0 && y1 == 0 && nx == 0 && ny == 0) { nx = rp->width; ny = rp->height; } else { if (x1 < 0 || x1 > rp->width || nx <= 0 || x1+nx > rp->width) return (ERR); if (y1 < 0 || y1 > rp->height || ny <= 0 || y1+ny > rp->height) return (ERR); } /* Set the pixels. */ if (rp->type == PixmapRaster) { Display *display = w->gterm.display; GC gc = w->gterm.clearGC; int use_backing_store; Raster sp = &w->gterm.rasters[0]; use_backing_store = (raster == 0 && w->gterm.pixmap && !(rop & R_Transient)); XSetForeground (display, gc, get_pixel(w,color)); XFillRectangle (display, rp->r.pixmap, gc, x1, y1, nx, ny); if (use_backing_store) { XFillRectangle (display, w->gterm.pixmap, gc, x1, y1, nx, ny); XFillRectangle (display, sp->shadow_pixmap, w->gterm.clear8GC, x1, y1, nx, ny); } XSetForeground (display, gc, w->gterm.color0); } else { register int n, i; register uchar *op; register Pixel pixel; int bytes_per_line; uchar *lp; pixel = get_pixel (w, color); bytes_per_line = rp->r.ximage->bytes_per_line; lp = (uchar *)rp->r.ximage->data + y1 * bytes_per_line + x1; /* Set all pixels in the indicated region. */ for (i=0; i < ny; i++) { for (n=nx, op=lp; --n >= 0; ) *op++ = pixel; lp += bytes_per_line; } } /* Execute any mappings that reference this raster. */ for (mp = w->gterm.mp_head; mp; mp = mp->next) { if (mp->enabled && mp->src == raster) { struct mapping *map=mp, p_mp; if (map->st != GtPixel || map->dt != GtPixel) { initialize_mapping (&p_mp); get_pixel_mapping (w, map, &p_mp, 1); update_mapping (w, map = &p_mp); } else update_mapping (w, map); refresh_source (w, map, x1, y1, nx, ny); if (map == &p_mp) free_mapping (w, map); } } return (OK); } /* GtRefreshPixels -- Update any mappings defined upon the given region of * the given source raster, as if the pixel values had been set with a * write pixels call. */ GtRefreshPixels (w, raster, ct, x1, y1, nx, ny) GtermWidget w; int raster; int ct; int x1, y1; int nx, ny; { register Raster rp = &w->gterm.rasters[raster]; register Mapping mp; if (!w || !XtIsRealized ((Widget)w)) return; if (DBG_TRACE) fprintf (stderr, "GtRefreshPixels: ENTER\n"); /* Get pixel coordinates. */ if (ct != GtPixel) { struct mapping sv_mp, p_mp; initialize_mapping (&sv_mp); save_mapping (&sv_mp, 0, 0, raster, ct, x1,y1,nx,ny, 0, GtPixel, 0,0,0,0); get_pixel_mapping (w, &sv_mp, &p_mp, 0); x1 = p_mp.sx; y1 = p_mp.sy; nx = p_mp.snx; ny = p_mp.sny; } /* Execute any mappings that reference this raster. */ for (mp = w->gterm.mp_head; mp; mp = mp->next) { if (DBG_TRACE) fprintf (stderr, "GtRefreshPixels: mp=0x%x enabled=%d src=%d/%d\n", mp, mp->enabled, mp->src, raster); if (mp->enabled && mp->src == raster) { struct mapping *map=mp, p_mp; if (map->st != GtPixel || map->dt != GtPixel) { if (DBG_TRACE) fprintf (stderr,"GtRefreshPixels: update pixtype raster\n"); initialize_mapping (&p_mp); get_pixel_mapping (w, map, &p_mp, 1); update_mapping (w, map = &p_mp); } else { if (DBG_TRACE) fprintf (stderr,"GtRefreshPixels: update non-pix raster\n"); update_mapping (w, map); } refresh_source (w, map, x1, y1, nx, ny); if (map == &p_mp) free_mapping (w, map); } } if (DBG_TRACE) fprintf (stderr, "GtRefreshPixels: LEAVING\n"); } /* GtExtractPixmap -- Extract a rectangular region of a raster and return * as a pixmap. The caller is responsible for later deleting this pixmap. */ Pixmap GtExtractPixmap (w, src, ctype, x, y, width, height) GtermWidget w; int src; int ctype; int x, y; int width, height; { register Raster rp; int x1, y1, nx, ny; String cache; int i; rp = &w->gterm.rasters[src]; if (!rp->type) return ((Pixmap)NULL); /* If width and height are zero, return the full raster. */ if (width <= 0) width = rp->width; if (height <= 0) height = rp->height; /* Get pixel coordinates. */ if (ctype != GtPixel) { struct mapping sv_mp, p_mp; initialize_mapping (&sv_mp); /* MF035 */ save_mapping (&sv_mp, 0, 0, 0, GtPixel, 0,0,0,0, src, ctype, x,y,width,height); get_pixel_mapping (w, &sv_mp, &p_mp, 0); x1 = p_mp.dx; y1 = p_mp.dy; nx = p_mp.dnx; ny = p_mp.dny; } else { x1 = x; y1 = y; nx = width; ny = height; } /* Find any empty raster slot and use it to generate the output pixmap. */ for (i=0; i < w->gterm.maxRasters; i++) { rp = &w->gterm.rasters[i]; if (!rp->type) { cache = w->gterm.cacheRasters; w->gterm.cacheRasters = "always"; if (GtCreateRaster (w, i, GtServer, nx, ny, /* MF006 */ RasterDepth) == ERR) { w->gterm.cacheRasters = cache; return ((Pixmap)NULL); } else if (rp->type != PixmapRaster) goto err; if (GtCopyRaster (w, 0, src,0, x1,y1,nx,ny, i,0, 0,0,nx,ny) == ERR) { err: GtDestroyRaster (w, i); /* MF005 */ w->gterm.cacheRasters = cache; return ((Pixmap)NULL); } rp->type = 0; w->gterm.nrasters--; w->gterm.cacheRasters = cache; return (rp->r.pixmap); } } return ((Pixmap)NULL); } /* GtInsertPixmap -- Insert the contents of the given pixmap into a raster * at the indicated coordinates. */ GtInsertPixmap (w, pixmap, dst, ctype, x, y, width, height) GtermWidget w; Pixmap pixmap; int dst; int ctype; int x, y; int width, height; { register Raster rp; XWindowAttributes wa; int x1, y1, nx, ny; int i; /* Check that the pixmap exists. */ if (!XGetWindowAttributes (w->gterm.display, pixmap, &wa)) return (ERR); /* Default to full dimensions of pixmap if no dimensions given. */ if (width <= 0) width = wa.width; if (height <= 0) height = wa.height; /* Get pixel coordinates. */ if (ctype != GtPixel) { struct mapping sv_mp, p_mp; initialize_mapping (&sv_mp); /* MF035 */ save_mapping (&sv_mp, 0, 0, 0, GtPixel, 0, 0, 0, 0, dst, ctype, x, y, width, height); get_pixel_mapping (w, &sv_mp, &p_mp, 0); x1 = p_mp.dx; y1 = p_mp.dy; nx = p_mp.dnx; ny = p_mp.dny; } else { x1 = x; y1 = y; nx = width; ny = height; } /* Create the destination raster if none exists. */ if (!w->gterm.rasters[dst].type) if (GtCreateRaster (w, dst, GtDefault, nx, ny, /* MF006 */ RasterDepth) == ERR) return (ERR); /* Find an empty raster slot and use it to build a fake source raster * using the supplied pixmap. */ for (i=0; i < w->gterm.maxRasters; i++) { rp = &w->gterm.rasters[i]; if (!rp->type) { rp->type = PixmapRaster; rp->width = nx; rp->height = ny; rp->r.pixmap = pixmap; if (GtCopyRaster (w, 0, i,0, 0,0,nx,ny, dst,0, x1,y1,nx,ny) < 0) return (ERR); rp->type = 0; return (OK); } } return (ERR); } /* GtWriteColormap -- Allocate or modify colormap cells. The Gterm widget * colormap consists of a fixed number of preassigned, read-only color cells, * plus a variable number of dynamically allocated application defined color * cells. The application sees only the preassigned pixel space 0-N where * N is the maximum pixel value. These values are mapped to Xlib pixel values * by the CMAP vector in the main Gterm widget descriptor. The server * colormap does the final mapping to RGB triplets. * * Our strategy here is as follows. If we have a monochrome screen set up a * one-to-one fake colormap so that we preserve the input pixel values and * render a one-bit image later. If we have a StaticGray or StaticColor * visual allocate read-only color cells to allow X to choose the closest * colors to what we request. If we have a GrayScale or PseudoColor visual * allocate private read-write colors. The TrueColor and DirectColor * visuals should not be seen here as we should have been able to set up the * PseudoColor visual on screens that support these visuals, but if they are * seen use a one-to-one mapping to preserve the 8 bit pixel values. */ GtWriteColormap (w, map, first, nelem, r, g, b) GtermWidget w; int map; int first; int nelem; ushort *r, *g, *b; { XWindowAttributes wa; register XColor *cp; register int i, j, v, n, use_wa = 1; unsigned long plane_masks[1]; int req, need, ncolors; Colormap colormap; if (!w || !XtIsRealized ((Widget)w)) return (ERR); if (DBG_TRACE) { fprintf (stderr, "GtWriteColormap: ENTER.....\n"); fprintf (stderr, "GtWriteColormap: map=%d first=%d nelem=%d gt.ncols=%d\n", map, first, nelem, w->gterm.ncolors); } if (map > 0) { /* Create or modify a colormap descriptor. The display colormap * is not affected. */ register struct colormap *cm; struct colormap *last_cm; register XColor *cp; register int i; if (DBG_TRACE) fprintf (stderr, "GtWriteColormap: create/modify map = %d\n", map); /* See if the colormap already exists. */ for (cm = w->gterm.colormaps, last_cm = NULL; cm; cm = cm->next) { last_cm = cm; if (cm->map == map) break; } /* If not, create it. */ if (!cm) { if (!(cm = (struct colormap *)XtCalloc(1,sizeof(struct colormap)))) return (ERR); if (last_cm) last_cm->next = cm; else w->gterm.colormaps = cm; /* Initialize static part of colormap. */ for (i=0; i < SZ_STATIC_CMAP; i++) { cp = &w->gterm.color[i]; cm->r[i] = cp->red; cm->g[i] = cp->green; cm->b[i] = cp->blue; } } cm->map = map; cm->ncells = max (cm->ncells, first + nelem); /* Ignore attempts to overwrite static part of colormap. */ for ( ; first < SZ_STATIC_CMAP; first++, nelem--) { r++; g++; b++; } if (nelem >= 0) { memmove (&cm->r[first], r, nelem * sizeof (ushort)); memmove (&cm->g[first], g, nelem * sizeof (ushort)); memmove (&cm->b[first], b, nelem * sizeof (ushort)); } if (DBG_TRACE) fprintf (stderr, "GtWriteColormap: map=%d -- RETURNING\n",map); return (OK); } /* Write to the display colormap. */ if (first < SZ_STATIC_CMAP || first + nelem > MAX_SZCMAP) return (ERR); /* Invalidate the cmap cache. */ invalidate_cmap (w); /* Get the window attributes. We need to do this to determine the type * of visual used for the window, which determines our color allocation * strategy. This is only done once since presumably the visual and * window depth will not change after the widget has been around long * enough to receive a GtWriteColormap call. */ if (w->gterm.w_depth == 0 && w->gterm.w_visual_class == 0) { if (!XGetWindowAttributes (w->gterm.display, w->gterm.window, &wa)) return (ERR); w->gterm.wa = wa; w->gterm.wa_defined++; if (wa.depth == 1) goto unitary; } else use_wa = 0; switch ((use_wa ? wa.visual->class : w->gterm.w_visual_class)) { case StaticGray: case StaticColor: /* Allocate "best-match" colors. */ for (i=first; i < first+nelem; i++) { cp = &w->gterm.color[i]; cp->red = r[i-first]; cp->green = g[i-first]; cp->blue = b[i-first]; cp->flags = (DoRed | DoGreen | DoBlue); if (XAllocColor (w->gterm.display, wa.colormap, cp)) { w->gterm.cmap[i] = cp->pixel; } else { w->gterm.cmap[i] = cp->pixel = BlackPixelOfScreen (w->gterm.screen); } } break; case GrayScale: case PseudoColor: if (DBG_TRACE) fprintf (stderr, "GtWriteColormap: PSEUDOCOLOR vis, defCM=%d, gt.ncols=%d\n", w->gterm.useDefaultCM, w->gterm.ncolors); if (w->gterm.useDefaultCM) { usedef: /* Allocate private r/w colors from default colormap. */ need = first + nelem - w->gterm.ncolors; /* Allocate new color cells if needed. If we can't allocate all * the requested cells the unallocated pixel values are set to * black. */ if ((n = need) > 0) { /* Get the colormap cells. */ req = min(w->gterm.maxColors, first + nelem - SZ_STATIC_CMAP) - (w->gterm.ncolors - SZ_STATIC_CMAP); for (n=0; req > 0 && n < need; ) if (XAllocColorCells (w->gterm.display, wa.colormap, False, plane_masks, 0, &w->gterm.cmap[w->gterm.ncolors+n], req)) { n += req; } else req /= 2; /* Initialize the color descriptors. */ for (i = w->gterm.ncolors; i < first+nelem; i++) { cp = &w->gterm.color[i]; if (i < w->gterm.ncolors + n) { cp->pixel = w->gterm.cmap[i]; cp->flags = (DoRed | DoGreen | DoBlue); } else { w->gterm.cmap[i] = cp->pixel = BlackPixelOfScreen (w->gterm.screen); cp->flags = 0; } } w->gterm.ncolors += n; } if (DBG_TRACE) fprintf (stderr, "GtWriteColormap: PSEUDOCOLOR defCM, need=%d gt.ncols=%d\n", need, w->gterm.ncolors); /* Assign RGB colors to the referenced cells. */ for (i=0; i < nelem; i++) { cp = &w->gterm.color[first+i]; cp->red = r[i]; cp->green = g[i]; cp->blue = b[i]; } n = w->gterm.ncolors - first; if (n > 0) XStoreColors (w->gterm.display, wa.colormap, &w->gterm.color[first], n); } else { /* Allocate colors in a custom colormap. If the named colormap * does not yet exist we create one. Multiple gterm widget * instances may share the same colormap. */ long timeval, time(); int shadow; /* get_colormap will direct us to the default colormap if the * custom colormap cannot be accessed or created. */ colormap = get_colormap (w); if (w->gterm.useDefaultCM) goto usedef; /* Assign RGB colors to the referenced cells. */ ncolors = min (w->gterm.maxColors, nelem); cp = &w->gterm.color[first]; if (DBG_TRACE) { fprintf (stderr, "GtWriteColormap: PSEUDOCOLOR custom cmap\n"); fprintf (stderr, "GtWriteColormap: Pseudo: first=%d nelem=%d -> ncols=%d/%d\n", first, nelem, ncolors, w->gterm.ncolors); } for (i=0; i < ncolors; i++, cp++) { cp->red = r[i]; cp->green = g[i]; cp->blue = b[i]; cp->flags = (DoRed | DoGreen | DoBlue); if (DBG_TRACE && DBG_CM_VERB) fprintf (stderr, "GtWriteColormap: Pseudo: %3d (%3d %3d %3d) %d / %d\n", i, r[i]>>8,g[i]>>8,b[i]>>8, w->gterm.ncolors, ncolors); } if (DBG_TRACE) fprintf (stderr, "GtWriteColormap: first=%d nelem=%d ncol=%d %d\n", first, nelem, ncolors, w->gterm.ncolors); /* Store the new colors. */ if (ncolors > 0) XStoreColors (w->gterm.display, colormap, &w->gterm.color[first], ncolors); /* Also attempt to store the colors in the default colortable, * by allocating, writing, and then deallocating cells. This * helps keeps the gterm window visible when the default * colormap is loaded. To avoid excessive server queries the * default colormap is only updated every so often. Updating is * disabled if cmapShadow is set to zero. If shadowing is * enabled the update is always performed if the WriteColormap * occurs when the pointer is not in the window (e.g., when a * button elsewhere in the GUI is pressed) as otherwise the * change will not be visible as the private colormap will not * be loaded by the window manager. */ shadow = w->gterm.cmapShadow; timeval = time((long *)NULL); if (shadow && (!w->gterm.in_window || (timeval - w->gterm.cmapLastShadow > shadow * 1000))) { update_default_colormap (w); w->gterm.cmapLastShadow = timeval; } } if (DBG_TRACE) fprintf (stderr, "GtWriteColormap: PSEUDOCOLOR DONE\n"); break; default: /* Set up a unitary, or one-to-one mapping, to preserve the input * pixel values so that we can render them later. */ if (DBG_TRACE) fprintf (stderr, "GtWriteColormap: visual default case\n"); unitary: colormap = get_colormap (w); if ((first+nelem) > MAX_SZCMAP) break; if (w->gterm.useGlobalCmap) break; first = SZ_STATIC_CMAP; ncolors = min (w->gterm.maxColors, nelem); cp = &w->gterm.color[first]; if (DBG_TRACE) fprintf (stderr, "GtWriteColormap: unitary: first=%d nelem=%d -> ncolors=%d/%d\n", first, nelem, ncolors, w->gterm.ncolors); for (i = 0; i < ncolors; i++, cp++) { w->gterm.cmap[i] = i; cp->pixel = i; cp->red = r[i]; cp->green = g[i]; cp->blue = b[i]; cp->flags = (DoRed | DoGreen | DoBlue); if (DBG_TRACE && DBG_CM_VERB) fprintf (stderr, "GtWriteColormap: True: %3d: (%3d, %3d, %3d) %d / %d\n", i, r[i]>>8, g[i]>>8, b[i]>>8, w->gterm.ncolors, ncolors); if (i+1 > w->gterm.ncolors) w->gterm.ncolors = i + 1; } if (DBG_TRACE) { fprintf (stderr, "GtWriteColormap: map=%d first=%d nelem=%d ncol=%d %d\n", map, first, nelem, ncolors, w->gterm.ncolors); fprintf (stderr, "GtWriteColormap: TRUECOLOR DONE\n"); } break; } if (DBG_TRACE) { dbg_printCmaps (w); fprintf (stderr, "GtWriteColormap: LEAVING\n"); } return (OK); } /* GtReadColormap -- Return the color assignments for a region of the named * colormap. */ GtReadColormap (w, map, first, nelem, r, g, b) GtermWidget w; int map; int first; int nelem; ushort *r, *g, *b; { register int i; if (DBG_TRACE) fprintf (stderr, "GtReadColormap: ENTER map=%d first=%d nelem=%d\n", map, first, nelem); if (w->gterm.useGlobalCmap) { for (i=0; i < MAX_SZCMAP; i++) { r[i] = global_color[i].red; g[i] = global_color[i].green; b[i] = global_color[i].blue; } return (SZ_STATIC_CMAP + SZ_DYNAMIC_CMAP + SZ_OVERLAY_CMAP); } /* Clear the output colormap. */ for (i=0; i < MAX_SZCMAP; i++) r[i] = g[i] = b[i] = 0; if (map > 0) { /* Read from a colormap descriptor. */ register struct colormap *cm; register int i, j; /* Locate colormap. */ for (cm = w->gterm.colormaps; cm; cm = cm->next) if (cm->map == map) break; if (!cm) return (0); /* Return RGB values. */ for (i=0; i < nelem; i++) { j = first + i; if (j < cm->ncells) { r[i] = cm->r[j]; g[i] = cm->g[j]; b[i] = cm->b[j]; } else break; } return (i); } else { /* Read the display colormap. */ register XColor *cp; /* Return RGB values. */ for (i=0; i < nelem; i++) { if (first+i < w->gterm.ncolors) { if (DBG_TRACE && DBG_CM_VERB) fprintf (stderr, "GtReadColormap: %3d %3d %3d %3d\t", first+i, r[i]>>8, g[i]>>8, b[i]>>8); cp = &w->gterm.color[first+i]; r[i] = (ushort) cp->red; g[i] = (ushort) cp->green; b[i] = (ushort) cp->blue; } else break; if (DBG_TRACE && DBG_CM_VERB) fprintf (stderr,"%3d\t%3d %3d %3d\n",i,r[i]>>8,g[i]>>8,b[i]>>8); } if (DBG_TRACE) fprintf (stderr, "GtReadColormap: LEAVING ncolors=%d\n", i); return (i); } } /* GtLoadColormap -- Load a colormap into the display, optionally scaling * the colormap via a linear transformation in the process. The colormap is * unaffected if offset=0.5, scale=1.0. A negative scale inverts the image. * If map=0 the linear transformation is applied directly to the display * colormap. * * The offset refers to the center of the mapped region of the transfer * function, which is why the center value is at 0.5. For example, if the * range of raster pixel intensities is normalized to the range 0.0 to 1.0, * then a transfer function of [offset=0.3,slope=3.0] will display the region * of intenstities centered around the normalized intenstity of 0.3, with a * contrast of 3.0 (the screen intensity changes 3 units for a unit change in * raster pixel intensity). The transfer function [offset=0.3,slope=-3.0] * will display the same range of pixel intensitites, but with a negative * contrast. The transfer function [offset=0.5,slope=1.0] has intercepts * of [0,0] and [1,1] hence it displays the full range of raster pixel * intensities - the input colormap is used as is, without resampling. */ GtLoadColormap (w, map, offset, slope) GtermWidget w; int map; float offset, slope; { register int i; register XColor *cp; register struct colormap *cm; struct colormap d_cmap, o_cmap; int noscale, nelem, c1, c2; float x, y, z, frac; ushort r, g, b; if (DBG_TRACE) fprintf (stderr, "GtLoadColormap: map=%d offset=%f slope=%f\n", map, offset, slope); /* Get the colormap to be loaded. */ if (map == 0) { /* Create a dummy colormap struct from the screen colormap. */ dummy: cm = &d_cmap; cm->map = 0; cm->next = NULL; cm->ncells = w->gterm.ncolors; for (i=0; i < cm->ncells; i++) { cp = &w->gterm.color[i]; cm->r[i] = cp->red; cm->g[i] = cp->green; cm->b[i] = cp->blue; } } else { /* Locate colormap. */ for (cm = w->gterm.colormaps; cm; cm = cm->next) if (cm->map == map) break; if (DBG_TRACE) fprintf (stderr, "GtLoadColormap: map=%d/%d cm=0x%x ncells=%d\n", map, cm->map, cm, cm->ncells); if (!cm) return (ERR); } /* Compute the scaled colormap. Only the dynamic part of the colormap ** is scaled, the static cells are not affected. */ o_cmap.map = 0; o_cmap.ncells = cm->ncells; if (w->gterm.useGlobalCmap) nelem = SZ_DYNAMIC_CMAP; else nelem = cm->ncells - SZ_STATIC_CMAP; noscale = (abs(offset - 0.5) < 0.0001 && abs(slope - 1.0) < 0.0001); if (noscale) { for (i=0; i < nelem; i++) { o_cmap.r[i] = cm->r[SZ_STATIC_CMAP+i]; o_cmap.g[i] = cm->g[SZ_STATIC_CMAP+i]; o_cmap.b[i] = cm->b[SZ_STATIC_CMAP+i]; } } else { if (DBG_TRACE) fprintf (stderr,"GtLoadColormap: scaling cmap; nelem=%d\n", nelem); for (i=0; i < nelem; i++) { x = (float)i / (float)(nelem - 1); y = (x - offset) * slope + 0.5; if (y <= 0.0) { r = cm->r[SZ_STATIC_CMAP]; g = cm->g[SZ_STATIC_CMAP]; b = cm->b[SZ_STATIC_CMAP]; } else if (y >= 1.0) { r = cm->r[cm->ncells-1]; g = cm->g[cm->ncells-1]; b = cm->b[cm->ncells-1]; } else { z = y * (nelem - 1) + SZ_STATIC_CMAP; if (w->gterm.cmapInterpolate) { c1 = (int)z; c2 = min (cm->ncells-1, c1 + 1); frac = z - c1; r = cm->r[c1] * (1.0 - frac) + cm->r[c2] * frac; g = cm->g[c1] * (1.0 - frac) + cm->g[c2] * frac; b = cm->b[c1] * (1.0 - frac) + cm->b[c2] * frac; } else { c1 = (int)z; r = cm->r[c1]; g = cm->g[c1]; b = cm->b[c1]; } } o_cmap.r[i] = r; o_cmap.g[i] = g; o_cmap.b[i] = b; } } if (w->gterm.useGlobalCmap) { for (i=0; i < nelem; i++) { global_color[i+SZ_STATIC_CMAP].red = o_cmap.r[i]; global_color[i+SZ_STATIC_CMAP].green = o_cmap.g[i]; global_color[i+SZ_STATIC_CMAP].blue = o_cmap.b[i]; } global_color[i+SZ_STATIC_CMAP-1].red = o_cmap.r[i] = o_cmap.r[i-2]; global_color[i+SZ_STATIC_CMAP-1].green = o_cmap.g[i] = o_cmap.g[i-2]; global_color[i+SZ_STATIC_CMAP-1].blue = o_cmap.b[i] = o_cmap.b[i-2]; nelem = SZ_DYNAMIC_CMAP; valid_lut = 0; } /* Load the colormap into the display. */ if (DBG_TRACE) fprintf (stderr, "GtLoadColormap: loading colormap into display\n"); GtWriteColormap (w, 0, SZ_STATIC_CMAP, nelem, o_cmap.r, o_cmap.g, o_cmap.b); /* If the colormap we loaded to the display was the display colormap, ** restore the original unscaled colors in the gterm descriptor so that ** we won't be scaling a scaled colormap in the next operation. */ if (map == 0) { for (i=SZ_STATIC_CMAP; i < cm->ncells; i++) { cp = &w->gterm.color[i]; cp->red = cm->r[i]; cp->green = cm->g[i]; cp->blue = cm->b[i]; } } if (w->gterm.useGlobalCmap) { Mapping mp; /* Execute any mappings that reference this raster. */ for (mp = w->gterm.mp_head; mp; mp = mp->next) { if (mp->enabled) { struct mapping *map=mp, p_mp; if (map->st != GtPixel || map->dt != GtPixel) { initialize_mapping (&p_mp); get_pixel_mapping (w, map, &p_mp, 1); update_mapping (w, map = &p_mp); } else update_mapping (w, map); if (colormap_focus > 0 && colormap_focus < mp->snx) { /* If we're doing a focused update, refresh only the ** central part of the main display. */ int half = colormap_focus; int full = ((half * 2 > 512) ? 512 : half * 2); /* refresh_source (w, map, mp->sx+(mp->snx/2 - 64), mp->sy+(mp->sny/2 - 64), 128, 128); */ refresh_source (w, map, mp->sx+(mp->snx/2 - half), mp->sy+(mp->sny/2 - half), full, full); } else { refresh_source (w, map, mp->sx, mp->sy, mp->snx, mp->sny); } if (map == &p_mp) free_mapping (w, map); /* if (colormap_focus) break; */ } } } return (OK); } GtSetColormapFocus (int box_size) { if (box_size != 0) colormap_focus = ((box_size > 0 && box_size < 64) ? 64 : box_size); } /* GtQueryColormap -- Return information on the size and state of a colormap. */ GtQueryColormap (w, map, first, nelem, maxelem) register GtermWidget w; int map; int *first, *nelem, *maxelem; { register struct colormap *cm; int nitems; if (first) *first = SZ_STATIC_CMAP; if (nelem) *nelem = 0; if (maxelem) *maxelem = min (w->gterm.maxColors, MAX_SZCMAP) - SZ_STATIC_CMAP; if (w->gterm.useGlobalCmap) { *first = SZ_STATIC_CMAP; *nelem = SZ_STATIC_CMAP + SZ_DYNAMIC_CMAP + SZ_OVERLAY_CMAP; *maxelem = SZ_STATIC_CMAP + SZ_DYNAMIC_CMAP + SZ_OVERLAY_CMAP; } else if (map > 0) { for (cm = w->gterm.colormaps; cm; cm = cm->next) if (cm->map == map) break; if (!cm) return (0); if (nelem) *nelem = cm->ncells - SZ_STATIC_CMAP; } else { if (nelem) *nelem = w->gterm.ncolors - SZ_STATIC_CMAP; if (maxelem) { nitems = min (MAX_SZCMAP, CellsOfScreen(w->gterm.screen)); *maxelem = min (nitems, min (w->gterm.maxColors, MAX_SZCMAP - w->gterm.base_pixel)); } } if (DBG_TRACE) fprintf (stderr, "GtQueryColormap: map=%d -> first=%d nelem=%d max=%d\n", map, *first, *nelem, *maxelem); return (1); } /* GtNextColormap -- Return a unique colormap number. */ GtNextColormap (w) register GtermWidget w; { register struct colormap *cm; register int mapno = 0; /* Get the next map number. */ for (cm = w->gterm.colormaps; cm; cm = cm->next) if (cm->map > mapno) mapno = cm->map; return (mapno + 1); } /* GtFreeColormap -- Free a colormap descriptor. */ GtFreeColormap (w, colormap) register GtermWidget w; int colormap; { register struct colormap *p_cm, *cm; /* Find the colormap and free it. */ for (p_cm = NULL, cm = w->gterm.colormaps; cm; p_cm = cm, cm = cm->next) if (cm->map == colormap) { if (p_cm) p_cm->next = cm->next; else w->gterm.colormaps = cm->next; XtFree ((char *)cm); return; } } /* GtWriteIomap -- An iomap is an optional lookup table used to isolate the * client application from the color model used within the Gterm widget. * To simplify color allocation the Gterm widget defines a logical color * space where color 0 is the background, 1 the foreground, 2-N are statically * allocated standard colors, and colors N+1 and above are dynamically * allocated by the graphics application. Less-demanding applications use * only the statically allocated, shared colors. The widget internally maps * these logical colors to whatever the window system requires, but providing * a well-defined logical color space isolates the client from the details of * color allocation in the underlying window system. * * An iomap can be used to define a mapping between the color model of the * client application and the Gterm color model (when we say color model here * we mean color allocation schemes for 8 bit pseudocolor). By default the * iomap is one-to-one. The use of an iomap frees the client from having to * worry about color index translations, and allows color tables to be * combined in the widget for greater efficiency when color tables are serially * applied. The iomap applies to all color indices or pixel values passed * in i/o operations between the client and the Gterm widget. */ GtWriteIomap (w, iomap, first, nelem) register GtermWidget w; ushort *iomap; int first, nelem; { register int c1, c2; if (w->gterm.useGlobalCmap) return; c1 = max(0, min(MAX_SZCMAP-1, first)); c2 = max(0, min(MAX_SZCMAP-1, first + nelem - 1)); if (DBG_TRACE) fprintf (stderr,"GtWriteIomap: first=%d nelem=%d -> c1=%d c2=%d (%d)\n", first, nelem, c1, c2, (c2-c1+1)); nelem = c2 - c1 + 1; memmove (&w->gterm.iomap[c1], iomap, nelem * sizeof(ushort)); invalidate_cmap (w); if (DBG_IOMAP) { int i; for (i=0; i < MAX_SZCMAP; i++) fprintf (stderr, "iomap[%3d] = %d\n", i, w->gterm.iomap[i]); } } /* GtReadIomap -- Read back the contents of the iomap. */ GtReadIomap (w, iomap, first, nelem) register GtermWidget w; uchar *iomap; int first, nelem; { register int c1, c2; c1 = max(0, min(MAX_SZCMAP-1, first)); c2 = max(0, min(MAX_SZCMAP-1, first + nelem - 1)); nelem = c2 - c1 + 1; memmove (iomap, &w->gterm.iomap[c1], nelem * sizeof(ushort)); } /* GtReadLUT -- Read back the contents of the global LUT. */ GtReadLUT (w, lut, first, nelem) register GtermWidget w; unsigned long *lut; int first, nelem; { register int c1, c2; c1 = max(0, min(MAX_SZCMAP-1, first)); c2 = max(0, min(MAX_SZCMAP-1, first + nelem - 1)); nelem = c2 - c1 + 1; memmove (lut, &global_lut[c1], nelem * sizeof(unsigned long)); } /* init_iomap -- Initialize the iomap and the cmap cache. */ static void init_iomap (w) GtermWidget w; { register ushort *iomap = w->gterm.iomap; register int i; for (i=0; i < MAX_SZCMAP; i++) iomap[i] = i; invalidate_cmap (w); } /* init_global_map -- Initialize the global cmap; */ static void init_global_cmap () { register int i; for (i=0; i < MAX_SZCMAP; i++) global_cmap[i] = (Pixel) i; } /* invalidate_cmap -- Invalidate the cmap cache. */ static void invalidate_cmap (w) register GtermWidget w; { w->gterm.cmap_in_valid = w->gterm.cmap_out_valid = 0; } /* get_cmap_in -- Get the combined input colormap, used to transform color * values received from the client to window system color indices. */ static Pixel * get_cmap_in (w) register GtermWidget w; { register Pixel *cmap, *cmap_in = w->gterm.cmap_in; register ushort *iomap; register int i, j; int ncolors; if (w->gterm.useGlobalCmap) return (global_cmap); if (DBG_TRACE && DBG_CM_VERB) fprintf (stderr, "get_cmap_in: valid=%d ncolors=%d\n", w->gterm.cmap_in_valid, (w->gterm.ncolors - SZ_STATIC_CMAP)); if (w->gterm.cmap_in_valid) return (cmap_in); cmap = w->gterm.cmap; iomap = w->gterm.iomap; ncolors = w->gterm.ncolors - SZ_STATIC_CMAP; /* If ncolors is small wrap around so that pixel values stay within * the mapped range of output pixels. */ for (i=0; i < MAX_SZCMAP; i++) { j = iomap[i]; if (j > SZ_STATIC_CMAP && ncolors) j = ((j - SZ_STATIC_CMAP) % ncolors) + SZ_STATIC_CMAP; cmap_in[i] = cmap[j]; } w->gterm.cmap_in_valid++; return (cmap_in); } /* get_cmap_out -- Get the combined output colormap, used to transform window * system color indices to the color system of the client. Note that this is * not necessarily a uniquely defined invertible transformation. */ static Pixel * get_cmap_out (w) GtermWidget w; { register Pixel *cmap; register ushort *iomap; Pixel *cmap_out = w->gterm.cmap_out; register int pixel, i; int j; if (w->gterm.useGlobalCmap) return (global_cmap); if (DBG_TRACE && DBG_CM_VERB) fprintf (stderr, "get_cmap_out: valid=%d ncolors=%d\n", w->gterm.cmap_out_valid, (w->gterm.ncolors - SZ_STATIC_CMAP)); if (w->gterm.cmap_out_valid) return (cmap_out); cmap = w->gterm.cmap; iomap = w->gterm.iomap; /* Invert the two colormaps. This is not very efficient, but we don't * have to do this very often (a GtReadPixels call is about the only * case, and even then the map is cached). */ for (j=0; j < MAX_SZCMAP; j++) { pixel = j; /* Lookup display pixel in cmap. */ for (i=0; i < MAX_SZCMAP; i++) if (cmap[i] == pixel) { pixel = i; break; } if (i >= MAX_SZCMAP) { cmap_out[j] = 0; continue; } /* Lookup cmap pixel in iomap. */ if (iomap[pixel] != pixel) { for (i=0; i < MAX_SZCMAP; i++) if (iomap[i] == pixel) { pixel = i; break; } if (i >= MAX_SZCMAP) { cmap_out[j] = 0; continue; } } cmap_out[j] = pixel; } w->gterm.cmap_out_valid++; return (cmap_out); } /* get_pixel -- Convert a client color index into a display pixel. */ static Pixel get_pixel (w, client_pixel) register GtermWidget w; register int client_pixel; { register Pixel *cmap = get_cmap_in (w); if (client_pixel < 0 || client_pixel >= MAX_SZCMAP) return (w->gterm.cmap[1]); else return (cmap[client_pixel]); } /* GtGetClientPixel -- Convert a gterm pixel into a client pixel. */ GtGetClientPixel (w, pixel) GtermWidget w; register int pixel; { register int i; register ushort *iomap; int client_pixel = 0; get_cmap_in (w); iomap = w->gterm.iomap; for (i=0; i < MAX_SZCMAP; i++) if (iomap[i] == pixel) { client_pixel = i; break; } return (client_pixel); } /* GtInitMappings -- Delete all mappings and initialize the mapping subsystem. */ GtInitMappings (w) register GtermWidget w; { register Mapping mp; register int i; invalidate_draw_context (w); /* Free any mapping storage. */ if (w->gterm.mappings) { for (i=0; i < w->gterm.maxMappings; i++) { mp = &w->gterm.mappings[i]; if (mp->defined) free_mapping (w, mp); } XtFree ((char *)w->gterm.mappings); w->gterm.mp_head = NULL; w->gterm.mp_tail = NULL; } /* Allocate the initially empty mapping descriptors. */ w->gterm.mappings = (Mapping) XtCalloc (w->gterm.maxMappings, sizeof (struct mapping)); for (i=0; i < w->gterm.maxMappings; i++) { mp = &w->gterm.mappings[i]; mp->mapping = i; } w->gterm.nmappings = 0; } /* GtNextMapping -- Return the index of the next available mapping descriptor. * This routine always returns a mapping index of 1 or higher. */ GtNextMapping (w) register GtermWidget w; { register Mapping mp; register int i; for (i=1; i < w->gterm.maxMappings; i++) { mp = &w->gterm.mappings[i]; if (!mp->defined) return (i); } return (-1); } /* GtFreeMapping -- Free a mapping descriptor. */ GtFreeMapping (w, mapping) register GtermWidget w; int mapping; { free_mapping (w, &w->gterm.mappings[mapping]); } /* GtRaiseMapping -- Set the stacking order of a mapping to one level * higher than the reference mapping. If no reference mapping is given * the mapping is raised to the top of the stacking order. */ GtRaiseMapping (w, mapping, reference) register GtermWidget w; int mapping, reference; { register Mapping mp, ref_mp; mp = &w->gterm.mappings[mapping]; if (!mp->defined) return; if (reference <= 0 || reference >= w->gterm.maxMappings) ref_mp = w->gterm.mp_tail; else ref_mp = &w->gterm.mappings[reference]; /* Already on top? */ if (mp == w->gterm.mp_tail) return; mp_unlink (w, mp); mp_linkafter (w, mp, ref_mp); } /* GtLowerMapping -- Change the stacking order of a mapping relative to another * mapping, causing the first mapping to be drawn below the second. */ GtLowerMapping (w, mapping, reference) register GtermWidget w; int mapping, reference; { register Mapping mp, ref_mp; mp = &w->gterm.mappings[mapping]; if (!mp->defined) return; if (reference <= 0 || reference >= w->gterm.maxMappings) ref_mp = NULL; else ref_mp = &w->gterm.mappings[reference]; /* Already lowered? */ if (mp == w->gterm.mp_head) return; /* Lower it. */ mp_unlink (w, mp); if (ref_mp && ref_mp->prev) mp_linkafter (w, mp, ref_mp->prev); else { mp->next = w->gterm.mp_head; w->gterm.mp_head = mp; if (mp->next) mp->next->prev = mp; if (!w->gterm.mp_tail) w->gterm.mp_tail = mp; } } /* GtCompareMappings -- Compare the stacking order of two mappings. A * negative value is returned if the m1 < m2, zero is returned if the * mappings are the same, and a positive value is returned if m1 > m2. */ GtCompareMappings (w, map1, map2) register GtermWidget w; int map1, map2; { register Mapping mp, mp1, mp2; if (map1 == map2) return (0); mp1 = &w->gterm.mappings[map1]; mp2 = &w->gterm.mappings[map2]; for (mp = w->gterm.mp_head; mp; mp = mp->next) if (mp == mp1) return (-1); else if (mp == mp2) return (1); return (0); } /* GtSelectRaster -- Select the raster which maps to the given raster pixel, * and transform the coordinates back to the source raster. The raster number * and the raster coordinates of the source raster are returned. If no raster * maps to the given pixel, raster=src and source raster coordinates are * returned. * * The raster pixel coordinate system is best explained by an example. * Suppose we have a 10x10 raster mapped into a 500x500 window. The * window pixel 0 on an axis has raster pixel coordinate 0.0; pixel 500 * (which is outside the window) has raster pixel coordinate 10.0. The * coordinates correspond to the edge of the pixel as displayed in the * window, i.e., the left edge of the (nonflipped) window is at x=0.0, and * the right edge at x=10.0. Due to the pixelization of the screen, the * maximum value is a limit which is only approached as the magnification * increases. * * The client application may have a different coordinate system than the * above. For example, if the client wants an integer pixel value to be * at the center of a pixel, the first pixel has the coordinate [1,1], and * the raster is 10 pixels wide, the client coordinate system would range * from 0.5 to 10.5 at the edges of the NDC space. */ GtSelectRaster (w, dras, dt, dx, dy, rt, rx, ry, rmap) GtermWidget w; int dras; /* display raster */ int dt; /* coordinate type of input coords */ int dx, dy; /* display raster coordinates */ int rt; /* coordinate type for output */ int *rx, *ry; /* raster coordinates (output) */ int *rmap; /* mapping selected */ { register Mapping mp; float x, y, x2, y2; int raster, mapping; /* Get display raster pixel coordinates. */ if (dt != GtPixel) { struct mapping sv_mp, p_mp; initialize_mapping (&sv_mp); /* MF035 */ save_mapping (&sv_mp, 0, 0, 0, 0, 0,0,0,0, 0, dt, dx,dy,0,0); get_pixel_mapping (w, &sv_mp, &p_mp, 0); dx = p_mp.dx; dy = p_mp.dy; } /* Search for a mapping which maps to this pixel. The mapping we are * looking for is the mapping closest to the tail of the mapping list * (highest stacking order) which is defined and enabled and which * includes the given display raster pixel in its destination rect. */ for (mp = w->gterm.mp_tail, mapping = -1; mp; mp = mp->prev) { if (mp->defined && mp->enabled && mp->dst == dras) { struct mapping *map, p_mp; int dnx, dny; get_pixel_mapping (w, mp, &p_mp, 0); map = &p_mp; if ((dnx = map->dnx) < 0) dnx = -dnx; if ((dny = map->dny) < 0) dny = -dny; /* Is display raster pixel in destination rect for this mapping? */ if (dnx > 0 && dx >= map->dx && dx < map->dx + dnx && dny > 0 && dy >= map->dy && dy < map->dy + dny) { /* Compute offset into destination rect and apply axis flip * if any from mapping. */ x = dx - map->dx + 0.5; if (map->dnx < 0) x = dnx - x; y = dy - map->dy + 0.5; if (map->dny < 0) y = dny - y; /* Compute the source raster coordinates corresponding to * the given display pixel. This is done in floating point * to permit fractional pixel resolution if the mapping * zooms the raster. */ x = x * (float)map->snx / (float)dnx + map->sx; y = y * (float)map->sny / (float)dny + map->sy; mapping = map->mapping; raster = map->src; x2 = w->gterm.rasters[raster].width; y2 = w->gterm.rasters[raster].height; break; } } } /* Return display raster coordinates if no mapped raster was found. */ if (mapping < 0) { x = dx; y = dy; x2 = (int)w->core.width - 1; y2 = (int)w->core.height - 1; raster = dras; } /* Output coordinates of the requested type. The increased resolution * of NDC coordinates allows fractional pixel coordinates to be returned * (e.g. 1/32 of a pixel for a 1K raster). */ if (rt == GtPixel) { *rx = x; *ry = y; } else { *rx = ( x) / x2 * MAXNDC; *ry = (y2 - y) / y2 * MAXNDC; /* NDC is flipped in Y */ } *rmap = mapping; return (raster); } /* GtCopyRaster -- Copy a region of the source raster to a region of the * destination raster. If the input and output regions are not the same * size the subimage is automatically scaled to fit the destination region. * If the destination extent DNX or DNY is negative, the image is flipped in * that axis. The type of spatial scaling performed is determined by the * scale factors (zoom, dezoom, or no scaling). The rasterop argument is * used to exercise fine control over how the mapping is performed, e.g., to * force a refresh, implement a transient mapping, or in the case of a dezoom * (many-to-one) mapping, select the antialiasing technique to be used. */ GtCopyRaster (w, rop, src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) GtermWidget w; int rop; /* rasterop */ int src; /* 0=window, >0 = raster number */ int st; /* coordinate type for source raster */ int sx,sy,snx,sny; /* source raster */ int dst; /* 0=window, >0 = raster number */ int dt; /* coordinate type for destination raster */ int dx,dy,dnx,dny; /* destination raster */ { struct mapping sv_mp, p_mp; /* MF007 */ int status; if (DBG_TRACE) fprintf(stderr, "GtCopyRaster() -- ENTER\n"); if (!w || !XtIsRealized ((Widget)w)) return (OK); /* Construct a temporary mapping describing the desired raster copy. */ initialize_mapping (&sv_mp); /* MF035 */ save_mapping (&sv_mp, w->gterm.maxMappings, 0, src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny); initialize_mapping (&p_mp); get_pixel_mapping (w, &sv_mp, &p_mp, 1); update_mapping (w, &p_mp); /* Refresh the destination pixels. */ status = refresh_destination (w, &p_mp, dx, dy, abs(dnx), abs(dny)); /* Discard the temporary mapping. */ free_mapping (w, &p_mp); if (DBG_TRACE) fprintf(stderr, "GtCopyRaster() -- RETURNING\n"); return (status); } /* GtSetMapping -- Define a new mapping function, or modify an old one. * If a new mapping is defined it is merely enabled, and no refreshing * of the screen takes place until either some mapped data is written * to or the mapping is explicitly refreshed. If an existing mapping is * modified the old and new mappings are examined and only those portions * of the destination rect for which the mapping changed are updated. * This permits minor changes to a mapping (e.g. moving an edge) without * having to redraw the entire region. Regions of the destination drawable * which were previously covered by the mapping but which were exposed by * modifying the mapping are redrawn. */ GtSetMapping (w, mapping, rop, src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) GtermWidget w; int mapping; /* mapping number */ int rop; /* rasterop */ int src; /* 0=window, >0 = raster number */ int st; /* coordinate type for source raster */ int sx,sy,snx,sny; /* source raster */ int dst; /* 0=window, >0 = raster number */ int dt; /* coordinate type for source raster */ int dx,dy,dnx,dny; /* destination raster */ { register int i, j; register Mapping mp, o_mp, n_mp; struct mapping pix_mp, new_mp; int defined, scale_changed, offset, current, state, old_i; int nx, xs[MAX_REGIONS], xe[MAX_REGIONS], xv[MAX_REGIONS]; int ny, ys[MAX_REGIONS], ye[MAX_REGIONS], yv[MAX_REGIONS]; int n_dnx, n_dny, n_xflip=0, n_yflip=0, i1, i2; int o_dnx, o_dny, o_xflip=0, o_yflip=0; int *o_xymap, *o_xmap, *o_ymap; int *n_xymap, *n_xmap, *n_ymap; int dummy_rop; /* MF011 */ XRectangle rl[MAX_REGIONS]; int nrect, buflen, refresh; if (DBG_TRACE) fprintf (stderr, "GtSetMapping - ENTER\n"); /* Check mapping number in range. */ if (mapping < 0 || mapping >= w->gterm.maxMappings) return (ERR); else mp = &w->gterm.mappings[mapping]; invalidate_draw_context (w); initialize_mapping (&pix_mp); initialize_mapping (&new_mp); /* Get local pixel space copy of old mapping, store new mapping. */ get_pixel_mapping (w, mp, &pix_mp, 1); mp->src = src; mp->st = st; mp->sx = sx; mp->sy = sy; mp->snx = snx; mp->sny = sny; mp->dst = dst; mp->dt = dt; mp->dx = dx; mp->dy = dy; mp->dnx = dnx; mp->dny = dny; mp->rop = (rop & ~(R_RefreshNone|R_RefreshAll)); mp->updated = 0; /* Newly defined mappings are linked at the tail of the mapping list, * i.e. they stack (display) on top of any other mappings. If the * mapping is already defined the stacking order is not changed. */ if (!(defined = mp->defined)) { mp_linkafter (w, mp, w->gterm.mp_tail); mp->defined = 1; } if (!valid_mapping (w, mp)) { mp_unlink (w, mp); mp->defined = 0; return (ERR); } update_mapping (w, mp); /* If we are defining a new mapping just define it and quit, without * refreshing the window, unless R_RefreshAll is explicitly set in the * mapping. If the mapping is not enabled merely store the new mapping. * If the mapping is a null mapping (no pixels) do nothing. If refresh * is disabled in the rasterop merely store the new mapping. If we are * editing an existing mapping which is enabled with the default rasterop, * we continue on to compare the old and new mappings and refresh any * changed pixels in the destination rect. */ if (!defined || src != mp->src || dst != mp->dst) { mp->enabled = mp->defined = 1; mp->refresh = 0; return (OK); } else if (!mp->enabled) { return (OK); } else if (snx == 0 || sny == 0 || dnx == 0 || dny == 0) return (OK); if (rop & R_RefreshNone) return (OK); /* Convert input mappings to pixel coordinates, we deal with only pixel * coordinates from here on. */ get_pixel_mapping (w, mp, &new_mp, 1); load_mapping (&new_mp, &mapping, &dummy_rop, /* MF011 */ &src,&st,&sx,&sy,&snx,&sny, &dst,&dt,&dx,&dy,&dnx,&dny); update_mapping (w, n_mp = &new_mp); update_mapping (w, o_mp = &pix_mp); /* * We are editing an existing mapping. Determine what has changed in * the mapping and refresh the changed regions. */ /* Get old XY scaling maps. */ o_xmap = o_mp->x_srcpix; o_ymap = o_mp->y_srcpix; if ((o_dnx = o_mp->dnx) < 0) { o_dnx = -o_dnx; o_xflip = 1; } if ((o_dny = o_mp->dny) < 0) { o_dny = -o_dny; o_yflip = 1; } /* Get new XY scaling maps. */ n_xmap = n_mp->x_srcpix; n_ymap = n_mp->y_srcpix; if (dnx < 0) { dnx = -dnx; n_xflip = 1; } if (dny < 0) { dny = -dny; n_yflip = 1; } /* Refresh the entire region if the refresh flag is set, a flip has * occurred, or we are doing a complex dezoom mapping. */ refresh = (o_mp->refresh || (rop & R_RefreshAll)); if (n_xflip != o_xflip || n_yflip != o_yflip) refresh = 1; if (n_mp->scaling == M_DEZOOM) refresh = 1; /* Has the spatial scale changed? */ scale_changed = abs (o_mp->xscale - n_mp->xscale) > 1.0E-4 || abs (o_mp->yscale - n_mp->yscale) > 1.0E-4; /* If any of the above conditions are true refresh the entire mapping, * otherwise compare the old and new mappings and refresh any subregions * which have changed. */ if (refresh || scale_changed || n_mp->scaling == M_DEZOOM) { refresh_destination (w, n_mp, dx, dy, dnx, dny); } else { /* Compare the old and new mappings to see what needs to be * refreshed. For speed reasons we only want to refresh the pixels * which have been remapped. Any destination pixel in the new mapping * which does not map to the same source pixel as in the old mapping * must be refreshed. We examine each X and Y coordinate in the new * destination rect and see if the source coordinate this maps to is * the same as in the old mapping. If a given destination pixel [i,j] * maps to the same pixel [i,j] in the source as it did in the * previous mapping, we do not need to refresh that pixel. We examine * the X and Y axis in turn and build up a list of regions which do or * do not need to be refreshed. */ /* Get a list of ranges {XS,XE,XV} in X. */ nx = get_regions (xs,xe,xv, MAX_REGIONS, dx, dnx, n_xmap, o_mp->dx, o_dnx, o_xmap); /* Get a list of ranges {YS,YE,YV} in Y. */ ny = get_regions (ys,ye,yv, MAX_REGIONS, dy, dny, n_ymap, o_mp->dy, o_dny, o_ymap); /* The list of ranges in X and Y together define a raster of arbitary * sized subrectangles filling the destination rect. If the state * value is nonzero (bit 1 set) in either X or Y, the subrectangle * must be refreshed. The get_rects routine returns a list of the * rectangular subregions matching the given condition (bit 1 set in * either axis). Adjacent rectangles are merged to minimize the * number of calls to refresh_destination. */ nrect = get_rects (rl, MAX_REGIONS, xs,xe,xv,nx, ys,ye,yv,ny, 1,1); for (i=0; i < nrect; i++) refresh_destination (w, n_mp, rl[i].x, rl[i].y, rl[i].width, rl[i].height); } /* Refresh any lower level mappings exposed when the current mapping was * modified. These will be regions of the old rect not present in the * new, modified rect for the current mapping. */ nx = get_regions (xs,xe,xv, MAX_REGIONS, o_mp->dx, o_dnx, o_xmap, dx, dnx, n_xmap); ny = get_regions (ys,ye,yv, MAX_REGIONS, o_mp->dy, o_dny, o_ymap, dy, dny, n_ymap); nrect = get_rects (rl, MAX_REGIONS, xs,xe,xv,nx, ys,ye,yv,ny, 2,2); for (i=0; i < nrect; i++) { XRectangle r, in; Mapping mp; /* Clear the uncovered region. */ GtSetPixels (w, dst, GtPixel, rl[i].x, rl[i].y, rl[i].width, rl[i].height, GtGetClientPixel(w,0), 0); /* Refresh any lower level mappings the destination rects of * which intersect the uncovered region. */ for (mp = w->gterm.mp_head; mp && mp->mapping != mapping; mp = mp->next) { if (mp->enabled && mp->dst == dst) { r.x = mp->dx; r.y = mp->dy; r.width = mp->dnx; r.height = mp->dny; if (rect_intersect (&in, &r, &rl[i])) refresh_destination (w, mp, in.x, in.y, in.width, in.height); } } } free_mapping (w, n_mp); free_mapping (w, o_mp); mp = &w->gterm.mappings[mapping]; mp->refresh = 0; if (DBG_TRACE) fprintf (stderr, "GtSetMapping - LEAVING\n"); return (OK); } /* GtGetMapping -- Return the external parameters of a mapping. If the * numberd mapping is undefined -1 is returned; 0 is returned if the * mapping is defined but not enabled, and 1 is returned if the mapping * is active. */ GtGetMapping (w, mapping, rop, src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) GtermWidget w; int mapping; /* mapping number */ int *rop; /* rasterop */ int *src; /* 0=window, >0 = raster number */ int *st; /* coordinate type for source raster */ int *sx,*sy,*snx,*sny; /* source raster */ int *dst; /* 0=window, >0 = raster number */ int *dt; /* coordinate type for source raster */ int *dx,*dy,*dnx,*dny; /* destination raster */ { register Mapping mp; if (mapping < 0 || mapping >= w->gterm.maxMappings) return (-1); else if (!(mp = &w->gterm.mappings[mapping])->defined) return (-1); *rop = mp->rop; *src = mp->src; *st = mp->st; *sx = mp->sx; *sy = mp->sy; *snx = mp->snx; *sny = mp->sny; *dst = mp->dst; *dt = mp->dt; *dx = mp->dx; *dy = mp->dy; *dnx = mp->dnx; *dny = mp->dny; return (mp->enabled != 0); } /* GtActiveMapping -- Query whether a mapping is active. */ GtActiveMapping (w, mapping) register GtermWidget w; int mapping; /* mapping number */ { register Mapping mp; if (mapping < 0 || mapping >= w->gterm.maxMappings) return (0); else if (!(mp = &w->gterm.mappings[mapping])->defined) return (0); return (mp->enabled != 0); } /* GtEnableMapping -- Enable a mapping. Enabling a mapping does not * update the destination unless the refresh flag is set. Enabling a * mapping activates the mapping so that any changes to the source will * be mapped to the destination. */ GtEnableMapping (w, mapping, refresh) GtermWidget w; int mapping; /* mapping number */ int refresh; /* refresh destination */ { register Mapping mp; if (DBG_TRACE) fprintf (stderr, "GtEnableMapping: mapping=%d refresh=%d\n", mapping, refresh); invalidate_draw_context (w); if (mapping >= 0 && mapping < w->gterm.maxMappings) { mp = &w->gterm.mappings[mapping]; if (mp->defined) { if (!mp->enabled) { mp->enabled = True; if (refresh) GtRefreshMapping (w, mapping); } return (OK); } } return (ERR); } /* GtSetDisplayRaster -- Set the currently displayed raster. On TrueColor ** visuals the XImage for a frame represents the actual pixel values and ** not the colormapped image. We need this for pixel readback where the ** display pixmap (i.e. w->gterm.pixmap) is the 24-bits colormapped image ** and we have no way to get back to the pixel values. ** */ GtSetDisplayRaster (gt, raster) GtermWidget gt; int raster; /* raster number */ { if (DBG_TRACE) fprintf (stderr, "GtSetDisplayRaster: raster=%d\n", raster); gt->gterm.d_raster = (raster * 2); } /* GtDisableMapping -- Disable a mapping. Disabling a mapping does not * affect the mapping definition, hence a disabled mapping may later be * reenabled. If the ERASE flag is set the destination region is redrawn * with the mapping disabled. */ GtDisableMapping (w, mapping, erase) GtermWidget w; int mapping; /* mapping number */ int erase; /* erase the destination */ { register int i; register Mapping mp, dmp; XRectangle r, d, in; invalidate_draw_context (w); if (mapping >= 0 && mapping < w->gterm.maxMappings) { mp = &w->gterm.mappings[mapping]; if (mp->defined) { if (mp->enabled) { mp->enabled = False; if (erase) { d.x = mp->dx; d.y = mp->dy; d.width = abs(mp->dnx); d.height = abs(mp->dny); /* Clear the uncovered region. */ GtSetPixels (w, mp->dst, GtPixel, d.x, d.y, d.width, d.height, GtGetClientPixel(w,0), 0); /* Refresh any lower level mappings the destination rects of * which intersect the uncovered region. */ for (dmp = w->gterm.mp_head; dmp && dmp->mapping != mapping; dmp = dmp->next) { if (dmp->enabled && dmp->dst == mp->dst) { r.x = dmp->dx; r.y = dmp->dy; r.width = dmp->dnx; r.height = dmp->dny; if (rect_intersect (&in, &r, &d)) refresh_destination (w, dmp, in.x, in.y, in.width, in.height); } } } } return (OK); } } return (ERR); } /* GtRefreshMapping -- Refresh the destination region defined by a mapping. */ GtRefreshMapping (w, mapping) GtermWidget w; int mapping; /* mapping number */ { register Mapping mp; struct mapping p_mp; if (DBG_TRACE) fprintf(stderr, "GtRefreshMapping() -- ENTER\n"); if (mapping >= 0 && mapping < w->gterm.maxMappings) { mp = &w->gterm.mappings[mapping]; if (mp->defined) { if (mp->st != GtPixel || mp->dt != GtPixel) { initialize_mapping (&p_mp); get_pixel_mapping (w, mp, &p_mp, 1); update_mapping (w, mp = &p_mp); } else update_mapping (w, mp); if (CacheXImage) /* MF004 */ DestroyCachedXImage(); /* MF004 */ refresh_destination (w, mp, mp->dx, mp->dy, abs(mp->dnx), abs(mp->dny)); if (mp == &p_mp) free_mapping (w, mp); } } if (DBG_TRACE) fprintf(stderr, "GtRefreshMapping() -- LEAVING\n"); } /* GtMapVector -- Map a point vector from the coordinate system of one raster * to another as defined by the given mapping. The mapping may be either in * the forward direction (dir = GtMap) or the reverse (dir = GtUnmap). The * point vector is maintained in floating point for this operation to avoid * loss of precision. The input and output vectors may be the same vector. */ GtMapVector (w, mapping, dir, pv1, pv2, npts) GtermWidget w; int mapping; int dir; /* GtMap, GtUnmap */ DPoint *pv1; DPoint *pv2; int npts; { register DPoint *ip = pv1; register DPoint *op = pv2; register Mapping mp; register int n; struct mapping p_mp; double xscale, xoffset; double yscale, yoffset; int sx, sy; xscale = yscale = 1.0; xoffset = yoffset = 0.0; sx = sy = 0; if (mapping >= 0 && mapping < w->gterm.maxMappings) { mp = &w->gterm.mappings[mapping]; if (valid_mapping (w, mp)) { /* Determine the transformation coefficients. */ get_pixel_mapping (w, mp, &p_mp, 0); mp = &p_mp; xscale = (float)mp->dnx / (float)mp->snx; if (xscale < 0) xoffset = mp->dx + abs(mp->dnx) - 1; else xoffset = mp->dx; yscale = (float)mp->dny / (float)mp->sny; if (yscale < 0) yoffset = mp->dy + abs(mp->dny) - 1; else yoffset = mp->dy; sx = mp->sx; sy = mp->sy; } } /* Map the vector. */ if (dir == GtMap) { for (n=npts; --n >= 0; ip++, op++) { op->x = (ip->x - sx) * xscale + xoffset; op->y = (ip->y - sy) * yscale + yoffset; } } else { for (n=npts; --n >= 0; ip++, op++) { op->x = (ip->x - xoffset) / xscale + sx; op->y = (ip->y - yoffset) / yscale + sy; } } } /* GtPixelToNDC -- Transform a vector from pixel to NDC coordinates in the * coordinate system of the given reference raster. The input and output * vectors may be the same vector. */ GtPixelToNDC (w, raster, pv1, pv2, npts) GtermWidget w; int raster; DPoint *pv1; DPoint *pv2; int npts; { register Raster rp = &w->gterm.rasters[raster]; register DPoint *ip = pv1; register DPoint *op = pv2; register int n; for (n=npts; --n >= 0; ip++, op++) { op->x = ( ip->x) / rp->width * MAXNDC; op->y = (rp->height - ip->y) / rp->height * MAXNDC; } } /* GtNDCToPixel -- Transform a vector from NDC to pixel coordinates in the * coordinate system of the given reference raster. The input and output * vectors may be the same vector. */ GtNDCToPixel (w, raster, pv1, pv2, npts) GtermWidget w; int raster; DPoint *pv1; DPoint *pv2; int npts; { register Raster rp = &w->gterm.rasters[raster]; register DPoint *ip = pv1; register DPoint *op = pv2; register int n; for (n=npts; --n >= 0; ip++, op++) { op->x = ip->x / MAXNDC * rp->width; op->y = rp->height - (ip->y / MAXNDC * rp->height); } } /* GtDebug -- Print debug info. If the file descriptor is NULL output is * to the process stdout. The "what" argument may be used to select the * type of output desired. If what=0 the full output is generated, * otherwise bits are used to select what output is to be generated. * * "what" bitflags: * * 001 Widget information * 002 List rasters * 004 List mappings * 010 List colormaps * 020 List markers * * This routine is intended only for use during debugging. */ GtDebug (w, fp, what) GtermWidget w; FILE *fp; int what; { /* Default is to write everything to the stdout. */ what = what ? what : 0777; fp = fp ? fp : stdout; /* Print widget header. */ if (what & 001) { fprintf (fp, "Widget 0x%x (%s) %dx%d raster=%d\n", w, w->core.name, w->core.width, w->core.height, w->gterm.raster); fprintf (fp, "--------------------------------------------------------------\n"); } /* Print raster information. */ if (what & 002) { register int i; register Raster rp; if (w->gterm.rasters) { for (i=0; i < w->gterm.maxRasters; i++) { rp = &w->gterm.rasters[i]; if (!rp->type) continue; fprintf (fp, "raster %4d type=%s delete=%d size=%dx%d\n", i, rp->type == ImageRaster ? "client" : "server", rp->delete, rp->width, rp->height); } } else fprintf (fp, "no rasters\n"); } /* Print mapping information. */ if (what & 004) { register int i; register Mapping mp; char flags[32]; if (w->gterm.mappings) { for (i=0; i < w->gterm.maxMappings; i++) { mp = &w->gterm.mappings[i]; if (!mp->defined) continue; flags[0] = mp->enabled ? 'E' : 'D'; flags[1] = mp->updated ? 'U' : ' '; flags[2] = mp->refresh ? 'R' : ' '; flags[3] = '\0'; fprintf (fp, "mapping %3d %s %8o", i, flags, mp->rop); fprintf (fp, " %2d %s %3d %3d %3d %3d", mp->src, mp->st == GtPixel ? "pix" : "ndc", mp->sx, mp->sy, mp->snx, mp->sny); fprintf (fp, " %2d %s %3d %3d %3d %3d\n", mp->dst, mp->dt == GtPixel ? "pix" : "ndc", mp->dx, mp->dy, mp->dnx, mp->dny); } } else fprintf (fp, "no mappings\n"); fprintf (fp, "mappings from head: "); for (mp = w->gterm.mp_head; mp; mp = mp->next) fprintf (fp, " %d", mp->mapping); fprintf (fp, "\n"); fprintf (fp, "mappings from tail: "); for (mp = w->gterm.mp_tail; mp; mp = mp->prev) fprintf (fp, " %d", mp->mapping); fprintf (fp, "\n"); } /* Print colormap information. */ if (what & 010) { register struct colormap *cm; fprintf (fp, "cmapName=%s ncolors=%d basePixel=%d\n", w->gterm.cmapName, w->gterm.ncolors, w->gterm.base_pixel); for (cm = w->gterm.colormaps; cm; cm = cm->next) fprintf (fp, "colormap %2d ncells=%d\n", cm->map, cm->ncells); } /* Print marker information. */ if (what & 020) { register Marker mm; char value[256]; for (mm = w->gterm.gm_head; mm; mm = mm->next) { GmGetAttribute (mm, GmType, (XtArgVal)value, XtRString); fprintf (fp, "marker 0x%x: %10s flags=0x%x [%d %d %d %d] %0.5g\n", mm, value, mm->flags, mm->x, mm->y, mm->width, mm->height, mm->rotangle); } } }