X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Ftags%2Fsynfig_0_61_06%2Fsrc%2Fsynfig%2Flayer_shape.cpp;fp=synfig-core%2Ftags%2Fsynfig_0_61_06%2Fsrc%2Fsynfig%2Flayer_shape.cpp;h=0000000000000000000000000000000000000000;hb=6fa8f2f38d4b0b35f8539bf94e27ae27015c7689;hp=2dd949ee72b81fd69c6c74856fc4b9f8292df60f;hpb=47fce282611fbba1044921d22ca887f9b53ad91a;p=synfig.git diff --git a/synfig-core/tags/synfig_0_61_06/src/synfig/layer_shape.cpp b/synfig-core/tags/synfig_0_61_06/src/synfig/layer_shape.cpp deleted file mode 100644 index 2dd949e..0000000 --- a/synfig-core/tags/synfig_0_61_06/src/synfig/layer_shape.cpp +++ /dev/null @@ -1,3097 +0,0 @@ -/* === S Y N F I G ========================================================= */ -/*! \file layer_shape.cpp -** \brief Template Header -** -** $Id$ -** -** \legal -** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley -** -** This package is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public License as -** published by the Free Software Foundation; either version 2 of -** the License, or (at your option) any later version. -** -** This package is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** General Public License for more details. -** \endlegal -*/ -/* ========================================================================= */ - -/* === H E A D E R S ======================================================= */ - -#ifdef USING_PCH -# include "pch.h" -#else -#ifdef HAVE_CONFIG_H -# include -#endif - -#include "layer_shape.h" -#include "string.h" -#include "time.h" -#include "context.h" -#include "paramdesc.h" -#include "renddesc.h" -#include "surface.h" -#include "value.h" -#include "valuenode.h" -#include "float.h" -#include "blur.h" - -#include "curve_helper.h" - -#include - -#include - -#endif - -/* === U S I N G =========================================================== */ - -using namespace synfig; -using namespace std; -using namespace etl; - -/* === G L O B A L S ======================================================= */ - -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_VERSION(Layer_Shape,"0.1"); -SYNFIG_LAYER_SET_CVS_ID(Layer_Shape,"$Id$"); - -#define EPSILON 1e-12 - -template < class T > -inline bool IsZero(const T &n) -{ - return (n < EPSILON) && (n > -EPSILON); -} - -/* === C L A S S E S ======================================================= */ - -//Assumes 64 byte aligned structures if at all -struct Primitive -{ - int operation; - int number; - - //Point data[0]; - - enum Operations - { - NONE = -1, - MOVE_TO = 0, //(x,y)+ after first point treated as line_to - CLOSE, // NOT RUNLENGTH enabled - LINE_TO, //(x,y)+ continuous func - CONIC_TO, //(x1,y1,x,y)+ " " - CONIC_TO_SMOOTH, //(x,y)+ " " - CUBIC_TO, //(x1,y1,x2,y2,x,y)+ " " - CUBIC_TO_SMOOTH, //(x2,y2,x,y)+ " " - END - }; -}; - -//******** CURVE FUNCTIONS ***************** -const int MAX_SUBDIVISION_SIZE = 64; -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 * - * - - 0.1.2 -> 0.1 2 3.4 - - */ - - Real a,b; - - - arc[4][0] = arc[2][0]; - b = arc[1][0]; - - a = arc[1][0] = (arc[0][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 - * 1+2 b * 0+3*1+3*2+3 - 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; - - */ -} - -//************** PARAMETRIC RENDERER SUPPORT STRUCTURES **************** - -// super segment -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; - aabb.maxy = y1; - - ydir = dir; - } - - int intersect(Real x,Real y) const - { - if((y < aabb.miny) || (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]); - } - else - { - 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; - } -}; - -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); - } - - 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) - { - 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; - - //degenerate line max - if(ymin == ymax == p[1][1]) - return 0; - - //degenerate accept - to the right and crossing the base line - if(x > xmax) - { - return (y <= ymax && y >= ymin); - } - - //solve for curve = y - - //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) - { - 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) - { - 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 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 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!!!"); - return t; - } - - newt = t - p/dp; - - if(newt == t || fabs(p) >= oldpmag) - { - *dpdt = dp; - return t; - } - - t = newt; - oldpmag = fabs(p); - } - } - - 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; - - //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 - if(b == 0) - { - //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)) - { - t3 = 0; - - //0 roots = 0 intersections, 1 root = 2 intersections at the same place (0 effective) - if(quadratic_eqn(a,b,c,&t1,&t2) != 2) - { - t1 = t2 = INVALIDROOT; - } - } - 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) t2 = t3 = INVALIDROOT; - if(t1 == t2) t1 = t2 = INVALIDROOT; - if(t1 == t3) t1 = t3 = INVALIDROOT; - }else - { - 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) - { - //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); - } - } - } - - //if(t2 != INVALIDROOT) - { - t = t2;//polish_cubicroot(a,b,c,d,t2,&dydt); - 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); - } - } - } - - //if(t3 != INVALIDROOT) - { - t = t3;//polish_cubicroot(a,b,c,d,t3,&dydt); - 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; - - 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: - { - 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 initialised 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 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); - } - - 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) - { - 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 (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)); - segs.push_back(seg); - } - //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 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 - if(prim != TYPE_CURVE) - { - CurveArray c; - - 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); - } - - void curve_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y) - { - //if we're not already a curve start one - if(prim != TYPE_CURVE) - { - CurveArray c; - - 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) - { - if(cur_x != close_x || cur_y != close_y) - { - 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(); - - 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; - tangent[0] = tangent[1] = 0; - initaabb = true; - } -}; - -//*********** SCANLINE RENDERER SUPPORT STRUCTURES *************** -struct PenMark -{ - int y,x; - Real cover,area; - - 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 - { - return y == rhs.y ? x < rhs.x : y < rhs.y; - } -}; - -typedef rect ContextRect; - -class Layer_Shape::PolySpan -{ -public: - typedef deque cover_array; - - Point arc[3*MAX_SUBDIVISION_SIZE + 1]; - - cover_array covers; - PenMark current; - - int open_index; - - //ending position of last primitive - Real cur_x; - Real cur_y; - - //starting position of current primitive list - Real close_x; - Real close_y; - - //flags for the current segment - int flags; - - //the window that will be drawn (used for clipping) - ContextRect window; - - //for assignment to flags value - enum PolySpanFlags - { - 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); - } - - //0 out all the variables involved in processing - void clear() - { - covers.clear(); - cur_x = cur_y = close_x = close_y = 0; - open_index = 0; - current.set(0,0,0,0); - flags = NotSorted; - } - - //add the current cell, but only if there is information to add - void addcurrent() - { - if(current.cover || current.area) - { - 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) - { - addcurrent(); - current.set(x,y,0,0); - } - } - - //close the primitives with a line (or rendering will not work as expected) - void close() - { - if(flags & NotClosed) - { - if(cur_x != close_x || cur_y != close_y) - { - line_to(close_x,close_y); - addcurrent(); - current.setcover(0,0); - } - 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 - 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() - { - //sort the current list then reposition the open list section - 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) - { - close(); - if(isnan(x))x=0; - if(isnan(y))y=0; - move_pen((int)floor(x),(int)floor(y)); - close_y = cur_y = y; - close_x = cur_x = x; - } - - //primitive_to functions - 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, 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; - } -}; - -/* === M E T H O D S ======================================================= */ - -Layer_Shape::Layer_Shape(const Real &a, const Color::BlendMethod m): - Layer_Composite (a,m), - edge_table (new Intersector), - color (Color::black()), - offset (0,0), - invert (false), - antialias (true), - blurtype (Blur::FASTGAUSSIAN), - feather (0), - winding_style (WINDING_NON_ZERO), - bytestream (0), - lastbyteop (Primitive::NONE), - lastoppos (-1) -{ -} - -Layer_Shape::~Layer_Shape() -{ - delete edge_table; -} - -void -Layer_Shape::clear() -{ - edge_table->clear(); - bytestream.clear(); -} - -bool -Layer_Shape::set_param(const String & param, const ValueBase &value) -{ - IMPORT(color); - IMPORT(offset); - IMPORT(invert); - IMPORT(antialias); - IMPORT(feather); - IMPORT(blurtype); - IMPORT(winding_style); - - return Layer_Composite::set_param(param,value); -} - -ValueBase -Layer_Shape::get_param(const String ¶m)const -{ - EXPORT(color); - EXPORT(offset); - EXPORT(invert); - EXPORT(antialias); - EXPORT(feather); - EXPORT(blurtype); - EXPORT(winding_style); - - EXPORT_NAME(); - EXPORT_VERSION(); - - return Layer_Composite::get_param(param); -} - -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")) - ); - 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() - ); - ret.push_back(ParamDesc("blurtype") - .set_local_name(_("Type of Feather")) - .set_description(_("Type of feathering to use")) - .set_hint("enum") - .add_enum_value(Blur::BOX,"box",_("Box Blur")) - .add_enum_value(Blur::FASTGAUSSIAN,"fastgaussian",_("Fast Gaussian Blur")) - .add_enum_value(Blur::CROSS,"cross",_("Cross-Hatch Blur")) - .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; -} - -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. - // If we have an even number of intercepts, we are outside. - bool intersect = ((!!intercepts) ^ invert); - - if(get_amount() == 0 || get_blend_method() == Color::BLEND_ALPHA_OVER) - { - intersect = false; - } - - if(intersect) - { - synfig::Layer::Handle tmp; - if(get_blend_method()==Color::BLEND_BEHIND && (tmp=context.hit_check(p))) - return tmp; - if(Color::is_onto(get_blend_method())) - { - //if there's something in the lower layer then we're set... - if(!context.hit_check(p).empty()) - return const_cast(this); - }else if(get_blend_method() == Color::BLEND_ALPHA_OVER) - { - synfig::info("layer_shape::hit_check - we've got alphaover"); - //if there's something in the lower layer then we're set... - if(color.get_a() < 0.1 && get_amount() > .9) - { - synfig::info("layer_shape::hit_check - can see through us... so nothing"); - return Handle(); - }else return context.hit_check(p); - }else - return const_cast(this); - } - - return context.hit_check(p); -} - -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); - - //Ok, we're inside... bummmm ba bum buM... - if(get_blend_method() == Color::BLEND_STRAIGHT && get_amount() == 1) - return color; - else - return Color::blend(color,context.get_color(p),get_amount(),get_blend_method()); -} - -//************** SCANLINE RENDERING ********************* -void Layer_Shape::PolySpan::line_to(Real x, Real y) -{ - Real n[4]; - 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) ) - { - cur_x = x; - 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 - if(y > window.maxy) - { - //intial line to intersection (and degenerate) - n[2] = x + (window.maxy - y) * dx / dy; - - //intersect coords - x = n[2]; - y = window.maxy; - } - } - else - { - //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 - if(y < window.miny) - { - //intial 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) ) - { - //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 - 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 - if(dx > 0) - { - //initial degenerate - initial clip - if(cur_x < window.minx) - { - //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 - if(x > window.maxx) - { - //intial 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]; - afterx = true; - } - }else - { - //initial degenerate - initial clip - if(cur_x > window.maxx) - { - //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 - if(x < window.minx) - { - //intial 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; - } - } - } catch(...) { synfig::error("line_to: cur_x=%f, cur_y=%f, x=%f, y=%f", cur_x, cur_y, x, y); throw; } - - flags |= NotClosed|NotSorted; -} - -static inline bool clip_conic(const Point *const p, const ContextRect &r) -{ - const Real minx = min(min(p[0][0],p[1][0]),p[2][0]); - const Real miny = min(min(p[0][1],p[1][1]),p[2][1]); - 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) || - (maxy < r.miny); -} - -static inline bool clip_cubic(const Point *const p, const ContextRect &r) -{ - /*const Real minx = min(min(p[0][0],p[1][0]),min(p[2][0],p[3][0])); - const Real miny = min(min(p[0][1],p[1][1]),min(p[2][1],p[3][1])); - const Real maxx = max(max(p[0][0],p[1][0]),max(p[2][0],p[3][1])); - const Real maxy = max(max(p[0][1],p[1][1]),max(p[2][1],p[3][1])); - - return (minx > r.maxx) || - (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)); -} - -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); -} - -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); -} - -void Layer_Shape::PolySpan::conic_to(Real x1, Real y1, Real x, Real y) -{ - Point *current = arc; - 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!"); - - //do something... - assert(0); - return; - }else - //if the curve is clipping then draw degenerate - if(clip_conic(current,window)) - { - line_to(current[0][0],current[0][1]); //backwards so front is destination - current -= 2; - if(onsecond) level--; - onsecond = true; - num--; - continue; - }else - //if we are not at the level minimum - if(level < MIN_SUBDIVISION_DRAW_LEVELS) - { - Subd_Conic_Stack(current); - current += 2; //cursor on second curve - level ++; - num ++; - onsecond = false; - continue; - }else - //split it again, if it's too big - if(max_edges_conic(current) > 0.25) //distance of .5 (cover no more than half the pixel) - { - Subd_Conic_Stack(current); - current += 2; //cursor on second curve - level ++; - num ++; - onsecond = false; - } - else //NOT TOO BIG? RENDER!!! - { - //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--; - onsecond = true; - } - } -} - -void Layer_Shape::PolySpan::cubic_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y) -{ - Point *current = arc; - 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!"); - - //do something... - assert(0); - return; - }else - - //if we are not at the level minimum - if(level < MIN_SUBDIVISION_DRAW_LEVELS) - { - Subd_Cubic_Stack(current); - current += 3; //cursor on second curve - level ++; - num ++; - onsecond = false; - continue; - }else - //if the curve is clipping then draw degenerate - if(clip_cubic(current,window)) - { - line_to(current[0][0],current[0][1]); //backwards so front is destination - current -= 3; - if(onsecond) level--; - 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> - { - Subd_Cubic_Stack(current); - current += 3; //cursor on second curve - level ++; - num ++; - onsecond = false; - } - else //NOT TOO BIG? RENDER!!! - { - //cur_x,cur_y = current[3], so we need to go 2,1,0 - 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), -// 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) -{ - int ix1 = (int)floor(x1); - 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 - return; - } - - if(dx > 0) - { - // ----> 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 - - //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 - - //move to next pixel (+1) - ix1++; - fy1 += dydx; - move_pen(ix1,y); - } - - //last pixel - //final y-pos - last intersect pos - mult = fx2 * dydx; - current.addcover(mult,(0+fx2)*mult/2); - }else - { - // fx2...1 0...1 ... 0...1 0...fx1 <---- - //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 - - //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 - - //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); - } -} - -void Layer_Shape::PolySpan::draw_line(Real x1, Real y1, Real x2, Real y2) -{ - int iy1 = (int)floor(y1); - int iy2 = (int)floor(y2); - Real fy1 = y1 - iy1; - Real fy2 = y2 - iy2; - - 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; - - //case vertical line - if(dx < SLOPE_EPSILON && dx > -SLOPE_EPSILON) - { - //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_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_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); - } - //draw the last one, fractional - draw_scanline(iy2,x_from,1,x2,fy2); - } -} - -//****** LAYER PEN OPERATIONS (move_to, line_to, etc.) ****** -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]; - memcpy(ptr,&op,sizeof(op)); - memcpy(ptr+sizeof(op),&p,sizeof(p)); - } - else //make a new op - { - 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... -} - -void Layer_Shape::endpath() -{ - Primitive op; - - op.operation = Primitive::END; - op.number = 0; - - if(lastbyteop == Primitive::END || lastbyteop == Primitive::NONE) - { - }else - { - bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); - } - //should not affect the bounding box since it would just be returning to old point... if at all -} - -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) - { - //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); -} - -void Layer_Shape::conic_to(Real x1, Real y1, Real x, Real y) -{ - //const int sizeblock = sizeof(Primitive)+sizeof(Point)*2; - 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); -} - -void Layer_Shape::conic_to_smooth(Real x, Real y) //x1,y1 derived from current tangent -{ - //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); -} - -void Layer_Shape::curve_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y) -{ - //const int sizeblock = sizeof(Primitive)+sizeof(Point)*3; - Primitive op; - 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); -} - -void Layer_Shape::curve_to_smooth(Real x2, Real y2, Real x, Real y) //x1,y1 derived from current tangent -{ - //const int sizeblock = sizeof(Primitive)+sizeof(Point)*3; - 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 - } -} - -// ACCELERATED RENDER FUNCTION - TRANSLATE BYTE CODE INTO FUNCTION CALLS - -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)); - PolySpan::cover_array::iterator cur_mark = polyspan.covers.begin(); - 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 - if(invert) - { - p.move_to(polyspan.window.minx,polyspan.window.miny); - p.put_block(polyspan.window.maxy - polyspan.window.miny,polyspan.window.maxx - polyspan.window.minx); - } - return true; - } - - //fill initial rect / line - if(invert) - { - //fill all the area above the first vertex - 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) - 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, 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); - - //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; - - continue; - } - - //draw span to next pixel - based on total amount of pixel cover - if(x < cur_mark->x) - { - alpha = polyspan.ExtractAlpha(cover, winding_style); - if(invert) alpha = 1 - alpha; - - if(!antialias) - { - if(alpha >= .5) p.put_hline(cur_mark->x - x); - } - 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); - - //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; -} - -bool Layer_Shape::render_polyspan(etl::surface *surface, PolySpan &polyspan) const -{ - etl::surface::pen p(surface->begin()); - PolySpan::cover_array::iterator cur_mark = polyspan.covers.begin(); - PolySpan::cover_array::iterator end_mark = polyspan.covers.end(); - - Real cover,area,alpha; - - int y,x; - - 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 - if(invert) - { - p.move_to(polyspan.window.minx,polyspan.window.miny); - p.put_block(polyspan.window.maxy - polyspan.window.miny,polyspan.window.maxx - polyspan.window.minx); - } - return true; - } - - //fill initial rect / line - if(invert) - { - //fill all the area above the first vertex - 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) - 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, 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; - - continue; - } - - //draw span to next pixel - based on total amount of pixel cover - if(x < cur_mark->x) - { - alpha = 1 - polyspan.ExtractAlpha(cover, winding_style); - if(!antialias) - { - if(alpha >= .5) p.put_hline(cur_mark->x - x); - } - 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); - - //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); - }else - { - 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) - 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, 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; - - continue; - } - - //draw span to next pixel - based on total amount of pixel cover - if(x < cur_mark->x) - { - alpha = polyspan.ExtractAlpha(cover, winding_style); - if(!antialias) - { - if(alpha >= .5) p.put_hline(cur_mark->x - x); - } - else if(alpha) p.put_hline(cur_mark->x - x,alpha); - } - } - } - - return true; -} - -bool -Layer_Shape::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const -{ - 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); - - 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); - optimizedbacksurf.blit_to(p); - } - }else - { - if(!context.accelerated_render(surface,quality,renddesc,&stageone)) - return false; - } - - if(cb && !cb->amount_complete(10000,10001+renddesc.get_h())) return false; - - if(feather) - { - //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) - { - case Blur::DISC: - case Blur::BOX: - case Blur::CROSS: - { - workdesc.set_subwindow(-max(1,halfsizex),-max(1,halfsizey),w+2*max(1,halfsizex),h+2*max(1,halfsizey)); - break; - } - case Blur::FASTGAUSSIAN: - { - if(quality < 4) - { - halfsizex*=2; - halfsizey*=2; - } - workdesc.set_subwindow(-max(1,halfsizex),-max(1,halfsizey),w+2*max(1,halfsizex),h+2*max(1,halfsizey)); - 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... - unsigned int u = halfsizex, v = halfsizey, x = 0, y = 0; - for(y = 0; y < h; y++,v++) - { - u = halfsizex; - for(x = 0; x < w; x++,u++) - { - float a = shapesurface[v][u]; - if(a) - { - //a = floor(a*255+0.5f)/255; - (*surface)[y][x]=Color::blend(color,(*surface)[y][x],a*get_amount(),get_blend_method()); - } - //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 - { - //might take out to reduce code size - return render_shape(surface,true,quality,renddesc,&stagetwo); - } - -} - -bool -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 - const int w = renddesc.get_w(); - 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 - span.window.minx = 0; - span.window.miny = 0; - span.window.maxx = w; - span.window.maxy = h; - - //pointers for processing the bytestream - const char *current = &bytestream[0]; - 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) - { - 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; - } - - //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(); - } - continue; - } - - data = (Point*)current; - current += sizeof(Point)*number; - - //check data positioning - if(current > end) - { - warning("Layer_Shape::accelerated_render - Error in the byte stream, in sufficient data space for declared number of points"); - return false; - } - - } catch(...) { synfig::error("Layer_Shape::render_shape()1: Caught an exception after %d loops, rethrowing...", tmp); throw; } - - //transfer all the data - RLE optimized - for(curnum=0; curnum < number;) - { - switch(operation) - { - case Primitive::MOVE_TO: - { - x = data[curnum][0]; - 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; - } - else - { - 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; - 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, - useblend?get_amount():1.0); -} - -bool -Layer_Shape::render_shape(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 - const int w = renddesc.get_w(); - 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 - span.window.minx = 0; - span.window.miny = 0; - span.window.maxx = w; - span.window.maxy = h; - - //pointers for processing the bytestream - const char *current = &bytestream[0]; - 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; - } - - //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(); - } - continue; - } - - data = (Point*)current; - current += sizeof(Point)*number; - - //check data positioning - if(current > end) - { - warning("Layer_Shape::accelerated_render - Error in the byte stream, in sufficient data space for declared number of points"); - return false; - } - - //transfer all the data - for(curnum=0; curnum < number;) - { - switch(operation) - { - case Primitive::MOVE_TO: - { - x = data[curnum][0]; - 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; - } - else - { - 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; - 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); -} - -Rect -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)); - - return bounds; -}