mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Add a couple of png smoke tests that encode and then decode an image in png then does a visual compare to verify they render the same.pull/152/head
9 changed files with 470 additions and 18 deletions
@ -0,0 +1,62 @@ |
|||
// <copyright file="PngSmokeTests.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Tests.Formats.Png |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.IO; |
|||
using Xunit; |
|||
using ImageSharp.Formats; |
|||
using System.Linq; |
|||
using ImageSharp.IO; |
|||
|
|||
public class PngSmokeTests |
|||
{ |
|||
[Theory] |
|||
[WithTestPatternImages(300, 300, PixelTypes.All)] |
|||
public void WritesFileMarker<TColor>(TestImageProvider<TColor> provider) |
|||
where TColor : struct, IPixel<TColor> |
|||
{ |
|||
// does saving a file then repoening mean both files are identical???
|
|||
using (Image<TColor> image = provider.GetImage()) |
|||
using (MemoryStream ms = new MemoryStream()) |
|||
{ |
|||
image.Save(provider.Utility.GetTestOutputFileName("bmp")); |
|||
|
|||
image.Save(ms, new PngEncoder()); |
|||
ms.Position = 0; |
|||
using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) |
|||
{ |
|||
img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); |
|||
ImageComparer.VisualComparer(image, img2); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[WithTestPatternImages(300, 300, PixelTypes.All)] |
|||
public void Resize<TColor>(TestImageProvider<TColor> provider) |
|||
where TColor : struct, IPixel<TColor> |
|||
{ |
|||
// does saving a file then repoening mean both files are identical???
|
|||
using (Image<TColor> image = provider.GetImage()) |
|||
using (MemoryStream ms = new MemoryStream()) |
|||
{ |
|||
// image.Save(provider.Utility.GetTestOutputFileName("png"));
|
|||
image.Resize(100, 100); |
|||
// image.Save(provider.Utility.GetTestOutputFileName("png", "resize"));
|
|||
|
|||
image.Save(ms, new PngEncoder()); |
|||
ms.Position = 0; |
|||
using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) |
|||
{ |
|||
ImageComparer.VisualComparer(image, img2); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using System; |
|||
using ImageSharp; |
|||
using Xunit; |
|||
|
|||
/// <summary>
|
|||
/// Class to perform simple image comparisons.
|
|||
/// </summary>
|
|||
public static class ImageComparer |
|||
{ |
|||
const int DefaultScalingFactor = 32; |
|||
const int DefaultSegmentThreshold = 3; |
|||
const float DefaultImageThreshold = 0.000f; |
|||
|
|||
public static void VisualComparer<TColorA, TColorB>(Image<TColorA> expected, Image<TColorB> actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) |
|||
where TColorA : struct, IPixel<TColorA> |
|||
where TColorB : struct, IPixel<TColorB> |
|||
{ |
|||
var percentage = expected.PercentageDifference(actual, segmentThreshold, scalingFactor); |
|||
|
|||
Assert.InRange(percentage, 0, imageTheshold); |
|||
} |
|||
|
|||
public static float PercentageDifference<TColorA, TColorB>(this Image<TColorA> source, Image<TColorB> target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) |
|||
where TColorA : struct, IPixel<TColorA> |
|||
where TColorB : struct, IPixel<TColorB> |
|||
{ |
|||
// code adapted from https://www.codeproject.com/Articles/374386/Simple-image-comparison-in-NET
|
|||
Fast2DArray<byte> differences = GetDifferences(source, target, scalingFactor); |
|||
|
|||
int diffPixels = 0; |
|||
|
|||
foreach (byte b in differences.Data) |
|||
{ |
|||
if (b > segmentThreshold) { diffPixels++; } |
|||
} |
|||
|
|||
return diffPixels / (scalingFactor * scalingFactor); |
|||
} |
|||
|
|||
private static Fast2DArray<byte> GetDifferences<TColorA, TColorB>(Image<TColorA> source, Image<TColorB> target, int scalingFactor) |
|||
where TColorA : struct, IPixel<TColorA> |
|||
where TColorB : struct, IPixel<TColorB> |
|||
{ |
|||
Fast2DArray<byte> differences = new Fast2DArray<byte>(scalingFactor, scalingFactor); |
|||
Fast2DArray<byte> firstGray = source.GetGrayScaleValues(scalingFactor); |
|||
Fast2DArray<byte> secondGray = target.GetGrayScaleValues(scalingFactor); |
|||
|
|||
for (int y = 0; y < scalingFactor; y++) |
|||
{ |
|||
for (int x = 0; x < scalingFactor; x++) |
|||
{ |
|||
differences[x, y] = (byte)Math.Abs(firstGray[x, y] - secondGray[x, y]); |
|||
} |
|||
} |
|||
|
|||
return differences; |
|||
} |
|||
|
|||
private static Fast2DArray<byte> GetGrayScaleValues<TColorA>(this Image<TColorA> source, int scalingFactor) |
|||
where TColorA : struct, IPixel<TColorA> |
|||
{ |
|||
byte[] buffer = new byte[4]; |
|||
using (Image<TColorA> img = new Image<TColorA>(source).Resize(scalingFactor, scalingFactor).Grayscale()) |
|||
{ |
|||
using (PixelAccessor<TColorA> pixels = img.Lock()) |
|||
{ |
|||
Fast2DArray<byte> grayScale = new Fast2DArray<byte>(scalingFactor, scalingFactor); |
|||
for (int y = 0; y < scalingFactor; y++) |
|||
{ |
|||
for (int x = 0; x < scalingFactor; x++) |
|||
{ |
|||
pixels[x, y].ToXyzBytes(buffer, 0); |
|||
grayScale[x, y] = buffer[1]; |
|||
} |
|||
} |
|||
|
|||
return grayScale; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
// <copyright file="WithBlankImagesAttribute.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using System; |
|||
using System.Reflection; |
|||
|
|||
/// <summary>
|
|||
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which produce a blank image of size width * height.
|
|||
/// One <see cref="TestImageProvider{TColor}"/> instance will be passed for each the pixel format defined by the pixelTypes parameter
|
|||
/// </summary>
|
|||
public class WithTestPatternImagesAttribute : ImageDataAttributeBase |
|||
{ |
|||
/// <summary>
|
|||
/// Triggers passing an <see cref="TestImageProvider{TColor}"/> that produces a test pattern image of size width * height
|
|||
/// </summary>
|
|||
/// <param name="width">The required width</param>
|
|||
/// <param name="height">The required height</param>
|
|||
/// <param name="pixelTypes">The requested parameter</param>
|
|||
/// <param name="additionalParameters">Additional theory parameter values</param>
|
|||
public WithTestPatternImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) |
|||
: base(pixelTypes, additionalParameters) |
|||
{ |
|||
this.Width = width; |
|||
this.Height = height; |
|||
} |
|||
|
|||
public int Width { get; } |
|||
public int Height { get; } |
|||
|
|||
protected override string GetFactoryMethodName(MethodInfo testMethod) => "TestPattern"; |
|||
|
|||
protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.Width, this.Height }; |
|||
} |
|||
} |
|||
@ -0,0 +1,184 @@ |
|||
// <copyright file="BlankProvider.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Tests |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Numerics; |
|||
|
|||
public abstract partial class TestImageProvider<TColor> |
|||
where TColor : struct, IPixel<TColor> |
|||
{ |
|||
private class TestPatternProvider : TestImageProvider<TColor> |
|||
{ |
|||
static Dictionary<string, Image<TColor>> testImages = new Dictionary<string, Image<TColor>>(); |
|||
|
|||
public TestPatternProvider(int width, int height) |
|||
{ |
|||
this.Width = width; |
|||
this.Height = height; |
|||
} |
|||
|
|||
public override string SourceFileOrDescription => $"TestPattern{this.Width}x{this.Height}"; |
|||
|
|||
protected int Height { get; } |
|||
|
|||
protected int Width { get; } |
|||
|
|||
public override Image<TColor> GetImage() |
|||
{ |
|||
lock (testImages) |
|||
{ |
|||
if (!testImages.ContainsKey(this.SourceFileOrDescription)) |
|||
{ |
|||
var image = new Image<TColor>(this.Width, this.Height); |
|||
DrawTestPattern(image); |
|||
testImages.Add(this.SourceFileOrDescription, image); |
|||
} |
|||
|
|||
return new Image<TColor>(testImages[this.SourceFileOrDescription]); |
|||
} |
|||
} |
|||
|
|||
private static void DrawTestPattern(Image<TColor> image) |
|||
{ |
|||
// first lets split the image into 4 quadrants
|
|||
using (var pixels = image.Lock()) |
|||
{ |
|||
BlackWhiteChecker(pixels); // top left
|
|||
HorizontalLines(pixels); // top right
|
|||
TransparentGradients(pixels); // bottom left
|
|||
Raninbow(pixels); // bottom right
|
|||
} |
|||
} |
|||
|
|||
private static void HorizontalLines(PixelAccessor<TColor> pixels) |
|||
{ |
|||
// topLeft
|
|||
int left = pixels.Width / 2; |
|||
int right = pixels.Width; |
|||
int top = 0; |
|||
int bottom = pixels.Height / 2; |
|||
int stride = pixels.Width / 12; |
|||
TColor[] c = { |
|||
NamedColors<TColor>.HotPink, |
|||
NamedColors<TColor>.Blue |
|||
}; |
|||
int p = 0; |
|||
for (var y = top; y < bottom; y++) |
|||
{ |
|||
for (var x = left; x < right; x++) |
|||
{ |
|||
if (x % stride == 0) |
|||
{ |
|||
p++; |
|||
p = p % c.Length; |
|||
} |
|||
pixels[x, y] = c[p]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static void BlackWhiteChecker(PixelAccessor<TColor> pixels) |
|||
{ |
|||
// topLeft
|
|||
int left = 0; |
|||
int right = pixels.Width / 2; |
|||
int top = 0; |
|||
int bottom = pixels.Height / 2; |
|||
int stride = pixels.Width / 6; |
|||
TColor[] c = { |
|||
NamedColors<TColor>.Black, |
|||
NamedColors<TColor>.White |
|||
}; |
|||
|
|||
int p = 0; |
|||
for (var y = top; y < bottom; y++) |
|||
{ |
|||
if (y % stride == 0) |
|||
{ |
|||
p++; |
|||
p = p % c.Length; |
|||
} |
|||
var pstart = p; |
|||
for (var x = left; x < right; x++) |
|||
{ |
|||
if (x % stride == 0) |
|||
{ |
|||
p++; |
|||
p = p % c.Length; |
|||
} |
|||
pixels[x, y] = c[p]; |
|||
} |
|||
p = pstart; |
|||
} |
|||
} |
|||
|
|||
private static void TransparentGradients(PixelAccessor<TColor> pixels) |
|||
{ |
|||
// topLeft
|
|||
int left = 0; |
|||
int right = pixels.Width / 2; |
|||
int top = pixels.Height / 2; |
|||
int bottom = pixels.Height; |
|||
int height = (int)Math.Ceiling(pixels.Height / 6f); |
|||
|
|||
Vector4 red = Color.Red.ToVector4(); // use real color so we can see har it translates in the test pattern
|
|||
Vector4 green = Color.Green.ToVector4(); // use real color so we can see har it translates in the test pattern
|
|||
Vector4 blue = Color.Blue.ToVector4(); // use real color so we can see har it translates in the test pattern
|
|||
|
|||
TColor c = default(TColor); |
|||
|
|||
for (var x = left; x < right; x++) |
|||
{ |
|||
blue.W = red.W = green.W = (float)x / (float)right; |
|||
|
|||
c.PackFromVector4(red); |
|||
var topBand = top; |
|||
for (var y = topBand; y < top + height; y++) |
|||
{ |
|||
pixels[x, y] = c; |
|||
} |
|||
topBand = topBand + height; |
|||
c.PackFromVector4(green); |
|||
for (var y = topBand; y < topBand + height; y++) |
|||
{ |
|||
pixels[x, y] = c; |
|||
} |
|||
topBand = topBand + height; |
|||
c.PackFromVector4(blue); |
|||
for (var y = topBand; y < bottom; y++) |
|||
{ |
|||
pixels[x, y] = c; |
|||
} |
|||
} |
|||
|
|||
} |
|||
private static void Raninbow(PixelAccessor<TColor> pixels) |
|||
{ |
|||
int left = pixels.Width / 2; |
|||
int right = pixels.Width; |
|||
int top = pixels.Height / 2; |
|||
int bottom = pixels.Height; |
|||
|
|||
int pixelCount = left * top; |
|||
uint stepsPerPixel = (uint)(uint.MaxValue / pixelCount); |
|||
TColor c = default(TColor); |
|||
Color t = new Color(0); |
|||
uint inital = 0; |
|||
for (var x = left; x < right; x++) |
|||
for (var y = top; y < bottom; y++) |
|||
{ |
|||
t.PackedValue += stepsPerPixel; |
|||
var v = t.ToVector4(); |
|||
//v.W = (x - left) / (float)left;
|
|||
c.PackFromVector4(v); |
|||
pixels[x, y] = c; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue