/* * plotPnm.c -- * * This file contains procedures that generate PNM format files * to describe a section of layout. * * ********************************************************************* * * Copyright (C) 2000 Cornell University * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. Cornell University * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[]="$Header: /ufs/repository/magic/plot/plotPNM.c,v 1.5 2001/07/23 20:37:01 tim Exp $"; #endif #include #include #include "misc/magic.h" #include "utils/geometry.h" #include "utils/geofast.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "tech/tech.h" #include "utils/malloc.h" #include "utils/utils.h" #include "windows/windows.h" #include "dbwind/dbwind.h" #include "main/main.h" #include "commands/commands.h" #include "router/router.h" #include "textio/textio.h" #include "signals/signals.h" #define LANCZOS_KERNEL_SIZE 1024 #define PI 3.14159265 #define PIXELSZ sizeof(unsigned char) #define STRLEN 200 int PlotPNMmaxmem = 10*1024; /* 10MB */ int PlotPNMBGr = 255; /* background color for PNM plotting */ int PlotPNMBGg = 255; int PlotPNMBGb = 255; /* * Local variables, modified/shared by callbacks. * */ static int Init_Error; static float lk[2*LANCZOS_KERNEL_SIZE+1]; static int *lkstep; /* lanczos kernel steps */ static unsigned char *rtile; static int tile_xsize, tile_ysize; static Rect bb; static unsigned long BBinit; static int tile_yshift, tile_xshift; static int scale_over_2; static int im_x, im_y; static int im_yoffset; static int y_pixels; #define ZERO(x) (x = 0) /* rgb colormap for pnm plotting */ static unsigned char plotCMAPr[128]; static unsigned char plotCMAPg[128]; static unsigned char plotCMAPb[128]; static int ncolors; static void PlotLoadStyles(); static struct { char *name; int init; unsigned int wmask, color; } Dstyles[128]; static int ndstyles = 0; static struct { char *name; unsigned int wmask, color; } PaintStyles[128]; static int npaintstyles = 0; #if 0 static void dumpRect (r) Rect *r; { TxPrintf ("(%d %d) -> (%d %d)", r->r_xbot, r->r_ybot, r->r_xtop, r->r_ytop); } #endif static void pnmRenderTile (fp,scale,scale_over_2,temp) FILE *fp; float scale; int scale_over_2; float *temp; { int i, j; int jmax; int x, y; int dx, dy; float r, g, b; i = 0; jmax = MIN(y_pixels, im_yoffset+1); /* x, y : pixel coords */ if (scale_over_2 == 0) { for (j=0; j < jmax; j++) { y = bb.r_ybot + ((float)(im_yoffset-j)*scale) - tile_yshift; for (i=0; i < im_x; i++) { x = bb.r_xbot + ((float)i*scale) - tile_xshift; r = plotCMAPr[*(rtile + x + y*tile_xsize)]; g = plotCMAPg[*(rtile + x + y*tile_xsize)]; b = plotCMAPb[*(rtile + x + y*tile_xsize)]; fprintf (fp, "%c%c%c", (unsigned char)r, (unsigned char)g, (unsigned char)b); } } } else { for (j=0; j < jmax; j++) { y = bb.r_ybot + ((float)(im_yoffset-j)*scale) - tile_yshift; for (i=0; i < im_x; i++) { x = bb.r_xbot + ((float)i*scale) - tile_xshift; for (dx = -scale_over_2; dx < scale_over_2; dx++) { r = 0.0; g = 0.0; b = 0.0; for (dy = -scale_over_2; dy < scale_over_2; dy++) { if (dy + y >= tile_ysize) continue; /* grab rgb for (x + dx, y + dy) */ r += plotCMAPr[*(rtile + x + dx + (y + dy)*tile_xsize)] *lk[lkstep[dy+scale_over_2]]; g += plotCMAPg[*(rtile + x + dx + (y + dy)*tile_xsize)] *lk[lkstep[dy+scale_over_2]]; b += plotCMAPb[*(rtile + x + dx + (y + dy)*tile_xsize)] *lk[lkstep[dy+scale_over_2]]; } temp[3*(dx + scale_over_2)] = r; temp[3*(dx + scale_over_2)+1] = g; temp[3*(dx + scale_over_2)+2] = b; } r = 0.0; g = 0.0; b = 0.0; for (dx = 0; dx < 2*scale_over_2; dx++) { r += temp[3*dx]*lk[lkstep[dx]]; g += temp[3*dx+1]*lk[lkstep[dx]]; b += temp[3*dx+2]*lk[lkstep[dx]]; } r /= (scale_over_2*2)*(scale_over_2*2); g /= (scale_over_2*2)*(scale_over_2*2); b /= (scale_over_2*2)*(scale_over_2*2); fprintf (fp, "%c%c%c", (unsigned char)r, (unsigned char)g, (unsigned char)b); } } } } /* * ---------------------------------------------------------------------------- * * pnmPaint -- * * Draw a tile according to colors read in from plot style file. * * Results: * Returns 1 on error, 0 if okay * * Side effects: * Modifies rtile array. * * ---------------------------------------------------------------------------- */ static int pnmPaint (type, r) TileType type; Rect *r; { int i, j; int x, y, dx, dy; unsigned char *t; unsigned wmask, col; /* paint rectangle! */ if (DBTypeLongNameTbl[type]) { for (i=0; i < npaintstyles; i++) { if (strcmp (PaintStyles[i].name, DBTypeLongNameTbl[type]) == 0) { wmask = PaintStyles[i].wmask; col = PaintStyles[i].color & wmask; /* match, paint rectangle. */ x = r->r_xbot - tile_xshift; y = r->r_ybot - tile_yshift; dx = r->r_xtop - r->r_xbot; dy = r->r_ytop - r->r_ybot; if (x < 0 || y < 0 || x >= tile_xsize || y >= tile_ysize) { /* error */ return 1; } t = rtile + x + tile_xsize*y; for (dy; dy > 0; dy--) { for (j=0; j < dx; j++) { *t = (*t & ~wmask) | col; t++; } t = t - dx + tile_xsize; } break; } } } /* okay */ return 0; } /* * ---------------------------------------------------------------------------- * * pnmBBOX -- * * Callback for DBTreeSrTiles; compute bounding box of plot * * Results: * None. * * Side effects: * Modifies BBinit, updates bounding box "bb" * * ---------------------------------------------------------------------------- */ static int pnmBBOX (tile,cxp) register Tile *tile; TreeContext *cxp; { Rect targetRect, sourceRect; SearchContext *scx = cxp->tc_scx; Rect *arg; TileType type; if ((type = TiGetType(tile)) == TT_SPACE) return 0; /* grab rectangle from tile */ TITORECT(tile,&targetRect); /* coordinate transform */ GEOTRANSRECT(&scx->scx_trans, &targetRect, &sourceRect); /* Clip */ arg = (Rect *)cxp->tc_filter->tf_arg; GEOCLIP(&sourceRect,arg); /* compute bbox */ if (!BBinit) { bb = sourceRect; } else { bb.r_xbot = MIN(bb.r_xbot, sourceRect.r_xbot); bb.r_ybot = MIN(bb.r_ybot, sourceRect.r_ybot); bb.r_xtop = MAX(bb.r_xtop, sourceRect.r_xtop); bb.r_ytop = MAX(bb.r_ytop, sourceRect.r_ytop); } BBinit = 1; return 0; } /* * ---------------------------------------------------------------------------- * * pnmTile -- * * Callback for DBTreeSrTiles; paints tiles in the current rtile buffer. * * Results: * None. * * Side effects: * Modifies rtile array. * * ---------------------------------------------------------------------------- */ static int pnmTile (tile,cxp) register Tile *tile; TreeContext *cxp; { Rect targetRect, sourceRect; SearchContext *scx = cxp->tc_scx; Rect *arg; TileType type; if ((type = TiGetType(tile)) == TT_SPACE) return 0; /* grab rectangle from tile */ TITORECT(tile,&targetRect); /* coordinate transform */ GEOTRANSRECT(&scx->scx_trans, &targetRect, &sourceRect); /* Clip */ arg = (Rect *)cxp->tc_filter->tf_arg; GEOCLIP(&sourceRect,arg); /* RENDER TILE */ return pnmPaint (type, &sourceRect); } /* * ---------------------------------------------------------------------------- * * PlotPNM -- * * This procedure generates a PNM file to describe an area of * a layout. * * Results: * None. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ void PlotPNM(fileName, scx, layers, xMask, invscale) char *fileName; /* Name of PNM file to write. */ SearchContext *scx; /* The use and area and transformation * in this describe what to plot. */ TileTypeBitMask *layers; /* Tells what layers to plot. Only * paint layers in this mask, and also * expanded according to xMask, are * plotted. If L_LABELS is set, then * labels on the layers are also * plotted, if expanded according to * xMask. If L_CELL is set, then * subcells that are unexpanded * according to xMask are plotted as * bounding boxes. */ int xMask; /* An expansion mask, used to indicate * the window whose expansion status * will be used to determine * visibility. Zero means treat * everything as expanded. */ float invscale; /* Indicates the width of the * plot; 0 = pixel width. */ { FILE *fp; Rect bbox; int bb_ysize; int i, x, y; float *strip; float scale; if (Init_Error) { TxError ("PNM module initialization had failed; cannot plot\n"); return; } if (invscale <= 0) { TxError ("PNM module given negative scale; cannot plot\n"); return; } scale = 1.0 / invscale; if ((scale > 2) || (invscale != ceil(invscale))) { scale_over_2 = (int) ceil(scale / 2.0); } else scale_over_2 = 0; /* image: * ----- * | xxx | * | xxx | * | xxx | * ----- * * Use -scale/2 to scale/2 pixels for each pixel. * */ /* Rendering Tile: * * 0.. bbox size + 2 * scale_over_2. * * To sample, pixel (i,j) will be at: * (scale_over_2 + scale*i, scale_over_2 + scale*j) * * Given an initial pixel position at (i,j), we sample from * -scale_over_2 to scale_over_2 */ /* Compute bounding box size in lambda */ BBinit = 0; DBTreeSrTiles (scx, layers, xMask, pnmBBOX,(ClientData)&scx->scx_area); tile_xsize = bb.r_xtop - bb.r_xbot + 2*scale_over_2; bb_ysize = bb.r_ytop - bb.r_ybot; /* bump search context by scale_over_2 pixels on each side */ scx->scx_area.r_xbot = bb.r_xbot - scale_over_2; scx->scx_area.r_ybot = bb.r_ybot - scale_over_2; scx->scx_area.r_xtop = bb.r_xtop + scale_over_2; scx->scx_area.r_ytop = bb.r_ytop + scale_over_2; /* check for empty region */ if (BBinit == 0 || tile_xsize <= 0 || bb_ysize <= 0) { TxPrintf ("Empty region, no plot\n"); return; } /* Compute memory requirements; a single pixel line needs a tile that has size "xsize" by "scale." To keep inter-tile overlap low, we insist that a single tile must have at least 3*scale pixels in it. */ if (PlotPNMmaxmem*1024 < ((3*scale+2*scale_over_2)*PIXELSZ*tile_xsize)) { TxPrintf ("Insufficent memory to antialias image.\n"); TxPrintf ("Current: %d KB; Required: %d KB\n", PlotPNMmaxmem, (int) (1023+(3*scale+2*scale_over_2)*PIXELSZ*tile_xsize)/1024); return; } /* Compute the maximum y size for a tile. */ tile_ysize = PlotPNMmaxmem*1024/(PIXELSZ*tile_xsize); if (tile_ysize > (bb_ysize + 2*scale_over_2)) tile_ysize = bb_ysize + 2*scale_over_2; MALLOC (unsigned char *, rtile, tile_xsize*tile_ysize*PIXELSZ); /* bump search context by scale_over_2 pixels on each side */ scx->scx_area.r_ybot = scx->scx_area.r_ytop - tile_ysize; tile_yshift = scx->scx_area.r_ybot; tile_xshift = scx->scx_area.r_xbot; /* open PNM file */ fp = PaOpen (fileName, "w", ".pnm", ".", NULL, NULL); if (fp == NULL) { TxError ("Could not open file `%s' for writing\n", fileName); goto done; } fprintf (fp, "P6\n"); fprintf (fp, "%d %d\n", im_x = (int)((bb.r_xtop - bb.r_xbot)/scale), im_y = (int)((bb.r_ytop - bb.r_ybot)/scale)); fprintf (fp, "255\n"); im_yoffset = im_y-1; y_pixels = (tile_ysize - scale_over_2*2)/scale; if (y_pixels > im_y) y_pixels = im_y; #if 0 TxPrintf ("Image: %d x %d\n", im_x, im_y); TxPrintf ("Pixels per tile: %d\n", y_pixels); #endif MALLOC (float *, strip, scale_over_2*2*3*sizeof(float)); MALLOC (int *, lkstep, scale_over_2*2*sizeof(int)); for (x = -scale_over_2; x < scale_over_2; x++) { lkstep[scale_over_2+x] = ((float)ABS(x))/scale*LANCZOS_KERNEL_SIZE; if (lkstep[scale_over_2+x] >= LANCZOS_KERNEL_SIZE) lkstep[scale_over_2+x] = LANCZOS_KERNEL_SIZE-1; } while (im_yoffset > 0) { /* clear tile */ for (x=0; x < tile_xsize; x++) for (y=0; y < tile_ysize; y++) { ZERO (rtile[y + tile_ysize*x]); } if (SigInterruptPending) { TxPrintf (" *** interrupted ***\n"); goto done; } DBTreeSrTiles (scx, layers, xMask, pnmTile,(ClientData)&scx->scx_area); /* anti-aliased rendering */ pnmRenderTile (fp,scale,scale_over_2,strip); /* advance to the next strip */ im_yoffset -= y_pixels; tile_yshift -= (y_pixels*scale); scx->scx_area.r_ybot -= (y_pixels*scale); scx->scx_area.r_ytop -= (y_pixels*scale); } /*TxPrintf ("Save to file `%s', scale = %f\n", fileName, scale);*/ fclose (fp); done: FREE (rtile); FREE (strip); FREE (lkstep); return; } /* * ---------------------------------------------------------------------------- * * lanczos_kernel -- * * Compute the value of the lanczos kernel at the given position. * * * Results: * Returns kernel value at arg x. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ float lanczos_kernel(x) int x; /* position at which to evaluate the * lanczos kernel */ { if (x == 0.0) return 1.0; return (float)(sin(PI*x)/(PI*x))*(sin(PI*0.5*x)/(PI*0.5*x)); } /* * ---------------------------------------------------------------------------- * * PlotPNMTechInit -- * * Called when magic starts up. * * * Results: * None. * * Side effects: * Initializes lk[...] array with the lanczos kernel. * * ---------------------------------------------------------------------------- */ Void PlotPNMTechInit() { int i; Init_Error = 0; /* Initialize Lanczos kernel */ for (i=0; i <= 2*LANCZOS_KERNEL_SIZE; i++) lk[i] = lanczos_kernel ((i+0.0)/LANCZOS_KERNEL_SIZE); PlotLoadStyles (); } /* * ---------------------------------------------------------------------------- * * PlotPnmTechLine -- * * Parse a magic technology file line for the pnm plot style * * Results: * None. * * Side effects: * Modifies paintstyles[] array. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ bool PlotPNMTechLine(sectionName, argc, argv) char *sectionName; /* Name of this section (unused). */ int argc; /* Number of arguments on line. */ char *argv[]; /* Pointers to fields of line. */ { int i, j; if (strcmp (argv[0], "draw") == 0) { for (i=0; i < ndstyles; i++) { if (strcmp (argv[1], Dstyles[i].name) == 0) { PaintStyles[npaintstyles].name = Dstyles[i].name; PaintStyles[npaintstyles].wmask = Dstyles[i].wmask; PaintStyles[npaintstyles++].color = Dstyles[i].color; break; } } } else if (strcmp (argv[0], "map") == 0) { PaintStyles[npaintstyles].name = StrDup (NULL, argv[1]); for (j=2; j < argc; j++) { for (i=0; i < npaintstyles; i++) { if (strcmp (argv[j],PaintStyles[i].name) == 0) { PaintStyles[npaintstyles].wmask |= PaintStyles[i].wmask; PaintStyles[npaintstyles].color |= PaintStyles[i].color; break; } } } npaintstyles++; } return TRUE; } /* * ---------------------------------------------------------------------------- * * PlotLoadStyles -- * * Read in the plotting style + colormap for rendering. This * always uses the 7bit colormap. * * Results: * None. * * Side effects: * Initializes arrays for drawing/plotting. * * ---------------------------------------------------------------------------- */ static void PlotLoadStyles() { FILE *inp; char fullName[256]; char *buf = fullName; int newsec; int ord, mask, color, outline, nfill, stipple; char shortname; char longname[128]; char fill[42]; char *which; which = "display styles"; (void) sprintf(fullName, "%.100s.7bit.mraster_dstyle5", DBWStyleType); inp = PaOpen(fullName, "r", (char *)NULL, ".", SysLibPath, (char **) NULL); if (inp == NULL) goto err; /* read in the dstyle5 file for mraster */ newsec = FALSE; while (fgets (buf, 256, inp)) { if (buf[0] == '#') continue; if (StrIsWhite (buf, FALSE)) { newsec = TRUE; continue; } else if (newsec) { if (strcmp (buf, "display_styles 7\n") != 0) { goto err; } newsec = FALSE; } else { if (sscanf (buf, "%d %o %o %o %40s %d %c %s", &ord, &mask, &color, &outline, fill, &stipple, &shortname, longname) != 8) { goto err; } if (ndstyles == 128) { goto err; } Dstyles[ndstyles].wmask = mask; Dstyles[ndstyles].color = color; Dstyles[ndstyles++].name = StrDup (NULL, longname); } } fclose (inp); /* read in color map */ (void) sprintf(fullName, "%.100s.7bit.mraster.cmap1", DBWStyleType); inp = PaOpen(fullName, "r", (char *) NULL, ".", SysLibPath, (char **) NULL); if (inp == NULL) { TxError("Couldn't open colormap file \"%s\"\n", fullName); Init_Error = 1; return; } ncolors = 0; which = "colormap"; while (fgets (buf, 256, inp)) { if (buf[0] == '#') continue; if (StrIsWhite (buf, FALSE)) continue; if (ncolors == 128) { goto err; } if (sscanf (buf, "%d %d %d", &mask, &color, &outline) != 3) { goto err; } plotCMAPr[ncolors] = mask; plotCMAPg[ncolors] = color; plotCMAPb[ncolors] = outline; ncolors ++; } fclose (inp); PlotPNMSetBG (); return; err: Init_Error = 1; if (inp) { TxError ("Format error in %s file\n", which); fclose (inp); } else { TxError ("PNM plot: Could not open %s file\n", which); } return; } /* * ---------------------------------------------------------------------------- * * PlotPNMSetBG -- * * Set background color for PNM file * * Results: * None. * * Side effects: * Sets colormap[0] entry to specified rgb value. * * ---------------------------------------------------------------------------- */ Void PlotPNMSetBG () { plotCMAPr[0] = PlotPNMBGr; plotCMAPg[0] = PlotPNMBGg; plotCMAPb[0] = PlotPNMBGb; }