mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 20:07:36 +00:00
LibGfx: Clip path rasterizer scanlines sooner
This moves the clipping from per-pixel/draw command to a step before the main drawing/plotting loop.
This commit is contained in:
parent
b1adabdac4
commit
e2152a5b3d
2 changed files with 77 additions and 37 deletions
|
@ -235,6 +235,8 @@ Color EdgeFlagPathRasterizer<SamplesPerPixel>::scanline_color(int scanline, int
|
||||||
[&](auto& function) {
|
[&](auto& function) {
|
||||||
return function({ offset, scanline });
|
return function({ offset, scanline });
|
||||||
});
|
});
|
||||||
|
if (color.alpha() == 255)
|
||||||
|
return color.with_alpha(alpha);
|
||||||
return color.with_alpha(color.alpha() * alpha / 255);
|
return color.with_alpha(color.alpha() * alpha / 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,21 +300,21 @@ Detail::Edge* EdgeFlagPathRasterizer<SamplesPerPixel>::plot_edges_for_scanline(i
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned SamplesPerPixel>
|
template<unsigned SamplesPerPixel>
|
||||||
void EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_even_odd_scanline(EdgeExtent edge_extent, auto sample_callback)
|
auto EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_even_odd_scanline(EdgeExtent edge_extent, auto init, auto sample_callback)
|
||||||
{
|
{
|
||||||
SampleType sample = 0;
|
SampleType sample = init;
|
||||||
for (int x = edge_extent.min_x; x <= edge_extent.max_x; x += 1) {
|
for (int x = edge_extent.min_x; x <= edge_extent.max_x; x += 1) {
|
||||||
sample ^= m_scanline[x];
|
sample ^= m_scanline[x];
|
||||||
sample_callback(x, sample);
|
sample_callback(x, sample);
|
||||||
m_scanline[x] = 0;
|
m_scanline[x] = 0;
|
||||||
}
|
}
|
||||||
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned SamplesPerPixel>
|
template<unsigned SamplesPerPixel>
|
||||||
void EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_non_zero_scanline(EdgeExtent edge_extent, auto sample_callback)
|
auto EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_non_zero_scanline(EdgeExtent edge_extent, auto init, auto sample_callback)
|
||||||
{
|
{
|
||||||
SampleType sample = 0;
|
NonZeroAcc acc = init;
|
||||||
WindingCounts sum_winding = {};
|
|
||||||
for (int x = edge_extent.min_x; x <= edge_extent.max_x; 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.
|
||||||
|
@ -320,64 +322,81 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_non_zero_scanline(EdgeE
|
||||||
auto subpixel_bit = 1 << y_sub;
|
auto subpixel_bit = 1 << y_sub;
|
||||||
if (edges & subpixel_bit) {
|
if (edges & subpixel_bit) {
|
||||||
auto winding = m_windings[x].counts[y_sub];
|
auto winding = m_windings[x].counts[y_sub];
|
||||||
auto previous_winding_count = sum_winding.counts[y_sub];
|
auto previous_winding_count = acc.winding.counts[y_sub];
|
||||||
sum_winding.counts[y_sub] += winding;
|
acc.winding.counts[y_sub] += winding;
|
||||||
// Toggle fill on change to/from zero.
|
// Toggle fill on change to/from zero.
|
||||||
if (bool(previous_winding_count) ^ bool(sum_winding.counts[y_sub]))
|
if (bool(previous_winding_count) ^ bool(acc.winding.counts[y_sub]))
|
||||||
sample ^= subpixel_bit;
|
acc.sample ^= subpixel_bit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sample_callback(x, sample);
|
sample_callback(x, acc.sample);
|
||||||
m_scanline[x] = 0;
|
m_scanline[x] = 0;
|
||||||
m_windings[x] = {};
|
m_windings[x] = {};
|
||||||
}
|
}
|
||||||
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned SamplesPerPixel>
|
template<unsigned SamplesPerPixel>
|
||||||
template<Painter::WindingRule WindingRule, typename Callback>
|
template<Painter::WindingRule WindingRule, typename Callback>
|
||||||
void EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_scanline(EdgeExtent edge_extent, Callback callback)
|
auto EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_scanline(EdgeExtent edge_extent, auto init, Callback callback)
|
||||||
{
|
{
|
||||||
if constexpr (WindingRule == Painter::WindingRule::EvenOdd)
|
if constexpr (WindingRule == Painter::WindingRule::EvenOdd)
|
||||||
accumulate_even_odd_scanline(edge_extent, callback);
|
return accumulate_even_odd_scanline(edge_extent, init, callback);
|
||||||
else
|
else
|
||||||
accumulate_non_zero_scanline(edge_extent, callback);
|
return accumulate_non_zero_scanline(edge_extent, init, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned SamplesPerPixel>
|
template<unsigned SamplesPerPixel>
|
||||||
void EdgeFlagPathRasterizer<SamplesPerPixel>::write_pixel(Painter& painter, int scanline, int offset, SampleType sample, auto& color_or_function)
|
void EdgeFlagPathRasterizer<SamplesPerPixel>::write_pixel(BitmapFormat format, ARGB32* scanline_ptr, int scanline, int offset, SampleType sample, auto& color_or_function)
|
||||||
{
|
{
|
||||||
if (!sample)
|
if (!sample)
|
||||||
return;
|
return;
|
||||||
auto dest = IntPoint { offset, scanline } + m_blit_origin;
|
auto dest_x = offset + m_blit_origin.x();
|
||||||
if (!m_clip.contains_horizontally(dest.x()))
|
|
||||||
return;
|
|
||||||
auto coverage = SubpixelSample::compute_coverage(sample);
|
auto coverage = SubpixelSample::compute_coverage(sample);
|
||||||
auto paint_color = scanline_color(scanline, offset, coverage_to_alpha(coverage), color_or_function);
|
auto paint_color = scanline_color(scanline, offset, coverage_to_alpha(coverage), color_or_function);
|
||||||
painter.set_physical_pixel(dest, paint_color, true);
|
scanline_ptr[dest_x] = color_for_format(format, scanline_ptr[dest_x]).blend(paint_color).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned SamplesPerPixel>
|
template<unsigned SamplesPerPixel>
|
||||||
void EdgeFlagPathRasterizer<SamplesPerPixel>::fast_fill_solid_color_span(Painter& painter, int scanline, int start, int end, Color color)
|
void EdgeFlagPathRasterizer<SamplesPerPixel>::fast_fill_solid_color_span(ARGB32* scanline_ptr, int start, int end, Color color)
|
||||||
{
|
{
|
||||||
auto dest_y = scanline + m_blit_origin.y();
|
auto start_x = start + m_blit_origin.x();
|
||||||
auto start_x = max(m_clip.left(), start + m_blit_origin.x());
|
auto end_x = end + m_blit_origin.x();
|
||||||
auto end_x = min(m_clip.right() - 1, end + m_blit_origin.x());
|
fast_u32_fill(scanline_ptr + start_x, color.value(), end_x - start_x + 1);
|
||||||
if (start_x > end_x)
|
|
||||||
return;
|
|
||||||
auto* dest_ptr = painter.target()->scanline(dest_y) + start_x;
|
|
||||||
fast_u32_fill(dest_ptr, color.value(), end_x - start_x + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned SamplesPerPixel>
|
template<unsigned SamplesPerPixel>
|
||||||
template<Painter::WindingRule WindingRule>
|
template<Painter::WindingRule WindingRule>
|
||||||
void EdgeFlagPathRasterizer<SamplesPerPixel>::write_scanline(Painter& painter, int scanline, EdgeExtent edge_extent, auto& color_or_function)
|
void EdgeFlagPathRasterizer<SamplesPerPixel>::write_scanline(Painter& painter, int scanline, EdgeExtent edge_extent, auto& color_or_function)
|
||||||
{
|
{
|
||||||
|
// Handle scanline clipping.
|
||||||
|
auto left_clip = m_clip.left() - m_blit_origin.x();
|
||||||
|
auto right_clip = m_clip.right() - m_blit_origin.x() - 1;
|
||||||
|
EdgeExtent clipped_extent { max(left_clip, edge_extent.min_x), min(right_clip, edge_extent.max_x) };
|
||||||
|
if (clipped_extent.min_x > clipped_extent.max_x) {
|
||||||
|
// Fully clipped. Unfortunately we still need to zero the scanline data (before the right clip).
|
||||||
|
EdgeExtent zero_extent { edge_extent.min_x, clipped_extent.max_x };
|
||||||
|
zero_extent.memset_extent(m_scanline.data(), 0);
|
||||||
|
if constexpr (WindingRule == Painter::WindingRule::Nonzero)
|
||||||
|
zero_extent.memset_extent(m_windings.data(), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate non-visible section (without plotting pixels).
|
||||||
|
auto acc = accumulate_scanline<WindingRule>(EdgeExtent { edge_extent.min_x, left_clip - 1 }, initial_acc<WindingRule>(), [](int, SampleType) {
|
||||||
|
// Do nothing!
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get pointer to current scanline pixels.
|
||||||
|
auto dest_format = painter.target()->format();
|
||||||
|
auto dest_ptr = painter.target()->scanline(scanline + m_blit_origin.y());
|
||||||
|
|
||||||
// Simple case: Handle each pixel individually.
|
// Simple case: Handle each pixel individually.
|
||||||
// Used for PaintStyle fills and semi-transparent colors.
|
// Used for PaintStyle fills and semi-transparent colors.
|
||||||
auto write_scanline_pixelwise = [&](auto& color_or_function) {
|
auto write_scanline_pixelwise = [&](auto& color_or_function) {
|
||||||
accumulate_scanline<WindingRule>(edge_extent, [&](int x, SampleType sample) {
|
accumulate_scanline<WindingRule>(clipped_extent, acc, [&](int x, SampleType sample) {
|
||||||
write_pixel(painter, scanline, x, sample, color_or_function);
|
write_pixel(dest_format, dest_ptr, scanline, x, sample, color_or_function);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
// Fast fill case: Track spans of solid color and set the entire span via a fast_u32_fill().
|
// Fast fill case: Track spans of solid color and set the entire span via a fast_u32_fill().
|
||||||
|
@ -387,20 +406,20 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::write_scanline(Painter& painter, i
|
||||||
return write_scanline_pixelwise(color);
|
return write_scanline_pixelwise(color);
|
||||||
constexpr SampleType full_converage = NumericLimits<SampleType>::max();
|
constexpr SampleType full_converage = NumericLimits<SampleType>::max();
|
||||||
int full_converage_count = 0;
|
int full_converage_count = 0;
|
||||||
accumulate_scanline<WindingRule>(edge_extent, [&](int x, SampleType sample) {
|
accumulate_scanline<WindingRule>(clipped_extent, acc, [&](int x, SampleType sample) {
|
||||||
if (sample == full_converage) {
|
if (sample == full_converage) {
|
||||||
full_converage_count++;
|
full_converage_count++;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
write_pixel(painter, scanline, x, sample, color);
|
write_pixel(dest_format, dest_ptr, scanline, x, sample, color);
|
||||||
}
|
}
|
||||||
if (full_converage_count > 0) {
|
if (full_converage_count > 0) {
|
||||||
fast_fill_solid_color_span(painter, scanline, x - full_converage_count, x - 1, color);
|
fast_fill_solid_color_span(dest_ptr, x - full_converage_count, x - 1, color);
|
||||||
full_converage_count = 0;
|
full_converage_count = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (full_converage_count > 0)
|
if (full_converage_count > 0)
|
||||||
fast_fill_solid_color_span(painter, scanline, edge_extent.max_x - full_converage_count + 1, edge_extent.max_x, color);
|
fast_fill_solid_color_span(dest_ptr, clipped_extent.max_x - full_converage_count + 1, clipped_extent.max_x, color);
|
||||||
};
|
};
|
||||||
switch_on_color_or_function(
|
switch_on_color_or_function(
|
||||||
color_or_function, write_scanline_with_fast_fills, write_scanline_pixelwise);
|
color_or_function, write_scanline_with_fast_fills, write_scanline_pixelwise);
|
||||||
|
|
|
@ -163,6 +163,13 @@ private:
|
||||||
struct EdgeExtent {
|
struct EdgeExtent {
|
||||||
int min_x;
|
int min_x;
|
||||||
int max_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);
|
||||||
|
@ -171,19 +178,33 @@ private:
|
||||||
template<Painter::WindingRule>
|
template<Painter::WindingRule>
|
||||||
void write_scanline(Painter&, int scanline, EdgeExtent, auto& color_or_function);
|
void write_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(BitmapFormat format, ARGB32* scanline_ptr, int scanline, int offset, SampleType sample, auto& color_or_function);
|
||||||
void fast_fill_solid_color_span(Painter&, int scanline, int start, int end, Color color);
|
void fast_fill_solid_color_span(ARGB32* scanline_ptr, int start, int end, Color color);
|
||||||
|
|
||||||
template<Painter::WindingRule, typename Callback>
|
template<Painter::WindingRule, typename Callback>
|
||||||
void accumulate_scanline(EdgeExtent, Callback);
|
auto accumulate_scanline(EdgeExtent, auto, Callback);
|
||||||
void accumulate_even_odd_scanline(EdgeExtent, auto sample_callback);
|
auto accumulate_even_odd_scanline(EdgeExtent, auto, auto sample_callback);
|
||||||
void accumulate_non_zero_scanline(EdgeExtent, auto sample_callback);
|
auto accumulate_non_zero_scanline(EdgeExtent, auto, auto sample_callback);
|
||||||
|
|
||||||
struct WindingCounts {
|
struct WindingCounts {
|
||||||
// NOTE: This only allows up to 256 winding levels. Increase this if required (i.e. to an i16).
|
// NOTE: This only allows up to 256 winding levels. Increase this if required (i.e. to an i16).
|
||||||
i8 counts[SamplesPerPixel];
|
i8 counts[SamplesPerPixel];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct NonZeroAcc {
|
||||||
|
SampleType sample;
|
||||||
|
WindingCounts winding;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<Painter::WindingRule WindingRule>
|
||||||
|
constexpr auto initial_acc() const
|
||||||
|
{
|
||||||
|
if constexpr (WindingRule == Painter::WindingRule::EvenOdd)
|
||||||
|
return SampleType {};
|
||||||
|
else
|
||||||
|
return NonZeroAcc {};
|
||||||
|
}
|
||||||
|
|
||||||
IntSize m_size;
|
IntSize m_size;
|
||||||
IntPoint m_blit_origin;
|
IntPoint m_blit_origin;
|
||||||
IntRect m_clip;
|
IntRect m_clip;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue