1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 *
4 * diagdkrenderer.c - refactoring of the render to gdk facility
5 * Copyright (C) 2002 Hans Breuer
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include <config.h>
23
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <gdk/gdk.h>
28
29 #define PANGO_ENABLE_ENGINE
30 #include <pango/pango-engine.h>
31 #include <pango/pango.h>
32
33 #include "diagdkrenderer.h"
34 #include "dia_image.h"
35 #include "message.h"
36 #include "color.h"
37 #include "font.h"
38 #include "text.h"
39 #include "object.h"
40 #include "textline.h"
41
42 #include "time.h"
43
44 #ifdef HAVE_FREETYPE
45 #include <pango/pango.h>
46 #include <pango/pangoft2.h>
47 #endif
48
49 static int get_width_pixels (DiaRenderer *);
50 static int get_height_pixels (DiaRenderer *);
51
52 static void begin_render (DiaRenderer *);
53 static void end_render (DiaRenderer *);
54
55 static void set_linewidth (DiaRenderer *renderer, real linewidth);
56 static void set_linecaps (DiaRenderer *renderer, LineCaps mode);
57 static void set_linejoin (DiaRenderer *renderer, LineJoin mode);
58 static void set_linestyle (DiaRenderer *renderer, LineStyle mode);
59 static void set_dashlength (DiaRenderer *renderer, real length);
60 static void set_fillstyle (DiaRenderer *renderer, FillStyle mode);
61
62 static void draw_line (DiaRenderer *renderer,
63 Point *start, Point *end,
64 Color *color);
65 static void fill_polygon (DiaRenderer *renderer,
66 Point *points, int num_points,
67 Color *color);
68 static void draw_arc (DiaRenderer *renderer,
69 Point *center,
70 real width, real height,
71 real angle1, real angle2,
72 Color *color);
73 static void fill_arc (DiaRenderer *renderer,
74 Point *center,
75 real width, real height,
76 real angle1, real angle2,
77 Color *color);
78 static void draw_ellipse (DiaRenderer *renderer,
79 Point *center,
80 real width, real height,
81 Color *color);
82 static void fill_ellipse (DiaRenderer *renderer,
83 Point *center,
84 real width, real height,
85 Color *color);
86 static void draw_string (DiaRenderer *renderer,
87 const gchar *text,
88 Point *pos,
89 Alignment alignment,
90 Color *color);
91 static void draw_text_line (DiaRenderer *renderer,
92 TextLine *text,
93 Point *pos,
94 Alignment alignment,
95 Color *color);
96 static void draw_image (DiaRenderer *renderer,
97 Point *point,
98 real width, real height,
99 DiaImage *image);
100
101 static void draw_rect (DiaRenderer *renderer,
102 Point *ul_corner, Point *lr_corner,
103 Color *color);
104 static void fill_rect (DiaRenderer *renderer,
105 Point *ul_corner, Point *lr_corner,
106 Color *color);
107 static void draw_fill_rect (DiaGdkRenderer *renderer,
108 Point *ul_corner, Point *lr_corner,
109 Color *color, gboolean fill);
110 static void draw_polyline (DiaRenderer *renderer,
111 Point *points, int num_points,
112 Color *color);
113 static void draw_polygon (DiaRenderer *renderer,
114 Point *points, int num_points,
115 Color *color);
116 static void draw_rounded_rect (DiaRenderer *renderer,
117 Point *ul_corner, Point *lr_corner,
118 Color *color, real radius);
119 static void fill_rounded_rect (DiaRenderer *renderer,
120 Point *ul_corner, Point *lr_corner,
121 Color *color, real radius);
122 static void draw_object (DiaRenderer *renderer, DiaObject *object);
123
124 static real get_text_width (DiaRenderer *renderer,
125 const gchar *text, int length);
126
127 static void dia_gdk_renderer_class_init (DiaGdkRendererClass *klass);
128 static void renderer_init (DiaGdkRenderer *renderer, void*);
129
130 static gpointer parent_class = NULL;
131
132 /** Get the type object for the GdkRenderer.
133 * @return A static GType object describing GdkRenderers.
134 */
135 GType
dia_gdk_renderer_get_type(void)136 dia_gdk_renderer_get_type(void)
137 {
138 static GType object_type = 0;
139
140 if (!object_type)
141 {
142 static const GTypeInfo object_info =
143 {
144 sizeof (DiaGdkRendererClass),
145 (GBaseInitFunc) NULL,
146 (GBaseFinalizeFunc) NULL,
147 (GClassInitFunc) dia_gdk_renderer_class_init,
148 NULL, /* class_finalize */
149 NULL, /* class_data */
150 sizeof (DiaGdkRenderer),
151 0, /* n_preallocs */
152 (GInstanceInitFunc)renderer_init /* init */
153 };
154
155 object_type = g_type_register_static (DIA_TYPE_RENDERER,
156 "DiaGdkRenderer",
157 &object_info, 0);
158 }
159
160 return object_type;
161 }
162
163 /** Initialize a renderer object.
164 * @param renderer A renderer object to initialize.
165 * @param p Ignored, purpose unknown.
166 */
167 static void
renderer_init(DiaGdkRenderer * renderer,void * p)168 renderer_init(DiaGdkRenderer *renderer, void* p)
169 {
170 renderer->line_width = 1;
171 renderer->line_style = GDK_LINE_SOLID;
172 renderer->cap_style = GDK_CAP_BUTT;
173 renderer->join_style = GDK_JOIN_ROUND;
174
175 renderer->saved_line_style = LINESTYLE_SOLID;
176 renderer->dash_length = 10;
177 renderer->dot_length = 2;
178
179 renderer->highlight_color = NULL;
180 }
181
182 /** Clean up a renderer object after use.
183 * @param object Renderer object to free subobjects from.
184 */
185 static void
renderer_finalize(GObject * object)186 renderer_finalize(GObject *object)
187 {
188 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
189
190 if (renderer->pixmap != NULL)
191 gdk_pixmap_unref(renderer->pixmap);
192
193 if (renderer->gc != NULL)
194 gdk_gc_unref(renderer->gc);
195
196 if (renderer->clip_region != NULL)
197 gdk_region_destroy(renderer->clip_region);
198
199 if (renderer->transform)
200 g_object_unref (renderer->transform);
201
202 G_OBJECT_CLASS (parent_class)->finalize (object);
203 }
204
205 /** Initialize members of the renderer class object. This sets up a bunch
206 * of functions to call for various render functions.
207 * @param klass The class object to initialize.
208 */
209 static void
dia_gdk_renderer_class_init(DiaGdkRendererClass * klass)210 dia_gdk_renderer_class_init(DiaGdkRendererClass *klass)
211 {
212 GObjectClass *object_class = G_OBJECT_CLASS (klass);
213 DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
214
215 parent_class = g_type_class_peek_parent (klass);
216
217 object_class->finalize = renderer_finalize;
218
219 renderer_class->get_width_pixels = get_width_pixels;
220 renderer_class->get_height_pixels = get_height_pixels;
221
222 renderer_class->begin_render = begin_render;
223 renderer_class->end_render = end_render;
224
225 renderer_class->set_linewidth = set_linewidth;
226 renderer_class->set_linecaps = set_linecaps;
227 renderer_class->set_linejoin = set_linejoin;
228 renderer_class->set_linestyle = set_linestyle;
229 renderer_class->set_dashlength = set_dashlength;
230 renderer_class->set_fillstyle = set_fillstyle;
231
232 renderer_class->draw_line = draw_line;
233 renderer_class->fill_polygon = fill_polygon;
234 renderer_class->draw_rect = draw_rect;
235 renderer_class->fill_rect = fill_rect;
236 renderer_class->draw_arc = draw_arc;
237 renderer_class->fill_arc = fill_arc;
238 renderer_class->draw_ellipse = draw_ellipse;
239 renderer_class->fill_ellipse = fill_ellipse;
240
241 /* use <draw|fill>_bezier from DiaRenderer */
242
243 renderer_class->draw_string = draw_string;
244 renderer_class->draw_text_line = draw_text_line;
245 renderer_class->draw_image = draw_image;
246
247 /* medium level functions */
248 renderer_class->draw_rect = draw_rect;
249 renderer_class->draw_polyline = draw_polyline;
250 renderer_class->draw_polygon = draw_polygon;
251 renderer_class->draw_object = draw_object;
252
253 /* highest level functions */
254 renderer_class->draw_rounded_rect = draw_rounded_rect;
255 renderer_class->fill_rounded_rect = fill_rounded_rect;
256
257 /* Interactive functions */
258 renderer_class->get_text_width = get_text_width;
259 }
260
261 /** Convert Dia color objects into GDK color objects.
262 * If the highlight color is set, that will be used instead. This allows
263 * rendering of an object to do highlight rendering.
264 * @param renderer The renderer to check for highlight color.
265 * @param col A color object to convert.
266 * @param gdk_col Resulting GDK convert.
267 */
268 static void
renderer_color_convert(DiaGdkRenderer * renderer,Color * col,GdkColor * gdk_col)269 renderer_color_convert(DiaGdkRenderer *renderer,
270 Color *col, GdkColor *gdk_col)
271 {
272 if (renderer->highlight_color != NULL) {
273 color_convert(renderer->highlight_color, gdk_col);
274 } else {
275 color_convert(col, gdk_col);
276 }
277 }
278
279 static void
begin_render(DiaRenderer * object)280 begin_render (DiaRenderer *object)
281 {
282 }
283
284 static void
end_render(DiaRenderer * object)285 end_render (DiaRenderer *object)
286 {
287 }
288
289 static void
set_linewidth(DiaRenderer * object,real linewidth)290 set_linewidth (DiaRenderer *object, real linewidth)
291 {
292 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
293
294 if (renderer->highlight_color != NULL) {
295 /* 6 pixels wide -> 3 pixels beyond normal obj */
296 real border = dia_untransform_length(renderer->transform, 6);
297 linewidth += border;
298 }
299
300 /* 0 == hairline **/
301 renderer->line_width =
302 dia_transform_length(renderer->transform, linewidth);
303
304 if (renderer->line_width<=0)
305 renderer->line_width = 1; /* Minimum 1 pixel. */
306
307 gdk_gc_set_line_attributes(renderer->gc,
308 renderer->line_width,
309 renderer->line_style,
310 renderer->cap_style,
311 renderer->join_style);
312 }
313
314 static void
set_linecaps(DiaRenderer * object,LineCaps mode)315 set_linecaps (DiaRenderer *object, LineCaps mode)
316 {
317 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
318
319 if (renderer->highlight_color != NULL) {
320 renderer->cap_style = GDK_CAP_ROUND;
321 } else {
322 switch(mode) {
323 case LINECAPS_BUTT:
324 renderer->cap_style = GDK_CAP_BUTT;
325 break;
326 case LINECAPS_ROUND:
327 renderer->cap_style = GDK_CAP_ROUND;
328 break;
329 case LINECAPS_PROJECTING:
330 renderer->cap_style = GDK_CAP_PROJECTING;
331 break;
332 }
333 }
334
335 gdk_gc_set_line_attributes(renderer->gc,
336 renderer->line_width,
337 renderer->line_style,
338 renderer->cap_style,
339 renderer->join_style);
340 }
341
342 static void
set_linejoin(DiaRenderer * object,LineJoin mode)343 set_linejoin (DiaRenderer *object, LineJoin mode)
344 {
345 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
346
347 if (renderer->highlight_color != NULL) {
348 renderer->join_style = GDK_JOIN_ROUND;
349 } else {
350 switch(mode) {
351 case LINEJOIN_MITER:
352 renderer->join_style = GDK_JOIN_MITER;
353 break;
354 case LINEJOIN_ROUND:
355 renderer->join_style = GDK_JOIN_ROUND;
356 break;
357 case LINEJOIN_BEVEL:
358 renderer->join_style = GDK_JOIN_BEVEL;
359 break;
360 default :
361 /* invalid mode, just here to set a breakpoint */
362 renderer->join_style = GDK_JOIN_ROUND;
363 break;
364 }
365 }
366
367 gdk_gc_set_line_attributes(renderer->gc,
368 renderer->line_width,
369 renderer->line_style,
370 renderer->cap_style,
371 renderer->join_style);
372 }
373
374 /** Set the dashes for this renderer.
375 * offset determines where in the pattern the dashes will start.
376 * It is used by the grid in particular to make the grid dashes line up.
377 */
378 void
dia_gdk_renderer_set_dashes(DiaGdkRenderer * renderer,int offset)379 dia_gdk_renderer_set_dashes(DiaGdkRenderer *renderer, int offset)
380 {
381 gint8 dash_list[6];
382 int hole_width;
383 int pattern_length;
384
385 switch(renderer->saved_line_style) {
386 case LINESTYLE_SOLID:
387 break;
388 case LINESTYLE_DASHED:
389 dash_list[0] = renderer->dash_length;
390 dash_list[1] = renderer->dash_length;
391 pattern_length = renderer->dash_length*2;
392 gdk_gc_set_dashes(renderer->gc, offset, dash_list, 2);
393 break;
394 case LINESTYLE_DASH_DOT:
395 hole_width = (renderer->dash_length - renderer->dot_length) / 2;
396 if (hole_width==0)
397 hole_width = 1;
398 dash_list[0] = renderer->dash_length;
399 dash_list[1] = hole_width;
400 dash_list[2] = renderer->dot_length;
401 dash_list[3] = hole_width;
402 pattern_length = renderer->dash_length+renderer->dot_length+2*hole_width;
403 gdk_gc_set_dashes(renderer->gc, offset, dash_list, 4);
404 break;
405 case LINESTYLE_DASH_DOT_DOT:
406 hole_width = (renderer->dash_length - 2*renderer->dot_length) / 3;
407 if (hole_width==0)
408 hole_width = 1;
409 dash_list[0] = renderer->dash_length;
410 dash_list[1] = hole_width;
411 dash_list[2] = renderer->dot_length;
412 dash_list[3] = hole_width;
413 dash_list[4] = renderer->dot_length;
414 dash_list[5] = hole_width;
415 pattern_length = renderer->dash_length+2*renderer->dot_length+3*hole_width;
416 gdk_gc_set_dashes(renderer->gc, offset, dash_list, 6);
417 break;
418 case LINESTYLE_DOTTED:
419 dash_list[0] = renderer->dot_length;
420 dash_list[1] = renderer->dot_length;
421 pattern_length = renderer->dot_length;
422 gdk_gc_set_dashes(renderer->gc, offset, dash_list, 2);
423 break;
424 }
425
426 }
427
428 static void
set_linestyle(DiaRenderer * object,LineStyle mode)429 set_linestyle (DiaRenderer *object, LineStyle mode)
430 {
431 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
432
433 renderer->saved_line_style = mode;
434 switch(mode) {
435 case LINESTYLE_SOLID:
436 renderer->line_style = GDK_LINE_SOLID;
437 break;
438 case LINESTYLE_DASHED:
439 renderer->line_style = GDK_LINE_ON_OFF_DASH;
440 dia_gdk_renderer_set_dashes(renderer, 0);
441 break;
442 case LINESTYLE_DASH_DOT:
443 renderer->line_style = GDK_LINE_ON_OFF_DASH;
444 dia_gdk_renderer_set_dashes(renderer, 0);
445 break;
446 case LINESTYLE_DASH_DOT_DOT:
447 renderer->line_style = GDK_LINE_ON_OFF_DASH;
448 dia_gdk_renderer_set_dashes(renderer, 0);
449 break;
450 case LINESTYLE_DOTTED:
451 renderer->line_style = GDK_LINE_ON_OFF_DASH;
452 dia_gdk_renderer_set_dashes(renderer, 0);
453 break;
454 }
455 gdk_gc_set_line_attributes(renderer->gc,
456 renderer->line_width,
457 renderer->line_style,
458 renderer->cap_style,
459 renderer->join_style);
460 }
461
462 static void
set_dashlength(DiaRenderer * object,real length)463 set_dashlength (DiaRenderer *object, real length)
464 {
465 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
466 /* dot = 10% of len */
467 real ddisp_len;
468
469 ddisp_len =
470 dia_transform_length(renderer->transform, length);
471
472 renderer->dash_length = (int)floor(ddisp_len+0.5);
473 renderer->dot_length = (int)floor(ddisp_len*0.1+0.5);
474
475 if (renderer->dash_length<=0)
476 renderer->dash_length = 1;
477 if (renderer->dash_length>255)
478 renderer->dash_length = 255;
479 if (renderer->dot_length<=0)
480 renderer->dot_length = 1;
481 if (renderer->dot_length>255)
482 renderer->dot_length = 255;
483 set_linestyle(object, renderer->saved_line_style);
484 }
485
486 static void
set_fillstyle(DiaRenderer * object,FillStyle mode)487 set_fillstyle (DiaRenderer *object, FillStyle mode)
488 {
489 switch(mode) {
490 case FILLSTYLE_SOLID:
491 break;
492 default:
493 message_error("gdk_renderer: Unsupported fill mode specified!\n");
494 }
495 }
496
497 static void
draw_line(DiaRenderer * object,Point * start,Point * end,Color * line_color)498 draw_line (DiaRenderer *object, Point *start, Point *end, Color *line_color)
499 {
500 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
501
502 GdkGC *gc = renderer->gc;
503 GdkColor color;
504 int x1,y1,x2,y2;
505
506 dia_transform_coords(renderer->transform, start->x, start->y, &x1, &y1);
507 dia_transform_coords(renderer->transform, end->x, end->y, &x2, &y2);
508
509 renderer_color_convert(renderer, line_color, &color);
510 gdk_gc_set_foreground(gc, &color);
511
512 gdk_draw_line(renderer->pixmap, gc,
513 x1, y1, x2, y2);
514 }
515
516 static void
fill_polygon(DiaRenderer * object,Point * points,int num_points,Color * line_color)517 fill_polygon (DiaRenderer *object, Point *points, int num_points, Color *line_color)
518 {
519 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
520 GdkGC *gc = renderer->gc;
521 GdkColor color;
522 GdkPoint *gdk_points;
523 int i,x,y;
524
525 gdk_points = g_new(GdkPoint, num_points);
526
527 for (i=0;i<num_points;i++) {
528 dia_transform_coords(renderer->transform, points[i].x, points[i].y, &x, &y);
529 gdk_points[i].x = x;
530 gdk_points[i].y = y;
531 }
532
533 renderer_color_convert(renderer, line_color, &color);
534 gdk_gc_set_foreground(gc, &color);
535
536 gdk_draw_polygon(renderer->pixmap, gc, TRUE, gdk_points, num_points);
537 g_free(gdk_points);
538 }
539
540 static void
draw_fill_arc(DiaRenderer * object,Point * center,real width,real height,real angle1,real angle2,Color * color,gboolean fill)541 draw_fill_arc (DiaRenderer *object,
542 Point *center,
543 real width, real height,
544 real angle1, real angle2,
545 Color *color,
546 gboolean fill)
547 {
548 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
549 GdkGC *gc = renderer->gc;
550 GdkColor gdkcolor;
551 gint top, left, bottom, right;
552 real dangle;
553
554 dia_transform_coords(renderer->transform,
555 center->x - width/2, center->y - height/2,
556 &left, &top);
557 dia_transform_coords(renderer->transform,
558 center->x + width/2, center->y + height/2,
559 &right, &bottom);
560
561 if ((left>right) || (top>bottom))
562 return;
563
564 renderer_color_convert(renderer, color, &gdkcolor);
565 gdk_gc_set_foreground(gc, &gdkcolor);
566
567 dangle = angle2-angle1;
568 if (dangle<0)
569 dangle += 360.0;
570
571 gdk_draw_arc(renderer->pixmap,
572 gc, fill,
573 left, top, right-left, bottom-top,
574 (int) (angle1*64.0), (int) (dangle*64.0));
575 }
576 static void
draw_arc(DiaRenderer * object,Point * center,real width,real height,real angle1,real angle2,Color * color)577 draw_arc (DiaRenderer *object,
578 Point *center,
579 real width, real height,
580 real angle1, real angle2,
581 Color *color)
582 {
583 draw_fill_arc (object, center, width, height, angle1, angle2, color, FALSE);
584 }
585
586 static void
fill_arc(DiaRenderer * object,Point * center,real width,real height,real angle1,real angle2,Color * color)587 fill_arc (DiaRenderer *object, Point *center,
588 real width, real height, real angle1, real angle2,
589 Color *color)
590 {
591 draw_fill_arc (object, center, width, height, angle1, angle2, color, TRUE);
592 }
593
594 static void
draw_ellipse(DiaRenderer * object,Point * center,real width,real height,Color * color)595 draw_ellipse (DiaRenderer *object, Point *center,
596 real width, real height,
597 Color *color)
598 {
599 draw_arc(object, center, width, height, 0.0, 360.0, color);
600 }
601
602 static void
fill_ellipse(DiaRenderer * object,Point * center,real width,real height,Color * color)603 fill_ellipse (DiaRenderer *object, Point *center,
604 real width, real height, Color *color)
605 {
606 fill_arc(object, center, width, height, 0.0, 360.0, color);
607 }
608
609 /* Draw a highlighted version of a string.
610 */
611 static void
draw_highlighted_string(DiaGdkRenderer * renderer,PangoLayout * layout,int x,int y,GdkColor * color)612 draw_highlighted_string(DiaGdkRenderer *renderer,
613 PangoLayout *layout,
614 int x, int y,
615 GdkColor *color)
616 {
617 gint width, height;
618
619 pango_layout_get_pixel_size(layout, &width, &height);
620
621 gdk_gc_set_foreground(renderer->gc, color);
622
623 gdk_draw_rectangle (renderer->pixmap,
624 renderer->gc, TRUE,
625 x-3, y-3,
626 width+6, height+6);
627 }
628
629 static void
draw_string(DiaRenderer * object,const gchar * text,Point * pos,Alignment alignment,Color * color)630 draw_string (DiaRenderer *object,
631 const gchar *text, Point *pos, Alignment alignment,
632 Color *color)
633 {
634 TextLine *text_line = text_line_new(text, object->font, object->font_height);
635 draw_text_line(object, text_line, pos, alignment, color);
636 text_line_destroy(text_line);
637 }
638
639 #ifdef HAVE_FREETYPE
640 static void
initialize_ft_bitmap(FT_Bitmap * ftbitmap,int width,int height)641 initialize_ft_bitmap(FT_Bitmap *ftbitmap, int width, int height)
642 {
643 int rowstride = 32*((width+31)/31);
644 guint8 *graybitmap = (guint8*)g_new0(guint8, height*rowstride);
645
646 ftbitmap->rows = height;
647 ftbitmap->width = width;
648 ftbitmap->pitch = rowstride;
649 ftbitmap->buffer = graybitmap;
650 ftbitmap->num_grays = 256;
651 ftbitmap->pixel_mode = ft_pixel_mode_grays;
652 ftbitmap->palette_mode = 0;
653 ftbitmap->palette = 0;
654 }
655 #endif
656
657 /** Draw a TextLine object.
658 * @param object The renderer object to use for transform and output
659 * @param text_line The TextLine to render, including font and height.
660 * @param pos The position to render it at.
661 * @param color The color to render it with.
662 */
663 static void
draw_text_line(DiaRenderer * object,TextLine * text_line,Point * pos,Alignment alignment,Color * color)664 draw_text_line (DiaRenderer *object, TextLine *text_line,
665 Point *pos, Alignment alignment, Color *color)
666 {
667 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
668 GdkColor gdkcolor;
669 int x,y;
670 Point start_pos;
671 PangoLayout* layout = NULL;
672 const gchar *text = text_line_get_string(text_line);
673 int height_pixels;
674 real font_height = text_line_get_height(text_line);
675 real scale = dia_transform_length(renderer->transform, 1.0);
676
677 if (text == NULL || *text == '\0') return; /* Don't render empty strings. */
678
679 point_copy(&start_pos,pos);
680
681 renderer_color_convert(renderer, color, &gdkcolor);
682
683 height_pixels = dia_transform_length(renderer->transform, font_height);
684 if (height_pixels < 2) { /* "Greeking" instead of making tiny font */
685 int width_pixels = dia_transform_length(renderer->transform,
686 text_line_get_width(text_line));
687 gdk_gc_set_foreground(renderer->gc, &gdkcolor);
688 gdk_gc_set_dashes(renderer->gc, 0, (guint8*)"\1\2", 2);
689 dia_transform_coords(renderer->transform, start_pos.x, start_pos.y, &x, &y);
690 gdk_draw_line(renderer->pixmap, renderer->gc, x, y, x + width_pixels, y);
691 return;
692 } else {
693 start_pos.y -= text_line_get_ascent(text_line);
694 start_pos.x -= text_line_get_alignment_adjustment (text_line, alignment);
695
696 dia_transform_coords(renderer->transform,
697 start_pos.x, start_pos.y, &x, &y);
698
699 layout = dia_font_build_layout(text, text_line->font,
700 dia_transform_length(renderer->transform, text_line->height)/20.0);
701 #if defined(PANGO_VERSION_ENCODE)
702 # if (PANGO_VERSION >= PANGO_VERSION_ENCODE(1,16,0))
703 /* I'd say the former Pango API was broken, i.e. leaky */
704 # define HAVE_pango_layout_get_line_readonly
705 # endif
706 #endif
707 text_line_adjust_layout_line (text_line,
708 #if defined(HAVE_pango_layout_get_line_readonly)
709 pango_layout_get_line_readonly(layout, 0),
710 #else
711 pango_layout_get_line(layout, 0),
712 #endif
713 scale/20.0);
714
715 if (renderer->highlight_color != NULL) {
716 draw_highlighted_string(renderer, layout, x, y, &gdkcolor);
717 } else {
718 #if defined HAVE_FREETYPE
719 {
720 FT_Bitmap ftbitmap;
721 int width, height;
722 GdkPixbuf *rgba = NULL;
723
724 width = dia_transform_length(renderer->transform,
725 text_line_get_width(text_line));
726 height = dia_transform_length(renderer->transform,
727 text_line_get_height(text_line));
728
729 if (width > 0) {
730 int stride;
731 guchar* pixels;
732 int i,j;
733 guint8 *graybitmap;
734
735 initialize_ft_bitmap(&ftbitmap, width, height);
736 pango_ft2_render_layout(&ftbitmap, layout, 0, 0);
737
738 graybitmap = ftbitmap.buffer;
739
740 rgba = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
741 stride = gdk_pixbuf_get_rowstride(rgba);
742 pixels = gdk_pixbuf_get_pixels(rgba);
743 for (i = 0; i < height; i++) {
744 for (j = 0; j < width; j++) {
745 pixels[i*stride+j*4] = gdkcolor.red>>8;
746 pixels[i*stride+j*4+1] = gdkcolor.green>>8;
747 pixels[i*stride+j*4+2] = gdkcolor.blue>>8;
748 pixels[i*stride+j*4+3] = graybitmap[i*ftbitmap.pitch+j];
749 }
750 }
751 g_free(graybitmap);
752
753 gdk_draw_pixbuf(renderer->pixmap, renderer->gc, rgba, 0, 0, x, y, width, height, GDK_RGB_DITHER_NONE, 0, 0);
754
755 g_object_unref(G_OBJECT(rgba));
756 }
757 }
758 #else
759 gdk_gc_set_foreground(renderer->gc, &gdkcolor);
760
761 gdk_draw_layout(renderer->pixmap, renderer->gc, x, y, layout);
762 #endif
763 } /* !higlight_color */
764 g_object_unref(G_OBJECT(layout));
765 } /* !greeking */
766 }
767
768 /* Get the width of the given text in cm */
769 static real
get_text_width(DiaRenderer * object,const gchar * text,int length)770 get_text_width(DiaRenderer *object,
771 const gchar *text, int length)
772 {
773 real result;
774 TextLine *text_line;
775
776 if (length != g_utf8_strlen(text, -1)) {
777 char *othertx;
778 int ulen;
779 /* A couple UTF8-chars: æblegrød Š Ť Ž ę ć ń уфхцНОПРЄ є Ґ Њ Ћ Џ */
780 ulen = g_utf8_offset_to_pointer(text, length)-text;
781 if (!g_utf8_validate(text, ulen, NULL)) {
782 g_warning ("Text at char %d not valid\n", length);
783 }
784 othertx = g_strndup(text, ulen);
785 text_line = text_line_new(othertx, object->font, object->font_height);
786 } else {
787 text_line = text_line_new(text, object->font, object->font_height);
788 }
789 result = text_line_get_width(text_line);
790 text_line_destroy(text_line);
791 return result;
792 }
793
794 static void
draw_image(DiaRenderer * object,Point * point,real width,real height,DiaImage * image)795 draw_image (DiaRenderer *object,
796 Point *point,
797 real width, real height,
798 DiaImage *image)
799 {
800 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
801 if (renderer->highlight_color != NULL) {
802 Point lr;
803 DiaRendererClass *self_class = DIA_RENDERER_GET_CLASS (object);
804
805 lr = *point;
806 lr.x += width;
807 lr.y += height;
808 self_class->fill_rect(object, point, &lr, renderer->highlight_color);
809 } else {
810 int real_width, real_height, real_x, real_y;
811 const GdkPixbuf *org = dia_image_pixbuf (image);
812 int org_width = gdk_pixbuf_get_width(org);
813 int org_height = gdk_pixbuf_get_height(org);
814
815 real_width = dia_transform_length(renderer->transform, width);
816 real_height = dia_transform_length(renderer->transform, height);
817 dia_transform_coords(renderer->transform, point->x, point->y,
818 &real_x, &real_y);
819
820 if (real_width == org_width && real_height == org_height) {
821 gdk_draw_pixbuf(renderer->pixmap, renderer->gc, (GdkPixbuf *)org,
822 0, 0, real_x, real_y, real_width, real_height,
823 GDK_RGB_DITHER_NORMAL, 0, 0);
824 } else if (real_width > org_width || real_height > org_height) {
825 /* don't use dia_image_draw for big zooms, it scales the whole pixbuf even if not needed */
826 int sub_width = real_width - (real_x >= 0 ? 0 : -real_x);
827 int sub_height = real_height - (real_y >= 0 ? 0 : -real_y);
828
829 /* we can also clip to our pixmap size */
830 if (get_width_pixels (object) < sub_width)
831 sub_width = get_width_pixels (object);
832 if (get_height_pixels (object) < sub_height)
833 sub_height = get_height_pixels (object);
834
835 if (sub_height > 0 && sub_width > 0) {
836 GdkPixbuf *scaled = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (org),
837 gdk_pixbuf_get_has_alpha (org),
838 gdk_pixbuf_get_bits_per_sample (org),
839 sub_width, sub_height);
840 double scale_x = (double)real_width/org_width;
841 double scale_y = (double)real_height/org_height;
842 gdk_pixbuf_scale (org, scaled,
843 0, 0, sub_width, sub_height,
844 real_x >= 0 ? 0 : real_x, real_y >= 0 ? 0 : real_y,
845 scale_x, scale_y, GDK_INTERP_TILES);
846 gdk_draw_pixbuf(renderer->pixmap, renderer->gc, scaled,
847 0, 0, real_x >= 0 ? real_x : 0, real_y >= 0 ? real_y : 0,
848 sub_width, sub_height, GDK_RGB_DITHER_NORMAL, 0, 0);
849 g_object_unref (scaled);
850 }
851 } else {
852 /* otherwise still using the caching variant */
853 dia_image_draw(image, renderer->pixmap, renderer->gc, real_x, real_y,
854 real_width, real_height);
855 }
856 }
857 }
858
859 /*
860 * medium level functions
861 */
862 static void
draw_fill_rect(DiaGdkRenderer * renderer,Point * ul_corner,Point * lr_corner,Color * color,gboolean fill)863 draw_fill_rect (DiaGdkRenderer *renderer,
864 Point *ul_corner, Point *lr_corner,
865 Color *color, gboolean fill)
866 {
867 GdkGC *gc = renderer->gc;
868 GdkColor gdkcolor;
869 gint top, bottom, left, right;
870
871 dia_transform_coords(renderer->transform,
872 ul_corner->x, ul_corner->y, &left, &top);
873 dia_transform_coords(renderer->transform,
874 lr_corner->x, lr_corner->y, &right, &bottom);
875
876 if ((left>right) || (top>bottom))
877 return;
878
879 renderer_color_convert(renderer, color, &gdkcolor);
880 gdk_gc_set_foreground(gc, &gdkcolor);
881
882 gdk_draw_rectangle (renderer->pixmap,
883 gc, fill,
884 left, top,
885 right-left,
886 bottom-top);
887 }
888
889 static void
draw_rect(DiaRenderer * object,Point * ul_corner,Point * lr_corner,Color * color)890 draw_rect (DiaRenderer *object,
891 Point *ul_corner, Point *lr_corner,
892 Color *color)
893 {
894 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
895
896 draw_fill_rect (renderer, ul_corner, lr_corner, color, FALSE);
897 }
898
899 static void
fill_rect(DiaRenderer * object,Point * ul_corner,Point * lr_corner,Color * color)900 fill_rect (DiaRenderer *object,
901 Point *ul_corner, Point *lr_corner,
902 Color *color)
903 {
904 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
905
906 draw_fill_rect (renderer, ul_corner, lr_corner, color, TRUE);
907 }
908
909 static void
draw_polyline(DiaRenderer * self,Point * points,int num_points,Color * line_color)910 draw_polyline (DiaRenderer *self,
911 Point *points, int num_points,
912 Color *line_color)
913 {
914 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (self);
915 GdkGC *gc = renderer->gc;
916 GdkColor color;
917 GdkPoint *gdk_points;
918 int i,x,y;
919
920 gdk_points = g_new(GdkPoint, num_points);
921
922 for (i=0;i<num_points;i++) {
923 dia_transform_coords(renderer->transform, points[i].x, points[i].y, &x, &y);
924 gdk_points[i].x = x;
925 gdk_points[i].y = y;
926 }
927
928 renderer_color_convert(renderer, line_color, &color);
929 gdk_gc_set_foreground(gc, &color);
930
931 gdk_draw_lines(renderer->pixmap, gc, gdk_points, num_points);
932 g_free(gdk_points);
933 }
934
935 static void
draw_polygon(DiaRenderer * self,Point * points,int num_points,Color * line_color)936 draw_polygon (DiaRenderer *self,
937 Point *points, int num_points,
938 Color *line_color)
939 {
940 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (self);
941 GdkGC *gc = renderer->gc;
942 GdkColor color;
943 GdkPoint *gdk_points;
944 int i,x,y;
945
946 gdk_points = g_new(GdkPoint, num_points);
947
948 for (i=0;i<num_points;i++) {
949 dia_transform_coords(renderer->transform, points[i].x, points[i].y, &x, &y);
950 gdk_points[i].x = x;
951 gdk_points[i].y = y;
952 }
953
954 renderer_color_convert(renderer, line_color, &color);
955 gdk_gc_set_foreground(gc, &color);
956
957 gdk_draw_polygon(renderer->pixmap, gc, FALSE, gdk_points, num_points);
958 g_free(gdk_points);
959 }
960
961 /*!
962 * Implemented to avoid seams between arcs and lines caused by the base class working in real
963 * which than gets rounded independently to int here
964 */
965 static void
draw_fill_rounded_rect(DiaRenderer * self,Point * ul_corner,Point * lr_corner,Color * color,real radius,gboolean fill)966 draw_fill_rounded_rect (DiaRenderer *self,
967 Point *ul_corner, Point *lr_corner,
968 Color *color, real radius,
969 gboolean fill)
970 {
971 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (self);
972 GdkGC *gc = renderer->gc;
973 GdkColor gdkcolor;
974 gint top, bottom, left, right, r, d;
975 gint offset = 0; /* to compensate for a radius smaller than line_width */
976
977 dia_transform_coords(renderer->transform,
978 ul_corner->x, ul_corner->y, &left, &top);
979 dia_transform_coords(renderer->transform,
980 lr_corner->x, lr_corner->y, &right, &bottom);
981 r = dia_transform_length(renderer->transform, radius);
982
983 if ((left>right) || (top>bottom))
984 return;
985 /* adjust radius to possible size */
986 if (r>(right-left)/2)
987 r = (right-left)/2;
988 if (r>(bottom-top)/2)
989 r = (bottom-top)/2;
990
991 d = r<<1;
992 /* line_width is already scaled */
993 if (renderer->line_width > d)
994 offset = (renderer->line_width + 1) / 2;
995
996 renderer_color_convert(renderer, color, &gdkcolor);
997 gdk_gc_set_foreground(gc, &gdkcolor);
998
999 if (d > 0) {
1000 if (offset > 0) {
1001 /* avoid windowing system defined arc artifacts drawing by some adjustments */
1002 gdk_gc_set_line_attributes(renderer->gc,
1003 r, renderer->line_style,
1004 renderer->cap_style, renderer->join_style);
1005 gdk_draw_arc(renderer->pixmap, gc, TRUE, left-offset, top-offset, d, d, 90<<6, 90<<6);
1006 gdk_draw_arc(renderer->pixmap, gc, TRUE, right-d+offset, top-offset, d, d, 0<<6, 90<<6);
1007 gdk_draw_arc(renderer->pixmap, gc, TRUE, right-d+offset, bottom-d+offset, d, d, 270<<6, 90<<6);
1008 gdk_draw_arc(renderer->pixmap, gc, TRUE, left-offset, bottom-d+offset, d, d, 180<<6, 90<<6);
1009 gdk_gc_set_line_attributes(renderer->gc,
1010 renderer->line_width, renderer->line_style,
1011 renderer->cap_style, renderer->join_style);
1012 } else {
1013 gdk_draw_arc(renderer->pixmap, gc, fill, left, top, d, d, 90<<6, 90<<6);
1014 gdk_draw_arc(renderer->pixmap, gc, fill, right-d, top, d, d, 0<<6, 90<<6);
1015 gdk_draw_arc(renderer->pixmap, gc, fill, right-d, bottom-d, d, d, 270<<6, 90<<6);
1016 gdk_draw_arc(renderer->pixmap, gc, fill, left, bottom-d, d, d, 180<<6, 90<<6);
1017 }
1018 }
1019
1020 if (fill) {
1021 gdk_draw_rectangle (renderer->pixmap, renderer->gc, TRUE,
1022 left+r-offset, top, right-left-d+offset, bottom-top);
1023 gdk_draw_rectangle (renderer->pixmap, renderer->gc, TRUE,
1024 left, top+r-offset, right-left, bottom-top-d+offset);
1025 } else {
1026 gdk_draw_line(renderer->pixmap, gc, left+r-offset, top, right-r+offset, top);
1027 gdk_draw_line(renderer->pixmap, gc, right, top+r-offset, right, bottom-r+offset);
1028 gdk_draw_line(renderer->pixmap, gc, right-r+offset, bottom, left+r-1-offset, bottom);
1029 gdk_draw_line(renderer->pixmap, gc, left, bottom-r+offset, left, top+r-1-offset);
1030 }
1031 }
1032 static void
draw_rounded_rect(DiaRenderer * self,Point * ul_corner,Point * lr_corner,Color * color,real radius)1033 draw_rounded_rect (DiaRenderer *self,
1034 Point *ul_corner, Point *lr_corner,
1035 Color *color, real radius)
1036 {
1037 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (self);
1038 gint r = dia_transform_length(renderer->transform, radius);
1039
1040 if (r > 0)
1041 draw_fill_rounded_rect (self, ul_corner, lr_corner, color, radius, FALSE);
1042 else
1043 draw_rect (self, ul_corner, lr_corner, color);
1044 }
1045 static void
fill_rounded_rect(DiaRenderer * self,Point * ul_corner,Point * lr_corner,Color * color,real radius)1046 fill_rounded_rect (DiaRenderer *self,
1047 Point *ul_corner, Point *lr_corner,
1048 Color *color, real radius)
1049 {
1050 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (self);
1051 gint r = dia_transform_length(renderer->transform, radius);
1052
1053 if (r > 0)
1054 draw_fill_rounded_rect (self, ul_corner, lr_corner, color, radius, TRUE);
1055 else
1056 fill_rect (self, ul_corner, lr_corner, color);
1057 }
1058
1059 static void
draw_object(DiaRenderer * renderer,DiaObject * object)1060 draw_object (DiaRenderer *renderer, DiaObject *object)
1061 {
1062 if (renderer->is_interactive &&
1063 object->highlight_color != NULL) {
1064 DiaGdkRenderer *gdk_rend = DIA_GDK_RENDERER(renderer);
1065 gdk_rend->highlight_color = object->highlight_color;
1066 object->ops->draw(object, renderer);
1067 gdk_rend->highlight_color = NULL;
1068 }
1069 object->ops->draw(object, renderer);
1070 }
1071
1072 static int
get_width_pixels(DiaRenderer * object)1073 get_width_pixels (DiaRenderer *object)
1074 {
1075 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
1076 int width = 0;
1077
1078 if (renderer->pixmap)
1079 gdk_drawable_get_size (GDK_DRAWABLE (renderer->pixmap), &width, NULL);
1080
1081 return width;
1082 }
1083
1084 static int
get_height_pixels(DiaRenderer * object)1085 get_height_pixels (DiaRenderer *object)
1086 {
1087 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
1088 int height = 0;
1089
1090 if (renderer->pixmap)
1091 gdk_drawable_get_size (GDK_DRAWABLE (renderer->pixmap), NULL, &height);
1092
1093 return height;
1094 }
1095