1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <config.h>
20 
21 #include <math.h>
22 #include <string.h> /* strlen */
23 #include <gdk/gdk.h>
24 
25 #include "render_libart.h"
26 
27 #ifdef HAVE_LIBART
28 
29 #include "dialibartrenderer.h"
30 #include <libart_lgpl/art_rgb.h>
31 #include "font.h"
32 #include "color.h"
33 
34 static void clip_region_clear(DiaRenderer *self);
35 static void clip_region_add_rect(DiaRenderer *self,
36 				 Rectangle *rect);
37 
38 static void draw_pixel_line(DiaRenderer *self,
39 			    int x1, int y1,
40 			    int x2, int y2,
41 			    Color *color);
42 static void draw_pixel_rect(DiaRenderer *self,
43 				 int x, int y,
44 				 int width, int height,
45 				 Color *color);
46 static void fill_pixel_rect(DiaRenderer *self,
47 				 int x, int y,
48 				 int width, int height,
49 				 Color *color);
50 static void set_size(DiaRenderer *self, gpointer window,
51                      int width, int height);
52 static void copy_to_window (DiaRenderer *self, gpointer window,
53                             int x, int y, int width, int height);
54 
55 
56 void
dia_libart_renderer_iface_init(DiaInteractiveRendererInterface * iface)57 dia_libart_renderer_iface_init (DiaInteractiveRendererInterface* iface)
58 {
59   iface->clip_region_clear = clip_region_clear;
60   iface->clip_region_add_rect = clip_region_add_rect;
61   iface->draw_pixel_line = draw_pixel_line;
62   iface->draw_pixel_rect = draw_pixel_rect;
63   iface->fill_pixel_rect = fill_pixel_rect;
64   iface->copy_to_window = copy_to_window;
65   iface->set_size = set_size;
66 }
67 
68 
69 DiaRenderer *
new_libart_renderer(DiaTransform * trans,int interactive)70 new_libart_renderer(DiaTransform *trans, int interactive)
71 {
72   DiaLibartRenderer *renderer;
73   GType renderer_type = 0;
74 
75   renderer = g_object_new(DIA_TYPE_LIBART_RENDERER, NULL);
76   renderer->transform = trans;
77   renderer->parent_instance.is_interactive = interactive;
78 
79   return DIA_RENDERER (renderer);
80 }
81 
82 static void
set_size(DiaRenderer * self,gpointer window,int width,int height)83 set_size(DiaRenderer *self, gpointer window,
84          int width, int height)
85 {
86   DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
87   int i;
88 
89   if ( (renderer->pixel_width==width) &&
90        (renderer->pixel_height==height) )
91     return;
92 
93   if (renderer->rgb_buffer != NULL) {
94     g_free(renderer->rgb_buffer);
95   }
96 
97   renderer->rgb_buffer = g_new (guint8, width * height * 3);
98   for (i=0;i<width * height * 3;i++)
99     renderer->rgb_buffer[i] = 0xff;
100   renderer->pixel_width = width;
101   renderer->pixel_height = height;
102 }
103 
104 static void
copy_to_window(DiaRenderer * self,gpointer window,int x,int y,int width,int height)105 copy_to_window (DiaRenderer *self, gpointer window,
106                 int x, int y, int width, int height)
107 {
108   GdkGC *copy_gc;
109   DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
110   int w;
111 
112   copy_gc = gdk_gc_new(GDK_WINDOW(window));
113 
114   w = renderer->pixel_width;
115 
116   gdk_draw_rgb_image(window,
117 		     copy_gc,
118 		     x,y,
119 		     width, height,
120 		     GDK_RGB_DITHER_NONE,
121 		     renderer->rgb_buffer+x*3+y*3*w,
122 		     w*3);
123   g_object_unref(copy_gc);
124 }
125 
126 static void
clip_region_clear(DiaRenderer * self)127 clip_region_clear(DiaRenderer *self)
128 {
129   DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
130 
131   renderer->clip_rect_empty = 1;
132   renderer->clip_rect.top = 0;
133   renderer->clip_rect.bottom = 0;
134   renderer->clip_rect.left = 0;
135   renderer->clip_rect.right = 0;
136 }
137 
138 static void
clip_region_add_rect(DiaRenderer * self,Rectangle * rect)139 clip_region_add_rect(DiaRenderer *self,
140 		     Rectangle *rect)
141 {
142   DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
143   int x1,y1;
144   int x2,y2;
145   IntRectangle r;
146 
147   dia_transform_coords(renderer->transform, rect->left, rect->top,  &x1, &y1);
148   dia_transform_coords(renderer->transform, rect->right, rect->bottom,  &x2, &y2);
149 
150   if (x1 < 0)
151     x1 = 0;
152   if (y1 < 0)
153     y1 = 0;
154   if (x2 >= renderer->pixel_width)
155     x2 = renderer->pixel_width - 1;
156   if (y2 >= renderer->pixel_height)
157     y2 = renderer->pixel_height - 1;
158 
159   r.top = y1;
160   r.bottom = y2;
161   r.left = x1;
162   r.right = x2;
163 
164   if (renderer->clip_rect_empty) {
165     renderer->clip_rect = r;
166     renderer->clip_rect_empty = 0;
167   } else {
168     int_rectangle_union(&renderer->clip_rect, &r);
169   }
170 }
171 
172 
173 /* BIG FAT WARNING:
174  * This code is used to draw pixel based stuff in the RGB buffer.
175  * This code is *NOT* as efficient as it could be!
176  */
177 
178 /* All lines and rectangles specifies the coordinates inclusive.
179  * This means that a line from x1 to x2 renders both points.
180  * If a length is specified the line x to (inclusive) x+width is rendered.
181  *
182  * The boundaries of the clipping rectangle *are* rendered.
183  * so min=5 and max=10 means point 5 and 10 might be rendered to.
184  */
185 
186 /* If the start-end interval is totaly outside the min-max,
187    then the returned clipped values can have len<0! */
188 #define CLIP_1D_LEN(min, max, start, len) \
189    if ((start) < (min)) {                 \
190       (len) -= (min) - (start);           \
191       (start) = (min);                    \
192    }                                      \
193    if ((start)+(len) > (max)) {           \
194       (len) = (max) - (start);            \
195    }
196 
197 /* Does no clipping! */
198 static void
draw_hline(DiaRenderer * self,int x,int y,int length,guint8 r,guint8 g,guint8 b)199 draw_hline(DiaRenderer *self,
200 	   int x, int y, int length,
201 	   guint8 r, guint8 g, guint8 b)
202 {
203   DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
204   int stride;
205   guint8 *ptr;
206 
207   stride = renderer->pixel_width*3;
208   ptr = renderer->rgb_buffer + x*3 + y*stride;
209   if (length>=0)
210     art_rgb_fill_run(ptr, r, g, b, length+1);
211 }
212 
213 /* Does no clipping! */
214 static void
draw_vline(DiaRenderer * self,int x,int y,int height,guint8 r,guint8 g,guint8 b)215 draw_vline(DiaRenderer *self,
216 	   int x, int y, int height,
217 	   guint8 r, guint8 g, guint8 b)
218 {
219   DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
220   int stride;
221   guint8 *ptr;
222 
223   stride = renderer->pixel_width*3;
224   ptr = renderer->rgb_buffer + x*3 + y*stride;
225   height+=y;
226   while (y<=height) {
227     *ptr++ = r;
228     *ptr++ = g;
229     *ptr++ = b;
230     ptr += stride - 3;
231     y++;
232   }
233 }
234 
235 static void
draw_pixel_line(DiaRenderer * self,int x1,int y1,int x2,int y2,Color * color)236 draw_pixel_line(DiaRenderer *self,
237 		int x1, int y1,
238 		int x2, int y2,
239 		Color *color)
240 {
241   DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
242   guint8 r,g,b;
243   guint8 *ptr;
244   int start, len;
245   int stride;
246   int i;
247   int x, y;
248   int dx, dy, adx, ady;
249   int incx, incy;
250   int incx_ptr, incy_ptr;
251   int frac_pos;
252   IntRectangle *clip_rect;
253 
254 
255   r = color->red*0xff;
256   g = color->green*0xff;
257   b = color->blue*0xff;
258 
259   if (y1==y2) { /* Horizontal line */
260     start = x1;
261     len = x2-x1;
262     CLIP_1D_LEN(renderer->clip_rect.left, renderer->clip_rect.right, start, len);
263 
264     /* top line */
265     if ( (y1>=renderer->clip_rect.top) &&
266 	 (y1<=renderer->clip_rect.bottom) ) {
267       draw_hline(self, start, y1, len, r, g, b);
268     }
269     return;
270   }
271 
272   if (x1==x2) { /* Vertical line */
273     start = y1;
274     len = y2-y1;
275     CLIP_1D_LEN(renderer->clip_rect.top, renderer->clip_rect.bottom, start, len);
276 
277     /* left line */
278     if ( (x1>=renderer->clip_rect.left) &&
279 	 (x1<=renderer->clip_rect.right) ) {
280       draw_vline(self, x1, start, len, r, g, b);
281     }
282     return;
283   }
284 
285   /* Ugh, kill me slowly for writing this line-drawer.
286    * It is actually a standard bresenham, but not very optimized.
287    * It is also not very well tested.
288    */
289 
290   stride = renderer->pixel_width*3;
291   clip_rect = &renderer->clip_rect;
292 
293   dx = x2-x1;
294   dy = y2-y1;
295   adx = (dx>=0)?dx:-dx;
296   ady = (dy>=0)?dy:-dy;
297 
298   x = x1; y = y1;
299   ptr = renderer->rgb_buffer + x*3 + y*stride;
300 
301   if (adx>=ady) { /* x-major */
302     if (dx>0) {
303       incx = 1;
304       incx_ptr = 3;
305     } else {
306       incx = -1;
307       incx_ptr = -3;
308     }
309     if (dy>0) {
310       incy = 1;
311       incy_ptr = stride;
312     } else {
313       incy = -1;
314       incy_ptr = -stride;
315     }
316     frac_pos = adx;
317 
318     for (i=0;i<=adx;i++) {
319       /* Amazing... He does the clipping in the inner loop!
320 	 It must be horribly inefficient! */
321       if ( (x>=clip_rect->left) &&
322 	   (x<=clip_rect->right) &&
323 	   (y>=clip_rect->top) &&
324 	   (y<=clip_rect->bottom) ) {
325 	ptr[0] = r;
326 	ptr[1] = g;
327 	ptr[2] = b;
328       }
329       x += incx;
330       ptr += incx_ptr;
331       frac_pos += ady*2;
332       if ((frac_pos > 2*adx) || ((dy>0)&&(frac_pos == 2*adx))) {
333 	y += incy;
334 	ptr += incy_ptr;
335 	frac_pos -= 2*adx;
336       }
337     }
338   } else { /* y-major */
339     if (dx>0) {
340       incx = 1;
341       incx_ptr = 3;
342     } else {
343       incx = -1;
344       incx_ptr = -3;
345     }
346     if (dy>0) {
347       incy = 1;
348       incy_ptr = stride;
349     } else {
350       incy = -1;
351       incy_ptr = -stride;
352     }
353     frac_pos = ady;
354 
355     for (i=0;i<=ady;i++) {
356       /* Amazing... He does the clipping in the inner loop!
357 	 It must be horribly inefficient! */
358       if ( (x>=clip_rect->left) &&
359 	   (x<=clip_rect->right) &&
360 	   (y>=clip_rect->top) &&
361 	   (y<=clip_rect->bottom) ) {
362 	ptr[0] = r;
363 	ptr[1] = g;
364 	ptr[2] = b;
365       }
366       y += incy;
367       ptr += incy_ptr;
368       frac_pos += adx*2;
369       if ((frac_pos > 2*ady) || ((dx>0)&&(frac_pos == 2*ady))) {
370 	x += incx;
371 	ptr += incx_ptr;
372 	frac_pos -= 2*ady;
373       }
374     }
375   }
376 }
377 
378 static void
draw_pixel_rect(DiaRenderer * self,int x,int y,int width,int height,Color * color)379 draw_pixel_rect(DiaRenderer *self,
380 		int x, int y,
381 		int width, int height,
382 		Color *color)
383 {
384   DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
385   guint8 r,g,b;
386   int start, len;
387   int stride;
388 
389   r = color->red*0xff;
390   g = color->green*0xff;
391   b = color->blue*0xff;
392 
393   stride = renderer->pixel_width*3;
394 
395   /* clip in x */
396   start = x;
397   len = width;
398   CLIP_1D_LEN(renderer->clip_rect.left, renderer->clip_rect.right, start, len);
399 
400   /* top line */
401   if ( (y>=renderer->clip_rect.top) &&
402        (y<=renderer->clip_rect.bottom) ) {
403     draw_hline(self, start, y, len, r, g, b);
404   }
405 
406   /* bottom line */
407   if ( (y+height>=renderer->clip_rect.top) &&
408        (y+height<=renderer->clip_rect.bottom) ) {
409     draw_hline(self, start, y+height, len, r, g, b);
410   }
411 
412   /* clip in y */
413   start = y;
414   len = height;
415   CLIP_1D_LEN(renderer->clip_rect.top, renderer->clip_rect.bottom, start, len);
416 
417   /* left line */
418   if ( (x>=renderer->clip_rect.left) &&
419        (x<renderer->clip_rect.right) ) {
420     draw_vline(self, x, start, len, r, g, b);
421   }
422 
423   /* right line */
424   if ( (x+width>=renderer->clip_rect.left) &&
425        (x+width<renderer->clip_rect.right) ) {
426     draw_vline(self, x+width, start, len, r, g, b);
427   }
428 }
429 
430 static void
fill_pixel_rect(DiaRenderer * self,int x,int y,int width,int height,Color * color)431 fill_pixel_rect(DiaRenderer *self,
432 		int x, int y,
433 		int width, int height,
434 		Color *color)
435 {
436   DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
437   guint8 r,g,b;
438   guint8 *ptr;
439   int i;
440   int stride;
441 
442 
443   CLIP_1D_LEN(renderer->clip_rect.left, renderer->clip_rect.right, x, width);
444   if (width < 0)
445     return;
446 
447   CLIP_1D_LEN(renderer->clip_rect.top, renderer->clip_rect.bottom, y, height);
448   if (height < 0)
449     return;
450 
451   r = color->red*0xff;
452   g = color->green*0xff;
453   b = color->blue*0xff;
454 
455   stride = renderer->pixel_width*3;
456   ptr = renderer->rgb_buffer + x*3 + y*stride;
457   for (i=0;i<=height;i++) {
458     art_rgb_fill_run(ptr, r, g, b, width+1);
459     ptr += stride;
460   }
461 }
462 
463 #else
464 
465 DiaRenderer *
new_libart_renderer(DiaTransform * transform,int interactive)466 new_libart_renderer(DiaTransform *transform, int interactive)
467 {
468   return NULL;
469 }
470 
471 #endif
472