/* === S Y N F I G ========================================================= */
/*! \file layer_shape.cpp
-** \brief Template Header
+** \brief Implementation of the "Shape" layer
**
** $Id$
**
** \legal
** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
+** Copyright (c) 2007 Chris Moore
**
** This package is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License as
SYNFIG_LAYER_INIT(Layer_Shape);
SYNFIG_LAYER_SET_NAME(Layer_Shape,"shape");
-SYNFIG_LAYER_SET_LOCAL_NAME(Layer_Shape,_("Shape"));
-SYNFIG_LAYER_SET_CATEGORY(Layer_Shape,_("Internal"));
+SYNFIG_LAYER_SET_LOCAL_NAME(Layer_Shape,N_("Shape"));
+SYNFIG_LAYER_SET_CATEGORY(Layer_Shape,N_("Internal"));
SYNFIG_LAYER_SET_VERSION(Layer_Shape,"0.1");
SYNFIG_LAYER_SET_CVS_ID(Layer_Shape,"$Id$");
int intersect(Real x,Real y) const
{
- if((y < aabb.miny) | (y > aabb.maxy) | (x < aabb.minx)) return 0;
+ if((y < aabb.miny+EPSILON) || (y > aabb.maxy) || (x < aabb.minx)) return 0;
if(x > aabb.maxx) return ydir;
//int i = 0;
//assumes that the rect culled away anything that would be beyond the edges
if(ydir > 0)
{
- while(y > (*++p)[1]);
+ while(y > (*++p)[1])
+ ;
}
else
{
- while(y < (*++p)[1]);
+ while(y < (*++p)[1])
+ ;
}
//for the loop to break there must have been a slope (straight line would do nothing)
degrees.push_back(2);
}
- static int intersect_conic(Real x, Real y, Point *p, int level = 0)
+ static int intersect_conic(Real x, Real y, Point *p, int /*level*/ = 0)
{
Real ymin,ymax,xmin,xmax;
int intersects = 0;
//to the left, to the right and out of range y, or completely out of range y
if( x < xmin ) return 0;
- if( x > xmax & (y > ymax | y < ymin) ) return 0;
- if( (y > ymax & y > p[1][1]) | (y < ymin & y < p[1][1]) ) return 0;
+ if( x > xmax && (y > ymax || y < ymin) ) return 0;
+ if( (y > ymax && y > p[1][1]) || (y < ymin && y < p[1][1]) ) return 0;
//degenerate line max
- if(ymin == ymax == p[1][1])
+ if(ymin == ymax && ymax == p[1][1])
return 0;
//degenerate accept - to the right and crossing the base line
if(x > xmax)
{
- return (y <= ymax & y >= ymin);
+ return (y <= ymax && y >= ymin);
}
//solve for curve = y
}
//calculate number of intersections
- if(t1 >= 0 & t1 <= 1)
+ if(t1 >= 0 && t1 <= 1)
{
const Real t = t1;
const Real invt = 1 - t;
}
}
- if(t2 >= 0 & t2 <= 1)
+ if(t2 >= 0 && t2 <= 1)
{
const Real t = t2;
const Real invt = 1 - t;
const Real cn[4] = {a,b,c,d};
Real p,dp,newt,oldpmag=FLT_MAX;
- //eval cubic eqn and it's derivative
+ //eval cubic eqn and its derivative
for(;;)
{
p = cn[0]*t + cn[1];
}
}
- static int intersect_cubic(Real x, Real y, Point *p, int level = 0)
+ static int intersect_cubic(Real x, Real y, Point *p, int /*level*/ = 0)
{
const Real INVALIDROOT = -FLT_MAX;
Real ymin,ymax,xmin,xmax;
//right of curve (and outside base range)
if( x > xmax )
{
- if( (y > ymax) | (y < ymin) ) return 0;
+ if( (y > ymax) || (y < ymin) ) return 0;
//degenerate accept - to the right and inside the [ymin,ymax] range (already rejected if out of range)
const Real n = p[3][1] - p[0][1];
t3 = -2.0*sqrt(Q)*cos((theta-2*PI)/3.0) - an/3.0;
//don't need to reorder,l just need to eliminate double/triple roots
- //if(t3 == t2 & t1 == t2) t2 = t3 = INVALIDROOT;
+ //if(t3 == t2 && t1 == t2) t2 = t3 = INVALIDROOT;
if(t3 == t2) t2 = t3 = INVALIDROOT;
if(t1 == t2) t1 = t2 = INVALIDROOT;
if(t1 == t3) t1 = t3 = INVALIDROOT;
//if(t1 != INVALIDROOT)
{
t = t1;//polish_cubicroot(a,b,c,d,t1,&dydt);
- if(t >= 0 & t < 1)
+ if(t >= 0 && t < 1)
{
//const Real invt = 1 - t;
//if(t2 != INVALIDROOT)
{
t = t2;//polish_cubicroot(a,b,c,d,t2,&dydt);
- if(t >= 0 & t < 1)
+ if(t >= 0 && t < 1)
{
//const Real invt = 1 - t;
//if(t3 != INVALIDROOT)
{
t = t3;//polish_cubicroot(a,b,c,d,t3,&dydt);
- if(t >= 0 & t < 1)
+ if(t >= 0 && t < 1)
{
//const Real invt = 1 - t;
int intersect(Real x,Real y, Point *table) const
{
- if((y < aabb.miny) | (y > aabb.maxy) | (x < aabb.minx)) return 0;
+ if((y < aabb.miny) || (y > aabb.maxy) || (x < aabb.minx)) return 0;
int i, curdeg, intersects = 0;
const int numcurves = degrees.size();
struct Layer_Shape::Intersector
{
Rect aabb;
+
+ //! true iff aabb hasn't been initialized yet
bool initaabb;
int flags;
bool notclosed()
{
- return (flags & NotClosed) | (cur_x != close_x) | (cur_y != close_y);
+ return (flags & NotClosed) || (cur_x != close_x) || (cur_y != close_y);
}
void move_to(Real x, Real y)
int dir = (y > cur_y)*1 + (-1)*(y < cur_y);
//check for context (if not line start a new segment)
- //if we're not in line mode (cover's 0 set case), or if directions are different (not valid for 0 direction)
+ //if we're not in line mode (covers 0 set case), or if directions are different (not valid for 0 direction)
if(prim != TYPE_LINE || (dir && segs.back().ydir != dir))
{
MonoSegment seg(dir,x,x,y,y);
cur_x = x;
cur_y = y;
- aabb.expand(x,y); //expand the entire things bounding box
+ aabb.expand(x,y); //expand the entire thing's bounding box
tangent[0] = x - cur_x;
tangent[1] = x - cur_y;
bool notclosed() const
{
- return (flags & NotClosed) | (cur_x != close_x) | (cur_y != close_y);
+ return (flags & NotClosed) || (cur_x != close_x) || (cur_y != close_y);
}
//0 out all the variables involved in processing
//move to the next cell (cover values 0 initially), keeping the current if necessary
void move_pen(int x, int y)
{
- if(y != current.y | x != current.x)
+ if(y != current.y || x != current.x)
{
addcurrent();
current.set(x,y,0,0);
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)
+ Real ExtractAlpha(Real area, WindingStyle winding_style)
{
- //non-zero winding style
- if(area < 0) area = -area;
- if(area > 1) area = 1;
+ if (area < 0)
+ area = -area;
- //even-odd 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;
- while(area > 2) area -= 2;
- if(area > 1) area = 1-area; //want pyramid like thing
- */
- //broken? - yep broken
+ // want pyramid like thing
+ if (area < 0)
+ area = -area;
+ }
return area;
}
antialias (true),
blurtype (Blur::FASTGAUSSIAN),
feather (0),
+ winding_style (WINDING_NON_ZERO),
bytestream (0),
lastbyteop (Primitive::NONE),
lastoppos (-1)
bool
Layer_Shape::set_param(const String & param, const ValueBase &value)
{
- IMPORT(color);
+ IMPORT_PLUS(color, { if (color.get_a() == 0) { if (converted_blend_) {
+ set_blend_method(Color::BLEND_ALPHA_OVER);
+ color.set_a(1); } else transparent_color_ = true; } });
IMPORT(offset);
IMPORT(invert);
IMPORT(antialias);
IMPORT(feather);
IMPORT(blurtype);
+ IMPORT(winding_style);
return Layer_Composite::set_param(param,value);
}
EXPORT(antialias);
EXPORT(feather);
EXPORT(blurtype);
+ EXPORT(winding_style);
EXPORT_NAME();
EXPORT_VERSION();
.set_description(_("Layer_Shape Color"))
);
ret.push_back(ParamDesc("offset")
- .set_local_name(_("Position"))
+ .set_local_name(_("Offset"))
);
ret.push_back(ParamDesc("invert")
.set_local_name(_("Invert"))
.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;
}
//************** SCANLINE RENDERING *********************
void Layer_Shape::PolySpan::line_to(Real x, Real y)
{
- Real n[4];
+ Real n[4] = {0,0,0,0};
bool afterx = false;
const Real xin(x), yin(y);
//CLIP IT!!!!
try {
//outside y - ignore entirely
- if( (cur_y >= window.maxy & y >= window.maxy)
- |(cur_y < window.miny & y < window.miny) )
+ if( (cur_y >= window.maxy && y >= window.maxy)
+ ||(cur_y < window.miny && y < window.miny) )
{
cur_x = x;
cur_y = y;
//generate data for the ending clipped info
if(y > window.maxy)
{
- //intial line to intersection (and degenerate)
+ //initial line to intersection (and degenerate)
n[2] = x + (window.maxy - y) * dx / dy;
//intersect coords
//generate data for the ending clipped info
if(y < window.miny)
{
- //intial line to intersection (and degenerate)
+ //initial line to intersection (and degenerate)
n[2] = x + (window.miny - y) * dx / dy;
//intersect coords
//generate data for the ending clipped info
if(x > window.maxx)
{
- //intial line to intersection (and degenerate)
+ //initial line to intersection (and degenerate)
n[2] = y + (window.maxx - x) * dy / dx;
n[0] = window.maxx;
//generate data for the ending clipped info
if(x < window.minx)
{
- //intial line to intersection (and degenerate)
+ //initial line to intersection (and degenerate)
n[2] = y + (window.minx - x) * dy / dx;
n[0] = window.minx;
const Real maxx = max(max(p[0][0],p[1][0]),p[2][0]);
const Real maxy = max(max(p[0][1],p[1][1]),p[2][1]);
- return (minx > r.maxx) |
- (maxx < r.minx) |
- (miny > r.maxy) |
+ return (minx > r.maxx) ||
+ (maxx < r.minx) ||
+ (miny > r.maxy) ||
(maxy < r.miny);
}
(miny > r.maxy) ||
(maxy < r.miny);*/
- return ((p[0][0] > r.maxx) & (p[1][0] > r.maxx) & (p[2][0] > r.maxx) & (p[3][0] > r.maxx)) |
- ((p[0][0] < r.minx) & (p[1][0] < r.minx) & (p[2][0] < r.minx) & (p[3][0] < r.minx)) |
- ((p[0][1] > r.maxy) & (p[1][1] > r.maxy) & (p[2][1] > r.maxy) & (p[3][1] > r.maxy)) |
- ((p[0][1] < r.miny) & (p[1][1] < r.miny) & (p[2][1] < r.miny) & (p[3][1] < r.miny));
+ return ((p[0][0] > r.maxx) && (p[1][0] > r.maxx) && (p[2][0] > r.maxx) && (p[3][0] > r.maxx)) ||
+ ((p[0][0] < r.minx) && (p[1][0] < r.minx) && (p[2][0] < r.minx) && (p[3][0] < r.minx)) ||
+ ((p[0][1] > r.maxy) && (p[1][1] > r.maxy) && (p[2][1] > r.maxy) && (p[3][1] > r.maxy)) ||
+ ((p[0][1] < r.miny) && (p[1][1] < r.miny) && (p[2][1] < r.miny) && (p[3][1] < r.miny));
}
static inline Real max_edges_cubic(const Point *const p)
{
if(num >= MAX_SUBDIVISION_SIZE)
{
- warning("Curve subdivision somehow ran out of space while tesselating!");
+ warning("Curve subdivision somehow ran out of space while tessellating!");
//do something...
assert(0);
{
if(num >= MAX_SUBDIVISION_SIZE)
{
- warning("Curve subdivision somehow ran out of space while tesselating!");
+ warning("Curve subdivision somehow ran out of space while tessellating!");
//do something...
assert(0);
//case all in same pixel
if(ix1 == ix2) //impossible for degenerate case (covered by the previous cases)
{
- current.addcover(dy,(fx1 + fx2)*dy/2); //horizontal trapazoid area
+ current.addcover(dy,(fx1 + fx2)*dy/2); //horizontal trapezoid area
return;
}
mult = (1 - fx1)*dydx; //next y intersection diff value (at 1)
//first pixel
- current.addcover(mult,(1 + fx1)*mult/2); // fx1,fy1,1,fy@1 - starting trapazoidal area
+ current.addcover(mult,(1 + fx1)*mult/2); // fx1,fy1,1,fy@1 - starting trapezoidal area
//move to the next pixel
fy1 += mult;
mult = fx1*dydx; //next y intersection diff value
//first pixel
- current.addcover(mult,fx1*mult/2); // fx1,fy1,0,fy@0 - starting trapazoidal area
+ current.addcover(mult,fx1*mult/2); // fx1,fy1,0,fy@0 - starting trapezoidal area
//move to next pixel
fy1 += mult;
mult = (1 - fy1) * dxdy;
- //x interset scanline
+ //x intersect scanline
x_from = x1 + mult;
draw_scanline(iy1,x1,fy1,x_from,1);
mult = fy1 * dxdy;
- //x interset scanline
+ //x intersect scanline
x_from = x1 + mult;
draw_scanline(iy1,x1,fy1,x_from,0);
op.operation = Primitive::END;
op.number = 0;
- if(lastbyteop == Primitive::END | lastbyteop == Primitive::NONE)
+ if(lastbyteop == Primitive::END || lastbyteop == Primitive::NONE)
{
}else
{
op.operation = Primitive::LINE_TO;
op.number = 1; //one point for now
- if(lastbyteop == Primitive::MOVE_TO | lastbyteop == Primitive::LINE_TO)
+ if(lastbyteop == Primitive::MOVE_TO || lastbyteop == Primitive::LINE_TO)
{
//only need to insert the point
bytestream.insert(bytestream.end(),(char*)&p,(char*)(&p+1));
//accumulate for the current pixel
while(++cur_mark != polyspan.covers.end())
{
- if(y != cur_mark->y | x != cur_mark->x)
+ if(y != cur_mark->y || x != cur_mark->x)
break;
area += cur_mark->area;
//draw pixel - based on covered area
if(area) //if we're ok, draw the current pixel
{
- alpha = polyspan.ExtractAlpha(cover - area);
+ alpha = polyspan.ExtractAlpha(cover - area, winding_style);
if(invert) alpha = 1 - alpha;
if(!antialias)
//draw span to next pixel - based on total amount of pixel cover
if(x < cur_mark->x)
{
- alpha = polyspan.ExtractAlpha(cover);
+ alpha = polyspan.ExtractAlpha(cover, winding_style);
if(invert) alpha = 1 - alpha;
if(!antialias)
//accumulate for the current pixel
while(++cur_mark != polyspan.covers.end())
{
- if(y != cur_mark->y | x != cur_mark->x)
+ if(y != cur_mark->y || x != cur_mark->x)
break;
area += cur_mark->area;
//draw pixel - based on covered area
if(area) //if we're ok, draw the current pixel
{
- alpha = 1 - polyspan.ExtractAlpha(cover - area);
+ alpha = 1 - polyspan.ExtractAlpha(cover - area, winding_style);
if(!antialias)
{
if(alpha >= .5) p.put_value();
//draw span to next pixel - based on total amount of pixel cover
if(x < cur_mark->x)
{
- alpha = 1 - polyspan.ExtractAlpha(cover);
+ alpha = 1 - polyspan.ExtractAlpha(cover, winding_style);
if(!antialias)
{
if(alpha >= .5) p.put_hline(cur_mark->x - x);
//accumulate for the current pixel
while(++cur_mark != polyspan.covers.end())
{
- if(y != cur_mark->y | x != cur_mark->x)
+ if(y != cur_mark->y || x != cur_mark->x)
break;
area += cur_mark->area;
//draw pixel - based on covered area
if(area) //if we're ok, draw the current pixel
{
- alpha = polyspan.ExtractAlpha(cover - area);
+ alpha = polyspan.ExtractAlpha(cover - area, winding_style);
if(!antialias)
{
if(alpha >= .5) p.put_value();
//draw span to next pixel - based on total amount of pixel cover
if(x < cur_mark->x)
{
- alpha = polyspan.ExtractAlpha(cover);
+ alpha = polyspan.ExtractAlpha(cover, winding_style);
if(!antialias)
{
if(alpha >= .5) p.put_hline(cur_mark->x - x);
Rect nrect;
- Real pixelfeatherx = abs(feather/pw),
- pixelfeathery = abs(feather/ph);
+ Real pixelfeatherx = quality == 10 ? 0 : abs(feather/pw),
+ pixelfeathery = quality == 10 ? 0 : abs(feather/ph);
nrect.set_point((aabb.minx - tl[0])/pw,(aabb.miny - tl[1])/ph);
nrect.expand((aabb.maxx - tl[0])/pw,(aabb.maxy - tl[1])/ph);
if(cb && !cb->amount_complete(10000,10001+renddesc.get_h())) return false;
- if(feather)
+ if(feather && quality != 10)
{
//we have to blur rather than be crappy
}
bool
-Layer_Shape::render_shape(Surface *surface,bool useblend,int quality,
+Layer_Shape::render_shape(Surface *surface,bool useblend,int /*quality*/,
const RendDesc &renddesc, ProgressCallback *cb)const
{
int tmp(0);
PolySpan span;
- //optimization for tesselating only inside tiles
+ // if the pixels are zero sized then we're too zoomed out to see anything
+ if (pw == 0 || ph == 0)
+ return true;
+
+ //optimization for tessellating only inside tiles
span.window.minx = 0;
span.window.miny = 0;
span.window.maxx = w;
}
bool
-Layer_Shape::render_shape(surface<float> *surface,int quality,
- const RendDesc &renddesc, ProgressCallback *cb)const
+Layer_Shape::render_shape(etl::surface<float> *surface,int /*quality*/,
+ const RendDesc &renddesc, ProgressCallback */*cb*/)const
{
// If our amount is set to zero, no need to render anything
if(!get_amount())
PolySpan span;
- //optimization for tesselating only inside tiles
+ //optimization for tessellating only inside tiles
span.window.minx = 0;
span.window.miny = 0;
span.window.maxx = w;
if(invert)
return Rect::full_plane();
- Rect bounds(edge_table->aabb+offset);
- bounds.expand(max((bounds.get_min()-bounds.get_max()).mag()*0.01,feather));
+ 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;
}