|
2 | 2 | // Licensed under the Six Labors Split License. |
3 | 3 |
|
4 | 4 | using System.Diagnostics.CodeAnalysis; |
5 | | -using System.Numerics; |
6 | | -using SixLabors.ImageSharp.Advanced; |
7 | 5 | using SixLabors.ImageSharp.Drawing.Shapes.Rasterization; |
8 | 6 | using SixLabors.ImageSharp.Memory; |
9 | 7 |
|
@@ -175,13 +173,12 @@ internal void FlushPreparedBatch<TPixel>( |
175 | 173 | applicators, |
176 | 174 | destinationBounds, |
177 | 175 | definition.RasterizerOptions.Interest.Top); |
178 | | - DefaultRasterizer.Instance.Rasterize( |
| 176 | + |
| 177 | + DefaultRasterizer.RasterizeRows( |
179 | 178 | definition.Path, |
180 | 179 | definition.RasterizerOptions, |
181 | 180 | configuration.MemoryAllocator, |
182 | | - ref operation, |
183 | | - static (int y, Span<float> scanline, ref RowOperation<TPixel> callbackState) => |
184 | | - callbackState.InvokeScanline(y, scanline)); |
| 181 | + operation.InvokeCoverageRow); |
185 | 182 | } |
186 | 183 | finally |
187 | 184 | { |
@@ -212,153 +209,40 @@ public RowOperation( |
212 | 209 | this.coverageTop = coverageTop; |
213 | 210 | } |
214 | 211 |
|
215 | | - public void InvokeScanline(int y, Span<float> scanline) |
| 212 | + public void InvokeCoverageRow(int y, int startX, Span<float> coverage) |
216 | 213 | { |
217 | 214 | int sourceY = y - this.coverageTop; |
| 215 | + int rowStart = startX; |
| 216 | + int rowEnd = startX + coverage.Length; |
| 217 | + |
| 218 | + Rectangle destinationBounds = this.destinationBounds; |
| 219 | + BrushApplicator<TPixel>[] applicators = this.applicators; |
218 | 220 | for (int i = 0; i < this.commands.Count; i++) |
219 | 221 | { |
220 | 222 | PreparedCompositionCommand command = this.commands[i]; |
| 223 | + Rectangle commandDestination = command.DestinationRegion; |
| 224 | + |
221 | 225 | int commandY = sourceY - command.SourceOffset.Y; |
222 | | - if ((uint)commandY >= (uint)command.DestinationRegion.Height) |
| 226 | + if ((uint)commandY >= (uint)commandDestination.Height) |
223 | 227 | { |
224 | 228 | continue; |
225 | 229 | } |
226 | 230 |
|
227 | | - int destinationX = this.destinationBounds.X + command.DestinationRegion.X; |
228 | | - int destinationY = this.destinationBounds.Y + command.DestinationRegion.Y + commandY; |
229 | 231 | int sourceStartX = command.SourceOffset.X; |
230 | | - Span<float> rowSlice = scanline.Slice(sourceStartX, command.DestinationRegion.Width); |
231 | | - ApplyCoverageSpans(this.applicators[i], rowSlice, destinationX, destinationY); |
232 | | - } |
233 | | - } |
234 | | - |
235 | | - /// <summary> |
236 | | - /// Applies only contiguous non-zero coverage spans for a scanline. |
237 | | - /// </summary> |
238 | | - /// <param name="applicator">Brush applicator used to composite pixels.</param> |
239 | | - /// <param name="coverage">Scanline coverage values for the current command row.</param> |
240 | | - /// <param name="destinationX">Destination x coordinate for the start of <paramref name="coverage"/>.</param> |
241 | | - /// <param name="destinationY">Destination y coordinate for the scanline.</param> |
242 | | - private static void ApplyCoverageSpans( |
243 | | - BrushApplicator<TPixel> applicator, |
244 | | - Span<float> coverage, |
245 | | - int destinationX, |
246 | | - int destinationY) |
247 | | - { |
248 | | - // Use SIMD path when available and the span is large enough to amortize setup. |
249 | | - if (Vector.IsHardwareAccelerated && coverage.Length >= (Vector<float>.Count * 2)) |
250 | | - { |
251 | | - ApplyCoverageSpansSimd(applicator, coverage, destinationX, destinationY); |
252 | | - return; |
253 | | - } |
254 | | - |
255 | | - ApplyCoverageSpansScalar(applicator, coverage, destinationX, destinationY); |
256 | | - } |
257 | | - |
258 | | - /// <summary> |
259 | | - /// Applies contiguous non-zero coverage spans using SIMD-accelerated zero/non-zero chunk checks. |
260 | | - /// </summary> |
261 | | - /// <param name="applicator">Brush applicator used to composite pixels.</param> |
262 | | - /// <param name="coverage">Scanline coverage values for the current command row.</param> |
263 | | - /// <param name="destinationX">Destination x coordinate for the start of <paramref name="coverage"/>.</param> |
264 | | - /// <param name="destinationY">Destination y coordinate for the scanline.</param> |
265 | | - private static void ApplyCoverageSpansSimd( |
266 | | - BrushApplicator<TPixel> applicator, |
267 | | - Span<float> coverage, |
268 | | - int destinationX, |
269 | | - int destinationY) |
270 | | - { |
271 | | - int i = 0; |
272 | | - int n = coverage.Length; |
273 | | - int width = Vector<float>.Count; |
274 | | - Vector<float> zero = Vector<float>.Zero; |
275 | | - |
276 | | - while (i < n) |
277 | | - { |
278 | | - // Phase 1: skip fully-zero SIMD blocks. |
279 | | - while (i <= n - width) |
280 | | - { |
281 | | - Vector<float> v = new(coverage.Slice(i, width)); |
282 | | - if (!Vector.EqualsAll(v, zero)) |
283 | | - { |
284 | | - break; |
285 | | - } |
286 | | - |
287 | | - i += width; |
288 | | - } |
289 | | - |
290 | | - while (i < n && coverage[i] == 0F) |
291 | | - { |
292 | | - i++; |
293 | | - } |
294 | | - |
295 | | - if (i >= n) |
296 | | - { |
297 | | - return; |
298 | | - } |
299 | | - |
300 | | - int runStart = i; |
301 | | - |
302 | | - // Phase 2: advance across fully non-zero SIMD blocks. |
303 | | - while (i <= n - width) |
| 232 | + int sourceEndX = sourceStartX + commandDestination.Width; |
| 233 | + int overlapStart = Math.Max(rowStart, sourceStartX); |
| 234 | + int overlapEnd = Math.Min(rowEnd, sourceEndX); |
| 235 | + if (overlapEnd <= overlapStart) |
304 | 236 | { |
305 | | - Vector<float> v = new(coverage.Slice(i, width)); |
306 | | - Vector<int> eqZero = Vector.Equals(v, zero); |
307 | | - if (!Vector.EqualsAll(eqZero, Vector<int>.Zero)) |
308 | | - { |
309 | | - break; |
310 | | - } |
311 | | - |
312 | | - i += width; |
| 237 | + continue; |
313 | 238 | } |
314 | 239 |
|
315 | | - while (i < n && coverage[i] != 0F) |
316 | | - { |
317 | | - i++; |
318 | | - } |
| 240 | + int localStart = overlapStart - rowStart; |
| 241 | + int localLength = overlapEnd - overlapStart; |
| 242 | + int destinationX = destinationBounds.X + commandDestination.X + (overlapStart - sourceStartX); |
| 243 | + int destinationY = destinationBounds.Y + commandDestination.Y + commandY; |
319 | 244 |
|
320 | | - // Apply exactly one contiguous non-zero run. |
321 | | - applicator.Apply(coverage[runStart..i], destinationX + runStart, destinationY); |
322 | | - } |
323 | | - } |
324 | | - |
325 | | - /// <summary> |
326 | | - /// Applies contiguous non-zero coverage spans using a scalar scan. |
327 | | - /// </summary> |
328 | | - /// <param name="applicator">Brush applicator used to composite pixels.</param> |
329 | | - /// <param name="coverage">Scanline coverage values for the current command row.</param> |
330 | | - /// <param name="destinationX">Destination x coordinate for the start of <paramref name="coverage"/>.</param> |
331 | | - /// <param name="destinationY">Destination y coordinate for the scanline.</param> |
332 | | - private static void ApplyCoverageSpansScalar( |
333 | | - BrushApplicator<TPixel> applicator, |
334 | | - Span<float> coverage, |
335 | | - int destinationX, |
336 | | - int destinationY) |
337 | | - { |
338 | | - // Track the start of a contiguous non-zero coverage run. |
339 | | - int runStart = -1; |
340 | | - for (int i = 0; i < coverage.Length; i++) |
341 | | - { |
342 | | - if (coverage[i] > 0F) |
343 | | - { |
344 | | - // Enter a new run when transitioning from zero to non-zero coverage. |
345 | | - if (runStart < 0) |
346 | | - { |
347 | | - runStart = i; |
348 | | - } |
349 | | - } |
350 | | - else if (runStart >= 0) |
351 | | - { |
352 | | - // Coverage returned to zero: apply the finished run only. |
353 | | - applicator.Apply(coverage[runStart..i], destinationX + runStart, destinationY); |
354 | | - runStart = -1; |
355 | | - } |
356 | | - } |
357 | | - |
358 | | - if (runStart >= 0) |
359 | | - { |
360 | | - // Flush trailing run that reaches end-of-scanline. |
361 | | - applicator.Apply(coverage[runStart..], destinationX + runStart, destinationY); |
| 245 | + applicators[i].Apply(coverage.Slice(localStart, localLength), destinationX, destinationY); |
362 | 246 | } |
363 | 247 | } |
364 | 248 | } |
|
0 commit comments