1 /* === S I N F G =========================================================== */
2 /*! \file layer_shape.cpp
3 ** \brief Template Header
5 ** $Id: layer_shape.cpp,v 1.2 2005/01/24 03:08:18 darco Exp $
8 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
10 ** This software and associated documentation
11 ** are CONFIDENTIAL and PROPRIETARY property of
12 ** the above-mentioned copyright holder.
14 ** You may not copy, print, publish, or in any
15 ** other way distribute this software without
16 ** a prior written agreement with
17 ** the copyright holder.
20 /* ========================================================================= */
22 /* === H E A D E R S ======================================================= */
31 #include "layer_shape.h"
35 #include "paramdesc.h"
39 #include "valuenode.h"
43 #include "curve_helper.h"
51 /* === U S I N G =========================================================== */
53 using namespace sinfg;
57 /* === G L O B A L S ======================================================= */
59 SINFG_LAYER_INIT(Layer_Shape);
60 SINFG_LAYER_SET_NAME(Layer_Shape,"shape");
61 SINFG_LAYER_SET_LOCAL_NAME(Layer_Shape,_("Shape"));
62 SINFG_LAYER_SET_CATEGORY(Layer_Shape,_("Internal"));
63 SINFG_LAYER_SET_VERSION(Layer_Shape,"0.1");
64 SINFG_LAYER_SET_CVS_ID(Layer_Shape,"$Id: layer_shape.cpp,v 1.2 2005/01/24 03:08:18 darco Exp $");
69 inline bool IsZero(const T &n)
71 return (n < EPSILON) && (n > -EPSILON);
74 /* === C L A S S E S ======================================================= */
76 //Assumes 64 byte aligned structures if at all
87 MOVE_TO = 0, //(x,y)+ after first point treated as line_to
88 CLOSE, // NOT RUNLENGTH enabled
89 LINE_TO, //(x,y)+ continuous func
90 CONIC_TO, //(x1,y1,x,y)+ " "
91 CONIC_TO_SMOOTH, //(x,y)+ " "
92 CUBIC_TO, //(x1,y1,x2,y2,x,y)+ " "
93 CUBIC_TO_SMOOTH, //(x2,y2,x,y)+ " "
98 //******** CURVE FUNCTIONS *****************
99 const int MAX_SUBDIVISION_SIZE = 64;
100 const int MIN_SUBDIVISION_DRAW_LEVELS = 4;
102 static void Subd_Conic_Stack(Point *arc)
120 arc[4][0] = arc[2][0];
123 a = arc[1][0] = (arc[0][0] + b)/2;
124 b = arc[3][0] = (arc[4][0] + b)/2;
125 arc[2][0] = (a + b)/2;
128 arc[4][1] = arc[2][1];
131 a = arc[1][1] = (arc[0][1] + b)/2;
132 b = arc[3][1] = (arc[4][1] + b)/2;
133 arc[2][1] = (a + b)/2;
139 arc[3] = (arc[2] + arc[1])/2;
140 arc[1] = (arc[0] + arc[1])/2;
142 arc[2] = (arc[1] + arc[3])/2;
148 static void Subd_Cubic_Stack(Point *arc)
157 * 1+2 b * 0+3*1+3*2+3
163 0.1 2.3 -> 0.1 2 3 4 5.6
167 arc[6][0] = arc[3][0];
172 a = arc[1][0] = (arc[0][0] + b)/2;
174 c = arc[5][0] = (arc[6][0] + c)/2;
176 a = arc[2][0] = (a + b)/2;
177 b = arc[4][0] = (b + c)/2;
179 arc[3][0] = (a + b)/2;
182 arc[6][1] = arc[3][1];
187 a = arc[1][1] = (arc[0][1] + b)/2;
189 c = arc[5][1] = (arc[6][1] + c)/2;
191 a = arc[2][1] = (a + b)/2;
192 b = arc[4][1] = (b + c)/2;
194 arc[3][1] = (a + b)/2;
201 //backwards to avoid overwriting
202 arc[5] = (arc[2] + arc[3])/2;
203 temp = (arc[1] + arc[2])/2;
204 arc[1] = (arc[0] + arc[1])/2;
206 arc[4] = (temp + arc[5])/2;
207 arc[2] = (arc[1] + temp)/2;
209 arc[3] = (arc[2] + arc[4])/2;
214 //************** PARAMETRIC RENDERER SUPPORT STRUCTURES ****************
221 vector<Point> pointlist;
223 MonoSegment(int dir = 0, Real x0 = 0, Real x1 = 0, Real y0 = 0, Real y1 = 0)
233 int intersect(Real x,Real y) const
235 if((y < aabb.miny) | (y > aabb.maxy) | (x < aabb.minx)) return 0;
236 if(x > aabb.maxx) return ydir;
239 //int size = pointlist.size();
240 //vector<Point>::const_iterator end = pointlist.end();
241 vector<Point>::const_iterator p = pointlist.begin();
243 //assumes that the rect culled away anything that would be beyond the edges
246 while(y > (*++p)[1]);
250 while(y < (*++p)[1]);
253 //for the loop to break there must have been a slope (straight line would do nothing)
254 //vector<Point>::const_iterator p1 = p-1;
255 Real dy = p[-1][1] - p[0][1];
256 Real dx = p[-1][0] - p[0][0];
260 Real xi = p[0][0] + (y - p[0][1]) * dx / dy;
261 return (x > xi)*ydir;
267 Rect aabb; //not necessarily as effective - can only reject values
268 vector<Point> pointlist; //run length - p0, p1, p2, p3 = p10, p11, p12, p13 = p20 ...
269 vector<char> degrees;
271 CurveArray(Real x0 = 0, Real x1 = 0, Real y0 = 0, Real y1 = 0)
273 aabb.set(x0,y0,x1,y1);
276 void reset(Real x0 = 0, Real x1 = 0, Real y0 = 0, Real y1 = 0)
278 aabb.set(x0,y0,x1,y1);
285 return degrees.size();
290 reset(m[0],m[0],m[1],m[1]);
291 pointlist.push_back(m);
294 void AddCubic(Point p1, Point p2, Point dest)
296 aabb.expand(p1[0],p1[1]);
297 aabb.expand(p2[0],p2[1]);
298 aabb.expand(dest[0],dest[1]);
300 pointlist.push_back(p1);
301 pointlist.push_back(p2);
302 pointlist.push_back(dest);
304 degrees.push_back(3);
307 void AddConic(Point p1, Point dest)
309 aabb.expand(p1[0],p1[1]);
310 aabb.expand(dest[0],dest[1]);
312 pointlist.push_back(p1);
313 pointlist.push_back(dest);
315 degrees.push_back(2);
318 static int intersect_conic(Real x, Real y, Point *p, int level = 0)
320 Real ymin,ymax,xmin,xmax;
323 //sort the overall curve ys - degenerate detection
324 ymin = min(p[0][1],p[2][1]);
325 ymax = max(p[0][1],p[2][1]);
327 xmin = min(min(p[0][0],p[1][0]),p[2][0]);
328 xmax = max(max(p[0][0],p[1][0]),p[2][0]);
330 //to the left, to the right and out of range y, or completely out of range y
331 if( x < xmin ) return 0;
332 if( x > xmax & (y > ymax | y < ymin) ) return 0;
333 if( (y > ymax & y > p[1][1]) | (y < ymin & y < p[1][1]) ) return 0;
335 //degenerate line max
336 if(ymin == ymax == p[1][1])
339 //degenerate accept - to the right and crossing the base line
342 return (y <= ymax & y >= ymin);
345 //solve for curve = y
348 //0 roots - 0 intersection
349 //1 root - get x, and figure out x
350 //2 roots (non-double root) - get 2 xs, and count xs to the left
352 //for conic we can assume 1 intersection for monotonic curve
353 Real a = p[2][1] - 2*p[1][1] + p[0][1],
354 b = 2*p[1][1] - 2*p[0][1],
357 Real t1 = -1, t2 = -1;
362 if(b == 0) return 0; //may not need this check
364 t1 = - c / b; //bt + c = 0 solved
367 //2 degree polynomial
368 Real b2_4ac = b*b - 4*a*c;
370 //if there are double/no roots - no intersections (in real #s that is)
376 b2_4ac = sqrt(b2_4ac);
378 t1 = (-b - b2_4ac) / 2*a,
379 t2 = (-b + b2_4ac) / 2*a;
382 //calculate number of intersections
383 if(t1 >= 0 & t1 <= 1)
386 const Real invt = 1 - t;
388 //find x val and it counts if it's to the left of the point
389 const Real xi = invt*invt*p[0][0] + 2*t*invt*p[1][0] + t*t*p[2][0];
390 const Real dy_t = 2*a*t + b;
394 intersects += (x >= xi) * ( dy_t > 0 ? 1 : -1);
398 if(t2 >= 0 & t2 <= 1)
401 const Real invt = 1 - t;
403 //find x val and it counts if it's to the left of the point
404 const Real xi = invt*invt*p[0][0] + 2*t*invt*p[1][0] + t*t*p[2][0];
405 const Real dy_t = 2*a*t + b;
409 intersects += (x >= xi) * ( dy_t > 0 ? 1 : -1);
416 static int quadratic_eqn(Real a, Real b, Real c, Real *t0, Real *t1)
418 const Real b2_4ac = b*b - 4*a*c;
420 //degenerate reject (can't take sqrt)
426 const Real sqrtb2_4ac = sqrt(b2_4ac);
427 const Real signb = b < 0 ? -1 : 1;
428 const Real q = - 0.5 * (b + signb * sqrtb2_4ac);
433 return sqrtb2_4ac == 0 ? 1 : 2;
436 //Newton-Raphson root polishing (we don't care about bounds, assumes very near the desired root)
437 static Real polish_cubicroot(Real a, Real b, Real c, Real d, Real t, Real *dpdt)
439 const Real cn[4] = {a,b,c,d};
440 Real p,dp,newt,oldpmag=FLT_MAX;
442 //eval cubic eqn and it's derivative
448 for(int i = 2; i < 4; i++)
456 sinfg::warning("polish_cubicroot: Derivative should not vanish!!!");
462 if(newt == t || fabs(p) >= oldpmag)
473 static int intersect_cubic(Real x, Real y, Point *p, int level = 0)
475 const Real INVALIDROOT = -FLT_MAX;
476 Real ymin,ymax,xmin,xmax;
477 Real ymin2,ymax2,ymintot,ymaxtot;
480 //sort the overall curve ys and xs - degenerate detection
482 //open span for the two end points
483 ymin = min(p[0][1],p[3][1]);
484 ymax = max(p[0][1],p[3][1]);
487 ymin2 = min(p[1][1],p[2][1]);
488 ymax2 = max(p[1][1],p[2][1]);
490 ymintot = min(ymin,ymin2);
491 ymaxtot = max(ymax,ymax2);
493 //the entire curve control polygon is in this x range
494 xmin = min(min(p[0][0],p[1][0]),min(p[2][0],p[3][0]));
495 xmax = max(max(p[0][0],p[1][0]),max(p[2][0],p[3][0]));
497 //outside all y boundaries (no intersect)
498 if( (y > ymaxtot) || (y < ymintot) ) return 0;
500 //left of curve (no intersect)
501 if(x < xmin) return 0;
503 //right of curve (and outside base range)
506 if( (y > ymax) | (y < ymin) ) return 0;
508 //degenerate accept - to the right and inside the [ymin,ymax] range (already rejected if out of range)
509 const Real n = p[3][1] - p[0][1];
511 //extract the sign from the value (we need valid data)
512 return n < 0 ? -1 : 1;
515 //degenerate horizontal line max -- doesn't happen enough to check for
516 if( ymintot == ymaxtot ) return 0;
519 // can have 0,1,2, or 3 real roots
520 // if any of them are double then reject the two...
522 // y-coefficients for f_y(t) - y = 0
523 Real a = p[3][1] - 3*p[2][1] + 3*p[1][1] - p[0][1],
524 b = 3*p[2][1] - 6*p[1][1] + 3*p[0][1],
525 c = 3*p[1][1] - 3*p[0][1],
528 Real ax = p[3][0] - 3*p[2][0] + 3*p[1][0] - p[0][0],
529 bx = 3*p[2][0] - 6*p[1][0] + 3*p[0][0],
530 cx = 3*p[1][0] - 3*p[0][0],
533 Real t1 = INVALIDROOT, t2 = INVALIDROOT, t3 = INVALIDROOT, t, dydt;
543 t1 = - d / c; //equation devolved into: ct + d = 0 - solve...
546 //0 roots = 0 intersections, 1 root = 2 intersections at the same place (0 effective)
547 if(quadratic_eqn(a,b,c,&t1,&t2) != 2) return 0;
553 //algorithm courtesy of Numerical Recipes in C (algorithm copied from pg. 184/185)
558 //if cn is 0 (or really really close), then we can simplify this...
563 //0 roots = 0 intersections, 1 root = 2 intersections at the same place (0 effective)
564 if(quadratic_eqn(a,b,c,&t1,&t2) != 2)
566 t1 = t2 = INVALIDROOT;
571 //otherwise run the normal cubic root equation
572 Real Q = (an*an - 3.0*bn) / 9.0;
573 Real R = ((2.0*an*an - 9.0*bn)*an + 27.0*cn)/54.0;
577 Real theta = acos(R / sqrt(Q*Q*Q));
579 t1 = -2.0*sqrt(Q)*cos(theta/3) - an/3.0;
580 t2 = -2.0*sqrt(Q)*cos((theta+2*PI)/3.0) - an/3.0;
581 t3 = -2.0*sqrt(Q)*cos((theta-2*PI)/3.0) - an/3.0;
583 //don't need to reorder,l just need to eliminate double/triple roots
584 //if(t3 == t2 & t1 == t2) t2 = t3 = INVALIDROOT;
585 if(t3 == t2) t2 = t3 = INVALIDROOT;
586 if(t1 == t2) t1 = t2 = INVALIDROOT;
587 if(t1 == t3) t1 = t3 = INVALIDROOT;
590 Real signR = R < 0 ? -1 : 1;
591 Real A = - signR * pow(signR*R + sqrt(R*R - Q*Q*Q),1/3.0);
597 //single real root in this case
598 t1 = (A + B) - an/3.0;
603 //if(t1 != INVALIDROOT)
605 t = t1;//polish_cubicroot(a,b,c,d,t1,&dydt);
608 //const Real invt = 1 - t;
610 //find x val and it counts if it's to the left of the point
611 const Real xi = ((ax*t + bx)*t + cx)*t + dx;
612 dydt = (3*a*t + 2*b)*t + c;
616 intersects += (x >= xi) * ( dydt > 0 ? 1 : -1);
621 //if(t2 != INVALIDROOT)
623 t = t2;//polish_cubicroot(a,b,c,d,t2,&dydt);
626 //const Real invt = 1 - t;
628 //find x val and it counts if it's to the left of the point
629 const Real xi = ((ax*t + bx)*t + cx)*t + dx;
630 dydt = (3*a*t + 2*b)*t + c;
634 intersects += (x >= xi) * ( dydt > 0 ? 1 : -1);
639 //if(t3 != INVALIDROOT)
641 t = t3;//polish_cubicroot(a,b,c,d,t3,&dydt);
644 //const Real invt = 1 - t;
646 //find x val and it counts if it's to the left of the point
647 const Real xi = ((ax*t + bx)*t + cx)*t + dx;
648 dydt = (3*a*t + 2*b)*t + c;
652 intersects += (x >= xi) * ( dydt > 0 ? 1 : -1);
660 int intersect(Real x,Real y, Point *table) const
662 if((y < aabb.miny) | (y > aabb.maxy) | (x < aabb.minx)) return 0;
664 int i, curdeg, intersects = 0;
665 const int numcurves = degrees.size();
667 vector<Point>::const_iterator p = pointlist.begin();
669 for(i=0; i < numcurves; i++)
679 table[2] = *p; //we want to include the last point for the next curve
681 intersects += intersect_conic(x,y,table);
691 table[3] = *p; //we want to include the last point for the next curve
693 intersects += intersect_cubic(x,y,table);
700 warning("Invalid degree (%d) inserted into the list (index: %d)\n", curdeg, i);
710 struct Layer_Shape::Intersector
717 enum IntersectorFlags
730 Real close_x,close_y;
732 vector<MonoSegment> segs; //monotonically increasing
733 vector<CurveArray> curves; //big array of consecutive curves
745 return (flags & NotClosed) | (cur_x != close_x) | (cur_y != close_y);
748 void move_to(Real x, Real y)
755 tangent[0] = tangent[1] = 0;
761 }else aabb.expand(x,y);
766 void line_to(Real x, Real y)
768 int dir = (y > cur_y)*1 + (-1)*(y < cur_y);
770 //check for context (if not line start a new segment)
771 //if we're not in line mode (cover's 0 set case), or if directions are different (not valid for 0 direction)
772 if(prim != TYPE_LINE || (dir && segs.back().ydir != dir))
774 MonoSegment seg(dir,x,x,y,y);
776 seg.aabb.expand(cur_x,cur_y);
777 seg.pointlist.push_back(Point(cur_x,cur_y));
778 seg.pointlist.push_back(Point(x,y));
781 //add to the last segment, because it works
784 segs.back().pointlist.push_back(Point(x,y));
785 segs.back().aabb.expand(x,y);
792 aabb.expand(x,y); //expand the entire things bounding box
794 tangent[0] = x - cur_x;
795 tangent[1] = x - cur_y;
801 void conic_to_smooth(Real x, Real y)
803 const Real x1 = tangent[0]/2.0 + cur_x;
804 const Real y1 = tangent[1]/2.0 + cur_y;
809 void conic_to(Real x1, Real y1, Real x, Real y)
811 //if we're not already a curve start one
812 if(prim != TYPE_CURVE)
816 c.Start(Point(cur_x,cur_y));
817 c.AddConic(Point(x1,y1),Point(x,y));
822 curves.back().AddConic(Point(x1,y1),Point(x,y));
831 tangent[0] = 2*(x - x1);
832 tangent[1] = 2*(y - y1);
838 void curve_to_smooth(Real x2, Real y2, Real x, Real y)
840 Real x1 = tangent[0]/3.0 + cur_x;
841 Real y1 = tangent[1]/3.0 + cur_y;
843 curve_to(x1,y1,x2,y2,x,y);
846 void curve_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y)
848 //if we're not already a curve start one
849 if(prim != TYPE_CURVE)
853 c.Start(Point(cur_x,cur_y));
854 c.AddCubic(Point(x1,y1),Point(x2,y2),Point(x,y));
859 curves.back().AddCubic(Point(x1,y1),Point(x2,y2),Point(x,y));
865 //expand bounding box around ALL of it
870 tangent[0] = 3*(x - x2);
871 tangent[1] = 3*(y - y2);
879 if(flags & NotClosed)
881 if(cur_x != close_x || cur_y != close_y)
883 line_to(close_x,close_y);
890 //assumes the line to count the intersections with is (-1,0)
891 int intersect (Real x, Real y) const
895 vector<MonoSegment>::const_iterator s = segs.begin();
896 vector<CurveArray>::const_iterator c = curves.begin();
898 Point memory[3*MAX_SUBDIVISION_SIZE + 1];
900 for(i = 0; i < segs.size(); i++,s++)
902 inter += s->intersect(x,y);
905 for(i=0; i < curves.size(); i++,c++)
906 inter += c->intersect(x,y,memory);
911 //intersect an arbitrary line
912 //int intersect (Real x, Real y, Real vx, Real vy) {return 0;}
920 cur_x = cur_y = close_x = close_y = 0;
922 tangent[0] = tangent[1] = 0;
927 //*********** SCANLINE RENDERER SUPPORT STRUCTURES ***************
934 PenMark(int xin, int yin, Real c, Real a)
935 :y(yin),x(xin),cover(c),area(a) {}
937 void set(int xin, int yin, Real c, Real a) { y = yin; x = xin; cover = c; area = a; }
939 void setcoord(int xin, int yin) { y = yin; x = xin; }
941 void setcover(Real c, Real a) { cover = c; area = a; }
942 void addcover(Real c, Real a) { cover += c; area += a; }
944 bool operator < (const PenMark &rhs) const
946 return y == rhs.y ? x < rhs.x : y < rhs.y;
950 typedef rect<int> ContextRect;
952 class Layer_Shape::PolySpan
955 typedef deque<PenMark> cover_array;
957 Point arc[3*MAX_SUBDIVISION_SIZE + 1];
964 //ending position of last primitive
968 //starting position of current primitive list
972 //flags for the current segment
975 //the window that will be drawn (used for clipping)
978 //for assignment to flags value
985 //default constructor - 0 everything
986 PolySpan() :current(0,0,0,0),flags(NotSorted)
988 cur_x = cur_y = close_x = close_y = 0;
992 bool notclosed() const
994 return (flags & NotClosed) | (cur_x != close_x) | (cur_y != close_y);
997 //0 out all the variables involved in processing
1001 cur_x = cur_y = close_x = close_y = 0;
1003 current.set(0,0,0,0);
1007 //add the current cell, but only if there is information to add
1010 if(current.cover || current.area)
1012 covers.push_back(current);
1016 //move to the next cell (cover values 0 initially), keeping the current if necessary
1017 void move_pen(int x, int y)
1019 if(y != current.y | x != current.x)
1022 current.set(x,y,0,0);
1026 //close the primitives with a line (or rendering will not work as expected)
1029 if(flags & NotClosed)
1031 if(cur_x != close_x || cur_y != close_y)
1033 line_to(close_x,close_y);
1035 current.setcover(0,0);
1037 flags &= ~NotClosed;
1041 // Not recommended - destroys any separation of spans currently held
1044 sort(covers.begin(),covers.end());
1048 //will sort the marks if they are not sorted
1051 if(flags & NotSorted)
1053 //only sort the open index
1055 current.setcover(0,0);
1057 sort(covers.begin() + open_index,covers.end());
1058 flags &= ~NotSorted;
1062 //encapsulate the current sublist of marks (used for drawing)
1063 void encapsulate_current()
1065 //sort the current list then reposition the open list section
1067 open_index = covers.size();
1070 //move to start a new primitive list (enclose the last primitive if need be)
1071 void move_to(Real x, Real y)
1076 move_pen((int)floor(x),(int)floor(y));
1077 close_y = cur_y = y;
1078 close_x = cur_x = x;
1081 //primitive_to functions
1082 void line_to(Real x, Real y);
1083 void conic_to(Real x1, Real y1, Real x, Real y);
1084 void cubic_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y);
1086 void draw_scanline(int y, Real x1, Real y1, Real x2, Real y2);
1087 void draw_line(Real x1, Real y1, Real x2, Real y2);
1089 Real ExtractAlpha(Real area)
1091 //non-zero winding style
1092 if(area < 0) area = -area;
1093 if(area > 1) area = 1;
1095 //even-odd winding style
1096 /*if(area < 0) area = -area;
1098 while(area > 2) area -= 2;
1099 if(area > 1) area = 1-area; //want pyramid like thing
1101 //broken? - yep broken
1107 /* === M E T H O D S ======================================================= */
1109 Layer_Shape::Layer_Shape(const Real &a, const Color::BlendMethod m):
1110 Layer_Composite (a,m),
1111 edge_table (new Intersector),
1112 color (Color::black()),
1116 blurtype (Blur::FASTGAUSSIAN),
1119 lastbyteop (Primitive::NONE),
1124 Layer_Shape::~Layer_Shape()
1130 Layer_Shape::clear()
1132 edge_table->clear();
1137 Layer_Shape::set_param(const String & param, const ValueBase &value)
1146 return Layer_Composite::set_param(param,value);
1150 Layer_Shape::get_param(const String ¶m)const
1162 return Layer_Composite::get_param(param);
1166 Layer_Shape::get_param_vocab()const
1168 Layer::Vocab ret(Layer_Composite::get_param_vocab());
1170 ret.push_back(ParamDesc("color")
1171 .set_local_name(_("Color"))
1172 .set_description(_("Layer_Shape Color"))
1174 ret.push_back(ParamDesc("offset")
1175 .set_local_name(_("Position"))
1177 ret.push_back(ParamDesc("invert")
1178 .set_local_name(_("Invert"))
1180 ret.push_back(ParamDesc("antialias")
1181 .set_local_name(_("Antialiasing"))
1183 ret.push_back(ParamDesc("feather")
1184 .set_local_name(_("Feather"))
1187 ret.push_back(ParamDesc("blurtype")
1188 .set_local_name(_("Type of Feather"))
1189 .set_description(_("Type of feathering to use"))
1191 .add_enum_value(Blur::BOX,"box",_("Box Blur"))
1192 .add_enum_value(Blur::FASTGAUSSIAN,"fastgaussian",_("Fast Gaussian Blur"))
1193 .add_enum_value(Blur::CROSS,"cross",_("Cross-Hatch Blur"))
1194 .add_enum_value(Blur::GAUSSIAN,"gaussian",_("Gaussian Blur"))
1195 .add_enum_value(Blur::DISC,"disc",_("Disc Blur"))
1201 sinfg::Layer::Handle
1202 Layer_Shape::hit_check(sinfg::Context context, const sinfg::Point &p)const
1204 Point pos(p-offset);
1206 int intercepts = edge_table->intersect(pos[0],pos[1]);
1208 // If we have an odd number of intercepts, we are inside.
1209 // If we have an even number of intercepts, we are outside.
1210 bool intersect = ((!!intercepts) ^ invert);
1212 if(get_amount() == 0 || get_blend_method() == Color::BLEND_ALPHA_OVER)
1219 sinfg::Layer::Handle tmp;
1220 if(get_blend_method()==Color::BLEND_BEHIND && (tmp=context.hit_check(p)))
1222 if(Color::is_onto(get_blend_method()))
1224 //if there's something in the lower layer then we're set...
1225 if(!context.hit_check(p).empty())
1226 return const_cast<Layer_Shape*>(this);
1227 }else if(get_blend_method() == Color::BLEND_ALPHA_OVER)
1229 sinfg::info("layer_shape::hit_check - we've got alphaover");
1230 //if there's something in the lower layer then we're set...
1231 if(color.get_a() < 0.1 && get_amount() > .9)
1233 sinfg::info("layer_shape::hit_check - can see through us... so nothing");
1235 }else return context.hit_check(p);
1237 return const_cast<Layer_Shape*>(this);
1240 return context.hit_check(p);
1244 Layer_Shape::get_color(Context context, const Point &p)const
1249 pp = Blur(feather,feather,blurtype)(p);
1251 Point pos(pp-offset);
1253 int intercepts = edge_table->intersect(pos[0],pos[1]);
1255 // If we have an odd number of intercepts, we are inside.
1256 // If we have an even number of intercepts, we are outside.
1257 bool intersect = ((!!intercepts) ^ invert);
1260 return context.get_color(pp);
1262 //Ok, we're inside... bummmm ba bum buM...
1263 if(get_blend_method() == Color::BLEND_STRAIGHT && get_amount() == 1)
1266 return Color::blend(color,context.get_color(p),get_amount(),get_blend_method());
1269 //************** SCANLINE RENDERING *********************
1270 void Layer_Shape::PolySpan::line_to(Real x, Real y)
1273 bool afterx = false;
1275 const Real xin(x), yin(y);
1277 Real dx = x - cur_x;
1278 Real dy = y - cur_y;
1282 //outside y - ignore entirely
1283 if( (cur_y >= window.maxy & y >= window.maxy)
1284 |(cur_y < window.miny & y < window.miny) )
1289 else //not degenerate - more complicated
1291 if(dy > 0) //be sure it's not tooooo small
1293 // cur_y ... window.miny ... window.maxy ... y
1295 //initial degenerate - initial clip
1296 if(cur_y < window.miny)
1298 //new clipped start point (must also move pen)
1299 n[2] = cur_x + (window.miny - cur_y) * dx / dy;
1302 cur_y = window.miny;
1303 move_pen((int)floor(cur_x),window.miny);
1306 //generate data for the ending clipped info
1309 //intial line to intersection (and degenerate)
1310 n[2] = x + (window.maxy - y) * dx / dy;
1319 //initial degenerate - initial clip
1320 if(cur_y > window.maxy)
1322 //new clipped start point (must also move pen)
1323 n[2] = cur_x + (window.maxy - cur_y) * dx / dy;
1326 cur_y = window.maxy;
1327 move_pen((int)floor(cur_x),window.maxy);
1330 //generate data for the ending clipped info
1333 //intial line to intersection (and degenerate)
1334 n[2] = x + (window.miny - y) * dx / dy;
1342 //all degenerate - but require bounded clipped values
1343 if( (cur_x >= window.maxx && x >= window.maxx)
1344 ||(cur_x < window.minx && x < window.minx) )
1346 //clip both vertices - but only needed in the x direction
1347 cur_x = max(cur_x, (Real)window.minx);
1348 cur_x = min(cur_x, (Real)window.maxx);
1350 //clip the dest values - y is already clipped
1351 x = max(x,(Real)window.minx);
1352 x = min(x,(Real)window.maxx);
1354 //must start at new point...
1355 move_pen((int)floor(cur_x),(int)floor(cur_y));
1357 draw_line(cur_x,cur_y,x,y);
1367 //initial degenerate - initial clip
1368 if(cur_x < window.minx)
1370 //need to draw an initial segment from clippedx,cur_y to clippedx,intersecty
1371 n[2] = cur_y + (window.minx - cur_x) * dy / dx;
1373 move_pen(window.minx,(int)floor(cur_y));
1374 draw_line(window.minx,cur_y,window.minx,n[2]);
1376 cur_x = window.minx;
1380 //generate data for the ending clipped info
1383 //intial line to intersection (and degenerate)
1384 n[2] = y + (window.maxx - x) * dy / dx;
1396 //initial degenerate - initial clip
1397 if(cur_x > window.maxx)
1399 //need to draw an initial segment from clippedx,cur_y to clippedx,intersecty
1400 n[2] = cur_y + (window.maxx - cur_x) * dy / dx;
1402 move_pen(window.maxx,(int)floor(cur_y));
1403 draw_line(window.maxx,cur_y,window.maxx,n[2]);
1405 cur_x = window.maxx;
1409 //generate data for the ending clipped info
1412 //intial line to intersection (and degenerate)
1413 n[2] = y + (window.minx - x) * dy / dx;
1425 move_pen((int)floor(cur_x),(int)floor(cur_y));
1426 //draw the relevant line (clipped)
1427 draw_line(cur_x,cur_y,x,y);
1431 draw_line(x,y,n[0],n[1]);
1438 } catch(...) { sinfg::error("line_to: cur_x=%f, cur_y=%f, x=%f, y=%f", cur_x, cur_y, x, y); throw; }
1440 flags |= NotClosed|NotSorted;
1443 static inline bool clip_conic(const Point *const p, const ContextRect &r)
1445 const Real minx = min(min(p[0][0],p[1][0]),p[2][0]);
1446 const Real miny = min(min(p[0][1],p[1][1]),p[2][1]);
1447 const Real maxx = max(max(p[0][0],p[1][0]),p[2][0]);
1448 const Real maxy = max(max(p[0][1],p[1][1]),p[2][1]);
1450 return (minx > r.maxx) |
1456 static inline bool clip_cubic(const Point *const p, const ContextRect &r)
1458 /*const Real minx = min(min(p[0][0],p[1][0]),min(p[2][0],p[3][0]));
1459 const Real miny = min(min(p[0][1],p[1][1]),min(p[2][1],p[3][1]));
1460 const Real maxx = max(max(p[0][0],p[1][0]),max(p[2][0],p[3][1]));
1461 const Real maxy = max(max(p[0][1],p[1][1]),max(p[2][1],p[3][1]));
1463 return (minx > r.maxx) ||
1468 return ((p[0][0] > r.maxx) & (p[1][0] > r.maxx) & (p[2][0] > r.maxx) & (p[3][0] > r.maxx)) |
1469 ((p[0][0] < r.minx) & (p[1][0] < r.minx) & (p[2][0] < r.minx) & (p[3][0] < r.minx)) |
1470 ((p[0][1] > r.maxy) & (p[1][1] > r.maxy) & (p[2][1] > r.maxy) & (p[3][1] > r.maxy)) |
1471 ((p[0][1] < r.miny) & (p[1][1] < r.miny) & (p[2][1] < r.miny) & (p[3][1] < r.miny));
1474 static inline Real max_edges_cubic(const Point *const p)
1476 const Real x1 = p[1][0] - p[0][0];
1477 const Real y1 = p[1][1] - p[0][1];
1479 const Real x2 = p[2][0] - p[1][0];
1480 const Real y2 = p[2][1] - p[1][1];
1482 const Real x3 = p[3][0] - p[2][0];
1483 const Real y3 = p[3][1] - p[2][1];
1485 const Real d1 = x1*x1 + y1*y1;
1486 const Real d2 = x2*x2 + y2*y2;
1487 const Real d3 = x3*x3 + y3*y3;
1489 return max(max(d1,d2),d3);
1492 static inline Real max_edges_conic(const Point *const p)
1494 const Real x1 = p[1][0] - p[0][0];
1495 const Real y1 = p[1][1] - p[0][1];
1497 const Real x2 = p[2][0] - p[1][0];
1498 const Real y2 = p[2][1] - p[1][1];
1500 const Real d1 = x1*x1 + y1*y1;
1501 const Real d2 = x2*x2 + y2*y2;
1506 void Layer_Shape::PolySpan::conic_to(Real x1, Real y1, Real x, Real y)
1508 Point *current = arc;
1511 bool onsecond = false;
1513 arc[0] = Point(x,y);
1514 arc[1] = Point(x1,y1);
1515 arc[2] = Point(cur_x,cur_y);
1517 //just draw the line if it's outside
1518 if(clip_conic(arc,window))
1524 //Ok so it's not super degenerate, subdivide and draw (run through minimum subdivision levels first)
1525 while(current >= arc)
1527 if(num >= MAX_SUBDIVISION_SIZE)
1529 warning("Curve subdivision somehow ran out of space while tesselating!");
1535 //if the curve is clipping then draw degenerate
1536 if(clip_conic(current,window))
1538 line_to(current[0][0],current[0][1]); //backwards so front is destination
1540 if(onsecond) level--;
1545 //if we are not at the level minimum
1546 if(level < MIN_SUBDIVISION_DRAW_LEVELS)
1548 Subd_Conic_Stack(current);
1549 current += 2; //cursor on second curve
1555 //split it again, if it's too big
1556 if(max_edges_conic(current) > 0.25) //distance of .5 (cover no more than half the pixel)
1558 Subd_Conic_Stack(current);
1559 current += 2; //cursor on second curve
1564 else //NOT TOO BIG? RENDER!!!
1566 //cur_x,cur_y = current[2], so we need to go 1,0
1567 line_to(current[1][0],current[1][1]);
1568 line_to(current[0][0],current[0][1]);
1571 if(onsecond) level--;
1578 void Layer_Shape::PolySpan::cubic_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y)
1580 Point *current = arc;
1583 bool onsecond = false;
1585 arc[0] = Point(x,y);
1586 arc[1] = Point(x2,y2);
1587 arc[2] = Point(x1,y1);
1588 arc[3] = Point(cur_x,cur_y);
1590 //just draw the line if it's outside
1591 if(clip_cubic(arc,window))
1597 //Ok so it's not super degenerate, subdivide and draw (run through minimum subdivision levels first)
1598 while(current >= arc) //once current goes below arc, there are no more curves left
1600 if(num >= MAX_SUBDIVISION_SIZE)
1602 warning("Curve subdivision somehow ran out of space while tesselating!");
1609 //if we are not at the level minimum
1610 if(level < MIN_SUBDIVISION_DRAW_LEVELS)
1612 Subd_Cubic_Stack(current);
1613 current += 3; //cursor on second curve
1619 //if the curve is clipping then draw degenerate
1620 if(clip_cubic(current,window))
1622 line_to(current[0][0],current[0][1]); //backwards so front is destination
1624 if(onsecond) level--;
1630 //split it again, if it's too big
1631 if(max_edges_cubic(current) > 0.25) //could use max_edges<3>
1633 Subd_Cubic_Stack(current);
1634 current += 3; //cursor on second curve
1639 else //NOT TOO BIG? RENDER!!!
1641 //cur_x,cur_y = current[3], so we need to go 2,1,0
1642 line_to(current[2][0],current[2][1]);
1643 line_to(current[1][0],current[1][1]);
1644 line_to(current[0][0],current[0][1]);
1647 if(onsecond) level--;
1654 //******************** LINE ALGORITHMS ****************************
1655 // THESE CALCULATE THE AREA AND THE COVER FOR THE MARKS, TO THEN SCAN CONVERT
1656 // - BROKEN UP INTO SCANLINES (draw_line - y intersections),
1657 // THEN THE COVER AND AREA PER TOUCHED PIXEL IS CALCULATED (draw_scanline - x intersections)
1658 void Layer_Shape::PolySpan::draw_scanline(int y, Real x1, Real fy1, Real x2, Real fy2)
1660 int ix1 = (int)floor(x1);
1661 int ix2 = (int)floor(x2);
1662 Real fx1 = x1 - ix1;
1663 Real fx2 = x2 - ix2;
1665 Real dx,dy,dydx,mult;
1670 //case horizontal line
1673 move_pen(ix2,y); //pen needs to be at the last coord
1677 //case all in same pixel
1678 if(ix1 == ix2) //impossible for degenerate case (covered by the previous cases)
1680 current.addcover(dy,(fx1 + fx2)*dy/2); //horizontal trapazoid area
1686 // ----> fx1...1 0...1 ... 0...1 0...fx2
1689 //set initial values
1690 //Iterate through the covered pixels
1691 mult = (1 - fx1)*dydx; //next y intersection diff value (at 1)
1694 current.addcover(mult,(1 + fx1)*mult/2); // fx1,fy1,1,fy@1 - starting trapazoidal area
1696 //move to the next pixel
1702 //set up for whole ones
1705 //trapezoid(0,y1,1,y1+dydx);
1706 current.addcover(dydx,dydx/2); //accumulated area 1/2 the cover
1708 //move to next pixel (+1)
1715 //final y-pos - last intersect pos
1717 current.addcover(mult,(0+fx2)*mult/2);
1720 // fx2...1 0...1 ... 0...1 0...fx1 <----
1721 //mult = (0 - fx1) * dy / dx;
1722 //neg sign sucked into dydx
1725 //set initial values
1726 //Iterate through the covered pixels
1727 mult = fx1*dydx; //next y intersection diff value
1730 current.addcover(mult,fx1*mult/2); // fx1,fy1,0,fy@0 - starting trapazoidal area
1732 //move to next pixel
1738 //set up for whole ones
1741 //trapezoid(0,y1,1,y1+dydx);
1742 current.addcover(dydx,dydx/2); //accumulated area 1/2 the cover
1744 //move to next pixel (-1)
1751 mult = fy2 - fy1; //final y-pos - last intersect pos
1753 current.addcover(mult,(fx2+1)*mult/2);
1757 void Layer_Shape::PolySpan::draw_line(Real x1, Real y1, Real x2, Real y2)
1759 int iy1 = (int)floor(y1);
1760 int iy2 = (int)floor(y2);
1761 Real fy1 = y1 - iy1;
1762 Real fy2 = y2 - iy2;
1764 assert(!isnan(fy1));
1765 assert(!isnan(fy2));
1767 Real dx,dy,dxdy,mult,x_from,x_to;
1769 const Real SLOPE_EPSILON = 1e-10;
1771 //case all one scanline
1774 draw_scanline(iy1,x1,y1,x2,y2);
1782 //case vertical line
1783 if(dx < SLOPE_EPSILON && dx > -SLOPE_EPSILON)
1785 //calc area and cover on vertical line
1788 // ----> fx1...1 0...1 ... 0...1 0...fx2
1791 int ix1 = (int)floor(x1);
1792 Real fx1 = x1 - ix1;
1797 current.addcover(sub,fx1*sub);
1802 //move pen to next pixel
1808 current.addcover(1,fx1);
1816 current.addcover(fy2,fy2*fx1);
1821 int ix1 = (int)floor(x1);
1822 Real fx1 = x1 - ix1;
1827 current.addcover(sub,fx1*sub);
1836 //accumulate in current pixel
1837 current.addcover(-1,-fx1);
1844 current.addcover(fy2-1,(fy2-1)*fx1);
1849 //case normal line - guaranteed dx != 0 && dy != 0
1851 //calculate the initial intersection with "next" scanline
1856 mult = (1 - fy1) * dxdy;
1858 //x interset scanline
1860 draw_scanline(iy1,x1,fy1,x_from,1);
1865 move_pen((int)floor(x_from),iy1);
1869 //keep up on the x axis, and render the current scanline
1870 x_to = x_from + dxdy;
1871 draw_scanline(iy1,x_from,0,x_to,1);
1874 //move to next pixel
1876 move_pen((int)floor(x_from),iy1);
1879 //draw the last one, fractional
1880 draw_scanline(iy2,x_from,0,x2,fy2);
1888 //x interset scanline
1890 draw_scanline(iy1,x1,fy1,x_from,0);
1895 move_pen((int)floor(x_from),iy1);
1899 x_to = x_from + dxdy;
1900 draw_scanline(iy1,x_from,1,x_to,0);
1904 move_pen((int)floor(x_from),iy1);
1906 //draw the last one, fractional
1907 draw_scanline(iy2,x_from,1,x2,fy2);
1911 //****** LAYER PEN OPERATIONS (move_to, line_to, etc.) ******
1912 void Layer_Shape::move_to(Real x, Real y)
1914 //const int sizeblock = sizeof(Primitive)+sizeof(Point);
1918 op.operation = Primitive::MOVE_TO;
1919 op.number = 1; //one point for now
1921 if(lastbyteop == Primitive::MOVE_TO)
1923 char *ptr = &bytestream[lastoppos];
1924 memcpy(ptr,&op,sizeof(op));
1925 memcpy(ptr+sizeof(op),&p,sizeof(p));
1927 else //make a new op
1929 lastbyteop = Primitive::MOVE_TO;
1930 lastoppos = bytestream.size();
1932 bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header
1933 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data
1936 edge_table->move_to(x,y);
1939 void Layer_Shape::close()
1943 op.operation = Primitive::CLOSE;
1946 if(lastbyteop == Primitive::CLOSE)
1950 lastbyteop = Primitive::CLOSE;
1951 lastoppos = bytestream.size();
1953 bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert header
1956 edge_table->close();
1957 //should not affect the bounding box since it would just be returning to old point...
1960 void Layer_Shape::endpath()
1964 op.operation = Primitive::END;
1967 if(lastbyteop == Primitive::END | lastbyteop == Primitive::NONE)
1971 bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1));
1973 //should not affect the bounding box since it would just be returning to old point... if at all
1976 void Layer_Shape::line_to(Real x, Real y)
1981 //const int sizeblock = sizeof(Primitive)+sizeof(Point);
1985 op.operation = Primitive::LINE_TO;
1986 op.number = 1; //one point for now
1988 if(lastbyteop == Primitive::MOVE_TO | lastbyteop == Primitive::LINE_TO)
1990 //only need to insert the point
1991 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1));
1993 Primitive * prim = (Primitive *)&bytestream[lastoppos];
1994 prim->number++; //increment number of points in the list
1997 lastbyteop = Primitive::LINE_TO;
1998 lastoppos = bytestream.size();
2000 bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header
2001 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data
2004 edge_table->line_to(x,y);
2007 void Layer_Shape::conic_to(Real x1, Real y1, Real x, Real y)
2009 //const int sizeblock = sizeof(Primitive)+sizeof(Point)*2;
2014 op.operation = Primitive::CONIC_TO;
2015 op.number = 2; //2 points for now
2017 if(lastbyteop == Primitive::CONIC_TO)
2019 //only need to insert the new points
2020 bytestream.insert(bytestream.end(),(char*)&p1,(char*)(&p1+1));
2021 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1));
2023 Primitive * prim = (Primitive *)&bytestream[lastoppos];
2024 prim->number += 2; //increment number of points in the list
2027 lastbyteop = Primitive::CONIC_TO;
2028 lastoppos = bytestream.size();
2030 bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header
2031 bytestream.insert(bytestream.end(),(char*)&p1,(char*)(&p1+1)); //insert the bytes for data
2032 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data
2035 edge_table->conic_to(x1,y1,x,y);
2038 void Layer_Shape::conic_to_smooth(Real x, Real y) //x1,y1 derived from current tangent
2040 //const int sizeblock = sizeof(Primitive)+sizeof(Point);
2044 op.operation = Primitive::CONIC_TO_SMOOTH;
2045 op.number = 1; //2 points for now
2047 if(lastbyteop == Primitive::CONIC_TO_SMOOTH)
2049 //only need to insert the new point
2050 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1));
2052 Primitive * prim = (Primitive *)&bytestream[lastoppos];
2053 prim->number += 1; //increment number of points in the list
2056 lastbyteop = Primitive::CONIC_TO_SMOOTH;
2057 lastoppos = bytestream.size();
2059 bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header
2060 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data
2063 edge_table->conic_to_smooth(x,y);
2066 void Layer_Shape::curve_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y)
2068 //const int sizeblock = sizeof(Primitive)+sizeof(Point)*3;
2074 op.operation = Primitive::CUBIC_TO;
2075 op.number = 3; //3 points for now
2077 if(lastbyteop == Primitive::CUBIC_TO)
2079 //only need to insert the new points
2080 bytestream.insert(bytestream.end(),(char*)&p1,(char*)(&p1+1));
2081 bytestream.insert(bytestream.end(),(char*)&p2,(char*)(&p2+1));
2082 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1));
2084 Primitive * prim = (Primitive *)&bytestream[lastoppos];
2085 prim->number += 3; //increment number of points in the list
2088 lastbyteop = Primitive::CUBIC_TO;
2089 lastoppos = bytestream.size();
2091 bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header
2092 bytestream.insert(bytestream.end(),(char*)&p1,(char*)(&p1+1)); //insert the bytes for data
2093 bytestream.insert(bytestream.end(),(char*)&p2,(char*)(&p2+1)); //insert the bytes for data
2094 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data
2097 edge_table->curve_to(x1,y1,x2,y2,x,y);
2100 void Layer_Shape::curve_to_smooth(Real x2, Real y2, Real x, Real y) //x1,y1 derived from current tangent
2102 //const int sizeblock = sizeof(Primitive)+sizeof(Point)*3;
2107 op.operation = Primitive::CUBIC_TO_SMOOTH;
2108 op.number = 2; //3 points for now
2110 if(lastbyteop == Primitive::CUBIC_TO_SMOOTH)
2112 //only need to insert the new points
2113 bytestream.insert(bytestream.end(),(char*)&p2,(char*)(&p2+1));
2114 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1));
2116 Primitive * prim = (Primitive *)&bytestream[lastoppos];
2117 prim->number += 2; //increment number of points in the list
2120 lastbyteop = Primitive::CUBIC_TO_SMOOTH;
2121 lastoppos = bytestream.size();
2123 bytestream.insert(bytestream.end(),(char*)&op,(char*)(&op+1)); //insert the bytes for the header
2124 bytestream.insert(bytestream.end(),(char*)&p2,(char*)(&p2+1)); //insert the bytes for data
2125 bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1)); //insert the bytes for data
2129 // ACCELERATED RENDER FUNCTION - TRANSLATE BYTE CODE INTO FUNCTION CALLS
2131 bool Layer_Shape::render_polyspan(Surface *surface, PolySpan &polyspan,
2132 Color::BlendMethod got_blend_method, Color::value_type got_amount) const
2134 Surface::alpha_pen p(surface->begin(),got_amount,_BlendFunc(got_blend_method));
2135 PolySpan::cover_array::iterator cur_mark = polyspan.covers.begin();
2136 PolySpan::cover_array::iterator end_mark = polyspan.covers.end();
2138 Real cover,area,alpha;
2145 if(cur_mark == end_mark)
2150 p.move_to(polyspan.window.minx,polyspan.window.miny);
2151 p.put_block(polyspan.window.maxy - polyspan.window.miny,polyspan.window.maxx - polyspan.window.minx);
2156 //fill initial rect / line
2159 //fill all the area above the first vertex
2160 p.move_to(polyspan.window.minx,polyspan.window.miny);
2161 y = polyspan.window.miny;
2162 int l = polyspan.window.maxx - polyspan.window.minx;
2164 p.put_block(cur_mark->y - polyspan.window.miny,l);
2166 //fill the area to the left of the first vertex on that line
2167 l = cur_mark->x - polyspan.window.minx;
2168 p.move_to(polyspan.window.minx,cur_mark->y);
2169 if(l) p.put_hline(l);
2179 area = cur_mark->area;
2180 cover += cur_mark->cover;
2182 //accumulate for the current pixel
2183 while(++cur_mark != polyspan.covers.end())
2185 if(y != cur_mark->y | x != cur_mark->x)
2188 area += cur_mark->area;
2189 cover += cur_mark->cover;
2192 //draw pixel - based on covered area
2193 if(area) //if we're ok, draw the current pixel
2195 alpha = polyspan.ExtractAlpha(cover - area);
2196 if(invert) alpha = 1 - alpha;
2200 if(alpha >= .5) p.put_value();
2202 else if(alpha) p.put_value_alpha(alpha);
2208 //if we're done, don't use iterator and exit
2209 if(cur_mark == end_mark) break;
2211 //if there is no more live pixels on this line, goto next
2212 if(y != cur_mark->y)
2216 //fill the area at the end of the line
2217 p.put_hline(polyspan.window.maxx - x);
2219 //fill area at the beginning of the next line
2220 p.move_to(polyspan.window.minx,cur_mark->y);
2221 p.put_hline(cur_mark->x - polyspan.window.minx);
2229 //draw span to next pixel - based on total amount of pixel cover
2232 alpha = polyspan.ExtractAlpha(cover);
2233 if(invert) alpha = 1 - alpha;
2237 if(alpha >= .5) p.put_hline(cur_mark->x - x);
2239 else if(alpha) p.put_hline(cur_mark->x - x,alpha);
2243 //fill the after stuff
2246 //fill the area at the end of the line
2247 p.put_hline(polyspan.window.maxx - x);
2249 //fill area at the beginning of the next line
2250 p.move_to(polyspan.window.minx,y+1);
2251 p.put_block(polyspan.window.maxy - y - 1,polyspan.window.maxx - polyspan.window.minx);
2257 bool Layer_Shape::render_polyspan(etl::surface<float> *surface, PolySpan &polyspan) const
2259 etl::surface<float>::pen p(surface->begin());
2260 PolySpan::cover_array::iterator cur_mark = polyspan.covers.begin();
2261 PolySpan::cover_array::iterator end_mark = polyspan.covers.end();
2263 Real cover,area,alpha;
2269 //the pen always writes 1 (unless told to do otherwise)
2272 if(cur_mark == end_mark)
2277 p.move_to(polyspan.window.minx,polyspan.window.miny);
2278 p.put_block(polyspan.window.maxy - polyspan.window.miny,polyspan.window.maxx - polyspan.window.minx);
2283 //fill initial rect / line
2286 //fill all the area above the first vertex
2287 p.move_to(polyspan.window.minx,polyspan.window.miny);
2288 y = polyspan.window.miny;
2289 int l = polyspan.window.maxx - polyspan.window.minx;
2291 p.put_block(cur_mark->y - polyspan.window.miny,l);
2293 //fill the area to the left of the first vertex on that line
2294 l = cur_mark->x - polyspan.window.minx;
2295 p.move_to(polyspan.window.minx,cur_mark->y);
2296 if(l) p.put_hline(l);
2305 area = cur_mark->area;
2306 cover += cur_mark->cover;
2308 //accumulate for the current pixel
2309 while(++cur_mark != polyspan.covers.end())
2311 if(y != cur_mark->y | x != cur_mark->x)
2314 area += cur_mark->area;
2315 cover += cur_mark->cover;
2318 //draw pixel - based on covered area
2319 if(area) //if we're ok, draw the current pixel
2321 alpha = 1 - polyspan.ExtractAlpha(cover - area);
2324 if(alpha >= .5) p.put_value();
2326 else if(alpha) p.put_value(alpha);
2332 //if we're done, don't use iterator and exit
2333 if(cur_mark == end_mark) break;
2335 //if there is no more live pixels on this line, goto next
2336 if(y != cur_mark->y)
2338 //fill the area at the end of the line
2339 p.put_hline(polyspan.window.maxx - x);
2341 //fill area at the beginning of the next line
2342 p.move_to(polyspan.window.minx,cur_mark->y);
2343 p.put_hline(cur_mark->x - polyspan.window.minx);
2350 //draw span to next pixel - based on total amount of pixel cover
2353 alpha = 1 - polyspan.ExtractAlpha(cover);
2356 if(alpha >= .5) p.put_hline(cur_mark->x - x);
2358 else if(alpha) p.put_hline(cur_mark->x - x,alpha);
2362 //fill the area at the end of the line
2363 p.put_hline(polyspan.window.maxx - x);
2365 //fill area at the beginning of the next line
2366 p.move_to(polyspan.window.minx,y+1);
2367 p.put_block(polyspan.window.maxy - y - 1,polyspan.window.maxx - polyspan.window.minx);
2377 area = cur_mark->area;
2378 cover += cur_mark->cover;
2380 //accumulate for the current pixel
2381 while(++cur_mark != polyspan.covers.end())
2383 if(y != cur_mark->y | x != cur_mark->x)
2386 area += cur_mark->area;
2387 cover += cur_mark->cover;
2390 //draw pixel - based on covered area
2391 if(area) //if we're ok, draw the current pixel
2393 alpha = polyspan.ExtractAlpha(cover - area);
2396 if(alpha >= .5) p.put_value();
2398 else if(alpha) p.put_value(alpha);
2404 //if we're done, don't use iterator and exit
2405 if(cur_mark == end_mark) break;
2407 //if there is no more live pixels on this line, goto next
2408 if(y != cur_mark->y)
2415 //draw span to next pixel - based on total amount of pixel cover
2418 alpha = polyspan.ExtractAlpha(cover);
2421 if(alpha >= .5) p.put_hline(cur_mark->x - x);
2423 else if(alpha) p.put_hline(cur_mark->x - x,alpha);
2432 Layer_Shape::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
2434 const unsigned int w = renddesc.get_w();
2435 const unsigned int h = renddesc.get_h();
2437 const Real pw = abs(renddesc.get_pw());
2438 const Real ph = abs(renddesc.get_ph());
2440 //const Real OFFSET_EPSILON = 1e-8;
2441 SuperCallback stageone(cb,1,10000,15001+renddesc.get_h());
2442 SuperCallback stagetwo(cb,10000,10001+renddesc.get_h(),15001+renddesc.get_h());
2443 SuperCallback stagethree(cb,10001+renddesc.get_h(),15001+renddesc.get_h(),15001+renddesc.get_h());
2445 // Render what is behind us
2447 //clip if it satisfies the invert solid thing
2448 if(is_solid_color() && invert)
2450 Rect aabb = edge_table->aabb;
2451 Point tl = renddesc.get_tl() - offset;
2453 Real pw = renddesc.get_pw(),
2454 ph = renddesc.get_ph();
2458 Real pixelfeatherx = abs(feather/pw),
2459 pixelfeathery = abs(feather/ph);
2461 nrect.set_point((aabb.minx - tl[0])/pw,(aabb.miny - tl[1])/ph);
2462 nrect.expand((aabb.maxx - tl[0])/pw,(aabb.maxy - tl[1])/ph);
2464 RendDesc optdesc(renddesc);
2466 //make sure to expand so we gain subpixels rather than lose them
2467 nrect.minx = floor(nrect.minx-pixelfeatherx); nrect.miny = floor(nrect.miny-pixelfeathery);
2468 nrect.maxx = ceil(nrect.maxx+pixelfeatherx); nrect.maxy = ceil(nrect.maxy+pixelfeathery);
2470 //make sure the subwindow is clipped with our tile window (minimize useless drawing)
2471 set_intersect(nrect,nrect,Rect(0,0,renddesc.get_w(),renddesc.get_h()));
2473 //must resize the surface first
2474 surface->set_wh(renddesc.get_w(),renddesc.get_h());
2477 //only render anything if it's visible from our current tile
2480 //set the subwindow to the viewable pixels and render it to the subsurface
2481 optdesc.set_subwindow((int)nrect.minx, (int)nrect.miny,
2482 (int)(nrect.maxx - nrect.minx), (int)(nrect.maxy - nrect.miny));
2484 Surface optimizedbacksurf;
2485 if(!context.accelerated_render(&optimizedbacksurf,quality,optdesc,&stageone))
2488 //blit that onto the original surface so we can pretend that nothing ever happened
2489 Surface::pen p = surface->get_pen((int)nrect.minx,(int)nrect.miny);
2490 optimizedbacksurf.blit_to(p);
2494 if(!context.accelerated_render(surface,quality,renddesc,&stageone))
2498 if(cb && !cb->amount_complete(10000,10001+renddesc.get_h())) return false;
2502 //we have to blur rather than be crappy
2504 //so make a separate surface
2505 RendDesc workdesc(renddesc);
2507 etl::surface<float> shapesurface;
2509 //the expanded size = 1/2 the size in each direction rounded up
2510 int halfsizex = (int) (abs(feather*.5/pw) + 3),
2511 halfsizey = (int) (abs(feather*.5/ph) + 3);
2513 //expand by 1/2 size in each direction on either side
2520 workdesc.set_subwindow(-max(1,halfsizex),-max(1,halfsizey),w+2*max(1,halfsizex),h+2*max(1,halfsizey));
2523 case Blur::FASTGAUSSIAN:
2530 workdesc.set_subwindow(-max(1,halfsizex),-max(1,halfsizey),w+2*max(1,halfsizex),h+2*max(1,halfsizey));
2533 case Blur::GAUSSIAN:
2535 #define GAUSSIAN_ADJUSTMENT (0.05)
2536 Real pw = (Real)workdesc.get_w()/(workdesc.get_br()[0]-workdesc.get_tl()[0]);
2537 Real ph = (Real)workdesc.get_h()/(workdesc.get_br()[1]-workdesc.get_tl()[1]);
2542 halfsizex = (int)(abs(pw)*feather*GAUSSIAN_ADJUSTMENT+0.5);
2543 halfsizey = (int)(abs(ph)*feather*GAUSSIAN_ADJUSTMENT+0.5);
2545 halfsizex = (halfsizex + 1)/2;
2546 halfsizey = (halfsizey + 1)/2;
2547 workdesc.set_subwindow( -halfsizex, -halfsizey, w+2*halfsizex, h+2*halfsizey );
2553 shapesurface.set_wh(workdesc.get_w(),workdesc.get_h());
2554 shapesurface.clear();
2557 if(!render_shape(&shapesurface,quality,workdesc,&stagetwo))return false;
2560 Blur(feather,feather,blurtype,&stagethree)(shapesurface,workdesc.get_br()-workdesc.get_tl(),shapesurface);
2562 //blend with stuff below it...
2563 unsigned int u = halfsizex, v = halfsizey, x = 0, y = 0;
2564 for(y = 0; y < h; y++,v++)
2567 for(x = 0; x < w; x++,u++)
2569 float a = shapesurface[v][u];
2572 //a = floor(a*255+0.5f)/255;
2573 (*surface)[y][x]=Color::blend(color,(*surface)[y][x],a*get_amount(),get_blend_method());
2575 //else (*surface)[y][x] = worksurface[v][u];
2580 if(cb && !cb->amount_complete(100,100))
2582 sinfg::warning("Layer_Shape: could not set amount complete");
2589 //might take out to reduce code size
2590 return render_shape(surface,true,quality,renddesc,&stagetwo);
2596 Layer_Shape::render_shape(Surface *surface,bool useblend,int quality,
2597 const RendDesc &renddesc, ProgressCallback *cb)const
2601 SuperCallback progress(cb,0,renddesc.get_h(),renddesc.get_h());
2603 // If our amount is set to zero, no need to render anything
2607 //test new polygon renderer
2609 // Width and Height of a pixel
2610 const int w = renddesc.get_w();
2611 const int h = renddesc.get_h();
2612 const Real pw = renddesc.get_w()/(renddesc.get_br()[0]-renddesc.get_tl()[0]);
2613 const Real ph = renddesc.get_h()/(renddesc.get_br()[1]-renddesc.get_tl()[1]);
2615 const Point tl = renddesc.get_tl();
2617 Vector tangent (0,0);
2621 //optimization for tesselating only inside tiles
2622 span.window.minx = 0;
2623 span.window.miny = 0;
2624 span.window.maxx = w;
2625 span.window.maxy = h;
2627 //pointers for processing the bytestream
2628 const char *current = &bytestream[0];
2629 const char *end = &bytestream[bytestream.size()];
2631 int operation = Primitive::NONE;
2638 Real x,y,x1,y1,x2,y2;
2641 while(current < end)
2647 //get the op code safely
2648 curprim = (Primitive *)current;
2650 //advance past indices
2651 current += sizeof(Primitive);
2654 warning("Layer_Shape::accelerated_render - Error in the byte stream, not enough space for next declaration");
2658 //get the relevant data
2659 operation = curprim->operation;
2660 number = curprim->number;
2662 if(operation == Primitive::END)
2665 if(operation == Primitive::CLOSE)
2667 if(span.notclosed())
2669 tangent[0] = span.close_x - span.cur_x;
2670 tangent[1] = span.close_y - span.cur_y;
2676 data = (Point*)current;
2677 current += sizeof(Point)*number;
2679 //check data positioning
2682 warning("Layer_Shape::accelerated_render - Error in the byte stream, in sufficient data space for declared number of points");
2686 } catch(...) { sinfg::error("Layer_Shape::render_shape()1: Caught an exception after %d loops, rethrowing...", tmp); throw; }
2688 //transfer all the data - RLE optimized
2689 for(curnum=0; curnum < number;)
2693 case Primitive::MOVE_TO:
2695 x = data[curnum][0];
2696 x = (x - tl[0] + offset[0])*pw;
2697 y = data[curnum][1];
2698 y = (y - tl[1] + offset[1])*ph;
2709 tangent[0] = x - span.cur_x;
2710 tangent[1] = y - span.cur_y;
2715 curnum++; //only advance one point
2720 case Primitive::LINE_TO:
2722 x = data[curnum][0];
2723 x = (x - tl[0] + offset[0])*pw;
2724 y = data[curnum][1];
2725 y = (y - tl[1] + offset[1])*ph;
2727 tangent[0] = x - span.cur_x;
2728 tangent[1] = y - span.cur_y;
2735 case Primitive::CONIC_TO:
2737 x = data[curnum+1][0];
2738 x = (x - tl[0] + offset[0])*pw;
2739 y = data[curnum+1][1];
2740 y = (y - tl[1] + offset[1])*ph;
2742 x1 = data[curnum][0];
2743 x1 = (x1 - tl[0] + offset[0])*pw;
2744 y1 = data[curnum][1];
2745 y1 = (y1 - tl[1] + offset[1])*ph;
2747 tangent[0] = 2*(x - x1);
2748 tangent[1] = 2*(y - y1);
2750 span.conic_to(x1,y1,x,y);
2755 case Primitive::CONIC_TO_SMOOTH:
2757 x = data[curnum][0];
2758 x = (x - tl[0] + offset[0])*pw;
2759 y = data[curnum][1];
2760 y = (y - tl[1] + offset[1])*ph;
2762 x1 = span.cur_x + tangent[0]/2;
2763 y1 = span.cur_y + tangent[1]/2;
2765 tangent[0] = 2*(x - x1);
2766 tangent[1] = 2*(y - y1);
2768 span.conic_to(x1,y1,x,y);
2774 case Primitive::CUBIC_TO:
2776 x = data[curnum+2][0];
2777 x = (x - tl[0] + offset[0])*pw;
2778 y = data[curnum+2][1];
2779 y = (y - tl[1] + offset[1])*ph;
2781 x2 = data[curnum+1][0];
2782 x2 = (x2 - tl[0] + offset[0])*pw;
2783 y2 = data[curnum+1][1];
2784 y2 = (y2 - tl[1] + offset[1])*ph;
2786 x1 = data[curnum][0];
2787 x1 = (x1 - tl[0] + offset[0])*pw;
2788 y1 = data[curnum][1];
2789 y1 = (y1 - tl[1] + offset[1])*ph;
2791 tangent[0] = 2*(x - x2);
2792 tangent[1] = 2*(y - y2);
2794 span.cubic_to(x1,y1,x2,y2,x,y);
2800 case Primitive::CUBIC_TO_SMOOTH:
2802 x = data[curnum+1][0];
2803 x = (x - tl[0] + offset[0])*pw;
2804 y = data[curnum+1][1];
2805 y = (y - tl[1] + offset[1])*ph;
2807 x2 = data[curnum][0];
2808 x2 = (x2 - tl[0] + offset[0])*pw;
2809 y2 = data[curnum][1];
2810 y2 = (y2 - tl[1] + offset[1])*ph;
2812 x1 = span.cur_x + tangent[0]/3.0;
2813 y1 = span.cur_y + tangent[1]/3.0;
2815 tangent[0] = 2*(x - x2);
2816 tangent[1] = 2*(y - y2);
2818 span.cubic_to(x1,y1,x2,y2,x,y);
2827 //sort the bastards so we can render everything
2830 return render_polyspan(surface, span,
2831 useblend?get_blend_method():Color::BLEND_STRAIGHT,
2832 useblend?get_amount():1.0);
2836 Layer_Shape::render_shape(surface<float> *surface,int quality,
2837 const RendDesc &renddesc, ProgressCallback *cb)const
2839 // If our amount is set to zero, no need to render anything
2843 //test new polygon renderer
2845 // Width and Height of a pixel
2846 const int w = renddesc.get_w();
2847 const int h = renddesc.get_h();
2848 const Real pw = renddesc.get_w()/(renddesc.get_br()[0]-renddesc.get_tl()[0]);
2849 const Real ph = renddesc.get_h()/(renddesc.get_br()[1]-renddesc.get_tl()[1]);
2851 const Point tl = renddesc.get_tl();
2853 Vector tangent (0,0);
2857 //optimization for tesselating only inside tiles
2858 span.window.minx = 0;
2859 span.window.miny = 0;
2860 span.window.maxx = w;
2861 span.window.maxy = h;
2863 //pointers for processing the bytestream
2864 const char *current = &bytestream[0];
2865 const char *end = &bytestream[bytestream.size()];
2867 int operation = Primitive::NONE;
2874 Real x,y,x1,y1,x2,y2;
2876 while(current < end)
2878 //get the op code safely
2879 curprim = (Primitive *)current;
2881 //advance past indices
2882 current += sizeof(Primitive);
2885 warning("Layer_Shape::accelerated_render - Error in the byte stream, not enough space for next declaration");
2889 //get the relevant data
2890 operation = curprim->operation;
2891 number = curprim->number;
2893 if(operation == Primitive::END)
2896 if(operation == Primitive::CLOSE)
2898 if(span.notclosed())
2900 tangent[0] = span.close_x - span.cur_x;
2901 tangent[1] = span.close_y - span.cur_y;
2907 data = (Point*)current;
2908 current += sizeof(Point)*number;
2910 //check data positioning
2913 warning("Layer_Shape::accelerated_render - Error in the byte stream, in sufficient data space for declared number of points");
2917 //transfer all the data
2918 for(curnum=0; curnum < number;)
2922 case Primitive::MOVE_TO:
2924 x = data[curnum][0];
2925 x = (x - tl[0] + offset[0])*pw;
2926 y = data[curnum][1];
2927 y = (y - tl[1] + offset[1])*ph;
2938 tangent[0] = x - span.cur_x;
2939 tangent[1] = y - span.cur_y;
2944 curnum++; //only advance one point
2949 case Primitive::LINE_TO:
2951 x = data[curnum][0];
2952 x = (x - tl[0] + offset[0])*pw;
2953 y = data[curnum][1];
2954 y = (y - tl[1] + offset[1])*ph;
2956 tangent[0] = x - span.cur_x;
2957 tangent[1] = y - span.cur_y;
2964 case Primitive::CONIC_TO:
2966 x = data[curnum+1][0];
2967 x = (x - tl[0] + offset[0])*pw;
2968 y = data[curnum+1][1];
2969 y = (y - tl[1] + offset[1])*ph;
2971 x1 = data[curnum][0];
2972 x1 = (x1 - tl[0] + offset[0])*pw;
2973 y1 = data[curnum][1];
2974 y1 = (y1 - tl[1] + offset[1])*ph;
2976 tangent[0] = 2*(x - x1);
2977 tangent[1] = 2*(y - y1);
2979 span.conic_to(x1,y1,x,y);
2984 case Primitive::CONIC_TO_SMOOTH:
2986 x = data[curnum][0];
2987 x = (x - tl[0] + offset[0])*pw;
2988 y = data[curnum][1];
2989 y = (y - tl[1] + offset[1])*ph;
2991 x1 = span.cur_x + tangent[0]/2;
2992 y1 = span.cur_y + tangent[1]/2;
2994 tangent[0] = 2*(x - x1);
2995 tangent[1] = 2*(y - y1);
2997 span.conic_to(x1,y1,x,y);
3003 case Primitive::CUBIC_TO:
3005 x = data[curnum+2][0];
3006 x = (x - tl[0] + offset[0])*pw;
3007 y = data[curnum+2][1];
3008 y = (y - tl[1] + offset[1])*ph;
3010 x2 = data[curnum+1][0];
3011 x2 = (x2 - tl[0] + offset[0])*pw;
3012 y2 = data[curnum+1][1];
3013 y2 = (y2 - tl[1] + offset[1])*ph;
3015 x1 = data[curnum][0];
3016 x1 = (x1 - tl[0] + offset[0])*pw;
3017 y1 = data[curnum][1];
3018 y1 = (y1 - tl[1] + offset[1])*ph;
3020 tangent[0] = 2*(x - x2);
3021 tangent[1] = 2*(y - y2);
3023 span.cubic_to(x1,y1,x2,y2,x,y);
3029 case Primitive::CUBIC_TO_SMOOTH:
3031 x = data[curnum+1][0];
3032 x = (x - tl[0] + offset[0])*pw;
3033 y = data[curnum+1][1];
3034 y = (y - tl[1] + offset[1])*ph;
3036 x2 = data[curnum][0];
3037 x2 = (x2 - tl[0] + offset[0])*pw;
3038 y2 = data[curnum][1];
3039 y2 = (y2 - tl[1] + offset[1])*ph;
3041 x1 = span.cur_x + tangent[0]/3.0;
3042 y1 = span.cur_y + tangent[1]/3.0;
3044 tangent[0] = 2*(x - x2);
3045 tangent[1] = 2*(y - y2);
3047 span.cubic_to(x1,y1,x2,y2,x,y);
3056 //sort the bastards so we can render everything
3059 return render_polyspan(surface, span);
3063 Layer_Shape::get_bounding_rect()const
3066 return Rect::full_plane();
3068 Rect bounds(edge_table->aabb+offset);
3069 bounds.expand(max((bounds.get_min()-bounds.get_max()).mag()*0.01,feather));