// SPDX-License-Identifier: GPL-3.0-or-later /** * @file fill_curve.cpp * @brief Fill the interior of a closed curve in an image * * (C) 2011-2014, 2019, Pascal Monasse */ #ifdef FILL_CURVE_H #include #include /// Sign of f2-f1 inline signed char sign(float f1, float f2) { return ((f1& curve); void add_point(const Point& pi, std::vector< std::vector >& inter); }; /// Find index of last point of the curve static int last_point(const std::vector& curve) { Point p0 = curve.front(); std::vector::const_reverse_iterator it=curve.rbegin(), end=curve.rend(); size_t i = curve.size()-1; for(; it!=end; ++it, --i) if(*it!=p0) return i; return 0; // Single vertex } /// Constructor PolyIterator::PolyIterator(const std::vector& curve) { size_t i = last_point(curve); Point q = curve[i]; // Previous vertex p = curve[0]; if(q.y==p.y) { bHorizontal = is_integer(p.y); dir = sign(q.x,p.x); } else { bHorizontal = false; dir = sign(q.y,p.y); } } /// Allocate inter static void init_inter(std::vector< std::vector >& inter, size_t size) { inter.resize(size); std::vector< std::vector >::iterator it=inter.begin(); for(; it!=inter.end(); ++it) it->clear(); } /// Add bound of interval on line iy at position x static void bound(std::vector< std::vector >& inter, float x, int iy) { if(0<=iy && iy<(int)inter.size()) inter[iy].push_back(x); } /// Add segment to point i to current polyline: see [1]Figure 4 for the rules. void PolyIterator::add_point(const Point& pi, std::vector< std::vector >& inter) { Point q = p; p = pi; signed char dirP = dir; // Previous direction if(q.y==p.y) { // Horizontal segment if(q.x!=p.x && is_integer(q.y)) { dir = sign(q.x,p.x); if(bHorizontal) { // Half-turn, rule (f) if(dirP!=dir) bound(inter, q.x, (int)q.y); } else { // Rules (b), (c) bHorizontal = true; // First among horizontal edgels if(dirP==dir) // Rule (b) bound(inter, q.x, (int)q.y); } } return; } dir = sign(q.y,p.y); int iy1 = (int)q.y; int iy2 = (int)p.y + dir; float a = (q.x-p.x) / (q.y-p.y); // Slope if(bHorizontal) { // Away from horizontal edgel, rules (d), (e) bHorizontal = false; if(dirP!=dir) // Rule (d) bound(inter, q.x, iy1); iy1 += dir; } else if(dir!=dirP && q.y==(float)iy1) { // Local peak, rule (g) bound(inter, q.x, iy1); // Single point interval bound(inter, q.x, iy1); iy1 += dir; } else if(dir>0 && (float)iy1 0) { if(p.y<=(float)j) continue; // Out of bounds } else if((float)j<=p.y) continue; // Out of bounds float xj = q.x + a*((float)j-q.y); assert((q.x<=xj && xj<=p.x) || (p.x<=xj && xj<=q.x)); bound(inter, xj, j); } } /// Fill curve with a single vertex template void fill_point(Point p, T value, T* out, size_t w) { if(is_integer(p.x) && is_integer(p.y)) out[(int)p.y*w+(int)p.x] = value; } /// Fill line of image template void fill_line(T value, T* im, T* end, std::vector& inter) { std::sort(inter.begin(), inter.end()); bool bIn = false; // inside/outside std::vector::const_iterator it=inter.begin(); for(;it!=inter.end() && *it<0; ++it) // Handle curve outside left boundary bIn = !bIn; if(it==inter.end()) return; if(bIn) std::fill(im, std::min(end,im+(int)*it), value); float i = (float)(int)*it; // Current pixel number (int stored as float) im += (int)i; for(; im void fill_inter(T value, T* im, size_t w, size_t h, std::vector< std::vector >& inter) { for(size_t i=0; i void fill_curve(const std::vector& line, T value, T* out, size_t w, size_t h, std::vector< std::vector >* inter) { if(line.empty()) return; PolyIterator p(line); if(p.dir==0) { // Single vertex fill_point(line.front(), value, out,w); return; } bool bAllocInter=(inter==0); if(bAllocInter) inter = new std::vector< std::vector >(h); else init_inter(*inter, h); std::vector::const_iterator it=line.begin()+1; for(; it!=line.end(); ++it) p.add_point(*it, *inter); p.add_point(line.front(), *inter); // Close polygon fill_inter(value, out, w, h, *inter); if(bAllocInter) delete inter; } #endif