Graphviz  2.31.20130618.0446
lib/gvc/gvrender.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 /*
00015  *  graphics code generator wrapper
00016  *
00017  *  This library forms the socket for run-time loadable render plugins.
00018  */
00019 
00020 #ifdef HAVE_CONFIG_H
00021 #include "config.h"
00022 #endif
00023 
00024 #include <string.h>
00025 #include "memory.h"
00026 #include "const.h"
00027 #include "macros.h"
00028 #include "colorprocs.h"
00029 #include "gvplugin_render.h"
00030 #if WITH_CGRAPH
00031 #include "cgraph.h"
00032 #else
00033 #include "graph.h"
00034 #endif
00035 #include "gvcint.h"
00036 #include "geom.h"
00037 #include "geomprocs.h"
00038 #include "gvcproc.h"
00039 
00040 extern int emit_once(char *str);
00041 extern shape_desc *find_user_shape(char *name);
00042 extern boolean mapbool(char *s);
00043 
00044 #ifndef HAVE_STRCASECMP
00045 extern int strcasecmp(const char *s1, const char *s2);
00046 #endif
00047 
00048 /* storage for temporary hacks until client API is FP */
00049 static pointf *AF;
00050 static int sizeAF;
00051 /* end hack */
00052 
00053 int gvrender_select(GVJ_t * job, const char *str)
00054 {
00055     GVC_t *gvc = job->gvc;
00056     gvplugin_available_t *plugin;
00057     gvplugin_installed_t *typeptr;
00058 
00059     gvplugin_load(gvc, API_device, str);
00060 
00061     /* When job is created, it is zeroed out.
00062      * Some flags, such as OUTPUT_NOT_REQUIRED, may already be set,
00063      * so don't reset.
00064      */
00065     /* job->flags = 0; */
00066     plugin = gvc->api[API_device];
00067     if (plugin) {
00068         typeptr = plugin->typeptr;
00069         job->device.engine = (gvdevice_engine_t *) (typeptr->engine);
00070         job->device.features = (gvdevice_features_t *) (typeptr->features);
00071         job->device.id = typeptr->id;
00072         job->device.type = plugin->typestr;
00073 
00074         job->flags |= job->device.features->flags;
00075     } else
00076         return NO_SUPPORT;      /* FIXME - should differentiate problem */
00077 
00078     /* The device plugin has a dependency on a render plugin,
00079      * so the render plugin should be available as well now */
00080     plugin = gvc->api[API_render];
00081     if (plugin) {
00082         typeptr = plugin->typeptr;
00083         job->render.engine = (gvrender_engine_t *) (typeptr->engine);
00084         job->render.features = (gvrender_features_t *) (typeptr->features);
00085         job->render.type = plugin->typestr;
00086 
00087         job->flags |= job->render.features->flags;
00088 
00089         if (job->device.engine)
00090             job->render.id = typeptr->id;
00091         else
00092             /* A null device engine indicates that the device id is also the renderer id
00093              * and that the renderer doesn't need "device" functions.
00094              * Device "features" settings are still available */
00095             job->render.id = job->device.id;
00096         return GVRENDER_PLUGIN;
00097     }
00098     job->render.engine = NULL;
00099     return NO_SUPPORT;          /* FIXME - should differentiate problem */
00100 }
00101 
00102 int gvrender_features(GVJ_t * job)
00103 {
00104     gvrender_engine_t *gvre = job->render.engine;
00105     int features = 0;
00106 
00107     if (gvre) {
00108         features = job->render.features->flags;
00109     }
00110     return features;
00111 }
00112 
00113 /* gvrender_begin_job:
00114  * Return 0 on success
00115  */
00116 int gvrender_begin_job(GVJ_t * job)
00117 {
00118     gvrender_engine_t *gvre = job->render.engine;
00119 
00120     if (gvdevice_initialize(job))
00121         return 1;
00122     if (gvre) {
00123         if (gvre->begin_job)
00124             gvre->begin_job(job);
00125     }
00126     return 0;
00127 }
00128 
00129 void gvrender_end_job(GVJ_t * job)
00130 {
00131     gvrender_engine_t *gvre = job->render.engine;
00132 
00133     if (gvre) {
00134         if (gvre->end_job)
00135             gvre->end_job(job);
00136     }
00137     job->gvc->common.lib = NULL;        /* FIXME - minimally this doesn't belong here */
00138     gvdevice_finalize(job);
00139 }
00140 
00141 /* font modifiers */
00142 #define REGULAR 0
00143 #define BOLD    1
00144 #define ITALIC  2
00145 
00146 pointf gvrender_ptf(GVJ_t * job, pointf p)
00147 {
00148     pointf rv, translation, scale;
00149 
00150     translation = job->translation;
00151     scale.x = job->zoom * job->devscale.x;
00152     scale.y = job->zoom * job->devscale.y;
00153 
00154     if (job->rotation) {
00155         rv.x = -(p.y + translation.y) * scale.x;
00156         rv.y = (p.x + translation.x) * scale.y;
00157     } else {
00158         rv.x = (p.x + translation.x) * scale.x;
00159         rv.y = (p.y + translation.y) * scale.y;
00160     }
00161     return rv;
00162 }
00163 
00164 /* transform an array of n points */
00165 /*  *AF and *af must be preallocated */
00166 /*  *AF can be the same as *af for inplace transforms */
00167 pointf *gvrender_ptf_A(GVJ_t * job, pointf * af, pointf * AF, int n)
00168 {
00169     int i;
00170     double t;
00171     pointf translation, scale;
00172 
00173     translation = job->translation;
00174     scale.x = job->zoom * job->devscale.x;
00175     scale.y = job->zoom * job->devscale.y;
00176 
00177     if (job->rotation) {
00178         for (i = 0; i < n; i++) {
00179             t = -(af[i].y + translation.y) * scale.x;
00180             AF[i].y = (af[i].x + translation.x) * scale.y;
00181             AF[i].x = t;
00182         }
00183     } else {
00184         for (i = 0; i < n; i++) {
00185             AF[i].x = (af[i].x + translation.x) * scale.x;
00186             AF[i].y = (af[i].y + translation.y) * scale.y;
00187         }
00188     }
00189     return AF;
00190 }
00191 
00192 static int gvrender_comparestr(const void *s1, const void *s2)
00193 {
00194     return strcmp(*(char **) s1, *(char **) s2);
00195 }
00196 
00197 static void gvrender_resolve_color(gvrender_features_t * features,
00198                                    char *name, gvcolor_t * color)
00199 {
00200     char *tok;
00201     int rc;
00202 
00203     color->u.string = name;
00204     color->type = COLOR_STRING;
00205     tok = canontoken(name);
00206     if (!features->knowncolors
00207         ||
00208         (bsearch
00209          (&tok, features->knowncolors, features->sz_knowncolors,
00210           sizeof(char *), gvrender_comparestr)) == NULL) {
00211         /* if tok was not found in known_colors */
00212         rc = colorxlate(name, color, features->color_type);
00213         if (rc != COLOR_OK) {
00214             if (rc == COLOR_UNKNOWN) {
00215                 char *missedcolor = gmalloc(strlen(name) + 16);
00216                 sprintf(missedcolor, "color %s", name);
00217                 if (emit_once(missedcolor))
00218                     agerr(AGWARN, "%s is not a known color.\n", name);
00219                 free(missedcolor);
00220             } else {
00221                 agerr(AGERR, "error in colxlate()\n");
00222             }
00223         }
00224     }
00225 }
00226 
00227 void gvrender_begin_graph(GVJ_t * job, graph_t * g)
00228 {
00229     /* GVC_t *gvc = job->gvc; */
00230     gvrender_engine_t *gvre = job->render.engine;
00231     /* char *s; */
00232 
00233     if (gvre) {
00234         /* render specific init */
00235         if (gvre->begin_graph)
00236             gvre->begin_graph(job);
00237 
00238 #if 0
00239         /* background color */
00240         if (((s = agget(g, "bgcolor")) != 0) && s[0]) {
00241             gvrender_resolve_color(job->render.features, s,
00242                                    &(gvc->bgcolor));
00243             if (gvre->resolve_color)
00244                 gvre->resolve_color(job, &(gvc->bgcolor));
00245         }
00246 #endif
00247 
00248     }
00249 }
00250 
00251 void gvrender_end_graph(GVJ_t * job)
00252 {
00253     gvrender_engine_t *gvre = job->render.engine;
00254 
00255     if (gvre) {
00256         if (gvre->end_graph)
00257             gvre->end_graph(job);
00258     }
00259     gvdevice_format(job);
00260 }
00261 
00262 void gvrender_begin_page(GVJ_t * job)
00263 {
00264     gvrender_engine_t *gvre = job->render.engine;
00265 
00266     if (gvre) {
00267         if (gvre->begin_page)
00268             gvre->begin_page(job);
00269     }
00270 }
00271 
00272 void gvrender_end_page(GVJ_t * job)
00273 {
00274     gvrender_engine_t *gvre = job->render.engine;
00275 
00276     if (gvre) {
00277         if (gvre->end_page)
00278             gvre->end_page(job);
00279     }
00280 }
00281 
00282 void gvrender_begin_layer(GVJ_t * job)
00283 {
00284     gvrender_engine_t *gvre = job->render.engine;
00285 
00286     if (gvre) {
00287         if (gvre->begin_layer)
00288             gvre->begin_layer(job, job->gvc->layerIDs[job->layerNum],
00289                               job->layerNum, job->numLayers);
00290     }
00291 }
00292 
00293 void gvrender_end_layer(GVJ_t * job)
00294 {
00295     gvrender_engine_t *gvre = job->render.engine;
00296 
00297     if (gvre) {
00298         if (gvre->end_layer)
00299             gvre->end_layer(job);
00300     }
00301 }
00302 
00303 void gvrender_begin_cluster(GVJ_t * job, graph_t * sg)
00304 {
00305     gvrender_engine_t *gvre = job->render.engine;
00306 
00307     if (gvre) {
00308         if (gvre->begin_cluster)
00309             gvre->begin_cluster(job);
00310     }
00311 }
00312 
00313 void gvrender_end_cluster(GVJ_t * job, graph_t * g)
00314 {
00315     gvrender_engine_t *gvre = job->render.engine;
00316 
00317     if (gvre) {
00318         if (gvre->end_cluster)
00319             gvre->end_cluster(job);
00320     }
00321 }
00322 
00323 void gvrender_begin_nodes(GVJ_t * job)
00324 {
00325     gvrender_engine_t *gvre = job->render.engine;
00326 
00327     if (gvre) {
00328         if (gvre->begin_nodes)
00329             gvre->begin_nodes(job);
00330     }
00331 }
00332 
00333 void gvrender_end_nodes(GVJ_t * job)
00334 {
00335     gvrender_engine_t *gvre = job->render.engine;
00336 
00337     if (gvre) {
00338         if (gvre->end_nodes)
00339             gvre->end_nodes(job);
00340     }
00341 }
00342 
00343 void gvrender_begin_edges(GVJ_t * job)
00344 {
00345     gvrender_engine_t *gvre = job->render.engine;
00346 
00347     if (gvre) {
00348         if (gvre->begin_edges)
00349             gvre->begin_edges(job);
00350     }
00351 }
00352 
00353 void gvrender_end_edges(GVJ_t * job)
00354 {
00355     gvrender_engine_t *gvre = job->render.engine;
00356 
00357     if (gvre) {
00358         if (gvre->end_edges)
00359             gvre->end_edges(job);
00360     }
00361 }
00362 
00363 void gvrender_begin_node(GVJ_t * job, node_t * n)
00364 {
00365     gvrender_engine_t *gvre = job->render.engine;
00366 
00367     if (gvre) {
00368         if (gvre->begin_node)
00369             gvre->begin_node(job);
00370     }
00371 }
00372 
00373 void gvrender_end_node(GVJ_t * job)
00374 {
00375     gvrender_engine_t *gvre = job->render.engine;
00376 
00377     if (gvre) {
00378         if (gvre->end_node)
00379             gvre->end_node(job);
00380     }
00381 }
00382 
00383 void gvrender_begin_edge(GVJ_t * job, edge_t * e)
00384 {
00385     gvrender_engine_t *gvre = job->render.engine;
00386 
00387     if (gvre) {
00388         if (gvre->begin_edge)
00389             gvre->begin_edge(job);
00390     }
00391 }
00392 
00393 void gvrender_end_edge(GVJ_t * job)
00394 {
00395     gvrender_engine_t *gvre = job->render.engine;
00396 
00397     if (gvre) {
00398         if (gvre->end_edge)
00399             gvre->end_edge(job);
00400     }
00401 }
00402 
00403 void gvrender_begin_anchor(GVJ_t * job, char *href, char *tooltip,
00404                            char *target, char *id)
00405 {
00406     gvrender_engine_t *gvre = job->render.engine;
00407 
00408     if (gvre) {
00409         if (gvre->begin_anchor)
00410             gvre->begin_anchor(job, href, tooltip, target, id);
00411     }
00412 }
00413 
00414 void gvrender_end_anchor(GVJ_t * job)
00415 {
00416     gvrender_engine_t *gvre = job->render.engine;
00417 
00418     if (gvre) {
00419         if (gvre->end_anchor)
00420             gvre->end_anchor(job);
00421     }
00422 }
00423 
00424 void gvrender_begin_label(GVJ_t * job, label_type type)
00425 {
00426     gvrender_engine_t *gvre = job->render.engine;
00427 
00428     if (gvre) {
00429         if (gvre->begin_label)
00430             gvre->begin_label(job, type);
00431     }
00432 }
00433 
00434 void gvrender_end_label(GVJ_t * job)
00435 {
00436     gvrender_engine_t *gvre = job->render.engine;
00437 
00438     if (gvre) {
00439         if (gvre->end_label)
00440             gvre->end_label(job);
00441     }
00442 }
00443 
00444 void gvrender_textpara(GVJ_t * job, pointf p, textpara_t * para)
00445 {
00446     gvrender_engine_t *gvre = job->render.engine;
00447     pointf PF;
00448 
00449     if (para->str && para->str[0]
00450         && (!job->obj           /* because of xdgen non-conformity */
00451             || job->obj->pen != PEN_NONE)) {
00452         if (job->flags & GVRENDER_DOES_TRANSFORM)
00453             PF = p;
00454         else
00455             PF = gvrender_ptf(job, p);
00456         if (gvre) {
00457             if (gvre->textpara)
00458                 gvre->textpara(job, PF, para);
00459         }
00460     }
00461 }
00462 
00463 void gvrender_set_pencolor(GVJ_t * job, char *name)
00464 {
00465     gvrender_engine_t *gvre = job->render.engine;
00466     gvcolor_t *color = &(job->obj->pencolor);
00467     char *cp = NULL;
00468 
00469     if ((cp = strstr(name, ":")))       /* if its a color list, then use only first */
00470         *cp = '\0';
00471     if (gvre) {
00472         gvrender_resolve_color(job->render.features, name, color);
00473         if (gvre->resolve_color)
00474             gvre->resolve_color(job, color);
00475     }
00476     if (cp)                     /* restore color list */
00477         *cp = ':';
00478 }
00479 
00480 void gvrender_set_fillcolor(GVJ_t * job, char *name)
00481 {
00482     gvrender_engine_t *gvre = job->render.engine;
00483     gvcolor_t *color = &(job->obj->fillcolor);
00484     char *cp = NULL;
00485 
00486     if ((cp = strstr(name, ":")))       /* if its a color list, then use only first */
00487         *cp = '\0';
00488     if (gvre) {
00489         gvrender_resolve_color(job->render.features, name, color);
00490         if (gvre->resolve_color)
00491             gvre->resolve_color(job, color);
00492     }
00493     if (cp)
00494         *cp = ':';
00495 }
00496 
00497 void gvrender_set_gradient_vals (GVJ_t * job, char *stopcolor, int angle)
00498 {
00499     gvrender_engine_t *gvre = job->render.engine;
00500     gvcolor_t *color = &(job->obj->stopcolor);
00501 
00502     if (gvre) {
00503         gvrender_resolve_color(job->render.features, stopcolor, color);
00504         if (gvre->resolve_color)
00505             gvre->resolve_color(job, color);
00506     }
00507     job->obj->gradient_angle = angle;
00508 }
00509 
00510 void gvrender_set_style(GVJ_t * job, char **s)
00511 {
00512     gvrender_engine_t *gvre = job->render.engine;
00513     obj_state_t *obj = job->obj;
00514     char *line, *p;
00515 
00516     obj->rawstyle = s;
00517     if (gvre) {
00518         if (s)
00519             while ((p = line = *s++)) {
00520                 if (streq(line, "solid"))
00521                     obj->pen = PEN_SOLID;
00522                 else if (streq(line, "dashed"))
00523                     obj->pen = PEN_DASHED;
00524                 else if (streq(line, "dotted"))
00525                     obj->pen = PEN_DOTTED;
00526                 else if (streq(line, "invis") || streq(line, "invisible"))
00527                     obj->pen = PEN_NONE;
00528                 else if (streq(line, "bold"))
00529                     obj->penwidth = PENWIDTH_BOLD;
00530                 else if (streq(line, "setlinewidth")) {
00531                     while (*p)
00532                         p++;
00533                     p++;
00534                     obj->penwidth = atof(p);
00535                 } else if (streq(line, "filled"))
00536                     obj->fill = FILL_SOLID;
00537                 else if (streq(line, "unfilled"))
00538                     obj->fill = FILL_NONE;
00539                 else if (streq(line, "tapered"));
00540                 else {
00541                     agerr(AGWARN,
00542                           "gvrender_set_style: unsupported style %s - ignoring\n",
00543                           line);
00544                 }
00545             }
00546     }
00547 }
00548 
00549 void gvrender_ellipse(GVJ_t * job, pointf * pf, int n, int filled)
00550 {
00551     gvrender_engine_t *gvre = job->render.engine;
00552 
00553     if (gvre) {
00554         if (gvre->ellipse && job->obj->pen != PEN_NONE) {
00555             pointf af[2];
00556 
00557             /* center */
00558             af[0].x = (pf[0].x + pf[1].x) / 2.;
00559             af[0].y = (pf[0].y + pf[1].y) / 2.;
00560             /* corner */
00561             af[1] = pf[1];
00562 
00563             if (!(job->flags & GVRENDER_DOES_TRANSFORM))
00564                 gvrender_ptf_A(job, af, af, 2);
00565             gvre->ellipse(job, af, filled);
00566         }
00567     }
00568 }
00569 
00570 void gvrender_polygon(GVJ_t * job, pointf * af, int n, int filled)
00571 {
00572     int noPoly = 0;
00573     gvcolor_t save_pencolor;
00574 
00575     gvrender_engine_t *gvre = job->render.engine;
00576     if (gvre) {
00577         if (gvre->polygon && job->obj->pen != PEN_NONE) {
00578             if (filled & NO_POLY) {
00579                 noPoly = 1;
00580                 filled &= ~NO_POLY;
00581                 save_pencolor = job->obj->pencolor;
00582                 job->obj->pencolor = job->obj->fillcolor;
00583             }
00584             if (job->flags & GVRENDER_DOES_TRANSFORM)
00585                 gvre->polygon(job, af, n, filled);
00586             else {
00587                 if (sizeAF < n) {
00588                     sizeAF = n + 10;
00589                     AF = grealloc(AF, sizeAF * sizeof(pointf));
00590                 }
00591                 gvrender_ptf_A(job, af, AF, n);
00592                 gvre->polygon(job, AF, n, filled);
00593             }
00594             if (noPoly)
00595                 job->obj->pencolor = save_pencolor;
00596         }
00597     }
00598 }
00599 
00600 
00601 void gvrender_box(GVJ_t * job, boxf B, int filled)
00602 {
00603     pointf A[4];
00604 
00605     A[0] = B.LL;
00606     A[2] = B.UR;
00607     A[1].x = A[0].x;
00608     A[1].y = A[2].y;
00609     A[3].x = A[2].x;
00610     A[3].y = A[0].y;
00611 
00612     gvrender_polygon(job, A, 4, filled);
00613 }
00614 
00615 void gvrender_beziercurve(GVJ_t * job, pointf * af, int n,
00616                           int arrow_at_start, int arrow_at_end,
00617                           boolean filled)
00618 {
00619     gvrender_engine_t *gvre = job->render.engine;
00620 
00621     if (gvre) {
00622         if (gvre->beziercurve && job->obj->pen != PEN_NONE) {
00623             if (job->flags & GVRENDER_DOES_TRANSFORM)
00624                 gvre->beziercurve(job, af, n, arrow_at_start, arrow_at_end,
00625                                   filled);
00626             else {
00627                 if (sizeAF < n) {
00628                     sizeAF = n + 10;
00629                     AF = grealloc(AF, sizeAF * sizeof(pointf));
00630                 }
00631                 gvrender_ptf_A(job, af, AF, n);
00632                 gvre->beziercurve(job, AF, n, arrow_at_start, arrow_at_end,
00633                                   filled);
00634             }
00635         }
00636     }
00637 }
00638 
00639 void gvrender_polyline(GVJ_t * job, pointf * af, int n)
00640 {
00641     gvrender_engine_t *gvre = job->render.engine;
00642 
00643     if (gvre) {
00644         if (gvre->polyline && job->obj->pen != PEN_NONE) {
00645             if (job->flags & GVRENDER_DOES_TRANSFORM)
00646                 gvre->polyline(job, af, n);
00647             else {
00648                 if (sizeAF < n) {
00649                     sizeAF = n + 10;
00650                     AF = grealloc(AF, sizeAF * sizeof(pointf));
00651                 }
00652                 gvrender_ptf_A(job, af, AF, n);
00653                 gvre->polyline(job, AF, n);
00654             }
00655         }
00656     }
00657 }
00658 
00659 void gvrender_comment(GVJ_t * job, char *str)
00660 {
00661     gvrender_engine_t *gvre = job->render.engine;
00662 
00663     if (!str || !str[0])
00664         return;
00665 
00666     if (gvre) {
00667         if (gvre->comment)
00668             gvre->comment(job, str);
00669     }
00670 }
00671 
00672 static imagescale_t get_imagescale(char *s)
00673 {
00674     if (*s == '\0')
00675         return IMAGESCALE_FALSE;
00676     if (!strcasecmp(s, "width"))
00677         return IMAGESCALE_WIDTH;
00678     if (!strcasecmp(s, "height"))
00679         return IMAGESCALE_HEIGHT;
00680     if (!strcasecmp(s, "both"))
00681         return IMAGESCALE_BOTH;
00682     if (mapbool(s))
00683         return IMAGESCALE_TRUE;
00684     return IMAGESCALE_FALSE;
00685 }
00686 
00687 /* gvrender_usershape:
00688  * Scale image to fill polygon bounding box according to "imagescale"
00689  */
00690 void gvrender_usershape(GVJ_t * job, char *name, pointf * a, int n,
00691                         boolean filled, char *imagescale)
00692 {
00693     gvrender_engine_t *gvre = job->render.engine;
00694     usershape_t *us;
00695     double iw, ih, pw, ph;
00696     double scalex, scaley;      /* scale factors */
00697     boxf b;                     /* target box */
00698     int i;
00699     point isz;
00700 
00701     if (!(us = gvusershape_find(name))) {
00702         if (find_user_shape(name)) {
00703             if (gvre && gvre->library_shape)
00704                 gvre->library_shape(job, name, a, n, filled);
00705         }
00706         return;
00707     }
00708 
00709     isz = gvusershape_size_dpi(us, job->dpi);
00710     if ((isz.x <= 0) && (isz.y <= 0))
00711         return;
00712 
00713     /* compute bb of polygon */
00714     b.LL = b.UR = a[0];
00715     for (i = 1; i < n; i++) {
00716         EXPANDBP(b, a[i]);
00717     }
00718 
00719     pw = b.UR.x - b.LL.x;
00720     ph = b.UR.y - b.LL.y;
00721     ih = (double) isz.y;
00722     iw = (double) isz.x;
00723 
00724     scalex = pw / iw;
00725     scaley = ph / ih;
00726 
00727     switch (get_imagescale(imagescale)) {
00728     case IMAGESCALE_TRUE:
00729         /* keep aspect ratio fixed by just using the smaller scale */
00730         if (scalex < scaley) {
00731             iw *= scalex;
00732             ih *= scalex;
00733         } else {
00734             iw *= scaley;
00735             ih *= scaley;
00736         }
00737         break;
00738     case IMAGESCALE_WIDTH:
00739         iw *= scalex;
00740         break;
00741     case IMAGESCALE_HEIGHT:
00742         ih *= scaley;
00743         break;
00744     case IMAGESCALE_BOTH:
00745         iw *= scalex;
00746         ih *= scaley;
00747         break;
00748     case IMAGESCALE_FALSE:
00749     default:
00750         break;
00751     }
00752 
00753     /* if image is smaller than target area then center it */
00754     if (iw < pw) {
00755         b.LL.x += (pw - iw) / 2.0;
00756         b.UR.x -= (pw - iw) / 2.0;
00757     }
00758     if (ih < ph) {
00759         b.LL.y += (ph - ih) / 2.0;
00760         b.UR.y -= (ph - ih) / 2.0;
00761     }
00762 
00763     /* convert from graph to device coordinates */
00764     if (!(job->flags & GVRENDER_DOES_TRANSFORM)) {
00765         b.LL = gvrender_ptf(job, b.LL);
00766         b.UR = gvrender_ptf(job, b.UR);
00767     }
00768 
00769     if (b.LL.x > b.UR.x) {
00770         double d = b.LL.x;
00771         b.LL.x = b.UR.x;
00772         b.UR.x = d;
00773     }
00774     if (b.LL.y > b.UR.y) {
00775         double d = b.LL.y;
00776         b.LL.y = b.UR.y;
00777         b.UR.y = d;
00778     }
00779     if (gvre) {
00780         gvloadimage(job, us, b, filled, job->render.type);
00781     }
00782 }
00783 
00784 void gvrender_set_penwidth(GVJ_t * job, double penwidth)
00785 {
00786     gvrender_engine_t *gvre = job->render.engine;
00787 
00788     if (gvre) {
00789         job->obj->penwidth = penwidth;
00790         /*if (gvre->set_penwidth) gvre->set_penwidth(job, penwidth); */
00791     }
00792 }