More text_line happiness
[dia.git] / lib / dialibartrenderer.c
blob80a9b655c479288f9eeb22870fdd794fa2534262
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>
62 static inline guint32
63 color_to_abgr(Color *col)
65 int rgba;
67 rgba = 0x0;
68 rgba |= (guint)(0xFF*col->blue) << 16;
69 rgba |= (guint)(0xFF*col->green) << 8;
70 rgba |= (guint)(0xFF*col->red);
72 return rgba;
75 static inline guint32
76 color_to_rgba(DiaLibartRenderer *renderer, Color *col)
78 int rgba;
80 if (renderer->highlight_color != NULL) {
81 rgba = 0xFF;
82 rgba |= (guint)(0xFF*renderer->highlight_color->red) << 24;
83 rgba |= (guint)(0xFF*renderer->highlight_color->green) << 16;
84 rgba |= (guint)(0xFF*renderer->highlight_color->blue) << 8;
85 } else {
86 rgba = 0xFF;
87 rgba |= (guint)(0xFF*col->red) << 24;
88 rgba |= (guint)(0xFF*col->green) << 16;
89 rgba |= (guint)(0xFF*col->blue) << 8;
92 return rgba;
95 static int
96 get_width_pixels (DiaRenderer *self)
98 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
100 return renderer->pixel_width;
103 static int
104 get_height_pixels (DiaRenderer *self)
106 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
108 return renderer->pixel_height;
111 static void
112 begin_render(DiaRenderer *self)
114 #ifdef HAVE_FREETYPE
115 /* pango_ft2_get_context API docs :
116 * ... Use of this function is discouraged, ...
118 * I've tried using the proper font_map stuff, but it kills font size:(
120 dia_font_push_context(pango_ft2_get_context(75, 75));
121 # define FONT_SCALE (1.0)
122 #elif defined G_OS_WIN32
123 dia_font_push_context(gdk_pango_context_get());
124 /* I shall never claim again to have understood Dias/Pangos font size
125 * relations ;). This was (1.0/22.0) but nowadays 1.0 seems to be
126 * fine with Pango/win32, too. --hb
128 # define FONT_SCALE (1.0)
129 #else
130 dia_font_push_context (gdk_pango_context_get ());
131 # define FONT_SCALE (0.8)
132 #endif
135 static void
136 end_render(DiaRenderer *self)
138 dia_font_pop_context();
143 static void
144 set_linewidth(DiaRenderer *self, real linewidth)
145 { /* 0 == hairline **/
146 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
148 if (renderer->highlight_color != NULL) {
149 /* 6 pixels wide -> 3 pixels beyond normal obj */
150 real border = dia_untransform_length(renderer->transform, 6);
151 linewidth += border;
153 renderer->line_width =
154 dia_transform_length(renderer->transform, linewidth);
155 if (renderer->line_width<=0.5)
156 renderer->line_width = 0.5; /* Minimum 0.5 pixel. */
159 static void
160 set_linecaps(DiaRenderer *self, LineCaps mode)
162 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
164 if (renderer->highlight_color != NULL) {
165 /* Can't tell that this does anything:( -LC */
166 renderer->cap_style = ART_PATH_STROKE_CAP_ROUND;
167 } else {
168 switch(mode) {
169 case LINECAPS_BUTT:
170 renderer->cap_style = ART_PATH_STROKE_CAP_BUTT;
171 break;
172 case LINECAPS_ROUND:
173 renderer->cap_style = ART_PATH_STROKE_CAP_ROUND;
174 break;
175 case LINECAPS_PROJECTING:
176 renderer->cap_style = ART_PATH_STROKE_CAP_SQUARE;
177 break;
182 static void
183 set_linejoin(DiaRenderer *self, LineJoin mode)
185 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
187 if (renderer->highlight_color != NULL) {
188 /* Can't tell that this does anything:( -LC */
189 renderer->join_style = ART_PATH_STROKE_JOIN_ROUND;
190 } else {
191 switch(mode) {
192 case LINEJOIN_MITER:
193 renderer->join_style = ART_PATH_STROKE_JOIN_MITER;
194 break;
195 case LINEJOIN_ROUND:
196 renderer->join_style = ART_PATH_STROKE_JOIN_ROUND;
197 break;
198 case LINEJOIN_BEVEL:
199 renderer->join_style = ART_PATH_STROKE_JOIN_BEVEL;
200 break;
205 static void
206 set_linestyle(DiaRenderer *self, LineStyle mode)
208 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
209 static double dash[10];
210 double hole_width;
212 renderer->saved_line_style = mode;
213 switch(mode) {
214 case LINESTYLE_SOLID:
215 renderer->dash_enabled = 0;
216 break;
217 case LINESTYLE_DASHED:
218 renderer->dash_enabled = 1;
219 renderer->dash.offset = 0.0;
220 renderer->dash.n_dash = 2;
221 renderer->dash.dash = dash;
222 dash[0] = renderer->dash_length;
223 dash[1] = renderer->dash_length;
224 break;
225 case LINESTYLE_DASH_DOT:
226 renderer->dash_enabled = 1;
227 renderer->dash.offset = 0.0;
228 renderer->dash.n_dash = 4;
229 renderer->dash.dash = dash;
230 hole_width = (renderer->dash_length - renderer->dot_length) / 2.0;
231 if (hole_width<1.0)
232 hole_width = 1.0;
233 dash[0] = renderer->dash_length;
234 dash[1] = hole_width;
235 dash[2] = renderer->dot_length;
236 dash[3] = hole_width;
237 break;
238 case LINESTYLE_DASH_DOT_DOT:
239 renderer->dash_enabled = 1;
240 renderer->dash.offset = 0.0;
241 renderer->dash.n_dash = 6;
242 renderer->dash.dash = dash;
243 hole_width = (renderer->dash_length - 2*renderer->dot_length) / 3;
244 if (hole_width<1.0)
245 hole_width = 1.0;
246 dash[0] = renderer->dash_length;
247 dash[1] = hole_width;
248 dash[2] = renderer->dot_length;
249 dash[3] = hole_width;
250 dash[4] = renderer->dot_length;
251 dash[5] = hole_width;
252 break;
253 case LINESTYLE_DOTTED:
254 renderer->dash_enabled = 1;
255 renderer->dash.offset = 0.0;
256 renderer->dash.n_dash = 2;
257 renderer->dash.dash = dash;
258 dash[0] = renderer->dot_length;
259 dash[1] = renderer->dot_length;
260 break;
264 static void
265 set_dashlength(DiaRenderer *self, real length)
266 { /* dot = 10% of len */
267 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
268 real ddisp_len;
270 ddisp_len =
271 dia_transform_length(renderer->transform, length);
273 renderer->dash_length = ddisp_len;
274 renderer->dot_length = ddisp_len*0.1;
276 if (renderer->dash_length<1.0)
277 renderer->dash_length = 1.0;
278 if (renderer->dash_length>255.0)
279 renderer->dash_length = 255.0;
280 if (renderer->dot_length<1.0)
281 renderer->dot_length = 1.0;
282 if (renderer->dot_length>255.0)
283 renderer->dot_length = 255.0;
284 set_linestyle(self, renderer->saved_line_style);
287 static void
288 set_fillstyle(DiaRenderer *self, FillStyle mode)
290 switch(mode) {
291 case FILLSTYLE_SOLID:
292 break;
293 default:
294 message_error(_("gdk_renderer: Unsupported fill mode specified!\n"));
298 static void
299 set_font(DiaRenderer *self, DiaFont *font, real height)
301 self->font_height = height * FONT_SCALE;
303 dia_font_ref(font);
304 if (self->font)
305 dia_font_unref(self->font);
306 self->font = font;
309 static void
310 draw_line(DiaRenderer *self,
311 Point *start, Point *end,
312 Color *line_color)
314 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
315 ArtVpath *vpath, *vpath_dashed;
316 ArtSVP *svp;
317 guint32 rgba;
318 double x,y;
320 rgba = color_to_rgba(renderer, line_color);
322 vpath = art_new (ArtVpath, 3);
324 dia_transform_coords_double(renderer->transform, start->x, start->y, &x, &y);
325 vpath[0].code = ART_MOVETO;
326 vpath[0].x = x;
327 vpath[0].y = y;
329 dia_transform_coords_double(renderer->transform, end->x, end->y, &x, &y);
330 vpath[1].code = ART_LINETO;
331 vpath[1].x = x;
332 vpath[1].y = y;
334 vpath[2].code = ART_END;
335 vpath[2].x = 0;
336 vpath[2].y = 0;
338 if (renderer->dash_enabled) {
339 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
340 art_free( vpath );
341 vpath = vpath_dashed;
344 svp = art_svp_vpath_stroke (vpath,
345 renderer->join_style,
346 renderer->cap_style,
347 renderer->line_width,
349 0.25);
351 art_free( vpath );
353 art_rgb_svp_alpha (svp,
354 0, 0,
355 renderer->pixel_width,
356 renderer->pixel_height,
357 rgba,
358 renderer->rgb_buffer, renderer->pixel_width*3,
359 NULL);
361 art_svp_free( svp );
364 static void
365 draw_polyline(DiaRenderer *self,
366 Point *points, int num_points,
367 Color *line_color)
369 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
370 ArtVpath *vpath, *vpath_dashed;
371 ArtSVP *svp;
372 guint32 rgba;
373 double x,y;
374 int i;
376 rgba = color_to_rgba(renderer, line_color);
378 vpath = art_new (ArtVpath, num_points+1);
380 for (i=0;i<num_points;i++) {
381 dia_transform_coords_double(renderer->transform,
382 points[i].x, points[i].y,
383 &x, &y);
384 vpath[i].code = (i==0)?ART_MOVETO:ART_LINETO;
385 vpath[i].x = x;
386 vpath[i].y = y;
388 vpath[i].code = ART_END;
389 vpath[i].x = 0;
390 vpath[i].y = 0;
392 if (renderer->dash_enabled) {
393 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
394 art_free( vpath );
395 vpath = vpath_dashed;
398 svp = art_svp_vpath_stroke (vpath,
399 renderer->join_style,
400 renderer->cap_style,
401 renderer->line_width,
403 0.25);
405 art_free( vpath );
407 art_rgb_svp_alpha (svp,
408 0, 0,
409 renderer->pixel_width,
410 renderer->pixel_height,
411 rgba,
412 renderer->rgb_buffer, renderer->pixel_width*3,
413 NULL);
415 art_svp_free( svp );
418 static void
419 draw_polygon(DiaRenderer *self,
420 Point *points, int num_points,
421 Color *line_color)
423 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
424 ArtVpath *vpath, *vpath_dashed;
425 ArtSVP *svp;
426 guint32 rgba;
427 double x,y;
428 int i;
430 rgba = color_to_rgba(renderer, line_color);
432 vpath = art_new (ArtVpath, num_points+2);
434 for (i=0;i<num_points;i++) {
435 dia_transform_coords_double(renderer->transform,
436 points[i].x, points[i].y,
437 &x, &y);
438 vpath[i].code = (i==0)?ART_MOVETO:ART_LINETO;
439 vpath[i].x = x;
440 vpath[i].y = y;
442 dia_transform_coords_double(renderer->transform,
443 points[0].x, points[0].y,
444 &x, &y);
445 vpath[i].code = ART_LINETO;
446 vpath[i].x = x;
447 vpath[i].y = y;
448 vpath[i+1].code = ART_END;
449 vpath[i+1].x = 0;
450 vpath[i+1].y = 0;
452 if (renderer->dash_enabled) {
453 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
454 art_free( vpath );
455 vpath = vpath_dashed;
458 svp = art_svp_vpath_stroke (vpath,
459 renderer->join_style,
460 renderer->cap_style,
461 renderer->line_width,
463 0.25);
465 art_free( vpath );
467 art_rgb_svp_alpha (svp,
468 0, 0,
469 renderer->pixel_width,
470 renderer->pixel_height,
471 rgba,
472 renderer->rgb_buffer, renderer->pixel_width*3,
473 NULL);
475 art_svp_free( svp );
478 static void
479 fill_polygon(DiaRenderer *self,
480 Point *points, int num_points,
481 Color *color)
483 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
484 ArtVpath *vpath;
485 ArtSVP *svp;
486 guint32 rgba;
487 double x,y;
488 int i;
490 rgba = color_to_rgba(renderer, color);
492 vpath = art_new (ArtVpath, num_points+2);
494 for (i=0;i<num_points;i++) {
495 dia_transform_coords_double(renderer->transform,
496 points[i].x, points[i].y,
497 &x, &y);
498 vpath[i].code = (i==0)?ART_MOVETO:ART_LINETO;
499 vpath[i].x = x;
500 vpath[i].y = y;
502 dia_transform_coords_double(renderer->transform,
503 points[0].x, points[0].y,
504 &x, &y);
505 vpath[i].code = ART_LINETO;
506 vpath[i].x = x;
507 vpath[i].y = y;
508 vpath[i+1].code = ART_END;
509 vpath[i+1].x = 0;
510 vpath[i+1].y = 0;
512 svp = art_svp_from_vpath (vpath);
514 art_free( vpath );
516 art_rgb_svp_alpha (svp,
517 0, 0,
518 renderer->pixel_width,
519 renderer->pixel_height,
520 rgba,
521 renderer->rgb_buffer, renderer->pixel_width*3,
522 NULL);
524 art_svp_free( svp );
527 static void
528 draw_rect(DiaRenderer *self,
529 Point *ul_corner, Point *lr_corner,
530 Color *color)
532 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
533 ArtVpath *vpath, *vpath_dashed;
534 ArtSVP *svp;
535 guint32 rgba;
536 double top, bottom, left, right;
538 dia_transform_coords_double(renderer->transform,
539 ul_corner->x, ul_corner->y, &left, &top);
540 dia_transform_coords_double(renderer->transform,
541 lr_corner->x, lr_corner->y, &right, &bottom);
543 if ((left>right) || (top>bottom))
544 return;
546 rgba = color_to_rgba(renderer, color);
548 vpath = art_new (ArtVpath, 6);
550 vpath[0].code = ART_MOVETO;
551 vpath[0].x = left;
552 vpath[0].y = top;
553 vpath[1].code = ART_LINETO;
554 vpath[1].x = right;
555 vpath[1].y = top;
556 vpath[2].code = ART_LINETO;
557 vpath[2].x = right;
558 vpath[2].y = bottom;
559 vpath[3].code = ART_LINETO;
560 vpath[3].x = left;
561 vpath[3].y = bottom;
562 vpath[4].code = ART_LINETO;
563 vpath[4].x = left;
564 vpath[4].y = top;
565 vpath[5].code = ART_END;
566 vpath[5].x = 0;
567 vpath[5].y = 0;
569 if (renderer->dash_enabled) {
570 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
571 art_free( vpath );
572 vpath = vpath_dashed;
575 svp = art_svp_vpath_stroke (vpath,
576 renderer->join_style,
577 renderer->cap_style,
578 renderer->line_width,
580 0.25);
582 art_free( vpath );
584 art_rgb_svp_alpha (svp,
585 0, 0,
586 renderer->pixel_width,
587 renderer->pixel_height,
588 rgba,
589 renderer->rgb_buffer, renderer->pixel_width*3,
590 NULL);
592 art_svp_free( svp );
595 static void
596 fill_rect(DiaRenderer *self,
597 Point *ul_corner, Point *lr_corner,
598 Color *color)
600 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
601 ArtVpath *vpath;
602 ArtSVP *svp;
603 guint32 rgba;
604 double top, bottom, left, right;
606 dia_transform_coords_double(renderer->transform,
607 ul_corner->x, ul_corner->y, &left, &top);
608 dia_transform_coords_double(renderer->transform,
609 lr_corner->x, lr_corner->y, &right, &bottom);
611 if ((left>right) || (top>bottom))
612 return;
614 rgba = color_to_rgba(renderer, color);
616 vpath = art_new (ArtVpath, 6);
618 vpath[0].code = ART_MOVETO;
619 vpath[0].x = left;
620 vpath[0].y = top;
621 vpath[1].code = ART_LINETO;
622 vpath[1].x = right;
623 vpath[1].y = top;
624 vpath[2].code = ART_LINETO;
625 vpath[2].x = right;
626 vpath[2].y = bottom;
627 vpath[3].code = ART_LINETO;
628 vpath[3].x = left;
629 vpath[3].y = bottom;
630 vpath[4].code = ART_LINETO;
631 vpath[4].x = left;
632 vpath[4].y = top;
633 vpath[5].code = ART_END;
634 vpath[5].x = 0;
635 vpath[5].y = 0;
637 svp = art_svp_from_vpath (vpath);
639 art_free( vpath );
641 art_rgb_svp_alpha (svp,
642 0, 0,
643 renderer->pixel_width,
644 renderer->pixel_height,
645 rgba,
646 renderer->rgb_buffer, renderer->pixel_width*3,
647 NULL);
649 art_svp_free( svp );
652 static void
653 draw_arc(DiaRenderer *self,
654 Point *center,
655 real width, real height,
656 real angle1, real angle2,
657 Color *line_color)
659 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
660 ArtVpath *vpath, *vpath_dashed;
661 ArtSVP *svp;
662 guint32 rgba;
663 real dangle;
664 real circ;
665 double x,y;
666 int num_points;
667 double theta, dtheta;
668 int i;
670 width = dia_transform_length(renderer->transform, width);
671 height = dia_transform_length(renderer->transform, height);
672 dia_transform_coords_double(renderer->transform,
673 center->x, center->y, &x, &y);
675 if ((width<0.0) || (height<0.0))
676 return;
678 dangle = angle2-angle1;
679 if (dangle<0)
680 dangle += 360.0;
682 /* Over-approximate the circumference */
683 if (width>height)
684 circ = M_PI*width;
685 else
686 circ = M_PI*height;
688 circ *= dangle/360.0;
690 #define LEN_PER_SEGMENT 3.0
692 num_points = circ/LEN_PER_SEGMENT;
693 if (num_points<5) /* Don't be too coarse */
694 num_points = 5;
696 rgba = color_to_rgba(renderer, line_color);
698 vpath = art_new (ArtVpath, num_points+1);
700 theta = M_PI*angle1/180.0;
701 dtheta = (M_PI*dangle/180.0)/(num_points-1);
702 for (i=0;i<num_points;i++) {
703 vpath[i].code = (i==0)?ART_MOVETO:ART_LINETO;
704 vpath[i].x = x + width/2.0*cos(theta);
705 vpath[i].y = y - height/2.0*sin(theta);
706 theta += dtheta;
708 vpath[i].code = ART_END;
709 vpath[i].x = 0;
710 vpath[i].y = 0;
712 if (renderer->dash_enabled) {
713 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
714 art_free( vpath );
715 vpath = vpath_dashed;
718 svp = art_svp_vpath_stroke (vpath,
719 renderer->join_style,
720 renderer->cap_style,
721 renderer->line_width,
723 0.25);
725 art_free( vpath );
727 art_rgb_svp_alpha (svp,
728 0, 0,
729 renderer->pixel_width,
730 renderer->pixel_height,
731 rgba,
732 renderer->rgb_buffer, renderer->pixel_width*3,
733 NULL);
735 art_svp_free( svp );
738 static void
739 fill_arc(DiaRenderer *self,
740 Point *center,
741 real width, real height,
742 real angle1, real angle2,
743 Color *color)
745 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
746 ArtVpath *vpath;
747 ArtSVP *svp;
748 guint32 rgba;
749 real dangle;
750 real circ;
751 double x,y;
752 int num_points;
753 double theta, dtheta;
754 int i;
756 width = dia_transform_length(renderer->transform, width);
757 height = dia_transform_length(renderer->transform, height);
758 dia_transform_coords_double(renderer->transform,
759 center->x, center->y, &x, &y);
761 if ((width<0.0) || (height<0.0))
762 return;
764 dangle = angle2-angle1;
765 if (dangle<0)
766 dangle += 360.0;
768 /* Over-approximate the circumference */
769 if (width>height)
770 circ = M_PI*width;
771 else
772 circ = M_PI*height;
774 circ *= dangle/360.0;
776 #define LEN_PER_SEGMENT 3.0
778 num_points = circ/LEN_PER_SEGMENT;
779 if (num_points<5) /* Don't be too coarse */
780 num_points = 5;
782 rgba = color_to_rgba(renderer, color);
784 vpath = art_new (ArtVpath, num_points+2+1);
786 vpath[0].code = ART_MOVETO;
787 vpath[0].x = x;
788 vpath[0].y = y;
789 theta = M_PI*angle1/180.0;
790 dtheta = (M_PI*dangle/180.0)/(num_points-1);
791 for (i=0;i<num_points;i++) {
792 vpath[i+1].code = ART_LINETO;
793 vpath[i+1].x = x + width/2.0*cos(theta);
794 vpath[i+1].y = y - height/2.0*sin(theta);
795 theta += dtheta;
797 vpath[i+1].code = ART_LINETO;
798 vpath[i+1].x = x;
799 vpath[i+1].y = y;
800 vpath[i+2].code = ART_END;
801 vpath[i+2].x = 0;
802 vpath[i+2].y = 0;
804 svp = art_svp_from_vpath (vpath);
806 art_free( vpath );
808 art_rgb_svp_alpha (svp,
809 0, 0,
810 renderer->pixel_width,
811 renderer->pixel_height,
812 rgba,
813 renderer->rgb_buffer, renderer->pixel_width*3,
814 NULL);
816 art_svp_free( svp );
819 static void
820 draw_ellipse(DiaRenderer *self,
821 Point *center,
822 real width, real height,
823 Color *color)
825 draw_arc(self, center, width, height, 0.0, 360.0, color);
828 static void
829 fill_ellipse(DiaRenderer *self,
830 Point *center,
831 real width, real height,
832 Color *color)
834 fill_arc(self, center, width, height, 0.0, 360.0, color);
837 static void
838 draw_bezier(DiaRenderer *self,
839 BezPoint *points,
840 int numpoints,
841 Color *line_color)
843 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
844 ArtVpath *vpath, *vpath_dashed;
845 ArtBpath *bpath;
846 ArtSVP *svp;
847 guint32 rgba;
848 double x,y;
849 int i;
851 rgba = color_to_rgba(renderer, line_color);
853 bpath = art_new (ArtBpath, numpoints+1);
855 for (i=0;i<numpoints;i++) {
856 switch(points[i].type) {
857 case BEZ_MOVE_TO:
858 dia_transform_coords_double(renderer->transform,
859 points[i].p1.x, points[i].p1.y,
860 &x, &y);
861 bpath[i].code = ART_MOVETO;
862 bpath[i].x3 = x;
863 bpath[i].y3 = y;
864 break;
865 case BEZ_LINE_TO:
866 dia_transform_coords_double(renderer->transform,
867 points[i].p1.x, points[i].p1.y,
868 &x, &y);
869 bpath[i].code = ART_LINETO;
870 bpath[i].x3 = x;
871 bpath[i].y3 = y;
872 break;
873 case BEZ_CURVE_TO:
874 bpath[i].code = ART_CURVETO;
875 dia_transform_coords_double(renderer->transform,
876 points[i].p1.x, points[i].p1.y,
877 &x, &y);
878 bpath[i].x1 = x;
879 bpath[i].y1 = y;
880 dia_transform_coords_double(renderer->transform,
881 points[i].p2.x, points[i].p2.y,
882 &x, &y);
883 bpath[i].x2 = x;
884 bpath[i].y2 = y;
885 dia_transform_coords_double(renderer->transform,
886 points[i].p3.x, points[i].p3.y,
887 &x, &y);
888 bpath[i].x3 = x;
889 bpath[i].y3 = y;
890 break;
893 bpath[i].code = ART_END;
894 bpath[i].x1 = 0;
895 bpath[i].y1 = 0;
897 vpath = art_bez_path_to_vec(bpath, 0.25);
898 art_free(bpath);
900 if (renderer->dash_enabled) {
901 vpath_dashed = art_vpath_dash(vpath, &renderer->dash);
902 art_free( vpath );
903 vpath = vpath_dashed;
906 svp = art_svp_vpath_stroke (vpath,
907 renderer->join_style,
908 renderer->cap_style,
909 renderer->line_width,
911 0.25);
913 art_free( vpath );
915 art_rgb_svp_alpha (svp,
916 0, 0,
917 renderer->pixel_width,
918 renderer->pixel_height,
919 rgba,
920 renderer->rgb_buffer, renderer->pixel_width*3,
921 NULL);
923 art_svp_free( svp );
926 static void
927 fill_bezier(DiaRenderer *self,
928 BezPoint *points, /* Last point must be same as first point */
929 int numpoints,
930 Color *color)
932 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
933 ArtVpath *vpath;
934 ArtBpath *bpath;
935 ArtSVP *svp;
936 guint32 rgba;
937 double x,y;
938 int i;
940 rgba = color_to_rgba(renderer, color);
942 bpath = art_new (ArtBpath, numpoints+1);
944 for (i=0;i<numpoints;i++) {
945 switch(points[i].type) {
946 case BEZ_MOVE_TO:
947 dia_transform_coords_double(renderer->transform,
948 points[i].p1.x, points[i].p1.y,
949 &x, &y);
950 bpath[i].code = ART_MOVETO;
951 bpath[i].x3 = x;
952 bpath[i].y3 = y;
953 break;
954 case BEZ_LINE_TO:
955 dia_transform_coords_double(renderer->transform,
956 points[i].p1.x, points[i].p1.y,
957 &x, &y);
958 bpath[i].code = ART_LINETO;
959 bpath[i].x3 = x;
960 bpath[i].y3 = y;
961 break;
962 case BEZ_CURVE_TO:
963 bpath[i].code = ART_CURVETO;
964 dia_transform_coords_double(renderer->transform,
965 points[i].p1.x, points[i].p1.y,
966 &x, &y);
967 bpath[i].x1 = x;
968 bpath[i].y1 = y;
969 dia_transform_coords_double(renderer->transform,
970 points[i].p2.x, points[i].p2.y,
971 &x, &y);
972 bpath[i].x2 = x;
973 bpath[i].y2 = y;
974 dia_transform_coords_double(renderer->transform,
975 points[i].p3.x, points[i].p3.y,
976 &x, &y);
977 bpath[i].x3 = x;
978 bpath[i].y3 = y;
979 break;
982 bpath[i].code = ART_END;
983 bpath[i].x1 = 0;
984 bpath[i].y1 = 0;
986 vpath = art_bez_path_to_vec(bpath, 0.25);
987 art_free(bpath);
989 svp = art_svp_from_vpath (vpath);
991 art_free( vpath );
993 art_rgb_svp_alpha (svp,
994 0, 0,
995 renderer->pixel_width,
996 renderer->pixel_height,
997 rgba,
998 renderer->rgb_buffer, renderer->pixel_width*3,
999 NULL);
1001 art_svp_free( svp );
1005 /* Draw a highlighted version of a string.
1007 static void
1008 draw_highlighted_string(DiaLibartRenderer *renderer,
1009 PangoLayout *layout,
1010 real x, real y,
1011 guint32 rgba)
1013 ArtVpath *vpath;
1014 ArtSVP *svp;
1015 int width, height;
1016 double top, bottom, left, right;
1018 pango_layout_get_pixel_size(layout, &width, &height);
1019 dia_transform_coords_double(renderer->transform,
1020 x, y, &left, &top);
1021 left -= 3;
1022 right = left+width+6;
1023 bottom = top+height;
1025 if ((left>right) || (top>bottom))
1026 return;
1028 vpath = art_new (ArtVpath, 6);
1030 vpath[0].code = ART_MOVETO;
1031 vpath[0].x = left;
1032 vpath[0].y = top;
1033 vpath[1].code = ART_LINETO;
1034 vpath[1].x = right;
1035 vpath[1].y = top;
1036 vpath[2].code = ART_LINETO;
1037 vpath[2].x = right;
1038 vpath[2].y = bottom;
1039 vpath[3].code = ART_LINETO;
1040 vpath[3].x = left;
1041 vpath[3].y = bottom;
1042 vpath[4].code = ART_LINETO;
1043 vpath[4].x = left;
1044 vpath[4].y = top;
1045 vpath[5].code = ART_END;
1046 vpath[5].x = 0;
1047 vpath[5].y = 0;
1049 svp = art_svp_from_vpath (vpath);
1051 art_free( vpath );
1053 art_rgb_svp_alpha (svp,
1054 0, 0,
1055 renderer->pixel_width,
1056 renderer->pixel_height,
1057 rgba,
1058 renderer->rgb_buffer, renderer->pixel_width*3,
1059 NULL);
1061 art_svp_free( svp );
1064 static void
1065 draw_text_line(DiaRenderer *self, TextLine *text_line,
1066 Point *pos, Color *color)
1068 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
1069 /* Not working with Pango */
1070 guint8 *bitmap = NULL;
1071 double x,y;
1072 Point start_pos;
1073 PangoLayout* layout;
1074 int width, height;
1075 int i, j;
1076 double affine[6], tmpaffine[6];
1077 guint32 rgba;
1078 int rowstride;
1079 gchar *text = text_line_get_string(text_line);
1080 real scale = dia_transform_length(renderer->transform, 1.0);
1082 point_copy(&start_pos,pos);
1084 rgba = color_to_rgba(renderer, color);
1086 /* Something's screwed up with the zooming for fonts.
1087 It's like it zooms double. Yet removing some of the zooms
1088 don't seem to work, either.
1091 old_zoom = ddisp->zoom_factor;
1092 ddisp->zoom_factor = ddisp->zoom_factor;
1095 start_pos.y -= text_line_get_ascent(text_line);
1097 dia_transform_coords_double(renderer->transform,
1098 start_pos.x, start_pos.y, &x, &y);
1100 layout = dia_font_build_layout(text, text_line->font,
1101 dia_transform_length(renderer->transform,
1102 text_line_get_height(text_line)) / 20.0);
1104 text_line_adjust_layout_line(text_line, pango_layout_get_line(layout, 0),
1105 scale/20.0);
1107 if (renderer->highlight_color != NULL) {
1108 draw_highlighted_string(renderer, layout, start_pos.x, start_pos.y, rgba);
1109 g_object_unref(G_OBJECT(layout));
1110 return;
1114 ddisp->zoom_factor = old_zoom;
1116 pango_layout_get_pixel_size(layout, &width, &height);
1117 /* Pango doesn't have a 'render to raw bits' function, so we have
1118 * to render based on what other engines are available.
1120 #define DEPTH 4
1121 #ifdef HAVE_FREETYPE
1122 /* Freetype version */
1124 FT_Bitmap ftbitmap;
1125 guint8 *graybitmap;
1127 rowstride = 32*((width+31)/31);
1129 graybitmap = (guint8*)g_new0(guint8, height*rowstride);
1131 ftbitmap.rows = height;
1132 ftbitmap.width = width;
1133 ftbitmap.pitch = rowstride;
1134 ftbitmap.buffer = graybitmap;
1135 ftbitmap.num_grays = 256;
1136 ftbitmap.pixel_mode = ft_pixel_mode_grays;
1137 ftbitmap.palette_mode = 0;
1138 ftbitmap.palette = 0;
1139 pango_ft2_render_layout(&ftbitmap, layout, 0, 0);
1140 bitmap = (guint8*)g_new0(guint8, height*rowstride*DEPTH);
1141 for (i = 0; i < height; i++) {
1142 for (j = 0; j < width; j++) {
1143 bitmap[DEPTH*(i*rowstride+j)] = color->red*255;
1144 bitmap[DEPTH*(i*rowstride+j)+1] = color->green*255;
1145 bitmap[DEPTH*(i*rowstride+j)+2] = color->blue*255;
1146 bitmap[DEPTH*(i*rowstride+j)+3] = graybitmap[i*rowstride+j];
1149 g_free(graybitmap);
1151 #else
1152 /* gdk does not like 0x0 sized (it does not make much sense with the others as well,
1153 * but they don't complain ;) */
1154 if (width * height > 0)
1156 GdkPixmap *pixmap = gdk_pixmap_new (NULL, width, height, 24);
1157 GdkGC *gc = gdk_gc_new (pixmap);
1158 GdkImage *image;
1160 rowstride = 32*((width+31)/31);
1161 #if 1 /* with 8 bit pixmap we would probably need to set the whole gray palette */
1162 gdk_gc_set_foreground (gc, &color_gdk_black);
1163 gdk_gc_set_background (gc, &color_gdk_white);
1164 gdk_draw_rectangle (GDK_DRAWABLE (pixmap), gc, TRUE, 0, 0, width, height);
1165 #endif
1166 gdk_gc_set_foreground (gc, &color_gdk_white);
1167 gdk_gc_set_background (gc, &color_gdk_black);
1168 gdk_draw_layout (GDK_DRAWABLE (pixmap), gc, 0, 0, layout);
1169 image = gdk_drawable_get_image (GDK_DRAWABLE (pixmap), 0, 0, width, height);
1170 g_object_unref (G_OBJECT (gc));
1171 g_object_unref (G_OBJECT (pixmap));
1172 bitmap = (guint8*)g_new0(guint8, height*rowstride*DEPTH);
1173 for (i = 0; i < height; i++) {
1174 for (j = 0; j < width; j++) {
1175 bitmap[DEPTH*(i*rowstride+j)] = color->red*255;
1176 bitmap[DEPTH*(i*rowstride+j)+1] = color->green*255;
1177 bitmap[DEPTH*(i*rowstride+j)+2] = color->blue*255;
1178 bitmap[DEPTH*(i*rowstride+j)+3] = gdk_image_get_pixel (image, j, i) & 0xFF;
1181 g_object_unref (G_OBJECT (image));
1183 #endif
1185 /* abuse_layout_object(layout,text); */
1187 g_object_unref(G_OBJECT(layout));
1189 art_affine_identity(affine);
1190 art_affine_translate(tmpaffine, x, y);
1191 art_affine_multiply(affine, affine, tmpaffine);
1193 if (bitmap != NULL)
1194 art_rgb_rgba_affine (renderer->rgb_buffer,
1195 0, 0,
1196 renderer->pixel_width,
1197 renderer->pixel_height,
1198 renderer->pixel_width * 3,
1199 bitmap,
1200 width,
1201 height,
1202 rowstride*DEPTH,
1203 affine,
1204 ART_FILTER_NEAREST, NULL);
1206 g_free(bitmap);
1207 #undef DEPTH
1210 static void
1211 draw_string (DiaRenderer *self,
1212 const gchar *text,
1213 Point *pos, Alignment alignment,
1214 Color *color)
1216 TextLine *text_line = text_line_new(text, self->font, self->font_height);
1217 Point realigned_pos = *pos;
1218 realigned_pos.x -= text_line_get_alignment_adjustment(text_line, alignment);
1219 draw_text_line(self, text_line, &realigned_pos, color);
1223 /* Get the width of the given text in cm */
1224 static real
1225 get_text_width(DiaRenderer *object,
1226 const gchar *text, int length)
1228 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (object);
1229 real result;
1230 TextLine *text_line;
1232 if (length != strlen(text)) {
1233 char *othertx;
1234 int ulen;
1235 /* A couple UTF8-chars: æblegrød Å  Ť Ž Ä™ ć Å„ уфхцÐ?ОПРЄ Ñ” Ò? Њ Ћ Ð? */
1236 ulen = g_utf8_offset_to_pointer(text, length)-text;
1237 if (!g_utf8_validate(text, ulen, NULL)) {
1238 g_warning ("Text at char %d not valid\n", length);
1240 othertx = g_strndup(text, ulen);
1241 text_line = text_line_new(othertx, object->font, object->font_height);
1242 } else {
1243 text_line = text_line_new(text, object->font, object->font_height);
1245 result = text_line_get_width(text_line);
1246 text_line_destroy(text_line);
1247 return result;
1251 static void
1252 draw_image(DiaRenderer *self,
1253 Point *point,
1254 real width, real height,
1255 DiaImage image)
1257 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
1259 /* Todo: Handle some kind of clipping! */
1261 if (renderer->highlight_color != NULL) {
1262 Point lr;
1263 DiaRendererClass *self_class = DIA_RENDERER_GET_CLASS (self);
1265 lr = *point;
1266 lr.x += width;
1267 lr.y += height;
1268 self_class->fill_rect(self, point, &lr, renderer->highlight_color);
1269 } else {
1270 double real_width, real_height;
1271 double x,y;
1272 int src_width, src_height;
1273 double affine[6];
1274 int rowstride;
1275 real_width = dia_transform_length(renderer->transform, width);
1276 real_height = dia_transform_length(renderer->transform, height);
1277 dia_transform_coords_double(renderer->transform,
1278 point->x, point->y, &x, &y);
1280 src_width = dia_image_width(image);
1281 src_height = dia_image_height(image);
1282 rowstride = dia_image_rowstride(image);
1284 affine[0] = real_width/(double)src_width;
1285 affine[1] = 0;
1286 affine[2] = 0;
1287 affine[3] = real_height/(double)src_height;
1288 affine[4] = x;
1289 affine[5] = y;
1291 if (dia_image_rgba_data(image)) {
1292 /* If there is an alpha channel, we can use it directly. */
1293 const guint8 *img_data = dia_image_rgba_data(image);
1294 art_rgb_rgba_affine(renderer->rgb_buffer,
1295 0, 0,
1296 renderer->pixel_width,
1297 renderer->pixel_height,
1298 renderer->pixel_width*3,
1299 img_data, src_width, src_height,
1300 rowstride,
1301 affine, ART_FILTER_NEAREST, NULL);
1302 /* Note that dia_image_rgba_data doesn't copy */
1303 } else {
1304 guint8 *img_data = dia_image_rgb_data(image);
1306 art_rgb_affine(renderer->rgb_buffer,
1307 0, 0,
1308 renderer->pixel_width,
1309 renderer->pixel_height,
1310 renderer->pixel_width*3,
1311 img_data, src_width, src_height,
1312 rowstride,
1313 affine, ART_FILTER_NEAREST, NULL);
1315 g_free(img_data);
1321 static void
1322 draw_object (DiaRenderer *renderer, DiaObject *object)
1324 if (renderer->is_interactive &&
1325 object->highlight_color != NULL) {
1326 DiaLibartRenderer *libart_rend = DIA_LIBART_RENDERER(renderer);
1327 libart_rend->highlight_color = object->highlight_color;
1328 object->ops->draw(object, renderer);
1329 libart_rend->highlight_color = NULL;
1331 object->ops->draw(object, renderer);
1334 static void
1335 renderer_init (DiaLibartRenderer *renderer, gpointer g_class)
1337 renderer->rgb_buffer = NULL;
1339 renderer->line_width = 1.0;
1340 renderer->cap_style = ART_PATH_STROKE_CAP_BUTT;
1341 renderer->join_style = ART_PATH_STROKE_JOIN_MITER;
1343 renderer->saved_line_style = LINESTYLE_SOLID;
1344 renderer->dash_enabled = 0;
1345 renderer->dash_length = 10;
1346 renderer->dot_length = 1;
1348 renderer->highlight_color = NULL;
1350 renderer->parent_instance.font = NULL;
1353 static void dia_libart_renderer_class_init (DiaLibartRendererClass *klass);
1354 static gpointer parent_class = NULL;
1356 static void
1357 renderer_finalize (GObject *object)
1359 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (object);
1361 if (renderer->rgb_buffer != NULL)
1362 g_free(renderer->rgb_buffer);
1364 G_OBJECT_CLASS (parent_class)->finalize (object);
1367 GType
1368 dia_libart_renderer_get_type (void)
1370 static GType object_type = 0;
1372 if (!object_type)
1374 static const GTypeInfo object_info =
1376 sizeof (DiaLibartRendererClass),
1377 (GBaseInitFunc) NULL,
1378 (GBaseFinalizeFunc) NULL,
1379 (GClassInitFunc) dia_libart_renderer_class_init,
1380 NULL, /* class_finalize */
1381 NULL, /* class_data */
1382 sizeof (DiaLibartRenderer),
1383 0, /* n_preallocs */
1384 (GInstanceInitFunc)renderer_init /* init */
1387 object_type = g_type_register_static (DIA_TYPE_RENDERER,
1388 "DiaLibartRenderer",
1389 &object_info, 0);
1392 return object_type;
1395 static void
1396 dia_libart_renderer_class_init (DiaLibartRendererClass *klass)
1398 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1399 DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
1401 parent_class = g_type_class_peek_parent (klass);
1403 gobject_class->finalize = renderer_finalize;
1405 /* Here we set the functions that we define for this renderer. */
1406 renderer_class->get_width_pixels = get_width_pixels;
1407 renderer_class->get_height_pixels = get_height_pixels;
1409 renderer_class->begin_render = begin_render;
1410 renderer_class->end_render = end_render;
1412 renderer_class->set_linewidth = set_linewidth;
1413 renderer_class->set_linecaps = set_linecaps;
1414 renderer_class->set_linejoin = set_linejoin;
1415 renderer_class->set_linestyle = set_linestyle;
1416 renderer_class->set_dashlength = set_dashlength;
1417 renderer_class->set_fillstyle = set_fillstyle;
1418 renderer_class->set_font = set_font;
1420 renderer_class->draw_line = draw_line;
1421 renderer_class->draw_polyline = draw_polyline;
1423 renderer_class->draw_polygon = draw_polygon;
1424 renderer_class->fill_polygon = fill_polygon;
1426 renderer_class->draw_rect = draw_rect;
1427 renderer_class->fill_rect = fill_rect;
1429 renderer_class->draw_arc = draw_arc;
1430 renderer_class->fill_arc = fill_arc;
1432 renderer_class->draw_ellipse = draw_ellipse;
1433 renderer_class->fill_ellipse = fill_ellipse;
1435 renderer_class->draw_bezier = draw_bezier;
1436 renderer_class->fill_bezier = fill_bezier;
1438 renderer_class->draw_string = draw_string;
1439 renderer_class->draw_text_line = draw_text_line;
1441 renderer_class->draw_image = draw_image;
1443 renderer_class->draw_object = draw_object;
1445 /* Interactive functions */
1446 renderer_class->get_text_width = get_text_width;
1449 #endif