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