aboutsummaryrefslogtreecommitdiff
path: root/tests/Jellyfin.Drawing.Skia.Tests/SkiaEncoderResizeTests.cs
blob: 18518df1c2a7f25281f0085e65b4f4d4cf61138e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
using SkiaSharp;
using Xunit;

namespace Jellyfin.Drawing.Skia.Tests;

/// <summary>
/// Covers what <see cref="SkiaEncoder.ResizeImage"/> does either side of a resize: at matching
/// dimensions it must not touch the image at all, and sharpening belongs to downscales only.
/// </summary>
public class SkiaEncoderResizeTests
{
    private static SKBitmap CreateEdgeBitmap(int width, int height)
    {
        var bitmap = new SKBitmap(new SKImageInfo(width, height, SKColorType.Rgba8888, SKAlphaType.Premul));
        using var canvas = new SKCanvas(bitmap);
        canvas.Clear(new SKColor(40, 60, 80));
        using var paint = new SKPaint { Color = new SKColor(220, 210, 200) };
        canvas.DrawRect(SKRect.Create(0, 0, width / 2f, height), paint);

        return bitmap;
    }

    private static SKImageInfo InfoFor(SKBitmap source, int width, int height)
        => new SKImageInfo(width, height, source.ColorType, source.AlphaType, source.ColorSpace);

    /// <summary>
    /// Draws without sharpening, which is what the resize is expected to reduce to when it is not
    /// downscaling.
    /// </summary>
    private static SKBitmap DrawOnly(SKBitmap source, SKImageInfo targetInfo, SKSamplingOptions sampling)
    {
        var target = new SKBitmap(targetInfo);
        using var canvas = new SKCanvas(target);
        using var paint = new SKPaint();
        canvas.DrawBitmap(
            source,
            SKRect.Create(0, 0, source.Width, source.Height),
            SKRect.Create(0, 0, targetInfo.Width, targetInfo.Height),
            sampling,
            paint);

        return target;
    }

    private static void AssertSamePixels(SKBitmap expected, SKBitmap actual)
    {
        Assert.Equal(expected.Width, actual.Width);
        Assert.Equal(expected.Height, actual.Height);

        for (var y = 0; y < expected.Height; y++)
        {
            for (var x = 0; x < expected.Width; x++)
            {
                Assert.Equal(expected.GetPixel(x, y), actual.GetPixel(x, y));
            }
        }
    }

    [Fact]
    public void ResizeImage_MatchingDimensions_ReturnsTheImageUntouched()
    {
        using var source = CreateEdgeBitmap(16, 16);

        using var result = SkiaEncoder.ResizeImage(source, InfoFor(source, 16, 16));
        using var resultBitmap = SKBitmap.FromImage(result);

        // Unsharpened, so the edge is still exactly where it was.
        AssertSamePixels(source, resultBitmap);
    }

    [Fact]
    public void ResizeImage_Upscale_DoesNotSharpen()
    {
        using var source = CreateEdgeBitmap(8, 8);
        var targetInfo = InfoFor(source, 24, 24);

        using var result = SkiaEncoder.ResizeImage(source, targetInfo);
        using var resultBitmap = SKBitmap.FromImage(result);
        using var expected = DrawOnly(source, targetInfo, SkiaEncoder.UpscaleSamplingOptions);

        AssertSamePixels(expected, resultBitmap);
    }

    [Fact]
    public void ResizeImage_Downscale_StillSharpens()
    {
        using var source = CreateEdgeBitmap(32, 32);
        var targetInfo = InfoFor(source, 16, 16);

        using var result = SkiaEncoder.ResizeImage(source, targetInfo);
        using var resultBitmap = SKBitmap.FromImage(result);
        using var unsharpened = DrawOnly(source, targetInfo, SkiaEncoder.DefaultSamplingOptions);
        using var sharpened = DrawOnly(source, targetInfo, SkiaEncoder.DefaultSamplingOptions);
        SkiaEncoder.SharpenInPlace(sharpened);

        AssertSamePixels(sharpened, resultBitmap);
        // Guards the test itself: the edge has to be something sharpening actually changes.
        Assert.NotEqual(unsharpened.GetPixel(8, 8), sharpened.GetPixel(8, 8));
    }
}