#pragma prototyped
#include "render.h"
#include "gd.h"
#include "utils.h"
#include <fcntl.h>
#ifdef MSWIN32
#include <io.h>
#endif
#ifdef _UWIN
#ifndef DEFAULT_FONTPATH
#define DEFAULT_FONTPATH "/win/fonts"
#endif
#else
#ifndef MSWIN32
#ifndef DEFAULT_FONTPATH
#define DEFAULT_FONTPATH "/usr/share/ttf:/usr/local/share/ttf:/usr/share/fonts/ttf:/usr/local/share/fonts/ttf:/usr/lib/fonts:/usr/local/lib/fonts:/usr/lib/fonts/ttf:/usr/local/lib/fonts/ttf:/usr/common/graphviz/lib/fonts/ttf:/windows/fonts:/dos/windows/fonts:/usr/add-on/share/ttf:."
#endif
#else
#ifndef DEFAULT_FONTPATH
#define DEFAULT_FONTPATH "%WINDIR%/FONTS;C:/WINDOWS/FONTS;C:/WINNT/Fonts;C:/winnt/fonts"
#endif
#endif
#endif
#define SCALE ((double)GD_RESOLUTION/(double)POINTS_PER_INCH)
#define BEZIERSUBDIVISION 10
#define FONTSIZE_MUCH_TOO_SMALL 0.15
#define FONTSIZE_TOO_SMALL 1.5
#define REGULAR 0
#define BOLD 1
#define ITALIC 2
#define P_SOLID 0
#define P_DOTTED 4
#define P_DASHED 11
#define P_NONE 15
#define WIDTH_NORMAL 1
#define WIDTH_BOLD 3
static int Rot;
static point Viewport;
static pointf GraphFocus;
static double Zoom;
static gdImagePtr im;
static Dict_t *ImageDict;
typedef struct context_t {
int pencolor, fillcolor;
char *fontfam, fontopt, font_was_set, pen, fill, penwidth;
double fontsz;
} context_t;
#define MAXNEST 4
static context_t cstk[MAXNEST];
static int SP;
static node_t *Curnode;
int black, white, transparent;
static void init_gd()
{
SP = 0;
white = gdImageColorResolve(im, gdRedMax,gdGreenMax,gdBlueMax);
gdImageFilledRectangle(im, 0, 0, im->sx-1, im->sy-1, white);
black = gdImageColorResolve(im, 0, 0, 0);
if (! im->trueColor) {
transparent = gdImageColorResolveAlpha(im, gdRedMax,gdGreenMax,gdBlueMax-1, gdAlphaTransparent);
gdImageColorTransparent(im, transparent);
}
cstk[0].pencolor = black;
cstk[0].fillcolor = black;
cstk[0].fontfam = "times";
cstk[0].fontopt = REGULAR;
cstk[0].pen = P_SOLID;
cstk[0].fill = P_NONE;
cstk[0].penwidth = WIDTH_NORMAL;
}
static pointf gdpt(pointf p)
{
pointf rv;
if (Rot == 0) {
rv.x = (p.x - GraphFocus.x) * Zoom + Viewport.x/2.;
rv.y = (p.y - GraphFocus.y) * -Zoom + Viewport.y/2.;
} else {
rv.x = (p.y - GraphFocus.y) * Zoom + Viewport.x/2.;
rv.y = (p.x - GraphFocus.x) * -Zoom + Viewport.y/2.;
}
return rv;
}
void gd_font(context_t* cp)
{
}
static void gd_begin_job(FILE *ofp, graph_t *g, char **lib, char *user,
char *info[], point pages)
{
#ifdef MYTRACE
fprintf(stderr,"gd_begin_job\n");
#endif
}
static void gd_end_job(void)
{
#ifdef MYTRACE
fprintf(stderr,"gd_end_job\n");
#endif
}
static void gd_begin_graph(graph_t* g, box bb, point pb)
{
Viewport = GD_drawing(g)->viewport.size;
if (Viewport.x) {
GraphFocus = GD_drawing(g)->viewport.focus;
Zoom = GD_drawing(g)->viewport.zoom;
}
else {
Viewport.x = (bb.UR.x - bb.LL.x + 2*GD_drawing(g)->margin.x) * SCALE + 1;
Viewport.y = (bb.UR.y - bb.LL.y + 2*GD_drawing(g)->margin.y) * SCALE + 1;
GraphFocus.x = (GD_bb(g).UR.x - GD_bb(g).LL.x) / 2.;
GraphFocus.y = (GD_bb(g).UR.y - GD_bb(g).LL.y) / 2.;
Zoom = SCALE;
}
}
static void gd_begin_graph_to_file(graph_t* g, box bb, point pb)
{
char *truecolor_p;
gd_begin_graph(g, bb, pb);
if (Verbose)
fprintf(stderr,"%s: allocating a %dK GD image\n",
CmdName, ROUND( Viewport.x * Viewport.y / 1024. ));
truecolor_p = agget(g,"truecolor");
if (truecolor_p == NULL || *truecolor_p == '\0') {
truecolor_p="false";
if (agfindattr(g->proto->n, "shapefile")) {
truecolor_p = "true";
}
}
if (mapbool(truecolor_p))
im = gdImageCreateTrueColor(Viewport.x, Viewport.y);
else
im = gdImageCreate(Viewport.x, Viewport.y);
init_gd();
#ifdef MYTRACE
fprintf(stderr,"gd_begin_graph_to_file\n");
#endif
}
static void gd_begin_graph_to_memory(graph_t* g, box bb, point pb)
{
gd_begin_graph(g, bb, pb);
if (Verbose)
fprintf(stderr,"%s: using existing GD image\n", CmdName);
im = *(gdImagePtr *)Output_file;
init_gd();
#ifdef MYTRACE
fprintf(stderr,"gd_begin_graph_to_memory\n");
#endif
}
static void gd_end_graph_to_file(void)
{
#ifdef HAVE_SETMODE
#ifdef O_BINARY
setmode(fileno(Output_file), O_BINARY);
#endif
#endif
#define JPEG_QUALITY -1
if (Output_lang == GD) {
gdImageGd(im, Output_file);
#ifdef HAVE_LIBZ
} else if (Output_lang == GD2) {
#define GD2_CHUNKSIZE 128
#define GD2_RAW 1
#define GD2_COMPRESSED 2
gdImageGd2(im, Output_file, GD2_CHUNKSIZE, GD2_COMPRESSED);
#endif
#ifdef WITH_GIF
} else if (Output_lang == GIF) {
gdImageGif(im, Output_file);
#endif
#ifdef HAVE_LIBPNG
#ifdef HAVE_LIBZ
} else if (Output_lang == PNG) {
gdImagePng(im, Output_file);
#endif
#endif
#ifdef HAVE_LIBJPEG
} else if (Output_lang == JPEG) {
gdImageJpeg(im, Output_file, JPEG_QUALITY);
#endif
} else if (Output_lang == WBMP) {
gdImageWBMP(im, black, Output_file);
#ifdef HAVE_LIBXPM
} else if (Output_lang == XBM) {
gdImageXbm(im, Output_file);
#endif
}
if (ImageDict) {
dtclose(ImageDict); ImageDict = 0;
}
gdImageDestroy(im);
#ifdef MYTRACE
fprintf(stderr,"gd_end_graph_to_file\n");
#endif
}
static void gd_end_graph_to_memory(void)
{
#ifdef MYTRACE
fprintf(stderr,"gd_end_graph_to_memory\n");
#endif
}
static void
gd_begin_page(graph_t *g, point page, double scale, int rot, point offset)
{
Zoom *= scale;
Rot = rot;
#ifdef MYTRACE
fprintf(stderr,"gd_begin_page\n");
fprintf(stderr," page=%d,%d offset=%d,%d\n",page.x,page.y,offset.x,offset.y);
fprintf(stderr," page_number=%d\n",page_number);
#endif
}
void gd_end_page(void) {
#ifdef MYTRACE
fprintf(stderr,"gd_end_page\n");
#endif
}
static void
gd_begin_node(node_t* n)
{
Curnode = n;
}
static void
gd_end_node()
{
Curnode = NULL;
}
static void
gd_begin_context(void)
{
assert(SP + 1 < MAXNEST);
cstk[SP + 1] = cstk[SP];
SP++;
}
static void
gd_end_context(void)
{
int psp = SP - 1;
assert(SP > 0);
if (cstk[SP].font_was_set)
gd_font(&(cstk[psp]));
SP = psp;
}
static void
gd_set_font(char* name, double size)
{
char *p, *q;
context_t *cp;
cp = &(cstk[SP]);
cp->font_was_set = TRUE;
cp->fontsz = size * Zoom / SCALE;
p = strdup(name);
if ((q = strchr(p, '-'))) {
*q++ = 0;
if (strcasecmp(q, "italic") == 0)
cp->fontopt = ITALIC;
else if (strcasecmp(q, "bold") == 0)
cp->fontopt = BOLD;
}
cp->fontfam = p;
gd_font(&cstk[SP]);
}
static int gd_resolve_color(char* name)
{
color_t color;
if (!(strcmp(name,"transparent"))) {
return gdImageGetTransparent(im);
}
else {
colorxlate(name,&color,RGBA_BYTE);
return gdImageColorResolve(im,color.u.rgba[0],color.u.rgba[1],color.u.rgba[2]);
}
}
static void gd_set_pencolor(char* name)
{
cstk[SP].pencolor = gd_resolve_color(name);
}
static void gd_set_fillcolor(char* name)
{
cstk[SP].fillcolor = gd_resolve_color(name);
}
static void
gd_set_style(char** s)
{
char *line,*p;
context_t *cp;
cp = &(cstk[SP]);
while ((p = line = *s++)) {
if (streq(line, "solid")) cp->pen = P_SOLID;
else if (streq(line, "dashed")) cp->pen = P_DASHED;
else if (streq(line, "dotted")) cp->pen = P_DOTTED;
else if (streq(line, "invis")) cp->pen = P_NONE;
else if (streq(line, "bold")) cp->penwidth = WIDTH_BOLD;
else if (streq(line, "setlinewidth")) {
while (*p) p++;
p++;
cp->penwidth = atol(p);
}
else if (streq(line, "filled")) cp->fill = P_SOLID;
else if (streq(line, "unfilled")) cp->fill = P_NONE;
else agerr (AGWARN, "gd_set_style: unsupported style %s - ignoring\n",
line);
}
}
char *
gd_alternate_fontlist(char *font)
{
char *fontlist;
fontlist = font;
if (strcasecmp(font,"Times-Roman")==0)
fontlist = "Times-Roman;Times_New_Roman;Times-New-Roman;TimesNewRoman;Times;times";
else if (strcasecmp(font,"Times-New-Roman")==0)
fontlist = "Times-New-Roman;Times_New_Roman;TimesNewRoman;Times-Roman;Times;times";
else if (strcasecmp(font,"Times_New_Roman")==0)
fontlist = "Times_New_Roman;Times-New-Roman;TimesNewRoman;Times-Roman;Times;times";
else if (strcasecmp(font,"TimesNewRoman")==0)
fontlist = "TimesNewRoman;Times_New_Roman;Times-New-Roman;Times-Roman;Times;times";
else if (strcasecmp(font,"Times")==0)
fontlist = "Times;times;Times-Roman;Times_New_Roman;Times-New-Roman;TimesNewRoman";
else if (strcasecmp(font,"Helvetica")==0)
fontlist = "Helvetica;arial";
else if (strcasecmp(font,"Arial")==0)
fontlist = "Arial;arial";
else if (strcasecmp(font,"arialb")==0)
fontlist = "arialb;Arial-Bold";
else if (strcasecmp(font, "ariali")==0)
fontlist = "ariali;Arial-Italic";
else if (strcasecmp(font,"Courier")==0)
fontlist = "Courier;cour;Courier-New;Courier_New";
else if (strcasecmp(font,"Courier-New")==0)
fontlist = "Courier-New;Courier_New;Courier cour";
else if (strcasecmp(font,"Courier_New")==0)
fontlist = "Courier_New;Courier-New;Courier;cour";
return fontlist;
}
void gd_missingfont(char *err, char *fontreq)
{
static char *lastmissing = 0;
static int n_errors = 0;
char *p;
if (n_errors >= 20) return;
if ((lastmissing == 0) || (strcmp(lastmissing,fontreq))) {
if (!(p=getenv("GDFONTPATH"))) p = DEFAULT_FONTPATH;
agerr(AGERR, "%s : %s in %s\n",err,fontreq,p);
if (lastmissing) free(lastmissing);
lastmissing = strdup(fontreq);
n_errors++;
if (n_errors >= 20) agerr(AGWARN, "(font errors suppressed)\n");
}
}
extern gdFontPtr gdFontTiny, gdFontSmall, gdFontMediumBold, gdFontLarge, gdFontGiant;
static void
gd_textline(point p, textline_t *line)
{
char *fontlist, *err;
pointf mp,ep;
int brect[8];
char *str = line->str;
double fontsz = cstk[SP].fontsz;
if (cstk[SP].pen == P_NONE) return;
fontlist = gd_alternate_fontlist(cstk[SP].fontfam);
switch(line->just) {
case 'l':
mp.x = p.x;
break;
case 'r':
mp.x = p.x - line->width;
break;
default:
case 'n':
mp.x = p.x - line->width / 2;
break;
}
ep.y = mp.y = p.y;
ep.x = mp.x + line->width;
mp = gdpt(mp);
if (fontsz <= FONTSIZE_MUCH_TOO_SMALL) {
} else if (fontsz <= FONTSIZE_TOO_SMALL) {
ep = gdpt(ep);
gdImageLine(im, ROUND(mp.x), ROUND(mp.y),
ROUND(ep.x), ROUND(ep.y),
cstk[SP].pencolor);
} else {
err = gdImageStringFT(im, brect, cstk[SP].pencolor,
fontlist, fontsz, (Rot? 90.0 : 0.0) * PI / 180.0,
ROUND(mp.x), ROUND(mp.y), str);
if (err) {
gd_missingfont (err, cstk[SP].fontfam);
if (fontsz <= 8.5) {
gdImageString(im, gdFontTiny,
ROUND(mp.x), ROUND(mp.y-9.),
(unsigned char *)str, cstk[SP].pencolor);
} else if (fontsz <= 9.5) {
gdImageString(im, gdFontSmall,
ROUND(mp.x), ROUND(mp.y-12.),
(unsigned char *)str, cstk[SP].pencolor);
} else if (fontsz <= 10.5) {
gdImageString(im, gdFontMediumBold,
ROUND(mp.x), ROUND(mp.y-13.),
(unsigned char *)str, cstk[SP].pencolor);
} else if (fontsz <= 11.5) {
gdImageString(im, gdFontLarge,
ROUND(mp.x), ROUND(mp.y-14.),
(unsigned char *)str, cstk[SP].pencolor);
} else {
gdImageString(im, gdFontGiant,
ROUND(mp.x), ROUND(mp.y-15.),
(unsigned char *)str, cstk[SP].pencolor);
}
}
}
}
point gd_textsize(char *str, char *fontname, double fontsz)
{
char *fontlist,*err;
point rv;
int brect[8];
fontlist = gd_alternate_fontlist(fontname);
rv.x = rv.y = 0.0;
if (fontlist && *str) {
if (fontsz <= FONTSIZE_MUCH_TOO_SMALL) {
rv.x = rv.y = 0;
return rv;
} else if (fontsz <= FONTSIZE_TOO_SMALL) {
fontsz = FONTSIZE_TOO_SMALL;
}
err = gdImageStringFT(NULL, brect, -1, fontlist,
fontsz, 0, 0, 0, str);
if (!err) {
rv.x = (brect[4] - brect[0]);
rv.y = (brect[5] - 0 );
rv.x /= SCALE; rv.y /= SCALE;
}
}
return rv;
}
static void
gd_bezier(point* A, int n, int arrow_at_start, int arrow_at_end)
{
pointf p0, p1, V[4];
int i, j, step;
int style[20];
int pen, width;
gdImagePtr brush = NULL;
if (cstk[SP].pen != P_NONE) {
if (cstk[SP].pen == P_DASHED) {
for (i = 0; i < 10; i++)
style[i] = cstk[SP].pencolor;
for (; i < 20; i++)
style[i] = gdTransparent;
gdImageSetStyle(im, style, 20);
pen = gdStyled;
} else if (cstk[SP].pen == P_DOTTED) {
for (i = 0; i < 2; i++)
style[i] = cstk[SP].pencolor;
for (; i < 12; i++)
style[i] = gdTransparent;
gdImageSetStyle(im, style, 12);
pen = gdStyled;
} else {
pen = cstk[SP].pencolor;
}
#if 0
if (cstk[SP].penwidth != WIDTH_NORMAL) {
width=cstk[SP].penwidth;
brush = gdImageCreate(width,width);
gdImagePaletteCopy(brush, im);
gdImageFilledRectangle(brush,
0,0,width-1, width-1, cstk[SP].pencolor);
gdImageSetBrush(im, brush);
if (pen == gdStyled) pen = gdStyledBrushed;
else pen = gdBrushed;
}
#else
width = cstk[SP].penwidth;
gdImageSetThickness(im, width);
#endif
V[3].x = A[0].x; V[3].y = A[0].y;
for (i = 0; i+3 < n; i += 3) {
V[0] = V[3];
for (j = 1; j <= 3; j++) {
V[j].x = A[i+j].x; V[j].y = A[i+j].y;
}
p0 = gdpt(V[0]);
for (step = 1; step <= BEZIERSUBDIVISION; step++) {
p1 = gdpt(Bezier(V, 3, (double)step/BEZIERSUBDIVISION, NULL, NULL));
gdImageLine(im, ROUND(p0.x), ROUND(p0.y),
ROUND(p1.x), ROUND(p1.y), pen);
p0 = p1;
}
}
if (brush)
gdImageDestroy(brush);
}
}
static void
gd_polygon(point *A, int n, int filled)
{
pointf p;
int i;
gdPoint *points;
int style[20];
int pen, width;
gdImagePtr brush = NULL;
if (cstk[SP].pen != P_NONE) {
if (cstk[SP].pen == P_DASHED) {
for (i = 0; i < 10; i++)
style[i] = cstk[SP].pencolor;
for (; i < 20; i++)
style[i] = gdTransparent;
gdImageSetStyle(im, style, 20);
pen = gdStyled;
} else if (cstk[SP].pen == P_DOTTED) {
for (i = 0; i < 2; i++)
style[i] = cstk[SP].pencolor;
for (; i < 12; i++)
style[i] = gdTransparent;
gdImageSetStyle(im, style, 12);
pen = gdStyled;
} else {
pen = cstk[SP].pencolor;
}
#if 1
gdImageSetThickness(im, WIDTH_NORMAL);
if (cstk[SP].penwidth != WIDTH_NORMAL) {
width=cstk[SP].penwidth * Zoom;
brush = gdImageCreate(width,width);
gdImagePaletteCopy(brush, im);
gdImageFilledRectangle(brush,
0,0,width-1, width-1, cstk[SP].pencolor);
gdImageSetBrush(im, brush);
if (pen == gdStyled) pen = gdStyledBrushed;
else pen = gdBrushed;
}
#else
width = cstk[SP].penwidth;
gdImageSetThickness(im, width);
#endif
points = N_GNEW(n,gdPoint);
for (i = 0; i < n; i++) {
p.x = A[i].x; p.y = A[i].y;
p = gdpt(p);
points[i].x = ROUND(p.x); points[i].y = ROUND(p.y);
}
if (filled) gdImageFilledPolygon(im, points, n, cstk[SP].fillcolor);
gdImagePolygon(im, points, n, pen);
free(points);
if (brush)
gdImageDestroy(brush);
}
}
static void
gd_ellipse(point p, int rx, int ry, int filled)
{
pointf mp;
int i;
int style[40];
int pen, width;
gdImagePtr brush = NULL;
if (cstk[SP].pen != P_NONE) {
if (cstk[SP].pen == P_DASHED) {
for (i = 0; i < 20; i++)
style[i] = cstk[SP].pencolor;
for (; i < 40; i++)
style[i] = gdTransparent;
gdImageSetStyle(im, style, 40);
pen = gdStyled;
} else if (cstk[SP].pen == P_DOTTED) {
for (i = 0; i < 2; i++)
style[i] = cstk[SP].pencolor;
for (; i < 24; i++)
style[i] = gdTransparent;
gdImageSetStyle(im, style, 24);
pen = gdStyled;
} else {
pen = cstk[SP].pencolor;
}
#if 1
gdImageSetThickness(im, WIDTH_NORMAL);
if (cstk[SP].penwidth != WIDTH_NORMAL) {
width = cstk[SP].penwidth;
brush = gdImageCreate(width,width);
gdImagePaletteCopy(brush, im);
gdImageFilledRectangle(brush,
0,0,width-1, width-1, cstk[SP].pencolor);
gdImageSetBrush(im, brush);
if (pen == gdStyled) pen = gdStyledBrushed;
else pen = gdBrushed;
}
#else
width = cstk[SP].penwidth;
gdImageSetThickness(im, width);
#endif
if (Rot) {int t; t = rx; rx = ry; ry = t;}
mp.x = p.x; mp.y = p.y;
mp = gdpt(mp);
if (filled) {
gdImageFilledEllipse(im, ROUND(mp.x), ROUND(mp.y),
ROUND(Zoom*(rx + rx)), ROUND(Zoom*(ry + ry)),
cstk[SP].fillcolor);
}
gdImageArc(im, ROUND(mp.x), ROUND(mp.y),
ROUND(Zoom*(rx + rx)), ROUND(Zoom*(ry + ry)), 0, 360, pen);
if (brush)
gdImageDestroy(brush);
}
}
static void
gd_polyline(point* A, int n)
{
pointf p, p1;
int i;
int style[20];
int pen, width;
gdImagePtr brush = NULL;
if (cstk[SP].pen != P_NONE) {
if (cstk[SP].pen == P_DASHED) {
for (i = 0; i < 10; i++)
style[i] = cstk[SP].pencolor;
for (; i < 20; i++)
style[i] = gdTransparent;
gdImageSetStyle(im, style, 20);
pen = gdStyled;
} else if (cstk[SP].pen == P_DOTTED) {
for (i = 0; i < 2; i++)
style[i] = cstk[SP].pencolor;
for (; i < 12; i++)
style[i] = gdTransparent;
gdImageSetStyle(im, style, 12);
pen = gdStyled;
} else {
pen = cstk[SP].pencolor;
}
#if 0
if (cstk[SP].penwidth != WIDTH_NORMAL) {
width = cstk[SP].penwidth;
brush = gdImageCreate(width,width);
gdImagePaletteCopy(brush, im);
gdImageFilledRectangle(brush,
0,0,width-1,width-1,cstk[SP].pencolor);
gdImageSetBrush(im, brush);
if (pen == gdStyled) pen = gdStyledBrushed;
else pen = gdBrushed;
}
#else
width = cstk[SP].penwidth;
gdImageSetThickness(im, width);
#endif
p.x = A[0].x;
p.y = A[0].y;
p = gdpt(p);
for (i = 1; i < n; i++) {
p1.x = A[i].x;
p1.y = A[i].y;
p1 = gdpt(p1);
gdImageLine(im, ROUND(p.x), ROUND(p.y),
ROUND(p1.x), ROUND(p1.y), pen);
p.x = p1.x;
p.y = p1.y;
}
if (brush)
gdImageDestroy(brush);
}
}
static gdImagePtr loadshapeimage(char *name)
{
gdImagePtr rv = 0;
char *shapeimagefile,*suffix;
FILE *in = NULL;
if ((shapeimagefile=safefile(name))) {
#ifndef MSWIN32
in = fopen (shapeimagefile, "r");
#else
in = fopen (shapeimagefile, "rb");
#endif
}
if (!in)
agerr(AGERR, "couldn't open image file %s\n",shapeimagefile);
else {
suffix = strrchr(shapeimagefile,'.');
if (!suffix) suffix = shapeimagefile; else suffix++;
if (!strcasecmp(suffix,"wbmp")) rv = gdImageCreateFromWBMP(in);
#ifdef WITH_GIF
else if (!strcasecmp(suffix,"gif")) rv = gdImageCreateFromGif(in);
#endif
#ifdef HAVE_LIBPNG
else if (!strcasecmp(suffix,"png")) rv = gdImageCreateFromPng(in);
#endif
#ifdef HAVE_LIBJPEG
else if (!strcasecmp(suffix,"jpeg")||!strcasecmp(suffix,"jpg")) rv = gdImageCreateFromJpeg(in);
#endif
else if (!strcasecmp(suffix,"xbm")) rv = gdImageCreateFromXbm(in);
else agerr(AGERR, "image file %s suffix not recognized\n",name);
fclose(in);
if (!rv) agerr(AGERR, "image file %s contents were not recognized\n",name);
}
return rv;
}
typedef struct imagerec_s {
Dtlink_t link;
char *name;
gdImagePtr im;
} imagerec_t;
static void imagerec_free(Dict_t *dict, Void_t *p, Dtdisc_t *disc)
{
gdImagePtr im = ((imagerec_t*)p)->im;
if (im) gdImageDestroy(im);
}
static Dtdisc_t ImageDictDisc = {
offsetof(imagerec_t,name),
-1,
0,
NIL(Dtmake_f),
imagerec_free,
NIL(Dtcompar_f),
NIL(Dthash_f),
NIL(Dtmemory_f),
NIL(Dtevent_f)
};
static gdImagePtr getshapeimage(char *name)
{
imagerec_t probe, *val;
if (!name) return 0;
if (!ImageDict) ImageDict = dtopen(&ImageDictDisc,Dttree);
probe.name = name;
val = dtsearch(ImageDict,&probe);
if (!val) {
val = GNEW(imagerec_t);
val->name = name;
val->im = loadshapeimage(name);
dtinsert(ImageDict,val);
}
return val->im;
}
static void
gd_user_shape(char *name, point *A, int n, int filled)
{
gdImagePtr im2 = 0;
pointf destul, destlr;
pointf ul, lr;
double sx, sy;
double scalex, scaley;
int i;
char *shapeimagefile;
shapeimagefile = agget(Curnode,"shapefile");
im2 = getshapeimage(shapeimagefile);
if (im2) {
ul.x = lr.x = A[0].x; ul.y = lr.y = A[0].y;
for (i = 1; i < n; i++) {
if (ul.x > A[i].x) ul.x = A[i].x;
if (ul.y < A[i].y) ul.y = A[i].y;
if (lr.y > A[i].y) lr.y = A[i].y;
if (lr.x < A[i].x) lr.x = A[i].x;
}
destul = gdpt(ul);
destlr = gdpt(lr);
scalex = (destlr.x - destul.x)/(double)(im2->sx);
scaley = (destlr.y - destul.y)/(double)(im2->sy);
if (scalex < scaley) {
sx = ROUND(im2->sx * scalex);
sy = ROUND(im2->sy * scalex);
}
else {
sx = ROUND(im2->sx * scaley);
sy = ROUND(im2->sy * scaley);
}
gdImageCopyResized(im,im2,ROUND(destul.x),ROUND(destul.y),0,0,sx,sy,im2->sx,im2->sy);
}
}
point gd_user_shape_size(node_t *n, char *shapeimagefile)
{
point rv;
gdImagePtr im;
Curnode = n;
im = getshapeimage(shapeimagefile);
if (im) {rv.x = im->sx / SCALE; rv.y = im->sy / SCALE; }
else rv.x = rv.y = 0;
return rv;
}
codegen_t GD_CodeGen = {
0,
gd_begin_job, gd_end_job,
gd_begin_graph_to_file, gd_end_graph_to_file,
gd_begin_page, gd_end_page,
0, 0,
0, 0,
0, 0,
0, 0,
gd_begin_node, gd_end_node,
0, 0,
gd_begin_context, gd_end_context,
gd_set_font, gd_textline,
gd_set_pencolor, gd_set_fillcolor, gd_set_style,
gd_ellipse, gd_polygon,
gd_bezier, gd_polyline,
0,
0,
gd_textsize,
gd_user_shape,
gd_user_shape_size
};
codegen_t memGD_CodeGen = {
0,
gd_begin_job, gd_end_job,
gd_begin_graph_to_memory, gd_end_graph_to_memory,
gd_begin_page, gd_end_page,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
gd_begin_context, gd_end_context,
gd_set_font, gd_textline,
gd_set_pencolor, gd_set_fillcolor, gd_set_style,
gd_ellipse, gd_polygon,
gd_bezier, gd_polyline,
0,
0,
gd_textsize,
gd_user_shape,
gd_user_shape_size
};