2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / lib / dialibartrenderer.c
blob6135405491dab5bb872c1d3f3253b70c1377b216
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #include <config.h>
21 #include <math.h>
22 #include <string.h> /* strlen */
24 #include "dialibartrenderer.h"
26 #include "message.h"
27 #include "color.h"
28 #include "font.h"
29 #include "intl.h"
30 #include "dia_image.h"
31 #include "object.h"
32 #include "textline.h"
34 #ifdef HAVE_LIBART
36 #ifdef HAVE_FREETYPE
37 #include <pango/pango.h>
38 #include <pango/pangoft2.h>
39 #elif defined G_OS_WIN32
40 /* ugly namespace clashes */
41 #define Rectangle Win32Rectangle
42 #include <pango/pango.h>
43 #undef Rectangle
44 #else
45 #include <pango/pango.h>
46 #include <gdk/gdk.h>
47 #endif
49 #include <libart_lgpl/art_point.h>
50 #include <libart_lgpl/art_misc.h>
51 #include <libart_lgpl/art_affine.h>
52 #include <libart_lgpl/art_svp_vpath.h>
53 #include <libart_lgpl/art_bpath.h>
54 #include <libart_lgpl/art_vpath_bpath.h>
55 #include <libart_lgpl/art_rgb.h>
56 #include <libart_lgpl/art_rgb_svp.h>
57 #include <libart_lgpl/art_rgb_affine.h>
58 #include <libart_lgpl/art_rgb_rgba_affine.h>
59 #include <libart_lgpl/art_rgb_bitmap_affine.h>
60 #include <libart_lgpl/art_filterlevel.h>
61 #include <libart_lgpl/art_svp_intersect.h>
63 static inline guint32
64 color_to_abgr(Color *col)
66 int rgba;
68 rgba = 0x0;
69 rgba |= (guint)(0xFF*col->blue) << 16;
70 rgba |= (guint)(0xFF*col->green) << 8;
71 rgba |= (guint)(0xFF*col->red);
73 return rgba;
76 static inline guint32
77 color_to_rgba(DiaLibartRenderer *renderer, Color *col)
79 int rgba;
81 if (renderer->highlight_color != NULL) {
82 rgba = 0xFF;
83 rgba |= (guint)(0xFF*renderer->highlight_color->red) << 24;
84 rgba |= (guint)(0xFF*renderer->highlight_color->green) << 16;
85 rgba |= (guint)(0xFF*renderer->highlight_color->blue) << 8;
86 } else {
87 rgba = 0xFF;
88 rgba |= (guint)(0xFF*col->red) << 24;
89 rgba |= (guint)(0xFF*col->green) << 16;
90 rgba |= (guint)(0xFF*col->blue) << 8;
93 return rgba;
96 static int
97 get_width_pixels (DiaRenderer *self)
99 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
101 return renderer->pixel_width;
104 static int
105 get_height_pixels (DiaRenderer *self)
107 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
109 return renderer->pixel_height;
112 static void
113 begin_render(DiaRenderer *self)
115 #ifdef HAVE_FREETYPE
116 /* pango_ft2_get_context API docs :
117 * ... Use of this function is discouraged, ...
119 * I've tried using the proper font_map stuff, but it kills font size:(
121 dia_font_push_context(pango_ft2_get_context(75, 75));
122 # define FONT_SCALE (1.0)
123 #elif defined G_OS_WIN32
124 dia_font_push_context(gdk_pango_context_get());
125 /* I shall never claim again to have understood Dias/Pangos font size
126 * relations ;). This was (1.0/22.0) but nowadays 1.0 seems to be
127 * fine with Pango/win32, too. --hb
129 # define FONT_SCALE (1.0)
130 #else
131 dia_font_push_context (gdk_pango_context_get ());
132 # define FONT_SCALE (0.8)
133 #endif
136 static void
137 end_render(DiaRenderer *self)
139 dia_font_pop_context();
144 static void
145 set_linewidth(DiaRenderer *self, real linewidth)
146 { /* 0 == hairline **/
147 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
149 if (renderer->highlight_color != NULL) {
150 /* 6 pixels wide -> 3 pixels beyond normal obj */
151 real border = dia_untransform_length(renderer->transform, 6);
152 linewidth += border;
154 renderer->line_width =
155 dia_transform_length(renderer->transform, linewidth);
156 if (renderer->line_width<=0.5)
157 renderer->line_width = 0.5; /* Minimum 0.5 pixel. */
160 static void
161 set_linecaps(DiaRenderer *self, LineCaps mode)
163 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
165 if (renderer->highlight_color != NULL) {
166 /* Can't tell that this does anything:( -LC */
167 renderer->cap_style = ART_PATH_STROKE_CAP_ROUND;
168 } else {
169 switch(mode) {
170 case LINECAPS_BUTT:
171 renderer->cap_style = ART_PATH_STROKE_CAP_BUTT;
172 break;
173 case LINECAPS_ROUND:
174 renderer->cap_style = ART_PATH_STROKE_CAP_ROUND;
175 break;
176 case LINECAPS_PROJECTING:
177 renderer->cap_style = ART_PATH_STROKE_CAP_SQUARE;
178 break;
183 static void
184 set_linejoin(DiaRenderer *self, LineJoin mode)
186 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
188 if (renderer->highlight_color != NULL) {
189 /* Can't tell that this does anything:( -LC */
190 renderer->join_style = ART_PATH_STROKE_JOIN_ROUND;
191 } else {
192 switch(mode) {
193 case LINEJOIN_MITER:
194 renderer->join_style = ART_PATH_STROKE_JOIN_MITER;
195 break;
196 case LINEJOIN_ROUND:
197 renderer->join_style = ART_PATH_STROKE_JOIN_ROUND;
198 break;
199 case LINEJOIN_BEVEL:
200 renderer->join_style = ART_PATH_STROKE_JOIN_BEVEL;
201 break;
206 static void
207 set_linestyle(DiaRenderer *self, LineStyle mode)
209 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
210 static double dash[10];
211 double hole_width;
213 renderer->saved_line_style = mode;
214 switch(mode) {
215 case LINESTYLE_SOLID:
216 renderer->dash_enabled = 0;
217 break;
218 case LINESTYLE_DASHED:
219 renderer->dash_enabled = 1;
220 renderer->dash.offset = 0.0;
221 renderer->dash.n_dash = 2;
222 renderer->dash.dash = dash;
223 dash[0] = renderer->dash_length;
224 dash[1] = renderer->dash_length;
225 break;
226 case LINESTYLE_DASH_DOT:
227 renderer->dash_enabled = 1;
228 renderer->dash.offset = 0.0;
229 renderer->dash.n_dash = 4;
230 renderer->dash.dash = dash;
231 hole_width = (renderer->dash_length - renderer->dot_length) / 2.0;
232 if (hole_width<1.0)
233 hole_width = 1.0;
234 dash[0] = renderer->dash_length;
235 dash[1] = hole_width;
236 dash[2] = renderer->dot_length;
237 dash[3] = hole_width;
238 break;
239 case LINESTYLE_DASH_DOT_DOT:
240 renderer->dash_enabled = 1;
241 renderer->dash.offset = 0.0;
242 renderer->dash.n_dash = 6;
243 renderer->dash.dash = dash;
244 hole_width = (renderer->dash_length - 2*renderer->dot_length) / 3;
245 if (hole_width<1.0)
246 hole_width = 1.0;
247 dash[0] = renderer->dash_length;
248 dash[1] = hole_width;
249 dash[2] = renderer->dot_length;
250 dash[3] = hole_width;
251 dash[4] = renderer->dot_length;
252 dash[5] = hole_width;
253 break;
254 case LINESTYLE_DOTTED:
255 renderer->dash_enabled = 1;
256 renderer->dash.offset = 0.0;
257 renderer->dash.n_dash = 2;
258 renderer->dash.dash = dash;
259 dash[0] = renderer->dot_length;
260 dash[1] = renderer->dot_length;
261 break;
265 static void
266 set_dashlength(DiaRenderer *self, real length)
267 { /* dot = 10% of len */
268 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
269 real ddisp_len;
271 ddisp_len =
272 dia_transform_length(renderer->transform, length);
274 renderer->dash_length = ddisp_len;
275 renderer->dot_length = ddisp_len*0.1;
277 if (renderer->dash_length<1.0)
278 renderer->dash_length = 1.0;
279 if (renderer->dash_length>255.0)
280 renderer->dash_length = 255.0;
281 if (renderer->dot_length<1.0)
282 renderer->dot_length = 1.0;
283 if (renderer->dot_length>255.0)
284 renderer->dot_length = 255.0;
285 set_linestyle(self, renderer->saved_line_style);
288 static void
289 set_fillstyle(DiaRenderer *self, FillStyle mode)
291 switch(mode) {
292 case FILLSTYLE_SOLID:
293 break;
294 default:
295 message_error(_("gdk_renderer: Unsupported fill mode specified!\n"));
299 static void
300 set_font(DiaRenderer *self, DiaFont *font, real height)
302 self->font_height = height * FONT_SCALE;
304 dia_font_ref(font);
305 if (self->font)
306 dia_font_unref(self->font);
307 self->font = font;
310 static void
311 draw_line(DiaRenderer *self,
312 Point *start, Point *end,
313 Color *line_color)
315 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
316 ArtVpath *vpath, *vpath_dashed;
317 ArtSVP *svp;
318 guint32 rgba;
319 double x,y;
321 rgba = color_to_rgba(renderer, line_color);
323 vpath = art_new (ArtVpath, 3);
325 dia_transform_coords_double(renderer->transform, start->x, start->y, &x, &y);
326 vpath[0].code = ART_MOVETO;
327 vpath[0].x = x;
328 vpath[0].y = y;
330 dia_transform_coords_double(renderer->transform, end->x, end->y, &x, &y);
331 vpath[1].code = ART_LINETO;
332 vpath[1].x = x;
333 vpath[1].y = y;
335 vpath[2].code = ART_END;
336 vpath[2].x = 0;
337 vpath[2].y = 0;
339 if (renderer->dash_enabled) {
340 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
341 art_free( vpath );
342 vpath = vpath_dashed;
345 svp = art_svp_vpath_stroke (vpath,
346 renderer->join_style,
347 renderer->cap_style,
348 renderer->line_width,
350 0.25);
352 art_free( vpath );
354 art_rgb_svp_alpha (svp,
355 0, 0,
356 renderer->pixel_width,
357 renderer->pixel_height,
358 rgba,
359 renderer->rgb_buffer, renderer->pixel_width*3,
360 NULL);
362 art_svp_free( svp );
365 static void
366 draw_polyline(DiaRenderer *self,
367 Point *points, int num_points,
368 Color *line_color)
370 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
371 ArtVpath *vpath, *vpath_dashed;
372 ArtSVP *svp;
373 guint32 rgba;
374 double x,y;
375 int i;
377 rgba = color_to_rgba(renderer, line_color);
379 vpath = art_new (ArtVpath, num_points+1);
381 for (i=0;i<num_points;i++) {
382 dia_transform_coords_double(renderer->transform,
383 points[i].x, points[i].y,
384 &x, &y);
385 vpath[i].code = (i==0)?ART_MOVETO:ART_LINETO;
386 vpath[i].x = x;
387 vpath[i].y = y;
389 vpath[i].code = ART_END;
390 vpath[i].x = 0;
391 vpath[i].y = 0;
393 if (renderer->dash_enabled) {
394 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
395 art_free( vpath );
396 vpath = vpath_dashed;
399 svp = art_svp_vpath_stroke (vpath,
400 renderer->join_style,
401 renderer->cap_style,
402 renderer->line_width,
404 0.25);
406 art_free( vpath );
408 art_rgb_svp_alpha (svp,
409 0, 0,
410 renderer->pixel_width,
411 renderer->pixel_height,
412 rgba,
413 renderer->rgb_buffer, renderer->pixel_width*3,
414 NULL);
416 art_svp_free( svp );
419 static void
420 draw_polygon(DiaRenderer *self,
421 Point *points, int num_points,
422 Color *line_color)
424 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
425 ArtVpath *vpath, *vpath_dashed;
426 ArtSVP *svp;
427 guint32 rgba;
428 double x,y;
429 int i;
431 rgba = color_to_rgba(renderer, line_color);
433 vpath = art_new (ArtVpath, num_points+2);
435 for (i=0;i<num_points;i++) {
436 dia_transform_coords_double(renderer->transform,
437 points[i].x, points[i].y,
438 &x, &y);
439 vpath[i].code = (i==0)?ART_MOVETO:ART_LINETO;
440 vpath[i].x = x;
441 vpath[i].y = y;
443 dia_transform_coords_double(renderer->transform,
444 points[0].x, points[0].y,
445 &x, &y);
446 vpath[i].code = ART_LINETO;
447 vpath[i].x = x;
448 vpath[i].y = y;
449 vpath[i+1].code = ART_END;
450 vpath[i+1].x = 0;
451 vpath[i+1].y = 0;
453 if (renderer->dash_enabled) {
454 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
455 art_free( vpath );
456 vpath = vpath_dashed;
459 svp = art_svp_vpath_stroke (vpath,
460 renderer->join_style,
461 renderer->cap_style,
462 renderer->line_width,
464 0.25);
466 art_free( vpath );
468 art_rgb_svp_alpha (svp,
469 0, 0,
470 renderer->pixel_width,
471 renderer->pixel_height,
472 rgba,
473 renderer->rgb_buffer, renderer->pixel_width*3,
474 NULL);
476 art_svp_free( svp );
479 static void
480 fill_polygon(DiaRenderer *self,
481 Point *points, int num_points,
482 Color *color)
484 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
485 ArtVpath *vpath;
486 ArtSVP *svp, *temp;
487 guint32 rgba;
488 double x,y;
489 int i;
490 ArtSvpWriter *swr;
492 rgba = color_to_rgba(renderer, color);
494 vpath = art_new (ArtVpath, num_points+2);
496 for (i=0;i<num_points;i++) {
497 dia_transform_coords_double(renderer->transform,
498 points[i].x, points[i].y,
499 &x, &y);
500 vpath[i].code = (i==0)?ART_MOVETO:ART_LINETO;
501 vpath[i].x = x;
502 vpath[i].y = y;
504 dia_transform_coords_double(renderer->transform,
505 points[0].x, points[0].y,
506 &x, &y);
507 vpath[i].code = ART_LINETO;
508 vpath[i].x = x;
509 vpath[i].y = y;
510 vpath[i+1].code = ART_END;
511 vpath[i+1].x = 0;
512 vpath[i+1].y = 0;
514 temp = art_svp_from_vpath (vpath);
516 art_free( vpath );
518 /** We always use odd-even wind rule */
519 swr = art_svp_writer_rewind_new(ART_WIND_RULE_ODDEVEN);
521 art_svp_intersector(temp, swr);
522 svp = art_svp_writer_rewind_reap(swr);
523 art_svp_free(temp);
525 art_rgb_svp_alpha (svp,
526 0, 0,
527 renderer->pixel_width,
528 renderer->pixel_height,
529 rgba,
530 renderer->rgb_buffer, renderer->pixel_width*3,
531 NULL);
533 art_svp_free( svp );
536 static void
537 draw_rect(DiaRenderer *self,
538 Point *ul_corner, Point *lr_corner,
539 Color *color)
541 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
542 ArtVpath *vpath, *vpath_dashed;
543 ArtSVP *svp;
544 guint32 rgba;
545 double top, bottom, left, right;
547 dia_transform_coords_double(renderer->transform,
548 ul_corner->x, ul_corner->y, &left, &top);
549 dia_transform_coords_double(renderer->transform,
550 lr_corner->x, lr_corner->y, &right, &bottom);
552 if ((left>right) || (top>bottom))
553 return;
555 rgba = color_to_rgba(renderer, color);
557 vpath = art_new (ArtVpath, 6);
559 vpath[0].code = ART_MOVETO;
560 vpath[0].x = left;
561 vpath[0].y = top;
562 vpath[1].code = ART_LINETO;
563 vpath[1].x = right;
564 vpath[1].y = top;
565 vpath[2].code = ART_LINETO;
566 vpath[2].x = right;
567 vpath[2].y = bottom;
568 vpath[3].code = ART_LINETO;
569 vpath[3].x = left;
570 vpath[3].y = bottom;
571 vpath[4].code = ART_LINETO;
572 vpath[4].x = left;
573 vpath[4].y = top;
574 vpath[5].code = ART_END;
575 vpath[5].x = 0;
576 vpath[5].y = 0;
578 if (renderer->dash_enabled) {
579 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
580 art_free( vpath );
581 vpath = vpath_dashed;
584 svp = art_svp_vpath_stroke (vpath,
585 renderer->join_style,
586 renderer->cap_style,
587 renderer->line_width,
589 0.25);
591 art_free( vpath );
593 art_rgb_svp_alpha (svp,
594 0, 0,
595 renderer->pixel_width,
596 renderer->pixel_height,
597 rgba,
598 renderer->rgb_buffer, renderer->pixel_width*3,
599 NULL);
601 art_svp_free( svp );
604 static void
605 fill_rect(DiaRenderer *self,
606 Point *ul_corner, Point *lr_corner,
607 Color *color)
609 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
610 ArtVpath *vpath;
611 ArtSVP *svp;
612 guint32 rgba;
613 double top, bottom, left, right;
615 dia_transform_coords_double(renderer->transform,
616 ul_corner->x, ul_corner->y, &left, &top);
617 dia_transform_coords_double(renderer->transform,
618 lr_corner->x, lr_corner->y, &right, &bottom);
620 if ((left>right) || (top>bottom))
621 return;
623 rgba = color_to_rgba(renderer, color);
625 vpath = art_new (ArtVpath, 6);
627 vpath[0].code = ART_MOVETO;
628 vpath[0].x = left;
629 vpath[0].y = top;
630 vpath[1].code = ART_LINETO;
631 vpath[1].x = right;
632 vpath[1].y = top;
633 vpath[2].code = ART_LINETO;
634 vpath[2].x = right;
635 vpath[2].y = bottom;
636 vpath[3].code = ART_LINETO;
637 vpath[3].x = left;
638 vpath[3].y = bottom;
639 vpath[4].code = ART_LINETO;
640 vpath[4].x = left;
641 vpath[4].y = top;
642 vpath[5].code = ART_END;
643 vpath[5].x = 0;
644 vpath[5].y = 0;
646 svp = art_svp_from_vpath (vpath);
648 art_free( vpath );
650 art_rgb_svp_alpha (svp,
651 0, 0,
652 renderer->pixel_width,
653 renderer->pixel_height,
654 rgba,
655 renderer->rgb_buffer, renderer->pixel_width*3,
656 NULL);
658 art_svp_free( svp );
661 static void
662 draw_arc(DiaRenderer *self,
663 Point *center,
664 real width, real height,
665 real angle1, real angle2,
666 Color *line_color)
668 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
669 ArtVpath *vpath, *vpath_dashed;
670 ArtSVP *svp;
671 guint32 rgba;
672 real dangle;
673 real circ;
674 double x,y;
675 int num_points;
676 double theta, dtheta;
677 int i;
679 width = dia_transform_length(renderer->transform, width);
680 height = dia_transform_length(renderer->transform, height);
681 dia_transform_coords_double(renderer->transform,
682 center->x, center->y, &x, &y);
684 if ((width<0.0) || (height<0.0))
685 return;
687 dangle = angle2-angle1;
688 if (dangle<0)
689 dangle += 360.0;
691 /* Over-approximate the circumference */
692 if (width>height)
693 circ = M_PI*width;
694 else
695 circ = M_PI*height;
697 circ *= dangle/360.0;
699 #define LEN_PER_SEGMENT 3.0
701 num_points = circ/LEN_PER_SEGMENT;
702 if (num_points<5) /* Don't be too coarse */
703 num_points = 5;
705 rgba = color_to_rgba(renderer, line_color);
707 vpath = art_new (ArtVpath, num_points+1);
709 theta = M_PI*angle1/180.0;
710 dtheta = (M_PI*dangle/180.0)/(num_points-1);
711 for (i=0;i<num_points;i++) {
712 vpath[i].code = (i==0)?ART_MOVETO:ART_LINETO;
713 vpath[i].x = x + width/2.0*cos(theta);
714 vpath[i].y = y - height/2.0*sin(theta);
715 theta += dtheta;
717 vpath[i].code = ART_END;
718 vpath[i].x = 0;
719 vpath[i].y = 0;
721 if (renderer->dash_enabled) {
722 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
723 art_free( vpath );
724 vpath = vpath_dashed;
727 svp = art_svp_vpath_stroke (vpath,
728 renderer->join_style,
729 renderer->cap_style,
730 renderer->line_width,
732 0.25);
734 art_free( vpath );
736 art_rgb_svp_alpha (svp,
737 0, 0,
738 renderer->pixel_width,
739 renderer->pixel_height,
740 rgba,
741 renderer->rgb_buffer, renderer->pixel_width*3,
742 NULL);
744 art_svp_free( svp );
747 static void
748 fill_arc(DiaRenderer *self,
749 Point *center,
750 real width, real height,
751 real angle1, real angle2,
752 Color *color)
754 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
755 ArtVpath *vpath;
756 ArtSVP *svp;
757 guint32 rgba;
758 real dangle;
759 real circ;
760 double x,y;
761 int num_points;
762 double theta, dtheta;
763 int i;
765 width = dia_transform_length(renderer->transform, width);
766 height = dia_transform_length(renderer->transform, height);
767 dia_transform_coords_double(renderer->transform,
768 center->x, center->y, &x, &y);
770 if ((width<0.0) || (height<0.0))
771 return;
773 dangle = angle2-angle1;
774 if (dangle<0)
775 dangle += 360.0;
777 /* Over-approximate the circumference */
778 if (width>height)
779 circ = M_PI*width;
780 else
781 circ = M_PI*height;
783 circ *= dangle/360.0;
785 #define LEN_PER_SEGMENT 3.0
787 num_points = circ/LEN_PER_SEGMENT;
788 if (num_points<5) /* Don't be too coarse */
789 num_points = 5;
791 rgba = color_to_rgba(renderer, color);
793 vpath = art_new (ArtVpath, num_points+2+1);
795 vpath[0].code = ART_MOVETO;
796 vpath[0].x = x;
797 vpath[0].y = y;
798 theta = M_PI*angle1/180.0;
799 dtheta = (M_PI*dangle/180.0)/(num_points-1);
800 for (i=0;i<num_points;i++) {
801 vpath[i+1].code = ART_LINETO;
802 vpath[i+1].x = x + width/2.0*cos(theta);
803 vpath[i+1].y = y - height/2.0*sin(theta);
804 theta += dtheta;
806 vpath[i+1].code = ART_LINETO;
807 vpath[i+1].x = x;
808 vpath[i+1].y = y;
809 vpath[i+2].code = ART_END;
810 vpath[i+2].x = 0;
811 vpath[i+2].y = 0;
813 svp = art_svp_from_vpath (vpath);
815 art_free( vpath );
817 art_rgb_svp_alpha (svp,
818 0, 0,
819 renderer->pixel_width,
820 renderer->pixel_height,
821 rgba,
822 renderer->rgb_buffer, renderer->pixel_width*3,
823 NULL);
825 art_svp_free( svp );
828 static void
829 draw_ellipse(DiaRenderer *self,
830 Point *center,
831 real width, real height,
832 Color *color)
834 draw_arc(self, center, width, height, 0.0, 360.0, color);
837 static void
838 fill_ellipse(DiaRenderer *self,
839 Point *center,
840 real width, real height,
841 Color *color)
843 fill_arc(self, center, width, height, 0.0, 360.0, color);
846 static void
847 draw_bezier(DiaRenderer *self,
848 BezPoint *points,
849 int numpoints,
850 Color *line_color)
852 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
853 ArtVpath *vpath, *vpath_dashed;
854 ArtBpath *bpath;
855 ArtSVP *svp;
856 guint32 rgba;
857 double x,y;
858 int i;
860 rgba = color_to_rgba(renderer, line_color);
862 bpath = art_new (ArtBpath, numpoints+1);
864 for (i=0;i<numpoints;i++) {
865 switch(points[i].type) {
866 case BEZ_MOVE_TO:
867 dia_transform_coords_double(renderer->transform,
868 points[i].p1.x, points[i].p1.y,
869 &x, &y);
870 bpath[i].code = ART_MOVETO;
871 bpath[i].x3 = x;
872 bpath[i].y3 = y;
873 break;
874 case BEZ_LINE_TO:
875 dia_transform_coords_double(renderer->transform,
876 points[i].p1.x, points[i].p1.y,
877 &x, &y);
878 bpath[i].code = ART_LINETO;
879 bpath[i].x3 = x;
880 bpath[i].y3 = y;
881 break;
882 case BEZ_CURVE_TO:
883 bpath[i].code = ART_CURVETO;
884 dia_transform_coords_double(renderer->transform,
885 points[i].p1.x, points[i].p1.y,
886 &x, &y);
887 bpath[i].x1 = x;
888 bpath[i].y1 = y;
889 dia_transform_coords_double(renderer->transform,
890 points[i].p2.x, points[i].p2.y,
891 &x, &y);
892 bpath[i].x2 = x;
893 bpath[i].y2 = y;
894 dia_transform_coords_double(renderer->transform,
895 points[i].p3.x, points[i].p3.y,
896 &x, &y);
897 bpath[i].x3 = x;
898 bpath[i].y3 = y;
899 break;
902 bpath[i].code = ART_END;
903 bpath[i].x1 = 0;
904 bpath[i].y1 = 0;
906 vpath = art_bez_path_to_vec(bpath, 0.25);
907 art_free(bpath);
909 if (renderer->dash_enabled) {
910 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
911 art_free( vpath );
912 vpath = vpath_dashed;
915 svp = art_svp_vpath_stroke (vpath,
916 renderer->join_style,
917 renderer->cap_style,
918 renderer->line_width,
920 0.25);
922 art_free( vpath );
924 art_rgb_svp_alpha (svp,
925 0, 0,
926 renderer->pixel_width,
927 renderer->pixel_height,
928 rgba,
929 renderer->rgb_buffer, renderer->pixel_width*3,
930 NULL);
932 art_svp_free( svp );
935 static void
936 fill_bezier(DiaRenderer *self,
937 BezPoint *points, /* Last point must be same as first point */
938 int numpoints,
939 Color *color)
941 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
942 ArtVpath *vpath;
943 ArtBpath *bpath;
944 ArtSVP *svp;
945 guint32 rgba;
946 double x,y;
947 int i;
949 rgba = color_to_rgba(renderer, color);
951 bpath = art_new (ArtBpath, numpoints+1);
953 for (i=0;i<numpoints;i++) {
954 switch(points[i].type) {
955 case BEZ_MOVE_TO:
956 dia_transform_coords_double(renderer->transform,
957 points[i].p1.x, points[i].p1.y,
958 &x, &y);
959 bpath[i].code = ART_MOVETO;
960 bpath[i].x3 = x;
961 bpath[i].y3 = y;
962 break;
963 case BEZ_LINE_TO:
964 dia_transform_coords_double(renderer->transform,
965 points[i].p1.x, points[i].p1.y,
966 &x, &y);
967 bpath[i].code = ART_LINETO;
968 bpath[i].x3 = x;
969 bpath[i].y3 = y;
970 break;
971 case BEZ_CURVE_TO:
972 bpath[i].code = ART_CURVETO;
973 dia_transform_coords_double(renderer->transform,
974 points[i].p1.x, points[i].p1.y,
975 &x, &y);
976 bpath[i].x1 = x;
977 bpath[i].y1 = y;
978 dia_transform_coords_double(renderer->transform,
979 points[i].p2.x, points[i].p2.y,
980 &x, &y);
981 bpath[i].x2 = x;
982 bpath[i].y2 = y;
983 dia_transform_coords_double(renderer->transform,
984 points[i].p3.x, points[i].p3.y,
985 &x, &y);
986 bpath[i].x3 = x;
987 bpath[i].y3 = y;
988 break;
991 bpath[i].code = ART_END;
992 bpath[i].x1 = 0;
993 bpath[i].y1 = 0;
995 vpath = art_bez_path_to_vec(bpath, 0.25);
996 art_free(bpath);
998 svp = art_svp_from_vpath (vpath);
1000 art_free( vpath );
1002 art_rgb_svp_alpha (svp,
1003 0, 0,
1004 renderer->pixel_width,
1005 renderer->pixel_height,
1006 rgba,
1007 renderer->rgb_buffer, renderer->pixel_width*3,
1008 NULL);
1010 art_svp_free( svp );
1014 /* Draw a highlighted version of a string.
1016 static void
1017 draw_highlighted_string(DiaLibartRenderer *renderer,
1018 PangoLayout *layout,
1019 real x, real y,
1020 guint32 rgba)
1022 ArtVpath *vpath;
1023 ArtSVP *svp;
1024 int width, height;
1025 double top, bottom, left, right;
1027 pango_layout_get_pixel_size(layout, &width, &height);
1028 dia_transform_coords_double(renderer->transform,
1029 x, y, &left, &top);
1030 left -= 3;
1031 right = left+width+6;
1032 bottom = top+height;
1034 if ((left>right) || (top>bottom))
1035 return;
1037 vpath = art_new (ArtVpath, 6);
1039 vpath[0].code = ART_MOVETO;
1040 vpath[0].x = left;
1041 vpath[0].y = top;
1042 vpath[1].code = ART_LINETO;
1043 vpath[1].x = right;
1044 vpath[1].y = top;
1045 vpath[2].code = ART_LINETO;
1046 vpath[2].x = right;
1047 vpath[2].y = bottom;
1048 vpath[3].code = ART_LINETO;
1049 vpath[3].x = left;
1050 vpath[3].y = bottom;
1051 vpath[4].code = ART_LINETO;
1052 vpath[4].x = left;
1053 vpath[4].y = top;
1054 vpath[5].code = ART_END;
1055 vpath[5].x = 0;
1056 vpath[5].y = 0;
1058 svp = art_svp_from_vpath (vpath);
1060 art_free( vpath );
1062 art_rgb_svp_alpha (svp,
1063 0, 0,
1064 renderer->pixel_width,
1065 renderer->pixel_height,
1066 rgba,
1067 renderer->rgb_buffer, renderer->pixel_width*3,
1068 NULL);
1070 art_svp_free( svp );
1073 static void
1074 draw_text_line(DiaRenderer *self, TextLine *text_line,
1075 Point *pos, Color *color)
1077 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
1078 /* Not working with Pango */
1079 guint8 *bitmap = NULL;
1080 double x,y;
1081 Point start_pos;
1082 PangoLayout* layout;
1083 int width, height;
1084 int i, j;
1085 double affine[6], tmpaffine[6];
1086 guint32 rgba;
1087 int rowstride;
1088 gchar *text = text_line_get_string(text_line);
1089 real scale = dia_transform_length(renderer->transform, 1.0);
1091 point_copy(&start_pos,pos);
1093 rgba = color_to_rgba(renderer, color);
1095 /* Something's screwed up with the zooming for fonts.
1096 It's like it zooms double. Yet removing some of the zooms
1097 don't seem to work, either.
1100 old_zoom = ddisp->zoom_factor;
1101 ddisp->zoom_factor = ddisp->zoom_factor;
1104 start_pos.y -= text_line_get_ascent(text_line);
1106 dia_transform_coords_double(renderer->transform,
1107 start_pos.x, start_pos.y, &x, &y);
1109 layout = dia_font_build_layout(text, text_line->font,
1110 dia_transform_length(renderer->transform,
1111 text_line_get_height(text_line)) / 20.0);
1113 text_line_adjust_layout_line(text_line, pango_layout_get_line(layout, 0),
1114 scale/20.0);
1116 if (renderer->highlight_color != NULL) {
1117 draw_highlighted_string(renderer, layout, start_pos.x, start_pos.y, rgba);
1118 g_object_unref(G_OBJECT(layout));
1119 return;
1123 ddisp->zoom_factor = old_zoom;
1125 pango_layout_get_pixel_size(layout, &width, &height);
1126 /* Pango doesn't have a 'render to raw bits' function, so we have
1127 * to render based on what other engines are available.
1129 #define DEPTH 4
1130 #ifdef HAVE_FREETYPE
1131 /* Freetype version */
1133 FT_Bitmap ftbitmap;
1134 guint8 *graybitmap;
1136 rowstride = 32*((width+31)/31);
1138 graybitmap = (guint8*)g_new0(guint8, height*rowstride);
1140 ftbitmap.rows = height;
1141 ftbitmap.width = width;
1142 ftbitmap.pitch = rowstride;
1143 ftbitmap.buffer = graybitmap;
1144 ftbitmap.num_grays = 256;
1145 ftbitmap.pixel_mode = ft_pixel_mode_grays;
1146 ftbitmap.palette_mode = 0;
1147 ftbitmap.palette = 0;
1148 pango_ft2_render_layout(&ftbitmap, layout, 0, 0);
1149 bitmap = (guint8*)g_new0(guint8, height*rowstride*DEPTH);
1150 for (i = 0; i < height; i++) {
1151 for (j = 0; j < width; j++) {
1152 bitmap[DEPTH*(i*rowstride+j)] = color->red*255;
1153 bitmap[DEPTH*(i*rowstride+j)+1] = color->green*255;
1154 bitmap[DEPTH*(i*rowstride+j)+2] = color->blue*255;
1155 bitmap[DEPTH*(i*rowstride+j)+3] = graybitmap[i*rowstride+j];
1158 g_free(graybitmap);
1160 #else
1161 /* gdk does not like 0x0 sized (it does not make much sense with the others as well,
1162 * but they don't complain ;) */
1163 if (width * height > 0)
1165 GdkPixmap *pixmap = gdk_pixmap_new (NULL, width, height, 24);
1166 GdkGC *gc = gdk_gc_new (pixmap);
1167 GdkImage *image;
1169 rowstride = 32*((width+31)/31);
1170 #if 1 /* with 8 bit pixmap we would probably need to set the whole gray palette */
1171 gdk_gc_set_foreground (gc, &color_gdk_black);
1172 gdk_gc_set_background (gc, &color_gdk_white);
1173 gdk_draw_rectangle (GDK_DRAWABLE (pixmap), gc, TRUE, 0, 0, width, height);
1174 #endif
1175 gdk_gc_set_foreground (gc, &color_gdk_white);
1176 gdk_gc_set_background (gc, &color_gdk_black);
1177 gdk_draw_layout (GDK_DRAWABLE (pixmap), gc, 0, 0, layout);
1178 image = gdk_drawable_get_image (GDK_DRAWABLE (pixmap), 0, 0, width, height);
1179 g_object_unref (G_OBJECT (gc));
1180 g_object_unref (G_OBJECT (pixmap));
1181 bitmap = (guint8*)g_new0(guint8, height*rowstride*DEPTH);
1182 for (i = 0; i < height; i++) {
1183 for (j = 0; j < width; j++) {
1184 bitmap[DEPTH*(i*rowstride+j)] = color->red*255;
1185 bitmap[DEPTH*(i*rowstride+j)+1] = color->green*255;
1186 bitmap[DEPTH*(i*rowstride+j)+2] = color->blue*255;
1187 bitmap[DEPTH*(i*rowstride+j)+3] = gdk_image_get_pixel (image, j, i) & 0xFF;
1190 g_object_unref (G_OBJECT (image));
1192 #endif
1194 /* abuse_layout_object(layout,text); */
1196 g_object_unref(G_OBJECT(layout));
1198 art_affine_identity(affine);
1199 art_affine_translate(tmpaffine, x, y);
1200 art_affine_multiply(affine, affine, tmpaffine);
1202 if (bitmap != NULL)
1203 art_rgb_rgba_affine (renderer->rgb_buffer,
1204 0, 0,
1205 renderer->pixel_width,
1206 renderer->pixel_height,
1207 renderer->pixel_width * 3,
1208 bitmap,
1209 width,
1210 height,
1211 rowstride*DEPTH,
1212 affine,
1213 ART_FILTER_NEAREST, NULL);
1215 g_free(bitmap);
1216 #undef DEPTH
1219 static void
1220 draw_string (DiaRenderer *self,
1221 const gchar *text,
1222 Point *pos, Alignment alignment,
1223 Color *color)
1225 TextLine *text_line = text_line_new(text, self->font, self->font_height);
1226 Point realigned_pos = *pos;
1227 realigned_pos.x -= text_line_get_alignment_adjustment(text_line, alignment);
1228 draw_text_line(self, text_line, &realigned_pos, color);
1232 /* Get the width of the given text in cm */
1233 static real
1234 get_text_width(DiaRenderer *object,
1235 const gchar *text, int length)
1237 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (object);
1238 real result;
1239 TextLine *text_line;
1241 if (length != strlen(text)) {
1242 char *othertx;
1243 int ulen;
1244 /* A couple UTF8-chars: æblegrød Å  Ť Ž Ä™ ć Å„ уфхцÐ?ОПРЄ Ñ” Ò? Њ Ћ Ð? */
1245 ulen = g_utf8_offset_to_pointer(text, length)-text;
1246 if (!g_utf8_validate(text, ulen, NULL)) {
1247 g_warning ("Text at char %d not valid\n", length);
1249 othertx = g_strndup(text, ulen);
1250 text_line = text_line_new(othertx, object->font, object->font_height);
1251 } else {
1252 text_line = text_line_new(text, object->font, object->font_height);
1254 result = text_line_get_width(text_line);
1255 text_line_destroy(text_line);
1256 return result;
1260 static void
1261 draw_image(DiaRenderer *self,
1262 Point *point,
1263 real width, real height,
1264 DiaImage image)
1266 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
1268 /* Todo: Handle some kind of clipping! */
1270 if (renderer->highlight_color != NULL) {
1271 Point lr;
1272 DiaRendererClass *self_class = DIA_RENDERER_GET_CLASS (self);
1274 lr = *point;
1275 lr.x += width;
1276 lr.y += height;
1277 self_class->fill_rect(self, point, &lr, renderer->highlight_color);
1278 } else {
1279 double real_width, real_height;
1280 double x,y;
1281 int src_width, src_height;
1282 double affine[6];
1283 int rowstride;
1284 real_width = dia_transform_length(renderer->transform, width);
1285 real_height = dia_transform_length(renderer->transform, height);
1286 dia_transform_coords_double(renderer->transform,
1287 point->x, point->y, &x, &y);
1289 src_width = dia_image_width(image);
1290 src_height = dia_image_height(image);
1291 rowstride = dia_image_rowstride(image);
1293 affine[0] = real_width/(double)src_width;
1294 affine[1] = 0;
1295 affine[2] = 0;
1296 affine[3] = real_height/(double)src_height;
1297 affine[4] = x;
1298 affine[5] = y;
1300 if (dia_image_rgba_data(image)) {
1301 /* If there is an alpha channel, we can use it directly. */
1302 const guint8 *img_data = dia_image_rgba_data(image);
1303 art_rgb_rgba_affine(renderer->rgb_buffer,
1304 0, 0,
1305 renderer->pixel_width,
1306 renderer->pixel_height,
1307 renderer->pixel_width*3,
1308 img_data, src_width, src_height,
1309 rowstride,
1310 affine, ART_FILTER_NEAREST, NULL);
1311 /* Note that dia_image_rgba_data doesn't copy */
1312 } else {
1313 guint8 *img_data = dia_image_rgb_data(image);
1315 art_rgb_affine(renderer->rgb_buffer,
1316 0, 0,
1317 renderer->pixel_width,
1318 renderer->pixel_height,
1319 renderer->pixel_width*3,
1320 img_data, src_width, src_height,
1321 rowstride,
1322 affine, ART_FILTER_NEAREST, NULL);
1324 g_free(img_data);
1330 static void
1331 draw_object (DiaRenderer *renderer, DiaObject *object)
1333 if (renderer->is_interactive &&
1334 object->highlight_color != NULL) {
1335 DiaLibartRenderer *libart_rend = DIA_LIBART_RENDERER(renderer);
1336 libart_rend->highlight_color = object->highlight_color;
1337 object->ops->draw(object, renderer);
1338 libart_rend->highlight_color = NULL;
1340 object->ops->draw(object, renderer);
1343 static void
1344 renderer_init (DiaLibartRenderer *renderer, gpointer g_class)
1346 renderer->rgb_buffer = NULL;
1348 renderer->line_width = 1.0;
1349 renderer->cap_style = ART_PATH_STROKE_CAP_BUTT;
1350 renderer->join_style = ART_PATH_STROKE_JOIN_MITER;
1352 renderer->saved_line_style = LINESTYLE_SOLID;
1353 renderer->dash_enabled = 0;
1354 renderer->dash_length = 10;
1355 renderer->dot_length = 1;
1357 renderer->highlight_color = NULL;
1359 renderer->parent_instance.font = NULL;
1362 static void dia_libart_renderer_class_init (DiaLibartRendererClass *klass);
1363 static gpointer parent_class = NULL;
1365 static void
1366 renderer_finalize (GObject *object)
1368 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (object);
1370 if (renderer->rgb_buffer != NULL)
1371 g_free(renderer->rgb_buffer);
1373 G_OBJECT_CLASS (parent_class)->finalize (object);
1376 GType
1377 dia_libart_renderer_get_type (void)
1379 static GType object_type = 0;
1381 if (!object_type)
1383 static const GTypeInfo object_info =
1385 sizeof (DiaLibartRendererClass),
1386 (GBaseInitFunc) NULL,
1387 (GBaseFinalizeFunc) NULL,
1388 (GClassInitFunc) dia_libart_renderer_class_init,
1389 NULL, /* class_finalize */
1390 NULL, /* class_data */
1391 sizeof (DiaLibartRenderer),
1392 0, /* n_preallocs */
1393 (GInstanceInitFunc)renderer_init /* init */
1396 object_type = g_type_register_static (DIA_TYPE_RENDERER,
1397 "DiaLibartRenderer",
1398 &object_info, 0);
1401 return object_type;
1404 static void
1405 dia_libart_renderer_class_init (DiaLibartRendererClass *klass)
1407 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1408 DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
1410 parent_class = g_type_class_peek_parent (klass);
1412 gobject_class->finalize = renderer_finalize;
1414 /* Here we set the functions that we define for this renderer. */
1415 renderer_class->get_width_pixels = get_width_pixels;
1416 renderer_class->get_height_pixels = get_height_pixels;
1418 renderer_class->begin_render = begin_render;
1419 renderer_class->end_render = end_render;
1421 renderer_class->set_linewidth = set_linewidth;
1422 renderer_class->set_linecaps = set_linecaps;
1423 renderer_class->set_linejoin = set_linejoin;
1424 renderer_class->set_linestyle = set_linestyle;
1425 renderer_class->set_dashlength = set_dashlength;
1426 renderer_class->set_fillstyle = set_fillstyle;
1427 renderer_class->set_font = set_font;
1429 renderer_class->draw_line = draw_line;
1430 renderer_class->draw_polyline = draw_polyline;
1432 renderer_class->draw_polygon = draw_polygon;
1433 renderer_class->fill_polygon = fill_polygon;
1435 renderer_class->draw_rect = draw_rect;
1436 renderer_class->fill_rect = fill_rect;
1438 renderer_class->draw_arc = draw_arc;
1439 renderer_class->fill_arc = fill_arc;
1441 renderer_class->draw_ellipse = draw_ellipse;
1442 renderer_class->fill_ellipse = fill_ellipse;
1444 renderer_class->draw_bezier = draw_bezier;
1445 renderer_class->fill_bezier = fill_bezier;
1447 renderer_class->draw_string = draw_string;
1448 renderer_class->draw_text_line = draw_text_line;
1450 renderer_class->draw_image = draw_image;
1452 renderer_class->draw_object = draw_object;
1454 /* Interactive functions */
1455 renderer_class->get_text_width = get_text_width;
1458 #endif