using XFESystem.Browser;
using XFESystem.Desktop;
namespace XFESystem.Browser.Tests;
public sealed class RasterCoveragePerformanceTests
{
[Fact]
public void SolidSpansPreserveRoundedFractionalClippedPixelsAtBothScales()
{
var random=new Random(240617);
foreach(int scale in new[]{1,2})
for(int sample=0;sample<120;sample++)
{
var bounds=new BrowserRect(3+random.NextDouble(),4+random.NextDouble(),2+random.NextDouble()*45,2+random.NextDouble()*40);
double maximum=Math.Min(bounds.Width,bounds.Height)/2;
var radii=new BrowserEdges(random.NextDouble()*maximum,random.NextDouble()*maximum,random.NextDouble()*maximum,random.NextDouble()*maximum);
double alpha=sample%3==0?1:random.NextDouble(),opacity=sample%4==0?1:random.NextDouble();
var decoration=new BrowserBoxDecoration(radii,default,new(0x123456,alpha),default,default,default,default,[],bounds,[]);
var command=new BrowserDrawCommand(BrowserDrawKind.Fill,bounds,0,null,0x123456,Opacity:opacity,Decoration:decoration);
var clip=sample%2==0?new BrowserRect(0,0,70,70):new BrowserRect(8.25,10.75,26.5,23.25);
var canvas=new CoveragePixels(scale);
BrowserBoxRasterPainter.Paint(canvas,command,bounds,clip);
var area=bounds.Intersect(clip);var native=new BrowserRect(bounds.X*scale,bounds.Y*scale,bounds.Width*scale,bounds.Height*scale);
var nativeRadii=new BrowserEdges(radii.Top*scale,radii.Right*scale,radii.Bottom*scale,radii.Left*scale);
for(int y=0;y<140;y++)for(int x=0;x<140;x++)
{
bool touched=x>=Math.Floor(area.X*scale)&&x<Math.Ceiling(area.Right*scale)&&y>=Math.Floor(area.Y*scale)&&y<Math.Ceiling(area.Bottom*scale);
byte expected=touched?(byte)Math.Clamp(Math.Round(BrowserBoxRasterPainter.Coverage(native,nativeRadii,x+.5,y+.5)*alpha*opacity*255),0,255):(byte)0;
Assert.Equal(expected,canvas.Alpha[y*140+x]);
}
}
}
private sealed class CoveragePixels(int scale):IDesktopCoverageCanvas
{
public int Width=>70;public int Height=>70;public int ScalePercent=>scale*100;
public byte[] Alpha=new byte[140*140];
public void BlendCoverage(uint rgb,int x,int y,int width,int height,ReadOnlySpan<byte> source)
{for(int row=0;row<height;row++)source.Slice(row*width,width).CopyTo(Alpha.AsSpan((y+row)*140+x,width));}
public void Fill(uint rgb,int x,int y,int width,int height){}
public void Line(uint rgb,int x1,int y1,int x2,int y2){}
public void Text(string text,uint rgb,int x,int y){}
public void Present(){}
}
[Fact]
public void RoundedCoverageFastPathsKeepOriginalDevicePixelAlpha()
{
var random = new Random(617071);
for (int sample = 0; sample < 20000; sample++)
{
double width = 1 + random.NextDouble() * 1000, height = 1 + random.NextDouble() * 1000;
var bounds = new BrowserRect(random.NextDouble() * 30, random.NextDouble() * 30, width, height);
double maximum = Math.Min(width, height) / 2;
var radii = new BrowserEdges(random.NextDouble() * maximum, random.NextDouble() * maximum,
random.NextDouble() * maximum, random.NextDouble() * maximum);
double x = bounds.X - 1 + random.NextDouble() * (width + 2);
double y = bounds.Y - 1 + random.NextDouble() * (height + 2);
double expected = Original(bounds, radii, x, y), actual = BrowserBoxRasterPainter.Coverage(bounds, radii, x, y);
Assert.InRange(Math.Abs(actual - expected), 0, 1e-12);
Assert.Equal(Math.Round(expected * 255), Math.Round(actual * 255));
}
foreach (double radius in new[] { 0d, .125, .5, 1, 4.25, 8.5, 16, 20 })
for (double y = -.5; y <= 40.5; y += .125)
for (double x = -.5; x <= 40.5; x += .125)
{
var bounds = new BrowserRect(0, 0, 40, 40); var radii = new BrowserEdges(radius, radius, radius, radius);
Assert.Equal(Math.Round(Original(bounds, radii, x, y) * 255), Math.Round(BrowserBoxRasterPainter.Coverage(bounds, radii, x, y) * 255));
}
}
private static double Original(BrowserRect rect, BrowserEdges radii, double x, double y)
{
if (rect.Width <= 0 || rect.Height <= 0) return 0;
double radius = x < rect.X + rect.Width / 2 ? y < rect.Y + rect.Height / 2 ? radii.Top : radii.Left : y < rect.Y + rect.Height / 2 ? radii.Right : radii.Bottom;
double qx = Math.Abs(x - rect.X - rect.Width / 2) - rect.Width / 2 + radius;
double qy = Math.Abs(y - rect.Y - rect.Height / 2) - rect.Height / 2 + radius;
double distance = Math.Sqrt(Math.Max(0, qx) * Math.Max(0, qx) + Math.Max(0, qy) * Math.Max(0, qy)) + Math.Min(Math.Max(qx, qy), 0) - radius;
return Math.Clamp(.5 - distance, 0, 1);
}
}
using XFESystem.Browser;
using XFESystem.Desktop;
namespace XFESystem.Browser.Tests;
public sealed class RasterCoveragePerformanceTests
{
[Fact]
public void SolidSpansPreserveRoundedFractionalClippedPixelsAtBothScales()
{
var random=new Random(240617);
foreach(int scale in new[]{1,2})
for(int sample=0;sample<120;sample++)
{
var bounds=new BrowserRect(3+random.NextDouble(),4+random.NextDouble(),2+random.NextDouble()*45,2+random.NextDouble()*40);
double maximum=Math.Min(bounds.Width,bounds.Height)/2;
var radii=new BrowserEdges(random.NextDouble()*maximum,random.NextDouble()*maximum,random.NextDouble()*maximum,random.NextDouble()*maximum);
double alpha=sample%3==0?1:random.NextDouble(),opacity=sample%4==0?1:random.NextDouble();
var decoration=new BrowserBoxDecoration(radii,default,new(0x123456,alpha),default,default,default,default,[],bounds,[]);
var command=new BrowserDrawCommand(BrowserDrawKind.Fill,bounds,0,null,0x123456,Opacity:opacity,Decoration:decoration);
var clip=sample%2==0?new BrowserRect(0,0,70,70):new BrowserRect(8.25,10.75,26.5,23.25);
var canvas=new CoveragePixels(scale);
BrowserBoxRasterPainter.Paint(canvas,command,bounds,clip);
var area=bounds.Intersect(clip);var native=new BrowserRect(bounds.X*scale,bounds.Y*scale,bounds.Width*scale,bounds.Height*scale);
var nativeRadii=new BrowserEdges(radii.Top*scale,radii.Right*scale,radii.Bottom*scale,radii.Left*scale);
for(int y=0;y<140;y++)for(int x=0;x<140;x++)
{
bool touched=x>=Math.Floor(area.X*scale)&&x<Math.Ceiling(area.Right*scale)&&y>=Math.Floor(area.Y*scale)&&y<Math.Ceiling(area.Bottom*scale);
byte expected=touched?(byte)Math.Clamp(Math.Round(BrowserBoxRasterPainter.Coverage(native,nativeRadii,x+.5,y+.5)*alpha*opacity*255),0,255):(byte)0;
Assert.Equal(expected,canvas.Alpha[y*140+x]);
}
}
}
private sealed class CoveragePixels(int scale):IDesktopCoverageCanvas
{
public int Width=>70;public int Height=>70;public int ScalePercent=>scale*100;
public byte[] Alpha=new byte[140*140];
public void BlendCoverage(uint rgb,int x,int y,int width,int height,ReadOnlySpan<byte> source)
{for(int row=0;row<height;row++)source.Slice(row*width,width).CopyTo(Alpha.AsSpan((y+row)*140+x,width));}
public void Fill(uint rgb,int x,int y,int width,int height){}
public void Line(uint rgb,int x1,int y1,int x2,int y2){}
public void Text(string text,uint rgb,int x,int y){}
public void Present(){}
}
[Fact]
public void RoundedCoverageFastPathsKeepOriginalDevicePixelAlpha()
{
var random = new Random(617071);
for (int sample = 0; sample < 20000; sample++)
{
double width = 1 + random.NextDouble() * 1000, height = 1 + random.NextDouble() * 1000;
var bounds = new BrowserRect(random.NextDouble() * 30, random.NextDouble() * 30, width, height);
double maximum = Math.Min(width, height) / 2;
var radii = new BrowserEdges(random.NextDouble() * maximum, random.NextDouble() * maximum,
random.NextDouble() * maximum, random.NextDouble() * maximum);
double x = bounds.X - 1 + random.NextDouble() * (width + 2);
double y = bounds.Y - 1 + random.NextDouble() * (height + 2);
double expected = Original(bounds, radii, x, y), actual = BrowserBoxRasterPainter.Coverage(bounds, radii, x, y);
Assert.InRange(Math.Abs(actual - expected), 0, 1e-12);
Assert.Equal(Math.Round(expected * 255), Math.Round(actual * 255));
}
foreach (double radius in new[] { 0d, .125, .5, 1, 4.25, 8.5, 16, 20 })
for (double y = -.5; y <= 40.5; y += .125)
for (double x = -.5; x <= 40.5; x += .125)
{
var bounds = new BrowserRect(0, 0, 40, 40); var radii = new BrowserEdges(radius, radius, radius, radius);
Assert.Equal(Math.Round(Original(bounds, radii, x, y) * 255), Math.Round(BrowserBoxRasterPainter.Coverage(bounds, radii, x, y) * 255));
}
}
private static double Original(BrowserRect rect, BrowserEdges radii, double x, double y)
{
if (rect.Width <= 0 || rect.Height <= 0) return 0;
double radius = x < rect.X + rect.Width / 2 ? y < rect.Y + rect.Height / 2 ? radii.Top : radii.Left : y < rect.Y + rect.Height / 2 ? radii.Right : radii.Bottom;
double qx = Math.Abs(x - rect.X - rect.Width / 2) - rect.Width / 2 + radius;
double qy = Math.Abs(y - rect.Y - rect.Height / 2) - rect.Height / 2 + radius;
double distance = Math.Sqrt(Math.Max(0, qx) * Math.Max(0, qx) + Math.Max(0, qy) * Math.Max(0, qy)) + Math.Min(Math.Max(qx, qy), 0) - radius;
return Math.Clamp(.5 - distance, 0, 1);
}
}