pre3 updates
[dia.git] / app / render_libart.c
blob7bd98c1164634eabaef702b3a3385ed2142aa0fb
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
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.
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.
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.
19 #include <config.h>
21 #include <math.h>
22 #include <string.h> /* strlen */
23 #include <gdk/gdk.h>
25 #include "render_libart.h"
27 #ifdef HAVE_LIBART
29 #include "dialibartrenderer.h"
30 #include <libart_lgpl/art_rgb.h>
31 #include "font.h"
32 #include "color.h"
34 static void clip_region_clear(DiaRenderer *self);
35 static void clip_region_add_rect(DiaRenderer *self,
36 Rectangle *rect);
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);
56 static void
57 dia_libart_renderer_iface_init (DiaInteractiveRendererInterface* iface)
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;
69 DiaRenderer *
70 new_libart_renderer(DiaTransform *trans, int interactive)
72 DiaLibartRenderer *renderer;
73 GType renderer_type = 0;
75 renderer = g_object_new(DIA_TYPE_LIBART_RENDERER, NULL);
76 renderer->transform = trans;
78 if (!DIA_GET_INTERACTIVE_RENDERER_INTERFACE (renderer))
80 static const GInterfaceInfo irenderer_iface_info =
82 (GInterfaceInitFunc) dia_libart_renderer_iface_init,
83 NULL, /* iface_finalize */
84 NULL /* iface_data */
87 renderer_type = DIA_TYPE_LIBART_RENDERER;
88 /* register the interactive renderer interface */
89 g_type_add_interface_static (renderer_type,
90 DIA_TYPE_INTERACTIVE_RENDERER_INTERFACE,
91 &irenderer_iface_info);
93 renderer->parent_instance.is_interactive = interactive;
95 return DIA_RENDERER (renderer);
98 static void
99 set_size(DiaRenderer *self, gpointer window,
100 int width, int height)
102 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
103 int i;
105 if ( (renderer->pixel_width==width) &&
106 (renderer->pixel_height==height) )
107 return;
109 if (renderer->rgb_buffer != NULL) {
110 g_free(renderer->rgb_buffer);
113 renderer->rgb_buffer = g_new (guint8, width * height * 3);
114 for (i=0;i<width * height * 3;i++)
115 renderer->rgb_buffer[i] = 0xff;
116 renderer->pixel_width = width;
117 renderer->pixel_height = height;
120 static void
121 copy_to_window (DiaRenderer *self, gpointer window,
122 int x, int y, int width, int height)
124 GdkGC *copy_gc;
125 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
126 int w;
128 copy_gc = gdk_gc_new(GDK_WINDOW(window));
130 w = renderer->pixel_width;
132 gdk_draw_rgb_image(window,
133 copy_gc,
134 x,y,
135 width, height,
136 GDK_RGB_DITHER_NONE,
137 renderer->rgb_buffer+x*3+y*3*w,
138 w*3);
139 g_object_unref(copy_gc);
142 static void
143 clip_region_clear(DiaRenderer *self)
145 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
147 renderer->clip_rect_empty = 1;
148 renderer->clip_rect.top = 0;
149 renderer->clip_rect.bottom = 0;
150 renderer->clip_rect.left = 0;
151 renderer->clip_rect.right = 0;
154 static void
155 clip_region_add_rect(DiaRenderer *self,
156 Rectangle *rect)
158 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
159 int x1,y1;
160 int x2,y2;
161 IntRectangle r;
163 dia_transform_coords(renderer->transform, rect->left, rect->top, &x1, &y1);
164 dia_transform_coords(renderer->transform, rect->right, rect->bottom, &x2, &y2);
166 if (x1 < 0)
167 x1 = 0;
168 if (y1 < 0)
169 y1 = 0;
170 if (x2 >= renderer->pixel_width)
171 x2 = renderer->pixel_width - 1;
172 if (y2 >= renderer->pixel_height)
173 y2 = renderer->pixel_height - 1;
175 r.top = y1;
176 r.bottom = y2;
177 r.left = x1;
178 r.right = x2;
180 if (renderer->clip_rect_empty) {
181 renderer->clip_rect = r;
182 renderer->clip_rect_empty = 0;
183 } else {
184 int_rectangle_union(&renderer->clip_rect, &r);
189 /* BIG FAT WARNING:
190 * This code is used to draw pixel based stuff in the RGB buffer.
191 * This code is *NOT* as efficient as it could be!
194 /* All lines and rectangles specifies the coordinates inclusive.
195 * This means that a line from x1 to x2 renders both points.
196 * If a length is specified the line x to (inclusive) x+width is rendered.
198 * The boundaries of the clipping rectangle *are* rendered.
199 * so min=5 and max=10 means point 5 and 10 might be rendered to.
202 /* If the start-end interval is totaly outside the min-max,
203 then the returned clipped values can have len<0! */
204 #define CLIP_1D_LEN(min, max, start, len) \
205 if ((start) < (min)) { \
206 (len) -= (min) - (start); \
207 (start) = (min); \
209 if ((start)+(len) > (max)) { \
210 (len) = (max) - (start); \
213 /* Does no clipping! */
214 static void
215 draw_hline(DiaRenderer *self,
216 int x, int y, int length,
217 guint8 r, guint8 g, guint8 b)
219 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
220 int stride;
221 guint8 *ptr;
223 stride = renderer->pixel_width*3;
224 ptr = renderer->rgb_buffer + x*3 + y*stride;
225 if (length>=0)
226 art_rgb_fill_run(ptr, r, g, b, length+1);
229 /* Does no clipping! */
230 static void
231 draw_vline(DiaRenderer *self,
232 int x, int y, int height,
233 guint8 r, guint8 g, guint8 b)
235 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
236 int stride;
237 guint8 *ptr;
239 stride = renderer->pixel_width*3;
240 ptr = renderer->rgb_buffer + x*3 + y*stride;
241 height+=y;
242 while (y<=height) {
243 *ptr++ = r;
244 *ptr++ = g;
245 *ptr++ = b;
246 ptr += stride - 3;
247 y++;
251 static void
252 draw_pixel_line(DiaRenderer *self,
253 int x1, int y1,
254 int x2, int y2,
255 Color *color)
257 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
258 guint8 r,g,b;
259 guint8 *ptr;
260 int start, len;
261 int stride;
262 int i;
263 int x, y;
264 int dx, dy, adx, ady;
265 int incx, incy;
266 int incx_ptr, incy_ptr;
267 int frac_pos;
268 IntRectangle *clip_rect;
271 r = color->red*0xff;
272 g = color->green*0xff;
273 b = color->blue*0xff;
275 if (y1==y2) { /* Horizontal line */
276 start = x1;
277 len = x2-x1;
278 CLIP_1D_LEN(renderer->clip_rect.left, renderer->clip_rect.right, start, len);
280 /* top line */
281 if ( (y1>=renderer->clip_rect.top) &&
282 (y1<=renderer->clip_rect.bottom) ) {
283 draw_hline(self, start, y1, len, r, g, b);
285 return;
288 if (x1==x2) { /* Vertical line */
289 start = y1;
290 len = y2-y1;
291 CLIP_1D_LEN(renderer->clip_rect.top, renderer->clip_rect.bottom, start, len);
293 /* left line */
294 if ( (x1>=renderer->clip_rect.left) &&
295 (x1<=renderer->clip_rect.right) ) {
296 draw_vline(self, x1, start, len, r, g, b);
298 return;
301 /* Ugh, kill me slowly for writing this line-drawer.
302 * It is actually a standard bresenham, but not very optimized.
303 * It is also not very well tested.
306 stride = renderer->pixel_width*3;
307 clip_rect = &renderer->clip_rect;
309 dx = x2-x1;
310 dy = y2-y1;
311 adx = (dx>=0)?dx:-dx;
312 ady = (dy>=0)?dy:-dy;
314 x = x1; y = y1;
315 ptr = renderer->rgb_buffer + x*3 + y*stride;
317 if (adx>=ady) { /* x-major */
318 if (dx>0) {
319 incx = 1;
320 incx_ptr = 3;
321 } else {
322 incx = -1;
323 incx_ptr = -3;
325 if (dy>0) {
326 incy = 1;
327 incy_ptr = stride;
328 } else {
329 incy = -1;
330 incy_ptr = -stride;
332 frac_pos = adx;
334 for (i=0;i<=adx;i++) {
335 /* Amazing... He does the clipping in the inner loop!
336 It must be horribly inefficient! */
337 if ( (x>=clip_rect->left) &&
338 (x<=clip_rect->right) &&
339 (y>=clip_rect->top) &&
340 (y<=clip_rect->bottom) ) {
341 ptr[0] = r;
342 ptr[1] = g;
343 ptr[2] = b;
345 x += incx;
346 ptr += incx_ptr;
347 frac_pos += ady*2;
348 if ((frac_pos > 2*adx) || ((dy>0)&&(frac_pos == 2*adx))) {
349 y += incy;
350 ptr += incy_ptr;
351 frac_pos -= 2*adx;
354 } else { /* y-major */
355 if (dx>0) {
356 incx = 1;
357 incx_ptr = 3;
358 } else {
359 incx = -1;
360 incx_ptr = -3;
362 if (dy>0) {
363 incy = 1;
364 incy_ptr = stride;
365 } else {
366 incy = -1;
367 incy_ptr = -stride;
369 frac_pos = ady;
371 for (i=0;i<=ady;i++) {
372 /* Amazing... He does the clipping in the inner loop!
373 It must be horribly inefficient! */
374 if ( (x>=clip_rect->left) &&
375 (x<=clip_rect->right) &&
376 (y>=clip_rect->top) &&
377 (y<=clip_rect->bottom) ) {
378 ptr[0] = r;
379 ptr[1] = g;
380 ptr[2] = b;
382 y += incy;
383 ptr += incy_ptr;
384 frac_pos += adx*2;
385 if ((frac_pos > 2*ady) || ((dx>0)&&(frac_pos == 2*ady))) {
386 x += incx;
387 ptr += incx_ptr;
388 frac_pos -= 2*ady;
394 static void
395 draw_pixel_rect(DiaRenderer *self,
396 int x, int y,
397 int width, int height,
398 Color *color)
400 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
401 guint8 r,g,b;
402 int start, len;
403 int stride;
405 r = color->red*0xff;
406 g = color->green*0xff;
407 b = color->blue*0xff;
409 stride = renderer->pixel_width*3;
411 /* clip in x */
412 start = x;
413 len = width;
414 CLIP_1D_LEN(renderer->clip_rect.left, renderer->clip_rect.right, start, len);
416 /* top line */
417 if ( (y>=renderer->clip_rect.top) &&
418 (y<=renderer->clip_rect.bottom) ) {
419 draw_hline(self, start, y, len, r, g, b);
422 /* bottom line */
423 if ( (y+height>=renderer->clip_rect.top) &&
424 (y+height<=renderer->clip_rect.bottom) ) {
425 draw_hline(self, start, y+height, len, r, g, b);
428 /* clip in y */
429 start = y;
430 len = height;
431 CLIP_1D_LEN(renderer->clip_rect.top, renderer->clip_rect.bottom, start, len);
433 /* left line */
434 if ( (x>=renderer->clip_rect.left) &&
435 (x<renderer->clip_rect.right) ) {
436 draw_vline(self, x, start, len, r, g, b);
439 /* right line */
440 if ( (x+width>=renderer->clip_rect.left) &&
441 (x+width<renderer->clip_rect.right) ) {
442 draw_vline(self, x+width, start, len, r, g, b);
446 static void
447 fill_pixel_rect(DiaRenderer *self,
448 int x, int y,
449 int width, int height,
450 Color *color)
452 DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self);
453 guint8 r,g,b;
454 guint8 *ptr;
455 int i;
456 int stride;
459 CLIP_1D_LEN(renderer->clip_rect.left, renderer->clip_rect.right, x, width);
460 if (width < 0)
461 return;
463 CLIP_1D_LEN(renderer->clip_rect.top, renderer->clip_rect.bottom, y, height);
464 if (height < 0)
465 return;
467 r = color->red*0xff;
468 g = color->green*0xff;
469 b = color->blue*0xff;
471 stride = renderer->pixel_width*3;
472 ptr = renderer->rgb_buffer + x*3 + y*stride;
473 for (i=0;i<=height;i++) {
474 art_rgb_fill_run(ptr, r, g, b, width+1);
475 ptr += stride;
479 #else
481 DiaRenderer *
482 new_libart_renderer(DiaTransform *transform, int interactive)
484 return NULL;
487 #endif