Files
magic/plot/plotPNM.c
T
R. Timothy Edwards 516c9d7635 First cut of pulling the TT_SIDE bit out of the tile database
and forcing it to be passed as an argument to all the callback
functions for the search routines that require it.  Magic now
compiles and runs with the new code, but there are a number of
known issues that need to be fixed up.  Committing now so that
I can rebase on the last update to the master branch.
2026-01-09 12:05:03 -05:00

1508 lines
37 KiB
C

/*
* 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 <stdio.h>
#include <string.h>
#include <math.h>
/* C99 compat */
#include <stdlib.h>
#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, dinfo, cxp)
Tile *tile;
TileType dinfo;
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, dinfo, cxp)
Tile *tile;
TileType dinfo;
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 newdinfo;
int w, h, llx, lly, urx, ury;
Rect scaledClip;
type = (dinfo & TT_SIDE) ? 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. . . */
newdinfo = DBTransformDiagonal(TiGetTypeExact(tile) | dinfo, &scx->scx_trans);
if (((newdinfo & TT_SIDE) >> 1) != (newdinfo & 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 (newdinfo & 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 (newdinfo & 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);
}