/* Dia -- an diagram creation/manipulation program * Copyright (C) 1998 Alexander Larsson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include /* strlen */ #include #include "render_libart.h" #ifdef HAVE_LIBART #include "dialibartrenderer.h" #include #include "font.h" #include "color.h" static void clip_region_clear(DiaRenderer *self); static void clip_region_add_rect(DiaRenderer *self, Rectangle *rect); static void draw_pixel_line(DiaRenderer *self, int x1, int y1, int x2, int y2, Color *color); static void draw_pixel_rect(DiaRenderer *self, int x, int y, int width, int height, Color *color); static void fill_pixel_rect(DiaRenderer *self, int x, int y, int width, int height, Color *color); static void set_size(DiaRenderer *self, gpointer window, int width, int height); static void copy_to_window (DiaRenderer *self, gpointer window, int x, int y, int width, int height); static void dia_libart_renderer_iface_init (DiaInteractiveRendererInterface* iface) { iface->clip_region_clear = clip_region_clear; iface->clip_region_add_rect = clip_region_add_rect; iface->draw_pixel_line = draw_pixel_line; iface->draw_pixel_rect = draw_pixel_rect; iface->fill_pixel_rect = fill_pixel_rect; iface->copy_to_window = copy_to_window; iface->set_size = set_size; } DiaRenderer * new_libart_renderer(DiaTransform *trans, int interactive) { DiaLibartRenderer *renderer; GType renderer_type = 0; renderer = g_object_new(DIA_TYPE_LIBART_RENDERER, NULL); renderer->transform = trans; if (!DIA_GET_INTERACTIVE_RENDERER_INTERFACE (renderer)) { static const GInterfaceInfo irenderer_iface_info = { (GInterfaceInitFunc) dia_libart_renderer_iface_init, NULL, /* iface_finalize */ NULL /* iface_data */ }; renderer_type = DIA_TYPE_LIBART_RENDERER; /* register the interactive renderer interface */ g_type_add_interface_static (renderer_type, DIA_TYPE_INTERACTIVE_RENDERER_INTERFACE, &irenderer_iface_info); } renderer->parent_instance.is_interactive = interactive; return DIA_RENDERER (renderer); } static void set_size(DiaRenderer *self, gpointer window, int width, int height) { DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self); int i; if ( (renderer->pixel_width==width) && (renderer->pixel_height==height) ) return; if (renderer->rgb_buffer != NULL) { g_free(renderer->rgb_buffer); } renderer->rgb_buffer = g_new (guint8, width * height * 3); for (i=0;irgb_buffer[i] = 0xff; renderer->pixel_width = width; renderer->pixel_height = height; } static void copy_to_window (DiaRenderer *self, gpointer window, int x, int y, int width, int height) { GdkGC *copy_gc; DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self); int w; copy_gc = gdk_gc_new(GDK_WINDOW(window)); w = renderer->pixel_width; gdk_draw_rgb_image(window, copy_gc, x,y, width, height, GDK_RGB_DITHER_NONE, renderer->rgb_buffer+x*3+y*3*w, w*3); g_object_unref(copy_gc); } static void clip_region_clear(DiaRenderer *self) { DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self); renderer->clip_rect_empty = 1; renderer->clip_rect.top = 0; renderer->clip_rect.bottom = 0; renderer->clip_rect.left = 0; renderer->clip_rect.right = 0; } static void clip_region_add_rect(DiaRenderer *self, Rectangle *rect) { DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self); int x1,y1; int x2,y2; IntRectangle r; dia_transform_coords(renderer->transform, rect->left, rect->top, &x1, &y1); dia_transform_coords(renderer->transform, rect->right, rect->bottom, &x2, &y2); if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x2 >= renderer->pixel_width) x2 = renderer->pixel_width - 1; if (y2 >= renderer->pixel_height) y2 = renderer->pixel_height - 1; r.top = y1; r.bottom = y2; r.left = x1; r.right = x2; if (renderer->clip_rect_empty) { renderer->clip_rect = r; renderer->clip_rect_empty = 0; } else { int_rectangle_union(&renderer->clip_rect, &r); } } /* BIG FAT WARNING: * This code is used to draw pixel based stuff in the RGB buffer. * This code is *NOT* as efficient as it could be! */ /* All lines and rectangles specifies the coordinates inclusive. * This means that a line from x1 to x2 renders both points. * If a length is specified the line x to (inclusive) x+width is rendered. * * The boundaries of the clipping rectangle *are* rendered. * so min=5 and max=10 means point 5 and 10 might be rendered to. */ /* If the start-end interval is totaly outside the min-max, then the returned clipped values can have len<0! */ #define CLIP_1D_LEN(min, max, start, len) \ if ((start) < (min)) { \ (len) -= (min) - (start); \ (start) = (min); \ } \ if ((start)+(len) > (max)) { \ (len) = (max) - (start); \ } /* Does no clipping! */ static void draw_hline(DiaRenderer *self, int x, int y, int length, guint8 r, guint8 g, guint8 b) { DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self); int stride; guint8 *ptr; stride = renderer->pixel_width*3; ptr = renderer->rgb_buffer + x*3 + y*stride; if (length>=0) art_rgb_fill_run(ptr, r, g, b, length+1); } /* Does no clipping! */ static void draw_vline(DiaRenderer *self, int x, int y, int height, guint8 r, guint8 g, guint8 b) { DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self); int stride; guint8 *ptr; stride = renderer->pixel_width*3; ptr = renderer->rgb_buffer + x*3 + y*stride; height+=y; while (y<=height) { *ptr++ = r; *ptr++ = g; *ptr++ = b; ptr += stride - 3; y++; } } static void draw_pixel_line(DiaRenderer *self, int x1, int y1, int x2, int y2, Color *color) { DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self); guint8 r,g,b; guint8 *ptr; int start, len; int stride; int i; int x, y; int dx, dy, adx, ady; int incx, incy; int incx_ptr, incy_ptr; int frac_pos; IntRectangle *clip_rect; r = color->red*0xff; g = color->green*0xff; b = color->blue*0xff; if (y1==y2) { /* Horizontal line */ start = x1; len = x2-x1; CLIP_1D_LEN(renderer->clip_rect.left, renderer->clip_rect.right, start, len); /* top line */ if ( (y1>=renderer->clip_rect.top) && (y1<=renderer->clip_rect.bottom) ) { draw_hline(self, start, y1, len, r, g, b); } return; } if (x1==x2) { /* Vertical line */ start = y1; len = y2-y1; CLIP_1D_LEN(renderer->clip_rect.top, renderer->clip_rect.bottom, start, len); /* left line */ if ( (x1>=renderer->clip_rect.left) && (x1<=renderer->clip_rect.right) ) { draw_vline(self, x1, start, len, r, g, b); } return; } /* Ugh, kill me slowly for writing this line-drawer. * It is actually a standard bresenham, but not very optimized. * It is also not very well tested. */ stride = renderer->pixel_width*3; clip_rect = &renderer->clip_rect; dx = x2-x1; dy = y2-y1; adx = (dx>=0)?dx:-dx; ady = (dy>=0)?dy:-dy; x = x1; y = y1; ptr = renderer->rgb_buffer + x*3 + y*stride; if (adx>=ady) { /* x-major */ if (dx>0) { incx = 1; incx_ptr = 3; } else { incx = -1; incx_ptr = -3; } if (dy>0) { incy = 1; incy_ptr = stride; } else { incy = -1; incy_ptr = -stride; } frac_pos = adx; for (i=0;i<=adx;i++) { /* Amazing... He does the clipping in the inner loop! It must be horribly inefficient! */ if ( (x>=clip_rect->left) && (x<=clip_rect->right) && (y>=clip_rect->top) && (y<=clip_rect->bottom) ) { ptr[0] = r; ptr[1] = g; ptr[2] = b; } x += incx; ptr += incx_ptr; frac_pos += ady*2; if ((frac_pos > 2*adx) || ((dy>0)&&(frac_pos == 2*adx))) { y += incy; ptr += incy_ptr; frac_pos -= 2*adx; } } } else { /* y-major */ if (dx>0) { incx = 1; incx_ptr = 3; } else { incx = -1; incx_ptr = -3; } if (dy>0) { incy = 1; incy_ptr = stride; } else { incy = -1; incy_ptr = -stride; } frac_pos = ady; for (i=0;i<=ady;i++) { /* Amazing... He does the clipping in the inner loop! It must be horribly inefficient! */ if ( (x>=clip_rect->left) && (x<=clip_rect->right) && (y>=clip_rect->top) && (y<=clip_rect->bottom) ) { ptr[0] = r; ptr[1] = g; ptr[2] = b; } y += incy; ptr += incy_ptr; frac_pos += adx*2; if ((frac_pos > 2*ady) || ((dx>0)&&(frac_pos == 2*ady))) { x += incx; ptr += incx_ptr; frac_pos -= 2*ady; } } } } static void draw_pixel_rect(DiaRenderer *self, int x, int y, int width, int height, Color *color) { DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self); guint8 r,g,b; int start, len; int stride; r = color->red*0xff; g = color->green*0xff; b = color->blue*0xff; stride = renderer->pixel_width*3; /* clip in x */ start = x; len = width; CLIP_1D_LEN(renderer->clip_rect.left, renderer->clip_rect.right, start, len); /* top line */ if ( (y>=renderer->clip_rect.top) && (y<=renderer->clip_rect.bottom) ) { draw_hline(self, start, y, len, r, g, b); } /* bottom line */ if ( (y+height>=renderer->clip_rect.top) && (y+height<=renderer->clip_rect.bottom) ) { draw_hline(self, start, y+height, len, r, g, b); } /* clip in y */ start = y; len = height; CLIP_1D_LEN(renderer->clip_rect.top, renderer->clip_rect.bottom, start, len); /* left line */ if ( (x>=renderer->clip_rect.left) && (xclip_rect.right) ) { draw_vline(self, x, start, len, r, g, b); } /* right line */ if ( (x+width>=renderer->clip_rect.left) && (x+widthclip_rect.right) ) { draw_vline(self, x+width, start, len, r, g, b); } } static void fill_pixel_rect(DiaRenderer *self, int x, int y, int width, int height, Color *color) { DiaLibartRenderer *renderer = DIA_LIBART_RENDERER (self); guint8 r,g,b; guint8 *ptr; int i; int stride; CLIP_1D_LEN(renderer->clip_rect.left, renderer->clip_rect.right, x, width); if (width < 0) return; CLIP_1D_LEN(renderer->clip_rect.top, renderer->clip_rect.bottom, y, height); if (height < 0) return; r = color->red*0xff; g = color->green*0xff; b = color->blue*0xff; stride = renderer->pixel_width*3; ptr = renderer->rgb_buffer + x*3 + y*stride; for (i=0;i<=height;i++) { art_rgb_fill_run(ptr, r, g, b, width+1); ptr += stride; } } #else DiaRenderer * new_libart_renderer(DiaTransform *transform, int interactive) { return NULL; } #endif