mirror of https://github.com/SixLabors/ImageSharp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
12 KiB
328 lines
12 KiB
// Copyright (c) Six Labors and contributors.
|
|
// Licensed under the Apache License, Version 2.0.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
using SixLabors.ImageSharp.Formats;
|
|
using SixLabors.ImageSharp.PixelFormats;
|
|
|
|
namespace SixLabors.ImageSharp.Tests
|
|
{
|
|
/// <summary>
|
|
/// Utility class to provide information about the test image & the test case for the test code,
|
|
/// and help managing IO.
|
|
/// </summary>
|
|
public class ImagingTestCaseUtility
|
|
{
|
|
/// <summary>
|
|
/// Name of the TPixel in the owner <see cref="TestImageProvider{TPixel}"/>
|
|
/// </summary>
|
|
public string PixelTypeName { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// The name of the file which is provided by <see cref="TestImageProvider{TPixel}"/>
|
|
/// Or a short string describing the image in the case of a non-file based image provider.
|
|
/// </summary>
|
|
public string SourceFileOrDescription { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// By default this is the name of the test class, but it's possible to change it
|
|
/// </summary>
|
|
public string TestGroupName { get; set; } = string.Empty;
|
|
|
|
public string OutputSubfolderName { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// The name of the test case (by default)
|
|
/// </summary>
|
|
public string TestName { get; set; } = string.Empty;
|
|
|
|
private string GetTestOutputFileNameImpl(
|
|
string extension,
|
|
string details,
|
|
bool appendPixelTypeToFileName,
|
|
bool appendSourceFileOrDescription)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(extension))
|
|
{
|
|
extension = null;
|
|
}
|
|
|
|
string fn = appendSourceFileOrDescription
|
|
? Path.GetFileNameWithoutExtension(this.SourceFileOrDescription)
|
|
: "";
|
|
|
|
if (string.IsNullOrWhiteSpace(extension))
|
|
{
|
|
extension = Path.GetExtension(this.SourceFileOrDescription);
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(extension))
|
|
{
|
|
extension = ".bmp";
|
|
}
|
|
extension = extension.ToLower();
|
|
|
|
if (extension[0] != '.')
|
|
{
|
|
extension = '.' + extension;
|
|
}
|
|
|
|
if (fn != string.Empty)
|
|
{
|
|
fn = '_' + fn;
|
|
}
|
|
|
|
string pixName = "";
|
|
|
|
if (appendPixelTypeToFileName)
|
|
{
|
|
pixName = this.PixelTypeName;
|
|
|
|
if (pixName != string.Empty)
|
|
{
|
|
pixName = '_' + pixName;
|
|
}
|
|
}
|
|
|
|
details = details ?? string.Empty;
|
|
if (details != string.Empty)
|
|
{
|
|
details = '_' + details;
|
|
}
|
|
|
|
return TestUtils.AsInvariantString($"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{details}{extension}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the recommended file name for the output of the test
|
|
/// </summary>
|
|
/// <param name="extension">The required extension</param>
|
|
/// <param name="testOutputDetails">The settings modifying the output path</param>
|
|
/// <param name="appendPixelTypeToFileName">A boolean indicating whether to append the pixel type to output file name.</param>
|
|
/// <param name="appendSourceFileOrDescription">A boolean indicating whether to append <see cref="ITestImageProvider.SourceFileOrDescription"/> to the test output file name.</param>
|
|
/// <returns>The file test name</returns>
|
|
public string GetTestOutputFileName(
|
|
string extension = null,
|
|
object testOutputDetails = null,
|
|
bool appendPixelTypeToFileName = true,
|
|
bool appendSourceFileOrDescription = true)
|
|
{
|
|
string detailsString = null;
|
|
|
|
if (testOutputDetails is FormattableString fs)
|
|
{
|
|
detailsString = fs.AsInvariantString();
|
|
}
|
|
else if (testOutputDetails is string s)
|
|
{
|
|
detailsString = s;
|
|
}
|
|
else if (testOutputDetails != null)
|
|
{
|
|
Type type = testOutputDetails.GetType();
|
|
TypeInfo info = type.GetTypeInfo();
|
|
if (info.IsPrimitive || info.IsEnum || type == typeof(decimal))
|
|
{
|
|
detailsString = TestUtils.AsInvariantString($"{testOutputDetails}");
|
|
}
|
|
else
|
|
{
|
|
IEnumerable<PropertyInfo> properties = testOutputDetails.GetType().GetRuntimeProperties();
|
|
|
|
detailsString = string.Join(
|
|
"_",
|
|
properties.ToDictionary(x => x.Name, x => x.GetValue(testOutputDetails))
|
|
.Select(x => TestUtils.AsInvariantString($"{x.Key}-{x.Value}"))
|
|
);
|
|
}
|
|
}
|
|
|
|
return this.GetTestOutputFileNameImpl(
|
|
extension,
|
|
detailsString,
|
|
appendPixelTypeToFileName,
|
|
appendSourceFileOrDescription);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encodes image by the format matching the required extension, than saves it to the recommended output file.
|
|
/// </summary>
|
|
/// <param name="image">The image instance</param>
|
|
/// <param name="extension">The requested extension</param>
|
|
/// <param name="encoder">Optional encoder</param>
|
|
/// <param name="appendPixelTypeToFileName">A value indicating whether to append the pixel type to the test output file name</param>
|
|
/// <param name="appendSourceFileOrDescription">A boolean indicating whether to append <see cref="ITestImageProvider.SourceFileOrDescription"/> to the test output file name.</param>
|
|
/// <param name="testOutputDetails">Additional information to append to the test output file name</param>
|
|
public string SaveTestOutputFile(
|
|
Image image,
|
|
string extension = null,
|
|
IImageEncoder encoder = null,
|
|
object testOutputDetails = null,
|
|
bool appendPixelTypeToFileName = true,
|
|
bool appendSourceFileOrDescription = true)
|
|
{
|
|
string path = this.GetTestOutputFileName(
|
|
extension,
|
|
testOutputDetails,
|
|
appendPixelTypeToFileName,
|
|
appendSourceFileOrDescription);
|
|
|
|
encoder = encoder ?? TestEnvironment.GetReferenceEncoder(path);
|
|
|
|
using (FileStream stream = File.OpenWrite(path))
|
|
{
|
|
image.Save(stream, encoder);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
public IEnumerable<string> GetTestOutputFileNamesMultiFrame(
|
|
int frameCount,
|
|
string extension = null,
|
|
object testOutputDetails = null,
|
|
bool appendPixelTypeToFileName = true,
|
|
bool appendSourceFileOrDescription = true)
|
|
{
|
|
string baseDir = this.GetTestOutputFileName("", testOutputDetails, appendPixelTypeToFileName, appendSourceFileOrDescription);
|
|
|
|
if (!Directory.Exists(baseDir))
|
|
{
|
|
Directory.CreateDirectory(baseDir);
|
|
}
|
|
|
|
for (int i = 0; i < frameCount; i++)
|
|
{
|
|
string filePath = $"{baseDir}/{i:D2}.{extension}";
|
|
yield return filePath;
|
|
}
|
|
}
|
|
|
|
public string[] SaveTestOutputFileMultiFrame<TPixel>(
|
|
Image<TPixel> image,
|
|
string extension = "png",
|
|
IImageEncoder encoder = null,
|
|
object testOutputDetails = null,
|
|
bool appendPixelTypeToFileName = true)
|
|
where TPixel : struct, IPixel<TPixel>
|
|
{
|
|
encoder = encoder ?? TestEnvironment.GetReferenceEncoder($"foo.{extension}");
|
|
|
|
string[] files = this.GetTestOutputFileNamesMultiFrame(
|
|
image.Frames.Count,
|
|
extension,
|
|
testOutputDetails,
|
|
appendPixelTypeToFileName).ToArray();
|
|
|
|
for (int i = 0; i < image.Frames.Count; i++)
|
|
{
|
|
using (Image<TPixel> frameImage = image.Frames.CloneFrame(i))
|
|
{
|
|
string filePath = files[i];
|
|
using (FileStream stream = File.OpenWrite(filePath))
|
|
{
|
|
frameImage.Save(stream, encoder);
|
|
}
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
internal string GetReferenceOutputFileName(
|
|
string extension,
|
|
object testOutputDetails,
|
|
bool appendPixelTypeToFileName,
|
|
bool appendSourceFileOrDescription)
|
|
{
|
|
return TestEnvironment.GetReferenceOutputFileName(
|
|
this.GetTestOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName, appendSourceFileOrDescription)
|
|
);
|
|
}
|
|
|
|
public string[] GetReferenceOutputFileNamesMultiFrame(
|
|
int frameCount,
|
|
string extension,
|
|
object testOutputDetails,
|
|
bool appendPixelTypeToFileName = true)
|
|
{
|
|
return this.GetTestOutputFileNamesMultiFrame(frameCount, extension, testOutputDetails)
|
|
.Select(TestEnvironment.GetReferenceOutputFileName).ToArray();
|
|
}
|
|
|
|
internal void Init(string typeName, string methodName, string outputSubfolderName)
|
|
{
|
|
this.TestGroupName = typeName;
|
|
this.TestName = methodName;
|
|
this.OutputSubfolderName = outputSubfolderName;
|
|
}
|
|
|
|
internal string GetTestOutputDir()
|
|
{
|
|
string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName);
|
|
|
|
if (!string.IsNullOrEmpty(this.OutputSubfolderName))
|
|
{
|
|
testGroupName = Path.Combine(this.OutputSubfolderName, testGroupName);
|
|
}
|
|
|
|
return TestEnvironment.CreateOutputDirectory(testGroupName);
|
|
}
|
|
|
|
public static void ModifyPixel<TPixel>(Image<TPixel> img, int x, int y, byte perChannelChange)
|
|
where TPixel : struct, IPixel<TPixel> => ModifyPixel(img.Frames.RootFrame, x, y, perChannelChange);
|
|
|
|
public static void ModifyPixel<TPixel>(ImageFrame<TPixel> img, int x, int y, byte perChannelChange)
|
|
where TPixel : struct, IPixel<TPixel>
|
|
{
|
|
TPixel pixel = img[x, y];
|
|
Rgba64 rgbaPixel = default;
|
|
rgbaPixel.FromScaledVector4(pixel.ToScaledVector4());
|
|
ushort change = (ushort)Math.Round((perChannelChange / 255F) * 65535F);
|
|
|
|
if (rgbaPixel.R + perChannelChange <= 255)
|
|
{
|
|
rgbaPixel.R += change;
|
|
}
|
|
else
|
|
{
|
|
rgbaPixel.R -= change;
|
|
}
|
|
|
|
if (rgbaPixel.G + perChannelChange <= 255)
|
|
{
|
|
rgbaPixel.G += change;
|
|
}
|
|
else
|
|
{
|
|
rgbaPixel.G -= change;
|
|
}
|
|
|
|
if (rgbaPixel.B + perChannelChange <= 255)
|
|
{
|
|
rgbaPixel.B += perChannelChange;
|
|
}
|
|
else
|
|
{
|
|
rgbaPixel.B -= perChannelChange;
|
|
}
|
|
|
|
if (rgbaPixel.A + perChannelChange <= 255)
|
|
{
|
|
rgbaPixel.A += perChannelChange;
|
|
}
|
|
else
|
|
{
|
|
rgbaPixel.A -= perChannelChange;
|
|
}
|
|
|
|
pixel.FromRgba64(rgbaPixel);
|
|
img[x, y] = pixel;
|
|
}
|
|
}
|
|
}
|