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

EdgeTable: Fix issue where edges of paths could be anti-aliased incorrectly at edges of clip regions

This commit is contained in:
reuk 2025-02-03 13:57:14 +00:00
parent 56ea531298
commit 19edd53842
No known key found for this signature in database
69 changed files with 73 additions and 30 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 912 B

After

Width:  |  Height:  |  Size: 912 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Before After
Before After

View file

@ -884,7 +884,13 @@ public:
{ {
beginTest ("Render image subsection"); beginTest ("Render image subsection");
{ {
renderImageSubsection (NativeImageType{}, NativeImageType{}); const SoftwareImageType softwareImageType;
const NativeImageType nativeImageType;
const ImageType* types[] { &softwareImageType, &nativeImageType };
for (auto* sourceType : types)
for (auto* targetType : types)
renderImageSubsection (*sourceType, *targetType);
} }
} }
@ -904,6 +910,8 @@ private:
{ {
// Render the subsection image so that it fills 'target' // Render the subsection image so that it fills 'target'
Graphics g { target }; Graphics g { target };
// Use low resampling quality, because we want to avoid our pixel getting blurry when it's scaled up
g.setImageResamplingQuality (Graphics::lowResamplingQuality);
g.drawImage (subsection, g.drawImage (subsection,
0, 0, target.getWidth(), target.getHeight(), 0, 0, target.getWidth(), target.getHeight(),
0, 0, 1, 1); 0, 0, 1, 1);

View file

@ -102,7 +102,7 @@ EdgeTable::EdgeTable (Rectangle<int> area, const Path& path, const AffineTransfo
{ {
auto step = jmin (stepSize, y2 - y1, 256 - (y1 & 255)); auto step = jmin (stepSize, y2 - y1, 256 - (y1 & 255));
auto x = static_cast<int64_t> (startX + multiplier * static_cast<double> ((y1 + (step >> 1)) - startY)); auto x = static_cast<int64_t> (startX + multiplier * static_cast<double> ((y1 + (step >> 1)) - startY));
auto clampedX = static_cast<int> (jlimit (leftLimit, rightLimit - 1, x)); auto clampedX = static_cast<int> (jlimit (leftLimit, rightLimit, x));
addEdgePoint (clampedX, static_cast<int> (y1 / scale), static_cast<int> (direction * step)); addEdgePoint (clampedX, static_cast<int> (y1 / scale), static_cast<int> (direction * step));
y1 += step; y1 += step;
@ -885,14 +885,44 @@ public:
}(); }();
expect (! contains (edgeTableContainingAPath, { 6, 2 }), expect (! contains (edgeTableContainingAPath, { 6, 2 }),
"The path doesn't enclose the point (6, 2) so it's EdgeTable shouldn't contain it"); "The path doesn't enclose the point (6, 2) so its EdgeTable shouldn't contain it");
expect (contains (edgeTableFromRectangle, { 6, 2 }), expect (contains (edgeTableFromRectangle, { 6, 2 }),
"The Rectangle covers the point (6, 2) so it's EdgeTable should contain it"); "The Rectangle covers the point (6, 2) so its EdgeTable should contain it");
expect (! contains (intersection, { 6, 2 }), expect (! contains (intersection, { 6, 2 }),
"The intersecting EdgeTable shouldn't contain (6, 2) because one of its constituents doesn't contain it either"); "The intersecting EdgeTable shouldn't contain (6, 2) because one of its constituents doesn't contain it either");
} }
beginTest ("An EdgeTable constructed from a pixel-aligned Rectangle should not anti-alias");
{
Rectangle<int> area { 5, 5 };
Path p;
p.addRectangle (area.reduced (1));
EdgeTable bordered { area, p, {} };
// Pixels at edges should be clear
expect (getLevel (bordered, { 0, 0 }) == 0);
expect (getLevel (bordered, { 0, 4 }) == 0);
expect (getLevel (bordered, { 4, 0 }) == 0);
expect (getLevel (bordered, { 4, 4 }) == 0);
// Corners of filled area should have max level
expect (getLevel (bordered, { 1, 1 }) == 255);
expect (getLevel (bordered, { 1, 3 }) == 255);
expect (getLevel (bordered, { 3, 1 }) == 255);
expect (getLevel (bordered, { 3, 3 }) == 255);
Path q;
q.addRectangle (area);
EdgeTable filled { area, q, {} };
// Pixels at edges should have max level
expect (getLevel (filled, { 0, 0 }) == 255);
expect (getLevel (filled, { 0, 4 }) == 255);
expect (getLevel (filled, { 4, 0 }) == 255);
expect (getLevel (filled, { 4, 4 }) == 255);
}
} }
private: private:
@ -910,48 +940,48 @@ private:
} }
void handleEdgeTablePixelFull (int x) void handleEdgeTablePixelFull (int x)
{
handleEdgeTablePixel (x, 255);
}
void handleEdgeTablePixel (int x, uint8_t level)
{ {
if (! (y < height && x < width)) if (! (y < height && x < width))
return; return;
auto* ptr = data.data() + width * y + x; auto* ptr = data.data() + width * y + x;
*ptr = 1; *ptr = level;
}
void handleEdgeTablePixel (int x, int)
{
handleEdgeTablePixelFull (x);
} }
void handleEdgeTableLineFull (int x, int w) void handleEdgeTableLineFull (int x, int w)
{
handleEdgeTableLine (x, w, 255);
}
void handleEdgeTableLine (int x, int w, uint8_t level)
{ {
if (! (y < height && x < width)) if (! (y < height && x < width))
return; return;
auto* ptr = data.data() + width * y + x; auto* ptr = data.data() + width * y + x;
std::fill (ptr, ptr + std::min (w, width - x), 1); std::fill (ptr, ptr + std::min (w, width - x), level);
}
void handleEdgeTableLine (int x, int w, int)
{
handleEdgeTableLineFull (x, w);
} }
void handleEdgeTableRectangleFull (int x, int yIn, int w, int h) noexcept void handleEdgeTableRectangleFull (int x, int yIn, int w, int h) noexcept
{
handleEdgeTableRectangle (x, yIn, w, h, 255);
}
void handleEdgeTableRectangle (int x, int yIn, int w, int h, uint8_t level) noexcept
{ {
for (int j = yIn; j < std::min (yIn + h, height); ++j) for (int j = yIn; j < std::min (yIn + h, height); ++j)
{ {
auto* ptr = data.data() + width * j + x; auto* ptr = data.data() + width * j + x;
std::fill (ptr, ptr + std::min (w, width - x), 1); std::fill (ptr, ptr + std::min (w, width - x), level);
} }
} }
void handleEdgeTableRectangle (int x, int yIn, int w, int h, int) noexcept uint8_t get (int x, int yIn) const
{
handleEdgeTableRectangleFull (x, yIn, w, h);
}
int get (int x, int yIn) const
{ {
const auto index = (size_t) (width * yIn + x); const auto index = (size_t) (width * yIn + x);
@ -963,17 +993,22 @@ private:
private: private:
const int width, height = 0; const int width, height = 0;
std::vector<int> data; std::vector<uint8_t> data;
int y = 0; int y = 0;
}; };
static bool contains (const EdgeTable& et, Point<int> p) static uint8_t getLevel (const EdgeTable& et, Point<int> p)
{ {
EdgeTableFiller filler { p.getX() + 2, p.getY() + 2 }; EdgeTableFiller filler { p.getX() + 2, p.getY() + 2 };
et.iterate (filler); et.iterate (filler);
return filler.get (p.getX(), p.getY());
return filler.get (p.getX(), p.getY()) == 1;
} }
static bool contains (const EdgeTable& et, Point<int> p)
{
return getLevel (et, p) == 255;
}
}; };
static EdgeTableTests edgeTableTests; static EdgeTableTests edgeTableTests;

