//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
namespace ImageSharp.Benchmarks.Image
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using CoreImage = ImageSharp.Image;
public abstract class MultiImageBenchmarkBase : BenchmarkBase
{
protected Dictionary FileNamesToBytes = new Dictionary();
protected Dictionary> FileNamesToImageSharpImages = new Dictionary>();
protected Dictionary FileNamesToSystemDrawingImages = new Dictionary();
///
/// The values of this enum separate input files into categories
///
public enum InputImageCategory
{
AllImages,
SmallImagesOnly,
LargeImagesOnly
}
[Params(InputImageCategory.AllImages, InputImageCategory.SmallImagesOnly, InputImageCategory.LargeImagesOnly)]
public virtual InputImageCategory InputCategory { get; set; }
protected virtual string BaseFolder => "../ImageSharp.Tests/TestImages/Formats/";
protected virtual IEnumerable SearchPatterns => new[] { "*.*" };
///
/// Gets the file names containing these strings are substrings are not processed by the benchmark.
///
protected IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof" };
///
/// Enumerates folders containing files OR files to be processed by the benchmark.
///
protected IEnumerable AllFoldersOrFiles => this.InputImageSubfoldersOrFiles.Select(f => Path.Combine(this.BaseFolder, f));
///
/// The images sized above this threshold will be included in
///
protected virtual int LargeImageThresholdInBytes => 100000;
protected IEnumerable> EnumeratePairsByBenchmarkSettings(
Dictionary input,
Predicate checkIfSmall)
{
switch (this.InputCategory)
{
case InputImageCategory.AllImages:
return input;
case InputImageCategory.SmallImagesOnly:
return input.Where(kv => checkIfSmall(kv.Value));
case InputImageCategory.LargeImagesOnly:
return input.Where(kv => !checkIfSmall(kv.Value));
default:
throw new ArgumentOutOfRangeException();
}
}
protected IEnumerable> FileNames2Bytes
=>
this.EnumeratePairsByBenchmarkSettings(
this.FileNamesToBytes,
arr => arr.Length < this.LargeImageThresholdInBytes);
protected abstract IEnumerable InputImageSubfoldersOrFiles { get; }
[GlobalSetup]
public void ReadImages()
{
if (!Vector.IsHardwareAccelerated)
{
throw new Exception("Vector.IsHardwareAccelerated == false! Check your build settings!");
}
// Console.WriteLine("Vector.IsHardwareAccelerated: " + Vector.IsHardwareAccelerated);
this.ReadFilesImpl();
}
protected virtual void ReadFilesImpl()
{
foreach (string path in this.AllFoldersOrFiles)
{
if (File.Exists(path))
{
this.FileNamesToBytes[path] = File.ReadAllBytes(path);
continue;
}
string[] allFiles =
this.SearchPatterns.SelectMany(
f =>
Directory.EnumerateFiles(path, f, SearchOption.AllDirectories)
.Where(fn => !this.ExcludeSubstringsInFileNames.Any(w => fn.ToLower().Contains(w)))).ToArray();
foreach (string fn in allFiles)
{
this.FileNamesToBytes[fn] = File.ReadAllBytes(fn);
}
}
}
///
/// Execute code for each image stream. If the returned object of the opearation is it will be disposed.
///
/// The operation to execute. If the returned object is <see cref="IDisposable"/> it will be disposed
protected void ForEachStream(Func operation)
{
foreach (KeyValuePair kv in this.FileNames2Bytes)
{
using (MemoryStream memoryStream = new MemoryStream(kv.Value))
{
try
{
object obj = operation(memoryStream);
(obj as IDisposable)?.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}");
}
}
}
}
public abstract class WithImagesPreloaded : MultiImageBenchmarkBase
{
protected override void ReadFilesImpl()
{
base.ReadFilesImpl();
foreach (KeyValuePair kv in this.FileNamesToBytes)
{
byte[] bytes = kv.Value;
string fn = kv.Key;
using (MemoryStream ms1 = new MemoryStream(bytes))
{
this.FileNamesToImageSharpImages[fn] = CoreImage.Load(ms1);
}
this.FileNamesToSystemDrawingImages[fn] = new Bitmap(new MemoryStream(bytes));
}
}
protected IEnumerable>> FileNames2ImageSharpImages
=>
this.EnumeratePairsByBenchmarkSettings(
this.FileNamesToImageSharpImages,
img => img.Width * img.Height < this.LargeImageThresholdInPixels);
protected IEnumerable> FileNames2SystemDrawingImages
=>
this.EnumeratePairsByBenchmarkSettings(
this.FileNamesToSystemDrawingImages,
img => img.Width * img.Height < this.LargeImageThresholdInPixels);
protected virtual int LargeImageThresholdInPixels => 700000;
protected void ForEachImageSharpImage(Func, object> operation)
{
foreach (KeyValuePair> kv in this.FileNames2ImageSharpImages)
{
try
{
object obj = operation(kv.Value);
(obj as IDisposable)?.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}");
}
}
}
protected void ForEachImageSharpImage(Func, MemoryStream, object> operation)
{
using (MemoryStream workStream = new MemoryStream())
{
this.ForEachImageSharpImage(
img =>
{
// ReSharper disable AccessToDisposedClosure
object result = operation(img, workStream);
workStream.Seek(0, SeekOrigin.Begin);
// ReSharper restore AccessToDisposedClosure
return result;
});
}
}
protected void ForEachSystemDrawingImage(Func operation)
{
foreach (KeyValuePair kv in this.FileNames2SystemDrawingImages)
{
try
{
object obj = operation(kv.Value);
(obj as IDisposable)?.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"Operation on {kv.Key} failed with {ex.Message}");
}
}
}
protected void ForEachSystemDrawingImage(Func operation)
{
using (MemoryStream workStream = new MemoryStream())
{
this.ForEachSystemDrawingImage(
img =>
{
// ReSharper disable AccessToDisposedClosure
object result = operation(img, workStream);
workStream.Seek(0, SeekOrigin.Begin);
// ReSharper restore AccessToDisposedClosure
return result;
});
}
}
}
}
}