More text_line happiness
[dia.git] / lib / diagdkrenderer.c
blob4ad6714065fc4bde4f74aef0f2ed84c9119e0f22
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * diagdkrenderer.c - refactoring of the render to gdk facility
5 * Copyright (C) 2002 Hans Breuer
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.
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.
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.
22 #include <config.h>
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <gdk/gdk.h>
29 #define PANGO_ENABLE_ENGINE
30 #include <pango/pango-engine.h>
31 #include <pango/pango.h>
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"
42 #include "time.h"
44 #ifdef HAVE_FREETYPE
45 #include <pango/pango.h>
46 #include <pango/pangoft2.h>
47 #endif
49 static int get_width_pixels (DiaRenderer *);
50 static int get_height_pixels (DiaRenderer *);
52 static void begin_render (DiaRenderer *);
53 static void end_render (DiaRenderer *);
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);
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 Color *color);
95 static void draw_text (DiaRenderer *renderer,
96 Text *text);
97 static void draw_image (DiaRenderer *renderer,
98 Point *point,
99 real width, real height,
100 DiaImage image);
102 static void draw_rect (DiaRenderer *renderer,
103 Point *ul_corner, Point *lr_corner,
104 Color *color);
105 static void fill_rect (DiaRenderer *renderer,
106 Point *ul_corner, Point *lr_corner,
107 Color *color);
108 static void draw_fill_rect (DiaGdkRenderer *renderer,
109 Point *ul_corner, Point *lr_corner,
110 Color *color, gboolean fill);
111 static void draw_polyline (DiaRenderer *renderer,
112 Point *points, int num_points,
113 Color *color);
114 static void draw_polygon (DiaRenderer *renderer,
115 Point *points, int num_points,
116 Color *color);
117 static void draw_object (DiaRenderer *renderer, DiaObject *object);
119 static real get_text_width (DiaRenderer *renderer,
120 const gchar *text, int length);
122 static void dia_gdk_renderer_class_init (DiaGdkRendererClass *klass);
123 static void renderer_init (DiaGdkRenderer *renderer, void*);
125 static gpointer parent_class = NULL;
127 /** Get the type object for the GdkRenderer.
128 * @return A static GType object describing GdkRenderers.
130 GType
131 dia_gdk_renderer_get_type(void)
133 static GType object_type = 0;
135 if (!object_type)
137 static const GTypeInfo object_info =
139 sizeof (DiaGdkRendererClass),
140 (GBaseInitFunc) NULL,
141 (GBaseFinalizeFunc) NULL,
142 (GClassInitFunc) dia_gdk_renderer_class_init,
143 NULL, /* class_finalize */
144 NULL, /* class_data */
145 sizeof (DiaGdkRenderer),
146 0, /* n_preallocs */
147 (GInstanceInitFunc)renderer_init /* init */
150 object_type = g_type_register_static (DIA_TYPE_RENDERER,
151 "DiaGdkRenderer",
152 &object_info, 0);
155 return object_type;
158 /** Initialize a renderer object.
159 * @param renderer A renderer object to initialize.
160 * @param p Ignored, purpose unknown.
162 static void
163 renderer_init(DiaGdkRenderer *renderer, void* p)
165 renderer->line_width = 1;
166 renderer->line_style = GDK_LINE_SOLID;
167 renderer->cap_style = GDK_CAP_BUTT;
168 renderer->join_style = GDK_JOIN_ROUND;
170 renderer->saved_line_style = LINESTYLE_SOLID;
171 renderer->dash_length = 10;
172 renderer->dot_length = 2;
174 renderer->highlight_color = NULL;
177 /** Clean up a renderer object after use.
178 * @param object Renderer object to free subobjects from.
180 static void
181 renderer_finalize(GObject *object)
183 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
185 if (renderer->pixmap != NULL)
186 gdk_pixmap_unref(renderer->pixmap);
188 if (renderer->gc != NULL)
189 gdk_gc_unref(renderer->gc);
191 if (renderer->clip_region != NULL)
192 gdk_region_destroy(renderer->clip_region);
194 if (renderer->transform)
195 g_object_unref (renderer->transform);
197 G_OBJECT_CLASS (parent_class)->finalize (object);
200 /** Initialize members of the renderer class object. This sets up a bunch
201 * of functions to call for various render functions.
202 * @param klass The class object to initialize.
204 static void
205 dia_gdk_renderer_class_init(DiaGdkRendererClass *klass)
207 GObjectClass *object_class = G_OBJECT_CLASS (klass);
208 DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
210 parent_class = g_type_class_peek_parent (klass);
212 object_class->finalize = renderer_finalize;
214 renderer_class->get_width_pixels = get_width_pixels;
215 renderer_class->get_height_pixels = get_height_pixels;
217 renderer_class->begin_render = begin_render;
218 renderer_class->end_render = end_render;
220 renderer_class->set_linewidth = set_linewidth;
221 renderer_class->set_linecaps = set_linecaps;
222 renderer_class->set_linejoin = set_linejoin;
223 renderer_class->set_linestyle = set_linestyle;
224 renderer_class->set_dashlength = set_dashlength;
225 renderer_class->set_fillstyle = set_fillstyle;
227 renderer_class->draw_line = draw_line;
228 renderer_class->fill_polygon = fill_polygon;
229 renderer_class->draw_rect = draw_rect;
230 renderer_class->fill_rect = fill_rect;
231 renderer_class->draw_arc = draw_arc;
232 renderer_class->fill_arc = fill_arc;
233 renderer_class->draw_ellipse = draw_ellipse;
234 renderer_class->fill_ellipse = fill_ellipse;
236 /* use <draw|fill>_bezier from DiaRenderer */
238 renderer_class->draw_string = draw_string;
239 renderer_class->draw_text = draw_text;
240 renderer_class->draw_text_line = draw_text_line;
241 renderer_class->draw_image = draw_image;
243 /* medium level functions */
244 renderer_class->draw_rect = draw_rect;
245 renderer_class->draw_polyline = draw_polyline;
246 renderer_class->draw_polygon = draw_polygon;
247 renderer_class->draw_object = draw_object;
249 /* Interactive functions */
250 renderer_class->get_text_width = get_text_width;
253 /** Convert Dia color objects into GDK color objects.
254 * If the highlight color is set, that will be used instead. This allows
255 * rendering of an object to do highlight rendering.
256 * @param renderer The renderer to check for highlight color.
257 * @param col A color object to convert.
258 * @param gdk_col Resulting GDK convert.
260 static void
261 renderer_color_convert(DiaGdkRenderer *renderer,
262 Color *col, GdkColor *gdk_col)
264 if (renderer->highlight_color != NULL) {
265 color_convert(renderer->highlight_color, gdk_col);
266 } else {
267 color_convert(col, gdk_col);
271 static void
272 begin_render (DiaRenderer *object)
276 static void
277 end_render (DiaRenderer *object)
281 static void
282 set_linewidth (DiaRenderer *object, real linewidth)
284 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
286 if (renderer->highlight_color != NULL) {
287 /* 6 pixels wide -> 3 pixels beyond normal obj */
288 real border = dia_untransform_length(renderer->transform, 6);
289 linewidth += border;
292 /* 0 == hairline **/
293 renderer->line_width =
294 dia_transform_length(renderer->transform, linewidth);
296 if (renderer->line_width<=0)
297 renderer->line_width = 1; /* Minimum 1 pixel. */
299 gdk_gc_set_line_attributes(renderer->gc,
300 renderer->line_width,
301 renderer->line_style,
302 renderer->cap_style,
303 renderer->join_style);
306 static void
307 set_linecaps (DiaRenderer *object, LineCaps mode)
309 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
311 if (renderer->highlight_color != NULL) {
312 renderer->cap_style = GDK_CAP_ROUND;
313 } else {
314 switch(mode) {
315 case LINECAPS_BUTT:
316 renderer->cap_style = GDK_CAP_BUTT;
317 break;
318 case LINECAPS_ROUND:
319 renderer->cap_style = GDK_CAP_ROUND;
320 break;
321 case LINECAPS_PROJECTING:
322 renderer->cap_style = GDK_CAP_PROJECTING;
323 break;
327 gdk_gc_set_line_attributes(renderer->gc,
328 renderer->line_width,
329 renderer->line_style,
330 renderer->cap_style,
331 renderer->join_style);
334 static void
335 set_linejoin (DiaRenderer *object, LineJoin mode)
337 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
339 if (renderer->highlight_color != NULL) {
340 renderer->join_style = GDK_JOIN_ROUND;
341 } else {
342 switch(mode) {
343 case LINEJOIN_MITER:
344 renderer->join_style = GDK_JOIN_MITER;
345 break;
346 case LINEJOIN_ROUND:
347 renderer->join_style = GDK_JOIN_ROUND;
348 break;
349 case LINEJOIN_BEVEL:
350 renderer->join_style = GDK_JOIN_BEVEL;
351 break;
352 default :
353 /* invalid mode, just here to set a breakpoint */
354 renderer->join_style = GDK_JOIN_ROUND;
355 break;
359 gdk_gc_set_line_attributes(renderer->gc,
360 renderer->line_width,
361 renderer->line_style,
362 renderer->cap_style,
363 renderer->join_style);
366 /** Set the dashes for this renderer.
367 * offset determines where in the pattern the dashes will start.
368 * It is used by the grid in particular to make the grid dashes line up.
370 void
371 dia_gdk_renderer_set_dashes(DiaGdkRenderer *renderer, int offset)
373 gint8 dash_list[6];
374 int hole_width;
375 int pattern_length;
377 switch(renderer->saved_line_style) {
378 case LINESTYLE_SOLID:
379 break;
380 case LINESTYLE_DASHED:
381 dash_list[0] = renderer->dash_length;
382 dash_list[1] = renderer->dash_length;
383 pattern_length = renderer->dash_length*2;
384 gdk_gc_set_dashes(renderer->gc, offset, dash_list, 2);
385 break;
386 case LINESTYLE_DASH_DOT:
387 hole_width = (renderer->dash_length - renderer->dot_length) / 2;
388 if (hole_width==0)
389 hole_width = 1;
390 dash_list[0] = renderer->dash_length;
391 dash_list[1] = hole_width;
392 dash_list[2] = renderer->dot_length;
393 dash_list[3] = hole_width;
394 pattern_length = renderer->dash_length+renderer->dot_length+2*hole_width;
395 gdk_gc_set_dashes(renderer->gc, offset, dash_list, 4);
396 break;
397 case LINESTYLE_DASH_DOT_DOT:
398 hole_width = (renderer->dash_length - 2*renderer->dot_length) / 3;
399 if (hole_width==0)
400 hole_width = 1;
401 dash_list[0] = renderer->dash_length;
402 dash_list[1] = hole_width;
403 dash_list[2] = renderer->dot_length;
404 dash_list[3] = hole_width;
405 dash_list[4] = renderer->dot_length;
406 dash_list[5] = hole_width;
407 pattern_length = renderer->dash_length+2*renderer->dot_length+3*hole_width;
408 gdk_gc_set_dashes(renderer->gc, offset, dash_list, 6);
409 break;
410 case LINESTYLE_DOTTED:
411 dash_list[0] = renderer->dot_length;
412 dash_list[1] = renderer->dot_length;
413 pattern_length = renderer->dot_length;
414 gdk_gc_set_dashes(renderer->gc, offset, dash_list, 2);
415 break;
420 static void
421 set_linestyle (DiaRenderer *object, LineStyle mode)
423 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
425 renderer->saved_line_style = mode;
426 switch(mode) {
427 case LINESTYLE_SOLID:
428 renderer->line_style = GDK_LINE_SOLID;
429 break;
430 case LINESTYLE_DASHED:
431 renderer->line_style = GDK_LINE_ON_OFF_DASH;
432 dia_gdk_renderer_set_dashes(renderer, 0);
433 break;
434 case LINESTYLE_DASH_DOT:
435 renderer->line_style = GDK_LINE_ON_OFF_DASH;
436 dia_gdk_renderer_set_dashes(renderer, 0);
437 break;
438 case LINESTYLE_DASH_DOT_DOT:
439 renderer->line_style = GDK_LINE_ON_OFF_DASH;
440 dia_gdk_renderer_set_dashes(renderer, 0);
441 break;
442 case LINESTYLE_DOTTED:
443 renderer->line_style = GDK_LINE_ON_OFF_DASH;
444 dia_gdk_renderer_set_dashes(renderer, 0);
445 break;
447 gdk_gc_set_line_attributes(renderer->gc,
448 renderer->line_width,
449 renderer->line_style,
450 renderer->cap_style,
451 renderer->join_style);
454 static void
455 set_dashlength (DiaRenderer *object, real length)
457 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
458 /* dot = 10% of len */
459 real ddisp_len;
461 ddisp_len =
462 dia_transform_length(renderer->transform, length);
464 renderer->dash_length = (int)floor(ddisp_len+0.5);
465 renderer->dot_length = (int)floor(ddisp_len*0.1+0.5);
467 if (renderer->dash_length<=0)
468 renderer->dash_length = 1;
469 if (renderer->dash_length>255)
470 renderer->dash_length = 255;
471 if (renderer->dot_length<=0)
472 renderer->dot_length = 1;
473 if (renderer->dot_length>255)
474 renderer->dot_length = 255;
475 set_linestyle(object, renderer->saved_line_style);
478 static void
479 set_fillstyle (DiaRenderer *object, FillStyle mode)
481 switch(mode) {
482 case FILLSTYLE_SOLID:
483 break;
484 default:
485 message_error("gdk_renderer: Unsupported fill mode specified!\n");
489 static void
490 draw_line (DiaRenderer *object, Point *start, Point *end, Color *line_color)
492 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
494 GdkGC *gc = renderer->gc;
495 GdkColor color;
496 int x1,y1,x2,y2;
498 dia_transform_coords(renderer->transform, start->x, start->y, &x1, &y1);
499 dia_transform_coords(renderer->transform, end->x, end->y, &x2, &y2);
501 renderer_color_convert(renderer, line_color, &color);
502 gdk_gc_set_foreground(gc, &color);
504 gdk_draw_line(renderer->pixmap, gc,
505 x1, y1, x2, y2);
508 static void
509 fill_polygon (DiaRenderer *object, Point *points, int num_points, Color *line_color)
511 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
512 GdkGC *gc = renderer->gc;
513 GdkColor color;
514 GdkPoint *gdk_points;
515 int i,x,y;
517 gdk_points = g_new(GdkPoint, num_points);
519 for (i=0;i<num_points;i++) {
520 dia_transform_coords(renderer->transform, points[i].x, points[i].y, &x, &y);
521 gdk_points[i].x = x;
522 gdk_points[i].y = y;
525 renderer_color_convert(renderer, line_color, &color);
526 gdk_gc_set_foreground(gc, &color);
528 gdk_draw_polygon(renderer->pixmap, gc, TRUE, gdk_points, num_points);
529 g_free(gdk_points);
532 static void
533 draw_fill_arc (DiaRenderer *object,
534 Point *center,
535 real width, real height,
536 real angle1, real angle2,
537 Color *color,
538 gboolean fill)
540 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
541 GdkGC *gc = renderer->gc;
542 GdkColor gdkcolor;
543 gint top, left, bottom, right;
544 real dangle;
546 dia_transform_coords(renderer->transform,
547 center->x - width/2, center->y - height/2,
548 &left, &top);
549 dia_transform_coords(renderer->transform,
550 center->x + width/2, center->y + height/2,
551 &right, &bottom);
553 if ((left>right) || (top>bottom))
554 return;
556 renderer_color_convert(renderer, color, &gdkcolor);
557 gdk_gc_set_foreground(gc, &gdkcolor);
559 dangle = angle2-angle1;
560 if (dangle<0)
561 dangle += 360.0;
563 gdk_draw_arc(renderer->pixmap,
564 gc, fill,
565 left, top, right-left+(fill?1:0), bottom-top+(fill?1:0),
566 (int) (angle1*64.0), (int) (dangle*64.0));
568 static void
569 draw_arc (DiaRenderer *object,
570 Point *center,
571 real width, real height,
572 real angle1, real angle2,
573 Color *color)
575 draw_fill_arc (object, center, width, height, angle1, angle2, color, FALSE);
578 static void
579 fill_arc (DiaRenderer *object, Point *center,
580 real width, real height, real angle1, real angle2,
581 Color *color)
583 draw_fill_arc (object, center, width, height, angle1, angle2, color, TRUE);
586 static void
587 draw_ellipse (DiaRenderer *object, Point *center,
588 real width, real height,
589 Color *color)
591 draw_arc(object, center, width, height, 0.0, 360.0, color);
594 static void
595 fill_ellipse (DiaRenderer *object, Point *center,
596 real width, real height, Color *color)
598 fill_arc(object, center, width, height, 0.0, 360.0, color);
601 /* Draw a highlighted version of a string.
603 static void
604 draw_highlighted_string(DiaGdkRenderer *renderer,
605 PangoLayout *layout,
606 int x, int y,
607 GdkColor *color)
609 gint width, height;
611 pango_layout_get_pixel_size(layout, &width, &height);
613 gdk_gc_set_foreground(renderer->gc, color);
615 gdk_draw_rectangle (renderer->pixmap,
616 renderer->gc, TRUE,
617 x-3, y-3,
618 width+6, height+6);
621 static void
622 draw_string (DiaRenderer *object,
623 const gchar *text, Point *pos, Alignment alignment,
624 Color *color)
626 TextLine *text_line = text_line_new(text, object->font, object->font_height);
627 Point realigned_pos = *pos;
628 realigned_pos.x -= text_line_get_alignment_adjustment(text_line, alignment);
629 draw_text_line(object, text_line, &realigned_pos, color);
632 #ifdef HAVE_FREETYPE
633 static void
634 initialize_ft_bitmap(FT_Bitmap *ftbitmap, int width, int height)
636 int rowstride = 32*((width+31)/31);
637 guint8 *graybitmap = (guint8*)g_new0(guint8, height*rowstride);
639 ftbitmap->rows = height;
640 ftbitmap->width = width;
641 ftbitmap->pitch = rowstride;
642 ftbitmap->buffer = graybitmap;
643 ftbitmap->num_grays = 256;
644 ftbitmap->pixel_mode = ft_pixel_mode_grays;
645 ftbitmap->palette_mode = 0;
646 ftbitmap->palette = 0;
649 typedef struct _FreetypeCacheData {
650 int x;
651 int y;
652 int width;
653 int height;
654 GdkPixbuf *rgba;
655 } FreetypeCacheData;
657 static void
658 free_freetype_cache_data(gpointer data) {
659 FreetypeCacheData *ftdata = (FreetypeCacheData*) data;
661 g_object_unref(ftdata->rgba);
662 g_free(ftdata);
664 #endif
666 /** Draw a TextLine object.
667 * @param object The renderer object to use for transform and output
668 * @param text_line The TextLine to render, including font and height.
669 * @param pos The position to render it at.
670 * @param color The color to render it with.
672 static void
673 draw_text_line (DiaRenderer *object, TextLine *text_line,
674 Point *pos, Color *color)
676 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
677 GdkColor gdkcolor;
678 int x,y;
679 Point start_pos;
680 PangoLayout* layout = NULL;
681 const gchar *text = text_line_get_string(text_line);
682 int height_pixels;
683 real font_height = text_line_get_height(text_line);
684 real scale = dia_transform_length(renderer->transform, 1.0);
686 if (text == NULL || *text == '\0') return; /* Don't render empty strings. */
688 point_copy(&start_pos,pos);
690 renderer_color_convert(renderer, color, &gdkcolor);
692 height_pixels = dia_transform_length(renderer->transform, font_height);
693 if (height_pixels < 2) { /* "Greeking" instead of making tiny font */
694 int width_pixels = dia_transform_length(renderer->transform,
695 text_line_get_width(text_line));
696 gdk_gc_set_foreground(renderer->gc, &gdkcolor);
697 gdk_gc_set_dashes(renderer->gc, 0, "\1\2", 2);
698 dia_transform_coords(renderer->transform, start_pos.x, start_pos.y, &x, &y);
699 gdk_draw_line(renderer->pixmap, renderer->gc, x, y, x + width_pixels, y);
700 return;
701 } else {
702 real adjust = 0.0;
704 adjust = text_line_get_ascent(text_line);
705 start_pos.y -= adjust;
707 dia_transform_coords(renderer->transform,
708 start_pos.x, start_pos.y, &x, &y);
710 #ifdef HAVE_FREETYPE
711 /* The cache appears to work, but there's no gain from it yet, since
712 * nobody hangs on to textline objects long enough.
714 #ifdef USE_TEXTLINE_CACHE
715 FreetypeCacheData *cache = text_line_get_renderer_cache(text_line,
716 object,
717 scale);
718 if (cache != NULL) {
719 gdk_draw_pixbuf(renderer->pixmap, renderer->gc, cache->rgba,
720 0, 0, x, y,
721 cache->width, cache->height, GDK_RGB_DITHER_NONE, 0, 0);
722 return;
724 #endif
725 #endif
727 layout = dia_font_build_layout(text, text_line->font,
728 dia_transform_length(renderer->transform, text_line->height)/20.0);
730 text_line_adjust_layout_line(text_line, pango_layout_get_line(layout, 0),
731 scale/20.0);
732 if (renderer->highlight_color != NULL) {
733 draw_highlighted_string(renderer, layout, x, y, &gdkcolor);
734 } else {
735 #if defined HAVE_FREETYPE
737 FT_Bitmap ftbitmap;
738 int width, height;
739 GdkPixbuf *rgba = NULL;
741 width = dia_transform_length(renderer->transform,
742 text_line_get_width(text_line));
743 height = dia_transform_length(renderer->transform,
744 text_line_get_height(text_line));
746 if (width > 0) {
747 int stride;
748 guchar* pixels;
749 int i,j;
750 guint8 *graybitmap;
751 FreetypeCacheData *cache;
753 initialize_ft_bitmap(&ftbitmap, width, height);
754 pango_ft2_render_layout(&ftbitmap, layout, 0, 0);
756 graybitmap = ftbitmap.buffer;
758 rgba = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
759 stride = gdk_pixbuf_get_rowstride(rgba);
760 pixels = gdk_pixbuf_get_pixels(rgba);
761 for (i = 0; i < height; i++) {
762 for (j = 0; j < width; j++) {
763 pixels[i*stride+j*4] = gdkcolor.red>>8;
764 pixels[i*stride+j*4+1] = gdkcolor.green>>8;
765 pixels[i*stride+j*4+2] = gdkcolor.blue>>8;
766 pixels[i*stride+j*4+3] = graybitmap[i*ftbitmap.pitch+j];
769 g_free(graybitmap);
771 gdk_draw_pixbuf(renderer->pixmap, renderer->gc, rgba, 0, 0, x, y, width, height, GDK_RGB_DITHER_NONE, 0, 0);
772 #ifdef USE_TEXTLINE_CACHE
773 /* This is not exactly useful until textline objects start hanging
774 * around longer than the duration of draw_string(). Nor is it
775 * entirely debugged (i.e. it crashes:)
777 cache = g_new(FreetypeCacheData, 1);
778 cache->rgba = rgba;
779 cache->width = width;
780 cache->height = height;
781 text_line_set_renderer_cache(text_line, object,
782 free_freetype_cache_data, scale, cache);
783 #else
784 g_object_unref(G_OBJECT(rgba));
785 #endif
788 #else
789 gdk_gc_set_foreground(renderer->gc, &gdkcolor);
791 gdk_draw_layout(renderer->pixmap, renderer->gc, x, y, layout);
792 #endif
793 g_object_unref(G_OBJECT(layout));
798 /** Caching Pango renderer */
799 static void
800 draw_text(DiaRenderer *renderer, Text *text)
802 Point pos;
803 int i;
805 DIA_RENDERER_GET_CLASS(renderer)->set_font(renderer, text->font, text->height);
806 pos = text->position;
808 for (i=0;i<text->numlines;i++) {
809 DIA_RENDERER_GET_CLASS(renderer)->draw_string(renderer,
810 text->line[i],
811 &pos, text->alignment,
812 &text->color);
813 pos.y += text->height;
818 /* Get the width of the given text in cm */
819 static real
820 get_text_width(DiaRenderer *object,
821 const gchar *text, int length)
823 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
824 real result;
825 TextLine *text_line;
827 if (length != strlen(text)) {
828 char *othertx;
829 int ulen;
830 /* A couple UTF8-chars: æblegrød Š Ť Ž ę ć ń уфхцНОПРЄ є Ґ Њ Ћ Џ */
831 ulen = g_utf8_offset_to_pointer(text, length)-text;
832 if (!g_utf8_validate(text, ulen, NULL)) {
833 g_warning ("Text at char %d not valid\n", length);
835 othertx = g_strndup(text, ulen);
836 text_line = text_line_new(othertx, object->font, object->font_height);
837 } else {
838 text_line = text_line_new(text, object->font, object->font_height);
840 result = text_line_get_width(text_line);
841 text_line_destroy(text_line);
842 return result;
845 static void
846 draw_image (DiaRenderer *object,
847 Point *point,
848 real width, real height,
849 DiaImage image)
851 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
852 if (renderer->highlight_color != NULL) {
853 Point lr;
854 DiaRendererClass *self_class = DIA_RENDERER_GET_CLASS (object);
856 lr = *point;
857 lr.x += width;
858 lr.y += height;
859 self_class->fill_rect(object, point, &lr, renderer->highlight_color);
860 } else {
861 int real_width, real_height, real_x, real_y;
863 real_width = dia_transform_length(renderer->transform, width);
864 real_height = dia_transform_length(renderer->transform, height);
865 dia_transform_coords(renderer->transform, point->x, point->y,
866 &real_x, &real_y);
868 dia_image_draw(image, renderer->pixmap, real_x, real_y,
869 real_width, real_height);
874 * medium level functions
876 static void
877 draw_fill_rect (DiaGdkRenderer *renderer,
878 Point *ul_corner, Point *lr_corner,
879 Color *color, gboolean fill)
881 GdkGC *gc = renderer->gc;
882 GdkColor gdkcolor;
883 gint top, bottom, left, right;
885 dia_transform_coords(renderer->transform,
886 ul_corner->x, ul_corner->y, &left, &top);
887 dia_transform_coords(renderer->transform,
888 lr_corner->x, lr_corner->y, &right, &bottom);
890 if ((left>right) || (top>bottom))
891 return;
893 renderer_color_convert(renderer, color, &gdkcolor);
894 gdk_gc_set_foreground(gc, &gdkcolor);
896 gdk_draw_rectangle (renderer->pixmap,
897 gc, fill,
898 left, top,
899 right-left,
900 bottom-top);
903 static void
904 draw_rect (DiaRenderer *object,
905 Point *ul_corner, Point *lr_corner,
906 Color *color)
908 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
910 draw_fill_rect (renderer, ul_corner, lr_corner, color, FALSE);
913 static void
914 fill_rect (DiaRenderer *object,
915 Point *ul_corner, Point *lr_corner,
916 Color *color)
918 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
920 draw_fill_rect (renderer, ul_corner, lr_corner, color, TRUE);
923 static void
924 draw_polyline (DiaRenderer *self,
925 Point *points, int num_points,
926 Color *line_color)
928 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (self);
929 GdkGC *gc = renderer->gc;
930 GdkColor color;
931 GdkPoint *gdk_points;
932 int i,x,y;
934 gdk_points = g_new(GdkPoint, num_points);
936 for (i=0;i<num_points;i++) {
937 dia_transform_coords(renderer->transform, points[i].x, points[i].y, &x, &y);
938 gdk_points[i].x = x;
939 gdk_points[i].y = y;
942 renderer_color_convert(renderer, line_color, &color);
943 gdk_gc_set_foreground(gc, &color);
945 gdk_draw_lines(renderer->pixmap, gc, gdk_points, num_points);
946 g_free(gdk_points);
949 static void
950 draw_polygon (DiaRenderer *self,
951 Point *points, int num_points,
952 Color *line_color)
954 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (self);
955 GdkGC *gc = renderer->gc;
956 GdkColor color;
957 GdkPoint *gdk_points;
958 int i,x,y;
960 gdk_points = g_new(GdkPoint, num_points);
962 for (i=0;i<num_points;i++) {
963 dia_transform_coords(renderer->transform, points[i].x, points[i].y, &x, &y);
964 gdk_points[i].x = x;
965 gdk_points[i].y = y;
968 renderer_color_convert(renderer, line_color, &color);
969 gdk_gc_set_foreground(gc, &color);
971 gdk_draw_polygon(renderer->pixmap, gc, FALSE, gdk_points, num_points);
972 g_free(gdk_points);
975 static void
976 draw_object (DiaRenderer *renderer, DiaObject *object)
978 if (renderer->is_interactive &&
979 object->highlight_color != NULL) {
980 DiaGdkRenderer *gdk_rend = DIA_GDK_RENDERER(renderer);
981 gdk_rend->highlight_color = object->highlight_color;
982 object->ops->draw(object, renderer);
983 gdk_rend->highlight_color = NULL;
985 object->ops->draw(object, renderer);
988 static int
989 get_width_pixels (DiaRenderer *object)
991 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
992 int width = 0;
994 if (renderer->pixmap)
995 gdk_drawable_get_size (GDK_DRAWABLE (renderer->pixmap), &width, NULL);
997 return width;
1000 static int
1001 get_height_pixels (DiaRenderer *object)
1003 DiaGdkRenderer *renderer = DIA_GDK_RENDERER (object);
1004 int height = 0;
1006 if (renderer->pixmap)
1007 gdk_drawable_get_size (GDK_DRAWABLE (renderer->pixmap), NULL, &height);
1009 return height;