From 8d726512c67cee86a9e536b69da565675265dcfb Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 20 Aug 2017 17:17:08 +0200 Subject: [PATCH] code for verifying original PDF.js output --- .../Formats/Jpg/JpegDecoderTests.cs | 45 +++++++++- .../Jpg/pdfjs}/baseline/ycck - Copy.jpg | 0 .../Formats/Jpg/pdfjs/jpeg-converter.htm | 89 +++++++++++++++++++ .../Jpg => Formats/Jpg/pdfjs}/jpeg.htm | 0 .../Formats/Jpg => Formats/Jpg/pdfjs}/jpg.js | 0 .../ImageComparison/ImageSimilarityReport.cs | 6 +- .../TestUtilities/ImagingTestCaseUtility.cs | 5 +- .../ReferenceCodecs/SystemDrawingBridge.cs | 68 +++++++++++++- .../SystemDrawingReferenceDecoder.cs | 13 ++- .../TestUtilities/TestImageExtensions.cs | 20 +++-- .../Tests/ReferenceCodecTests.cs | 61 ++++++++++++- 11 files changed, 288 insertions(+), 19 deletions(-) rename tests/ImageSharp.Tests/{TestImages/Formats/Jpg => Formats/Jpg/pdfjs}/baseline/ycck - Copy.jpg (100%) create mode 100644 tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm rename tests/ImageSharp.Tests/{TestImages/Formats/Jpg => Formats/Jpg/pdfjs}/jpeg.htm (100%) rename tests/ImageSharp.Tests/{TestImages/Formats/Jpg => Formats/Jpg/pdfjs}/jpg.js (100%) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index a2f7bd8b5..e6e469aea 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -18,6 +18,7 @@ namespace SixLabors.ImageSharp.Tests using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; using Xunit.Abstractions; @@ -45,6 +46,11 @@ namespace SixLabors.ImageSharp.Tests private static readonly ImageComparer VeryTolerantJpegComparer = ImageComparer.Tolerant(0.005f, pixelThresholdHammingDistance: 4); + // BUG: PDF.js output is wrong on spectral level! + private static readonly ImageComparer PdfJsProgressiveComparer = + ImageComparer.Tolerant(0.015f, pixelThresholdHammingDistance: 4); + + public JpegDecoderTests(ITestOutputHelper output) { this.Output = output; @@ -108,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests { image.DebugSave(provider); - image.CompareToReferenceOutput(provider, VeryTolerantJpegComparer, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput(provider, PdfJsProgressiveComparer, appendPixelTypeToFileName: false); } } @@ -130,11 +136,12 @@ namespace SixLabors.ImageSharp.Tests where TPixel : struct, IPixel { var reportingComparer = ImageComparer.Tolerant(0, 0); - + ImageSimilarityReport report = image.GetReferenceOutputSimilarityReports( provider, reportingComparer, - appendPixelTypeToFileName: false).SingleOrDefault(); + appendPixelTypeToFileName: false + ).SingleOrDefault(); if (report != null && report.TotalNormalizedDifference.HasValue) { @@ -258,5 +265,37 @@ namespace SixLabors.ImageSharp.Tests Assert.Null(image.MetaData.ExifProfile); } } + + // DEBUG ONLY! + // The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm" + // into "\tests\Images\ActualOutput\JpegDecoderTests\" + [Theory] + [WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgba32, "PdfJsOriginal_progress.png")] + public void ValidateProgressivePdfJsOutput(TestImageProvider provider, + string pdfJsOriginalResultImage) + where TPixel : struct, IPixel + { + // tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm + string pdfJsOriginalResultPath = Path.Combine( + provider.Utility.GetTestOutputDir(), + pdfJsOriginalResultImage); + + byte[] sourceBytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; + + provider.Utility.TestName = nameof(this.DecodeProgressiveJpeg); + + var comparer = ImageComparer.Tolerant(0, 0); + + using (Image expectedImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) + using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) + using (var pdfJsPortResult = Image.Load(sourceBytes, PdfJsJpegDecoder)) + { + ImageSimilarityReport originalReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsOriginalResult); + ImageSimilarityReport portReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsPortResult); + + this.Output.WriteLine($"Difference for PDF.js ORIGINAL: {originalReport.DifferencePercentage}"); + this.Output.WriteLine($"Difference for PORT: {portReport.DifferencePercentage}"); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ycck - Copy.jpg b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/baseline/ycck - Copy.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ycck - Copy.jpg rename to tests/ImageSharp.Tests/Formats/Jpg/pdfjs/baseline/ycck - Copy.jpg diff --git a/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm new file mode 100644 index 000000000..40af957fc --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm @@ -0,0 +1,89 @@ + + + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/jpeg.htm b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg.htm similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/jpeg.htm rename to tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg.htm diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/jpg.js b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpg.js similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/jpg.js rename to tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpg.js diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs index e0e847a83..22a8d2cff 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs @@ -24,6 +24,10 @@ public float? TotalNormalizedDifference { get; } + public string DifferencePercentage => this.TotalNormalizedDifference.HasValue + ? $"{this.TotalNormalizedDifference.Value * 100:0.0000}%" + : "?"; + public IImageBase ExpectedImage { get; } public IImageBase ActualImage { get; } @@ -42,7 +46,7 @@ var sb = new StringBuilder(); if (this.TotalNormalizedDifference.HasValue) { - sb.AppendLine($"Total difference: {this.TotalNormalizedDifference.Value * 100:0.0000}%"); + sb.AppendLine($"Total difference: {this.DifferencePercentage}"); } int max = Math.Min(5, this.Differences.Length); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index d0626c87e..88c69a979 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests /// The image instance /// The requested extension /// Optional encoder - public void SaveTestOutputFile( + public string SaveTestOutputFile( Image image, string extension = null, IImageEncoder encoder = null, @@ -146,6 +146,7 @@ namespace SixLabors.ImageSharp.Tests { image.Save(stream, encoder); } + return path; } internal string GetReferenceOutputFileName( @@ -190,7 +191,7 @@ namespace SixLabors.ImageSharp.Tests // return encoder; //} - private string GetTestOutputDir() + internal string GetTestOutputDir() { string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName); return this.CreateOutputDirectory(testGroupName); diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index 474569c4c..758be0c36 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -52,7 +52,29 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } } - internal static unsafe Image FromSystemDrawingBitmap(System.Drawing.Bitmap bmp) + private static void FromRgb24(Span source, Span dest) + where TPixel : struct, IPixel + { + int length = source.Length; + Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); + + using (var rgbaBuffer = new Buffer(length)) + { + PixelOperations.Instance.ToRgb24(source, rgbaBuffer, length); + + for (int i = 0; i < length; i++) + { + ref Rgb24 s = ref rgbaBuffer[i]; + ref TPixel d = ref dest[i]; + var rgba = default(Rgba32); + s.ToRgba32(ref rgba); + + d.PackFromRgba32(rgba); + } + } + } + + internal static unsafe Image FromFromArgb32SystemDrawingBitmap(System.Drawing.Bitmap bmp) where TPixel : struct, IPixel { int w = bmp.Width; @@ -62,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs if (bmp.PixelFormat != PixelFormat.Format32bppArgb) { - throw new ArgumentException("FromSystemDrawingBitmap(): pixel format not supported", nameof(bmp)); + throw new ArgumentException($"FromFromArgb32SystemDrawingBitmap(): pixel format should be Argb32!", nameof(bmp)); } BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); @@ -91,6 +113,48 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return image; } + /// + /// TODO: Doesn not work yet! + /// + internal static unsafe Image FromFromRgb24SystemDrawingBitmap(System.Drawing.Bitmap bmp) + where TPixel : struct, IPixel + { + int w = bmp.Width; + int h = bmp.Height; + + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); + + if (bmp.PixelFormat != PixelFormat.Format24bppRgb) + { + throw new ArgumentException($"FromFromArgb32SystemDrawingBitmap(): pixel format should be Rgb24!", nameof(bmp)); + } + + BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); + byte* sourcePtrBase = (byte*)data.Scan0; + + long sourceRowByteCount = data.Stride; + long destRowByteCount = w * sizeof(Rgb24); + + var image = new Image(w, h); + + using (var workBuffer = new Buffer(w)) + { + var destPtr = (Rgb24*)workBuffer.Pin(); + for (int y = 0; y < h; y++) + { + Span row = image.GetRowSpan(y); + + byte* sourcePtr = sourcePtrBase + data.Stride * y; + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + + FromRgb24(workBuffer, row); + } + } + + return image; + } + internal static unsafe System.Drawing.Bitmap ToSystemDrawingBitmap(Image image) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 789012ec4..b5039fcf2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -9,6 +9,8 @@ using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; +using PixelFormat = System.Drawing.Imaging.PixelFormat; + namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { public class SystemDrawingReferenceDecoder : IImageDecoder @@ -20,9 +22,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { using (var sourceBitmap = new System.Drawing.Bitmap(stream)) { - if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) + if (sourceBitmap.PixelFormat == PixelFormat.Format32bppArgb) + { + return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sourceBitmap); + } + + if (sourceBitmap.PixelFormat == PixelFormat.Format24bppRgb) { - return SystemDrawingBridge.FromSystemDrawingBitmap(sourceBitmap); + } using (var convertedBitmap = new System.Drawing.Bitmap( @@ -39,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs g.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height); } - return SystemDrawingBridge.FromSystemDrawingBitmap(convertedBitmap); + return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(convertedBitmap); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 42ee89f74..063137ea9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -90,6 +90,7 @@ namespace SixLabors.ImageSharp.Tests /// The extension /// A boolean indicating whether we should debug save + compare against a grayscale image, smaller in size. /// A boolean indicating whether to append the pixel type to the output file name. + /// Custom decoder /// public static Image CompareToReferenceOutput( this Image image, @@ -98,14 +99,16 @@ namespace SixLabors.ImageSharp.Tests object testOutputDetails = null, string extension = "png", bool grayscale = false, - bool appendPixelTypeToFileName = true) + bool appendPixelTypeToFileName = true, + IImageDecoder decoder = null) where TPixel : struct, IPixel { using (Image referenceImage = GetReferenceOutputImage( provider, testOutputDetails, extension, - appendPixelTypeToFileName)) + appendPixelTypeToFileName, + decoder)) { comparer.VerifySimilarity(referenceImage, image); } @@ -116,7 +119,8 @@ namespace SixLabors.ImageSharp.Tests public static Image GetReferenceOutputImage(this ITestImageProvider provider, object testOutputDetails = null, string extension = "png", - bool appendPixelTypeToFileName = true) + bool appendPixelTypeToFileName = true, + IImageDecoder decoder = null) where TPixel : struct, IPixel { string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); @@ -126,7 +130,9 @@ namespace SixLabors.ImageSharp.Tests throw new Exception("Reference output file missing: " + referenceOutputFile); } - return Image.Load(referenceOutputFile); + decoder = decoder ?? TestEnvironment.GetReferenceDecoder(referenceOutputFile); + + return Image.Load(referenceOutputFile, decoder); } public static IEnumerable GetReferenceOutputSimilarityReports( @@ -135,13 +141,15 @@ namespace SixLabors.ImageSharp.Tests ImageComparer comparer, object testOutputDetails = null, string extension = "png", - bool appendPixelTypeToFileName = true) + bool appendPixelTypeToFileName = true, + IImageDecoder decoder = null) where TPixel : struct, IPixel { using (Image referenceImage = provider.GetReferenceOutputImage( testOutputDetails, extension, - appendPixelTypeToFileName)) + appendPixelTypeToFileName, + decoder)) { return comparer.CompareImages(referenceImage, image); } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs index 8b7a828b6..f223550cc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs @@ -1,6 +1,8 @@ namespace SixLabors.ImageSharp.Tests { + using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; @@ -32,20 +34,75 @@ namespace SixLabors.ImageSharp.Tests [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32 | PixelTypes.Bgra32)] - public void FromSystemDrawingBitmap(TestImageProvider dummyProvider) + public void FromFromArgb32SystemDrawingBitmap(TestImageProvider dummyProvider) where TPixel : struct, IPixel { string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); using (var sdBitmap = new System.Drawing.Bitmap(path)) { - using (Image image = SystemDrawingBridge.FromSystemDrawingBitmap(sdBitmap)) + using (Image image = SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sdBitmap)) { image.DebugSave(dummyProvider); } } } + private static string SavePng(TestImageProvider provider, PngColorType pngColorType) + where TPixel : struct, IPixel + { + using (Image sourceImage = provider.GetImage()) + { + if (pngColorType != PngColorType.RgbWithAlpha) + { + sourceImage.Mutate(c => c.Alpha(1)); + } + + var encoder = new PngEncoder() { PngColorType = pngColorType }; + return provider.Utility.SaveTestOutputFile(sourceImage, "png", encoder); + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void FromFromArgb32SystemDrawingBitmap2(TestImageProvider provider) + where TPixel : struct, IPixel + { + string path = SavePng(provider, PngColorType.RgbWithAlpha); + + using (var sdBitmap = new System.Drawing.Bitmap(path)) + { + using (Image original = provider.GetImage()) + using (Image resaved = SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sdBitmap)) + { + ImageComparer comparer = ImageComparer.Exact; + comparer.VerifySimilarity(original, resaved); + } + } + } + + [Theory(Skip = "Doesen't work yet :(")] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void FromFromRgb24SystemDrawingBitmap2(TestImageProvider provider) + where TPixel : struct, IPixel + { + string path = SavePng(provider, PngColorType.Rgb); + + using (Image original = provider.GetImage()) + { + original.Mutate(c => c.Alpha(1)); + using (var sdBitmap = new System.Drawing.Bitmap(path)) + { + using (Image resaved = SystemDrawingBridge.FromFromRgb24SystemDrawingBitmap(sdBitmap)) + { + resaved.Mutate(c => c.Alpha(1)); + ImageComparer comparer = ImageComparer.Exact; + comparer.VerifySimilarity(original, resaved); + } + } + } + } + [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32 | PixelTypes.Bgra32)] public void OpenWithReferenceDecoder(TestImageProvider dummyProvider)