diff --git a/ImageSharp.sln b/ImageSharp.sln
index 1f848b34d..f1e9fb104 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -53,6 +53,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E574A
src\Shared\stylecop.json = src\Shared\stylecop.json
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{96188137-5FA6-4924-AB6E-4EFF79C6E0BB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {299D8E18-102C-42DE-ADBF-79098EE706A8} = {299D8E18-102C-42DE-ADBF-79098EE706A8}
+ {2E33181E-6E28-4662-A801-E2E7DC206029} = {2E33181E-6E28-4662-A801-E2E7DC206029}
+ {2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}
+ {27AD4B5F-ECC4-4C63-9ECB-04EC772FDB6F} = {27AD4B5F-ECC4-4C63-9ECB-04EC772FDB6F}
+ {7213767C-0003-41CA-AB18-0223CFA7CE4B} = {7213767C-0003-41CA-AB18-0223CFA7CE4B}
+ {C77661B9-F793-422E-8E27-AC60ECC5F215} = {C77661B9-F793-422E-8E27-AC60ECC5F215}
+ {556ABDCF-ED93-4327-BE98-F6815F78B9B8} = {556ABDCF-ED93-4327-BE98-F6815F78B9B8}
+ {A623CFE9-9D2B-4528-AD1F-2E834B061134} = {A623CFE9-9D2B-4528-AD1F-2E834B061134}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -99,6 +111,10 @@ Global
{A623CFE9-9D2B-4528-AD1F-2E834B061134}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A623CFE9-9D2B-4528-AD1F-2E834B061134}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A623CFE9-9D2B-4528-AD1F-2E834B061134}.Release|Any CPU.Build.0 = Release|Any CPU
+ {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -115,5 +131,6 @@ Global
{556ABDCF-ED93-4327-BE98-F6815F78B9B8} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{A623CFE9-9D2B-4528-AD1F-2E834B061134} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{9E574A07-F879-4811-9C41-5CBDC6BAFDB7} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
+ {96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
EndGlobalSection
EndGlobal
diff --git a/src/ImageSharp.Formats.Jpeg/Components/Block8x8F.cs b/src/ImageSharp.Formats.Jpeg/Components/Block8x8F.cs
index 723ccd6b8..e21ba2d02 100644
--- a/src/ImageSharp.Formats.Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp.Formats.Jpeg/Components/Block8x8F.cs
@@ -333,18 +333,19 @@ namespace ImageSharp.Formats.Jpg
/// Destination block
/// Quantization table
/// Pointer to elements
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe void UnZigDivRound(Block8x8F* src, Block8x8F* dest, Block8x8F* qt, int* unzigPtr)
+ // [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe void UnZigDivRound(Block8x8F* src, int* dest, Block8x8F* qt, int* unzigPtr)
{
float* s = (float*)src;
- float* d = (float*)dest;
float* q = (float*)qt;
for (int zig = 0; zig < ScalarCount; zig++)
{
- float val = s[unzigPtr[zig]] / q[zig];
- val = (int)val;
- d[zig] = val;
+ int a = (int)s[unzigPtr[zig]];
+ int b = (int)q[zig];
+
+ int val = DivideRound(a, b);
+ dest[zig] = val;
}
}
@@ -373,5 +374,22 @@ namespace ImageSharp.Formats.Jpg
}
}
}
+
+ ///
+ /// Performs division and rounding of a rational number represented by a dividend and a divisior into an integer.
+ ///
+ /// The dividend
+ /// The divisor
+ /// The result integer
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int DivideRound(int dividend, int divisor)
+ {
+ if (dividend >= 0)
+ {
+ return (dividend + (divisor >> 1)) / divisor;
+ }
+
+ return -((-dividend + (divisor >> 1)) / divisor);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs
index 18fd99008..a672949c3 100644
--- a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs
+++ b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs
@@ -71,7 +71,8 @@ namespace ImageSharp.Formats
}
else
{
- encode.Encode(image, stream, this.Quality, this.Quality >= 80 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
+ // Match Photoshop and use 4:2:0 SUpsampling at quality < 51%
+ encode.Encode(image, stream, this.Quality, this.Quality >= 51 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
}
}
}
diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs
index 92bb79924..aa7f2495d 100644
--- a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs
@@ -450,7 +450,9 @@ namespace ImageSharp.Formats
UnzigData unzig = UnzigData.Create();
// ReSharper disable once InconsistentNaming
- float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
+ int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
+
+ int* unzigDest = stackalloc int[Block8x8F.ScalarCount];
using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz))
{
@@ -465,6 +467,7 @@ namespace ImageSharp.Formats
prevDCY,
&b,
&temp1,
+ unzigDest,
&temp2,
&onStackLuminanceQuantTable,
unzig.Data);
@@ -473,6 +476,7 @@ namespace ImageSharp.Formats
prevDCCb,
&cb,
&temp1,
+ unzigDest,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
@@ -481,6 +485,7 @@ namespace ImageSharp.Formats
prevDCCr,
&cr,
&temp1,
+ unzigDest,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
@@ -538,31 +543,31 @@ namespace ImageSharp.Formats
/// The previous DC value.
/// Source block
/// Temporal block to be used as FDCT Destination
- /// Temporal block 2
+ /// Working buffer for unzigged stuff
+ /// Temporal block 2
/// Quantization table
/// The 8x8 Unzig block pointer
///
/// The
///
- private float WriteBlock(
+ private int WriteBlock(
QuantIndex index,
- float prevDC,
+ int prevDC,
Block8x8F* src,
Block8x8F* tempDest,
- Block8x8F* temp2,
+ int* d,
+ Block8x8F* tempWorker,
Block8x8F* quant,
int* unzigPtr)
{
- DCT.TransformFDCT(ref *src, ref *tempDest, ref *temp2);
-
- Block8x8F.UnZigDivRound(tempDest, temp2, quant, unzigPtr);
+ DCT.TransformFDCT(ref *src, ref *tempDest, ref *tempWorker);
- float* d = (float*)temp2;
+ Block8x8F.UnZigDivRound(tempDest, d, quant, unzigPtr);
// Emit the DC delta.
- float dc = d[0];
+ int dc = d[0];
- this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, (int)(dc - prevDC));
+ this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC);
// Emit the AC components.
HuffIndex h = (HuffIndex)((2 * (int)index) + 1);
@@ -570,7 +575,7 @@ namespace ImageSharp.Formats
for (int zig = 1; zig < Block8x8F.ScalarCount; zig++)
{
- float ac = d[zig];
+ int ac = d[zig];
if (ac == 0)
{
@@ -584,7 +589,7 @@ namespace ImageSharp.Formats
runLength -= 16;
}
- this.EmitHuffRLE(h, runLength, (int)ac);
+ this.EmitHuffRLE(h, runLength, ac);
runLength = 0;
}
}
@@ -818,8 +823,10 @@ namespace ImageSharp.Formats
UnzigData unzig = UnzigData.Create();
+ int* unzigDest = stackalloc int[Block8x8F.ScalarCount];
+
// ReSharper disable once InconsistentNaming
- float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
+ int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz))
{
@@ -839,6 +846,7 @@ namespace ImageSharp.Formats
prevDCY,
&b,
&temp1,
+ unzigDest,
&temp2,
&onStackLuminanceQuantTable,
unzig.Data);
@@ -850,6 +858,7 @@ namespace ImageSharp.Formats
prevDCCb,
&b,
&temp1,
+ unzigDest,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
@@ -860,6 +869,7 @@ namespace ImageSharp.Formats
prevDCCr,
&b,
&temp1,
+ unzigDest,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
diff --git a/src/ImageSharp.Formats.Png/PngDecoderCore.cs b/src/ImageSharp.Formats.Png/PngDecoderCore.cs
index ed5862e07..7ed38b6e8 100644
--- a/src/ImageSharp.Formats.Png/PngDecoderCore.cs
+++ b/src/ImageSharp.Formats.Png/PngDecoderCore.cs
@@ -129,7 +129,7 @@ namespace ImageSharp.Formats
/// Decodes the stream to the image.
///
/// The pixel format.
- /// The image to decode to.
+ /// The image to decode to.
/// The stream containing image data.
///
/// Thrown if the stream does not contain and end chunk.
@@ -139,7 +139,7 @@ namespace ImageSharp.Formats
///
public void Decode(Image image, Stream stream)
where TColor : struct, IPackedPixel, IEquatable
- {
+ {
Image currentImage = image;
this.currentStream = stream;
this.currentStream.Skip(8);
@@ -259,11 +259,11 @@ namespace ImageSharp.Formats
/// Reads the data chunk containing physical dimension data.
///
/// The pixel format.
- /// The image to read to.
+ /// The image to read to.
/// The data containing physical data.
private void ReadPhysicalChunk(Image image, byte[] data)
where TColor : struct, IPackedPixel, IEquatable
- {
+ {
data.ReverseBytes(0, 4);
data.ReverseBytes(4, 4);
@@ -322,11 +322,11 @@ namespace ImageSharp.Formats
/// Reads the scanlines within the image.
///
/// The pixel format.
- /// The containing data.
+ /// The containing data.
/// The pixel data.
private void ReadScanlines(MemoryStream dataStream, PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
- {
+ {
this.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1;
this.bytesPerSample = 1;
@@ -353,15 +353,16 @@ namespace ImageSharp.Formats
/// Decodes the raw pixel data row by row
///
/// The pixel format.
- /// The compressed pixel data stream.
+ /// The compressed pixel data stream.
/// The image pixel accessor.
private void DecodePixelData(Stream compressedStream, PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
- {
+ {
byte[] previousScanline = ArrayPool.Shared.Rent(this.bytesPerScanline);
byte[] scanline = ArrayPool.Shared.Rent(this.bytesPerScanline);
- // Zero out the previousScanline, because the bytes that are rented from the arraypool may not be zero.
+ // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero.
+ Array.Clear(scanline, 0, this.bytesPerScanline);
Array.Clear(previousScanline, 0, this.bytesPerScanline);
try
@@ -425,11 +426,11 @@ namespace ImageSharp.Formats
///
///
/// The pixel format.
- /// The compressed pixel data stream.
+ /// The compressed pixel data stream.
/// The image pixel accessor.
private void DecodeInterlacedPixelData(Stream compressedStream, PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
- {
+ {
byte[] previousScanline = ArrayPool.Shared.Rent(this.bytesPerScanline);
byte[] scanline = ArrayPool.Shared.Rent(this.bytesPerScanline);
@@ -437,7 +438,8 @@ namespace ImageSharp.Formats
{
for (int pass = 0; pass < 7; pass++)
{
- // Zero out the previousScanline, because the bytes that are rented from the arraypool may not be zero.
+ // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero.
+ Array.Clear(scanline, 0, this.bytesPerScanline);
Array.Clear(previousScanline, 0, this.bytesPerScanline);
int y = Adam7FirstRow[pass];
@@ -512,12 +514,12 @@ namespace ImageSharp.Formats
/// Processes the de-filtered scanline filling the image pixel data
///
/// The pixel format.
- /// The de-filtered scanline
+ /// The de-filtered scanline
/// The current image row.
/// The image pixels
private void ProcessDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
- {
+ {
TColor color = default(TColor);
switch (this.PngColorType)
{
@@ -635,14 +637,14 @@ namespace ImageSharp.Formats
/// Processes the interlaced de-filtered scanline filling the image pixel data
///
/// The pixel format.
- /// The de-filtered scanline
+ /// The de-filtered scanline
/// The current image row.
/// The image pixels
/// The column start index. Always 0 for none interlaced images.
/// The column increment. Always 1 for none interlaced images.
private void ProcessInterlacedDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels, int pixelOffset = 0, int increment = 1)
where TColor : struct, IPackedPixel, IEquatable
- {
+ {
TColor color = default(TColor);
switch (this.PngColorType)
@@ -755,12 +757,12 @@ namespace ImageSharp.Formats
/// Reads a text chunk containing image properties from the data.
///
/// The pixel format.
- /// The image to decode to.
+ /// The image to decode to.
/// The containing data.
/// The maximum length to read.
private void ReadTextChunk(Image image, byte[] data, int length)
where TColor : struct, IPackedPixel, IEquatable
- {
+ {
int zeroIndex = 0;
for (int i = 0; i < length; i++)
diff --git a/src/Shared/AssemblyInfo.Common.cs b/src/Shared/AssemblyInfo.Common.cs
index 09f2d0d02..11f16769b 100644
--- a/src/Shared/AssemblyInfo.Common.cs
+++ b/src/Shared/AssemblyInfo.Common.cs
@@ -35,4 +35,5 @@ using System.Runtime.CompilerServices;
// Ensure the internals can be tested.
[assembly: InternalsVisibleTo("ImageSharp.Benchmarks")]
-[assembly: InternalsVisibleTo("ImageSharp.Tests")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("ImageSharp.Tests")]
+[assembly: InternalsVisibleTo("ImageSharp.Sandbox46")]
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs
new file mode 100644
index 000000000..18c8cb372
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs
@@ -0,0 +1,237 @@
+namespace ImageSharp.Benchmarks
+{
+ public partial class RgbToYCbCr
+ {
+ ///
+ /// Scaled integer RGBA to YCbCr lookup tables
+ ///
+ private static class LookupTables
+ {
+ public static readonly int[] Y0 =
+ {
+ 0, 306, 612, 918, 1224, 1530, 1836, 2142, 2448, 2754, 3060, 3366, 3672, 3978, 4284,
+ 4590, 4896, 5202, 5508, 5814, 6120, 6426, 6732, 7038, 7344, 7650, 7956, 8262, 8568,
+ 8874, 9180, 9486, 9792, 10098, 10404, 10710, 11016, 11322, 11628, 11934, 12240,
+ 12546, 12852, 13158, 13464, 13770, 14076, 14382, 14688, 14994, 15300, 15606, 15912,
+ 16218, 16524, 16830, 17136, 17442, 17748, 18054, 18360, 18666, 18972, 19278, 19584,
+ 19890, 20196, 20502, 20808, 21114, 21420, 21726, 22032, 22338, 22644, 22950, 23256,
+ 23562, 23868, 24174, 24480, 24786, 25092, 25398, 25704, 26010, 26316, 26622, 26928,
+ 27234, 27540, 27846, 28152, 28458, 28764, 29070, 29376, 29682, 29988, 30294, 30600,
+ 30906, 31212, 31518, 31824, 32130, 32436, 32742, 33048, 33354, 33660, 33966, 34272,
+ 34578, 34884, 35190, 35496, 35802, 36108, 36414, 36720, 37026, 37332, 37638, 37944,
+ 38250, 38556, 38862, 39168, 39474, 39780, 40086, 40392, 40698, 41004, 41310, 41616,
+ 41922, 42228, 42534, 42840, 43146, 43452, 43758, 44064, 44370, 44676, 44982, 45288,
+ 45594, 45900, 46206, 46512, 46818, 47124, 47430, 47736, 48042, 48348, 48654, 48960,
+ 49266, 49572, 49878, 50184, 50490, 50796, 51102, 51408, 51714, 52020, 52326, 52632,
+ 52938, 53244, 53550, 53856, 54162, 54468, 54774, 55080, 55386, 55692, 55998, 56304,
+ 56610, 56916, 57222, 57528, 57834, 58140, 58446, 58752, 59058, 59364, 59670, 59976,
+ 60282, 60588, 60894, 61200, 61506, 61812, 62118, 62424, 62730, 63036, 63342, 63648,
+ 63954, 64260, 64566, 64872, 65178, 65484, 65790, 66096, 66402, 66708, 67014, 67320,
+ 67626, 67932, 68238, 68544, 68850, 69156, 69462, 69768, 70074, 70380, 70686, 70992,
+ 71298, 71604, 71910, 72216, 72522, 72828, 73134, 73440, 73746, 74052, 74358, 74664,
+ 74970, 75276, 75582, 75888, 76194, 76500, 76806, 77112, 77418, 77724, 78030
+ };
+
+ public static readonly int[] Y1 =
+ {
+ 0, 601, 1202, 1803, 2404, 3005, 3606, 4207, 4808, 5409, 6010, 6611, 7212, 7813, 8414,
+ 9015, 9616, 10217, 10818, 11419, 12020, 12621, 13222, 13823, 14424, 15025, 15626,
+ 16227, 16828, 17429, 18030, 18631, 19232, 19833, 20434, 21035, 21636, 22237, 22838,
+ 23439, 24040, 24641, 25242, 25843, 26444, 27045, 27646, 28247, 28848, 29449, 30050,
+ 30651, 31252, 31853, 32454, 33055, 33656, 34257, 34858, 35459, 36060, 36661, 37262,
+ 37863, 38464, 39065, 39666, 40267, 40868, 41469, 42070, 42671, 43272, 43873, 44474,
+ 45075, 45676, 46277, 46878, 47479, 48080, 48681, 49282, 49883, 50484, 51085, 51686,
+ 52287, 52888, 53489, 54090, 54691, 55292, 55893, 56494, 57095, 57696, 58297, 58898,
+ 59499, 60100, 60701, 61302, 61903, 62504, 63105, 63706, 64307, 64908, 65509, 66110,
+ 66711, 67312, 67913, 68514, 69115, 69716, 70317, 70918, 71519, 72120, 72721, 73322,
+ 73923, 74524, 75125, 75726, 76327, 76928, 77529, 78130, 78731, 79332, 79933, 80534,
+ 81135, 81736, 82337, 82938, 83539, 84140, 84741, 85342, 85943, 86544, 87145, 87746,
+ 88347, 88948, 89549, 90150, 90751, 91352, 91953, 92554, 93155, 93756, 94357, 94958,
+ 95559, 96160, 96761, 97362, 97963, 98564, 99165, 99766, 100367, 100968, 101569,
+ 102170, 102771, 103372, 103973, 104574, 105175, 105776, 106377, 106978, 107579,
+ 108180, 108781, 109382, 109983, 110584, 111185, 111786, 112387, 112988, 113589,
+ 114190, 114791, 115392, 115993, 116594, 117195, 117796, 118397, 118998, 119599,
+ 120200, 120801, 121402, 122003, 122604, 123205, 123806, 124407, 125008, 125609,
+ 126210, 126811, 127412, 128013, 128614, 129215, 129816, 130417, 131018, 131619,
+ 132220, 132821, 133422, 134023, 134624, 135225, 135826, 136427, 137028, 137629,
+ 138230, 138831, 139432, 140033, 140634, 141235, 141836, 142437, 143038, 143639,
+ 144240, 144841, 145442, 146043, 146644, 147245, 147846, 148447, 149048, 149649,
+ 150250, 150851, 151452, 152053, 152654, 153255
+ };
+
+ public static readonly int[] Y2 =
+ {
+ 0, 117, 234, 351, 468, 585, 702, 819, 936, 1053, 1170, 1287, 1404, 1521, 1638, 1755,
+ 1872, 1989, 2106, 2223, 2340, 2457, 2574, 2691, 2808, 2925, 3042, 3159, 3276, 3393,
+ 3510, 3627, 3744, 3861, 3978, 4095, 4212, 4329, 4446, 4563, 4680, 4797, 4914, 5031,
+ 5148, 5265, 5382, 5499, 5616, 5733, 5850, 5967, 6084, 6201, 6318, 6435, 6552, 6669,
+ 6786, 6903, 7020, 7137, 7254, 7371, 7488, 7605, 7722, 7839, 7956, 8073, 8190, 8307,
+ 8424, 8541, 8658, 8775, 8892, 9009, 9126, 9243, 9360, 9477, 9594, 9711, 9828, 9945,
+ 10062, 10179, 10296, 10413, 10530, 10647, 10764, 10881, 10998, 11115, 11232, 11349,
+ 11466, 11583, 11700, 11817, 11934, 12051, 12168, 12285, 12402, 12519, 12636, 12753,
+ 12870, 12987, 13104, 13221, 13338, 13455, 13572, 13689, 13806, 13923, 14040, 14157,
+ 14274, 14391, 14508, 14625, 14742, 14859, 14976, 15093, 15210, 15327, 15444, 15561,
+ 15678, 15795, 15912, 16029, 16146, 16263, 16380, 16497, 16614, 16731, 16848, 16965,
+ 17082, 17199, 17316, 17433, 17550, 17667, 17784, 17901, 18018, 18135, 18252, 18369,
+ 18486, 18603, 18720, 18837, 18954, 19071, 19188, 19305, 19422, 19539, 19656, 19773,
+ 19890, 20007, 20124, 20241, 20358, 20475, 20592, 20709, 20826, 20943, 21060, 21177,
+ 21294, 21411, 21528, 21645, 21762, 21879, 21996, 22113, 22230, 22347, 22464, 22581,
+ 22698, 22815, 22932, 23049, 23166, 23283, 23400, 23517, 23634, 23751, 23868, 23985,
+ 24102, 24219, 24336, 24453, 24570, 24687, 24804, 24921, 25038, 25155, 25272, 25389,
+ 25506, 25623, 25740, 25857, 25974, 26091, 26208, 26325, 26442, 26559, 26676, 26793,
+ 26910, 27027, 27144, 27261, 27378, 27495, 27612, 27729, 27846, 27963, 28080, 28197,
+ 28314, 28431, 28548, 28665, 28782, 28899, 29016, 29133, 29250, 29367, 29484, 29601,
+ 29718, 29835
+ };
+
+ public static readonly int[] Cb0 =
+ {
+ 0, -172, -344, -516, -688, -860, -1032, -1204, -1376, -1548, -1720, -1892,
+ -2064, -2236, -2408, -2580, -2752, -2924, -3096, -3268, -3440, -3612,
+ -3784, -3956, -4128, -4300, -4472, -4644, -4816, -4988, -5160, -5332,
+ -5504, -5676, -5848, -6020, -6192, -6364, -6536, -6708, -6880, -7052,
+ -7224, -7396, -7568, -7740, -7912, -8084, -8256, -8428, -8600, -8772,
+ -8944, -9116, -9288, -9460, -9632, -9804, -9976, -10148, -10320, -10492,
+ -10664, -10836, -11008, -11180, -11352, -11524, -11696, -11868, -12040,
+ -12212, -12384, -12556, -12728, -12900, -13072, -13244, -13416, -13588,
+ -13760, -13932, -14104, -14276, -14448, -14620, -14792, -14964, -15136,
+ -15308, -15480, -15652, -15824, -15996, -16168, -16340, -16512, -16684,
+ -16856, -17028, -17200, -17372, -17544, -17716, -17888, -18060, -18232,
+ -18404, -18576, -18748, -18920, -19092, -19264, -19436, -19608, -19780,
+ -19952, -20124, -20296, -20468, -20640, -20812, -20984, -21156, -21328,
+ -21500, -21672, -21844, -22016, -22188, -22360, -22532, -22704, -22876,
+ -23048, -23220, -23392, -23564, -23736, -23908, -24080, -24252, -24424,
+ -24596, -24768, -24940, -25112, -25284, -25456, -25628, -25800, -25972,
+ -26144, -26316, -26488, -26660, -26832, -27004, -27176, -27348, -27520,
+ -27692, -27864, -28036, -28208, -28380, -28552, -28724, -28896, -29068,
+ -29240, -29412, -29584, -29756, -29928, -30100, -30272, -30444, -30616,
+ -30788, -30960, -31132, -31304, -31476, -31648, -31820, -31992, -32164,
+ -32336, -32508, -32680, -32852, -33024, -33196, -33368, -33540, -33712,
+ -33884, -34056, -34228, -34400, -34572, -34744, -34916, -35088, -35260,
+ -35432, -35604, -35776, -35948, -36120, -36292, -36464, -36636, -36808,
+ -36980, -37152, -37324, -37496, -37668, -37840, -38012, -38184, -38356,
+ -38528, -38700, -38872, -39044, -39216, -39388, -39560, -39732, -39904,
+ -40076, -40248, -40420, -40592, -40764, -40936, -41108, -41280, -41452,
+ -41624, -41796, -41968, -42140, -42312, -42484, -42656, -42828, -43000,
+ -43172, -43344, -43516, -43688, -43860
+ };
+
+ public static readonly int[] Cb1 =
+ {
+ 0, 339, 678, 1017, 1356, 1695, 2034, 2373, 2712, 3051, 3390, 3729, 4068,
+ 4407, 4746, 5085, 5424, 5763, 6102, 6441, 6780, 7119, 7458, 7797, 8136,
+ 8475, 8814, 9153, 9492, 9831, 10170, 10509, 10848, 11187, 11526, 11865,
+ 12204, 12543, 12882, 13221, 13560, 13899, 14238, 14577, 14916, 15255,
+ 15594, 15933, 16272, 16611, 16950, 17289, 17628, 17967, 18306, 18645,
+ 18984, 19323, 19662, 20001, 20340, 20679, 21018, 21357, 21696, 22035,
+ 22374, 22713, 23052, 23391, 23730, 24069, 24408, 24747, 25086, 25425,
+ 25764, 26103, 26442, 26781, 27120, 27459, 27798, 28137, 28476, 28815,
+ 29154, 29493, 29832, 30171, 30510, 30849, 31188, 31527, 31866, 32205,
+ 32544, 32883, 33222, 33561, 33900, 34239, 34578, 34917, 35256, 35595,
+ 35934, 36273, 36612, 36951, 37290, 37629, 37968, 38307, 38646, 38985,
+ 39324, 39663, 40002, 40341, 40680, 41019, 41358, 41697, 42036, 42375,
+ 42714, 43053, 43392, 43731, 44070, 44409, 44748, 45087, 45426, 45765,
+ 46104, 46443, 46782, 47121, 47460, 47799, 48138, 48477, 48816, 49155,
+ 49494, 49833, 50172, 50511, 50850, 51189, 51528, 51867, 52206, 52545,
+ 52884, 53223, 53562, 53901, 54240, 54579, 54918, 55257, 55596, 55935,
+ 56274, 56613, 56952, 57291, 57630, 57969, 58308, 58647, 58986, 59325,
+ 59664, 60003, 60342, 60681, 61020, 61359, 61698, 62037, 62376, 62715,
+ 63054, 63393, 63732, 64071, 64410, 64749, 65088, 65427, 65766, 66105,
+ 66444, 66783, 67122, 67461, 67800, 68139, 68478, 68817, 69156, 69495,
+ 69834, 70173, 70512, 70851, 71190, 71529, 71868, 72207, 72546, 72885,
+ 73224, 73563, 73902, 74241, 74580, 74919, 75258, 75597, 75936, 76275,
+ 76614, 76953, 77292, 77631, 77970, 78309, 78648, 78987, 79326, 79665,
+ 80004, 80343, 80682, 81021, 81360, 81699, 82038, 82377, 82716, 83055,
+ 83394, 83733, 84072, 84411, 84750, 85089, 85428, 85767, 86106, 86445
+ };
+
+ public static readonly int[] Cb2Cr0 =
+ {
+ 0, 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5632, 6144,
+ 6656, 7168, 7680, 8192, 8704, 9216, 9728, 10240, 10752, 11264, 11776,
+ 12288, 12800, 13312, 13824, 14336, 14848, 15360, 15872, 16384, 16896,
+ 17408, 17920, 18432, 18944, 19456, 19968, 20480, 20992, 21504, 22016,
+ 22528, 23040, 23552, 24064, 24576, 25088, 25600, 26112, 26624, 27136,
+ 27648, 28160, 28672, 29184, 29696, 30208, 30720, 31232, 31744, 32256,
+ 32768, 33280, 33792, 34304, 34816, 35328, 35840, 36352, 36864, 37376,
+ 37888, 38400, 38912, 39424, 39936, 40448, 40960, 41472, 41984, 42496,
+ 43008, 43520, 44032, 44544, 45056, 45568, 46080, 46592, 47104, 47616,
+ 48128, 48640, 49152, 49664, 50176, 50688, 51200, 51712, 52224, 52736,
+ 53248, 53760, 54272, 54784, 55296, 55808, 56320, 56832, 57344, 57856,
+ 58368, 58880, 59392, 59904, 60416, 60928, 61440, 61952, 62464, 62976,
+ 63488, 64000, 64512, 65024, 65536, 66048, 66560, 67072, 67584, 68096,
+ 68608, 69120, 69632, 70144, 70656, 71168, 71680, 72192, 72704, 73216,
+ 73728, 74240, 74752, 75264, 75776, 76288, 76800, 77312, 77824, 78336,
+ 78848, 79360, 79872, 80384, 80896, 81408, 81920, 82432, 82944, 83456,
+ 83968, 84480, 84992, 85504, 86016, 86528, 87040, 87552, 88064, 88576,
+ 89088, 89600, 90112, 90624, 91136, 91648, 92160, 92672, 93184, 93696,
+ 94208, 94720, 95232, 95744, 96256, 96768, 97280, 97792, 98304, 98816,
+ 99328, 99840, 100352, 100864, 101376, 101888, 102400, 102912, 103424,
+ 103936, 104448, 104960, 105472, 105984, 106496, 107008, 107520, 108032,
+ 108544, 109056, 109568, 110080, 110592, 111104, 111616, 112128, 112640,
+ 113152, 113664, 114176, 114688, 115200, 115712, 116224, 116736, 117248,
+ 117760, 118272, 118784, 119296, 119808, 120320, 120832, 121344, 121856,
+ 122368, 122880, 123392, 123904, 124416, 124928, 125440, 125952, 126464,
+ 126976, 127488, 128000, 128512, 129024, 129536, 130048, 130560
+ };
+
+ public static readonly int[] Cr1 =
+ {
+ 0, 429, 858, 1287, 1716, 2145, 2574, 3003, 3432, 3861, 4290, 4719, 5148,
+ 5577, 6006, 6435, 6864, 7293, 7722, 8151, 8580, 9009, 9438, 9867, 10296,
+ 10725, 11154, 11583, 12012, 12441, 12870, 13299, 13728, 14157, 14586,
+ 15015, 15444, 15873, 16302, 16731, 17160, 17589, 18018, 18447, 18876,
+ 19305, 19734, 20163, 20592, 21021, 21450, 21879, 22308, 22737, 23166,
+ 23595, 24024, 24453, 24882, 25311, 25740, 26169, 26598, 27027, 27456,
+ 27885, 28314, 28743, 29172, 29601, 30030, 30459, 30888, 31317, 31746,
+ 32175, 32604, 33033, 33462, 33891, 34320, 34749, 35178, 35607, 36036,
+ 36465, 36894, 37323, 37752, 38181, 38610, 39039, 39468, 39897, 40326,
+ 40755, 41184, 41613, 42042, 42471, 42900, 43329, 43758, 44187, 44616,
+ 45045, 45474, 45903, 46332, 46761, 47190, 47619, 48048, 48477, 48906,
+ 49335, 49764, 50193, 50622, 51051, 51480, 51909, 52338, 52767, 53196,
+ 53625, 54054, 54483, 54912, 55341, 55770, 56199, 56628, 57057, 57486,
+ 57915, 58344, 58773, 59202, 59631, 60060, 60489, 60918, 61347, 61776,
+ 62205, 62634, 63063, 63492, 63921, 64350, 64779, 65208, 65637, 66066,
+ 66495, 66924, 67353, 67782, 68211, 68640, 69069, 69498, 69927, 70356,
+ 70785, 71214, 71643, 72072, 72501, 72930, 73359, 73788, 74217, 74646,
+ 75075, 75504, 75933, 76362, 76791, 77220, 77649, 78078, 78507, 78936,
+ 79365, 79794, 80223, 80652, 81081, 81510, 81939, 82368, 82797, 83226,
+ 83655, 84084, 84513, 84942, 85371, 85800, 86229, 86658, 87087, 87516,
+ 87945, 88374, 88803, 89232, 89661, 90090, 90519, 90948, 91377, 91806,
+ 92235, 92664, 93093, 93522, 93951, 94380, 94809, 95238, 95667, 96096,
+ 96525, 96954, 97383, 97812, 98241, 98670, 99099, 99528, 99957, 100386,
+ 100815, 101244, 101673, 102102, 102531, 102960, 103389, 103818, 104247,
+ 104676, 105105, 105534, 105963, 106392, 106821, 107250, 107679, 108108,
+ 108537, 108966, 109395
+ };
+
+ public static readonly int[] Cr2 =
+ {
+ 0, 83, 166, 249, 332, 415, 498, 581, 664, 747, 830, 913, 996, 1079, 1162,
+ 1245, 1328, 1411, 1494, 1577, 1660, 1743, 1826, 1909, 1992, 2075, 2158,
+ 2241, 2324, 2407, 2490, 2573, 2656, 2739, 2822, 2905, 2988, 3071, 3154,
+ 3237, 3320, 3403, 3486, 3569, 3652, 3735, 3818, 3901, 3984, 4067, 4150,
+ 4233, 4316, 4399, 4482, 4565, 4648, 4731, 4814, 4897, 4980, 5063, 5146,
+ 5229, 5312, 5395, 5478, 5561, 5644, 5727, 5810, 5893, 5976, 6059, 6142,
+ 6225, 6308, 6391, 6474, 6557, 6640, 6723, 6806, 6889, 6972, 7055, 7138,
+ 7221, 7304, 7387, 7470, 7553, 7636, 7719, 7802, 7885, 7968, 8051, 8134,
+ 8217, 8300, 8383, 8466, 8549, 8632, 8715, 8798, 8881, 8964, 9047, 9130,
+ 9213, 9296, 9379, 9462, 9545, 9628, 9711, 9794, 9877, 9960, 10043, 10126,
+ 10209, 10292, 10375, 10458, 10541, 10624, 10707, 10790, 10873, 10956,
+ 11039, 11122, 11205, 11288, 11371, 11454, 11537, 11620, 11703, 11786,
+ 11869, 11952, 12035, 12118, 12201, 12284, 12367, 12450, 12533, 12616,
+ 12699, 12782, 12865, 12948, 13031, 13114, 13197, 13280, 13363, 13446,
+ 13529, 13612, 13695, 13778, 13861, 13944, 14027, 14110, 14193, 14276,
+ 14359, 14442, 14525, 14608, 14691, 14774, 14857, 14940, 15023, 15106,
+ 15189, 15272, 15355, 15438, 15521, 15604, 15687, 15770, 15853, 15936,
+ 16019, 16102, 16185, 16268, 16351, 16434, 16517, 16600, 16683, 16766,
+ 16849, 16932, 17015, 17098, 17181, 17264, 17347, 17430, 17513, 17596,
+ 17679, 17762, 17845, 17928, 18011, 18094, 18177, 18260, 18343, 18426,
+ 18509, 18592, 18675, 18758, 18841, 18924, 19007, 19090, 19173, 19256,
+ 19339, 19422, 19505, 19588, 19671, 19754, 19837, 19920, 20003, 20086,
+ 20169, 20252, 20335, 20418, 20501, 20584, 20667, 20750, 20833, 20916,
+ 20999, 21082, 21165
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs
index fb738021b..0c777b86b 100644
--- a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs
+++ b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs
@@ -1,307 +1,376 @@
namespace ImageSharp.Benchmarks
{
+ using System;
+ using System.Buffers;
using System.Numerics;
+ using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
- public class RgbToYCbCr
+ using ImageSharp.Formats.Jpg;
+
+ public partial class RgbToYCbCr
{
+ private const int InputColorCount = 64;
+
+ private const int InputByteCount = InputColorCount * 3;
+
private static readonly Vector3 VectorY = new Vector3(0.299F, 0.587F, 0.114F);
+
private static readonly Vector3 VectorCb = new Vector3(-0.168736F, 0.331264F, 0.5F);
+
private static readonly Vector3 VectorCr = new Vector3(0.5F, 0.418688F, 0.081312F);
- private static readonly int[] Y0Lut =
- {
- 0, 306, 612, 918, 1224, 1530, 1836, 2142, 2448, 2754, 3060, 3366, 3672, 3978, 4284,
- 4590, 4896, 5202, 5508, 5814, 6120, 6426, 6732, 7038, 7344, 7650, 7956, 8262, 8568,
- 8874, 9180, 9486, 9792, 10098, 10404, 10710, 11016, 11322, 11628, 11934, 12240,
- 12546, 12852, 13158, 13464, 13770, 14076, 14382, 14688, 14994, 15300, 15606, 15912,
- 16218, 16524, 16830, 17136, 17442, 17748, 18054, 18360, 18666, 18972, 19278, 19584,
- 19890, 20196, 20502, 20808, 21114, 21420, 21726, 22032, 22338, 22644, 22950, 23256,
- 23562, 23868, 24174, 24480, 24786, 25092, 25398, 25704, 26010, 26316, 26622, 26928,
- 27234, 27540, 27846, 28152, 28458, 28764, 29070, 29376, 29682, 29988, 30294, 30600,
- 30906, 31212, 31518, 31824, 32130, 32436, 32742, 33048, 33354, 33660, 33966, 34272,
- 34578, 34884, 35190, 35496, 35802, 36108, 36414, 36720, 37026, 37332, 37638, 37944,
- 38250, 38556, 38862, 39168, 39474, 39780, 40086, 40392, 40698, 41004, 41310, 41616,
- 41922, 42228, 42534, 42840, 43146, 43452, 43758, 44064, 44370, 44676, 44982, 45288,
- 45594, 45900, 46206, 46512, 46818, 47124, 47430, 47736, 48042, 48348, 48654, 48960,
- 49266, 49572, 49878, 50184, 50490, 50796, 51102, 51408, 51714, 52020, 52326, 52632,
- 52938, 53244, 53550, 53856, 54162, 54468, 54774, 55080, 55386, 55692, 55998, 56304,
- 56610, 56916, 57222, 57528, 57834, 58140, 58446, 58752, 59058, 59364, 59670, 59976,
- 60282, 60588, 60894, 61200, 61506, 61812, 62118, 62424, 62730, 63036, 63342, 63648,
- 63954, 64260, 64566, 64872, 65178, 65484, 65790, 66096, 66402, 66708, 67014, 67320,
- 67626, 67932, 68238, 68544, 68850, 69156, 69462, 69768, 70074, 70380, 70686, 70992,
- 71298, 71604, 71910, 72216, 72522, 72828, 73134, 73440, 73746, 74052, 74358, 74664,
- 74970, 75276, 75582, 75888, 76194, 76500, 76806, 77112, 77418, 77724, 78030
- };
-
- private static readonly int[] Y1Lut =
- {
- 0, 601, 1202, 1803, 2404, 3005, 3606, 4207, 4808, 5409, 6010, 6611, 7212, 7813, 8414,
- 9015, 9616, 10217, 10818, 11419, 12020, 12621, 13222, 13823, 14424, 15025, 15626,
- 16227, 16828, 17429, 18030, 18631, 19232, 19833, 20434, 21035, 21636, 22237, 22838,
- 23439, 24040, 24641, 25242, 25843, 26444, 27045, 27646, 28247, 28848, 29449, 30050,
- 30651, 31252, 31853, 32454, 33055, 33656, 34257, 34858, 35459, 36060, 36661, 37262,
- 37863, 38464, 39065, 39666, 40267, 40868, 41469, 42070, 42671, 43272, 43873, 44474,
- 45075, 45676, 46277, 46878, 47479, 48080, 48681, 49282, 49883, 50484, 51085, 51686,
- 52287, 52888, 53489, 54090, 54691, 55292, 55893, 56494, 57095, 57696, 58297, 58898,
- 59499, 60100, 60701, 61302, 61903, 62504, 63105, 63706, 64307, 64908, 65509, 66110,
- 66711, 67312, 67913, 68514, 69115, 69716, 70317, 70918, 71519, 72120, 72721, 73322,
- 73923, 74524, 75125, 75726, 76327, 76928, 77529, 78130, 78731, 79332, 79933, 80534,
- 81135, 81736, 82337, 82938, 83539, 84140, 84741, 85342, 85943, 86544, 87145, 87746,
- 88347, 88948, 89549, 90150, 90751, 91352, 91953, 92554, 93155, 93756, 94357, 94958,
- 95559, 96160, 96761, 97362, 97963, 98564, 99165, 99766, 100367, 100968, 101569,
- 102170, 102771, 103372, 103973, 104574, 105175, 105776, 106377, 106978, 107579,
- 108180, 108781, 109382, 109983, 110584, 111185, 111786, 112387, 112988, 113589,
- 114190, 114791, 115392, 115993, 116594, 117195, 117796, 118397, 118998, 119599,
- 120200, 120801, 121402, 122003, 122604, 123205, 123806, 124407, 125008, 125609,
- 126210, 126811, 127412, 128013, 128614, 129215, 129816, 130417, 131018, 131619,
- 132220, 132821, 133422, 134023, 134624, 135225, 135826, 136427, 137028, 137629,
- 138230, 138831, 139432, 140033, 140634, 141235, 141836, 142437, 143038, 143639,
- 144240, 144841, 145442, 146043, 146644, 147245, 147846, 148447, 149048, 149649,
- 150250, 150851, 151452, 152053, 152654, 153255
- };
-
- private static readonly int[] Y2Lut =
+ private static class ScaledCoeffs
{
- 0, 117, 234, 351, 468, 585, 702, 819, 936, 1053, 1170, 1287, 1404, 1521, 1638, 1755,
- 1872, 1989, 2106, 2223, 2340, 2457, 2574, 2691, 2808, 2925, 3042, 3159, 3276, 3393,
- 3510, 3627, 3744, 3861, 3978, 4095, 4212, 4329, 4446, 4563, 4680, 4797, 4914, 5031,
- 5148, 5265, 5382, 5499, 5616, 5733, 5850, 5967, 6084, 6201, 6318, 6435, 6552, 6669,
- 6786, 6903, 7020, 7137, 7254, 7371, 7488, 7605, 7722, 7839, 7956, 8073, 8190, 8307,
- 8424, 8541, 8658, 8775, 8892, 9009, 9126, 9243, 9360, 9477, 9594, 9711, 9828, 9945,
- 10062, 10179, 10296, 10413, 10530, 10647, 10764, 10881, 10998, 11115, 11232, 11349,
- 11466, 11583, 11700, 11817, 11934, 12051, 12168, 12285, 12402, 12519, 12636, 12753,
- 12870, 12987, 13104, 13221, 13338, 13455, 13572, 13689, 13806, 13923, 14040, 14157,
- 14274, 14391, 14508, 14625, 14742, 14859, 14976, 15093, 15210, 15327, 15444, 15561,
- 15678, 15795, 15912, 16029, 16146, 16263, 16380, 16497, 16614, 16731, 16848, 16965,
- 17082, 17199, 17316, 17433, 17550, 17667, 17784, 17901, 18018, 18135, 18252, 18369,
- 18486, 18603, 18720, 18837, 18954, 19071, 19188, 19305, 19422, 19539, 19656, 19773,
- 19890, 20007, 20124, 20241, 20358, 20475, 20592, 20709, 20826, 20943, 21060, 21177,
- 21294, 21411, 21528, 21645, 21762, 21879, 21996, 22113, 22230, 22347, 22464, 22581,
- 22698, 22815, 22932, 23049, 23166, 23283, 23400, 23517, 23634, 23751, 23868, 23985,
- 24102, 24219, 24336, 24453, 24570, 24687, 24804, 24921, 25038, 25155, 25272, 25389,
- 25506, 25623, 25740, 25857, 25974, 26091, 26208, 26325, 26442, 26559, 26676, 26793,
- 26910, 27027, 27144, 27261, 27378, 27495, 27612, 27729, 27846, 27963, 28080, 28197,
- 28314, 28431, 28548, 28665, 28782, 28899, 29016, 29133, 29250, 29367, 29484, 29601,
- 29718, 29835
- };
-
- private static readonly int[] Cb0Lut =
- {
- 0, -172, -344, -516, -688, -860, -1032, -1204, -1376, -1548, -1720, -1892,
- -2064, -2236, -2408, -2580, -2752, -2924, -3096, -3268, -3440, -3612,
- -3784, -3956, -4128, -4300, -4472, -4644, -4816, -4988, -5160, -5332,
- -5504, -5676, -5848, -6020, -6192, -6364, -6536, -6708, -6880, -7052,
- -7224, -7396, -7568, -7740, -7912, -8084, -8256, -8428, -8600, -8772,
- -8944, -9116, -9288, -9460, -9632, -9804, -9976, -10148, -10320, -10492,
- -10664, -10836, -11008, -11180, -11352, -11524, -11696, -11868, -12040,
- -12212, -12384, -12556, -12728, -12900, -13072, -13244, -13416, -13588,
- -13760, -13932, -14104, -14276, -14448, -14620, -14792, -14964, -15136,
- -15308, -15480, -15652, -15824, -15996, -16168, -16340, -16512, -16684,
- -16856, -17028, -17200, -17372, -17544, -17716, -17888, -18060, -18232,
- -18404, -18576, -18748, -18920, -19092, -19264, -19436, -19608, -19780,
- -19952, -20124, -20296, -20468, -20640, -20812, -20984, -21156, -21328,
- -21500, -21672, -21844, -22016, -22188, -22360, -22532, -22704, -22876,
- -23048, -23220, -23392, -23564, -23736, -23908, -24080, -24252, -24424,
- -24596, -24768, -24940, -25112, -25284, -25456, -25628, -25800, -25972,
- -26144, -26316, -26488, -26660, -26832, -27004, -27176, -27348, -27520,
- -27692, -27864, -28036, -28208, -28380, -28552, -28724, -28896, -29068,
- -29240, -29412, -29584, -29756, -29928, -30100, -30272, -30444, -30616,
- -30788, -30960, -31132, -31304, -31476, -31648, -31820, -31992, -32164,
- -32336, -32508, -32680, -32852, -33024, -33196, -33368, -33540, -33712,
- -33884, -34056, -34228, -34400, -34572, -34744, -34916, -35088, -35260,
- -35432, -35604, -35776, -35948, -36120, -36292, -36464, -36636, -36808,
- -36980, -37152, -37324, -37496, -37668, -37840, -38012, -38184, -38356,
- -38528, -38700, -38872, -39044, -39216, -39388, -39560, -39732, -39904,
- -40076, -40248, -40420, -40592, -40764, -40936, -41108, -41280, -41452,
- -41624, -41796, -41968, -42140, -42312, -42484, -42656, -42828, -43000,
- -43172, -43344, -43516, -43688, -43860
- };
-
- private static readonly int[] Cb1Lut =
- {
- 0, 339, 678, 1017, 1356, 1695, 2034, 2373, 2712, 3051, 3390, 3729, 4068,
- 4407, 4746, 5085, 5424, 5763, 6102, 6441, 6780, 7119, 7458, 7797, 8136,
- 8475, 8814, 9153, 9492, 9831, 10170, 10509, 10848, 11187, 11526, 11865,
- 12204, 12543, 12882, 13221, 13560, 13899, 14238, 14577, 14916, 15255,
- 15594, 15933, 16272, 16611, 16950, 17289, 17628, 17967, 18306, 18645,
- 18984, 19323, 19662, 20001, 20340, 20679, 21018, 21357, 21696, 22035,
- 22374, 22713, 23052, 23391, 23730, 24069, 24408, 24747, 25086, 25425,
- 25764, 26103, 26442, 26781, 27120, 27459, 27798, 28137, 28476, 28815,
- 29154, 29493, 29832, 30171, 30510, 30849, 31188, 31527, 31866, 32205,
- 32544, 32883, 33222, 33561, 33900, 34239, 34578, 34917, 35256, 35595,
- 35934, 36273, 36612, 36951, 37290, 37629, 37968, 38307, 38646, 38985,
- 39324, 39663, 40002, 40341, 40680, 41019, 41358, 41697, 42036, 42375,
- 42714, 43053, 43392, 43731, 44070, 44409, 44748, 45087, 45426, 45765,
- 46104, 46443, 46782, 47121, 47460, 47799, 48138, 48477, 48816, 49155,
- 49494, 49833, 50172, 50511, 50850, 51189, 51528, 51867, 52206, 52545,
- 52884, 53223, 53562, 53901, 54240, 54579, 54918, 55257, 55596, 55935,
- 56274, 56613, 56952, 57291, 57630, 57969, 58308, 58647, 58986, 59325,
- 59664, 60003, 60342, 60681, 61020, 61359, 61698, 62037, 62376, 62715,
- 63054, 63393, 63732, 64071, 64410, 64749, 65088, 65427, 65766, 66105,
- 66444, 66783, 67122, 67461, 67800, 68139, 68478, 68817, 69156, 69495,
- 69834, 70173, 70512, 70851, 71190, 71529, 71868, 72207, 72546, 72885,
- 73224, 73563, 73902, 74241, 74580, 74919, 75258, 75597, 75936, 76275,
- 76614, 76953, 77292, 77631, 77970, 78309, 78648, 78987, 79326, 79665,
- 80004, 80343, 80682, 81021, 81360, 81699, 82038, 82377, 82716, 83055,
- 83394, 83733, 84072, 84411, 84750, 85089, 85428, 85767, 86106, 86445
- };
-
- private static readonly int[] Cb2Cr0Lut =
+ public static readonly int[] Y =
+ {
+ 306, 601, 117, 0,
+ 306, 601, 117, 0,
+ };
+
+ public static readonly int[] Cb =
+ {
+ -172, 339, 512, 0,
+ -172, 339, 512, 0,
+ };
+
+ public static readonly int[] Cr =
+ {
+ 512, 429, 83, 0,
+ 512, 429, 83, 0,
+ };
+
+ public static class SelectLeft
+ {
+ public static readonly int[] Y =
+ {
+ 1, 1, 1, 0,
+ 0, 0, 0, 0,
+ };
+
+ public static readonly int[] Cb =
+ {
+ 1, -1, 1, 0,
+ 0, 0, 0, 0,
+ };
+
+ public static readonly int[] Cr =
+ {
+ 1, -1, -1, 0,
+ 0, 0, 0, 0,
+ };
+ }
+
+ public static class SelectRight
+ {
+ public static readonly int[] Y =
+ {
+ 0, 0, 0, 0,
+ 1, 1, 1, 0,
+ };
+
+ public static readonly int[] Cb =
+ {
+ 0, 0, 0, 0,
+ 1, -1, 1, 0,
+ };
+
+ public static readonly int[] Cr =
+ {
+ 0, 0, 0, 0,
+ 1, -1, -1, 0,
+ };
+ }
+ }
+
+ // Waiting for C# 7 stackalloc keyword patiently ...
+ private static class OnStackInputCache
{
- 0, 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5632, 6144,
- 6656, 7168, 7680, 8192, 8704, 9216, 9728, 10240, 10752, 11264, 11776,
- 12288, 12800, 13312, 13824, 14336, 14848, 15360, 15872, 16384, 16896,
- 17408, 17920, 18432, 18944, 19456, 19968, 20480, 20992, 21504, 22016,
- 22528, 23040, 23552, 24064, 24576, 25088, 25600, 26112, 26624, 27136,
- 27648, 28160, 28672, 29184, 29696, 30208, 30720, 31232, 31744, 32256,
- 32768, 33280, 33792, 34304, 34816, 35328, 35840, 36352, 36864, 37376,
- 37888, 38400, 38912, 39424, 39936, 40448, 40960, 41472, 41984, 42496,
- 43008, 43520, 44032, 44544, 45056, 45568, 46080, 46592, 47104, 47616,
- 48128, 48640, 49152, 49664, 50176, 50688, 51200, 51712, 52224, 52736,
- 53248, 53760, 54272, 54784, 55296, 55808, 56320, 56832, 57344, 57856,
- 58368, 58880, 59392, 59904, 60416, 60928, 61440, 61952, 62464, 62976,
- 63488, 64000, 64512, 65024, 65536, 66048, 66560, 67072, 67584, 68096,
- 68608, 69120, 69632, 70144, 70656, 71168, 71680, 72192, 72704, 73216,
- 73728, 74240, 74752, 75264, 75776, 76288, 76800, 77312, 77824, 78336,
- 78848, 79360, 79872, 80384, 80896, 81408, 81920, 82432, 82944, 83456,
- 83968, 84480, 84992, 85504, 86016, 86528, 87040, 87552, 88064, 88576,
- 89088, 89600, 90112, 90624, 91136, 91648, 92160, 92672, 93184, 93696,
- 94208, 94720, 95232, 95744, 96256, 96768, 97280, 97792, 98304, 98816,
- 99328, 99840, 100352, 100864, 101376, 101888, 102400, 102912, 103424,
- 103936, 104448, 104960, 105472, 105984, 106496, 107008, 107520, 108032,
- 108544, 109056, 109568, 110080, 110592, 111104, 111616, 112128, 112640,
- 113152, 113664, 114176, 114688, 115200, 115712, 116224, 116736, 117248,
- 117760, 118272, 118784, 119296, 119808, 120320, 120832, 121344, 121856,
- 122368, 122880, 123392, 123904, 124416, 124928, 125440, 125952, 126464,
- 126976, 127488, 128000, 128512, 129024, 129536, 130048, 130560
- };
-
- private static readonly int[] Cr1Lut =
+ public unsafe struct Byte
+ {
+ public fixed byte Data[InputByteCount * 3];
+
+ public static Byte Create(byte[] data)
+ {
+ Byte result = default(Byte);
+ for (int i = 0; i < data.Length; i++)
+ {
+ result.Data[i] = data[i];
+ }
+ return result;
+ }
+ }
+ }
+
+ public struct Result
{
- 0, 429, 858, 1287, 1716, 2145, 2574, 3003, 3432, 3861, 4290, 4719, 5148,
- 5577, 6006, 6435, 6864, 7293, 7722, 8151, 8580, 9009, 9438, 9867, 10296,
- 10725, 11154, 11583, 12012, 12441, 12870, 13299, 13728, 14157, 14586,
- 15015, 15444, 15873, 16302, 16731, 17160, 17589, 18018, 18447, 18876,
- 19305, 19734, 20163, 20592, 21021, 21450, 21879, 22308, 22737, 23166,
- 23595, 24024, 24453, 24882, 25311, 25740, 26169, 26598, 27027, 27456,
- 27885, 28314, 28743, 29172, 29601, 30030, 30459, 30888, 31317, 31746,
- 32175, 32604, 33033, 33462, 33891, 34320, 34749, 35178, 35607, 36036,
- 36465, 36894, 37323, 37752, 38181, 38610, 39039, 39468, 39897, 40326,
- 40755, 41184, 41613, 42042, 42471, 42900, 43329, 43758, 44187, 44616,
- 45045, 45474, 45903, 46332, 46761, 47190, 47619, 48048, 48477, 48906,
- 49335, 49764, 50193, 50622, 51051, 51480, 51909, 52338, 52767, 53196,
- 53625, 54054, 54483, 54912, 55341, 55770, 56199, 56628, 57057, 57486,
- 57915, 58344, 58773, 59202, 59631, 60060, 60489, 60918, 61347, 61776,
- 62205, 62634, 63063, 63492, 63921, 64350, 64779, 65208, 65637, 66066,
- 66495, 66924, 67353, 67782, 68211, 68640, 69069, 69498, 69927, 70356,
- 70785, 71214, 71643, 72072, 72501, 72930, 73359, 73788, 74217, 74646,
- 75075, 75504, 75933, 76362, 76791, 77220, 77649, 78078, 78507, 78936,
- 79365, 79794, 80223, 80652, 81081, 81510, 81939, 82368, 82797, 83226,
- 83655, 84084, 84513, 84942, 85371, 85800, 86229, 86658, 87087, 87516,
- 87945, 88374, 88803, 89232, 89661, 90090, 90519, 90948, 91377, 91806,
- 92235, 92664, 93093, 93522, 93951, 94380, 94809, 95238, 95667, 96096,
- 96525, 96954, 97383, 97812, 98241, 98670, 99099, 99528, 99957, 100386,
- 100815, 101244, 101673, 102102, 102531, 102960, 103389, 103818, 104247,
- 104676, 105105, 105534, 105963, 106392, 106821, 107250, 107679, 108108,
- 108537, 108966, 109395
- };
-
- private static readonly int[] Cr2Lut =
+ internal Block8x8F Y;
+ internal Block8x8F Cb;
+ internal Block8x8F Cr;
+ }
+
+ // The operation is defined as "RGBA -> YCbCr Transform a stream of bytes into a stream of floats"
+ // We need to benchmark the whole operation, to get true results, not missing any side effects!
+ private byte[] inputSourceRGB = null;
+
+ private int[] inputSourceRGBAsInteger = null;
+
+ [Setup]
+ public void Setup()
{
- 0, 83, 166, 249, 332, 415, 498, 581, 664, 747, 830, 913, 996, 1079, 1162,
- 1245, 1328, 1411, 1494, 1577, 1660, 1743, 1826, 1909, 1992, 2075, 2158,
- 2241, 2324, 2407, 2490, 2573, 2656, 2739, 2822, 2905, 2988, 3071, 3154,
- 3237, 3320, 3403, 3486, 3569, 3652, 3735, 3818, 3901, 3984, 4067, 4150,
- 4233, 4316, 4399, 4482, 4565, 4648, 4731, 4814, 4897, 4980, 5063, 5146,
- 5229, 5312, 5395, 5478, 5561, 5644, 5727, 5810, 5893, 5976, 6059, 6142,
- 6225, 6308, 6391, 6474, 6557, 6640, 6723, 6806, 6889, 6972, 7055, 7138,
- 7221, 7304, 7387, 7470, 7553, 7636, 7719, 7802, 7885, 7968, 8051, 8134,
- 8217, 8300, 8383, 8466, 8549, 8632, 8715, 8798, 8881, 8964, 9047, 9130,
- 9213, 9296, 9379, 9462, 9545, 9628, 9711, 9794, 9877, 9960, 10043, 10126,
- 10209, 10292, 10375, 10458, 10541, 10624, 10707, 10790, 10873, 10956,
- 11039, 11122, 11205, 11288, 11371, 11454, 11537, 11620, 11703, 11786,
- 11869, 11952, 12035, 12118, 12201, 12284, 12367, 12450, 12533, 12616,
- 12699, 12782, 12865, 12948, 13031, 13114, 13197, 13280, 13363, 13446,
- 13529, 13612, 13695, 13778, 13861, 13944, 14027, 14110, 14193, 14276,
- 14359, 14442, 14525, 14608, 14691, 14774, 14857, 14940, 15023, 15106,
- 15189, 15272, 15355, 15438, 15521, 15604, 15687, 15770, 15853, 15936,
- 16019, 16102, 16185, 16268, 16351, 16434, 16517, 16600, 16683, 16766,
- 16849, 16932, 17015, 17098, 17181, 17264, 17347, 17430, 17513, 17596,
- 17679, 17762, 17845, 17928, 18011, 18094, 18177, 18260, 18343, 18426,
- 18509, 18592, 18675, 18758, 18841, 18924, 19007, 19090, 19173, 19256,
- 19339, 19422, 19505, 19588, 19671, 19754, 19837, 19920, 20003, 20086,
- 20169, 20252, 20335, 20418, 20501, 20584, 20667, 20750, 20833, 20916,
- 20999, 21082, 21165
- };
+ // Console.WriteLine("Vector.Count: " + Vector.Count);
+ this.inputSourceRGB = new byte[InputByteCount];
+ for (int i = 0; i < this.inputSourceRGB.Length; i++)
+ {
+ this.inputSourceRGB[i] = (byte)(42 + i);
+ }
+ this.inputSourceRGBAsInteger = new int[InputByteCount + Vector.Count]; // Filling this should be part of the measured operation
+ }
[Benchmark(Baseline = true, Description = "Floating Point Conversion")]
- public Vector3 RgbaToYcbCr()
+ public unsafe void RgbaToYcbCrScalarFloat()
{
- Vector3 v = new Vector3(255);
+ // Copy the input to the stack:
+ OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB);
- float yy = (0.299F * v.X) + (0.587F * v.Y) + (0.114F * v.Z);
- float cb = 128 + ((-0.168736F * v.X) - (0.331264F * v.Y) + (0.5F * v.Z));
- float cr = 128 + ((0.5F * v.X) - (0.418688F * v.Y) - (0.081312F * v.Z));
+ // On-stack output:
+ Result result = default(Result);
+ float* yPtr = (float*)&result.Y;
+ float* cbPtr = (float*)&result.Cb;
+ float* crPtr = (float*)&result.Cr;
+ // end of code-bloat block :)
- return new Vector3(yy, cb, cr);
+ for (int i = 0; i < InputColorCount; i++)
+ {
+ int i3 = i * 3;
+ float r = input.Data[i3 + 0];
+ float g = input.Data[i3 + 1];
+ float b = input.Data[i3 + 2];
+
+ *yPtr++ = (0.299F * r) + (0.587F * g) + (0.114F * b);
+ *cbPtr++ = 128 + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b));
+ *crPtr++ = 128 + ((0.5F * r) - (0.418688F * g) - (0.081312F * b));
+ }
}
[Benchmark(Description = "Simd Floating Point Conversion")]
- public Vector3 RgbaToYcbCrSimd()
+ public unsafe void RgbaToYcbCrSimdFloat()
+ {
+ // Copy the input to the stack:
+ OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB);
+
+ // On-stack output:
+ Result result = default(Result);
+ float* yPtr = (float*)&result.Y;
+ float* cbPtr = (float*)&result.Cb;
+ float* crPtr = (float*)&result.Cr;
+ // end of code-bloat block :)
+
+ for (int i = 0; i < InputColorCount; i++)
+ {
+ int i3 = i * 3;
+
+ Vector3 vectorRgb = new Vector3(
+ input.Data[i3 + 0],
+ input.Data[i3 + 1],
+ input.Data[i3 + 2]
+ );
+
+ Vector3 vectorY = VectorY * vectorRgb;
+ Vector3 vectorCb = VectorCb * vectorRgb;
+ Vector3 vectorCr = VectorCr * vectorRgb;
+
+ // Should be better in theory, but came out to be worse: :(
+ // Vector3 c = new Vector3(0, 128, 128);
+ // Vector3 xx = new Vector3(vectorY.X, vectorCb.X, vectorCr.X);
+ // Vector3 yy = new Vector3(vectorY.Y, -vectorCb.Y, -vectorCr.Y);
+ // Vector3 zz = new Vector3(vectorY.Z, vectorCb.Z, -vectorCr.Z);
+
+ // c += xx + yy + zz;
+ // *yPtr++ = c.X;
+ // *cbPtr++ = c.Y;
+ // *crPtr++ = c.Z;
+
+ *yPtr++ = vectorY.X + vectorY.Y + vectorY.Z;
+ *cbPtr++ = 128 + (vectorCb.X - vectorCb.Y + vectorCb.Z);
+ *crPtr++ = 128 + (vectorCr.X - vectorCr.Y - vectorCr.Z);
+ }
+ }
+
+ [Benchmark(Description = "Scaled Integer Conversion + Vector")]
+ public unsafe void RgbaToYcbCrScaledIntegerSimd()
{
- Vector3 vectorRgb = new Vector3(255);
- Vector3 vectorY = VectorY * vectorRgb;
- Vector3 vectorCb = VectorCb * vectorRgb;
- Vector3 vectorCr = VectorCr * vectorRgb;
+ // Copy the input to the stack:
+
+ // On-stack output:
+ Result result = default(Result);
+ float* yPtr = (float*)&result.Y;
+ float* cbPtr = (float*)&result.Cb;
+ float* crPtr = (float*)&result.Cr;
+ // end of code-bloat block :)
+
+ Vector yCoeffs = new Vector(ScaledCoeffs.Y);
+ Vector cbCoeffs = new Vector(ScaledCoeffs.Cb);
+ Vector crCoeffs = new Vector(ScaledCoeffs.Cr);
+
+ for (int i = 0; i < this.inputSourceRGB.Length; i++)
+ {
+ this.inputSourceRGBAsInteger[i] = this.inputSourceRGB[i];
+ }
- float yy = vectorY.X + vectorY.Y + vectorY.Z;
- float cb = 128 + (vectorCb.X - vectorCb.Y + vectorCb.Z);
- float cr = 128 + (vectorCr.X - vectorCr.Y - vectorCr.Z);
+ for (int i = 0; i < InputColorCount; i += 2)
+ {
+ Vector rgb = new Vector(this.inputSourceRGBAsInteger, i * 3);
- return new Vector3(yy, cb, cr);
+ Vector y = yCoeffs * rgb;
+ Vector cb = cbCoeffs * rgb;
+ Vector cr = crCoeffs * rgb;
+
+ *yPtr++ = (y[0] + y[1] + y[2]) >> 10;
+ *cbPtr++ = 128 + ((cb[0] - cb[1] + cb[2]) >> 10);
+ *crPtr++ = 128 + ((cr[0] - cr[1] - cr[2]) >> 10);
+
+ *yPtr++ = (y[4] + y[5] + y[6]) >> 10;
+ *cbPtr++ = 128 + ((cb[4] - cb[5] + cb[6]) >> 10);
+ *crPtr++ = 128 + ((cr[4] - cr[5] - cr[6]) >> 10);
+ }
}
- [Benchmark(Description = "Scaled Integer Conversion")]
- public Vector3 RgbaToYcbCrScaled()
+ ///
+ /// This should perform better. Coreclr emmitted Vector.Dot() code lacks the vectorization even with IsHardwareAccelerated == true.
+ /// Kept this benchmark because maybe it will be improved in a future CLR release.
+ ///
+ /// https://www.gamedev.net/topic/673396-c-systemnumericsvectors-slow/
+ ///
+ ///
+ [Benchmark(Description = "Scaled Integer Conversion + Vector + Dot Product")]
+ public unsafe void RgbaToYcbCrScaledIntegerSimdWithDotProduct()
{
- int r = 255;
- int g = 255;
- int b = 255;
+ // Copy the input to the stack:
+
+ // On-stack output:
+ Result result = default(Result);
+ float* yPtr = (float*)&result.Y;
+ float* cbPtr = (float*)&result.Cb;
+ float* crPtr = (float*)&result.Cr;
+ // end of code-bloat block :)
+
+ Vector yCoeffs = new Vector(ScaledCoeffs.Y);
+ Vector cbCoeffs = new Vector(ScaledCoeffs.Cb);
+ Vector crCoeffs = new Vector(ScaledCoeffs.Cr);
+
+ Vector leftY = new Vector(ScaledCoeffs.SelectLeft.Y);
+ Vector leftCb = new Vector(ScaledCoeffs.SelectLeft.Cb);
+ Vector leftCr = new Vector(ScaledCoeffs.SelectLeft.Cr);
- // Scale by 1024, add .5F and truncate value
- int y0 = 306 * r; // (0.299F * 1024) + .5F
- int y1 = 601 * g; // (0.587F * 1024) + .5F
- int y2 = 117 * b; // (0.114F * 1024) + .5F
+ Vector rightY = new Vector(ScaledCoeffs.SelectRight.Y);
+ Vector rightCb = new Vector(ScaledCoeffs.SelectRight.Cb);
+ Vector rightCr = new Vector(ScaledCoeffs.SelectRight.Cr);
- int cb0 = -172 * r; // (-0.168736F * 1024) + .5F
- int cb1 = 339 * g; // (0.331264F * 1024) + .5F
- int cb2 = 512 * b; // (0.5F * 1024) + .5F
+ for (int i = 0; i < this.inputSourceRGB.Length; i++)
+ {
+ this.inputSourceRGBAsInteger[i] = this.inputSourceRGB[i];
+ }
- int cr0 = 512 * r; // (0.5F * 1024) + .5F
- int cr1 = 429 * g; // (0.418688F * 1024) + .5F
- int cr2 = 83 * b; // (0.081312F * 1024) + .5F
+ for (int i = 0; i < InputColorCount; i += 2)
+ {
+ Vector rgb = new Vector(this.inputSourceRGBAsInteger, i * 3);
- float yy = (y0 + y1 + y2) >> 10;
- float cb = 128 + ((cb0 - cb1 + cb2) >> 10);
- float cr = 128 + ((cr0 - cr1 - cr2) >> 10);
+ Vector y = yCoeffs * rgb;
+ Vector cb = cbCoeffs * rgb;
+ Vector cr = crCoeffs * rgb;
- return new Vector3(yy, cb, cr);
+ VectorizedConvertImpl(ref yPtr, ref cbPtr, ref crPtr, y, cb, cr, leftY, leftCb, leftCr);
+ VectorizedConvertImpl(ref yPtr, ref cbPtr, ref crPtr, y, cb, cr, rightY, rightCb, rightCr);
+ }
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static unsafe void VectorizedConvertImpl(
+ ref float* yPtr,
+ ref float* cbPtr,
+ ref float* crPtr,
+ Vector y,
+ Vector cb,
+ Vector cr,
+ Vector yAgg,
+ Vector cbAgg,
+ Vector crAgg)
+ {
+ int ySum = Vector.Dot(y, yAgg);
+ int cbSum = Vector.Dot(cb, cbAgg);
+ int crSum = Vector.Dot(cr, crAgg);
+ *yPtr++ = ySum >> 10;
+ *cbPtr++ = 128 + (cbSum >> 10);
+ *crPtr++ = 128 + (crSum >> 10);
+ }
+
+ [Benchmark(Description = "Scaled Integer Conversion")]
+ public unsafe void RgbaToYcbCrScaledInteger()
+ {
+ // Copy the input to the stack:
+ OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB);
+
+ // On-stack output:
+ Result result = default(Result);
+ float* yPtr = (float*)&result.Y;
+ float* cbPtr = (float*)&result.Cb;
+ float* crPtr = (float*)&result.Cr;
+ // end of code-bloat block :)
+
+ for (int i = 0; i < InputColorCount; i++)
+ {
+ int i3 = i * 3;
+ int r = input.Data[i3 + 0];
+ int g = input.Data[i3 + 1];
+ int b = input.Data[i3 + 2];
+
+ // Scale by 1024, add .5F and truncate value
+ int y0 = 306 * r; // (0.299F * 1024) + .5F
+ int y1 = 601 * g; // (0.587F * 1024) + .5F
+ int y2 = 117 * b; // (0.114F * 1024) + .5F
+
+ int cb0 = -172 * r; // (-0.168736F * 1024) + .5F
+ int cb1 = 339 * g; // (0.331264F * 1024) + .5F
+ int cb2 = 512 * b; // (0.5F * 1024) + .5F
+
+ int cr0 = 512 * r; // (0.5F * 1024) + .5F
+ int cr1 = 429 * g; // (0.418688F * 1024) + .5F
+ int cr2 = 83 * b; // (0.081312F * 1024) + .5F
+
+ *yPtr++ = (y0 + y1 + y2) >> 10;
+ *cbPtr++ = 128 + ((cb0 - cb1 + cb2) >> 10);
+ *crPtr++ = 128 + ((cr0 - cr1 - cr2) >> 10);
+ }
+ }
+
[Benchmark(Description = "Scaled Integer LUT Conversion")]
- public Vector3 RgbaToYcbCrScaledLut()
+ public unsafe void RgbaToYcbCrScaledIntegerLut()
{
- int r = 255;
- int g = 255;
- int b = 255;
+ // Copy the input to the stack:
+ OnStackInputCache.Byte input = OnStackInputCache.Byte.Create(this.inputSourceRGB);
+
+ // On-stack output:
+ Result result = default(Result);
+ float* yPtr = (float*)&result.Y;
+ float* cbPtr = (float*)&result.Cb;
+ float* crPtr = (float*)&result.Cr;
+ // end of code-bloat block :)
+
+ for (int i = 0; i < InputColorCount; i++)
+ {
+ int i3 = i * 3;
- float yy = (Y0Lut[r] + Y1Lut[g] + Y2Lut[b]) >> 10;
- float cb = 128 + ((Cb0Lut[r] - Cb1Lut[g] + Cb2Cr0Lut[b]) >> 10);
- float cr = 128 + ((Cb2Cr0Lut[r] - Cr1Lut[g] - Cr2Lut[b]) >> 10);
+ int r = input.Data[i3 + 0];
+ int g = input.Data[i3 + 1];
+ int b = input.Data[i3 + 2];
- return new Vector3(yy, cb, cr);
+ // TODO: Maybe concatenating all the arrays in LookupTables to a flat one can improve this!
+ *yPtr++ = (LookupTables.Y0[r] + LookupTables.Y1[g] + LookupTables.Y2[b]) >> 10;
+ *cbPtr++ = 128 + ((LookupTables.Cb0[r] - LookupTables.Cb1[g] + LookupTables.Cb2Cr0[b]) >> 10);
+ *crPtr++ = 128 + ((LookupTables.Cb2Cr0[r] - LookupTables.Cr1[g] - LookupTables.Cr2[b]) >> 10);
+ }
}
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
new file mode 100644
index 000000000..19ff65f1d
--- /dev/null
+++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
@@ -0,0 +1,217 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}
+ Library
+ Properties
+ ImageSharp
+ ImageSharp.Sandbox46
+ v4.6.1
+ 512
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;BENCHMARKING
+ prompt
+ 4
+ true
+
+
+
+
+
+
+ ..\..\packages\System.Numerics.Vectors.4.1.1\lib\net46\System.Numerics.Vectors.dll
+ True
+
+
+
+
+
+
+
+
+ ..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll
+ True
+
+
+ ..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll
+ True
+
+
+ ..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\net45\xunit.core.dll
+ True
+
+
+ ..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\net45\xunit.execution.desktop.dll
+ True
+
+
+
+
+ ..\..\src\ImageSharp\bin\$(Configuration)\net45\ImageSharp.dll
+
+
+ ..\..\src\ImageSharp.Drawing\bin\$(Configuration)\net45\ImageSharp.Drawing.dll
+
+
+ ..\..\src\ImageSharp.Formats.Bmp\bin\$(Configuration)\net45\ImageSharp.Formats.Bmp.dll
+
+
+ ..\..\src\ImageSharp.Formats.Gif\bin\$(Configuration)\net45\ImageSharp.Formats.Gif.dll
+
+
+ ..\..\src\ImageSharp.Formats.Jpeg\bin\$(Configuration)\net45\ImageSharp.Formats.Jpeg.dll
+
+
+ ..\..\src\ImageSharp.Formats.Png\bin\$(Configuration)\net45\ImageSharp.Formats.Png.dll
+
+
+ ..\..\src\ImageSharp.Processing\bin\$(Configuration)\net45\ImageSharp.Processing.dll
+
+
+
+
+ Tests\Drawing\PolygonTests.cs
+
+
+ Tests\FileTestBase.cs
+
+
+ Tests\Formats\Jpg\Block8x8FTests.cs
+
+
+ Tests\Formats\Jpg\JpegTestBase.cs
+
+
+ Tests\Formats\Jpg\JpegTests.cs
+
+
+ Tests\Formats\Jpg\ReferenceImplementations.cs
+
+
+ Tests\Formats\Jpg\ReferenceImplementationsTests.cs
+
+
+ Tests\Formats\Jpg\YCbCrImageTests.cs
+
+
+ Tests\Image\ImagePropertyTests.cs
+
+
+ Tests\Image\ImageTests.cs
+
+
+ Tests\Image\PixelAccessorTests.cs
+
+
+ Tests\Processors\Filters\ResizeTests.cs
+
+
+ Tests\TestBase.cs
+
+
+ Tests\TestFile.cs
+
+
+ Tests\TestImages.cs
+
+
+ Tests\TestUtilities\Attributes\ImageDataAttributeBase.cs
+
+
+ Tests\TestUtilities\Attributes\WithBlankImageAttribute.cs
+
+
+ Tests\TestUtilities\Attributes\WithFileAttribute.cs
+
+
+ Tests\TestUtilities\Attributes\WithFileCollectionAttribute.cs
+
+
+ Tests\TestUtilities\Attributes\WithMemberFactoryAttribute.cs
+
+
+ Tests\TestUtilities\Attributes\WithSolidFilledImagesAttribute.cs
+
+
+ Tests\TestUtilities\Factories\GenericFactory.cs
+
+
+ Tests\TestUtilities\Factories\ImageFactory.cs
+
+
+ Tests\TestUtilities\ImageProviders\BlankProvider.cs
+
+
+ Tests\TestUtilities\ImageProviders\FileProvider.cs
+
+
+ Tests\TestUtilities\ImageProviders\LambdaProvider.cs
+
+
+ Tests\TestUtilities\ImageProviders\SolidProvider.cs
+
+
+ Tests\TestUtilities\ImageProviders\TestImageProvider.cs
+
+
+ Tests\TestUtilities\ImagingTestCaseUtility.cs
+
+
+ Tests\TestUtilities\MeasureFixture.cs
+
+
+ Tests\TestUtilities\PixelTypes.cs
+
+
+ Tests\TestUtilities\Tests\TestImageProviderTests.cs
+
+
+ Tests\TestUtilities\Tests\TestUtilityExtensionsTests.cs
+
+
+ Tests\TestUtilities\TestUtilityExtensions.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/ImageSharp.Sandbox46/Properties/AssemblyInfo.cs b/tests/ImageSharp.Sandbox46/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..51b205188
--- /dev/null
+++ b/tests/ImageSharp.Sandbox46/Properties/AssemblyInfo.cs
@@ -0,0 +1,25 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+[assembly: AssemblyTitle("ImageSharp.Sandbox46")]
+[assembly: AssemblyDescription("A cross-platform library for processing of image files written in C#")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ImageSharp.Sandbox46")]
+[assembly: AssemblyCopyright("Copyright © James Jackson-South and contributors.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("96188137-5fa6-4924-ab6e-4eff79c6e0bb")]
diff --git a/tests/ImageSharp.Sandbox46/README.md b/tests/ImageSharp.Sandbox46/README.md
new file mode 100644
index 000000000..b05afb853
--- /dev/null
+++ b/tests/ImageSharp.Sandbox46/README.md
@@ -0,0 +1,24 @@
+## Purpose
+This project aims to workaround certain .NET Core tooling issues in Visual Studio based developer workflow at the time of it's creation (January 2017):
+- .NET Core Performance profiling is not possible neither with Visual Studio nor with JetBrains profilers
+- ~~JetBrains Unit Test explorer does not work with .NET Core projects~~
+
+## How does it work?
+- By referencing .NET 4.5 dll-s created by net45 target's of ImageSharp projects. NOTE: These are not project references!
+- By including test classes (and utility classes) of the `ImageSharp.Tests` project using MSBUILD ``
+- Compiling `ImageSharp.Sandbox46` should trigger the compilation of ImageSharp subprojects using a manually defined solution dependencies
+
+## How to profile unit tests
+
+#### 1. With Visual Studio 2015 Test Runner
+- **Do not** build `ImageSharp.Tests`
+- Build `ImageSharp.Sandbox46`
+- Use the [context menu in Test Explorer](https://adamprescott.net/2012/12/12/performance-profiling-for-unit-tests/)
+
+NOTE:
+There was no *Profile test* option in my VS Professional. Maybe things were messed by VS2017 RC installation. [This post suggests](http://stackoverflow.com/questions/32034375/profiling-tests-in-visual-studio-community-2015) it's necessary to own Premium or Ultimate edition of Visual Studio to profile tests.
+
+#### 2. With JetBrains ReSharper Ultimate
+- The `Sandbox46` project is no longer needed here. The classic `ImageSharp.Tests` project can be discovered by Unit Test Explorer.
+- You can use [context menus](https://www.jetbrains.com/resharper/features/unit_testing.html) from your test class, or from unit Test Exporer/Unit Test Sessions windows.
+
\ No newline at end of file
diff --git a/tests/ImageSharp.Sandbox46/packages.config b/tests/ImageSharp.Sandbox46/packages.config
new file mode 100644
index 000000000..8163e6e2e
--- /dev/null
+++ b/tests/ImageSharp.Sandbox46/packages.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs
index 72a681a55..e9343b20d 100644
--- a/tests/ImageSharp.Tests/FileTestBase.cs
+++ b/tests/ImageSharp.Tests/FileTestBase.cs
@@ -33,6 +33,7 @@ namespace ImageSharp.Tests
// TestFile.Create(TestImages.Png.Blur), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Indexed), // Perf: Enable for local testing only
TestFile.Create(TestImages.Png.Splash),
+ TestFile.Create(TestImages.Png.Powerpoint),
// TestFile.Create(TestImages.Png.SplashInterlaced), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Interlaced), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Filter0), // Perf: Enable for local testing only
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
index 6736548e6..1f055bab4 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
@@ -12,14 +12,12 @@ namespace ImageSharp.Tests
{
using System.Diagnostics;
using System.Numerics;
-
- using ImageSharp.Formats;
using ImageSharp.Formats.Jpg;
using Xunit;
using Xunit.Abstractions;
- public class Block8x8FTests : UtilityTestClassBase
+ public class Block8x8FTests : JpegTestBase
{
#if BENCHMARKING
public const int Times = 1000000;
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegTestBase.cs
similarity index 75%
rename from tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs
rename to tests/ImageSharp.Tests/Formats/Jpg/JpegTestBase.cs
index c92c6aa9a..2e568ba34 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegTestBase.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
@@ -13,46 +13,15 @@ namespace ImageSharp.Tests
using System;
using System.Collections.Generic;
using System.Diagnostics;
- using System.Runtime.CompilerServices;
using ImageSharp.Formats.Jpg;
- ///
- /// Utility class to measure the execution of an operation.
- ///
- public class MeasureFixture : TestBase
+ public class JpegTestBase : MeasureFixture
{
- protected bool EnablePrinting = true;
-
- protected void Measure(int times, Action action, [CallerMemberName] string operationName = null)
- {
- if (this.EnablePrinting) this.Output?.WriteLine($"{operationName} X {times} ...");
- Stopwatch sw = Stopwatch.StartNew();
-
- for (int i = 0; i < times; i++)
- {
- action();
- }
-
- sw.Stop();
- if (this.EnablePrinting) this.Output?.WriteLine($"{operationName} finished in {sw.ElapsedMilliseconds} ms");
- }
-
- public MeasureFixture(ITestOutputHelper output)
+ public JpegTestBase(ITestOutputHelper output) : base(output)
{
- this.Output = output;
}
- protected ITestOutputHelper Output { get; }
- }
-
-
- public class UtilityTestClassBase : MeasureFixture
- {
- public UtilityTestClassBase(ITestOutputHelper output) : base(output)
- {
- }
-
// ReSharper disable once InconsistentNaming
public static float[] Create8x8FloatData()
{
@@ -66,7 +35,7 @@ namespace ImageSharp.Tests
}
return result;
}
-
+
// ReSharper disable once InconsistentNaming
public static int[] Create8x8IntData()
{
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
index 53c44d836..d21a4edf7 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
@@ -17,6 +17,7 @@ namespace ImageSharp.Tests
using System.Numerics;
using ImageSharp.Formats.Jpg;
+ using ImageSharp.Processing;
public class JpegTests : MeasureFixture
{
@@ -25,6 +26,64 @@ namespace ImageSharp.Tests
{
}
+ [Theory]
+ [WithFile(TestImages.Jpeg.Snake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio420)]
+ [WithFile(TestImages.Jpeg.Lake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio420)]
+ [WithFile(TestImages.Jpeg.Snake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio444)]
+ [WithFile(TestImages.Jpeg.Lake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio444)]
+ public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ var image = provider.GetImage()
+ .Resize(new ResizeOptions
+ {
+ Size = new Size(150, 100),
+ Mode = ResizeMode.Max
+ });
+ image.Quality = quality;
+ image.ExifProfile = null; // Reduce the size of the file
+ JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality };
+
+ provider.Utility.TestName += $"{subsample}_Q{quality}";
+ provider.Utility.SaveTestOutputFile(image, "png");
+ provider.Utility.SaveTestOutputFile(image, "jpg", encoder);
+ }
+
+ // Benchmark, enable manually!
+ // [Theory]
+ [InlineData(1, 75, JpegSubsample.Ratio420)]
+ [InlineData(30, 75, JpegSubsample.Ratio420)]
+ [InlineData(30, 75, JpegSubsample.Ratio444)]
+ [InlineData(30, 100, JpegSubsample.Ratio444)]
+ public void Encoder_Benchmark(int executionCount, int quality, JpegSubsample subsample)
+ {
+ string[] testFiles = TestImages.Bmp.All
+ .Concat(new[] { TestImages.Jpeg.Calliphora, TestImages.Jpeg.Cmyk })
+ .ToArray();
+
+ var testImages =
+ testFiles.Select(
+ tf => TestImageProvider.File(tf, pixelTypeOverride: PixelTypes.StandardImageClass).GetImage())
+ .ToArray();
+
+ using (MemoryStream ms = new MemoryStream())
+ {
+ this.Measure(executionCount,
+ () =>
+ {
+ foreach (Image img in testImages)
+ {
+ JpegEncoder encoder = new JpegEncoder() { Quality = quality, Subsample = subsample };
+ img.Save(ms, encoder);
+ ms.Seek(0, SeekOrigin.Begin);
+ }
+ },
+ // ReSharper disable once ExplicitCallerInfoArgument
+ $@"Encode {testFiles.Length} images"
+ );
+ }
+ }
+
public static IEnumerable AllJpegFiles => TestImages.Jpeg.All;
[Theory]
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
index 6aed0d3fa..60c136674 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
@@ -13,7 +13,7 @@ namespace ImageSharp.Tests
using ImageSharp.Formats;
using ImageSharp.Formats.Jpg;
-
+
///
/// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests
/// Floating point DCT code Ported from https://github.com/norishigefukushima/dct_simd
@@ -546,8 +546,8 @@ namespace ImageSharp.Tests
//y[1] = c0 + c3; y[7] = c0 - c3;
/*for(i = 0;i < 8;i++)
- {
- y[i] *= invsqrt2h;
+ {
+ y[i] *= invsqrt2h;
}*/
}
@@ -643,10 +643,10 @@ namespace ImageSharp.Tests
0: 1.414214
1: 1.387040
2: 1.306563
- 3:
+ 3:
4: 1.000000
5: 0.785695
- 6:
+ 6:
7: 0.275899
*/
@@ -852,7 +852,7 @@ namespace ImageSharp.Tests
bool offsetSourceByNeg128 = false)
{
MutableSpan sWorker = offsetSourceByNeg128 ? s.AddScalarToAllValues(-128f) : s;
-
+
for (int j = 0; j < 8; j++)
{
fDCT1Dllm_32f(sWorker.Slice(j * 8), temp.Slice(j * 8));
@@ -866,7 +866,7 @@ namespace ImageSharp.Tests
}
Transpose8x8(temp, d);
-
+
if (downscaleBy8)
{
for (int j = 0; j < 64; j++)
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
index 66ddeabc0..63878ea34 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
@@ -13,7 +13,7 @@ namespace ImageSharp.Tests.Formats.Jpg
using Xunit;
using Xunit.Abstractions;
- public class ReferenceImplementationsTests : UtilityTestClassBase
+ public class ReferenceImplementationsTests : JpegTestBase
{
public ReferenceImplementationsTests(ITestOutputHelper output)
: base(output)
@@ -31,7 +31,7 @@ namespace ImageSharp.Tests.Formats.Jpg
MutableSpan floatSrc = intData.ConvertToFloat32MutableSpan();
ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(intData);
-
+
MutableSpan dest = new MutableSpan(64);
MutableSpan temp = new MutableSpan(64);
@@ -57,14 +57,14 @@ namespace ImageSharp.Tests.Formats.Jpg
var block = original.AddScalarToAllValues(128);
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(block);
-
+
for (int i = 0; i < 64; i++)
{
block[i] /= 8;
}
ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(block);
-
+
for (int i = startAt; i < 64; i++)
{
float expected = original[i];
@@ -93,7 +93,7 @@ namespace ImageSharp.Tests.Formats.Jpg
{
float expected = data[i];
float actual = (float)src[i];
-
+
Assert.Equal(expected, actual, new ApproximateFloatComparer(2f));
}
}
@@ -112,9 +112,9 @@ namespace ImageSharp.Tests.Formats.Jpg
{
MutableSpan intData = Create8x8RandomIntData(-200, 200, seed);
MutableSpan floatSrc = intData.ConvertToFloat32MutableSpan();
-
+
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(intData);
-
+
MutableSpan dest = new MutableSpan(64);
MutableSpan temp = new MutableSpan(64);
@@ -127,6 +127,6 @@ namespace ImageSharp.Tests.Formats.Jpg
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f));
}
- }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs
index 61a6a9f64..3d6695841 100644
--- a/tests/ImageSharp.Tests/TestFile.cs
+++ b/tests/ImageSharp.Tests/TestFile.cs
@@ -5,11 +5,12 @@
namespace ImageSharp.Tests
{
+ using System;
using System.Collections.Concurrent;
+ using System.Collections.Generic;
using System.IO;
-
- using ImageSharp.Formats;
using System.Linq;
+ using System.Reflection;
///
/// A test image file.
@@ -35,7 +36,7 @@ namespace ImageSharp.Tests
/// The file.
///
private readonly string file;
-
+
///
/// Initializes a new instance of the class.
///
@@ -132,13 +133,19 @@ namespace ImageSharp.Tests
///
private static string GetFormatsDirectory()
{
- var directories = new[] {
+ List directories = new List< string > {
"TestImages/Formats/", // Here for code coverage tests.
"tests/ImageSharp.Tests/TestImages/Formats/", // from travis/build script
- "../../../../TestImages/Formats/"
+ "../../../ImageSharp.Tests/TestImages/Formats/", // from Sandbox46
+ "../../../../TestImages/Formats/"
};
- directories= directories.Select(x => Path.GetFullPath(x)).ToArray();
+ directories = directories.SelectMany(x => new[]
+ {
+ Path.GetFullPath(x)
+ }).ToList();
+
+ AddFormatsDirectoryFromTestAssebmlyPath(directories);
var directory = directories.FirstOrDefault(x => Directory.Exists(x));
@@ -149,5 +156,24 @@ namespace ImageSharp.Tests
throw new System.Exception($"Unable to find Formats directory at any of these locations [{string.Join(", ", directories)}]");
}
+
+ ///
+ /// The path returned by Path.GetFullPath(x) can be relative to dotnet framework directory
+ /// in certain scenarios like dotTrace test profiling.
+ /// This method calculates and adds the format directory based on the ImageSharp.Tests assembly location.
+ ///
+ /// The directories list
+ private static void AddFormatsDirectoryFromTestAssebmlyPath(List directories)
+ {
+ string assemblyLocation = typeof(TestFile).GetTypeInfo().Assembly.Location;
+ assemblyLocation = Path.GetDirectoryName(assemblyLocation);
+
+ if (assemblyLocation != null)
+ {
+ string dirFromAssemblyLocation = Path.Combine(assemblyLocation, "../../../TestImages/Formats/");
+ dirFromAssemblyLocation = Path.GetFullPath(dirFromAssemblyLocation);
+ directories.Add(dirFromAssemblyLocation);
+ }
+ }
}
}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 7265e45a4..89b3c0f0d 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -19,6 +19,7 @@ namespace ImageSharp.Tests
public const string Blur = "Png/blur.png";
public const string Indexed = "Png/indexed.png";
public const string Splash = "Png/splash.png";
+ public const string Powerpoint = "Png/pp.png";
public const string SplashInterlaced = "Png/splash-interlaced.png";
@@ -50,6 +51,9 @@ namespace ImageSharp.Tests
public const string Festzug = "Jpg/Festzug.jpg";
public const string Hiyamugi = "Jpg/Hiyamugi.jpg";
+ public const string Snake = "Jpg/Snake.jpg";
+ public const string Lake = "Jpg/Lake.jpg";
+
public const string Jpeg400 = "Jpg/baseline/jpeg400jfif.jpg";
public const string Jpeg420 = "Jpg/baseline/jpeg420exif.jpg";
public const string Jpeg422 = "Jpg/baseline/jpeg422jfif.jpg";
diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/Lake.jpg b/tests/ImageSharp.Tests/TestImages/Formats/Jpg/Lake.jpg
new file mode 100644
index 000000000..d77c175f8
--- /dev/null
+++ b/tests/ImageSharp.Tests/TestImages/Formats/Jpg/Lake.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b7332d4e0b1d31367e04458d7cb33fd83eac31a8299d59efacd200350ec032d6
+size 206342
diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/Snake.jpg b/tests/ImageSharp.Tests/TestImages/Formats/Jpg/Snake.jpg
new file mode 100644
index 000000000..8ec1b3b83
--- /dev/null
+++ b/tests/ImageSharp.Tests/TestImages/Formats/Jpg/Snake.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f3d1b46db3b5974820fd2737db3f025d21b09c75aff73fd40ba6535dddf2ad70
+size 165200
diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/pp.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/pp.png
new file mode 100644
index 000000000..f13accaee
--- /dev/null
+++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/pp.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:99879d99fa8589427023012a0388ab0380b6d30b54921e2b7a39f43ae4b1bcc3
+size 12867
diff --git a/tests/ImageSharp.Tests/TestUtilities/EnumHelper.cs b/tests/ImageSharp.Tests/TestUtilities/EnumHelper.cs
deleted file mode 100644
index 7b6b26e94..000000000
--- a/tests/ImageSharp.Tests/TestUtilities/EnumHelper.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Tests
-{
- using System;
-
- public class EnumHelper
- {
- public static T[] GetSortedValues()
- {
- T[] vals = (T[])Enum.GetValues(typeof(T));
- Array.Sort(vals);
- return vals;
- }
- }
-}
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
index ea3645874..8eb658073 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
@@ -74,16 +74,19 @@ namespace ImageSharp.Tests
/// The pixel format of the image
/// The image instance
/// The requested extension
- public void SaveTestOutputFile(Image image, string extension = null)
+ /// Optional encoder
+ public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null)
where TColor : struct, IPackedPixel, IEquatable
{
string path = this.GetTestOutputFileName(extension);
var format = GetImageFormatByExtension(extension);
+ encoder = encoder ?? format.Encoder;
+
using (var stream = File.OpenWrite(path))
{
- image.Save(stream, format);
+ image.Save(stream, encoder);
}
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs
new file mode 100644
index 000000000..1dadc4884
--- /dev/null
+++ b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs
@@ -0,0 +1,50 @@
+namespace ImageSharp.Tests
+{
+ using System;
+ using System.Diagnostics;
+ using System.Runtime.CompilerServices;
+
+ using Xunit.Abstractions;
+
+ ///
+ /// Utility class to measure the execution of an operation. It can be used either by inheritance or by composition.
+ ///
+ public class MeasureFixture : TestBase
+ {
+ ///
+ /// Value indicating whether priniting is enabled.
+ ///
+ protected bool EnablePrinting = true;
+
+ ///
+ /// Measures and prints the execution time of an , executed multiple times.
+ ///
+ /// A value indicating how many times to run the action
+ /// The to execute
+ /// The name of the operation to print to the output
+ public void Measure(int times, Action action, [CallerMemberName] string operationName = null)
+ {
+ if (this.EnablePrinting) this.Output?.WriteLine($"{operationName} X {times} ...");
+ Stopwatch sw = Stopwatch.StartNew();
+
+ for (int i = 0; i < times; i++)
+ {
+ action();
+ }
+
+ sw.Stop();
+ if (this.EnablePrinting) this.Output?.WriteLine($"{operationName} finished in {sw.ElapsedMilliseconds} ms");
+ }
+
+ ///
+ /// Initializes a new instance of
+ ///
+ /// A instance to print the results
+ public MeasureFixture(ITestOutputHelper output)
+ {
+ this.Output = output;
+ }
+
+ protected ITestOutputHelper Output { get; }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs
index 8b0c65a3c..d2726c16a 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs
@@ -22,7 +22,7 @@ namespace ImageSharp.Tests
private static readonly Dictionary PixelTypes2ClrTypes = new Dictionary();
- private static readonly PixelTypes[] AllConcretePixelTypes = EnumHelper.GetSortedValues()
+ private static readonly PixelTypes[] AllConcretePixelTypes = GetAllPixelTypes()
.Except(new [] {PixelTypes.Undefined, PixelTypes.All })
.ToArray();
@@ -130,5 +130,11 @@ namespace ImageSharp.Tests
.Where(pt => pixelTypes.HasFlag(pt))
.Select(pt => new KeyValuePair(pt, pt.ToType()));
}
+
+ ///
+ /// Enumerate all available -s
+ ///
+ /// The pixel types
+ internal static PixelTypes[] GetAllPixelTypes() => (PixelTypes[])Enum.GetValues(typeof(PixelTypes));
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs
index 93d6eab16..23a42c1a6 100644
--- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs
@@ -114,7 +114,7 @@ namespace ImageSharp.Tests
IEnumerable> pixelTypesExp)
{
Assert.Contains(new KeyValuePair(pt, typeof(T)), pixelTypesExp);
-
+
}
[Fact]
@@ -125,7 +125,7 @@ namespace ImageSharp.Tests
var expanded = pixelTypes.ExpandAllTypes();
Assert.Equal(expanded.Count(), 5);
-
+
AssertContainsPixelType(PixelTypes.Alpha8, expanded);
AssertContainsPixelType(PixelTypes.Bgr565, expanded);
AssertContainsPixelType(PixelTypes.Color, expanded);
@@ -138,7 +138,7 @@ namespace ImageSharp.Tests
{
var expanded = PixelTypes.All.ExpandAllTypes().ToArray();
- Assert.True(expanded.Length >= EnumHelper.GetSortedValues().Length - 2);
+ Assert.True(expanded.Length >= TestUtilityExtensions.GetAllPixelTypes().Length - 2);
AssertContainsPixelType(PixelTypes.Color, expanded);
AssertContainsPixelType(PixelTypes.StandardImageClass, expanded);
}