View file

@ -154,7 +154,7 @@ public:
if (levelAccumulator >= 255) if (levelAccumulator >= 255)
iterationCallback.handleEdgeTablePixelFull (x); iterationCallback.handleEdgeTablePixelFull (x);
else else
iterationCallback.handleEdgeTablePixel (x, levelAccumulator); iterationCallback.handleEdgeTablePixel (x, static_cast<uint8_t> (levelAccumulator));
} }
// if there's a run of similar pixels, do it all in one go.. // if there's a run of similar pixels, do it all in one go..
@ -164,7 +164,7 @@ public:
const int numPix = endOfRun - ++x; const int numPix = endOfRun - ++x;
if (numPix > 0) if (numPix > 0)
iterationCallback.handleEdgeTableLine (x, numPix, level); iterationCallback.handleEdgeTableLine (x, numPix, static_cast<uint8_t> (level));
} }
// save the bit at the end to be drawn next time round the loop. // save the bit at the end to be drawn next time round the loop.
@ -184,7 +184,7 @@ public:
if (levelAccumulator >= 255) if (levelAccumulator >= 255)
iterationCallback.handleEdgeTablePixelFull (x); iterationCallback.handleEdgeTablePixelFull (x);
else else
iterationCallback.handleEdgeTablePixel (x, levelAccumulator); iterationCallback.handleEdgeTablePixel (x, static_cast<uint8_t> (levelAccumulator));
} }
} }
} }