Image: Eliminate VLAs from polygon fill algorithm implementation

This commit is contained in:
Peter Keresztes Schmidt 2021-05-16 19:16:32 +02:00
parent a335e740f3
commit 6642ca4515
3 changed files with 74 additions and 93 deletions

View File

@ -2458,9 +2458,9 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) {
} // end foreach coordinate in the polygon } // end foreach coordinate in the polygon
} }
/* RGB32 compatible: complete */ // Polygon filling is based on the Scan-line Polygon filling algorithm
void Image::Fill(Rgb colour, int density, const Polygon &polygon) { void Image::Fill(Rgb colour, int density, const Polygon &polygon) {
if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) { if (!(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32)) {
Panic("Attempt to fill image with unexpected colours %d", colours); Panic("Attempt to fill image with unexpected colours %d", colours);
} }
@ -2468,115 +2468,91 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) {
colour = rgb_convert(colour, subpixelorder); colour = rgb_convert(colour, subpixelorder);
size_t n_coords = polygon.GetVertices().size(); size_t n_coords = polygon.GetVertices().size();
int n_global_edges = 0;
Edge global_edges[n_coords]; std::vector<Edge> global_edges;
global_edges.reserve(n_coords);
for (size_t j = 0, i = n_coords - 1; j < n_coords; i = j++) { for (size_t j = 0, i = n_coords - 1; j < n_coords; i = j++) {
const Vector2 &p1 = polygon.GetVertices()[i]; const Vector2 &p1 = polygon.GetVertices()[i];
const Vector2 &p2 = polygon.GetVertices()[j]; const Vector2 &p2 = polygon.GetVertices()[j];
int x1 = p1.x_; // Do not add horizontal edges to the global edge table.
int x2 = p2.x_; if (p1.y_ == p2.y_)
int y1 = p1.y_;
int y2 = p2.y_;
//Debug( 9, "x1:%d,y1:%d x2:%d,y2:%d", x1, y1, x2, y2 );
if ( y1 == y2 )
continue; continue;
double dx = x2 - x1; Vector2 d = p2 - p1;
double dy = y2 - y1;
global_edges.emplace_back(std::min(p1.y_, p2.y_),
std::max(p1.y_, p2.y_),
p1.y_ < p2.y_ ? p1.x_ : p2.x_,
d.x_ / static_cast<double>(d.y_));
global_edges[n_global_edges].min_y = y1<y2?y1:y2;
global_edges[n_global_edges].max_y = y1<y2?y2:y1;
global_edges[n_global_edges].min_x = y1<y2?x1:x2;
global_edges[n_global_edges]._1_m = dx/dy;
n_global_edges++;
} }
std::sort(global_edges, global_edges + n_global_edges, Edge::CompareYX); std::sort(global_edges.begin(), global_edges.end(), Edge::CompareYX);
#ifndef ZM_DBG_OFF std::vector<Edge> active_edges;
if ( logLevel() >= Logger::DEBUG9 ) { active_edges.reserve(global_edges.size());
for ( int i = 0; i < n_global_edges; i++ ) {
Debug(9, "%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f",
i, global_edges[i].min_y, global_edges[i].max_y, global_edges[i].min_x, global_edges[i]._1_m);
}
}
#endif
int n_active_edges = 0; int32 scan_line = global_edges[0].min_y;
Edge active_edges[n_global_edges]; while (!global_edges.empty() || !active_edges.empty()) {
int y = global_edges[0].min_y; // Deactivate edges with max_y < current scan line
do { for (auto it = active_edges.begin(); it != active_edges.end();) {
for ( int i = 0; i < n_global_edges; i++ ) { if (scan_line >= it->max_y) {
if ( global_edges[i].min_y == y ) { it = active_edges.erase(it);
Debug(9, "Moving global edge");
active_edges[n_active_edges++] = global_edges[i];
if ( i < (n_global_edges-1) ) {
memmove(&global_edges[i], &global_edges[i + 1], sizeof(*global_edges) * (n_global_edges - i - 1));
i--;
}
n_global_edges--;
} else { } else {
break; it->min_x += it->_1_m;
++it;
} }
} }
std::sort(active_edges, active_edges + n_active_edges, Edge::CompareX);
#ifndef ZM_DBG_OFF // Activate edges with min_y == current scan line
if ( logLevel() >= Logger::DEBUG9 ) { for (auto it = global_edges.begin(); it != global_edges.end();) {
for ( int i = 0; i < n_active_edges; i++ ) { if (it->min_y == scan_line) {
Debug(9, "%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", active_edges.emplace_back(*it);
y, i, active_edges[i].min_y, active_edges[i].max_y, active_edges[i].min_x, active_edges[i]._1_m ); it = global_edges.erase(it);
} else {
++it;
} }
} }
#endif std::sort(active_edges.begin(), active_edges.end(), Edge::CompareX);
if ( !(y%density) ) {
//Debug( 9, "%d", y ); if (!(scan_line % density)) {
for ( int i = 0; i < n_active_edges; ) { for (auto it = active_edges.begin(); it != active_edges.end(); ++it) {
int lo_x = int(round(active_edges[i++].min_x)); int32 lo_x = static_cast<int32>(it->min_x);
int hi_x = int(round(active_edges[i++].min_x)); int32 hi_x = static_cast<int32>(std::next(it)->min_x);
if ( colours == ZM_COLOUR_GRAY8 ) { if (colours == ZM_COLOUR_GRAY8) {
unsigned char *p = &buffer[(y*width)+lo_x]; uint8 *p = &buffer[(scan_line * width) + lo_x];
for ( int x = lo_x; x <= hi_x; x++, p++) {
if ( !(x%density) ) { for (int32 x = lo_x; x <= hi_x; x++, p++) {
//Debug( 9, " %d", x ); if (!(x % density)) {
*p = colour; *p = colour;
} }
} }
} else if ( colours == ZM_COLOUR_RGB24 ) { } else if (colours == ZM_COLOUR_RGB24) {
unsigned char *p = &buffer[colours*((y*width)+lo_x)]; constexpr uint8 bytesPerPixel = 3;
for ( int x = lo_x; x <= hi_x; x++, p += 3) { uint8 *ptr = &buffer[((scan_line * width) + lo_x) * bytesPerPixel];
if ( !(x%density) ) {
RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); for (int32 x = lo_x; x <= hi_x; x++, ptr += bytesPerPixel) {
GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); if (!(x % density)) {
BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); RED_PTR_RGBA(ptr) = RED_VAL_RGBA(colour);
GREEN_PTR_RGBA(ptr) = GREEN_VAL_RGBA(colour);
BLUE_PTR_RGBA(ptr) = BLUE_VAL_RGBA(colour);
} }
} }
} else if( colours == ZM_COLOUR_RGB32 ) { } else if (colours == ZM_COLOUR_RGB32) {
Rgb *p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; constexpr uint8 bytesPerPixel = 4;
for ( int x = lo_x; x <= hi_x; x++, p++) { Rgb *ptr = reinterpret_cast<Rgb *>(&buffer[((scan_line * width) + lo_x) * bytesPerPixel]);
if ( !(x%density) ) {
/* Fast, copies the entire pixel in a single pass */ for (int32 x = lo_x; x <= hi_x; x++, ptr++) {
*p = colour; if (!(x % density)) {
*ptr = colour;
} }
} }
} }
} }
} }
y++;
for ( int i = n_active_edges-1; i >= 0; i-- ) { scan_line++;
if ( y >= active_edges[i].max_y ) { }
// Or >= as per sheets
Debug(9, "Deleting active_edge");
if ( i < (n_active_edges-1) ) {
//memcpy( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) );
memmove( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) );
}
n_active_edges--;
} else {
active_edges[i].min_x += active_edges[i]._1_m;
}
}
} while ( n_global_edges || n_active_edges );
} }
void Image::Fill(Rgb colour, const Polygon &polygon) { void Image::Fill(Rgb colour, const Polygon &polygon) {

View File

@ -278,8 +278,8 @@ class Image {
void Fill( Rgb colour, const Box *limits=0 ); void Fill( Rgb colour, const Box *limits=0 );
void Fill( Rgb colour, int density, const Box *limits=0 ); void Fill( Rgb colour, int density, const Box *limits=0 );
void Outline( Rgb colour, const Polygon &polygon ); void Outline( Rgb colour, const Polygon &polygon );
void Fill( Rgb colour, const Polygon &polygon ); void Fill(Rgb colour, const Polygon &polygon);
void Fill( Rgb colour, int density, const Polygon &polygon ); void Fill(Rgb colour, int density, const Polygon &polygon);
void Rotate( int angle ); void Rotate( int angle );
void Flip( bool leftright ); void Flip( bool leftright );

View File

@ -23,11 +23,10 @@
#include "zm_box.h" #include "zm_box.h"
#include <vector> #include <vector>
struct Edge { class Edge {
int min_y; public:
int max_y; Edge() = default;
double min_x; Edge(int32 min_y, int32 max_y, double min_x, double _1_m) : min_y(min_y), max_y(max_y), min_x(min_x), _1_m(_1_m) {}
double _1_m;
static bool CompareYX(const Edge &e1, const Edge &e2) { static bool CompareYX(const Edge &e1, const Edge &e2) {
if (e1.min_y == e2.min_y) if (e1.min_y == e2.min_y)
@ -38,6 +37,12 @@ struct Edge {
static bool CompareX(const Edge &e1, const Edge &e2) { static bool CompareX(const Edge &e1, const Edge &e2) {
return e1.min_x < e2.min_x; return e1.min_x < e2.min_x;
} }
public:
int32 min_y;
int32 max_y;
double min_x;
double _1_m;
}; };
// This class represents convex or concave non-self-intersecting polygons. // This class represents convex or concave non-self-intersecting polygons.