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