/src/serenity/Userland/Libraries/LibGfx/EdgeFlagPathRasterizer.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023, MacDue <macdue@dueutil.tech> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/Array.h> |
10 | | #include <AK/GenericShorthands.h> |
11 | | #include <AK/IntegralMath.h> |
12 | | #include <AK/Vector.h> |
13 | | #include <LibGfx/Bitmap.h> |
14 | | #include <LibGfx/Forward.h> |
15 | | #include <LibGfx/PaintStyle.h> |
16 | | #include <LibGfx/Path.h> |
17 | | #include <LibGfx/WindingRule.h> |
18 | | |
19 | | namespace Gfx { |
20 | | |
21 | | namespace Detail { |
22 | | |
23 | | template<unsigned SampleCount> |
24 | | struct NoAA { |
25 | | static constexpr unsigned SamplesPerPixel = SampleCount; |
26 | | |
27 | | static u8 coverage_to_alpha(u8 coverage) |
28 | 0 | { |
29 | 0 | return coverage ? 255 : 0; |
30 | 0 | } Unexecuted instantiation: Gfx::Detail::NoAA<2u>::coverage_to_alpha(unsigned char) Unexecuted instantiation: Gfx::Detail::NoAA<8u>::coverage_to_alpha(unsigned char) |
31 | | }; |
32 | | |
33 | | template<unsigned SampleCount> |
34 | | struct AA { |
35 | | static constexpr unsigned SamplesPerPixel = SampleCount; |
36 | | |
37 | | static u8 coverage_to_alpha(u8 coverage) |
38 | 57.3M | { |
39 | 57.3M | constexpr auto alpha_shift = AK::log2(256 / SamplesPerPixel); |
40 | 57.3M | if (!coverage) |
41 | 0 | return 0; |
42 | 57.3M | return (coverage << alpha_shift) - 1; |
43 | 57.3M | } Unexecuted instantiation: Gfx::Detail::AA<8u>::coverage_to_alpha(unsigned char) Unexecuted instantiation: Gfx::Detail::AA<16u>::coverage_to_alpha(unsigned char) Gfx::Detail::AA<32u>::coverage_to_alpha(unsigned char) Line | Count | Source | 38 | 57.3M | { | 39 | 57.3M | constexpr auto alpha_shift = AK::log2(256 / SamplesPerPixel); | 40 | 57.3M | if (!coverage) | 41 | 0 | return 0; | 42 | 57.3M | return (coverage << alpha_shift) - 1; | 43 | 57.3M | } |
|
44 | | }; |
45 | | |
46 | | // 2-bit sample (mainly for use with NoAA). |
47 | | template<template<unsigned SamplesPerPixel> class AAMode> |
48 | | struct Sample2x : AAMode<2> { |
49 | | using Type = u8; |
50 | | static constexpr Array nrooks_subpixel_offsets { |
51 | | (0.0f / 2.0f), |
52 | | (1.0f / 2.0f), |
53 | | }; |
54 | | }; |
55 | | |
56 | | // See paper for diagrams for how these offsets work, but they allow for nicely spread out samples in each pixel. |
57 | | template<template<unsigned SamplesPerPixel> class AAMode> |
58 | | struct Sample8x : AAMode<8> { |
59 | | using Type = u8; |
60 | | static constexpr Array nrooks_subpixel_offsets { |
61 | | (5.0f / 8.0f), |
62 | | (0.0f / 8.0f), |
63 | | (3.0f / 8.0f), |
64 | | (6.0f / 8.0f), |
65 | | (1.0f / 8.0f), |
66 | | (4.0f / 8.0f), |
67 | | (7.0f / 8.0f), |
68 | | (2.0f / 8.0f), |
69 | | }; |
70 | | }; |
71 | | |
72 | | template<template<unsigned SamplesPerPixel> class AAMode> |
73 | | struct Sample16x : AAMode<16> { |
74 | | using Type = u16; |
75 | | static constexpr Array nrooks_subpixel_offsets { |
76 | | (1.0f / 16.0f), |
77 | | (8.0f / 16.0f), |
78 | | (4.0f / 16.0f), |
79 | | (15.0f / 16.0f), |
80 | | (11.0f / 16.0f), |
81 | | (2.0f / 16.0f), |
82 | | (6.0f / 16.0f), |
83 | | (14.0f / 16.0f), |
84 | | (10.0f / 16.0f), |
85 | | (3.0f / 16.0f), |
86 | | (7.0f / 16.0f), |
87 | | (12.0f / 16.0f), |
88 | | (0.0f / 16.0f), |
89 | | (9.0f / 16.0f), |
90 | | (5.0f / 16.0f), |
91 | | (13.0f / 16.0f), |
92 | | }; |
93 | | }; |
94 | | |
95 | | template<template<unsigned SamplesPerPixel> class AAMode> |
96 | | struct Sample32x : AAMode<32> { |
97 | | using Type = u32; |
98 | | static constexpr Array nrooks_subpixel_offsets { |
99 | | (28.0f / 32.0f), |
100 | | (13.0f / 32.0f), |
101 | | (6.0f / 32.0f), |
102 | | (23.0f / 32.0f), |
103 | | (0.0f / 32.0f), |
104 | | (17.0f / 32.0f), |
105 | | (10.0f / 32.0f), |
106 | | (27.0f / 32.0f), |
107 | | (4.0f / 32.0f), |
108 | | (21.0f / 32.0f), |
109 | | (14.0f / 32.0f), |
110 | | (31.0f / 32.0f), |
111 | | (8.0f / 32.0f), |
112 | | (25.0f / 32.0f), |
113 | | (18.0f / 32.0f), |
114 | | (3.0f / 32.0f), |
115 | | (12.0f / 32.0f), |
116 | | (29.0f / 32.0f), |
117 | | (22.0f / 32.0f), |
118 | | (7.0f / 32.0f), |
119 | | (16.0f / 32.0f), |
120 | | (1.0f / 32.0f), |
121 | | (26.0f / 32.0f), |
122 | | (11.0f / 32.0f), |
123 | | (20.0f / 32.0f), |
124 | | (5.0f / 32.0f), |
125 | | (30.0f / 32.0f), |
126 | | (15.0f / 32.0f), |
127 | | (24.0f / 32.0f), |
128 | | (9.0f / 32.0f), |
129 | | (2.0f / 32.0f), |
130 | | (19.0f / 32.0f), |
131 | | }; |
132 | | }; |
133 | | |
134 | | struct Edge { |
135 | | float x; |
136 | | int min_y; |
137 | | int max_y; |
138 | | float dxdy; |
139 | | i8 winding; |
140 | | Edge* next_edge; |
141 | | }; |
142 | | |
143 | | } |
144 | | |
145 | | template<typename SubpixelSample> |
146 | | class EdgeFlagPathRasterizer { |
147 | | public: |
148 | | EdgeFlagPathRasterizer(IntSize); |
149 | | |
150 | | void fill(Painter&, Path const&, Color, WindingRule, FloatPoint offset = {}); |
151 | | void fill(Painter&, Path const&, PaintStyle const&, float opacity, WindingRule, FloatPoint offset = {}); |
152 | | |
153 | | private: |
154 | | using SampleType = typename SubpixelSample::Type; |
155 | | static constexpr unsigned SamplesPerPixel = SubpixelSample::SamplesPerPixel; |
156 | | |
157 | | struct EdgeExtent { |
158 | | int min_x; |
159 | | int max_x; |
160 | | |
161 | | template<typename T> |
162 | | void memset_extent(T* data, int value) |
163 | 150k | { |
164 | 150k | if (min_x <= max_x) |
165 | 125k | memset(data + min_x, value, (max_x - min_x + 1) * sizeof(T)); |
166 | 150k | } Unexecuted instantiation: void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::AA> >::EdgeExtent::memset_extent<unsigned char>(unsigned char*, int) Unexecuted instantiation: void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::AA> >::EdgeExtent::memset_extent<Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::AA> >::WindingCounts>(Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::AA> >::WindingCounts*, int) Unexecuted instantiation: void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample16x<Gfx::Detail::AA> >::EdgeExtent::memset_extent<unsigned short>(unsigned short*, int) Unexecuted instantiation: void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample16x<Gfx::Detail::AA> >::EdgeExtent::memset_extent<Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample16x<Gfx::Detail::AA> >::WindingCounts>(Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample16x<Gfx::Detail::AA> >::WindingCounts*, int) void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample32x<Gfx::Detail::AA> >::EdgeExtent::memset_extent<unsigned int>(unsigned int*, int) Line | Count | Source | 163 | 126k | { | 164 | 126k | if (min_x <= max_x) | 165 | 104k | memset(data + min_x, value, (max_x - min_x + 1) * sizeof(T)); | 166 | 126k | } |
void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample32x<Gfx::Detail::AA> >::EdgeExtent::memset_extent<Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample32x<Gfx::Detail::AA> >::WindingCounts>(Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample32x<Gfx::Detail::AA> >::WindingCounts*, int) Line | Count | Source | 163 | 23.6k | { | 164 | 23.6k | if (min_x <= max_x) | 165 | 20.9k | memset(data + min_x, value, (max_x - min_x + 1) * sizeof(T)); | 166 | 23.6k | } |
Unexecuted instantiation: void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample2x<Gfx::Detail::NoAA> >::EdgeExtent::memset_extent<unsigned char>(unsigned char*, int) Unexecuted instantiation: void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample2x<Gfx::Detail::NoAA> >::EdgeExtent::memset_extent<Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample2x<Gfx::Detail::NoAA> >::WindingCounts>(Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample2x<Gfx::Detail::NoAA> >::WindingCounts*, int) Unexecuted instantiation: void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::NoAA> >::EdgeExtent::memset_extent<unsigned char>(unsigned char*, int) Unexecuted instantiation: void Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::NoAA> >::EdgeExtent::memset_extent<Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::NoAA> >::WindingCounts>(Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::NoAA> >::WindingCounts*, int) |
167 | | }; |
168 | | |
169 | | void fill_internal(Painter&, Path const&, auto color_or_function, WindingRule, FloatPoint offset); |
170 | | Detail::Edge* plot_edges_for_scanline(int scanline, auto plot_edge, EdgeExtent&, Detail::Edge* active_edges = nullptr); |
171 | | |
172 | | template<WindingRule> |
173 | | FLATTEN void write_scanline(Painter&, int scanline, EdgeExtent, auto& color_or_function); |
174 | | Color scanline_color(int scanline, int offset, u8 alpha, auto& color_or_function); |
175 | | void write_pixel(BitmapFormat format, ARGB32* scanline_ptr, int scanline, int offset, SampleType sample, auto& color_or_function); |
176 | | void fast_fill_solid_color_span(ARGB32* scanline_ptr, int start, int end, Color color); |
177 | | |
178 | | template<WindingRule, typename Callback> |
179 | | auto accumulate_scanline(EdgeExtent, auto, Callback); |
180 | | auto accumulate_even_odd_scanline(EdgeExtent, auto, auto sample_callback); |
181 | | auto accumulate_non_zero_scanline(EdgeExtent, auto, auto sample_callback); |
182 | | |
183 | | struct WindingCounts { |
184 | | // NOTE: This only allows up to 256 winding levels. Increase this if required (i.e. to an i16). |
185 | | i8 counts[SamplesPerPixel]; |
186 | | }; |
187 | | |
188 | | struct NonZeroAcc { |
189 | | SampleType sample; |
190 | | WindingCounts winding; |
191 | | }; |
192 | | |
193 | | template<WindingRule WindingRule> |
194 | | constexpr auto initial_acc() const |
195 | 1.43M | { |
196 | | if constexpr (WindingRule == WindingRule::EvenOdd) |
197 | 1.07M | return SampleType {}; |
198 | | else |
199 | 352k | return NonZeroAcc {}; |
200 | 1.43M | } Unexecuted instantiation: auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::AA> >::initial_acc<(Gfx::WindingRule)1>() const Unexecuted instantiation: auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::AA> >::initial_acc<(Gfx::WindingRule)0>() const Unexecuted instantiation: auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample16x<Gfx::Detail::AA> >::initial_acc<(Gfx::WindingRule)1>() const Unexecuted instantiation: auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample16x<Gfx::Detail::AA> >::initial_acc<(Gfx::WindingRule)0>() const auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample32x<Gfx::Detail::AA> >::initial_acc<(Gfx::WindingRule)1>() const Line | Count | Source | 195 | 1.07M | { | 196 | | if constexpr (WindingRule == WindingRule::EvenOdd) | 197 | 1.07M | return SampleType {}; | 198 | | else | 199 | | return NonZeroAcc {}; | 200 | 1.07M | } |
auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample32x<Gfx::Detail::AA> >::initial_acc<(Gfx::WindingRule)0>() const Line | Count | Source | 195 | 352k | { | 196 | | if constexpr (WindingRule == WindingRule::EvenOdd) | 197 | | return SampleType {}; | 198 | | else | 199 | 352k | return NonZeroAcc {}; | 200 | 352k | } |
Unexecuted instantiation: auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample2x<Gfx::Detail::NoAA> >::initial_acc<(Gfx::WindingRule)1>() const Unexecuted instantiation: auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample2x<Gfx::Detail::NoAA> >::initial_acc<(Gfx::WindingRule)0>() const Unexecuted instantiation: auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::NoAA> >::initial_acc<(Gfx::WindingRule)1>() const Unexecuted instantiation: auto Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::NoAA> >::initial_acc<(Gfx::WindingRule)0>() const |
201 | | |
202 | | IntSize m_size; |
203 | | IntPoint m_blit_origin; |
204 | | IntRect m_clip; |
205 | | |
206 | | Vector<SampleType> m_scanline; |
207 | | Vector<WindingCounts> m_windings; |
208 | | |
209 | | class EdgeTable { |
210 | | public: |
211 | 552k | EdgeTable() = default; Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::AA> >::EdgeTable::EdgeTable() Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample16x<Gfx::Detail::AA> >::EdgeTable::EdgeTable() Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample32x<Gfx::Detail::AA> >::EdgeTable::EdgeTable() Line | Count | Source | 211 | 552k | EdgeTable() = default; |
Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample2x<Gfx::Detail::NoAA> >::EdgeTable::EdgeTable() Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::NoAA> >::EdgeTable::EdgeTable() |
212 | | |
213 | | void set_scanline_range(int min_scanline, int max_scanline) |
214 | 59.6k | { |
215 | 59.6k | m_min_scanline = min_scanline; |
216 | 59.6k | m_edges.resize(max_scanline - min_scanline + 1); |
217 | 59.6k | } Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::AA> >::EdgeTable::set_scanline_range(int, int) Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample16x<Gfx::Detail::AA> >::EdgeTable::set_scanline_range(int, int) Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample32x<Gfx::Detail::AA> >::EdgeTable::set_scanline_range(int, int) Line | Count | Source | 214 | 59.6k | { | 215 | 59.6k | m_min_scanline = min_scanline; | 216 | 59.6k | m_edges.resize(max_scanline - min_scanline + 1); | 217 | 59.6k | } |
Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample2x<Gfx::Detail::NoAA> >::EdgeTable::set_scanline_range(int, int) Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::NoAA> >::EdgeTable::set_scanline_range(int, int) |
218 | | |
219 | 11.0M | auto& operator[](int scanline) { return m_edges[scanline - m_min_scanline]; }Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::AA> >::EdgeTable::operator[](int) Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample16x<Gfx::Detail::AA> >::EdgeTable::operator[](int) Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample32x<Gfx::Detail::AA> >::EdgeTable::operator[](int) Line | Count | Source | 219 | 11.0M | auto& operator[](int scanline) { return m_edges[scanline - m_min_scanline]; } |
Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample2x<Gfx::Detail::NoAA> >::EdgeTable::operator[](int) Unexecuted instantiation: Gfx::EdgeFlagPathRasterizer<Gfx::Detail::Sample8x<Gfx::Detail::NoAA> >::EdgeTable::operator[](int) |
220 | | |
221 | | private: |
222 | | Vector<Detail::Edge*> m_edges; |
223 | | int m_min_scanline { 0 }; |
224 | | } m_edge_table; |
225 | | }; |
226 | | |
227 | | using Sample8xAA = Detail::Sample8x<Detail::AA>; |
228 | | using Sample16xAA = Detail::Sample16x<Detail::AA>; |
229 | | using Sample32xAA = Detail::Sample32x<Detail::AA>; |
230 | | using Sample2xNoAA = Detail::Sample2x<Detail::NoAA>; |
231 | | using Sample8xNoAA = Detail::Sample8x<Detail::NoAA>; |
232 | | |
233 | | // The default sample types for antialiased/non-antialiased modes. |
234 | | using SampleAA = Sample8xAA; |
235 | | using SampleNoAA = Sample2xNoAA; |
236 | | |
237 | | extern template class EdgeFlagPathRasterizer<Sample8xAA>; |
238 | | extern template class EdgeFlagPathRasterizer<Sample16xAA>; |
239 | | extern template class EdgeFlagPathRasterizer<Sample32xAA>; |
240 | | extern template class EdgeFlagPathRasterizer<Sample2xNoAA>; |
241 | | extern template class EdgeFlagPathRasterizer<Sample8xNoAA>; |
242 | | |
243 | | } |