1 #define __SP_CANVAS_BPATH_C__
3 /*
4 * Simple bezier bpath CanvasItem for inkscape
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@ximian.com>
8 *
9 * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc.
10 *
11 * Released under GNU GPL
12 *
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18 #include "sp-canvas-util.h"
19 #include "canvas-bpath.h"
20 #include "display/display-forward.h"
21 #include "display/curve.h"
22 #include <livarot/Shape.h>
23 #include <livarot/Path.h>
24 #include <livarot/int-line.h>
25 #include <livarot/BitLigne.h>
26 #include <libnr/nr-pixops.h>
27 #include <libnr/nr-convert2geom.h>
29 void nr_pixblock_render_bpath_rgba (Shape* theS,uint32_t color,NRRectL &area,char* destBuf,int stride);
31 static void sp_canvas_bpath_class_init (SPCanvasBPathClass *klass);
32 static void sp_canvas_bpath_init (SPCanvasBPath *path);
33 static void sp_canvas_bpath_destroy (GtkObject *object);
35 static void sp_canvas_bpath_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
36 static void sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf);
37 static double sp_canvas_bpath_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
39 static SPCanvasItemClass *parent_class;
41 GtkType
42 sp_canvas_bpath_get_type (void)
43 {
44 static GtkType type = 0;
45 if (!type) {
46 GtkTypeInfo info = {
47 (gchar *)"SPCanvasBPath",
48 sizeof (SPCanvasBPath),
49 sizeof (SPCanvasBPathClass),
50 (GtkClassInitFunc) sp_canvas_bpath_class_init,
51 (GtkObjectInitFunc) sp_canvas_bpath_init,
52 NULL, NULL, NULL
53 };
54 type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info);
55 }
56 return type;
57 }
59 static void
60 sp_canvas_bpath_class_init (SPCanvasBPathClass *klass)
61 {
62 GtkObjectClass *object_class;
63 SPCanvasItemClass *item_class;
65 object_class = GTK_OBJECT_CLASS (klass);
66 item_class = (SPCanvasItemClass *) klass;
68 parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM);
70 object_class->destroy = sp_canvas_bpath_destroy;
72 item_class->update = sp_canvas_bpath_update;
73 item_class->render = sp_canvas_bpath_render;
74 item_class->point = sp_canvas_bpath_point;
75 }
77 static void
78 sp_canvas_bpath_init (SPCanvasBPath * bpath)
79 {
80 bpath->fill_rgba = 0x000000ff;
81 bpath->fill_rule = SP_WIND_RULE_EVENODD;
83 bpath->stroke_rgba = 0x00000000;
84 bpath->stroke_width = 1.0;
85 bpath->stroke_linejoin = SP_STROKE_LINEJOIN_MITER;
86 bpath->stroke_linecap = SP_STROKE_LINECAP_BUTT;
87 bpath->stroke_miterlimit = 11.0;
89 bpath->fill_shp=NULL;
90 bpath->stroke_shp=NULL;
91 }
93 static void
94 sp_canvas_bpath_destroy (GtkObject *object)
95 {
96 SPCanvasBPath *cbp = SP_CANVAS_BPATH (object);
97 if (cbp->fill_shp) {
98 delete cbp->fill_shp;
99 cbp->fill_shp = NULL;
100 }
102 if (cbp->stroke_shp) {
103 delete cbp->stroke_shp;
104 cbp->stroke_shp = NULL;
105 }
106 if (cbp->curve) {
107 cbp->curve = cbp->curve->unref();
108 }
110 if (GTK_OBJECT_CLASS (parent_class)->destroy)
111 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
112 }
114 static void
115 sp_canvas_bpath_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
116 {
117 SPCanvasBPath *cbp = SP_CANVAS_BPATH (item);
119 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2);
121 if (((SPCanvasItemClass *) parent_class)->update)
122 ((SPCanvasItemClass *) parent_class)->update (item, affine, flags);
124 sp_canvas_item_reset_bounds (item);
126 if (cbp->fill_shp) {
127 delete cbp->fill_shp;
128 cbp->fill_shp = NULL;
129 }
131 if (cbp->stroke_shp) {
132 delete cbp->stroke_shp;
133 cbp->stroke_shp = NULL;
134 }
135 if (!cbp->curve) return;
137 NRRect dbox;
139 dbox.x0 = dbox.y0 = 0.0;
140 dbox.x1 = dbox.y1 = -1.0;
142 if ((cbp->fill_rgba & 0xff) || (cbp->stroke_rgba & 0xff)) {
143 Path* thePath=new Path;
144 thePath->LoadPathVector(cbp->curve->get_pathvector(), to_2geom(affine), true);
145 thePath->Convert(0.25);
146 if ((cbp->fill_rgba & 0xff) && (cbp->curve->get_length() > 2)) {
147 Shape* theShape=new Shape;
148 thePath->Fill(theShape,0);
149 if ( cbp->fill_shp == NULL ) cbp->fill_shp=new Shape;
150 if ( cbp->fill_rule == SP_WIND_RULE_EVENODD ) {
151 cbp->fill_shp->ConvertToShape(theShape,fill_oddEven);
152 } else {
153 cbp->fill_shp->ConvertToShape(theShape,fill_nonZero);
154 }
155 delete theShape;
156 cbp->fill_shp->CalcBBox();
157 if ( cbp->fill_shp->leftX < cbp->fill_shp->rightX ) {
158 if ( dbox.x0 >= dbox.x1 ) {
159 dbox.x0 = cbp->fill_shp->leftX; dbox.x1 = cbp->fill_shp->rightX;
160 dbox.y0 = cbp->fill_shp->topY; dbox.y1 = cbp->fill_shp->bottomY;
161 } else {
162 if ( cbp->fill_shp->leftX < dbox.x0 ) dbox.x0=cbp->fill_shp->leftX;
163 if ( cbp->fill_shp->rightX > dbox.x1 ) dbox.x1=cbp->fill_shp->rightX;
164 if ( cbp->fill_shp->topY < dbox.y0 ) dbox.y0=cbp->fill_shp->topY;
165 if ( cbp->fill_shp->bottomY > dbox.y1 ) dbox.y1=cbp->fill_shp->bottomY;
166 }
167 }
168 }
169 if ((cbp->stroke_rgba & 0xff) && (cbp->curve->get_length() > 1)) {
170 JoinType join=join_straight;
171 // Shape* theShape=new Shape;
172 ButtType butt=butt_straight;
173 if ( cbp->stroke_shp == NULL ) cbp->stroke_shp=new Shape;
174 if ( cbp->stroke_linecap == SP_STROKE_LINECAP_BUTT ) butt=butt_straight;
175 if ( cbp->stroke_linecap == SP_STROKE_LINECAP_ROUND ) butt=butt_round;
176 if ( cbp->stroke_linecap == SP_STROKE_LINECAP_SQUARE ) butt=butt_square;
177 if ( cbp->stroke_linejoin == SP_STROKE_LINEJOIN_MITER ) join=join_pointy;
178 if ( cbp->stroke_linejoin == SP_STROKE_LINEJOIN_ROUND ) join=join_round;
179 if ( cbp->stroke_linejoin == SP_STROKE_LINEJOIN_BEVEL ) join=join_straight;
180 thePath->Stroke(cbp->stroke_shp,false,0.5*cbp->stroke_width, join,butt,cbp->stroke_width*cbp->stroke_miterlimit );
181 // thePath->Stroke(theShape,false,0.5*cbp->stroke_width, join,butt,cbp->stroke_width*cbp->stroke_miterlimit );
182 // cbp->stroke_shp->ConvertToShape(theShape,fill_nonZero);
184 cbp->stroke_shp->CalcBBox();
185 if ( cbp->stroke_shp->leftX < cbp->stroke_shp->rightX ) {
186 if ( dbox.x0 >= dbox.x1 ) {
187 dbox.x0 = cbp->stroke_shp->leftX;dbox.x1 = cbp->stroke_shp->rightX;
188 dbox.y0 = cbp->stroke_shp->topY;dbox.y1 = cbp->stroke_shp->bottomY;
189 } else {
190 if ( cbp->stroke_shp->leftX < dbox.x0 ) dbox.x0 = cbp->stroke_shp->leftX;
191 if ( cbp->stroke_shp->rightX > dbox.x1 ) dbox.x1 = cbp->stroke_shp->rightX;
192 if ( cbp->stroke_shp->topY < dbox.y0 ) dbox.y0 = cbp->stroke_shp->topY;
193 if ( cbp->stroke_shp->bottomY > dbox.y1 ) dbox.y1 = cbp->stroke_shp->bottomY;
194 }
195 }
196 // delete theShape;
197 }
198 delete thePath;
199 }
202 item->x1 = (int)dbox.x0;
203 item->y1 = (int)dbox.y0;
204 item->x2 = (int)dbox.x1;
205 item->y2 = (int)dbox.y1;
207 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2);
208 }
210 static void
211 sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf)
212 {
213 SPCanvasBPath *cbp = SP_CANVAS_BPATH (item);
215 sp_canvas_prepare_buffer(buf);
217 NRRectL area;
218 area.x0=buf->rect.x0;
219 area.x1=buf->rect.x1;
220 area.y0=buf->rect.y0;
221 area.y1=buf->rect.y1;
222 if ( cbp->fill_shp ) {
223 nr_pixblock_render_bpath_rgba (cbp->fill_shp,cbp->fill_rgba,area,(char*)buf->buf, buf->buf_rowstride);
224 }
225 if ( cbp->stroke_shp ) {
226 nr_pixblock_render_bpath_rgba (cbp->stroke_shp,cbp->stroke_rgba,area,(char*)buf->buf, buf->buf_rowstride);
227 }
229 }
231 #define BIGVAL 1e18
233 static double
234 sp_canvas_bpath_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
235 {
236 SPCanvasBPath *cbp = SP_CANVAS_BPATH (item);
238 if (cbp->fill_shp && (cbp->fill_shp->PtWinding(p) > 0 )) {
239 *actual_item = item;
240 return 0.0;
241 }
242 if (cbp->stroke_shp ) {
243 if (cbp->stroke_shp->PtWinding(p) > 0 ) {
244 *actual_item = item;
245 return 0.0;
246 }
247 return distance(cbp->stroke_shp, p);
248 }
249 if (cbp->fill_shp) {
250 return distance(cbp->fill_shp, p);
251 }
253 return BIGVAL;
254 }
256 SPCanvasItem *
257 sp_canvas_bpath_new (SPCanvasGroup *parent, SPCurve *curve)
258 {
259 g_return_val_if_fail (parent != NULL, NULL);
260 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
262 SPCanvasItem *item = sp_canvas_item_new (parent, SP_TYPE_CANVAS_BPATH, NULL);
264 sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (item), curve);
266 return item;
267 }
269 void
270 sp_canvas_bpath_set_bpath (SPCanvasBPath *cbp, SPCurve *curve)
271 {
272 g_return_if_fail (cbp != NULL);
273 g_return_if_fail (SP_IS_CANVAS_BPATH (cbp));
275 if (cbp->curve) {
276 cbp->curve = cbp->curve->unref();
277 }
279 if (curve) {
280 cbp->curve = curve->ref();
281 }
283 sp_canvas_item_request_update (SP_CANVAS_ITEM (cbp));
284 }
286 void
287 sp_canvas_bpath_set_fill (SPCanvasBPath *cbp, guint32 rgba, SPWindRule rule)
288 {
289 g_return_if_fail (cbp != NULL);
290 g_return_if_fail (SP_IS_CANVAS_BPATH (cbp));
292 cbp->fill_rgba = rgba;
293 cbp->fill_rule = rule;
295 sp_canvas_item_request_update (SP_CANVAS_ITEM (cbp));
296 }
298 void
299 sp_canvas_bpath_set_stroke (SPCanvasBPath *cbp, guint32 rgba, gdouble width, SPStrokeJoinType join, SPStrokeCapType cap)
300 {
301 g_return_if_fail (cbp != NULL);
302 g_return_if_fail (SP_IS_CANVAS_BPATH (cbp));
304 cbp->stroke_rgba = rgba;
305 cbp->stroke_width = MAX (width, 0.1);
306 cbp->stroke_linejoin = join;
307 cbp->stroke_linecap = cap;
309 sp_canvas_item_request_update (SP_CANVAS_ITEM (cbp));
310 }
314 static void
315 bpath_run_A8_OR (raster_info &dest,void *data,int st,float vst,int en,float ven)
316 {
317 union {
318 uint8_t comp[4];
319 uint32_t col;
320 } tempCol;
321 if ( st >= en ) return;
322 tempCol.col=*(uint32_t*)data;
324 unsigned int r, g, b, a;
325 r = NR_RGBA32_R (tempCol.col);
326 g = NR_RGBA32_G (tempCol.col);
327 b = NR_RGBA32_B (tempCol.col);
328 a = NR_RGBA32_A (tempCol.col);
329 if (a == 0) return;
331 vst*=a;
332 ven*=a;
334 if ( vst < 0 ) vst=0;
335 if ( vst > 255 ) vst=255;
336 if ( ven < 0 ) ven=0;
337 if ( ven > 255 ) ven=255;
338 float sv=vst;
339 float dv=ven-vst;
340 int len=en-st;
341 uint8_t* d=(uint8_t*)dest.buffer;
343 d+=3*(st-dest.startPix);
344 if ( fabs(dv) < 0.001 ) {
345 if ( sv > 249.999 ) {
346 /* Simple copy */
347 while (len > 0) {
348 d[0] = NR_COMPOSEN11_1111 (r, 255, d[0]);
349 d[1] = NR_COMPOSEN11_1111 (g, 255, d[1]);
350 d[2] = NR_COMPOSEN11_1111 (b, 255, d[2]);
351 d += 3;
352 len -= 1;
353 }
354 } else {
355 unsigned int c0_24=(int)sv;
356 c0_24&=0xFF;
357 while (len > 0) {
358 d[0] = NR_COMPOSEN11_1111 (r, c0_24, d[0]);
359 d[1] = NR_COMPOSEN11_1111 (g, c0_24, d[1]);
360 d[2] = NR_COMPOSEN11_1111 (b, c0_24, d[2]);
361 d += 3;
362 len -= 1;
363 }
364 }
365 } else {
366 if ( en <= st+1 ) {
367 sv=0.5*(vst+ven);
368 unsigned int c0_24=(int)sv;
369 c0_24&=0xFF;
370 d[0] = NR_COMPOSEN11_1111 (r, c0_24, d[0]);
371 d[1] = NR_COMPOSEN11_1111 (g, c0_24, d[1]);
372 d[2] = NR_COMPOSEN11_1111 (b, c0_24, d[2]);
373 } else {
374 dv/=len;
375 sv+=0.5*dv; // correction trapezoidale
376 sv*=65536;
377 dv*=65536;
378 int c0_24 = static_cast<int>(CLAMP(sv, 0, 16777216));
379 int s0_24 = static_cast<int>(dv);
380 while (len > 0) {
381 unsigned int ca;
382 /* Draw */
383 ca = c0_24 >> 16;
384 if ( ca > 255 ) ca=255;
385 d[0] = NR_COMPOSEN11_1111 (r, ca, d[0]);
386 d[1] = NR_COMPOSEN11_1111 (g, ca, d[1]);
387 d[2] = NR_COMPOSEN11_1111 (b, ca, d[2]);
388 d += 3;
389 c0_24 += s0_24;
390 c0_24 = CLAMP (c0_24, 0, 16777216);
391 len -= 1;
392 }
393 }
394 }
395 }
397 void nr_pixblock_render_bpath_rgba (Shape* theS,uint32_t color,NRRectL &area,char* destBuf,int stride)
398 {
400 theS->CalcBBox();
401 float l=theS->leftX,r=theS->rightX,t=theS->topY,b=theS->bottomY;
402 int il,ir,it,ib;
403 il=(int)floor(l);
404 ir=(int)ceil(r);
405 it=(int)floor(t);
406 ib=(int)ceil(b);
408 if ( il >= area.x1 || ir <= area.x0 || it >= area.y1 || ib <= area.y0 ) return;
409 if ( il < area.x0 ) il=area.x0;
410 if ( it < area.y0 ) it=area.y0;
411 if ( ir > area.x1 ) ir=area.x1;
412 if ( ib > area.y1 ) ib=area.y1;
414 /* // version par FloatLigne
415 int curPt;
416 float curY;
417 theS->BeginRaster(curY,curPt,1.0);
419 FloatLigne* theI=new FloatLigne();
420 IntLigne* theIL=new IntLigne();
422 theS->Scan(curY,curPt,(float)(it),1.0);
424 char* mdata=(char*)destBuf;
425 uint32_t* ligStart=((uint32_t*)(mdata+(3*(il-area.x0)+stride*(it-area.y0))));
426 for (int y=it;y<ib;y++) {
427 theI->Reset();
428 if ( y&0x00000003 ) {
429 theS->Scan(curY,curPt,((float)(y+1)),theI,false,1.0);
430 } else {
431 theS->Scan(curY,curPt,((float)(y+1)),theI,true,1.0);
432 }
433 theI->Flatten();
434 theIL->Copy(theI);
436 raster_info dest;
437 dest.startPix=il;
438 dest.endPix=ir;
439 dest.sth=il;
440 dest.stv=y;
441 dest.buffer=ligStart;
442 theIL->Raster(dest,&color,bpath_run_A8_OR);
443 ligStart=((uint32_t*)(((char*)ligStart)+stride));
444 }
445 theS->EndRaster();
446 delete theI;
447 delete theIL; */
449 // version par BitLigne directe
450 int curPt;
451 float curY;
452 theS->BeginQuickRaster(curY, curPt);
454 BitLigne* theI[4];
455 for (int i=0;i<4;i++) theI[i]=new BitLigne(il,ir);
456 IntLigne* theIL=new IntLigne();
458 theS->DirectQuickScan(curY,curPt,(float)(it),true,0.25);
460 char* mdata=(char*)destBuf;
461 uint32_t* ligStart=((uint32_t*)(mdata+(3*(il-area.x0)+stride*(it-area.y0))));
462 for (int y=it;y<ib;y++) {
463 for (int i = 0; i < 4; i++)
464 theI[i]->Reset();
466 for (int i = 0; i < 4; i++)
467 theS->QuickScan(curY, curPt, ((float)(y+0.25*(i+1))),
468 fill_oddEven, theI[i], 0.25);
470 theIL->Copy(4,theI);
471 // theI[0]->Affiche();
472 // theIL->Affiche();
474 raster_info dest;
475 dest.startPix=il;
476 dest.endPix=ir;
477 dest.sth=il;
478 dest.stv=y;
479 dest.buffer=ligStart;
480 theIL->Raster(dest,&color,bpath_run_A8_OR);
481 ligStart=((uint32_t*)(((char*)ligStart)+stride));
482 }
483 theS->EndQuickRaster();
484 for (int i=0;i<4;i++) delete theI[i];
485 delete theIL;
486 }
489 /*
490 Local Variables:
491 mode:c++
492 c-file-style:"stroustrup"
493 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
494 indent-tabs-mode:nil
495 fill-column:99
496 End:
497 */
498 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :