gvrender.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. /*
  11. * graphics code generator wrapper
  12. *
  13. * This library forms the socket for run-time loadable render plugins.
  14. */
  15. #include "config.h"
  16. #include <assert.h>
  17. #include <string.h>
  18. #include <common/const.h>
  19. #include <common/macros.h>
  20. #include <common/colorprocs.h>
  21. #include <gvc/gvplugin_render.h>
  22. #include <cgraph/cgraph.h>
  23. #include <gvc/gvcint.h>
  24. #include <common/geom.h>
  25. #include <common/geomprocs.h>
  26. #include <common/render.h>
  27. #include <gvc/gvcproc.h>
  28. #include <limits.h>
  29. #include <stdlib.h>
  30. #include <util/agxbuf.h>
  31. #include <util/alloc.h>
  32. #include <util/strcasecmp.h>
  33. #include <util/streq.h>
  34. extern bool mapbool(const char *s);
  35. int gvrender_select(GVJ_t * job, const char *str)
  36. {
  37. GVC_t *gvc = job->gvc;
  38. gvplugin_available_t *plugin;
  39. gvplugin_installed_t *typeptr;
  40. gvplugin_load(gvc, API_device, str, NULL);
  41. /* When job is created, it is zeroed out.
  42. * Some flags, such as OUTPUT_NOT_REQUIRED, may already be set,
  43. * so don't reset.
  44. */
  45. /* job->flags = 0; */
  46. plugin = gvc->api[API_device];
  47. if (plugin) {
  48. typeptr = plugin->typeptr;
  49. job->device.engine = typeptr->engine;
  50. job->device.features = typeptr->features;
  51. job->device.id = typeptr->id;
  52. job->device.type = plugin->typestr;
  53. job->flags |= job->device.features->flags;
  54. } else
  55. return NO_SUPPORT; /* FIXME - should differentiate problem */
  56. /* The device plugin has a dependency on a render plugin,
  57. * so the render plugin should be available as well now */
  58. plugin = gvc->api[API_render];
  59. if (plugin) {
  60. typeptr = plugin->typeptr;
  61. job->render.engine = typeptr->engine;
  62. job->render.features = typeptr->features;
  63. job->render.type = plugin->typestr;
  64. job->flags |= job->render.features->flags;
  65. if (job->device.engine)
  66. job->render.id = typeptr->id;
  67. else
  68. /* A null device engine indicates that the device id is also the renderer id
  69. * and that the renderer doesn't need "device" functions.
  70. * Device "features" settings are still available */
  71. job->render.id = job->device.id;
  72. return GVRENDER_PLUGIN;
  73. }
  74. job->render.engine = NULL;
  75. return NO_SUPPORT; /* FIXME - should differentiate problem */
  76. }
  77. int gvrender_features(GVJ_t * job)
  78. {
  79. gvrender_engine_t *gvre = job->render.engine;
  80. int features = 0;
  81. if (gvre) {
  82. features = job->render.features->flags;
  83. }
  84. return features;
  85. }
  86. /* gvrender_begin_job:
  87. * Return 0 on success
  88. */
  89. int gvrender_begin_job(GVJ_t * job)
  90. {
  91. gvrender_engine_t *gvre = job->render.engine;
  92. if (gvdevice_initialize(job))
  93. return 1;
  94. if (gvre) {
  95. if (gvre->begin_job)
  96. gvre->begin_job(job);
  97. }
  98. return 0;
  99. }
  100. void gvrender_end_job(GVJ_t * job)
  101. {
  102. gvrender_engine_t *gvre = job->render.engine;
  103. if (gvre) {
  104. if (gvre->end_job)
  105. gvre->end_job(job);
  106. }
  107. job->gvc->common.lib = NULL; /* FIXME - minimally this doesn't belong here */
  108. gvdevice_finalize(job);
  109. }
  110. /* font modifiers */
  111. #define REGULAR 0
  112. #define BOLD 1
  113. #define ITALIC 2
  114. pointf gvrender_ptf(GVJ_t * job, pointf p)
  115. {
  116. pointf rv, translation, scale;
  117. translation = job->translation;
  118. scale.x = job->zoom * job->devscale.x;
  119. scale.y = job->zoom * job->devscale.y;
  120. if (job->rotation) {
  121. rv.x = -(p.y + translation.y) * scale.x;
  122. rv.y = (p.x + translation.x) * scale.y;
  123. } else {
  124. rv.x = (p.x + translation.x) * scale.x;
  125. rv.y = (p.y + translation.y) * scale.y;
  126. }
  127. return rv;
  128. }
  129. /* transform an array of n points */
  130. /* *AF and *af must be preallocated */
  131. /* *AF can be the same as *af for inplace transforms */
  132. pointf *gvrender_ptf_A(GVJ_t *job, pointf *af, pointf *AF, size_t n) {
  133. double t;
  134. pointf translation, scale;
  135. translation = job->translation;
  136. scale.x = job->zoom * job->devscale.x;
  137. scale.y = job->zoom * job->devscale.y;
  138. if (job->rotation) {
  139. for (size_t i = 0; i < n; i++) {
  140. t = -(af[i].y + translation.y) * scale.x;
  141. AF[i].y = (af[i].x + translation.x) * scale.y;
  142. AF[i].x = t;
  143. }
  144. } else {
  145. for (size_t i = 0; i < n; i++) {
  146. AF[i].x = (af[i].x + translation.x) * scale.x;
  147. AF[i].y = (af[i].y + translation.y) * scale.y;
  148. }
  149. }
  150. return AF;
  151. }
  152. static int gvrender_comparestr(const void *s1, const void *s2)
  153. {
  154. return strcasecmp(s1, *(char *const *)s2);
  155. }
  156. /* gvrender_resolve_color:
  157. * N.B. strcasecmp cannot be used in bsearch, as it will pass a pointer
  158. * to an element in the array features->knowncolors (i.e., a char**)
  159. * as an argument of the compare function, while the arguments to
  160. * strcasecmp are both char*.
  161. */
  162. static void gvrender_resolve_color(gvrender_features_t * features,
  163. char *name, gvcolor_t * color)
  164. {
  165. int rc;
  166. color->u.string = name;
  167. color->type = COLOR_STRING;
  168. if (!features->knowncolors
  169. ||
  170. (bsearch(name, features->knowncolors, features->sz_knowncolors,
  171. sizeof(char *), gvrender_comparestr)) == NULL) {
  172. /* if name was not found in known_colors */
  173. rc = colorxlate(name, color, features->color_type);
  174. if (rc != COLOR_OK) {
  175. if (rc == COLOR_UNKNOWN) {
  176. agxbuf missedcolor = {0};
  177. agxbprint(&missedcolor, "color %s", name);
  178. if (emit_once(agxbuse(&missedcolor)))
  179. agwarningf("%s is not a known color.\n", name);
  180. agxbfree(&missedcolor);
  181. } else {
  182. agerrorf("error in colorxlate()\n");
  183. }
  184. }
  185. }
  186. }
  187. void gvrender_begin_graph(GVJ_t *job) {
  188. gvrender_engine_t *gvre = job->render.engine;
  189. if (gvre) {
  190. /* render specific init */
  191. if (gvre->begin_graph)
  192. gvre->begin_graph(job);
  193. }
  194. }
  195. void gvrender_end_graph(GVJ_t * job)
  196. {
  197. gvrender_engine_t *gvre = job->render.engine;
  198. if (gvre) {
  199. if (gvre->end_graph)
  200. gvre->end_graph(job);
  201. }
  202. gvdevice_format(job);
  203. }
  204. void gvrender_begin_page(GVJ_t * job)
  205. {
  206. gvrender_engine_t *gvre = job->render.engine;
  207. if (gvre) {
  208. if (gvre->begin_page)
  209. gvre->begin_page(job);
  210. }
  211. }
  212. void gvrender_end_page(GVJ_t * job)
  213. {
  214. gvrender_engine_t *gvre = job->render.engine;
  215. if (gvre) {
  216. if (gvre->end_page)
  217. gvre->end_page(job);
  218. }
  219. }
  220. void gvrender_begin_layer(GVJ_t * job)
  221. {
  222. gvrender_engine_t *gvre = job->render.engine;
  223. if (gvre) {
  224. if (gvre->begin_layer)
  225. gvre->begin_layer(job, job->gvc->layerIDs[job->layerNum],
  226. job->layerNum, job->numLayers);
  227. }
  228. }
  229. void gvrender_end_layer(GVJ_t * job)
  230. {
  231. gvrender_engine_t *gvre = job->render.engine;
  232. if (gvre) {
  233. if (gvre->end_layer)
  234. gvre->end_layer(job);
  235. }
  236. }
  237. void gvrender_begin_cluster(GVJ_t *job) {
  238. gvrender_engine_t *gvre = job->render.engine;
  239. if (gvre) {
  240. if (gvre->begin_cluster)
  241. gvre->begin_cluster(job);
  242. }
  243. }
  244. void gvrender_end_cluster(GVJ_t *job) {
  245. gvrender_engine_t *gvre = job->render.engine;
  246. if (gvre) {
  247. if (gvre->end_cluster)
  248. gvre->end_cluster(job);
  249. }
  250. }
  251. void gvrender_begin_nodes(GVJ_t * job)
  252. {
  253. gvrender_engine_t *gvre = job->render.engine;
  254. if (gvre) {
  255. if (gvre->begin_nodes)
  256. gvre->begin_nodes(job);
  257. }
  258. }
  259. void gvrender_end_nodes(GVJ_t * job)
  260. {
  261. gvrender_engine_t *gvre = job->render.engine;
  262. if (gvre) {
  263. if (gvre->end_nodes)
  264. gvre->end_nodes(job);
  265. }
  266. }
  267. void gvrender_begin_edges(GVJ_t * job)
  268. {
  269. gvrender_engine_t *gvre = job->render.engine;
  270. if (gvre) {
  271. if (gvre->begin_edges)
  272. gvre->begin_edges(job);
  273. }
  274. }
  275. void gvrender_end_edges(GVJ_t * job)
  276. {
  277. gvrender_engine_t *gvre = job->render.engine;
  278. if (gvre) {
  279. if (gvre->end_edges)
  280. gvre->end_edges(job);
  281. }
  282. }
  283. void gvrender_begin_node(GVJ_t *job) {
  284. gvrender_engine_t *gvre = job->render.engine;
  285. if (gvre) {
  286. if (gvre->begin_node)
  287. gvre->begin_node(job);
  288. }
  289. }
  290. void gvrender_end_node(GVJ_t * job)
  291. {
  292. gvrender_engine_t *gvre = job->render.engine;
  293. if (gvre) {
  294. if (gvre->end_node)
  295. gvre->end_node(job);
  296. }
  297. }
  298. void gvrender_begin_edge(GVJ_t *job) {
  299. gvrender_engine_t *gvre = job->render.engine;
  300. if (gvre) {
  301. if (gvre->begin_edge)
  302. gvre->begin_edge(job);
  303. }
  304. }
  305. void gvrender_end_edge(GVJ_t * job)
  306. {
  307. gvrender_engine_t *gvre = job->render.engine;
  308. if (gvre) {
  309. if (gvre->end_edge)
  310. gvre->end_edge(job);
  311. }
  312. }
  313. void gvrender_begin_anchor(GVJ_t * job, char *href, char *tooltip,
  314. char *target, char *id)
  315. {
  316. gvrender_engine_t *gvre = job->render.engine;
  317. if (gvre) {
  318. if (gvre->begin_anchor)
  319. gvre->begin_anchor(job, href, tooltip, target, id);
  320. }
  321. }
  322. void gvrender_end_anchor(GVJ_t * job)
  323. {
  324. gvrender_engine_t *gvre = job->render.engine;
  325. if (gvre) {
  326. if (gvre->end_anchor)
  327. gvre->end_anchor(job);
  328. }
  329. }
  330. void gvrender_begin_label(GVJ_t * job, label_type type)
  331. {
  332. gvrender_engine_t *gvre = job->render.engine;
  333. if (gvre) {
  334. if (gvre->begin_label)
  335. gvre->begin_label(job, type);
  336. }
  337. }
  338. void gvrender_end_label(GVJ_t * job)
  339. {
  340. gvrender_engine_t *gvre = job->render.engine;
  341. if (gvre) {
  342. if (gvre->end_label)
  343. gvre->end_label(job);
  344. }
  345. }
  346. void gvrender_textspan(GVJ_t * job, pointf p, textspan_t * span)
  347. {
  348. gvrender_engine_t *gvre = job->render.engine;
  349. pointf PF;
  350. if (span->str && span->str[0]
  351. && (!job->obj /* because of xdgen non-conformity */
  352. || job->obj->pen != PEN_NONE)) {
  353. if (job->flags & GVRENDER_DOES_TRANSFORM)
  354. PF = p;
  355. else
  356. PF = gvrender_ptf(job, p);
  357. if (gvre) {
  358. if (gvre->textspan)
  359. gvre->textspan(job, PF, span);
  360. }
  361. }
  362. }
  363. void gvrender_set_pencolor(GVJ_t * job, char *name)
  364. {
  365. gvrender_engine_t *gvre = job->render.engine;
  366. gvcolor_t *color = &(job->obj->pencolor);
  367. char *cp = NULL;
  368. if ((cp = strchr(name, ':'))) // if it’s a color list, then use only first
  369. *cp = '\0';
  370. if (gvre) {
  371. gvrender_resolve_color(job->render.features, name, color);
  372. if (gvre->resolve_color)
  373. gvre->resolve_color(job, color);
  374. }
  375. if (cp) /* restore color list */
  376. *cp = ':';
  377. }
  378. void gvrender_set_fillcolor(GVJ_t * job, char *name)
  379. {
  380. gvrender_engine_t *gvre = job->render.engine;
  381. gvcolor_t *color = &(job->obj->fillcolor);
  382. char *cp = NULL;
  383. if ((cp = strchr(name, ':'))) // if it’s a color list, then use only first
  384. *cp = '\0';
  385. if (gvre) {
  386. gvrender_resolve_color(job->render.features, name, color);
  387. if (gvre->resolve_color)
  388. gvre->resolve_color(job, color);
  389. }
  390. if (cp)
  391. *cp = ':';
  392. }
  393. void gvrender_set_gradient_vals(GVJ_t *job, char *stopcolor, int angle,
  394. double frac) {
  395. gvrender_engine_t *gvre = job->render.engine;
  396. gvcolor_t *color = &(job->obj->stopcolor);
  397. if (gvre) {
  398. gvrender_resolve_color(job->render.features, stopcolor, color);
  399. if (gvre->resolve_color)
  400. gvre->resolve_color(job, color);
  401. }
  402. job->obj->gradient_angle = angle;
  403. job->obj->gradient_frac = frac;
  404. }
  405. void gvrender_set_style(GVJ_t * job, char **s)
  406. {
  407. gvrender_engine_t *gvre = job->render.engine;
  408. obj_state_t *obj = job->obj;
  409. char *line, *p;
  410. obj->rawstyle = s;
  411. if (gvre) {
  412. if (s)
  413. while ((p = line = *s++)) {
  414. if (streq(line, "solid"))
  415. obj->pen = PEN_SOLID;
  416. else if (streq(line, "dashed"))
  417. obj->pen = PEN_DASHED;
  418. else if (streq(line, "dotted"))
  419. obj->pen = PEN_DOTTED;
  420. else if (streq(line, "invis") || streq(line, "invisible"))
  421. obj->pen = PEN_NONE;
  422. else if (streq(line, "bold"))
  423. obj->penwidth = PENWIDTH_BOLD;
  424. else if (streq(line, "setlinewidth")) {
  425. while (*p)
  426. p++;
  427. p++;
  428. obj->penwidth = atof(p);
  429. } else if (streq(line, "filled"))
  430. obj->fill = FILL_SOLID;
  431. else if (streq(line, "unfilled"))
  432. obj->fill = FILL_NONE;
  433. else if (streq(line, "tapered"));
  434. else {
  435. agwarningf(
  436. "gvrender_set_style: unsupported style %s - ignoring\n",
  437. line);
  438. }
  439. }
  440. }
  441. }
  442. void gvrender_ellipse(GVJ_t *job, pointf *pf, int filled) {
  443. gvrender_engine_t *gvre = job->render.engine;
  444. if (gvre) {
  445. if (gvre->ellipse && job->obj->pen != PEN_NONE) {
  446. pointf af[] = {
  447. mid_pointf(pf[0], pf[1]), // center
  448. pf[1] // corner
  449. };
  450. if (!(job->flags & GVRENDER_DOES_TRANSFORM))
  451. gvrender_ptf_A(job, af, af, 2);
  452. gvre->ellipse(job, af, filled);
  453. }
  454. }
  455. }
  456. void gvrender_polygon(GVJ_t *job, pointf *af, size_t n, int filled) {
  457. int noPoly = 0;
  458. gvcolor_t save_pencolor;
  459. gvrender_engine_t *gvre = job->render.engine;
  460. if (gvre) {
  461. if (gvre->polygon && job->obj->pen != PEN_NONE) {
  462. if (filled & NO_POLY) {
  463. noPoly = 1;
  464. filled &= ~NO_POLY;
  465. save_pencolor = job->obj->pencolor;
  466. job->obj->pencolor = job->obj->fillcolor;
  467. }
  468. if (job->flags & GVRENDER_DOES_TRANSFORM)
  469. gvre->polygon(job, af, n, filled);
  470. else {
  471. pointf *AF = gv_calloc(n, sizeof(pointf));
  472. gvrender_ptf_A(job, af, AF, n);
  473. gvre->polygon(job, AF, n, filled);
  474. free(AF);
  475. }
  476. if (noPoly)
  477. job->obj->pencolor = save_pencolor;
  478. }
  479. }
  480. }
  481. void gvrender_box(GVJ_t * job, boxf B, int filled)
  482. {
  483. pointf A[4];
  484. A[0] = B.LL;
  485. A[2] = B.UR;
  486. A[1].x = A[0].x;
  487. A[1].y = A[2].y;
  488. A[3].x = A[2].x;
  489. A[3].y = A[0].y;
  490. gvrender_polygon(job, A, 4, filled);
  491. }
  492. void gvrender_beziercurve(GVJ_t *job, pointf *af, size_t n, int filled) {
  493. gvrender_engine_t *gvre = job->render.engine;
  494. if (gvre) {
  495. if (gvre->beziercurve && job->obj->pen != PEN_NONE) {
  496. if (job->flags & GVRENDER_DOES_TRANSFORM)
  497. gvre->beziercurve(job, af, n, filled);
  498. else {
  499. pointf *AF = gv_calloc(n, sizeof(pointf));
  500. gvrender_ptf_A(job, af, AF, n);
  501. gvre->beziercurve(job, AF, n, filled);
  502. free(AF);
  503. }
  504. }
  505. }
  506. }
  507. void gvrender_polyline(GVJ_t *job, pointf *af, size_t n) {
  508. gvrender_engine_t *gvre = job->render.engine;
  509. if (gvre) {
  510. if (gvre->polyline && job->obj->pen != PEN_NONE) {
  511. if (job->flags & GVRENDER_DOES_TRANSFORM)
  512. gvre->polyline(job, af, n);
  513. else {
  514. pointf *AF = gv_calloc(n, sizeof(pointf));
  515. gvrender_ptf_A(job, af, AF, n);
  516. gvre->polyline(job, AF, n);
  517. free(AF);
  518. }
  519. }
  520. }
  521. }
  522. void gvrender_comment(GVJ_t * job, char *str)
  523. {
  524. gvrender_engine_t *gvre = job->render.engine;
  525. if (!str || !str[0])
  526. return;
  527. if (gvre) {
  528. if (gvre->comment)
  529. gvre->comment(job, str);
  530. }
  531. }
  532. static imagescale_t get_imagescale(char *s)
  533. {
  534. if (*s == '\0')
  535. return IMAGESCALE_FALSE;
  536. if (!strcasecmp(s, "width"))
  537. return IMAGESCALE_WIDTH;
  538. if (!strcasecmp(s, "height"))
  539. return IMAGESCALE_HEIGHT;
  540. if (!strcasecmp(s, "both"))
  541. return IMAGESCALE_BOTH;
  542. if (mapbool(s))
  543. return IMAGESCALE_TRUE;
  544. return IMAGESCALE_FALSE;
  545. }
  546. static imagepos_t get_imagepos(char *s)
  547. {
  548. if (*s == '\0')
  549. return IMAGEPOS_MIDDLE_CENTER;
  550. if (!strcasecmp(s, "tl"))
  551. return IMAGEPOS_TOP_LEFT;
  552. if (!strcasecmp(s, "tc"))
  553. return IMAGEPOS_TOP_CENTER;
  554. if (!strcasecmp(s, "tr"))
  555. return IMAGEPOS_TOP_RIGHT;
  556. if (!strcasecmp(s, "ml"))
  557. return IMAGEPOS_MIDDLE_LEFT;
  558. if (!strcasecmp(s, "mc"))
  559. return IMAGEPOS_MIDDLE_CENTER;
  560. if (!strcasecmp(s, "mr"))
  561. return IMAGEPOS_MIDDLE_RIGHT;
  562. if (!strcasecmp(s, "bl"))
  563. return IMAGEPOS_BOTTOM_LEFT;
  564. if (!strcasecmp(s, "bc"))
  565. return IMAGEPOS_BOTTOM_CENTER;
  566. if (!strcasecmp(s, "br"))
  567. return IMAGEPOS_BOTTOM_RIGHT;
  568. return IMAGEPOS_MIDDLE_CENTER;
  569. }
  570. /* gvrender_usershape:
  571. * Scale image to fill polygon bounding box according to "imagescale",
  572. * positioned at "imagepos"
  573. */
  574. void gvrender_usershape(GVJ_t *job, char *name, pointf *a, size_t n,
  575. bool filled, char *imagescale, char *imagepos) {
  576. gvrender_engine_t *gvre = job->render.engine;
  577. usershape_t *us;
  578. double iw, ih, pw, ph;
  579. double scalex, scaley; /* scale factors */
  580. boxf b; /* target box */
  581. point isz;
  582. imagepos_t position;
  583. assert(job);
  584. assert(name);
  585. assert(name[0]);
  586. if (!(us = gvusershape_find(name))) {
  587. if (find_user_shape(name)) {
  588. if (gvre && gvre->library_shape)
  589. gvre->library_shape(job, name, a, n, filled);
  590. }
  591. return;
  592. }
  593. isz = gvusershape_size_dpi(us, job->dpi);
  594. if ((isz.x <= 0) && (isz.y <= 0))
  595. return;
  596. /* compute bb of polygon */
  597. b.LL = b.UR = a[0];
  598. for (size_t i = 1; i < n; i++) {
  599. EXPANDBP(b, a[i]);
  600. }
  601. pw = b.UR.x - b.LL.x;
  602. ph = b.UR.y - b.LL.y;
  603. ih = (double) isz.y;
  604. iw = (double) isz.x;
  605. scalex = pw / iw;
  606. scaley = ph / ih;
  607. switch (get_imagescale(imagescale)) {
  608. case IMAGESCALE_TRUE:
  609. /* keep aspect ratio fixed by just using the smaller scale */
  610. if (scalex < scaley) {
  611. iw *= scalex;
  612. ih *= scalex;
  613. } else {
  614. iw *= scaley;
  615. ih *= scaley;
  616. }
  617. break;
  618. case IMAGESCALE_WIDTH:
  619. iw *= scalex;
  620. break;
  621. case IMAGESCALE_HEIGHT:
  622. ih *= scaley;
  623. break;
  624. case IMAGESCALE_BOTH:
  625. iw *= scalex;
  626. ih *= scaley;
  627. break;
  628. case IMAGESCALE_FALSE:
  629. default:
  630. break;
  631. }
  632. /* if image is smaller in any dimension, apply the specified positioning */
  633. position = get_imagepos(imagepos);
  634. if (iw < pw) {
  635. switch (position) {
  636. case IMAGEPOS_TOP_LEFT:
  637. case IMAGEPOS_MIDDLE_LEFT:
  638. case IMAGEPOS_BOTTOM_LEFT:
  639. b.UR.x = b.LL.x + iw;
  640. break;
  641. case IMAGEPOS_TOP_RIGHT:
  642. case IMAGEPOS_MIDDLE_RIGHT:
  643. case IMAGEPOS_BOTTOM_RIGHT:
  644. b.LL.x += (pw - iw);
  645. b.UR.x = b.LL.x + iw;
  646. break;
  647. default:
  648. b.LL.x += (pw - iw) / 2.0;
  649. b.UR.x -= (pw - iw) / 2.0;
  650. break;
  651. }
  652. }
  653. if (ih < ph) {
  654. switch (position) {
  655. case IMAGEPOS_TOP_LEFT:
  656. case IMAGEPOS_TOP_CENTER:
  657. case IMAGEPOS_TOP_RIGHT:
  658. b.LL.y = b.UR.y - ih;
  659. break;
  660. case IMAGEPOS_BOTTOM_LEFT:
  661. case IMAGEPOS_BOTTOM_CENTER:
  662. case IMAGEPOS_BOTTOM_RIGHT:
  663. b.LL.y += ih;
  664. b.UR.y = b.LL.y - ih;
  665. break;
  666. default:
  667. b.LL.y += (ph - ih) / 2.0;
  668. b.UR.y -= (ph - ih) / 2.0;
  669. break;
  670. }
  671. }
  672. /* convert from graph to device coordinates */
  673. if (!(job->flags & GVRENDER_DOES_TRANSFORM)) {
  674. b.LL = gvrender_ptf(job, b.LL);
  675. b.UR = gvrender_ptf(job, b.UR);
  676. }
  677. if (b.LL.x > b.UR.x) {
  678. double d = b.LL.x;
  679. b.LL.x = b.UR.x;
  680. b.UR.x = d;
  681. }
  682. if (b.LL.y > b.UR.y) {
  683. double d = b.LL.y;
  684. b.LL.y = b.UR.y;
  685. b.UR.y = d;
  686. }
  687. if (gvre) {
  688. gvloadimage(job, us, b, filled, job->render.type);
  689. }
  690. }
  691. void gvrender_set_penwidth(GVJ_t * job, double penwidth)
  692. {
  693. gvrender_engine_t *gvre = job->render.engine;
  694. if (gvre) {
  695. job->obj->penwidth = penwidth;
  696. }
  697. }