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