Image: Eliminate VLAs from polygon fill algorithm implementation
This commit is contained in:
parent
a335e740f3
commit
6642ca4515
146
src/zm_image.cpp
146
src/zm_image.cpp
|
@ -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[n_global_edges].min_y = y1<y2?y1:y2;
|
global_edges.emplace_back(std::min(p1.y_, p2.y_),
|
||||||
global_edges[n_global_edges].max_y = y1<y2?y2:y1;
|
std::max(p1.y_, p2.y_),
|
||||||
global_edges[n_global_edges].min_x = y1<y2?x1:x2;
|
p1.y_ < p2.y_ ? p1.x_ : p2.x_,
|
||||||
global_edges[n_global_edges]._1_m = dx/dy;
|
d.x_ / static_cast<double>(d.y_));
|
||||||
n_global_edges++;
|
|
||||||
}
|
|
||||||
std::sort(global_edges, global_edges + n_global_edges, Edge::CompareYX);
|
|
||||||
|
|
||||||
#ifndef ZM_DBG_OFF
|
|
||||||
if ( logLevel() >= Logger::DEBUG9 ) {
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
std::sort(global_edges.begin(), global_edges.end(), Edge::CompareYX);
|
||||||
#endif
|
|
||||||
|
|
||||||
int n_active_edges = 0;
|
std::vector<Edge> active_edges;
|
||||||
Edge active_edges[n_global_edges];
|
active_edges.reserve(global_edges.size());
|
||||||
int y = global_edges[0].min_y;
|
|
||||||
do {
|
int32 scan_line = global_edges[0].min_y;
|
||||||
for ( int i = 0; i < n_global_edges; i++ ) {
|
while (!global_edges.empty() || !active_edges.empty()) {
|
||||||
if ( global_edges[i].min_y == y ) {
|
// Deactivate edges with max_y < current scan line
|
||||||
Debug(9, "Moving global edge");
|
for (auto it = active_edges.begin(); it != active_edges.end();) {
|
||||||
active_edges[n_active_edges++] = global_edges[i];
|
if (scan_line >= it->max_y) {
|
||||||
if ( i < (n_global_edges-1) ) {
|
it = active_edges.erase(it);
|
||||||
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) {
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue