/* * 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. * * ********************************************************************* * * R. Timothy Edwards * Copyright (C) 2004 * MultiGiG, Inc. * Scotts Valley, CA * * Cleaned up the code, including optimization for speed * Added: Non-Manhattan geometry handling, 24-bit color, * plot styles automatically generated from display styles, * downsampling for large plots, extended syntax for the * technology file description. */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/plot/plotPNM.c,v 1.3 2010/06/24 12:37:25 tim Exp $"; #endif /* not lint */ #include #include #include /* C99 compat */ #include #include "utils/magic.h" #include "utils/geometry.h" #include "utils/geofast.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "utils/tech.h" #include "utils/malloc.h" #include "utils/utils.h" #include "utils/styles.h" #include "windows/windows.h" #include "graphics/graphics.h" #include "dbwind/dbwtech.h" #include "dbwind/dbwind.h" #include "utils/main.h" #include "commands/commands.h" #include "textio/textio.h" #include "utils/signals.h" #include "plot/plotInt.h" #define LANCZOS_KERNEL_SIZE 1024 #define PI 3.14159265 /* Structure for saving R, G, B components of colors */ /* from a non-default colormap. */ typedef struct _pnmcolor { unsigned char r, g, b; } pnmcolor; pnmcolor *PNMcolors = NULL; static int ncolors = 0; #define PIXELSZ sizeof(pnmcolor) int PlotPNMmaxmem = 64 * 1024; /* 64MB */ int PlotPNMdownsample = 0; /* No downsampling by default */ unsigned char PlotPNMBG = 0xff; /* White background by default */ #ifdef VERSATEC bool PlotPNMRTL = FALSE; /* If true, filter output through HP driver */ #endif /* * Local variables, modified/shared by callbacks. */ int Init_Error; float lk[2 * LANCZOS_KERNEL_SIZE + 1]; int *lkstep; /* lanczos kernel steps */ pnmcolor *rtile; int tile_xsize, tile_ysize; int ds_xsize, ds_ysize; Rect bb; unsigned long BBinit; int tile_yshift, tile_xshift; int im_x, im_y; int im_yoffset; int y_pixels; /* Structure for saving styles that are different from */ /* the styles loaded for this technology. */ typedef struct _dstyle { char *name; int init; unsigned int wmask; pnmcolor color; } dstyle; dstyle *Dstyles = NULL; static int ndstyles = 0; /* Structure which records how to paint a tile type. */ typedef struct _pstyle { unsigned int wmask; pnmcolor color; } pstyle; pstyle *PaintStyles = NULL; /* Forward declarations */ extern void PlotLoadStyles(); extern void PlotLoadColormap(); extern pnmcolor PNMColorBlend(); extern pnmcolor PNMColorIndexAndBlend(); /* * ---------------------------------------------------------------------------- * * Function for output of PNM line data to HPRTL format * * ---------------------------------------------------------------------------- */ #ifdef VERSATEC struct plotRTLdata { FILE *outfile; unsigned char *outbytes; }; int pnmRTLLineFunc(linebuffer, arg) unsigned char *linebuffer; struct plotRTLdata *arg; { int size; size = PlotRTLCompress(linebuffer, arg->outbytes, im_x * 3); fprintf(arg->outfile, "\033*b%dW", size); fwrite(arg->outbytes, size, 1, arg->outfile); return 0; } #endif /* * ---------------------------------------------------------------------------- * * Function for output of PNM line data to file fp * * ---------------------------------------------------------------------------- */ int pnmLineFunc(linebuffer, fp) unsigned char *linebuffer; FILE *fp; { fwrite(linebuffer, im_x * 3, 1, fp); return 0; } /* * ---------------------------------------------------------------------------- * * pnmRenderRegion -- * * Antialiased rendering. * * Results: * None. * * Side effects: * Writes output to file. * * ---------------------------------------------------------------------------- */ void pnmRenderRegion(scale, scale_over_2, normal, temp, func, arg) float scale; int scale_over_2; float normal; /* normalizing factor */ float *temp; /* passed so we don't have to allocate it * on every call. */ int (*func)(); /* Function to call per line of output */ ClientData arg; /* Arguments to function */ { int i, j; int jmax; int x, y; int dx, dy; int ds_over_2; pnmcolor *color; float r, g, b; unsigned char *linebuffer, *lineptr; jmax = MIN(y_pixels, im_yoffset + 1); ds_over_2 = scale_over_2 >> PlotPNMdownsample; linebuffer = mallocMagic(im_x * 3); /* x, y : pixel coords */ if (ds_over_2 == 0) { for (j = 0; j < jmax; j++) { lineptr = linebuffer; y = scale * (y_pixels - 1 - j); y >>= PlotPNMdownsample; for (i = 0; i < im_x; i++) { x = scale * i; x >>= PlotPNMdownsample; color = rtile + x + y * ds_xsize; *lineptr++ = color->r; *lineptr++ = color->g; *lineptr++ = color->b; } (*func)(linebuffer, arg); } } else { /* When the scale is small enough, we have to resort to antialiasing */ float lkval; int tidx; for (j = 0; j < jmax; j++) { y = scale_over_2 + scale * (y_pixels - 1 - j); y >>= PlotPNMdownsample; lineptr = linebuffer; for (i = 0; i < im_x; i++) { x = scale_over_2 + scale * i; x >>= PlotPNMdownsample; for (dx = -ds_over_2; dx < ds_over_2; dx++) { r = 0.0; g = 0.0; b = 0.0; for (dy = -ds_over_2; dy < ds_over_2; dy++) { if (dy + y >= ds_ysize) continue; /* grab rgb for (x + dx, y + dy) */ color = rtile + (x + dx) + (y + dy) * ds_xsize; lkval = lk[lkstep[dy + ds_over_2]]; r += (float)color->r * lkval; g += (float)color->g * lkval; b += (float)color->b * lkval; } tidx = 3 * (dx + ds_over_2); temp[tidx++] = r; temp[tidx++] = g; temp[tidx] = b; } r = 0.0; g = 0.0; b = 0.0; for (dx = 0; dx < 2 * ds_over_2; dx++) { tidx = 3 * dx; lkval = lk[lkstep[dx]]; r += temp[tidx++] * lkval; g += temp[tidx++] * lkval; b += temp[tidx] * lkval; } r /= normal; g /= normal; b /= normal; *lineptr++ = (unsigned char)r; *lineptr++ = (unsigned char)g; *lineptr++ = (unsigned char)b; } (*func)(linebuffer, arg); } } freeMagic(linebuffer); } /* * ---------------------------------------------------------------------------- * * pnmBBOX -- * * Callback for DBTreeSrTiles; compute bounding box of plot * * Results: * Always return 0 to keep search going. * * Side effects: * Modifies BBinit, updates bounding box "bb" * * ---------------------------------------------------------------------------- */ int pnmBBOX (tile,cxp) Tile *tile; TreeContext *cxp; { Rect targetRect, sourceRect; SearchContext *scx = cxp->tc_scx; Rect *arg; TileType type; if (!IsSplit(tile)) 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: * Return 0 to keep search going unless an error condition was * encountered. * * Side effects: * Modifies rtile array. * * ---------------------------------------------------------------------------- */ int pnmTile (tile, cxp) Tile *tile; TreeContext *cxp; { SearchContext *scx = cxp->tc_scx; Rect targetRect, sourceRect, *clipRect; int type, j, x, y, dx, dy; pnmcolor *t; pnmcolor col; if ((type = (int)TiGetTypeExact(tile)) == TT_SPACE) return 0; /* undefined type; paint nothing */ if (!IsSplit(tile)) if (PaintStyles[type].wmask == 0) return 0; /* grab rectangle from tile */ TITORECT(tile, &targetRect); /* coordinate transform */ GEOTRANSRECT(&scx->scx_trans, &targetRect, &sourceRect); /* Clip */ clipRect = (Rect *)cxp->tc_filter->tf_arg; /* Handle non-Manhattan geometry */ if (IsSplit(tile)) { TileType dinfo; int w, h, llx, lly, urx, ury; Rect scaledClip; type = (SplitSide(tile)) ? SplitRightType(tile) : SplitLeftType(tile); if (type == TT_SPACE) return 0; else if (PaintStyles[type].wmask == 0) return 0; llx = sourceRect.r_xbot - tile_xshift; lly = sourceRect.r_ybot - tile_yshift; llx >>= PlotPNMdownsample; lly >>= PlotPNMdownsample; dx = sourceRect.r_xtop - sourceRect.r_xbot; dy = sourceRect.r_ytop - sourceRect.r_ybot; dx >>= PlotPNMdownsample; dy >>= PlotPNMdownsample; urx = llx + dx; ury = lly + dy; col = PaintStyles[type].color; scaledClip = *clipRect; scaledClip.r_xbot -= tile_xshift; scaledClip.r_xtop -= tile_xshift; scaledClip.r_ybot -= tile_yshift; scaledClip.r_ytop -= tile_yshift; scaledClip.r_xbot >>= PlotPNMdownsample; scaledClip.r_xtop >>= PlotPNMdownsample; scaledClip.r_ybot >>= PlotPNMdownsample; scaledClip.r_ytop >>= PlotPNMdownsample; /* The following structures could be much better */ /* written for considerable speedup. . . */ dinfo = DBTransformDiagonal(TiGetTypeExact(tile), &scx->scx_trans); if (((dinfo & TT_SIDE) >> 1) != (dinfo & TT_DIRECTION)) { /* work top to bottom */ for (y = ury - 1; y >= lly; y--) { if (y >= scaledClip.r_ytop) continue; else if (y < scaledClip.r_ybot) break; if (dinfo & TT_SIDE) /* work right to left */ { for (x = urx - 1; x >= llx; x--) { if (x >= scaledClip.r_xtop) continue; else if (x < scaledClip.r_xbot) break; if (((urx - x) * dy) > ((ury - y) * dx)) break; t = rtile + x + ds_xsize * y; *t = PNMColorBlend(t, &col); } } else /* work left to right */ { for (x = llx; x < urx; x++) { if (x < scaledClip.r_xbot) continue; else if (x >= scaledClip.r_xtop) break; if (((x - llx) * dy) > ((ury - y) * dx)) break; t = rtile + x + ds_xsize * y; *t = PNMColorBlend(t, &col); } } } } else /* work bottom to top */ { for (y = lly; y < ury; y++) { if (y < scaledClip.r_ybot) continue; else if (y >= scaledClip.r_ytop) break; if (dinfo & TT_SIDE) /* work right to left */ { for (x = urx; x >= llx; x--) { if (x >= scaledClip.r_xtop) continue; else if (x < scaledClip.r_xbot) break; if (((urx - x) * dy) > ((y - lly) * dx)) break; t = rtile + x + ds_xsize * y; *t = PNMColorBlend(t, &col); } } else /* work left to right */ { for (x = llx; x < urx; x++) { if (x < scaledClip.r_xbot) continue; else if (x >= scaledClip.r_xtop) break; if (((x - llx) * dy) > ((y - lly) * dx)) break; t = rtile + x + ds_xsize * y; *t = PNMColorBlend(t, &col); } } } } return 0; } GEOCLIP(&sourceRect, clipRect); /* paint rectangle */ x = sourceRect.r_xbot - tile_xshift; y = sourceRect.r_ybot - tile_yshift; /* stop the search on an error condition */ /* (this should not happen---is guaranteed by GEOCLIP */ if ((x < 0) || (y < 0) || (x >= tile_xsize) || (y >= tile_ysize)) return 1; x >>= PlotPNMdownsample; y >>= PlotPNMdownsample; dx = sourceRect.r_xtop - sourceRect.r_xbot; dy = sourceRect.r_ytop - sourceRect.r_ybot; dx >>= PlotPNMdownsample; dy >>= PlotPNMdownsample; col = PaintStyles[type].color; t = rtile + x + ds_xsize * y; for ( ; dy > 0; dy--) { for (j = 0; j < dx; j++) { *t = PNMColorBlend(t, &col); t++; } t = t - dx + ds_xsize; } /* Continue search function */ return 0; } /* * ---------------------------------------------------------------------------- * * 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, width) 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. */ int width; /* Indicates the width of the * plot, in pixels. */ { FILE *fp = NULL; Rect bbox; int bb_ysize, bb_xsize; int i, x, y, tile_ydelta; int save_ds, iter; int scale_over_2, ds_over_2; float *strip = NULL; float scale, invscale, scaledown, normal; #ifdef VERSATEC struct plotRTLdata rtl_args; rtl_args.outfile = NULL; char command[200], tempFile[200]; #endif // Sanity check on PaintStyles---may be NULL if plot section // was missing from techfile. If so, run default init/final // procedures, flag a warning, and continue. if (PaintStyles == NULL) { TxError ("Warning: No plot section in techfile, using defaults.\n"); PlotPNMTechInit(); PlotPNMTechFinal(); } if (width <= 0) { TxError ("PNM module given negative pixel width; cannot plot\n"); return; } if (Init_Error) { TxError ("PNM module initialization had failed; cannot plot\n"); return; } /* image: * ----- * | xxx | * | xxx | * | xxx | * ----- * * Use -scale/2 to scale/2 magic coordinates for each output 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); /* Initial bounding box size */ bb_ysize = bb.r_ytop - bb.r_ybot; bb_xsize = bb.r_xtop - bb.r_xbot; /* Determine value of "scale" from the total pixel width. */ scale = (float)bb_xsize / (float)width; invscale = 1.0 / scale; scale = 1.0 / invscale; if ((scale > 2) || (invscale != ceil(invscale))) scale_over_2 = (int) ceil(scale / 2.0); else scale_over_2 = 0; /* 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; /* Recalculate bounding box with extended boundary */ bb_ysize = bb.r_ytop - bb.r_ybot; bb_xsize = bb.r_xtop - bb.r_xbot; tile_xsize = bb_xsize + 2 * 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. */ save_ds = PlotPNMdownsample; while ((PlotPNMmaxmem * 1024) < ((3 * scale + 2 * scale_over_2) * PIXELSZ * tile_xsize) / (1 << (PlotPNMdownsample * 2))) PlotPNMdownsample++; if (PlotPNMdownsample != save_ds) { TxPrintf ("%dX downsampling forced by memory size requirements.\n", PlotPNMdownsample); TxPrintf ("Current: %d KB; Required for non-downsampled image: %d KB\n", PlotPNMmaxmem, (int) (1023 + ((3 * scale + 2 * scale_over_2) * PIXELSZ * tile_xsize) / 1024) / (1 << (save_ds * 2))); TxPrintf ("Use \"plot parameter pnmmaxmem\" to increase allocation.\n"); } /* * Compute the maximum y size for a tile. */ tile_ysize = PlotPNMmaxmem * 1024 / (PIXELSZ * tile_xsize); tile_ydelta = (tile_ysize - scale_over_2 * 2); /* Determine the amount shifted in Y for each consecutively */ /* computed region. */ /* tile_ydelta is the amount of shift in magic units. */ /* y_pixels is the amount of shift in PNM pixels. */ /* tile_ydelta MUST EQUAL y_pixels * scale. If not, then */ /* we need to back-compute a better tile_ysize value. */ y_pixels = tile_ydelta / scale; if (y_pixels == 0) y_pixels = 1; if (y_pixels * scale != tile_ydelta) { tile_ydelta = scale * y_pixels; tile_ysize = tile_ydelta + (scale_over_2 * 2); } /* If there's enough memory allocation, tile_ysize bounds the whole plot */ if (tile_ysize > (bb_ysize + 2 * scale_over_2)) { tile_ysize = bb_ysize + 2 * scale_over_2; tile_ydelta = bb_ysize; y_pixels = tile_ydelta / scale; } ds_xsize = tile_xsize >> PlotPNMdownsample; ds_ysize = tile_ysize >> PlotPNMdownsample; ds_over_2 = scale_over_2 >> PlotPNMdownsample; rtile = (pnmcolor *) mallocMagic((ds_xsize * ds_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; im_x = (int)(bb_xsize / scale); im_y = (int)(bb_ysize / scale); #ifdef VERSATEC if (PlotPNMRTL) { if (fileName == NULL) { int result; sprintf(tempFile, "%s/magicPlotXXXXXX", PlotTempDirectory); result = mkstemp(tempFile); if (result == -1) { TxError("Failed to create temporary filename for %s\n", tempFile); return; } fileName = tempFile; } else if (strchr(fileName, '.') == NULL) { /* Add extention ".pnm" if the filename does not have an extension */ sprintf(tempFile, "%s.pnm", fileName); fileName = tempFile; } rtl_args.outfile = PaOpen(fileName, "w", (char *)NULL, ".", (char *)NULL, (char **)NULL); if (rtl_args.outfile == NULL) { TxError("Couldn't open file \"%s\" to write plot.\n", fileName); return; } switch (PlotVersPlotType) { case HPGL2: /* Write HPGL2 header */ /* Universal Command Language. */ fprintf(rtl_args.outfile, "\033%%-12345X"); /* Reset printer; set HPGL2 mode. */ fprintf(rtl_args.outfile, "@PJL ENTER LANGUAGE=HPGL2\r\n"); fprintf(rtl_args.outfile, "\033E\033%%0B"); /* Declare name; disable auto-rotate */ fprintf(rtl_args.outfile, "BP1,\"MAGIC\",5,1;"); /* Enter RTL mode. */ fprintf(rtl_args.outfile, "\033%%0A"); /* Source mode opaque */ fprintf(rtl_args.outfile, "\033*v1N"); /* Drop through */ case HPRTL: /* Write HPRTL header */ /* Direct pixel mode, 8 bits/component */ fwrite("\033*v6W\000\003\010\010\010\010", 11, 1, rtl_args.outfile); /* Image width in pixels. */ fprintf(rtl_args.outfile, "\033*r%dS", im_x); /* Image height in pixels.*/ fprintf(rtl_args.outfile, "\033*r%dT", im_y); /* No negative motion. */ fprintf(rtl_args.outfile, "\033&a1N"); /* Mode 2 row compression */ /* But, we REALLY ought to have delta row compression here. . . */ fprintf(rtl_args.outfile, "\033*b2M"); /* Printer resolution in DPI */ fprintf(rtl_args.outfile, "\033*t%dR", PlotVersDotsPerInch); /* Start raster data */ fprintf(rtl_args.outfile, "\033*r%cA", (PlotVersPlotType == HPGL2) ? '1' : '0'); break; } /* Reserve enough space for run-length encoding compression */ rtl_args.outbytes = mallocMagic((im_x * 3) + ((im_x * 3) / 127) + 1); } else #endif { /* open PNM file */ fp = PaOpen (fileName, "w", (strchr(fileName, '.') == NULL) ? ".pnm" : NULL, ".", 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, im_y); fprintf (fp, "255\n"); } im_yoffset = im_y - 1; TxPrintf ("PNM image dimensions: %d x %d\n", im_x, im_y); #if 0 TxPrintf ("Region size: %d x %d\n", tile_xsize, tile_ysize); TxPrintf ("Pixels per region: %d\n", y_pixels); TxPrintf ("Scale: %g\n", scale); TxPrintf ("Antialiasing overlap: %d\n", scale_over_2); if (PlotPNMdownsample > 0) { TxPrintf ("Downsampling: %d\n", PlotPNMdownsample); TxPrintf ("Downsampled region size: %d x %d\n", ds_xsize, ds_ysize); } #endif strip = (float *) mallocMagic((unsigned) (ds_over_2 * 2 * 3 * sizeof(float))); lkstep = (int *) mallocMagic((unsigned) (ds_over_2 * 2 * sizeof(int))); scaledown = scale / (2 * (1 << PlotPNMdownsample)); for (x = -ds_over_2; x < ds_over_2; x++) { lkstep[ds_over_2 + x] = ((float)ABS(x)) / scaledown * LANCZOS_KERNEL_SIZE; if (lkstep[ds_over_2 + x] >= LANCZOS_KERNEL_SIZE) lkstep[ds_over_2 + x] = LANCZOS_KERNEL_SIZE - 1; } /* Compute the normalization factor (what to divide by after adding up the */ /* weighted values of all pixels in the kernel area). */ normal = 0.0; for (x = 0; x < 2 * ds_over_2; x++) for (y = 0; y < 2 * ds_over_2; y++) normal += lk[lkstep[x]] * lk[lkstep[y]]; iter = 0; while (im_yoffset >= 0) { /* If this is a slow rendering, then we'll announce */ /* the progress every 20 steps. */ if ((++iter) % 10 == 0) { TxPrintf("%g%% done\n", 100 * (float)(im_y - im_yoffset + 1) / (float)im_y); TxFlushOut(); } /* Clear tile memory with the background gray level */ memset((void *)rtile, PlotPNMBG, (size_t)ds_xsize * ds_ysize * PIXELSZ); if (SigInterruptPending) { TxPrintf (" *** interrupted ***\n"); goto done; } /* Use the "UniqueTiles" function to avoid painting contacts twice */ DBTreeSrUniqueTiles(scx, layers, xMask, pnmTile, (ClientData)&scx->scx_area); /* anti-aliased rendering */ #ifdef VERSATEC if (PlotPNMRTL) pnmRenderRegion(scale, scale_over_2, normal, strip, pnmRTLLineFunc, (ClientData)(&rtl_args)); else #endif pnmRenderRegion(scale, scale_over_2, normal, strip, pnmLineFunc, (ClientData)fp); /* advance to the next strip */ im_yoffset -= y_pixels; /* in output coords */ tile_yshift -= tile_ydelta; /* in magic coords */ scx->scx_area.r_ybot -= tile_ydelta; scx->scx_area.r_ytop -= tile_ydelta; } /* TxPrintf ("Save to file `%s', scale = %f\n", fileName, scale);*/ #ifdef VERSATEC if (PlotPNMRTL) { switch (PlotVersPlotType) { case HPRTL: PlotHPRTLTrailer(rtl_args.outfile); break; case HPGL2: PlotHPGL2Trailer(rtl_args.outfile); break; } fflush(rtl_args.outfile); fclose(rtl_args.outfile); rtl_args.outfile = NULL; freeMagic(rtl_args.outbytes); /* Run spooler */ sprintf(command, PlotVersCommand, PlotVersPrinter, fileName); if (system(command) != 0) { TxError("Couldn't execute spooler command to print \"%s\"\n", fileName); } } else #endif { if(fp) { fclose (fp); fp = NULL; } } done: PlotPNMdownsample = save_ds; freeMagic(rtile); rtile = NULL; freeMagic(strip); freeMagic(lkstep); lkstep = NULL; #ifdef VERSATEC if(rtl_args.outfile) /* theoretical fp leak */ { fclose(rtl_args.outfile); rtl_args.outfile = NULL; } #endif if(fp) fclose(fp); 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(i, n) int i, n; { double x; /* position at which to evaluate the lanczos kernel */ if (i == 0) return (float)1.0; else x = (double)i / (double)n; return (float)(sin(PI * x) / (PI * x)) * (sin(PI * 0.5 * x) / (PI * 0.5 * x)); } /* * ---------------------------------------------------------------------------- * Color blending functions. * * PNMColorBlend blends two colors denoted by R, G, B components (0-255). * "c_have" is the color that is already present, and "c_put" is the * color being overlaid. * * PNMColorIndexAndBlend blends an R, G, B component color with a color * indexed into a colormap table. * * Both functions return an R, G, B component color. * ---------------------------------------------------------------------------- */ pnmcolor PNMColorBlend(c_have, c_put) pnmcolor *c_have, *c_put; { pnmcolor loccolor; short r, g, b; /* "127" is half the background color (which should be derived) */ r = (short)c_put->r - 127 + (short)c_have->r / 2; g = (short)c_put->g - 127 + (short)c_have->g / 2; b = (short)c_put->b - 127 + (short)c_have->b / 2; loccolor.r = (r < 0) ? 0 : (unsigned char)r; loccolor.g = (g < 0) ? 0 : (unsigned char)g; loccolor.b = (b < 0) ? 0 : (unsigned char)b; return loccolor; } pnmcolor PNMColorIndexAndBlend(c_have, cidx) pnmcolor *c_have; int cidx; { pnmcolor loccolor, *c_put; int ir, ig, ib; short r, g, b; if ((ncolors > 0) && (cidx < ncolors)) { c_put = &PNMcolors[cidx]; r = (short)c_put->r; g = (short)c_put->g; b = (short)c_put->b; } else { GrGetColor(cidx, &ir, &ig, &ib); r = (short)ir; g = (short)ig; b = (short)ib; } /* "127" is half the background color (which should be derived) */ r += (short)c_have->r / 2 - 127; g += (short)c_have->g / 2 - 127; b += (short)c_have->b / 2 - 127; loccolor.r = (r < 0) ? 0 : (unsigned char)r; loccolor.g = (g < 0) ? 0 : (unsigned char)g; loccolor.b = (b < 0) ? 0 : (unsigned char)b; return loccolor; } /* * ---------------------------------------------------------------------------- * * PlotPNMTechInit -- * * Called when magic starts up. * * * Results: * None. * * Side effects: * Initializes lk[...] array with the lanczos kernel. * * ---------------------------------------------------------------------------- */ void PlotPNMTechInit() { int i; /* Clear out any old information */ if (PaintStyles != NULL) freeMagic(PaintStyles); PaintStyles = (pstyle *)mallocMagic(DBNumUserLayers * sizeof(pstyle)); for (i = 0; i < DBNumUserLayers; i++) { PaintStyles[i].wmask = 0; PaintStyles[i].color.r = 0xff; PaintStyles[i].color.g = 0xff; PaintStyles[i].color.b = 0xff; } Init_Error = 0; /* Initialize Lanczos kernel */ for (i = 0; i <= 2 * LANCZOS_KERNEL_SIZE; i++) lk[i] = lanczos_kernel(i, LANCZOS_KERNEL_SIZE); } /* * ---------------------------------------------------------------------------- * * PlotPNMTechLine -- * * Parse a magic technology file line for the pnm plot style * * Results: * Return TRUE always (no errors flagged). * * 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, k, style; void PlotPNMSetDefaults(); /* Forward declaration */ if (!strncmp(argv[0], "color", 5)) PlotLoadColormap((argc == 1) ? NULL : argv[1]); else if (!strncmp(argv[0], "dstyle", 6)) PlotLoadStyles((argc == 1) ? NULL : argv[1]); else if (!strncmp(argv[0], "default", 7)) PlotPNMSetDefaults(); else if (!strncmp(argv[0], "draw", 4)) { if (argc == 2) { /* Use the default drawing style(s) for this type. */ i = (int)DBTechNameType(argv[1]); if (i >= 0 && i < DBNumUserLayers) { for (j = 0; j < DBWNumStyles; j++) { style = j + TECHBEGINSTYLES; if (TTMaskHasType(DBWStyleToTypes(j), i)) { PaintStyles[i].wmask |= GrStyleTable[style].mask; PaintStyles[i].color = PNMColorIndexAndBlend(&PaintStyles[i].color, GrStyleTable[style].color); } } } } else if (argc == 3) { pstyle savestyle; bool newcolor = FALSE; /* Use the specified drawing style(s) instead of the */ /* display drawing styles (used to override crosses */ /* on contacts and such). */ k = (int)DBTechNameType(argv[1]); if (k >= 0 && k < DBNumUserLayers) { savestyle = PaintStyles[k]; PaintStyles[k].wmask = 0; PaintStyles[k].color.r = 255; PaintStyles[k].color.g = 255; PaintStyles[k].color.b = 255; for (j = 2; j < argc; j++) { /* Use the specified display style, or the internal one */ if (ndstyles > 0) { for (i = 0; i < ndstyles; i++) { if (!strcmp(Dstyles[i].name, argv[j])) { PaintStyles[k].wmask |= Dstyles[i].wmask; PaintStyles[k].color = PNMColorBlend(&PaintStyles[k].color, &Dstyles[i].color); newcolor = TRUE; } } } else { i = (int)GrGetStyleFromName(argv[j]); if (i >= 0) { PaintStyles[k].wmask |= GrStyleTable[i].mask; PaintStyles[k].color = PNMColorIndexAndBlend(&PaintStyles[k].color, GrStyleTable[i].color); newcolor = TRUE; } else TxError("Unknown drawing style \"%s\" for PNM plot.\n", argv[j]); } /* In case of error, revert to the default style */ if (newcolor == FALSE) PaintStyles[k] = savestyle; } } else TxError("Unknown magic layer \"%s\" for PNM plot.\n", argv[1]); } } else if (!strncmp(argv[0], "map", 3)) { k = (int)DBTechNameType(argv[1]); if (k >= 0 && k < DBNumUserLayers) { for (j = 2; j < argc; j++) { i = (int)DBTechNameType(argv[j]); if (i >= 0) { PaintStyles[k].wmask |= PaintStyles[i].wmask; PaintStyles[k].color = PNMColorBlend(&PaintStyles[k].color, &PaintStyles[i].color); } } } } return TRUE; } /* * ---------------------------------------------------------------------------- * * PlotPNMSetDefaults -- * * Generate default colors for the PNM plot style from existing * graphics colors for the window. * * ---------------------------------------------------------------------------- */ void PlotPNMSetDefaults() { int i, j, style; for (i = TT_SPACE + 1; i < DBNumUserLayers; i++) { for (j = 0; j < DBWNumStyles; j++) { style = j + TECHBEGINSTYLES; if (TTMaskHasType(DBWStyleToTypes(j), i)) { PaintStyles[i].wmask |= GrStyleTable[style].mask; PaintStyles[i].color = PNMColorIndexAndBlend(&PaintStyles[i].color, GrStyleTable[style].color); } } } } /* * ---------------------------------------------------------------------------- * * PlotPNMTechFinal -- * * Routine to be run at the end of reading the "plot pnm" techfile * section. * * Results: * None. * * Side effects: * The "Dstyles" array is no longer needed and is free'd. * The "PNMTypeTable" is malloc'd and entries filled. * * ---------------------------------------------------------------------------- */ void PlotPNMTechFinal() { int i; for (i = 0; i < ndstyles; i++) freeMagic(Dstyles[i].name); if (Dstyles != NULL) { freeMagic(Dstyles); Dstyles = NULL; ndstyles = 0; } if (PNMcolors != NULL) { freeMagic(PNMcolors); PNMcolors = NULL; ncolors = 0; } /* If no "draw" or "map" lines were declared in the technology */ /* file, then we put together a default style where we use the */ /* display dstyles for each layer. We detect the condition as */ /* having all wmask values 0 in the PaintStyles array. */ for (i = TT_SPACE + 1; i < DBNumUserLayers; i++) if (PaintStyles[i].wmask != 0) break; if (i < DBNumUserLayers) return; PlotPNMSetDefaults(); } /* * ---------------------------------------------------------------------------- * * PlotLoadStyles -- * * Read in the plotting styles for rendering. * * Results: * None. * * Side effects: * Initializes arrays for drawing/plotting. * * ---------------------------------------------------------------------------- */ void PlotLoadStyles(filename) char *filename; { FILE *inp; char fullName[256]; char *buf; int newsec; int ord, mask, color, outline, nfill, stipple; int ir, ig, ib; char shortname; char longname[128]; char fill[42]; if (filename == NULL) { (void) sprintf(fullName, "%.100s.7bit.mraster_dstyle", DBWStyleType); buf = fullName; } else { buf = filename; } inp = PaOpen(buf, "r", (char *)NULL, ".", SysLibPath, (char **) NULL); if (inp == NULL) { TxError ("PNM plot: Could not open display style file\n"); Init_Error = 1; return; } buf = fullName; /* reuse this space for input */ ndstyles = 0; Dstyles = (dstyle *)mallocMagic(DBWNumStyles * sizeof(dstyle)); /* Read in the dstyle file */ newsec = FALSE; while (fgets (buf, 256, inp)) { if (buf[0] == '#') continue; if (StrIsWhite (buf, FALSE)) { newsec = TRUE; continue; } else if (newsec) { if (strncmp (buf, "display_styles", 14) != 0) goto dstyle_err; newsec = FALSE; } else { if (sscanf (buf, "%d %d %d %d %40s %d %c %126s", &ord, &mask, &color, &outline, fill, &stipple, &shortname, longname) != 8) goto dstyle_err; if (ndstyles == DBWNumStyles) goto dstyle_err; Dstyles[ndstyles].wmask = mask; if ((ncolors > 0) && (color >=0) && (color < ncolors)) { Dstyles[ndstyles].color = PNMcolors[color]; } else { GrGetColor(color, &ir, &ig, &ib); Dstyles[ndstyles].color.r = (unsigned char)ir; Dstyles[ndstyles].color.g = (unsigned char)ig; Dstyles[ndstyles].color.b = (unsigned char)ib; } Dstyles[ndstyles].name = StrDup(NULL, longname); ndstyles++; if (ndstyles == DBWNumStyles) break; } } fclose (inp); return; dstyle_err: Init_Error = 1; TxError ("Format error in display style file\n"); fclose (inp); } /* * ---------------------------------------------------------------------------- * * PlotLoadColormap -- * * Read in the colormap for rendering. * * Results: * None. * * Side effects: * Initializes arrays for drawing/plotting. * * ---------------------------------------------------------------------------- */ void PlotLoadColormap(filename) char *filename; { FILE *inp; char fullName[256]; char *buf; int red, blue, green; /* read in color map */ if (filename == NULL) { (void) sprintf(fullName, "%.100s.7bit.mraster.cmap", DBWStyleType); buf = fullName; } else buf = filename; inp = PaOpen(buf, "r", (char *) NULL, ".", SysLibPath, (char **) NULL); if (inp == NULL) { TxError("Couldn't open colormap file \"%s\"\n", buf); Init_Error = 1; return; } buf = fullName; /* reuse this space for input */ ncolors = 0; PNMcolors = (pnmcolor *)mallocMagic(128 * PIXELSZ); while (fgets (buf, 256, inp)) { if (buf[0] == '#') continue; if (StrIsWhite (buf, FALSE)) continue; if (ncolors == 128) { goto color_err; } if (sscanf (buf, "%d %d %d", &red, &green, &blue) != 3) { goto color_err; } PNMcolors[ncolors].r = (unsigned char)red; PNMcolors[ncolors].g = (unsigned char)green; PNMcolors[ncolors].b = (unsigned char)blue; ncolors++; } fclose(inp); return; color_err: Init_Error = 1; TxError ("Format error in colormap file\n"); fclose (inp); }