X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Ftrunk%2Fsrc%2Fsynfig%2Flayer_shape.cpp;h=e8f13a5d6156a40fd23dc0b0ecaa2af95b463cc6;hb=d15c4522466bedfbe61620c401becae0931854f5;hp=1d71677955cc15a21a50383fa7ef32d93e026e73;hpb=e8a065f2385c219c511b57dac52786120bfa097d;p=synfig.git diff --git a/synfig-core/trunk/src/synfig/layer_shape.cpp b/synfig-core/trunk/src/synfig/layer_shape.cpp index 1d71677..e8f13a5 100644 --- a/synfig-core/trunk/src/synfig/layer_shape.cpp +++ b/synfig-core/trunk/src/synfig/layer_shape.cpp @@ -1,11 +1,12 @@ /* === S Y N F I G ========================================================= */ /*! \file layer_shape.cpp -** \brief Template Header +** \brief Implementation of the "Shape" layer ** -** $Id: layer_shape.cpp,v 1.2 2005/01/24 03:08:18 darco Exp $ +** $Id$ ** ** \legal ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley +** Copyright (c) 2007, 2008 Chris Moore ** ** This package is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as @@ -59,10 +60,10 @@ using namespace etl; SYNFIG_LAYER_INIT(Layer_Shape); SYNFIG_LAYER_SET_NAME(Layer_Shape,"shape"); -SYNFIG_LAYER_SET_LOCAL_NAME(Layer_Shape,_("Shape")); -SYNFIG_LAYER_SET_CATEGORY(Layer_Shape,_("Internal")); +SYNFIG_LAYER_SET_LOCAL_NAME(Layer_Shape,N_("Shape")); +SYNFIG_LAYER_SET_CATEGORY(Layer_Shape,N_("Internal")); SYNFIG_LAYER_SET_VERSION(Layer_Shape,"0.1"); -SYNFIG_LAYER_SET_CVS_ID(Layer_Shape,"$Id: layer_shape.cpp,v 1.2 2005/01/24 03:08:18 darco Exp $"); +SYNFIG_LAYER_SET_CVS_ID(Layer_Shape,"$Id$"); #define EPSILON 1e-12 @@ -79,9 +80,9 @@ struct Primitive { int operation; int number; - + //Point data[0]; - + enum Operations { NONE = -1, @@ -103,55 +104,55 @@ const int MIN_SUBDIVISION_DRAW_LEVELS = 4; static void Subd_Conic_Stack(Point *arc) { /* - + b0 * 0+1 a b1 b * 1+2*1+2 a * 1+2 b * - b2 * - * - + b2 * + * + 0.1.2 -> 0.1 2 3.4 - + */ - + Real a,b; - - + + arc[4][0] = arc[2][0]; - b = arc[1][0]; - + b = arc[1][0]; + a = arc[1][0] = (arc[0][0] + b)/2; - b = arc[3][0] = (arc[4][0] + b)/2; + b = arc[3][0] = (arc[4][0] + b)/2; arc[2][0] = (a + b)/2; - - + + arc[4][1] = arc[2][1]; b = arc[1][1]; - + a = arc[1][1] = (arc[0][1] + b)/2; b = arc[3][1] = (arc[4][1] + b)/2; arc[2][1] = (a + b)/2; - + /* //USING SIMD - + arc[4] = arc[2]; - + arc[3] = (arc[2] + arc[1])/2; arc[1] = (arc[0] + arc[1])/2; arc[2] = (arc[1] + arc[3])/2; - + */ - + } static void Subd_Cubic_Stack(Point *arc) { Real a,b,c; - + /* - + b0 * 0+1 a b1 b * 1+2*1+2 a @@ -159,56 +160,56 @@ static void Subd_Cubic_Stack(Point *arc) b2 c * 1+2*2+2 b * * 2+3 c * b3 * - * - + * + 0.1 2.3 -> 0.1 2 3 4 5.6 - + */ - + arc[6][0] = arc[3][0]; - + b = arc[1][0]; c = arc[2][0]; - + a = arc[1][0] = (arc[0][0] + b)/2; b = (b + c)/2; c = arc[5][0] = (arc[6][0] + c)/2; - + a = arc[2][0] = (a + b)/2; b = arc[4][0] = (b + c)/2; - + arc[3][0] = (a + b)/2; - - + + arc[6][1] = arc[3][1]; - + b = arc[1][1]; c = arc[2][1]; - + a = arc[1][1] = (arc[0][1] + b)/2; b = (b + c)/2; c = arc[5][1] = (arc[6][1] + c)/2; - + a = arc[2][1] = (a + b)/2; b = arc[4][1] = (b + c)/2; - + arc[3][1] = (a + b)/2; - + /* //USING SIMD temp - + arc[6] = arc[3]; - + //backwards to avoid overwriting arc[5] = (arc[2] + arc[3])/2; temp = (arc[1] + arc[2])/2; arc[1] = (arc[0] + arc[1])/2; - + arc[4] = (temp + arc[5])/2; arc[2] = (arc[1] + temp)/2; - + arc[3] = (arc[2] + arc[4])/2; - + */ } @@ -220,9 +221,9 @@ struct MonoSegment Rect aabb; int ydir; vector pointlist; - + MonoSegment(int dir = 0, Real x0 = 0, Real x1 = 0, Real y0 = 0, Real y1 = 0) - { + { aabb.minx = x0; aabb.maxx = x1; aabb.miny = y0; @@ -230,34 +231,36 @@ struct MonoSegment ydir = dir; } - + int intersect(Real x,Real y) const { - if((y < aabb.miny) | (y > aabb.maxy) | (x < aabb.minx)) return 0; + if((y < aabb.miny+EPSILON) || (y > aabb.maxy) || (x < aabb.minx)) return 0; if(x > aabb.maxx) return ydir; - + //int i = 0; //int size = pointlist.size(); //vector::const_iterator end = pointlist.end(); vector::const_iterator p = pointlist.begin(); - + //assumes that the rect culled away anything that would be beyond the edges if(ydir > 0) { - while(y > (*++p)[1]); + while(y > (*++p)[1]) + ; } else { - while(y < (*++p)[1]); + while(y < (*++p)[1]) + ; } - + //for the loop to break there must have been a slope (straight line would do nothing) //vector::const_iterator p1 = p-1; Real dy = p[-1][1] - p[0][1]; Real dx = p[-1][0] - p[0][0]; - + assert(dy != 0); - + Real xi = p[0][0] + (y - p[0][1]) * dx / dy; return (x > xi)*ydir; } @@ -268,190 +271,190 @@ struct CurveArray Rect aabb; //not necessarily as effective - can only reject values vector pointlist; //run length - p0, p1, p2, p3 = p10, p11, p12, p13 = p20 ... vector degrees; - + CurveArray(Real x0 = 0, Real x1 = 0, Real y0 = 0, Real y1 = 0) - { + { aabb.set(x0,y0,x1,y1); } - + void reset(Real x0 = 0, Real x1 = 0, Real y0 = 0, Real y1 = 0) { aabb.set(x0,y0,x1,y1); pointlist.clear(); degrees.clear(); } - + int size () const { return degrees.size(); } - + void Start(Point m) - { + { reset(m[0],m[0],m[1],m[1]); - pointlist.push_back(m); + pointlist.push_back(m); } - + void AddCubic(Point p1, Point p2, Point dest) { aabb.expand(p1[0],p1[1]); aabb.expand(p2[0],p2[1]); aabb.expand(dest[0],dest[1]); - + pointlist.push_back(p1); pointlist.push_back(p2); pointlist.push_back(dest); - + degrees.push_back(3); } - + void AddConic(Point p1, Point dest) { aabb.expand(p1[0],p1[1]); aabb.expand(dest[0],dest[1]); - + pointlist.push_back(p1); pointlist.push_back(dest); - + degrees.push_back(2); } - - static int intersect_conic(Real x, Real y, Point *p, int level = 0) + + static int intersect_conic(Real x, Real y, Point *p, int /*level*/ = 0) { Real ymin,ymax,xmin,xmax; int intersects = 0; - + //sort the overall curve ys - degenerate detection ymin = min(p[0][1],p[2][1]); ymax = max(p[0][1],p[2][1]); - + xmin = min(min(p[0][0],p[1][0]),p[2][0]); xmax = max(max(p[0][0],p[1][0]),p[2][0]); - + //to the left, to the right and out of range y, or completely out of range y if( x < xmin ) return 0; - if( x > xmax & (y > ymax | y < ymin) ) return 0; - if( (y > ymax & y > p[1][1]) | (y < ymin & y < p[1][1]) ) return 0; - + if( x > xmax && (y > ymax || y < ymin) ) return 0; + if( (y > ymax && y > p[1][1]) || (y < ymin && y < p[1][1]) ) return 0; + //degenerate line max - if(ymin == ymax == p[1][1]) + if(ymin == ymax && ymax == p[1][1]) return 0; - + //degenerate accept - to the right and crossing the base line if(x > xmax) { - return (y <= ymax & y >= ymin); + return (y <= ymax && y >= ymin); } //solve for curve = y - - //real roots: + + //real roots: //0 roots - 0 intersection //1 root - get x, and figure out x //2 roots (non-double root) - get 2 xs, and count xs to the left - + //for conic we can assume 1 intersection for monotonic curve Real a = p[2][1] - 2*p[1][1] + p[0][1], b = 2*p[1][1] - 2*p[0][1], c = p[0][1] - y; - + Real t1 = -1, t2 = -1; - + if(a == 0) { //linear - easier :) if(b == 0) return 0; //may not need this check - + t1 = - c / b; //bt + c = 0 solved }else { //2 degree polynomial Real b2_4ac = b*b - 4*a*c; - + //if there are double/no roots - no intersections (in real #s that is) if(b2_4ac <= 0) { return 0; } - + b2_4ac = sqrt(b2_4ac); - + t1 = (-b - b2_4ac) / 2*a, t2 = (-b + b2_4ac) / 2*a; } - - //calculate number of intersections - if(t1 >= 0 & t1 <= 1) + + //calculate number of intersections + if(t1 >= 0 && t1 <= 1) { const Real t = t1; const Real invt = 1 - t; - + //find x val and it counts if it's to the left of the point const Real xi = invt*invt*p[0][0] + 2*t*invt*p[1][0] + t*t*p[2][0]; const Real dy_t = 2*a*t + b; - + if(dy_t) { intersects += (x >= xi) * ( dy_t > 0 ? 1 : -1); } } - if(t2 >= 0 & t2 <= 1) + if(t2 >= 0 && t2 <= 1) { const Real t = t2; const Real invt = 1 - t; - + //find x val and it counts if it's to the left of the point const Real xi = invt*invt*p[0][0] + 2*t*invt*p[1][0] + t*t*p[2][0]; const Real dy_t = 2*a*t + b; - + if(dy_t) { intersects += (x >= xi) * ( dy_t > 0 ? 1 : -1); } } - + return intersects; } - + static int quadratic_eqn(Real a, Real b, Real c, Real *t0, Real *t1) { const Real b2_4ac = b*b - 4*a*c; - + //degenerate reject (can't take sqrt) if(b2_4ac < 0) { return 0; } - - const Real sqrtb2_4ac = sqrt(b2_4ac); + + const Real sqrtb2_4ac = sqrt(b2_4ac); const Real signb = b < 0 ? -1 : 1; const Real q = - 0.5 * (b + signb * sqrtb2_4ac); - + *t0 = q/a; *t1 = c/q; - + return sqrtb2_4ac == 0 ? 1 : 2; } - + //Newton-Raphson root polishing (we don't care about bounds, assumes very near the desired root) static Real polish_cubicroot(Real a, Real b, Real c, Real d, Real t, Real *dpdt) { const Real cn[4] = {a,b,c,d}; Real p,dp,newt,oldpmag=FLT_MAX; - - //eval cubic eqn and it's derivative + + //eval cubic eqn and its derivative for(;;) { p = cn[0]*t + cn[1]; dp = cn[0]; - + for(int i = 2; i < 4; i++) { dp = p + dp*t; p = cn[i] + p*t; } - + if(dp == 0) { synfig::warning("polish_cubicroot: Derivative should not vanish!!!"); @@ -465,74 +468,74 @@ struct CurveArray *dpdt = dp; return t; } - - t = newt; + + t = newt; oldpmag = fabs(p); } } - - static int intersect_cubic(Real x, Real y, Point *p, int level = 0) + + static int intersect_cubic(Real x, Real y, Point *p, int /*level*/ = 0) { const Real INVALIDROOT = -FLT_MAX; Real ymin,ymax,xmin,xmax; Real ymin2,ymax2,ymintot,ymaxtot; int intersects = 0; - + //sort the overall curve ys and xs - degenerate detection - + //open span for the two end points ymin = min(p[0][1],p[3][1]); ymax = max(p[0][1],p[3][1]); - + //other points etc. ymin2 = min(p[1][1],p[2][1]); ymax2 = max(p[1][1],p[2][1]); - + ymintot = min(ymin,ymin2); ymaxtot = max(ymax,ymax2); - + //the entire curve control polygon is in this x range xmin = min(min(p[0][0],p[1][0]),min(p[2][0],p[3][0])); xmax = max(max(p[0][0],p[1][0]),max(p[2][0],p[3][0])); - + //outside all y boundaries (no intersect) if( (y > ymaxtot) || (y < ymintot) ) return 0; - + //left of curve (no intersect) if(x < xmin) return 0; - + //right of curve (and outside base range) if( x > xmax ) { - if( (y > ymax) | (y < ymin) ) return 0; - + if( (y > ymax) || (y < ymin) ) return 0; + //degenerate accept - to the right and inside the [ymin,ymax] range (already rejected if out of range) const Real n = p[3][1] - p[0][1]; - + //extract the sign from the value (we need valid data) return n < 0 ? -1 : 1; } - + //degenerate horizontal line max -- doesn't happen enough to check for if( ymintot == ymaxtot ) return 0; - + //calculate roots: // can have 0,1,2, or 3 real roots // if any of them are double then reject the two... - + // y-coefficients for f_y(t) - y = 0 Real a = p[3][1] - 3*p[2][1] + 3*p[1][1] - p[0][1], b = 3*p[2][1] - 6*p[1][1] + 3*p[0][1], c = 3*p[1][1] - 3*p[0][1], d = p[0][1] - y; - + Real ax = p[3][0] - 3*p[2][0] + 3*p[1][0] - p[0][0], bx = 3*p[2][0] - 6*p[1][0] + 3*p[0][0], cx = 3*p[1][0] - 3*p[0][0], dx = p[0][0]; - + Real t1 = INVALIDROOT, t2 = INVALIDROOT, t3 = INVALIDROOT, t, dydt; - + if(a == 0) { //only 2nd degree @@ -540,22 +543,22 @@ struct CurveArray { //linear if(c == 0) return 0; - + t1 = - d / c; //equation devolved into: ct + d = 0 - solve... }else { //0 roots = 0 intersections, 1 root = 2 intersections at the same place (0 effective) if(quadratic_eqn(a,b,c,&t1,&t2) != 2) return 0; - } + } }else { //cubic - sigh.... - + //algorithm courtesy of Numerical Recipes in C (algorithm copied from pg. 184/185) Real an = b / a, bn = c / a, cn = d / a; - + //if cn is 0 (or really really close), then we can simplify this... if(IsZero(cn)) { @@ -568,21 +571,21 @@ struct CurveArray } } else - { + { //otherwise run the normal cubic root equation Real Q = (an*an - 3.0*bn) / 9.0; Real R = ((2.0*an*an - 9.0*bn)*an + 27.0*cn)/54.0; - + if(R*R < Q*Q*Q) { Real theta = acos(R / sqrt(Q*Q*Q)); - + t1 = -2.0*sqrt(Q)*cos(theta/3) - an/3.0; t2 = -2.0*sqrt(Q)*cos((theta+2*PI)/3.0) - an/3.0; t3 = -2.0*sqrt(Q)*cos((theta-2*PI)/3.0) - an/3.0; - + //don't need to reorder,l just need to eliminate double/triple roots - //if(t3 == t2 & t1 == t2) t2 = t3 = INVALIDROOT; + //if(t3 == t2 && t1 == t2) t2 = t3 = INVALIDROOT; if(t3 == t2) t2 = t3 = INVALIDROOT; if(t1 == t2) t1 = t2 = INVALIDROOT; if(t1 == t3) t1 = t3 = INVALIDROOT; @@ -590,28 +593,28 @@ struct CurveArray { Real signR = R < 0 ? -1 : 1; Real A = - signR * pow(signR*R + sqrt(R*R - Q*Q*Q),1/3.0); - + Real B; if(A == 0) B = 0; else B = Q / A; - + //single real root in this case t1 = (A + B) - an/3.0; } } } - + //if(t1 != INVALIDROOT) { t = t1;//polish_cubicroot(a,b,c,d,t1,&dydt); - if(t >= 0 & t < 1) + if(t >= 0 && t < 1) { //const Real invt = 1 - t; - + //find x val and it counts if it's to the left of the point const Real xi = ((ax*t + bx)*t + cx)*t + dx; dydt = (3*a*t + 2*b)*t + c; - + if(dydt) { intersects += (x >= xi) * ( dydt > 0 ? 1 : -1); @@ -622,14 +625,14 @@ struct CurveArray //if(t2 != INVALIDROOT) { t = t2;//polish_cubicroot(a,b,c,d,t2,&dydt); - if(t >= 0 & t < 1) + if(t >= 0 && t < 1) { //const Real invt = 1 - t; - + //find x val and it counts if it's to the left of the point const Real xi = ((ax*t + bx)*t + cx)*t + dx; dydt = (3*a*t + 2*b)*t + c; - + if(dydt) { intersects += (x >= xi) * ( dydt > 0 ? 1 : -1); @@ -640,37 +643,37 @@ struct CurveArray //if(t3 != INVALIDROOT) { t = t3;//polish_cubicroot(a,b,c,d,t3,&dydt); - if(t >= 0 & t < 1) + if(t >= 0 && t < 1) { //const Real invt = 1 - t; - + //find x val and it counts if it's to the left of the point const Real xi = ((ax*t + bx)*t + cx)*t + dx; dydt = (3*a*t + 2*b)*t + c; - + if(dydt) { intersects += (x >= xi) * ( dydt > 0 ? 1 : -1); } } } - + return intersects; } - + int intersect(Real x,Real y, Point *table) const - { - if((y < aabb.miny) | (y > aabb.maxy) | (x < aabb.minx)) return 0; - + { + if((y < aabb.miny) || (y > aabb.maxy) || (x < aabb.minx)) return 0; + int i, curdeg, intersects = 0; const int numcurves = degrees.size(); - + vector::const_iterator p = pointlist.begin(); - + for(i=0; i < numcurves; i++) { curdeg = degrees[i]; - + switch(curdeg) { case 2: @@ -678,102 +681,104 @@ struct CurveArray table[0] = *p++; table[1] = *p++; table[2] = *p; //we want to include the last point for the next curve - + intersects += intersect_conic(x,y,table); - + break; } - + case 3: { table[0] = *p++; table[1] = *p++; table[2] = *p++; table[3] = *p; //we want to include the last point for the next curve - + intersects += intersect_cubic(x,y,table); - + break; } - + default: { warning("Invalid degree (%d) inserted into the list (index: %d)\n", curdeg, i); return 0; } - } + } } - + return intersects; } }; struct Layer_Shape::Intersector -{ +{ Rect aabb; + + //! true iff aabb hasn't been initialized yet bool initaabb; - + int flags; - + enum IntersectorFlags { NotClosed = 0x8000 }; - + enum PrimitiveType { TYPE_NONE = 0, TYPE_LINE, TYPE_CURVE }; - + Real cur_x,cur_y; Real close_x,close_y; - - vector segs; //monotonically increasing + + vector segs; //monotonically increasing vector curves; //big array of consecutive curves - + int prim; Vector tangent; - + Intersector() { clear(); } - + bool notclosed() { - return (flags & NotClosed) | (cur_x != close_x) | (cur_y != close_y); + return (flags & NotClosed) || (cur_x != close_x) || (cur_y != close_y); } - + void move_to(Real x, Real y) { close(); - + close_x = cur_x = x; close_y = cur_y = y; - + tangent[0] = tangent[1] = 0; - - if(initaabb) + + if(initaabb) { aabb.set_point(x,y); initaabb = false; }else aabb.expand(x,y); - + prim = TYPE_NONE; } - + void line_to(Real x, Real y) { int dir = (y > cur_y)*1 + (-1)*(y < cur_y); - + //check for context (if not line start a new segment) - //if we're not in line mode (cover's 0 set case), or if directions are different (not valid for 0 direction) + //if we're not in line mode (covers 0 set case), or if directions are different (not valid for 0 direction) if(prim != TYPE_LINE || (dir && segs.back().ydir != dir)) { MonoSegment seg(dir,x,x,y,y); - + seg.aabb.expand(cur_x,cur_y); seg.pointlist.push_back(Point(cur_x,cur_y)); seg.pointlist.push_back(Point(x,y)); @@ -781,32 +786,32 @@ struct Layer_Shape::Intersector } //add to the last segment, because it works else - { + { segs.back().pointlist.push_back(Point(x,y)); segs.back().aabb.expand(x,y); } - + cur_x = x; cur_y = y; - aabb.expand(x,y); //expand the entire things bounding box - + aabb.expand(x,y); //expand the entire thing's bounding box + tangent[0] = x - cur_x; tangent[1] = x - cur_y; - + flags |= NotClosed; prim = TYPE_LINE; } - + void conic_to_smooth(Real x, Real y) { const Real x1 = tangent[0]/2.0 + cur_x; const Real y1 = tangent[1]/2.0 + cur_y; - + conic_to(x1,y1,x,y); } - + void conic_to(Real x1, Real y1, Real x, Real y) { //if we're not already a curve start one @@ -816,34 +821,34 @@ struct Layer_Shape::Intersector c.Start(Point(cur_x,cur_y)); c.AddConic(Point(x1,y1),Point(x,y)); - + curves.push_back(c); }else { curves.back().AddConic(Point(x1,y1),Point(x,y)); } - + cur_x = x; cur_y = y; - + aabb.expand(x1,y1); aabb.expand(x,y); - + tangent[0] = 2*(x - x1); tangent[1] = 2*(y - y1); - + flags |= NotClosed; prim = TYPE_CURVE; } - + void curve_to_smooth(Real x2, Real y2, Real x, Real y) { Real x1 = tangent[0]/3.0 + cur_x; Real y1 = tangent[1]/3.0 + cur_y; - - curve_to(x1,y1,x2,y2,x,y); + + curve_to(x1,y1,x2,y2,x,y); } - + void curve_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y) { //if we're not already a curve start one @@ -853,28 +858,28 @@ struct Layer_Shape::Intersector c.Start(Point(cur_x,cur_y)); c.AddCubic(Point(x1,y1),Point(x2,y2),Point(x,y)); - + curves.push_back(c); }else { curves.back().AddCubic(Point(x1,y1),Point(x2,y2),Point(x,y)); } - + cur_x = x; cur_y = y; - + //expand bounding box around ALL of it aabb.expand(x1,y1); aabb.expand(x2,y2); aabb.expand(x,y); - + tangent[0] = 3*(x - x2); tangent[1] = 3*(y - y2); - + flags |= NotClosed; prim = TYPE_CURVE; } - + void close() { if(flags & NotClosed) @@ -883,40 +888,40 @@ struct Layer_Shape::Intersector { line_to(close_x,close_y); } - + flags &= ~NotClosed; } } - + //assumes the line to count the intersections with is (-1,0) int intersect (Real x, Real y) const { int inter = 0; unsigned int i; vector::const_iterator s = segs.begin(); - vector::const_iterator c = curves.begin(); - + vector::const_iterator c = curves.begin(); + Point memory[3*MAX_SUBDIVISION_SIZE + 1]; - + for(i = 0; i < segs.size(); i++,s++) { inter += s->intersect(x,y); } - + for(i=0; i < curves.size(); i++,c++) inter += c->intersect(x,y,memory); - + return inter; } - + //intersect an arbitrary line //int intersect (Real x, Real y, Real vx, Real vy) {return 0;} - + void clear() - { + { segs.clear(); curves.clear(); - + flags = 0; cur_x = cur_y = close_x = close_y = 0; prim = TYPE_NONE; @@ -930,19 +935,19 @@ struct PenMark { int y,x; Real cover,area; - - PenMark(){} + + PenMark(){} PenMark(int xin, int yin, Real c, Real a) :y(yin),x(xin),cover(c),area(a) {} - + void set(int xin, int yin, Real c, Real a) { y = yin; x = xin; cover = c; area = a; } - + void setcoord(int xin, int yin) { y = yin; x = xin; } - + void setcover(Real c, Real a) { cover = c; area = a; } void addcover(Real c, Real a) { cover += c; area += a; } - - bool operator < (const PenMark &rhs) const + + bool operator<(const PenMark &rhs) const { return y == rhs.y ? x < rhs.x : y < rhs.y; } @@ -969,7 +974,7 @@ public: //starting position of current primitive list Real close_x; Real close_y; - + //flags for the current segment int flags; @@ -982,30 +987,30 @@ public: NotSorted = 0x8000, NotClosed = 0x4000 }; - + //default constructor - 0 everything PolySpan() :current(0,0,0,0),flags(NotSorted) { cur_x = cur_y = close_x = close_y = 0; open_index = 0; } - + bool notclosed() const { - return (flags & NotClosed) | (cur_x != close_x) | (cur_y != close_y); + return (flags & NotClosed) || (cur_x != close_x) || (cur_y != close_y); } - - //0 out all the variables involved in processing + + //0 out all the variables involved in processing void clear() { covers.clear(); cur_x = cur_y = close_x = close_y = 0; - open_index = 0; + open_index = 0; current.set(0,0,0,0); flags = NotSorted; } - //add the current cell, but only if there is information to add + //add the current cell, but only if there is information to add void addcurrent() { if(current.cover || current.area) @@ -1013,17 +1018,17 @@ public: covers.push_back(current); } } - + //move to the next cell (cover values 0 initially), keeping the current if necessary void move_pen(int x, int y) { - if(y != current.y | x != current.x) + if(y != current.y || x != current.x) { addcurrent(); current.set(x,y,0,0); } } - + //close the primitives with a line (or rendering will not work as expected) void close() { @@ -1038,28 +1043,28 @@ public: flags &= ~NotClosed; } } - + // Not recommended - destroys any separation of spans currently held void merge_all() { sort(covers.begin(),covers.end()); open_index = 0; } - + //will sort the marks if they are not sorted void sort_marks() { if(flags & NotSorted) { - //only sort the open index + //only sort the open index addcurrent(); current.setcover(0,0); - + sort(covers.begin() + open_index,covers.end()); flags &= ~NotSorted; } } - + //encapsulate the current sublist of marks (used for drawing) void encapsulate_current() { @@ -1067,7 +1072,7 @@ public: sort_marks(); open_index = covers.size(); } - + //move to start a new primitive list (enclose the last primitive if need be) void move_to(Real x, Real y) { @@ -1083,24 +1088,32 @@ public: void line_to(Real x, Real y); void conic_to(Real x1, Real y1, Real x, Real y); void cubic_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y); - + void draw_scanline(int y, Real x1, Real y1, Real x2, Real y2); void draw_line(Real x1, Real y1, Real x2, Real y2); - - Real ExtractAlpha(Real area) - { - //non-zero winding style - if(area < 0) area = -area; - if(area > 1) area = 1; - - //even-odd winding style - /*if(area < 0) area = -area; - - while(area > 2) area -= 2; - if(area > 1) area = 1-area; //want pyramid like thing - */ - //broken? - yep broken - + + Real ExtractAlpha(Real area, WindingStyle winding_style) + { + if (area < 0) + area = -area; + + if (winding_style == WINDING_NON_ZERO) + { + // non-zero winding style + if (area > 1) + return 1; + } + else // if (winding_style == WINDING_EVEN_ODD) + { + // even-odd winding style + while (area > 1) + area -= 2; + + // want pyramid like thing + if (area < 0) + area = -area; + } + return area; } }; @@ -1116,6 +1129,7 @@ Layer_Shape::Layer_Shape(const Real &a, const Color::BlendMethod m): antialias (true), blurtype (Blur::FASTGAUSSIAN), feather (0), + winding_style (WINDING_NON_ZERO), bytestream (0), lastbyteop (Primitive::NONE), lastoppos (-1) @@ -1133,17 +1147,20 @@ Layer_Shape::clear() edge_table->clear(); bytestream.clear(); } - + bool Layer_Shape::set_param(const String & param, const ValueBase &value) { - IMPORT(color); + IMPORT_PLUS(color, { if (color.get_a() == 0) { if (converted_blend_) { + set_blend_method(Color::BLEND_ALPHA_OVER); + color.set_a(1); } else transparent_color_ = true; } }); IMPORT(offset); IMPORT(invert); IMPORT(antialias); IMPORT(feather); IMPORT(blurtype); - + IMPORT(winding_style); + return Layer_Composite::set_param(param,value); } @@ -1156,10 +1173,11 @@ Layer_Shape::get_param(const String ¶m)const EXPORT(antialias); EXPORT(feather); EXPORT(blurtype); - + EXPORT(winding_style); + EXPORT_NAME(); EXPORT_VERSION(); - + return Layer_Composite::get_param(param); } @@ -1167,20 +1185,20 @@ Layer::Vocab Layer_Shape::get_param_vocab()const { Layer::Vocab ret(Layer_Composite::get_param_vocab()); - + ret.push_back(ParamDesc("color") .set_local_name(_("Color")) .set_description(_("Layer_Shape Color")) ); ret.push_back(ParamDesc("offset") - .set_local_name(_("Position")) + .set_local_name(_("Offset")) ); ret.push_back(ParamDesc("invert") .set_local_name(_("Invert")) ); ret.push_back(ParamDesc("antialias") .set_local_name(_("Antialiasing")) - ); + ); ret.push_back(ParamDesc("feather") .set_local_name(_("Feather")) .set_is_distance() @@ -1195,7 +1213,14 @@ Layer_Shape::get_param_vocab()const .add_enum_value(Blur::GAUSSIAN,"gaussian",_("Gaussian Blur")) .add_enum_value(Blur::DISC,"disc",_("Disc Blur")) ); - + ret.push_back(ParamDesc("winding_style") + .set_local_name(_("Winding Style")) + .set_description(_("Winding style to use")) + .set_hint("enum") + .add_enum_value(WINDING_NON_ZERO,"nonzero",_("Non Zero")) + .add_enum_value(WINDING_EVEN_ODD,"evenodd",_("Even/Odd")) + ); + return ret; } @@ -1203,7 +1228,7 @@ synfig::Layer::Handle Layer_Shape::hit_check(synfig::Context context, const synfig::Point &p)const { Point pos(p-offset); - + int intercepts = edge_table->intersect(pos[0],pos[1]); // If we have an odd number of intercepts, we are inside. @@ -1245,18 +1270,18 @@ Color Layer_Shape::get_color(Context context, const Point &p)const { Point pp = p; - + if(feather) pp = Blur(feather,feather,blurtype)(p); - + Point pos(pp-offset); - + int intercepts = edge_table->intersect(pos[0],pos[1]); // If we have an odd number of intercepts, we are inside. // If we have an even number of intercepts, we are outside. bool intersect = ((!!intercepts) ^ invert); - + if(!intersect) return context.get_color(pp); @@ -1268,48 +1293,48 @@ Layer_Shape::get_color(Context context, const Point &p)const } //************** SCANLINE RENDERING ********************* -void Layer_Shape::PolySpan::line_to(Real x, Real y) -{ - Real n[4]; +void Layer_Shape::PolySpan::line_to(Real x, Real y) +{ + Real n[4] = {0,0,0,0}; bool afterx = false; - + const Real xin(x), yin(y); - + Real dx = x - cur_x; Real dy = y - cur_y; - + //CLIP IT!!!! try { //outside y - ignore entirely - if( (cur_y >= window.maxy & y >= window.maxy) - |(cur_y < window.miny & y < window.miny) ) + if( (cur_y >= window.maxy && y >= window.maxy) + ||(cur_y < window.miny && y < window.miny) ) { cur_x = x; - cur_y = y; + cur_y = y; } else //not degenerate - more complicated { if(dy > 0) //be sure it's not tooooo small - { + { // cur_y ... window.miny ... window.maxy ... y - + //initial degenerate - initial clip if(cur_y < window.miny) { //new clipped start point (must also move pen) n[2] = cur_x + (window.miny - cur_y) * dx / dy; - + cur_x = n[2]; cur_y = window.miny; move_pen((int)floor(cur_x),window.miny); } - - //generate data for the ending clipped info + + //generate data for the ending clipped info if(y > window.maxy) { - //intial line to intersection (and degenerate) + //initial line to intersection (and degenerate) n[2] = x + (window.maxy - y) * dx / dy; - + //intersect coords x = n[2]; y = window.maxy; @@ -1319,27 +1344,27 @@ void Layer_Shape::PolySpan::line_to(Real x, Real y) { //initial degenerate - initial clip if(cur_y > window.maxy) - { + { //new clipped start point (must also move pen) n[2] = cur_x + (window.maxy - cur_y) * dx / dy; - + cur_x = n[2]; cur_y = window.maxy; move_pen((int)floor(cur_x),window.maxy); } - - //generate data for the ending clipped info + + //generate data for the ending clipped info if(y < window.miny) { - //intial line to intersection (and degenerate) + //initial line to intersection (and degenerate) n[2] = x + (window.miny - y) * dx / dy; - + //intersect coords x = n[2]; y = window.miny; } } - + //all degenerate - but require bounded clipped values if( (cur_x >= window.maxx && x >= window.maxx) ||(cur_x < window.minx && x < window.minx) ) @@ -1347,19 +1372,19 @@ void Layer_Shape::PolySpan::line_to(Real x, Real y) //clip both vertices - but only needed in the x direction cur_x = max(cur_x, (Real)window.minx); cur_x = min(cur_x, (Real)window.maxx); - - //clip the dest values - y is already clipped + + //clip the dest values - y is already clipped x = max(x,(Real)window.minx); x = min(x,(Real)window.maxx); - + //must start at new point... move_pen((int)floor(cur_x),(int)floor(cur_y)); - + draw_line(cur_x,cur_y,x,y); - + cur_x = xin; cur_y = yin; - } + } else { //clip x @@ -1370,23 +1395,23 @@ void Layer_Shape::PolySpan::line_to(Real x, Real y) { //need to draw an initial segment from clippedx,cur_y to clippedx,intersecty n[2] = cur_y + (window.minx - cur_x) * dy / dx; - + move_pen(window.minx,(int)floor(cur_y)); draw_line(window.minx,cur_y,window.minx,n[2]); - + cur_x = window.minx; cur_y = n[2]; } - - //generate data for the ending clipped info + + //generate data for the ending clipped info if(x > window.maxx) { - //intial line to intersection (and degenerate) + //initial line to intersection (and degenerate) n[2] = y + (window.maxx - x) * dy / dx; - + n[0] = window.maxx; n[1] = y; - + //intersect coords x = window.maxx; y = n[2]; @@ -1399,39 +1424,39 @@ void Layer_Shape::PolySpan::line_to(Real x, Real y) { //need to draw an initial segment from clippedx,cur_y to clippedx,intersecty n[2] = cur_y + (window.maxx - cur_x) * dy / dx; - + move_pen(window.maxx,(int)floor(cur_y)); draw_line(window.maxx,cur_y,window.maxx,n[2]); - + cur_x = window.maxx; cur_y = n[2]; } - - //generate data for the ending clipped info + + //generate data for the ending clipped info if(x < window.minx) { - //intial line to intersection (and degenerate) + //initial line to intersection (and degenerate) n[2] = y + (window.minx - x) * dy / dx; - + n[0] = window.minx; n[1] = y; - + //intersect coords x = window.minx; y = n[2]; afterx = true; } } - + move_pen((int)floor(cur_x),(int)floor(cur_y)); //draw the relevant line (clipped) draw_line(cur_x,cur_y,x,y); - + if(afterx) { draw_line(x,y,n[0],n[1]); } - + cur_x = xin; cur_y = yin; } @@ -1448,9 +1473,9 @@ static inline bool clip_conic(const Point *const p, const ContextRect &r) const Real maxx = max(max(p[0][0],p[1][0]),p[2][0]); const Real maxy = max(max(p[0][1],p[1][1]),p[2][1]); - return (minx > r.maxx) | - (maxx < r.minx) | - (miny > r.maxy) | + return (minx > r.maxx) || + (maxx < r.minx) || + (miny > r.maxy) || (maxy < r.miny); } @@ -1465,28 +1490,28 @@ static inline bool clip_cubic(const Point *const p, const ContextRect &r) (maxx < r.minx) || (miny > r.maxy) || (maxy < r.miny);*/ - - return ((p[0][0] > r.maxx) & (p[1][0] > r.maxx) & (p[2][0] > r.maxx) & (p[3][0] > r.maxx)) | - ((p[0][0] < r.minx) & (p[1][0] < r.minx) & (p[2][0] < r.minx) & (p[3][0] < r.minx)) | - ((p[0][1] > r.maxy) & (p[1][1] > r.maxy) & (p[2][1] > r.maxy) & (p[3][1] > r.maxy)) | - ((p[0][1] < r.miny) & (p[1][1] < r.miny) & (p[2][1] < r.miny) & (p[3][1] < r.miny)); + + return ((p[0][0] > r.maxx) && (p[1][0] > r.maxx) && (p[2][0] > r.maxx) && (p[3][0] > r.maxx)) || + ((p[0][0] < r.minx) && (p[1][0] < r.minx) && (p[2][0] < r.minx) && (p[3][0] < r.minx)) || + ((p[0][1] > r.maxy) && (p[1][1] > r.maxy) && (p[2][1] > r.maxy) && (p[3][1] > r.maxy)) || + ((p[0][1] < r.miny) && (p[1][1] < r.miny) && (p[2][1] < r.miny) && (p[3][1] < r.miny)); } static inline Real max_edges_cubic(const Point *const p) { const Real x1 = p[1][0] - p[0][0]; const Real y1 = p[1][1] - p[0][1]; - + const Real x2 = p[2][0] - p[1][0]; const Real y2 = p[2][1] - p[1][1]; - + const Real x3 = p[3][0] - p[2][0]; const Real y3 = p[3][1] - p[2][1]; - + const Real d1 = x1*x1 + y1*y1; const Real d2 = x2*x2 + y2*y2; const Real d3 = x3*x3 + y3*y3; - + return max(max(d1,d2),d3); } @@ -1494,13 +1519,13 @@ static inline Real max_edges_conic(const Point *const p) { const Real x1 = p[1][0] - p[0][0]; const Real y1 = p[1][1] - p[0][1]; - + const Real x2 = p[2][0] - p[1][0]; const Real y2 = p[2][1] - p[1][1]; - + const Real d1 = x1*x1 + y1*y1; const Real d2 = x2*x2 + y2*y2; - + return max(d1,d2); } @@ -1510,25 +1535,25 @@ void Layer_Shape::PolySpan::conic_to(Real x1, Real y1, Real x, Real y) int level = 0; int num = 0; bool onsecond = false; - + arc[0] = Point(x,y); arc[1] = Point(x1,y1); arc[2] = Point(cur_x,cur_y); - + //just draw the line if it's outside if(clip_conic(arc,window)) { line_to(x,y); return; } - + //Ok so it's not super degenerate, subdivide and draw (run through minimum subdivision levels first) while(current >= arc) { if(num >= MAX_SUBDIVISION_SIZE) { - warning("Curve subdivision somehow ran out of space while tesselating!"); - + warning("Curve subdivision somehow ran out of space while tessellating!"); + //do something... assert(0); return; @@ -1567,7 +1592,7 @@ void Layer_Shape::PolySpan::conic_to(Real x1, Real y1, Real x, Real y) //cur_x,cur_y = current[2], so we need to go 1,0 line_to(current[1][0],current[1][1]); line_to(current[0][0],current[0][1]); - + current -= 2; if(onsecond) level--; num--; @@ -1582,32 +1607,32 @@ void Layer_Shape::PolySpan::cubic_to(Real x1, Real y1, Real x2, Real y2, Real x, int num = 0; int level = 0; bool onsecond = false; - + arc[0] = Point(x,y); arc[1] = Point(x2,y2); arc[2] = Point(x1,y1); arc[3] = Point(cur_x,cur_y); - + //just draw the line if it's outside if(clip_cubic(arc,window)) { line_to(x,y); return; } - + //Ok so it's not super degenerate, subdivide and draw (run through minimum subdivision levels first) while(current >= arc) //once current goes below arc, there are no more curves left { if(num >= MAX_SUBDIVISION_SIZE) { - warning("Curve subdivision somehow ran out of space while tesselating!"); - + warning("Curve subdivision somehow ran out of space while tessellating!"); + //do something... assert(0); return; }else - - //if we are not at the level minimum + + //if we are not at the level minimum if(level < MIN_SUBDIVISION_DRAW_LEVELS) { Subd_Cubic_Stack(current); @@ -1626,7 +1651,7 @@ void Layer_Shape::PolySpan::cubic_to(Real x1, Real y1, Real x2, Real y2, Real x, onsecond = true; num --; continue; - } + } else //split it again, if it's too big if(max_edges_cubic(current) > 0.25) //could use max_edges<3> @@ -1643,18 +1668,18 @@ void Layer_Shape::PolySpan::cubic_to(Real x1, Real y1, Real x2, Real y2, Real x, line_to(current[2][0],current[2][1]); line_to(current[1][0],current[1][1]); line_to(current[0][0],current[0][1]); - + current -= 3; if(onsecond) level--; num --; onsecond = true; } - } + } } //******************** LINE ALGORITHMS **************************** -// THESE CALCULATE THE AREA AND THE COVER FOR THE MARKS, TO THEN SCAN CONVERT -// - BROKEN UP INTO SCANLINES (draw_line - y intersections), +// THESE CALCULATE THE AREA AND THE COVER FOR THE MARKS, TO THEN SCAN CONVERT +// - BROKEN UP INTO SCANLINES (draw_line - y intersections), // THEN THE COVER AND AREA PER TOUCHED PIXEL IS CALCULATED (draw_scanline - x intersections) void Layer_Shape::PolySpan::draw_scanline(int y, Real x1, Real fy1, Real x2, Real fy2) { @@ -1662,23 +1687,23 @@ void Layer_Shape::PolySpan::draw_scanline(int y, Real x1, Real fy1, Real x2, Rea int ix2 = (int)floor(x2); Real fx1 = x1 - ix1; Real fx2 = x2 - ix2; - + Real dx,dy,dydx,mult; - + dx = x2 - x1; dy = fy2 - fy1; - + //case horizontal line if(fy1 == fy2) { move_pen(ix2,y); //pen needs to be at the last coord return; } - + //case all in same pixel if(ix1 == ix2) //impossible for degenerate case (covered by the previous cases) { - current.addcover(dy,(fx1 + fx2)*dy/2); //horizontal trapazoid area + current.addcover(dy,(fx1 + fx2)*dy/2); //horizontal trapezoid area return; } @@ -1686,32 +1711,32 @@ void Layer_Shape::PolySpan::draw_scanline(int y, Real x1, Real fy1, Real x2, Rea { // ----> fx1...1 0...1 ... 0...1 0...fx2 dydx = dy / dx; - + //set initial values //Iterate through the covered pixels mult = (1 - fx1)*dydx; //next y intersection diff value (at 1) - + //first pixel - current.addcover(mult,(1 + fx1)*mult/2); // fx1,fy1,1,fy@1 - starting trapazoidal area - + current.addcover(mult,(1 + fx1)*mult/2); // fx1,fy1,1,fy@1 - starting trapezoidal area + //move to the next pixel fy1 += mult; ix1++; - + move_pen(ix1,y); - + //set up for whole ones while(ix1 != ix2) { //trapezoid(0,y1,1,y1+dydx); - current.addcover(dydx,dydx/2); //accumulated area 1/2 the cover - + current.addcover(dydx,dydx/2); //accumulated area 1/2 the cover + //move to next pixel (+1) ix1++; fy1 += dydx; move_pen(ix1,y); } - + //last pixel //final y-pos - last intersect pos mult = fx2 * dydx; @@ -1722,35 +1747,35 @@ void Layer_Shape::PolySpan::draw_scanline(int y, Real x1, Real fy1, Real x2, Rea //mult = (0 - fx1) * dy / dx; //neg sign sucked into dydx dydx = -dy / dx; - + //set initial values //Iterate through the covered pixels mult = fx1*dydx; //next y intersection diff value - + //first pixel - current.addcover(mult,fx1*mult/2); // fx1,fy1,0,fy@0 - starting trapazoidal area - + current.addcover(mult,fx1*mult/2); // fx1,fy1,0,fy@0 - starting trapezoidal area + //move to next pixel fy1 += mult; ix1--; - + move_pen(ix1,y); - + //set up for whole ones while(ix1 != ix2) { //trapezoid(0,y1,1,y1+dydx); - current.addcover(dydx,dydx/2); //accumulated area 1/2 the cover - + current.addcover(dydx,dydx/2); //accumulated area 1/2 the cover + //move to next pixel (-1) fy1 += dydx; ix1--; move_pen(ix1,y); } - + //last pixel mult = fy2 - fy1; //final y-pos - last intersect pos - + current.addcover(mult,(fx2+1)*mult/2); } } @@ -1764,143 +1789,143 @@ void Layer_Shape::PolySpan::draw_line(Real x1, Real y1, Real x2, Real y2) assert(!isnan(fy1)); assert(!isnan(fy2)); - + Real dx,dy,dxdy,mult,x_from,x_to; - + const Real SLOPE_EPSILON = 1e-10; - + //case all one scanline if(iy1 == iy2) { draw_scanline(iy1,x1,y1,x2,y2); return; } - + //difference values dy = y2 - y1; - dx = x2 - x1; - + dx = x2 - x1; + //case vertical line if(dx < SLOPE_EPSILON && dx > -SLOPE_EPSILON) { - //calc area and cover on vertical line + //calc area and cover on vertical line if(dy > 0) { // ----> fx1...1 0...1 ... 0...1 0...fx2 Real sub; - + int ix1 = (int)floor(x1); Real fx1 = x1 - ix1; - + //current pixel sub = 1 - fy1; - + current.addcover(sub,fx1*sub); - + //next pixel iy1++; //move pen to next pixel move_pen(ix1,iy1); - + while(iy1 != iy2) - { + { //accumulate cover current.addcover(1,fx1); - + //next pixel iy1++; move_pen(ix1,iy1); } - + //last pixel current.addcover(fy2,fy2*fx1); }else { Real sub; - + int ix1 = (int)floor(x1); Real fx1 = x1 - ix1; - + //current pixel sub = 0 - fy1; - + current.addcover(sub,fx1*sub); - + //next pixel iy1--; - + move_pen(ix1,iy1); - + while(iy1 != iy2) { //accumulate in current pixel current.addcover(-1,-fx1); - + //move to next iy1--; move_pen(ix1,iy1); } - + current.addcover(fy2-1,(fy2-1)*fx1); } return; } //case normal line - guaranteed dx != 0 && dy != 0 - + //calculate the initial intersection with "next" scanline if(dy > 0) { dxdy = dx / dy; - + mult = (1 - fy1) * dxdy; - - //x interset scanline + + //x intersect scanline x_from = x1 + mult; draw_scanline(iy1,x1,fy1,x_from,1); - + //move to next line iy1++; - + move_pen((int)floor(x_from),iy1); - + while(iy1 != iy2) { //keep up on the x axis, and render the current scanline x_to = x_from + dxdy; draw_scanline(iy1,x_from,0,x_to,1); x_from = x_to; - + //move to next pixel iy1++; move_pen((int)floor(x_from),iy1); } - + //draw the last one, fractional draw_scanline(iy2,x_from,0,x2,fy2); - + }else { dxdy = -dx / dy; - + mult = fy1 * dxdy; - - //x interset scanline + + //x intersect scanline x_from = x1 + mult; draw_scanline(iy1,x1,fy1,x_from,0); - + //each line after iy1--; - + move_pen((int)floor(x_from),iy1); - + while(iy1 != iy2) { x_to = x_from + dxdy; draw_scanline(iy1,x_from,1,x_to,0); x_from = x_to; - + iy1--; move_pen((int)floor(x_from),iy1); } @@ -1915,10 +1940,10 @@ void Layer_Shape::move_to(Real x, Real y) //const int sizeblock = sizeof(Primitive)+sizeof(Point); Primitive op; Point p(x,y); - + op.operation = Primitive::MOVE_TO; op.number = 1; //one point for now - + if(lastbyteop == Primitive::MOVE_TO) { char *ptr = &bytestream[lastoppos]; @@ -1929,31 +1954,31 @@ void Layer_Shape::move_to(Real x, Real y) { lastbyteop = Primitive::MOVE_TO; lastoppos = bytestream.size(); - + bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data } - + edge_table->move_to(x,y); } void Layer_Shape::close() { Primitive op; - + op.operation = Primitive::CLOSE; op.number = 0; - + if(lastbyteop == Primitive::CLOSE) { }else { lastbyteop = Primitive::CLOSE; lastoppos = bytestream.size(); - + bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert header } - + edge_table->close(); //should not affect the bounding box since it would just be returning to old point... } @@ -1961,11 +1986,11 @@ void Layer_Shape::close() void Layer_Shape::endpath() { Primitive op; - + op.operation = Primitive::END; op.number = 0; - - if(lastbyteop == Primitive::END | lastbyteop == Primitive::NONE) + + if(lastbyteop == Primitive::END || lastbyteop == Primitive::NONE) { }else { @@ -1978,30 +2003,30 @@ void Layer_Shape::line_to(Real x, Real y) { assert(!isnan(x)); assert(!isnan(y)); - + //const int sizeblock = sizeof(Primitive)+sizeof(Point); Primitive op; Point p(x,y); - + op.operation = Primitive::LINE_TO; op.number = 1; //one point for now - - if(lastbyteop == Primitive::MOVE_TO | lastbyteop == Primitive::LINE_TO) + + if(lastbyteop == Primitive::MOVE_TO || lastbyteop == Primitive::LINE_TO) { //only need to insert the point bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); - + Primitive * prim = (Primitive *)&bytestream[lastoppos]; prim->number++; //increment number of points in the list }else { lastbyteop = Primitive::LINE_TO; lastoppos = bytestream.size(); - + bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data } - + edge_table->line_to(x,y); } @@ -2011,28 +2036,28 @@ void Layer_Shape::conic_to(Real x1, Real y1, Real x, Real y) Primitive op; Point p(x,y); Point p1(x1,y1); - + op.operation = Primitive::CONIC_TO; op.number = 2; //2 points for now - + if(lastbyteop == Primitive::CONIC_TO) { //only need to insert the new points bytestream.insert(bytestream.end(),(char*)&p1,(char*)(&p1+1)); bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); - + Primitive * prim = (Primitive *)&bytestream[lastoppos]; prim->number += 2; //increment number of points in the list }else { lastbyteop = Primitive::CONIC_TO; lastoppos = bytestream.size(); - + bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header bytestream.insert(bytestream.end(),(char*)&p1,(char*)(&p1+1)); //insert the bytes for data bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data } - + edge_table->conic_to(x1,y1,x,y); } @@ -2041,26 +2066,26 @@ void Layer_Shape::conic_to_smooth(Real x, Real y) //x1,y1 derived from curren //const int sizeblock = sizeof(Primitive)+sizeof(Point); Primitive op; Point p(x,y); - + op.operation = Primitive::CONIC_TO_SMOOTH; op.number = 1; //2 points for now - + if(lastbyteop == Primitive::CONIC_TO_SMOOTH) { //only need to insert the new point bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); - + Primitive * prim = (Primitive *)&bytestream[lastoppos]; prim->number += 1; //increment number of points in the list }else { lastbyteop = Primitive::CONIC_TO_SMOOTH; lastoppos = bytestream.size(); - + bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data } - + edge_table->conic_to_smooth(x,y); } @@ -2071,30 +2096,30 @@ void Layer_Shape::curve_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y) Point p(x,y); Point p1(x1,y1); Point p2(x2,y2); - + op.operation = Primitive::CUBIC_TO; op.number = 3; //3 points for now - + if(lastbyteop == Primitive::CUBIC_TO) { //only need to insert the new points bytestream.insert(bytestream.end(),(char*)&p1,(char*)(&p1+1)); bytestream.insert(bytestream.end(),(char*)&p2,(char*)(&p2+1)); bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); - + Primitive * prim = (Primitive *)&bytestream[lastoppos]; prim->number += 3; //increment number of points in the list }else { lastbyteop = Primitive::CUBIC_TO; lastoppos = bytestream.size(); - + bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header bytestream.insert(bytestream.end(),(char*)&p1,(char*)(&p1+1)); //insert the bytes for data bytestream.insert(bytestream.end(),(char*)&p2,(char*)(&p2+1)); //insert the bytes for data bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data } - + edge_table->curve_to(x1,y1,x2,y2,x,y); } @@ -2104,23 +2129,23 @@ void Layer_Shape::curve_to_smooth(Real x2, Real y2, Real x, Real y) //x1,y1 der Primitive op; Point p(x,y); Point p2(x2,y2); - + op.operation = Primitive::CUBIC_TO_SMOOTH; op.number = 2; //3 points for now - + if(lastbyteop == Primitive::CUBIC_TO_SMOOTH) { //only need to insert the new points bytestream.insert(bytestream.end(),(char*)&p2,(char*)(&p2+1)); bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); - + Primitive * prim = (Primitive *)&bytestream[lastoppos]; prim->number += 2; //increment number of points in the list }else { lastbyteop = Primitive::CUBIC_TO_SMOOTH; lastoppos = bytestream.size(); - + bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header bytestream.insert(bytestream.end(),(char*)&p2,(char*)(&p2+1)); //insert the bytes for data bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data @@ -2129,7 +2154,7 @@ void Layer_Shape::curve_to_smooth(Real x2, Real y2, Real x, Real y) //x1,y1 der // ACCELERATED RENDER FUNCTION - TRANSLATE BYTE CODE INTO FUNCTION CALLS -bool Layer_Shape::render_polyspan(Surface *surface, PolySpan &polyspan, +bool Layer_Shape::render_polyspan(Surface *surface, PolySpan &polyspan, Color::BlendMethod got_blend_method, Color::value_type got_amount) const { Surface::alpha_pen p(surface->begin(),got_amount,_BlendFunc(got_blend_method)); @@ -2137,12 +2162,12 @@ bool Layer_Shape::render_polyspan(Surface *surface, PolySpan &polyspan, PolySpan::cover_array::iterator end_mark = polyspan.covers.end(); Real cover,area,alpha; - + int y,x; - + p.set_value(color); cover = 0; - + if(cur_mark == end_mark) { //no marks at all @@ -2153,7 +2178,7 @@ bool Layer_Shape::render_polyspan(Surface *surface, PolySpan &polyspan, } return true; } - + //fill initial rect / line if(invert) { @@ -2161,76 +2186,76 @@ bool Layer_Shape::render_polyspan(Surface *surface, PolySpan &polyspan, p.move_to(polyspan.window.minx,polyspan.window.miny); y = polyspan.window.miny; int l = polyspan.window.maxx - polyspan.window.minx; - + p.put_block(cur_mark->y - polyspan.window.miny,l); - + //fill the area to the left of the first vertex on that line l = cur_mark->x - polyspan.window.minx; p.move_to(polyspan.window.minx,cur_mark->y); if(l) p.put_hline(l); } - + for(;;) { y = cur_mark->y; x = cur_mark->x; - + p.move_to(x,y); - + area = cur_mark->area; cover += cur_mark->cover; - + //accumulate for the current pixel while(++cur_mark != polyspan.covers.end()) { - if(y != cur_mark->y | x != cur_mark->x) + if(y != cur_mark->y || x != cur_mark->x) break; - + area += cur_mark->area; cover += cur_mark->cover; } - + //draw pixel - based on covered area if(area) //if we're ok, draw the current pixel { - alpha = polyspan.ExtractAlpha(cover - area); + alpha = polyspan.ExtractAlpha(cover - area, winding_style); if(invert) alpha = 1 - alpha; - + if(!antialias) { if(alpha >= .5) p.put_value(); } else if(alpha) p.put_value_alpha(alpha); - + p.inc_x(); x++; } - + //if we're done, don't use iterator and exit if(cur_mark == end_mark) break; - + //if there is no more live pixels on this line, goto next if(y != cur_mark->y) { if(invert) { //fill the area at the end of the line - p.put_hline(polyspan.window.maxx - x); - + p.put_hline(polyspan.window.maxx - x); + //fill area at the beginning of the next line p.move_to(polyspan.window.minx,cur_mark->y); p.put_hline(cur_mark->x - polyspan.window.minx); } - - cover = 0; - + + cover = 0; + continue; } - + //draw span to next pixel - based on total amount of pixel cover if(x < cur_mark->x) - { - alpha = polyspan.ExtractAlpha(cover); + { + alpha = polyspan.ExtractAlpha(cover, winding_style); if(invert) alpha = 1 - alpha; if(!antialias) @@ -2240,18 +2265,18 @@ bool Layer_Shape::render_polyspan(Surface *surface, PolySpan &polyspan, else if(alpha) p.put_hline(cur_mark->x - x,alpha); } } - + //fill the after stuff if(invert) { //fill the area at the end of the line - p.put_hline(polyspan.window.maxx - x); - + p.put_hline(polyspan.window.maxx - x); + //fill area at the beginning of the next line p.move_to(polyspan.window.minx,y+1); p.put_block(polyspan.window.maxy - y - 1,polyspan.window.maxx - polyspan.window.minx); } - + return true; } @@ -2262,14 +2287,14 @@ bool Layer_Shape::render_polyspan(etl::surface *surface, PolySpan &polysp PolySpan::cover_array::iterator end_mark = polyspan.covers.end(); Real cover,area,alpha; - + int y,x; - - cover = 0; - + + cover = 0; + //the pen always writes 1 (unless told to do otherwise) p.set_value(1); - + if(cur_mark == end_mark) { //no marks at all @@ -2280,7 +2305,7 @@ bool Layer_Shape::render_polyspan(etl::surface *surface, PolySpan &polysp } return true; } - + //fill initial rect / line if(invert) { @@ -2288,70 +2313,70 @@ bool Layer_Shape::render_polyspan(etl::surface *surface, PolySpan &polysp p.move_to(polyspan.window.minx,polyspan.window.miny); y = polyspan.window.miny; int l = polyspan.window.maxx - polyspan.window.minx; - + p.put_block(cur_mark->y - polyspan.window.miny,l); - + //fill the area to the left of the first vertex on that line l = cur_mark->x - polyspan.window.minx; p.move_to(polyspan.window.minx,cur_mark->y); if(l) p.put_hline(l); - + for(;;) { y = cur_mark->y; x = cur_mark->x; - + p.move_to(x,y); - + area = cur_mark->area; cover += cur_mark->cover; - + //accumulate for the current pixel while(++cur_mark != polyspan.covers.end()) { - if(y != cur_mark->y | x != cur_mark->x) + if(y != cur_mark->y || x != cur_mark->x) break; - + area += cur_mark->area; cover += cur_mark->cover; } - + //draw pixel - based on covered area if(area) //if we're ok, draw the current pixel { - alpha = 1 - polyspan.ExtractAlpha(cover - area); + alpha = 1 - polyspan.ExtractAlpha(cover - area, winding_style); if(!antialias) { if(alpha >= .5) p.put_value(); } else if(alpha) p.put_value(alpha); - + p.inc_x(); x++; } - + //if we're done, don't use iterator and exit if(cur_mark == end_mark) break; - + //if there is no more live pixels on this line, goto next if(y != cur_mark->y) { //fill the area at the end of the line p.put_hline(polyspan.window.maxx - x); - + //fill area at the beginning of the next line p.move_to(polyspan.window.minx,cur_mark->y); p.put_hline(cur_mark->x - polyspan.window.minx); - - cover = 0; - + + cover = 0; + continue; } - + //draw span to next pixel - based on total amount of pixel cover if(x < cur_mark->x) - { - alpha = 1 - polyspan.ExtractAlpha(cover); + { + alpha = 1 - polyspan.ExtractAlpha(cover, winding_style); if(!antialias) { if(alpha >= .5) p.put_hline(cur_mark->x - x); @@ -2359,10 +2384,10 @@ bool Layer_Shape::render_polyspan(etl::surface *surface, PolySpan &polysp else if(alpha) p.put_hline(cur_mark->x - x,alpha); } } - + //fill the area at the end of the line - p.put_hline(polyspan.window.maxx - x); - + p.put_hline(polyspan.window.maxx - x); + //fill area at the beginning of the next line p.move_to(polyspan.window.minx,y+1); p.put_block(polyspan.window.maxy - y - 1,polyspan.window.maxx - polyspan.window.minx); @@ -2372,51 +2397,51 @@ bool Layer_Shape::render_polyspan(etl::surface *surface, PolySpan &polysp { y = cur_mark->y; x = cur_mark->x; - + p.move_to(x,y); - + area = cur_mark->area; cover += cur_mark->cover; - + //accumulate for the current pixel while(++cur_mark != polyspan.covers.end()) { - if(y != cur_mark->y | x != cur_mark->x) + if(y != cur_mark->y || x != cur_mark->x) break; - + area += cur_mark->area; cover += cur_mark->cover; } - + //draw pixel - based on covered area if(area) //if we're ok, draw the current pixel { - alpha = polyspan.ExtractAlpha(cover - area); + alpha = polyspan.ExtractAlpha(cover - area, winding_style); if(!antialias) { if(alpha >= .5) p.put_value(); } else if(alpha) p.put_value(alpha); - + p.inc_x(); x++; } - + //if we're done, don't use iterator and exit if(cur_mark == end_mark) break; - + //if there is no more live pixels on this line, goto next if(y != cur_mark->y) - { - cover = 0; - + { + cover = 0; + continue; } - + //draw span to next pixel - based on total amount of pixel cover if(x < cur_mark->x) - { - alpha = polyspan.ExtractAlpha(cover); + { + alpha = polyspan.ExtractAlpha(cover, winding_style); if(!antialias) { if(alpha >= .5) p.put_hline(cur_mark->x - x); @@ -2425,7 +2450,7 @@ bool Layer_Shape::render_polyspan(etl::surface *surface, PolySpan &polysp } } } - + return true; } @@ -2434,60 +2459,60 @@ Layer_Shape::accelerated_render(Context context,Surface *surface,int quality, co { const unsigned int w = renddesc.get_w(); const unsigned int h = renddesc.get_h(); - + const Real pw = abs(renddesc.get_pw()); const Real ph = abs(renddesc.get_ph()); - + //const Real OFFSET_EPSILON = 1e-8; SuperCallback stageone(cb,1,10000,15001+renddesc.get_h()); SuperCallback stagetwo(cb,10000,10001+renddesc.get_h(),15001+renddesc.get_h()); SuperCallback stagethree(cb,10001+renddesc.get_h(),15001+renddesc.get_h(),15001+renddesc.get_h()); - + // Render what is behind us - + //clip if it satisfies the invert solid thing if(is_solid_color() && invert) - { + { Rect aabb = edge_table->aabb; Point tl = renddesc.get_tl() - offset; - + Real pw = renddesc.get_pw(), ph = renddesc.get_ph(); Rect nrect; - - Real pixelfeatherx = abs(feather/pw), - pixelfeathery = abs(feather/ph); - + + Real pixelfeatherx = quality == 10 ? 0 : abs(feather/pw), + pixelfeathery = quality == 10 ? 0 : abs(feather/ph); + nrect.set_point((aabb.minx - tl[0])/pw,(aabb.miny - tl[1])/ph); nrect.expand((aabb.maxx - tl[0])/pw,(aabb.maxy - tl[1])/ph); - + RendDesc optdesc(renddesc); - + //make sure to expand so we gain subpixels rather than lose them nrect.minx = floor(nrect.minx-pixelfeatherx); nrect.miny = floor(nrect.miny-pixelfeathery); nrect.maxx = ceil(nrect.maxx+pixelfeatherx); nrect.maxy = ceil(nrect.maxy+pixelfeathery); - + //make sure the subwindow is clipped with our tile window (minimize useless drawing) set_intersect(nrect,nrect,Rect(0,0,renddesc.get_w(),renddesc.get_h())); - + //must resize the surface first surface->set_wh(renddesc.get_w(),renddesc.get_h()); surface->clear(); - + //only render anything if it's visible from our current tile if(nrect.valid()) { //set the subwindow to the viewable pixels and render it to the subsurface optdesc.set_subwindow((int)nrect.minx, (int)nrect.miny, (int)(nrect.maxx - nrect.minx), (int)(nrect.maxy - nrect.miny)); - + Surface optimizedbacksurf; if(!context.accelerated_render(&optimizedbacksurf,quality,optdesc,&stageone)) return false; - + //blit that onto the original surface so we can pretend that nothing ever happened - Surface::pen p = surface->get_pen((int)nrect.minx,(int)nrect.miny); + Surface::pen p = surface->get_pen((int)nrect.minx,(int)nrect.miny); optimizedbacksurf.blit_to(p); } }else @@ -2495,22 +2520,22 @@ Layer_Shape::accelerated_render(Context context,Surface *surface,int quality, co if(!context.accelerated_render(surface,quality,renddesc,&stageone)) return false; } - + if(cb && !cb->amount_complete(10000,10001+renddesc.get_h())) return false; - - if(feather) + + if(feather && quality != 10) { //we have to blur rather than be crappy - + //so make a separate surface RendDesc workdesc(renddesc); - + etl::surface shapesurface; //the expanded size = 1/2 the size in each direction rounded up int halfsizex = (int) (abs(feather*.5/pw) + 3), halfsizey = (int) (abs(feather*.5/ph) + 3); - + //expand by 1/2 size in each direction on either side switch(blurtype) { @@ -2529,38 +2554,38 @@ Layer_Shape::accelerated_render(Context context,Surface *surface,int quality, co halfsizey*=2; } workdesc.set_subwindow(-max(1,halfsizex),-max(1,halfsizey),w+2*max(1,halfsizex),h+2*max(1,halfsizey)); - break; + break; } case Blur::GAUSSIAN: - { + { #define GAUSSIAN_ADJUSTMENT (0.05) Real pw = (Real)workdesc.get_w()/(workdesc.get_br()[0]-workdesc.get_tl()[0]); Real ph = (Real)workdesc.get_h()/(workdesc.get_br()[1]-workdesc.get_tl()[1]); - + pw=pw*pw; ph=ph*ph; - + halfsizex = (int)(abs(pw)*feather*GAUSSIAN_ADJUSTMENT+0.5); halfsizey = (int)(abs(ph)*feather*GAUSSIAN_ADJUSTMENT+0.5); - + halfsizex = (halfsizex + 1)/2; halfsizey = (halfsizey + 1)/2; workdesc.set_subwindow( -halfsizex, -halfsizey, w+2*halfsizex, h+2*halfsizey ); - + break; } } - + shapesurface.set_wh(workdesc.get_w(),workdesc.get_h()); shapesurface.clear(); - + //render the shape if(!render_shape(&shapesurface,quality,workdesc,&stagetwo))return false; - + //blur the image Blur(feather,feather,blurtype,&stagethree)(shapesurface,workdesc.get_br()-workdesc.get_tl(),shapesurface); - - //blend with stuff below it... + + //blend with stuff below it... unsigned int u = halfsizex, v = halfsizey, x = 0, y = 0; for(y = 0; y < h; y++,v++) { @@ -2576,14 +2601,14 @@ Layer_Shape::accelerated_render(Context context,Surface *surface,int quality, co //else (*surface)[y][x] = worksurface[v][u]; } } - + //we are done if(cb && !cb->amount_complete(100,100)) { synfig::warning("Layer_Shape: could not set amount complete"); return false; } - + return true; }else { @@ -2594,17 +2619,17 @@ Layer_Shape::accelerated_render(Context context,Surface *surface,int quality, co } bool -Layer_Shape::render_shape(Surface *surface,bool useblend,int quality, +Layer_Shape::render_shape(Surface *surface,bool useblend,int /*quality*/, const RendDesc &renddesc, ProgressCallback *cb)const { int tmp(0); - + SuperCallback progress(cb,0,renddesc.get_h(),renddesc.get_h()); - + // If our amount is set to zero, no need to render anything if(!get_amount()) return true; - + //test new polygon renderer // Build edge table // Width and Height of a pixel @@ -2612,14 +2637,18 @@ Layer_Shape::render_shape(Surface *surface,bool useblend,int quality, const int h = renddesc.get_h(); const Real pw = renddesc.get_w()/(renddesc.get_br()[0]-renddesc.get_tl()[0]); const Real ph = renddesc.get_h()/(renddesc.get_br()[1]-renddesc.get_tl()[1]); - + const Point tl = renddesc.get_tl(); - + Vector tangent (0,0); - + PolySpan span; - - //optimization for tesselating only inside tiles + + // if the pixels are zero sized then we're too zoomed out to see anything + if (pw == 0 || ph == 0) + return true; + + //optimization for tessellating only inside tiles span.window.minx = 0; span.window.miny = 0; span.window.maxx = w; @@ -2627,15 +2656,15 @@ Layer_Shape::render_shape(Surface *surface,bool useblend,int quality, //pointers for processing the bytestream const char *current = &bytestream[0]; - const char *end = &bytestream[bytestream.size()]; - + const char *end = &bytestream[bytestream.size()]; + int operation = Primitive::NONE; int number = 0; int curnum; - + Primitive *curprim; Point *data; - + Real x,y,x1,y1,x2,y2; @@ -2644,39 +2673,39 @@ Layer_Shape::render_shape(Surface *surface,bool useblend,int quality, tmp++; try { - + //get the op code safely curprim = (Primitive *)current; - + //advance past indices current += sizeof(Primitive); if(current > end) { warning("Layer_Shape::accelerated_render - Error in the byte stream, not enough space for next declaration"); - return false; + return false; } - + //get the relevant data operation = curprim->operation; number = curprim->number; - + if(operation == Primitive::END) break; - + if(operation == Primitive::CLOSE) { if(span.notclosed()) { tangent[0] = span.close_x - span.cur_x; tangent[1] = span.close_y - span.cur_y; - span.close(); + span.close(); } continue; } - + data = (Point*)current; current += sizeof(Point)*number; - + //check data positioning if(current > end) { @@ -2688,7 +2717,7 @@ Layer_Shape::render_shape(Surface *surface,bool useblend,int quality, //transfer all the data - RLE optimized for(curnum=0; curnum < number;) - { + { switch(operation) { case Primitive::MOVE_TO: @@ -2697,11 +2726,11 @@ Layer_Shape::render_shape(Surface *surface,bool useblend,int quality, x = (x - tl[0] + offset[0])*pw; y = data[curnum][1]; y = (y - tl[1] + offset[1])*ph; - + if(curnum == 0) { span.move_to(x,y); - + tangent[0] = 0; tangent[1] = 0; } @@ -2709,138 +2738,138 @@ Layer_Shape::render_shape(Surface *surface,bool useblend,int quality, { tangent[0] = x - span.cur_x; tangent[1] = y - span.cur_y; - + span.line_to(x,y); } - + curnum++; //only advance one point - + break; } - + case Primitive::LINE_TO: { x = data[curnum][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum][1]; y = (y - tl[1] + offset[1])*ph; - + tangent[0] = x - span.cur_x; tangent[1] = y - span.cur_y; - + span.line_to(x,y); curnum++; break; } - + case Primitive::CONIC_TO: { x = data[curnum+1][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum+1][1]; y = (y - tl[1] + offset[1])*ph; - + x1 = data[curnum][0]; x1 = (x1 - tl[0] + offset[0])*pw; y1 = data[curnum][1]; y1 = (y1 - tl[1] + offset[1])*ph; - + tangent[0] = 2*(x - x1); tangent[1] = 2*(y - y1); - + span.conic_to(x1,y1,x,y); curnum += 2; break; } - + case Primitive::CONIC_TO_SMOOTH: { x = data[curnum][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum][1]; y = (y - tl[1] + offset[1])*ph; - + x1 = span.cur_x + tangent[0]/2; y1 = span.cur_y + tangent[1]/2; tangent[0] = 2*(x - x1); tangent[1] = 2*(y - y1); - + span.conic_to(x1,y1,x,y); curnum ++; - + break; } - + case Primitive::CUBIC_TO: { x = data[curnum+2][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum+2][1]; y = (y - tl[1] + offset[1])*ph; - + x2 = data[curnum+1][0]; x2 = (x2 - tl[0] + offset[0])*pw; y2 = data[curnum+1][1]; y2 = (y2 - tl[1] + offset[1])*ph; - + x1 = data[curnum][0]; x1 = (x1 - tl[0] + offset[0])*pw; y1 = data[curnum][1]; y1 = (y1 - tl[1] + offset[1])*ph; - + tangent[0] = 2*(x - x2); tangent[1] = 2*(y - y2); - + span.cubic_to(x1,y1,x2,y2,x,y); curnum += 3; - + break; } - + case Primitive::CUBIC_TO_SMOOTH: { x = data[curnum+1][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum+1][1]; y = (y - tl[1] + offset[1])*ph; - + x2 = data[curnum][0]; x2 = (x2 - tl[0] + offset[0])*pw; y2 = data[curnum][1]; y2 = (y2 - tl[1] + offset[1])*ph; - - x1 = span.cur_x + tangent[0]/3.0; + + x1 = span.cur_x + tangent[0]/3.0; y1 = span.cur_y + tangent[1]/3.0; - + tangent[0] = 2*(x - x2); tangent[1] = 2*(y - y2); - + span.cubic_to(x1,y1,x2,y2,x,y); curnum += 2; - + break; } } } } - + //sort the bastards so we can render everything span.sort_marks(); - - return render_polyspan(surface, span, - useblend?get_blend_method():Color::BLEND_STRAIGHT, + + return render_polyspan(surface, span, + useblend?get_blend_method():Color::BLEND_STRAIGHT, useblend?get_amount():1.0); } bool -Layer_Shape::render_shape(surface *surface,int quality, - const RendDesc &renddesc, ProgressCallback *cb)const +Layer_Shape::render_shape(etl::surface *surface,int /*quality*/, + const RendDesc &renddesc, ProgressCallback */*cb*/)const { // If our amount is set to zero, no need to render anything if(!get_amount()) return true; - + //test new polygon renderer // Build edge table // Width and Height of a pixel @@ -2848,14 +2877,14 @@ Layer_Shape::render_shape(surface *surface,int quality, const int h = renddesc.get_h(); const Real pw = renddesc.get_w()/(renddesc.get_br()[0]-renddesc.get_tl()[0]); const Real ph = renddesc.get_h()/(renddesc.get_br()[1]-renddesc.get_tl()[1]); - + const Point tl = renddesc.get_tl(); - + Vector tangent (0,0); - + PolySpan span; - - //optimization for tesselating only inside tiles + + //optimization for tessellating only inside tiles span.window.minx = 0; span.window.miny = 0; span.window.maxx = w; @@ -2863,51 +2892,51 @@ Layer_Shape::render_shape(surface *surface,int quality, //pointers for processing the bytestream const char *current = &bytestream[0]; - const char *end = &bytestream[bytestream.size()]; - + const char *end = &bytestream[bytestream.size()]; + int operation = Primitive::NONE; int number = 0; int curnum; - + Primitive *curprim; Point *data; - + Real x,y,x1,y1,x2,y2; - + while(current < end) { //get the op code safely curprim = (Primitive *)current; - + //advance past indices current += sizeof(Primitive); if(current > end) { warning("Layer_Shape::accelerated_render - Error in the byte stream, not enough space for next declaration"); - return false; + return false; } - + //get the relevant data operation = curprim->operation; number = curprim->number; - + if(operation == Primitive::END) break; - + if(operation == Primitive::CLOSE) { if(span.notclosed()) { tangent[0] = span.close_x - span.cur_x; tangent[1] = span.close_y - span.cur_y; - span.close(); + span.close(); } continue; } - + data = (Point*)current; current += sizeof(Point)*number; - + //check data positioning if(current > end) { @@ -2917,7 +2946,7 @@ Layer_Shape::render_shape(surface *surface,int quality, //transfer all the data for(curnum=0; curnum < number;) - { + { switch(operation) { case Primitive::MOVE_TO: @@ -2926,11 +2955,11 @@ Layer_Shape::render_shape(surface *surface,int quality, x = (x - tl[0] + offset[0])*pw; y = data[curnum][1]; y = (y - tl[1] + offset[1])*ph; - + if(curnum == 0) { span.move_to(x,y); - + tangent[0] = 0; tangent[1] = 0; } @@ -2938,125 +2967,125 @@ Layer_Shape::render_shape(surface *surface,int quality, { tangent[0] = x - span.cur_x; tangent[1] = y - span.cur_y; - + span.line_to(x,y); } - + curnum++; //only advance one point - + break; } - + case Primitive::LINE_TO: { x = data[curnum][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum][1]; y = (y - tl[1] + offset[1])*ph; - + tangent[0] = x - span.cur_x; tangent[1] = y - span.cur_y; - + span.line_to(x,y); curnum++; break; } - + case Primitive::CONIC_TO: { x = data[curnum+1][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum+1][1]; y = (y - tl[1] + offset[1])*ph; - + x1 = data[curnum][0]; x1 = (x1 - tl[0] + offset[0])*pw; y1 = data[curnum][1]; y1 = (y1 - tl[1] + offset[1])*ph; - + tangent[0] = 2*(x - x1); tangent[1] = 2*(y - y1); - + span.conic_to(x1,y1,x,y); curnum += 2; break; } - + case Primitive::CONIC_TO_SMOOTH: { x = data[curnum][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum][1]; y = (y - tl[1] + offset[1])*ph; - + x1 = span.cur_x + tangent[0]/2; y1 = span.cur_y + tangent[1]/2; tangent[0] = 2*(x - x1); tangent[1] = 2*(y - y1); - + span.conic_to(x1,y1,x,y); curnum ++; - + break; } - + case Primitive::CUBIC_TO: { x = data[curnum+2][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum+2][1]; y = (y - tl[1] + offset[1])*ph; - + x2 = data[curnum+1][0]; x2 = (x2 - tl[0] + offset[0])*pw; y2 = data[curnum+1][1]; y2 = (y2 - tl[1] + offset[1])*ph; - + x1 = data[curnum][0]; x1 = (x1 - tl[0] + offset[0])*pw; y1 = data[curnum][1]; y1 = (y1 - tl[1] + offset[1])*ph; - + tangent[0] = 2*(x - x2); tangent[1] = 2*(y - y2); - + span.cubic_to(x1,y1,x2,y2,x,y); curnum += 3; - + break; } - + case Primitive::CUBIC_TO_SMOOTH: { x = data[curnum+1][0]; x = (x - tl[0] + offset[0])*pw; y = data[curnum+1][1]; y = (y - tl[1] + offset[1])*ph; - + x2 = data[curnum][0]; x2 = (x2 - tl[0] + offset[0])*pw; y2 = data[curnum][1]; y2 = (y2 - tl[1] + offset[1])*ph; - - x1 = span.cur_x + tangent[0]/3.0; + + x1 = span.cur_x + tangent[0]/3.0; y1 = span.cur_y + tangent[1]/3.0; - + tangent[0] = 2*(x - x2); tangent[1] = 2*(y - y2); - + span.cubic_to(x1,y1,x2,y2,x,y); curnum += 2; - + break; } } } } - + //sort the bastards so we can render everything span.sort_marks(); - + return render_polyspan(surface, span); } @@ -3066,9 +3095,12 @@ Layer_Shape::get_bounding_rect()const if(invert) return Rect::full_plane(); + if (edge_table->initaabb) + return Rect::zero(); + Rect bounds(edge_table->aabb+offset); - bounds.expand(max((bounds.get_min()-bounds.get_max()).mag()*0.01,feather)); - - + bounds.expand(max((bounds.get_min() - bounds.get_max()).mag()*0.01, + feather)); + return bounds; }