1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

EdgeTable: Fix buggy clipToRegion corner case

This commit is contained in:
attila 2024-08-05 14:17:41 +02:00 committed by reuk
parent b083d3c6d8
commit dda0719d56
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C

View file

@ -595,18 +595,15 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* const otherL
x2 = popHead (srcLine2);
}
if (lastX < nextX)
{
if (right <= nextX)
break;
lastX = nextX;
const auto nextLevel = (level1 * (level2 + 1)) / scale;
auto nextLevel = (level1 * (level2 + 1)) / scale;
if (std::exchange (lastX, nextX) < nextX || std::exchange (lastLevel, nextLevel) != nextLevel)
{
jassert (isPositiveAndBelow (nextLevel, 256));
if (nextLevel != lastLevel)
{
if (destTotal >= maxEdgesPerLine)
{
srcLine[0] = destTotal;
@ -644,7 +641,6 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* const otherL
srcLine[++destIndex] = nextLevel;
}
}
}
if (lastLevel > 0)
{
@ -866,4 +862,144 @@ bool EdgeTable::isEmpty() noexcept
JUCE_END_IGNORE_WARNINGS_MSVC
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class EdgeTableTests : public UnitTest
{
public:
EdgeTableTests() : UnitTest ("EdgeTable", UnitTestCategories::graphics) {}
void runTest() override
{
beginTest ("The result of clipToEdgeTable() shouldn't contain any point that's not present in both operands");
{
// The way this EdgeTable is constructed is significant in triggering a certain corner
// case.
const auto edgeTableContainingAPath = [&]
{
RectangleList<int> rl;
rl.add (Rectangle<int> (6, 1, 1, 4));
rl.add (Rectangle<int> (1, 1, 5, 5));
EdgeTable rectListEdgeTable { rl };
Path p;
p.startNewSubPath (2.0f, 6.0f);
p.lineTo (2.0f, 1.0f);
p.lineTo (6.0f, 1.0f);
p.lineTo (6.0f, 6.0f);
p.closeSubPath();
const EdgeTable pathEdgeTable { Rectangle<int> { 1, 1, 6, 5 }, p, {} };
rectListEdgeTable.clipToEdgeTable (pathEdgeTable);
return rectListEdgeTable;
}();
const EdgeTable edgeTableFromRectangle (Rectangle<float> (1.0f, 1.0f, 6.0f, 5.0f));
const auto intersection = [&]
{
auto result = edgeTableFromRectangle;
result.clipToEdgeTable (edgeTableContainingAPath);
return result;
}();
expect (! contains (edgeTableContainingAPath, { 6, 2 }),
"The path doesn't enclose the point (6, 2) so it's EdgeTable shouldn't contain it");
expect (contains (edgeTableFromRectangle, { 6, 2 }),
"The Rectangle covers the point (6, 2) so it's EdgeTable should contain it");
expect (! contains (intersection, { 6, 2 }),
"The intersecting EdgeTable shouldn't contain (6, 2) because one of its constituents doesn't contain it either");
}
}
private:
class EdgeTableFiller
{
public:
EdgeTableFiller (int w, int h)
: width (w), height (h), data ((size_t) (w * h))
{
}
void setEdgeTableYPos (int yIn)
{
y = yIn;
}
void handleEdgeTablePixelFull (int x)
{
if (! (y < height && x < width))
return;
auto* ptr = data.data() + width * y + x;
*ptr = 1;
}
void handleEdgeTablePixel (int x, int)
{
handleEdgeTablePixelFull (x);
}
void handleEdgeTableLineFull (int x, int w)
{
if (! (y < height && x < width))
return;
auto* ptr = data.data() + width * y + x;
std::fill (ptr, ptr + std::min (w, width - x), 1);
}
void handleEdgeTableLine (int x, int w, int)
{
handleEdgeTableLineFull (x, w);
}
void handleEdgeTableRectangleFull (int x, int yIn, int w, int h) noexcept
{
for (int j = yIn; j < std::min (yIn + h, height); ++j)
{
auto* ptr = data.data() + width * j + x;
std::fill (ptr, ptr + std::min (w, width - x), 1);
}
}
void handleEdgeTableRectangle (int x, int yIn, int w, int h, int) noexcept
{
handleEdgeTableRectangleFull (x, yIn, w, h);
}
int get (int x, int yIn) const
{
const auto index = (size_t) (width * yIn + x);
if (index >= data.size())
return 0;
return data[index];
}
private:
const int width, height = 0;
std::vector<int> data;
int y = 0;
};
static bool contains (const EdgeTable& et, Point<int> p)
{
EdgeTableFiller filler { p.getX() + 2, p.getY() + 2 };
et.iterate (filler);
return filler.get (p.getX(), p.getY()) == 1;
}
};
static EdgeTableTests edgeTableTests;
#endif
} // namespace juce