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>
28 void nr_pixblock_render_bpath_rgba (Shape* theS,uint32_t color,NRRectL &area,char* destBuf,int stride);
30 static void sp_canvas_bpath_class_init (SPCanvasBPathClass *klass);
31 static void sp_canvas_bpath_init (SPCanvasBPath *path);
32 static void sp_canvas_bpath_destroy (GtkObject *object);
34 static void sp_canvas_bpath_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
35 static void sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf);
36 static double sp_canvas_bpath_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
38 static SPCanvasItemClass *parent_class;
40 GtkType
41 sp_canvas_bpath_get_type (void)
42 {
43 static GtkType type = 0;
44 if (!type) {
45 GtkTypeInfo info = {
46 "SPCanvasBPath",
47 sizeof (SPCanvasBPath),
48 sizeof (SPCanvasBPathClass),
49 (GtkClassInitFunc) sp_canvas_bpath_class_init,
50 (GtkObjectInitFunc) sp_canvas_bpath_init,
51 NULL, NULL, NULL
52 };
53 type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info);
54 }
55 return type;
56 }
58 static void
59 sp_canvas_bpath_class_init (SPCanvasBPathClass *klass)
60 {
61 GtkObjectClass *object_class;
62 SPCanvasItemClass *item_class;
64 object_class = GTK_OBJECT_CLASS (klass);
65 item_class = (SPCanvasItemClass *) klass;
67 parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM);
69 object_class->destroy = sp_canvas_bpath_destroy;
71 item_class->update = sp_canvas_bpath_update;
72 item_class->render = sp_canvas_bpath_render;
73 item_class->point = sp_canvas_bpath_point;
74 }
76 static void
77 sp_canvas_bpath_init (SPCanvasBPath * bpath)
78 {
79 bpath->fill_rgba = 0x000000ff;
80 bpath->fill_rule = SP_WIND_RULE_EVENODD;
82 bpath->stroke_rgba = 0x00000000;
83 bpath->stroke_width = 1.0;
84 bpath->stroke_linejoin = SP_STROKE_LINEJOIN_MITER;
85 bpath->stroke_linecap = SP_STROKE_LINECAP_BUTT;
86 bpath->stroke_miterlimit = 11.0;
88 bpath->fill_shp=NULL;
89 bpath->stroke_shp=NULL;
90 }
92 static void
93 sp_canvas_bpath_destroy (GtkObject *object)
94 {
95 SPCanvasBPath *cbp = SP_CANVAS_BPATH (object);
96 if (cbp->fill_shp) {
97 delete cbp->fill_shp;
98 cbp->fill_shp = NULL;
99 }
101 if (cbp->stroke_shp) {
102 delete cbp->stroke_shp;
103 cbp->stroke_shp = NULL;
104 }
105 if (cbp->curve) {
106 cbp->curve = sp_curve_unref (cbp->curve);
107 }
109 if (GTK_OBJECT_CLASS (parent_class)->destroy)
110 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
111 }
113 static void
114 sp_canvas_bpath_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
115 {
116 SPCanvasBPath *cbp = SP_CANVAS_BPATH (item);
118 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2);
120 if (((SPCanvasItemClass *) parent_class)->update)
121 ((SPCanvasItemClass *) parent_class)->update (item, affine, flags);
123 sp_canvas_item_reset_bounds (item);
125 if (cbp->fill_shp) {
126 delete cbp->fill_shp;
127 cbp->fill_shp = NULL;
128 }
130 if (cbp->stroke_shp) {
131 delete cbp->stroke_shp;
132 cbp->stroke_shp = NULL;
133 }
134 if (!cbp->curve) return;
136 NRRect dbox;
138 dbox.x0 = dbox.y0 = 0.0;
139 dbox.x1 = dbox.y1 = -1.0;
141 if ((cbp->fill_rgba & 0xff) || (cbp->stroke_rgba & 0xff)) {
142 Path* thePath=new Path;
143 thePath->LoadArtBPath(SP_CURVE_BPATH(cbp->curve), affine, true);
144 thePath->Convert(0.25);
145 if ((cbp->fill_rgba & 0xff) && (cbp->curve->end > 2)) {
146 Shape* theShape=new Shape;
147 thePath->Fill(theShape,0);
148 if ( cbp->fill_shp == NULL ) cbp->fill_shp=new Shape;
149 if ( cbp->fill_rule == SP_WIND_RULE_EVENODD ) {
150 cbp->fill_shp->ConvertToShape(theShape,fill_oddEven);
151 } else {
152 cbp->fill_shp->ConvertToShape(theShape,fill_nonZero);
153 }
154 delete theShape;
155 cbp->fill_shp->CalcBBox();
156 if ( cbp->fill_shp->leftX < cbp->fill_shp->rightX ) {
157 if ( dbox.x0 >= dbox.x1 ) {
158 dbox.x0 = cbp->fill_shp->leftX; dbox.x1 = cbp->fill_shp->rightX;
159 dbox.y0 = cbp->fill_shp->topY; dbox.y1 = cbp->fill_shp->bottomY;
160 } else {
161 if ( cbp->fill_shp->leftX < dbox.x0 ) dbox.x0=cbp->fill_shp->leftX;
162 if ( cbp->fill_shp->rightX > dbox.x1 ) dbox.x1=cbp->fill_shp->rightX;
163 if ( cbp->fill_shp->topY < dbox.y0 ) dbox.y0=cbp->fill_shp->topY;
164 if ( cbp->fill_shp->bottomY > dbox.y1 ) dbox.y1=cbp->fill_shp->bottomY;
165 }
166 }
167 }
168 if ((cbp->stroke_rgba & 0xff) && (cbp->curve->end > 1)) {
169 JoinType join=join_straight;
170 // Shape* theShape=new Shape;
171 ButtType butt=butt_straight;
172 if ( cbp->stroke_shp == NULL ) cbp->stroke_shp=new Shape;
173 if ( cbp->stroke_linecap == SP_STROKE_LINECAP_BUTT ) butt=butt_straight;
174 if ( cbp->stroke_linecap == SP_STROKE_LINECAP_ROUND ) butt=butt_round;
175 if ( cbp->stroke_linecap == SP_STROKE_LINECAP_SQUARE ) butt=butt_square;
176 if ( cbp->stroke_linejoin == SP_STROKE_LINEJOIN_MITER ) join=join_pointy;
177 if ( cbp->stroke_linejoin == SP_STROKE_LINEJOIN_ROUND ) join=join_round;
178 if ( cbp->stroke_linejoin == SP_STROKE_LINEJOIN_BEVEL ) join=join_straight;
179 thePath->Stroke(cbp->stroke_shp,false,0.5*cbp->stroke_width, join,butt,cbp->stroke_width*cbp->stroke_miterlimit );
180 // thePath->Stroke(theShape,false,0.5*cbp->stroke_width, join,butt,cbp->stroke_width*cbp->stroke_miterlimit );
181 // cbp->stroke_shp->ConvertToShape(theShape,fill_nonZero);
183 cbp->stroke_shp->CalcBBox();
184 if ( cbp->stroke_shp->leftX < cbp->stroke_shp->rightX ) {
185 if ( dbox.x0 >= dbox.x1 ) {
186 dbox.x0 = cbp->stroke_shp->leftX;dbox.x1 = cbp->stroke_shp->rightX;
187 dbox.y0 = cbp->stroke_shp->topY;dbox.y1 = cbp->stroke_shp->bottomY;
188 } else {
189 if ( cbp->stroke_shp->leftX < dbox.x0 ) dbox.x0 = cbp->stroke_shp->leftX;
190 if ( cbp->stroke_shp->rightX > dbox.x1 ) dbox.x1 = cbp->stroke_shp->rightX;
191 if ( cbp->stroke_shp->topY < dbox.y0 ) dbox.y0 = cbp->stroke_shp->topY;
192 if ( cbp->stroke_shp->bottomY > dbox.y1 ) dbox.y1 = cbp->stroke_shp->bottomY;
193 }
194 }
195 // delete theShape;
196 }
197 delete thePath;
198 }
201 item->x1 = (int)dbox.x0;
202 item->y1 = (int)dbox.y0;
203 item->x2 = (int)dbox.x1;
204 item->y2 = (int)dbox.y1;
206 sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2);
207 }
209 static void
210 sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf)
211 {
212 SPCanvasBPath *cbp = SP_CANVAS_BPATH (item);
214 sp_canvas_prepare_buffer(buf);
216 NRRectL area;
217 area.x0=buf->rect.x0;
218 area.x1=buf->rect.x1;
219 area.y0=buf->rect.y0;
220 area.y1=buf->rect.y1;
221 if ( cbp->fill_shp ) {
222 nr_pixblock_render_bpath_rgba (cbp->fill_shp,cbp->fill_rgba,area,(char*)buf->buf, buf->buf_rowstride);
223 }
224 if ( cbp->stroke_shp ) {
225 nr_pixblock_render_bpath_rgba (cbp->stroke_shp,cbp->stroke_rgba,area,(char*)buf->buf, buf->buf_rowstride);
226 }
228 }
230 #define BIGVAL 1e18
232 static double
233 sp_canvas_bpath_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
234 {
235 SPCanvasBPath *cbp = SP_CANVAS_BPATH (item);
237 if (cbp->fill_shp && (cbp->fill_shp->PtWinding(p) > 0 )) {
238 *actual_item = item;
239 return 0.0;
240 }
241 if (cbp->stroke_shp ) {
242 if (cbp->stroke_shp->PtWinding(p) > 0 ) {
243 *actual_item = item;
244 return 0.0;
245 }
246 return distance(cbp->stroke_shp, p);
247 }
248 if (cbp->fill_shp) {
249 return distance(cbp->fill_shp, p);
250 }
252 return BIGVAL;
253 }
255 SPCanvasItem *
256 sp_canvas_bpath_new (SPCanvasGroup *parent, SPCurve *curve)
257 {
258 g_return_val_if_fail (parent != NULL, NULL);
259 g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL);
261 SPCanvasItem *item = sp_canvas_item_new (parent, SP_TYPE_CANVAS_BPATH, NULL);
263 sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (item), curve);
265 return item;
266 }
268 void
269 sp_canvas_bpath_set_bpath (SPCanvasBPath *cbp, SPCurve *curve)
270 {
271 g_return_if_fail (cbp != NULL);
272 g_return_if_fail (SP_IS_CANVAS_BPATH (cbp));
274 if (cbp->curve) {
275 cbp->curve = sp_curve_unref (cbp->curve);
276 }
278 if (curve) {
279 cbp->curve = sp_curve_ref (curve);
280 }
282 sp_canvas_item_request_update (SP_CANVAS_ITEM (cbp));
283 }
285 void
286 sp_canvas_bpath_set_fill (SPCanvasBPath *cbp, guint32 rgba, SPWindRule rule)
287 {
288 g_return_if_fail (cbp != NULL);
289 g_return_if_fail (SP_IS_CANVAS_BPATH (cbp));
291 cbp->fill_rgba = rgba;
292 cbp->fill_rule = rule;
294 sp_canvas_item_request_update (SP_CANVAS_ITEM (cbp));
295 }
297 void
298 sp_canvas_bpath_set_stroke (SPCanvasBPath *cbp, guint32 rgba, gdouble width, SPStrokeJoinType join, SPStrokeCapType cap)
299 {
300 g_return_if_fail (cbp != NULL);
301 g_return_if_fail (SP_IS_CANVAS_BPATH (cbp));
303 cbp->stroke_rgba = rgba;
304 cbp->stroke_width = MAX (width, 0.1);
305 cbp->stroke_linejoin = join;
306 cbp->stroke_linecap = cap;
308 sp_canvas_item_request_update (SP_CANVAS_ITEM (cbp));
309 }
313 static void
314 bpath_run_A8_OR (raster_info &dest,void *data,int st,float vst,int en,float ven)
315 {
316 union {
317 uint8_t comp[4];
318 uint32_t col;
319 } tempCol;
320 if ( st >= en ) return;
321 tempCol.col=*(uint32_t*)data;
323 unsigned int r, g, b, a;
324 r = NR_RGBA32_R (tempCol.col);
325 g = NR_RGBA32_G (tempCol.col);
326 b = NR_RGBA32_B (tempCol.col);
327 a = NR_RGBA32_A (tempCol.col);
328 if (a == 0) return;
330 vst*=a;
331 ven*=a;
333 if ( vst < 0 ) vst=0;
334 if ( vst > 255 ) vst=255;
335 if ( ven < 0 ) ven=0;
336 if ( ven > 255 ) ven=255;
337 float sv=vst;
338 float dv=ven-vst;
339 int len=en-st;
340 uint8_t* d=(uint8_t*)dest.buffer;
342 d+=3*(st-dest.startPix);
343 if ( fabs(dv) < 0.001 ) {
344 if ( sv > 249.999 ) {
345 /* Simple copy */
346 while (len > 0) {
347 d[0] = NR_COMPOSEN11 (r, 255, d[0]);
348 d[1] = NR_COMPOSEN11 (g, 255, d[1]);
349 d[2] = NR_COMPOSEN11 (b, 255, d[2]);
350 d += 3;
351 len -= 1;
352 }
353 } else {
354 unsigned int c0_24=(int)sv;
355 c0_24&=0xFF;
356 while (len > 0) {
357 d[0] = NR_COMPOSEN11 (r, c0_24, d[0]);
358 d[1] = NR_COMPOSEN11 (g, c0_24, d[1]);
359 d[2] = NR_COMPOSEN11 (b, c0_24, d[2]);
360 d += 3;
361 len -= 1;
362 }
363 }
364 } else {
365 if ( en <= st+1 ) {
366 sv=0.5*(vst+ven);
367 unsigned int c0_24=(int)sv;
368 c0_24&=0xFF;
369 d[0] = NR_COMPOSEN11 (r, c0_24, d[0]);
370 d[1] = NR_COMPOSEN11 (g, c0_24, d[1]);
371 d[2] = NR_COMPOSEN11 (b, c0_24, d[2]);
372 } else {
373 dv/=len;
374 sv+=0.5*dv; // correction trapezoidale
375 sv*=65536;
376 dv*=65536;
377 int c0_24 = static_cast<int>(CLAMP(sv, 0, 16777216));
378 int s0_24 = static_cast<int>(dv);
379 while (len > 0) {
380 unsigned int ca;
381 /* Draw */
382 ca = c0_24 >> 16;
383 if ( ca > 255 ) ca=255;
384 d[0] = NR_COMPOSEN11 (r, ca, d[0]);
385 d[1] = NR_COMPOSEN11 (g, ca, d[1]);
386 d[2] = NR_COMPOSEN11 (b, ca, d[2]);
387 d += 3;
388 c0_24 += s0_24;
389 c0_24 = CLAMP (c0_24, 0, 16777216);
390 len -= 1;
391 }
392 }
393 }
394 }
396 void nr_pixblock_render_bpath_rgba (Shape* theS,uint32_t color,NRRectL &area,char* destBuf,int stride)
397 {
399 theS->CalcBBox();
400 float l=theS->leftX,r=theS->rightX,t=theS->topY,b=theS->bottomY;
401 int il,ir,it,ib;
402 il=(int)floor(l);
403 ir=(int)ceil(r);
404 it=(int)floor(t);
405 ib=(int)ceil(b);
407 if ( il >= area.x1 || ir <= area.x0 || it >= area.y1 || ib <= area.y0 ) return;
408 if ( il < area.x0 ) il=area.x0;
409 if ( it < area.y0 ) it=area.y0;
410 if ( ir > area.x1 ) ir=area.x1;
411 if ( ib > area.y1 ) ib=area.y1;
413 /* // version par FloatLigne
414 int curPt;
415 float curY;
416 theS->BeginRaster(curY,curPt,1.0);
418 FloatLigne* theI=new FloatLigne();
419 IntLigne* theIL=new IntLigne();
421 theS->Scan(curY,curPt,(float)(it),1.0);
423 char* mdata=(char*)destBuf;
424 uint32_t* ligStart=((uint32_t*)(mdata+(3*(il-area.x0)+stride*(it-area.y0))));
425 for (int y=it;y<ib;y++) {
426 theI->Reset();
427 if ( y&0x00000003 ) {
428 theS->Scan(curY,curPt,((float)(y+1)),theI,false,1.0);
429 } else {
430 theS->Scan(curY,curPt,((float)(y+1)),theI,true,1.0);
431 }
432 theI->Flatten();
433 theIL->Copy(theI);
435 raster_info dest;
436 dest.startPix=il;
437 dest.endPix=ir;
438 dest.sth=il;
439 dest.stv=y;
440 dest.buffer=ligStart;
441 theIL->Raster(dest,&color,bpath_run_A8_OR);
442 ligStart=((uint32_t*)(((char*)ligStart)+stride));
443 }
444 theS->EndRaster();
445 delete theI;
446 delete theIL; */
448 // version par BitLigne directe
449 int curPt;
450 float curY;
451 theS->BeginQuickRaster(curY, curPt);
453 BitLigne* theI[4];
454 for (int i=0;i<4;i++) theI[i]=new BitLigne(il,ir);
455 IntLigne* theIL=new IntLigne();
457 theS->DirectQuickScan(curY,curPt,(float)(it),true,0.25);
459 char* mdata=(char*)destBuf;
460 uint32_t* ligStart=((uint32_t*)(mdata+(3*(il-area.x0)+stride*(it-area.y0))));
461 for (int y=it;y<ib;y++) {
462 for (int i = 0; i < 4; i++)
463 theI[i]->Reset();
465 for (int i = 0; i < 4; i++)
466 theS->QuickScan(curY, curPt, ((float)(y+0.25*(i+1))),
467 fill_oddEven, theI[i], 0.25);
469 theIL->Copy(4,theI);
470 // theI[0]->Affiche();
471 // theIL->Affiche();
473 raster_info dest;
474 dest.startPix=il;
475 dest.endPix=ir;
476 dest.sth=il;
477 dest.stv=y;
478 dest.buffer=ligStart;
479 theIL->Raster(dest,&color,bpath_run_A8_OR);
480 ligStart=((uint32_t*)(((char*)ligStart)+stride));
481 }
482 theS->EndQuickRaster();
483 for (int i=0;i<4;i++) delete theI[i];
484 delete theIL;
485 }