1 #define INKSCAPE_CANVAS_GRID_C
3 /*
4 * CXYGrid
5 *
6 * Copyright (C) Johan Engelen 2006 <johan@shouraizou.nl>
7 * Copyright (C) Lauris Kaplinski 2000
8 *
9 */
12 #include "sp-canvas-util.h"
13 #include "canvas-grid.h"
14 #include "display-forward.h"
15 #include <libnr/nr-pixops.h>
17 namespace Inkscape {
19 enum {
20 ARG_0,
21 ARG_ORIGINX,
22 ARG_ORIGINY,
23 ARG_SPACINGX,
24 ARG_SPACINGY,
25 ARG_COLOR,
26 ARG_EMPCOLOR,
27 ARG_EMPSPACING
28 };
31 static void cxygrid_class_init (CXYGridClass *klass);
32 static void cxygrid_init (CXYGrid *grid);
33 static void cxygrid_destroy (GtkObject *object);
34 static void cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
36 static void cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
37 static void cxygrid_render (SPCanvasItem *item, SPCanvasBuf *buf);
39 static SPCanvasItemClass * parent_class;
41 GtkType
42 cxygrid_get_type (void)
43 {
44 static GtkType cxygrid_type = 0;
46 if (!cxygrid_type) {
47 GtkTypeInfo cxygrid_info = {
48 "CXYGrid",
49 sizeof (CXYGrid),
50 sizeof (CXYGridClass),
51 (GtkClassInitFunc) cxygrid_class_init,
52 (GtkObjectInitFunc) cxygrid_init,
53 NULL, NULL,
54 (GtkClassInitFunc) NULL
55 };
56 cxygrid_type = gtk_type_unique (sp_canvas_item_get_type (), &cxygrid_info);
57 }
58 return cxygrid_type;
59 }
61 static void
62 cxygrid_class_init (CXYGridClass *klass)
63 {
64 GtkObjectClass *object_class;
65 SPCanvasItemClass *item_class;
67 object_class = (GtkObjectClass *) klass;
68 item_class = (SPCanvasItemClass *) klass;
70 parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
72 gtk_object_add_arg_type ("CXYGrid::originx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINX);
73 gtk_object_add_arg_type ("CXYGrid::originy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINY);
74 gtk_object_add_arg_type ("CXYGrid::spacingx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGX);
75 gtk_object_add_arg_type ("CXYGrid::spacingy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGY);
76 gtk_object_add_arg_type ("CXYGrid::color", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_COLOR);
77 gtk_object_add_arg_type ("CXYGrid::empcolor", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPCOLOR);
78 gtk_object_add_arg_type ("CXYGrid::empspacing", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPSPACING);
80 object_class->destroy = cxygrid_destroy;
81 object_class->set_arg = cxygrid_set_arg;
83 item_class->update = cxygrid_update;
84 item_class->render = cxygrid_render;
85 }
87 static void
88 cxygrid_init (CXYGrid *grid)
89 {
90 grid->origin[NR::X] = grid->origin[NR::Y] = 0.0;
91 grid->spacing[NR::X] = grid->spacing[NR::Y] = 8.0;
92 grid->color = 0x0000ff7f;
93 grid->empcolor = 0x3F3FFF40;
94 grid->empspacing = 5;
95 }
97 static void
98 cxygrid_destroy (GtkObject *object)
99 {
100 g_return_if_fail (object != NULL);
101 g_return_if_fail (INKSCAPE_IS_CXYGRID (object));
103 if (GTK_OBJECT_CLASS (parent_class)->destroy)
104 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
105 }
107 static void
108 cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
109 {
110 SPCanvasItem *item = SP_CANVAS_ITEM (object);
111 CXYGrid *grid = INKSCAPE_CXYGRID (object);
113 switch (arg_id) {
114 case ARG_ORIGINX:
115 grid->origin[NR::X] = GTK_VALUE_DOUBLE (* arg);
116 sp_canvas_item_request_update (item);
117 break;
118 case ARG_ORIGINY:
119 grid->origin[NR::Y] = GTK_VALUE_DOUBLE (* arg);
120 sp_canvas_item_request_update (item);
121 break;
122 case ARG_SPACINGX:
123 grid->spacing[NR::X] = GTK_VALUE_DOUBLE (* arg);
124 if (grid->spacing[NR::X] < 0.01) grid->spacing[NR::X] = 0.01;
125 sp_canvas_item_request_update (item);
126 break;
127 case ARG_SPACINGY:
128 grid->spacing[NR::Y] = GTK_VALUE_DOUBLE (* arg);
129 if (grid->spacing[NR::Y] < 0.01) grid->spacing[NR::Y] = 0.01;
130 sp_canvas_item_request_update (item);
131 break;
132 case ARG_COLOR:
133 grid->color = GTK_VALUE_INT (* arg);
134 sp_canvas_item_request_update (item);
135 break;
136 case ARG_EMPCOLOR:
137 grid->empcolor = GTK_VALUE_INT (* arg);
138 sp_canvas_item_request_update (item);
139 break;
140 case ARG_EMPSPACING:
141 grid->empspacing = GTK_VALUE_INT (* arg);
142 // std::cout << "Emphasis Spacing: " << grid->empspacing << std::endl;
143 sp_canvas_item_request_update (item);
144 break;
145 default:
146 break;
147 }
148 }
150 static void
151 grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba)
152 {
153 if ((y >= buf->rect.y0) && (y < buf->rect.y1)) {
154 guint r, g, b, a;
155 gint x0, x1, x;
156 guchar *p;
157 r = NR_RGBA32_R (rgba);
158 g = NR_RGBA32_G (rgba);
159 b = NR_RGBA32_B (rgba);
160 a = NR_RGBA32_A (rgba);
161 x0 = MAX (buf->rect.x0, xs);
162 x1 = MIN (buf->rect.x1, xe + 1);
163 p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 3;
164 for (x = x0; x < x1; x++) {
165 p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
166 p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
167 p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
168 p += 3;
169 }
170 }
171 }
173 static void
174 grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)
175 {
176 if ((x >= buf->rect.x0) && (x < buf->rect.x1)) {
177 guint r, g, b, a;
178 gint y0, y1, y;
179 guchar *p;
180 r = NR_RGBA32_R(rgba);
181 g = NR_RGBA32_G (rgba);
182 b = NR_RGBA32_B (rgba);
183 a = NR_RGBA32_A (rgba);
184 y0 = MAX (buf->rect.y0, ys);
185 y1 = MIN (buf->rect.y1, ye + 1);
186 p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
187 for (y = y0; y < y1; y++) {
188 p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
189 p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
190 p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
191 p += buf->buf_rowstride;
192 }
193 }
194 }
196 /**
197 \brief This function renders the grid on a particular canvas buffer
198 \param item The grid to render on the buffer
199 \param buf The buffer to render the grid on
201 This function gets called a touch more than you might believe,
202 about once per tile. This means that it could probably be optimized
203 and help things out.
205 Basically this function has to determine where in the canvas it is,
206 and how that associates with the grid. It does this first by looking
207 at the bounding box of the buffer, and then calculates where the grid
208 starts in that buffer. It will then step through grid lines until
209 it is outside of the buffer.
211 For each grid line it is drawn using the function \c sp_grid_hline
212 or \c sp_grid_vline. These are convience functions for the sake
213 of making the function easier to read.
215 Also, there are emphisized lines on the grid. While the \c syg and
216 \c sxg variable track grid positioning, the \c xlinestart and \c
217 ylinestart variables track the 'count' of what lines they are. If
218 that count is a multiple of the line seperation between emphisis
219 lines, then that line is drawn in the emphisis color.
220 */
221 static void
222 cxygrid_render (SPCanvasItem * item, SPCanvasBuf * buf)
223 {
224 CXYGrid *grid = INKSCAPE_CXYGRID (item);
226 sp_canvas_prepare_buffer (buf);
228 const gdouble sxg = floor ((buf->rect.x0 - grid->ow[NR::X]) / grid->sw[NR::X]) * grid->sw[NR::X] + grid->ow[NR::X];
229 const gint xlinestart = (gint) Inkscape::round((sxg - grid->ow[NR::X]) / grid->sw[NR::X]);
230 const gdouble syg = floor ((buf->rect.y0 - grid->ow[NR::Y]) / grid->sw[NR::Y]) * grid->sw[NR::Y] + grid->ow[NR::Y];
231 const gint ylinestart = (gint) Inkscape::round((syg - grid->ow[NR::Y]) / grid->sw[NR::Y]);
233 gint ylinenum;
234 gdouble y;
235 for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += grid->sw[NR::Y], ylinenum++) {
236 const gint y0 = (gint) Inkscape::round(y);
238 if (!grid->scaled[NR::Y] && (ylinenum % grid->empspacing) == 0) {
239 grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->empcolor);
240 } else {
241 grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->color);
242 }
243 }
245 gint xlinenum;
246 gdouble x;
247 for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += grid->sw[NR::X], xlinenum++) {
248 const gint ix = (gint) Inkscape::round(x);
249 if (!grid->scaled[NR::X] && (xlinenum % grid->empspacing) == 0) {
250 grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->empcolor);
251 } else {
252 grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->color);
253 }
254 }
255 }
257 static void
258 cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
259 {
260 CXYGrid *grid = INKSCAPE_CXYGRID (item);
262 if (parent_class->update)
263 (* parent_class->update) (item, affine, flags);
265 grid->ow = grid->origin * affine;
266 grid->sw = grid->spacing * affine;
267 grid->sw -= NR::Point(affine[4], affine[5]);
269 for(int dim = 0; dim < 2; dim++) {
270 gint scaling_factor = grid->empspacing;
272 if (scaling_factor <= 1)
273 scaling_factor = 5;
275 grid->scaled[dim] = FALSE;
276 grid->sw[dim] = fabs (grid->sw[dim]);
277 while (grid->sw[dim] < 8.0) {
278 grid->scaled[dim] = TRUE;
279 grid->sw[dim] *= scaling_factor;
280 /* First pass, go up to the major line spacing, then
281 keep increasing by two. */
282 scaling_factor = 2;
283 }
284 }
286 if (grid->empspacing == 0) {
287 grid->scaled[NR::Y] = TRUE;
288 grid->scaled[NR::X] = TRUE;
289 }
291 sp_canvas_request_redraw (item->canvas,
292 -1000000, -1000000,
293 1000000, 1000000);
295 item->x1 = item->y1 = -1000000;
296 item->x2 = item->y2 = 1000000;
297 }
300 }; /* namespace Inkscape */
302 /*
303 Local Variables:
304 mode:c++
305 c-file-style:"stroustrup"
306 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
307 indent-tabs-mode:nil
308 fill-column:99
309 End:
310 */
311 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :