Skip to content

Commit e0da197

Browse files
Copilotmattleibow
andcommitted
Add conceptual documentation for Pixel Comparer feature
Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
1 parent 6bf4160 commit e0da197

File tree

3 files changed

+144
-0
lines changed

3 files changed

+144
-0
lines changed

docs/docs/pixel-comparer.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Pixel Comparer
2+
3+
> [!TIP]
4+
> **[Try it live →](../sample/pixelcomparer)** — Interactive Pixel Comparer demo in the Blazor sample app.
5+
6+
Pixel Comparer lets you compare two images pixel by pixel and quantify their differences. It's ideal for visual regression testing, screenshot comparison in CI pipelines, and verifying that rendering output matches expected results.
7+
8+
## Quick Start
9+
10+
### Compare two images
11+
12+
```csharp
13+
using SkiaSharp;
14+
using SkiaSharp.Extended;
15+
16+
var result = SKPixelComparer.Compare("expected.png", "actual.png");
17+
18+
Console.WriteLine($"Total pixels: {result.TotalPixels}");
19+
Console.WriteLine($"Error pixels: {result.ErrorPixelCount}");
20+
Console.WriteLine($"Error percentage: {result.ErrorPixelPercentage:P2}");
21+
Console.WriteLine($"Absolute error: {result.AbsoluteError}");
22+
```
23+
24+
### Generate a difference mask
25+
26+
```csharp
27+
using var mask = SKPixelComparer.GenerateDifferenceMask("expected.png", "actual.png");
28+
29+
// Encode and save the mask
30+
using var data = mask.Encode(SKEncodedImageFormat.Png, 100);
31+
File.WriteAllBytes("diff-mask.png", data.ToArray());
32+
```
33+
34+
The mask is a black-and-white image where **white pixels** indicate differences and **black pixels** indicate matching areas.
35+
36+
## How It Works
37+
38+
The comparer normalizes both images to BGRA8888 format, then walks through every pixel and sums the per-channel (red, green, blue) absolute differences:
39+
40+
1. For each pixel, compute `|R₁ − R₂| + |G₁ − G₂| + |B₁ − B₂|`
41+
2. If the sum is greater than zero, that pixel is counted as an error
42+
3. The total per-pixel sums are accumulated into `AbsoluteError`
43+
44+
Both images must have the same dimensions; otherwise an `InvalidOperationException` is thrown.
45+
46+
## Comparison Results
47+
48+
The [`SKPixelComparisonResult`](xref:SkiaSharp.Extended.SKPixelComparisonResult) contains:
49+
50+
| Property | Type | Description |
51+
| :------- | :--- | :---------- |
52+
| `TotalPixels` | `int` | Width × Height of the images |
53+
| `ErrorPixelCount` | `int` | Number of pixels with any difference |
54+
| `ErrorPixelPercentage` | `double` | `ErrorPixelCount / TotalPixels` |
55+
| `AbsoluteError` | `int` | Sum of all per-channel differences |
56+
57+
## Mask-Based Comparison
58+
59+
When comparing images that have expected minor differences (e.g., anti-aliasing, compression artifacts), you can supply a tolerance mask. The mask image uses per-channel thresholds—a difference is only counted if it exceeds the corresponding channel value in the mask pixel:
60+
61+
```csharp
62+
using var expected = SKImage.FromEncodedData("expected.png");
63+
using var actual = SKImage.FromEncodedData("actual.png");
64+
using var mask = SKImage.FromEncodedData("tolerance-mask.png");
65+
66+
var result = SKPixelComparer.Compare(expected, actual, mask);
67+
```
68+
69+
For example, if a mask pixel has RGB values of `(10, 10, 10)`, differences of up to 10 per channel at that location are ignored. This lets you define region-specific tolerances.
70+
71+
## Input Overloads
72+
73+
All comparison methods accept multiple input types for convenience:
74+
75+
| Input Type | Example |
76+
| :--------- | :------ |
77+
| File paths | `Compare("a.png", "b.png")` |
78+
| `SKImage` | `Compare(imageA, imageB)` |
79+
| `SKBitmap` | `Compare(bitmapA, bitmapB)` |
80+
| `SKPixmap` | `Compare(pixmapA, pixmapB)` |
81+
82+
The same overloads are available for `GenerateDifferenceMask` (without mask support) and for the three-argument masked `Compare`.
83+
84+
## Usage Patterns
85+
86+
### Visual regression testing
87+
88+
Use the comparer in your test suite to catch unintended rendering changes:
89+
90+
```csharp
91+
[Fact]
92+
public void ChartRendering_MatchesBaseline()
93+
{
94+
using var actual = RenderChart(testData);
95+
using var expected = SKImage.FromEncodedData("baselines/chart.png");
96+
97+
var result = SKPixelComparer.Compare(expected, actual);
98+
99+
Assert.Equal(0, result.ErrorPixelCount);
100+
}
101+
```
102+
103+
### CI screenshot comparison with tolerance
104+
105+
For tests that may have minor platform-specific differences:
106+
107+
```csharp
108+
[Fact]
109+
public void UI_MatchesBaseline_WithTolerance()
110+
{
111+
using var actual = CaptureScreenshot();
112+
using var expected = SKImage.FromEncodedData("baselines/screen.png");
113+
114+
var result = SKPixelComparer.Compare(expected, actual);
115+
116+
// Allow up to 0.5% pixel difference
117+
Assert.True(result.ErrorPixelPercentage < 0.005,
118+
$"Too many differing pixels: {result.ErrorPixelPercentage:P2}");
119+
}
120+
```
121+
122+
### Saving a difference mask for debugging
123+
124+
When a comparison fails, generate and save the mask to help diagnose what changed:
125+
126+
```csharp
127+
if (result.ErrorPixelCount > 0)
128+
{
129+
using var diffMask = SKPixelComparer.GenerateDifferenceMask(expected, actual);
130+
using var encoded = diffMask.Encode(SKEncodedImageFormat.Png, 100);
131+
File.WriteAllBytes("test-output/diff-mask.png", encoded.ToArray());
132+
}
133+
```
134+
135+
## Learn More
136+
137+
- [API Reference — SKPixelComparer](xref:SkiaSharp.Extended.SKPixelComparer) — Full method documentation
138+
- [API Reference — SKPixelComparisonResult](xref:SkiaSharp.Extended.SKPixelComparisonResult) — Result class documentation

docs/docs/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ items:
1010
href: geometry.md
1111
- name: Path Interpolation
1212
href: path-interpolation.md
13+
- name: Pixel Comparer
14+
href: pixel-comparer.md
1315

1416
- name: SkiaSharp.Extended.UI.Maui
1517
- name: Confetti Effects

docs/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ Smoothly morph between two paths for shape transitions and animations.
5252

5353
![Path Interpolation][interpolation-img]
5454

55+
### [Pixel Comparer](docs/pixel-comparer.md)
56+
57+
Compare two images pixel by pixel, quantify differences, and generate visual difference masks—ideal for visual regression testing.
58+
5559
---
5660

5761
## SkiaSharp.Extended.UI.Maui

0 commit comments

Comments
 (0)