mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 08:47:34 +00:00
LibGfx: Track edge extent in EdgeFlagPathRasterizer
This allows avoiding accumulating values we know will be zero, this is not a huge speedup but does shave off a few %.
This commit is contained in:
parent
ec24d7555a
commit
2a914a7a59
2 changed files with 44 additions and 24 deletions
|
@ -141,7 +141,11 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::fill_internal(Painter& painter, Pa
|
||||||
// FIXME: We could probably clip some of the egde plotting if we know it won't be shown.
|
// FIXME: We could probably clip some of the egde plotting if we know it won't be shown.
|
||||||
// Though care would have to be taken to ensure the active edges are correct at the first drawn scaline.
|
// Though care would have to be taken to ensure the active edges are correct at the first drawn scaline.
|
||||||
|
|
||||||
auto for_each_sample = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y, auto callback) {
|
auto empty_edge_extent = [&] {
|
||||||
|
return EdgeExtent { m_size.width() - 1, 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
auto for_each_sample = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y, EdgeExtent& edge_extent, auto callback) {
|
||||||
for (int y = start_subpixel_y; y < end_subpixel_y; y++) {
|
for (int y = start_subpixel_y; y < end_subpixel_y; y++) {
|
||||||
int xi = static_cast<int>(edge.x + SubpixelSample::nrooks_subpixel_offsets[y]);
|
int xi = static_cast<int>(edge.x + SubpixelSample::nrooks_subpixel_offsets[y]);
|
||||||
if (xi < 0 || xi >= (int)m_scanline.size()) {
|
if (xi < 0 || xi >= (int)m_scanline.size()) {
|
||||||
|
@ -154,20 +158,23 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::fill_internal(Painter& painter, Pa
|
||||||
SampleType sample = 1 << y;
|
SampleType sample = 1 << y;
|
||||||
callback(xi, y, sample);
|
callback(xi, y, sample);
|
||||||
edge.x += edge.dxdy;
|
edge.x += edge.dxdy;
|
||||||
|
edge_extent.min_x = min(edge_extent.min_x, xi);
|
||||||
|
edge_extent.max_x = max(edge_extent.max_x, xi);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Detail::Edge* active_edges = nullptr;
|
Detail::Edge* active_edges = nullptr;
|
||||||
|
|
||||||
if (winding_rule == Painter::WindingRule::EvenOdd) {
|
if (winding_rule == Painter::WindingRule::EvenOdd) {
|
||||||
auto plot_edge = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y) {
|
auto plot_edge = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y, EdgeExtent& edge_extent) {
|
||||||
for_each_sample(edge, start_subpixel_y, end_subpixel_y, [&](int xi, int, SampleType sample) {
|
for_each_sample(edge, start_subpixel_y, end_subpixel_y, edge_extent, [&](int xi, int, SampleType sample) {
|
||||||
m_scanline[xi] ^= sample;
|
m_scanline[xi] ^= sample;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
for (int scanline = min_scanline; scanline <= max_scanline; scanline++) {
|
for (int scanline = min_scanline; scanline <= max_scanline; scanline++) {
|
||||||
active_edges = plot_edges_for_scanline(scanline, plot_edge, active_edges);
|
auto edge_extent = empty_edge_extent();
|
||||||
accumulate_even_odd_scanline(painter, scanline, color_or_function);
|
active_edges = plot_edges_for_scanline(scanline, plot_edge, edge_extent, active_edges);
|
||||||
|
accumulate_even_odd_scanline(painter, scanline, edge_extent, color_or_function);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
VERIFY(winding_rule == Painter::WindingRule::Nonzero);
|
VERIFY(winding_rule == Painter::WindingRule::Nonzero);
|
||||||
|
@ -176,15 +183,16 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::fill_internal(Painter& painter, Pa
|
||||||
if (m_windings.is_empty())
|
if (m_windings.is_empty())
|
||||||
m_windings.resize(m_size.width());
|
m_windings.resize(m_size.width());
|
||||||
|
|
||||||
auto plot_edge = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y) {
|
auto plot_edge = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y, EdgeExtent& edge_extent) {
|
||||||
for_each_sample(edge, start_subpixel_y, end_subpixel_y, [&](int xi, int y, SampleType sample) {
|
for_each_sample(edge, start_subpixel_y, end_subpixel_y, edge_extent, [&](int xi, int y, SampleType sample) {
|
||||||
m_scanline[xi] |= sample;
|
m_scanline[xi] |= sample;
|
||||||
m_windings[xi].counts[y] += edge.winding;
|
m_windings[xi].counts[y] += edge.winding;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
for (int scanline = min_scanline; scanline <= max_scanline; scanline++) {
|
for (int scanline = min_scanline; scanline <= max_scanline; scanline++) {
|
||||||
active_edges = plot_edges_for_scanline(scanline, plot_edge, active_edges);
|
auto edge_extent = empty_edge_extent();
|
||||||
accumulate_non_zero_scanline(painter, scanline, color_or_function);
|
active_edges = plot_edges_for_scanline(scanline, plot_edge, edge_extent, active_edges);
|
||||||
|
accumulate_non_zero_scanline(painter, scanline, edge_extent, color_or_function);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +213,7 @@ Color EdgeFlagPathRasterizer<SamplesPerPixel>::scanline_color(int scanline, int
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned SamplesPerPixel>
|
template<unsigned SamplesPerPixel>
|
||||||
Detail::Edge* EdgeFlagPathRasterizer<SamplesPerPixel>::plot_edges_for_scanline(int scanline, auto plot_edge, Detail::Edge* active_edges)
|
Detail::Edge* EdgeFlagPathRasterizer<SamplesPerPixel>::plot_edges_for_scanline(int scanline, auto plot_edge, EdgeExtent& edge_extent, Detail::Edge* active_edges)
|
||||||
{
|
{
|
||||||
auto y_subpixel = [](int y) {
|
auto y_subpixel = [](int y) {
|
||||||
return y & (SamplesPerPixel - 1);
|
return y & (SamplesPerPixel - 1);
|
||||||
|
@ -220,7 +228,7 @@ Detail::Edge* EdgeFlagPathRasterizer<SamplesPerPixel>::plot_edges_for_scanline(i
|
||||||
int end_scanline = current_edge->max_y / SamplesPerPixel;
|
int end_scanline = current_edge->max_y / SamplesPerPixel;
|
||||||
if (scanline == end_scanline) {
|
if (scanline == end_scanline) {
|
||||||
// This edge ends this scanline.
|
// This edge ends this scanline.
|
||||||
plot_edge(*current_edge, 0, y_subpixel(current_edge->max_y));
|
plot_edge(*current_edge, 0, y_subpixel(current_edge->max_y), edge_extent);
|
||||||
// Remove this edge from the AET
|
// Remove this edge from the AET
|
||||||
current_edge = current_edge->next_edge;
|
current_edge = current_edge->next_edge;
|
||||||
if (prev_edge)
|
if (prev_edge)
|
||||||
|
@ -229,7 +237,7 @@ Detail::Edge* EdgeFlagPathRasterizer<SamplesPerPixel>::plot_edges_for_scanline(i
|
||||||
active_edges = current_edge;
|
active_edges = current_edge;
|
||||||
} else {
|
} else {
|
||||||
// This egde sticks around for a few more scanlines.
|
// This egde sticks around for a few more scanlines.
|
||||||
plot_edge(*current_edge, 0, SamplesPerPixel);
|
plot_edge(*current_edge, 0, SamplesPerPixel, edge_extent);
|
||||||
prev_edge = current_edge;
|
prev_edge = current_edge;
|
||||||
current_edge = current_edge->next_edge;
|
current_edge = current_edge->next_edge;
|
||||||
}
|
}
|
||||||
|
@ -242,10 +250,10 @@ Detail::Edge* EdgeFlagPathRasterizer<SamplesPerPixel>::plot_edges_for_scanline(i
|
||||||
int end_scanline = current_edge->max_y / SamplesPerPixel;
|
int end_scanline = current_edge->max_y / SamplesPerPixel;
|
||||||
if (scanline == end_scanline) {
|
if (scanline == end_scanline) {
|
||||||
// This edge will end this scanlines (no need to add to AET).
|
// This edge will end this scanlines (no need to add to AET).
|
||||||
plot_edge(*current_edge, y_subpixel(current_edge->min_y), y_subpixel(current_edge->max_y));
|
plot_edge(*current_edge, y_subpixel(current_edge->min_y), y_subpixel(current_edge->max_y), edge_extent);
|
||||||
} else {
|
} else {
|
||||||
// This edge will live on for a few more scanlines.
|
// This edge will live on for a few more scanlines.
|
||||||
plot_edge(*current_edge, y_subpixel(current_edge->min_y), SamplesPerPixel);
|
plot_edge(*current_edge, y_subpixel(current_edge->min_y), SamplesPerPixel, edge_extent);
|
||||||
// Add this edge to the AET
|
// Add this edge to the AET
|
||||||
if (prev_edge)
|
if (prev_edge)
|
||||||
prev_edge->next_edge = current_edge;
|
prev_edge->next_edge = current_edge;
|
||||||
|
@ -278,18 +286,18 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::write_pixel(Painter& painter, int
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned SamplesPerPixel>
|
template<unsigned SamplesPerPixel>
|
||||||
void EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_even_odd_scanline(Painter& painter, int scanline, auto& color_or_function)
|
void EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_even_odd_scanline(Painter& painter, int scanline, EdgeExtent edge_extent, auto& color_or_function)
|
||||||
{
|
{
|
||||||
auto dest_y = m_blit_origin.y() + scanline;
|
auto dest_y = m_blit_origin.y() + scanline;
|
||||||
if (!m_clip.contains_vertically(dest_y)) {
|
if (!m_clip.contains_vertically(dest_y)) {
|
||||||
// FIXME: This memset only really needs to be done on transition from clipped to not clipped,
|
// FIXME: This memset only really needs to be done on transition from clipped to not clipped,
|
||||||
// or not at all if we properly clipped egde plotting.
|
// or not at all if we properly clipped egde plotting.
|
||||||
memset(m_scanline.data(), 0, sizeof(SampleType) * m_scanline.size());
|
edge_extent.memset_extent(m_scanline.data(), 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleType sample = 0;
|
SampleType sample = 0;
|
||||||
for (int x = 0; x < m_size.width(); x += 1) {
|
for (int x = edge_extent.min_x; x <= edge_extent.max_x; x += 1) {
|
||||||
sample ^= m_scanline[x];
|
sample ^= m_scanline[x];
|
||||||
write_pixel(painter, scanline, x, sample, color_or_function);
|
write_pixel(painter, scanline, x, sample, color_or_function);
|
||||||
m_scanline[x] = 0;
|
m_scanline[x] = 0;
|
||||||
|
@ -297,19 +305,19 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_even_odd_scanline(Paint
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned SamplesPerPixel>
|
template<unsigned SamplesPerPixel>
|
||||||
void EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_non_zero_scanline(Painter& painter, int scanline, auto& color_or_function)
|
void EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_non_zero_scanline(Painter& painter, int scanline, EdgeExtent edge_extent, auto& color_or_function)
|
||||||
{
|
{
|
||||||
// NOTE: Same FIXMEs apply from accumulate_even_odd_scanline()
|
// NOTE: Same FIXMEs apply from accumulate_even_odd_scanline()
|
||||||
auto dest_y = m_blit_origin.y() + scanline;
|
auto dest_y = m_blit_origin.y() + scanline;
|
||||||
if (!m_clip.contains_vertically(dest_y)) {
|
if (!m_clip.contains_vertically(dest_y)) {
|
||||||
memset(m_scanline.data(), 0, sizeof(SampleType) * m_scanline.size());
|
edge_extent.memset_extent(m_scanline.data(), 0);
|
||||||
memset(m_windings.data(), 0, sizeof(WindingCounts) * m_windings.size());
|
edge_extent.memset_extent(m_windings.data(), 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleType sample = 0;
|
SampleType sample = 0;
|
||||||
WindingCounts sum_winding = {};
|
WindingCounts sum_winding = {};
|
||||||
for (int x = 0; x < m_size.width(); x += 1) {
|
for (int x = edge_extent.min_x; x <= edge_extent.max_x; x += 1) {
|
||||||
if (auto edges = m_scanline[x]) {
|
if (auto edges = m_scanline[x]) {
|
||||||
// We only need to process the windings when we hit some edges.
|
// We only need to process the windings when we hit some edges.
|
||||||
for (auto y_sub = 0u; y_sub < SamplesPerPixel; y_sub++) {
|
for (auto y_sub = 0u; y_sub < SamplesPerPixel; y_sub++) {
|
||||||
|
|
|
@ -160,10 +160,22 @@ private:
|
||||||
return (coverage << alpha_shift) - 1;
|
return (coverage << alpha_shift) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct EdgeExtent {
|
||||||
|
int min_x;
|
||||||
|
int max_x;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void memset_extent(T* data, int value)
|
||||||
|
{
|
||||||
|
if (min_x <= max_x)
|
||||||
|
memset(data + min_x, value, (max_x - min_x + 1) * sizeof(T));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void fill_internal(Painter&, Path const&, auto color_or_function, Painter::WindingRule, FloatPoint offset);
|
void fill_internal(Painter&, Path const&, auto color_or_function, Painter::WindingRule, FloatPoint offset);
|
||||||
Detail::Edge* plot_edges_for_scanline(int scanline, auto plot_edge, Detail::Edge* active_edges = nullptr);
|
Detail::Edge* plot_edges_for_scanline(int scanline, auto plot_edge, EdgeExtent&, Detail::Edge* active_edges = nullptr);
|
||||||
void accumulate_even_odd_scanline(Painter&, int scanline, auto& color_or_function);
|
void accumulate_even_odd_scanline(Painter&, int scanline, EdgeExtent, auto& color_or_function);
|
||||||
void accumulate_non_zero_scanline(Painter&, int scanline, auto& color_or_function);
|
void accumulate_non_zero_scanline(Painter&, int scanline, EdgeExtent, auto& color_or_function);
|
||||||
Color scanline_color(int scanline, int offset, u8 alpha, auto& color_or_function);
|
Color scanline_color(int scanline, int offset, u8 alpha, auto& color_or_function);
|
||||||
void write_pixel(Painter&, int scanline, int offset, SampleType sample, auto& color_or_function);
|
void write_pixel(Painter&, int scanline, int offset, SampleType sample, auto& color_or_function);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue