using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
namespace Svg.UnitTests
{
///
///
[TestClass]
public class ImageComparisonTest
{
public TestContext TestContext { get; set; }
///
/// Compares SVG images against reference PNG images from the W3C SVG 1.1 test suite.
/// This tests 158 out of 179 passing tests - the rest will not pass
/// the test for several reasons.
/// Note that with the current test there are still a lot of false positives,
/// so this is not a definitive test for image equality yet.
///
[TestMethod]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV",
@"|DataDirectory|\..\..\PassingTests.csv",
"PassingTests#csv", DataAccessMethod.Sequential)]
public void CompareSvgImageWithReference()
{
var basePath = TestContext.TestRunDirectory;
while (!basePath.ToLower().EndsWith("svg"))
{
basePath = Path.GetDirectoryName(basePath);
}
basePath = Path.Combine(basePath, "Tests", "W3CTestSuite");
var svgBasePath = Path.Combine(basePath, "svg");
var baseName = TestContext.DataRow[0] as string;
var svgPath = Path.Combine(basePath, "svg", baseName + ".svg");
var pngPath = Path.Combine(basePath, "png", baseName + ".png");
var pngImage = Image.FromFile(pngPath);
var svgImage = LoadSvgImage(baseName, svgPath);
Assert.AreNotEqual(null, pngImage, "Failed to load " + pngPath);
Assert.AreNotEqual(null, svgImage, "Failed to load " + svgPath);
var difference = svgImage.PercentageDifference(pngImage);
Assert.IsTrue(difference < 0.05,
baseName + ": Difference is " + (difference * 100.0).ToString() + "%");
}
///
/// Enable this test to output the calculate percentage difference
/// of all considered W3C tests.
/// Can be used to enhance the difference calculation.
///
// [TestClass]
public void RecordDiffForAllSvgImagesWithReference()
{
var basePath = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(
TestContext.TestRunDirectory)));
basePath = Path.Combine(basePath, "Tests", "W3CTestSuite");
var svgBasePath = Path.Combine(basePath, "svg");
string[] lines = File.ReadAllLines(@"..\..\..\..\Tests\Svg.UnitTests\all.csv");
foreach (var baseName in lines)
{
var svgPath = Path.Combine(basePath, "svg", baseName + ".svg");
var pngPath = Path.Combine(basePath, "png", baseName + ".png");
if (File.Exists(pngPath) && File.Exists(svgPath))
{
var pngImage = Image.FromFile(pngPath);
var svgImage = LoadSvgImage(baseName, svgPath);
if (pngImage != null && svgImage != null)
{
var difference = svgImage.PercentageDifference(pngImage);
Console.WriteLine(baseName + " " + (difference * 100.0).ToString());
}
}
}
}
///
/// Load the SVG image the same way as in the SVGW3CTestRunner.
///
private static Image LoadSvgImage(string fileName, string svgPath)
{
var doc = new SvgDocument();
Image svgImage;
try
{
doc = SvgDocument.Open(svgPath);
if (fileName.StartsWith("__"))
{
svgImage = doc.Draw();
}
else
{
var img = new Bitmap(480, 360);
doc.Draw(img);
svgImage = img;
}
}
catch (Exception)
{
svgImage = null;
}
return svgImage;
}
}
///
/// Taken from https://web.archive.org/web/20130111215043/http://www.switchonthecode.com/tutorials/csharp-tutorial-convert-a-color-image-to-grayscale
/// and slightly modified.
/// Image width and height, default threshold and handling of alpha values have been adapted.
///
public static class ExtensionMethods
{
private static int ImageWidth = 64;
private static int ImageHeight = 64;
public static float PercentageDifference(this Image img1, Image img2, byte threshold = 10)
{
byte[,] differences = img1.GetDifferences(img2);
int diffPixels = 0;
foreach (byte b in differences)
{
if (b > threshold) { diffPixels++; }
}
return diffPixels / (float)(ImageWidth * ImageHeight);
}
public static Image Resize(this Image originalImage, int newWidth, int newHeight)
{
Image smallVersion = new Bitmap(newWidth, newHeight);
using (Graphics g = Graphics.FromImage(smallVersion))
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(originalImage, 0, 0, newWidth, newHeight);
}
return smallVersion;
}
public static byte[,] GetGrayScaleValues(this Image img)
{
using (Bitmap thisOne = (Bitmap)img.Resize(ImageWidth, ImageHeight).GetGrayScaleVersion())
{
byte[,] grayScale = new byte[ImageWidth, ImageHeight];
for (int y = 0; y < ImageHeight; y++)
{
for (int x = 0; x < ImageWidth; x++)
{
var pixel = thisOne.GetPixel(x, y);
var alpha = thisOne.GetPixel(x, y).A;
var gray = thisOne.GetPixel(x, y).R;
grayScale[x, y] = (byte)Math.Abs(gray * alpha / 255);
}
}
return grayScale;
}
}
//the colormatrix needed to grayscale an image
static readonly ColorMatrix ColorMatrix = new ColorMatrix(new float[][]
{
new float[] {.3f, .3f, .3f, 0, 0},
new float[] {.59f, .59f, .59f, 0, 0},
new float[] {.11f, .11f, .11f, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
});
public static Image GetGrayScaleVersion(this Image original)
{
//create a blank bitmap the same size as original
//https://web.archive.org/web/20130111215043/http://www.switchonthecode.com/tutorials/csharp-tutorial-convert-a-color-image-to-grayscale
Bitmap newBitmap = new Bitmap(original.Width, original.Height);
//get a graphics object from the new image
using (Graphics g = Graphics.FromImage(newBitmap))
{
//create some image attributes
ImageAttributes attributes = new ImageAttributes();
//set the color matrix attribute
attributes.SetColorMatrix(ColorMatrix);
//draw the original image on the new image
//using the grayscale color matrix
g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);
}
return newBitmap;
}
public static byte[,] GetDifferences(this Image img1, Image img2)
{
Bitmap thisOne = (Bitmap)img1.Resize(ImageWidth, ImageHeight).GetGrayScaleVersion();
Bitmap theOtherOne = (Bitmap)img2.Resize(ImageWidth, ImageHeight).GetGrayScaleVersion();
byte[,] differences = new byte[ImageWidth, ImageHeight];
byte[,] firstGray = thisOne.GetGrayScaleValues();
byte[,] secondGray = theOtherOne.GetGrayScaleValues();
for (int y = 0; y < ImageHeight; y++)
{
for (int x = 0; x < ImageWidth; x++)
{
differences[x, y] = (byte)Math.Abs(firstGray[x, y] - secondGray[x, y]);
}
}
thisOne.Dispose();
theOtherOne.Dispose();
return differences;
}
}
}