Browse Source

docs

af/merge-core
Anton Firszov 9 years ago
parent
commit
23a578df0f
  1. 55
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  2. 3
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs
  3. 43
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs
  4. 32
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs
  5. 17
      src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs
  6. 0
      src/ImageSharp/Memory/Buffer2D{T}.cs
  7. 40
      src/ImageSharp/Memory/BufferArea{T}.cs
  8. 0
      src/ImageSharp/Memory/Buffer{T}.cs
  9. 0
      src/ImageSharp/Memory/IBuffer2D{T}.cs
  10. 2
      src/ImageSharp/Memory/IBuffer{T}.cs
  11. 2
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs
  12. 3
      tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
  13. 3
      tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs
  14. 20
      tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs
  15. 3
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs
  16. 6
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs

55
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs

@ -16,8 +16,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// </summary>
public const int Size = 64;
/// <summary>
/// A fixed size buffer holding the values.
/// See: <see>
/// <cref>https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/fixed-size-buffers</cref>
/// </see>
/// </summary>
private fixed short data[Size];
/// <summary>
/// Initializes a new instance of the <see cref="Block8x8"/> struct.
/// </summary>
/// <param name="coefficients">A <see cref="Span{T}"/> of coefficients</param>
public Block8x8(Span<short> coefficients)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
@ -25,6 +35,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short));
}
/// <summary>
/// Gets or sets a <see cref="short"/> value at the given index
/// </summary>
/// <param name="idx">The index</param>
/// <returns>The value</returns>
public short this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -44,6 +59,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
}
}
/// <summary>
/// Gets or sets a value in a row+coulumn of the 8x8 block
/// </summary>
/// <param name="x">The x position index in the row</param>
/// <param name="y">The column index</param>
/// <returns>The value</returns>
public short this[int x, int y]
{
get => this[(y * 8) + x];
@ -60,6 +81,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return !left.Equals(right);
}
/// <summary>
/// Multiply all elements by a given <see cref="int"/>
/// </summary>
public static Block8x8 operator *(Block8x8 block, int value)
{
Block8x8 result = block;
@ -73,6 +97,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result;
}
/// <summary>
/// Divide all elements by a given <see cref="int"/>
/// </summary>
public static Block8x8 operator /(Block8x8 block, int value)
{
Block8x8 result = block;
@ -86,6 +113,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result;
}
/// <summary>
/// Add an <see cref="int"/> to all elements
/// </summary>
public static Block8x8 operator +(Block8x8 block, int value)
{
Block8x8 result = block;
@ -99,6 +129,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result;
}
/// <summary>
/// Subtract an <see cref="int"/> from all elements
/// </summary>
public static Block8x8 operator -(Block8x8 block, int value)
{
Block8x8 result = block;
@ -142,6 +175,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
fp[idx] = value;
}
/// <summary>
/// Convert into <see cref="Block8x8F"/>
/// </summary>
public Block8x8F AsFloatBlock()
{
// TODO: Optimize this
@ -154,6 +190,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result;
}
/// <summary>
/// Copy all elements to an array of <see cref="short"/>.
/// </summary>
public short[] ToArray()
{
short[] result = new short[Size];
@ -161,6 +200,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result;
}
/// <summary>
/// Copy elements into 'destination' Span of <see cref="short"/> values
/// </summary>
public void CopyTo(Span<short> destination)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
@ -168,6 +210,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short));
}
/// <summary>
/// Copy elements into 'destination' Span of <see cref="int"/> values
/// </summary>
public void CopyTo(Span<int> destination)
{
for (int i = 0; i < Size; i++)
@ -176,6 +221,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
}
}
/// <summary>
/// Cast and copy <see cref="Size"/> <see cref="int"/>-s from the beginning of 'source' span.
/// </summary>
public void LoadFrom(Span<int> source)
{
for (int i = 0; i < Size; i++)
@ -191,6 +239,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx));
}
/// <inheritdoc />
public override string ToString()
{
var bld = new StringBuilder();
@ -208,6 +257,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return bld.ToString();
}
/// <inheritdoc />
public bool Equals(Block8x8 other)
{
for (int i = 0; i < Size; i++)
@ -221,6 +271,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return true;
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
@ -231,11 +282,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return obj is Block8x8 && this.Equals((Block8x8)obj);
}
/// <inheritdoc />
public override int GetHashCode()
{
return (this[0] * 31) + this[1];
}
/// <summary>
/// Calculate the total sum of absoulute differences of elements in 'a' and 'b'.
/// </summary>
public static long TotalDifference(ref Block8x8 a, ref Block8x8 b)
{
long result = 0;

3
src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs

@ -5,6 +5,9 @@
/// </summary>
internal static class ComponentUtils
{
/// <summary>
/// Gets a reference to the <see cref="Block8x8"/> at the given row and column index from <see cref="IJpegComponent.SpectralBlocks"/>
/// </summary>
public static ref Block8x8 GetBlockReference(this IJpegComponent component, int bx, int by)
{
return ref component.SpectralBlocks[bx, by];

43
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs

@ -7,17 +7,32 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{
/// <summary>
/// Encapsulates the conversion of Jpeg channels to RGBA values packed in <see cref="Vector4"/> buffer.
/// </summary>
internal abstract partial class JpegColorConverter
{
/// <summary>
/// The avalilable converters
/// </summary>
private static readonly JpegColorConverter[] Converters = { new FromYCbCr(), new FromYccK(), new FromCmyk(), new FromGrayScale(), new FromRgb() };
/// <summary>
/// Initializes a new instance of the <see cref="JpegColorConverter"/> class.
/// </summary>
protected JpegColorConverter(JpegColorSpace colorSpace)
{
this.ColorSpace = colorSpace;
}
/// <summary>
/// Gets the <see cref="JpegColorSpace"/> of this converter.
/// </summary>
public JpegColorSpace ColorSpace { get; }
/// <summary>
/// Returns the <see cref="JpegColorConverter"/> corresponding to the given <see cref="JpegColorSpace"/>
/// </summary>
public static JpegColorConverter GetConverter(JpegColorSpace colorSpace)
{
JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace);
@ -29,20 +44,48 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
return converter;
}
/// <summary>
/// He implementation of the conversion.
/// </summary>
/// <param name="values">The input as a stack-only <see cref="ComponentValues"/> struct</param>
/// <param name="result">The destination buffer of <see cref="Vector4"/> values</param>
public abstract void ConvertToRGBA(ComponentValues values, Span<Vector4> result);
/// <summary>
/// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s.
/// </summary>
public struct ComponentValues
{
/// <summary>
/// The component count
/// </summary>
public readonly int ComponentCount;
/// <summary>
/// The component 0 (eg. Y)
/// </summary>
public readonly ReadOnlySpan<float> Component0;
/// <summary>
/// The component 1 (eg. Cb)
/// </summary>
public readonly ReadOnlySpan<float> Component1;
/// <summary>
/// The component 2 (eg. Cr)
/// </summary>
public readonly ReadOnlySpan<float> Component2;
/// <summary>
/// The component 4
/// </summary>
public readonly ReadOnlySpan<float> Component3;
/// <summary>
/// Initializes a new instance of the <see cref="ComponentValues"/> struct.
/// </summary>
/// <param name="componentBuffers">The 1-4 sized list of component buffers.</param>
/// <param name="row">The row to convert</param>
public ComponentValues(IReadOnlyList<IBuffer2D<float>> componentBuffers, int row)
{
this.ComponentCount = componentBuffers.Count;

32
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs

@ -1,16 +1,27 @@
using System;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder;
using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{
/// <summary>
/// Encapsulates postprocessing data for one component for <see cref="JpegImagePostProcessor"/>.
/// </summary>
internal class JpegComponentPostProcessor : IDisposable
{
/// <summary>
/// Points to the current row in <see cref="Component"/>.
/// </summary>
private int currentComponentRowInBlocks;
/// <summary>
/// The size of the area in <see cref="ColorBuffer"/> corrsponding to one 8x8 Jpeg block
/// </summary>
private readonly Size blockAreaSize;
/// <summary>
/// Initializes a new instance of the <see cref="JpegComponentPostProcessor"/> class.
/// </summary>
public JpegComponentPostProcessor(JpegImagePostProcessor imagePostProcessor, IJpegComponent component)
{
this.Component = component;
@ -21,21 +32,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
this.blockAreaSize = this.Component.SubSamplingDivisors * 8;
}
/// <summary>
/// Gets the <see cref="JpegImagePostProcessor"/>
/// </summary>
public JpegImagePostProcessor ImagePostProcessor { get; }
/// <summary>
/// Gets the <see cref="Component"/>
/// </summary>
public IJpegComponent Component { get; }
/// <summary>
/// Gets the temporal working buffer of color values.
/// </summary>
public Buffer2D<float> ColorBuffer { get; }
/// <summary>
/// Gets <see cref="IJpegComponent.SizeInBlocks"/>
/// </summary>
public Size SizeInBlocks => this.Component.SizeInBlocks;
/// <summary>
/// Gets the maximal number of block rows being processed in one step.
/// </summary>
public int BlockRowsPerStep { get; }
/// <inheritdoc />
public void Dispose()
{
this.ColorBuffer.Dispose();
}
/// <summary>
/// Invoke <see cref="JpegBlockPostProcessor"/> for <see cref="BlockRowsPerStep"/> block rows, copy the result into <see cref="ColorBuffer"/>.
/// </summary>
public unsafe void CopyBlocksToColorBuffer()
{
var blockPp = default(JpegBlockPostProcessor);

17
src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs

@ -8,10 +8,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// </summary>
internal static class SizeExtensions
{
/// <summary>
/// Multiplies 'a.Width' with 'b.Width' and 'a.Height' with 'b.Height'.
/// TODO: Shouldn't we expose this as operator in SixLabors.Core?
/// </summary>
public static Size MultiplyBy(this Size a, Size b) => new Size(a.Width * b.Width, a.Height * b.Height);
/// <summary>
/// Divides 'a.Width' with 'b.Width' and 'a.Height' with 'b.Height'.
/// TODO: Shouldn't we expose this as operator in SixLabors.Core?
/// </summary>
public static Size DivideBy(this Size a, Size b) => new Size(a.Width / b.Width, a.Height / b.Height);
/// <summary>
/// Divide Width and Height as real numbers and return the Ceiling.
/// </summary>
public static Size DivideRoundUp(this Size originalSize, int divX, int divY)
{
var sizeVect = (Vector2)(SizeF)originalSize;
@ -22,9 +33,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return new Size((int)sizeVect.X, (int)sizeVect.Y);
}
/// <summary>
/// Divide Width and Height as real numbers and return the Ceiling.
/// </summary>
public static Size DivideRoundUp(this Size originalSize, int divisor) =>
DivideRoundUp(originalSize, divisor, divisor);
/// <summary>
/// Divide Width and Height as real numbers and return the Ceiling.
/// </summary>
public static Size DivideRoundUp(this Size originalSize, Size divisor) =>
DivideRoundUp(originalSize, divisor.Width, divisor.Height);
}

0
src/ImageSharp/Memory/Buffer2D.cs → src/ImageSharp/Memory/Buffer2D{T}.cs

40
src/ImageSharp/Memory/BufferArea.cs → src/ImageSharp/Memory/BufferArea{T}.cs

@ -12,6 +12,9 @@ namespace SixLabors.ImageSharp.Memory
internal struct BufferArea<T>
where T : struct
{
/// <summary>
/// The rectangle specifying the boundaries of the area in <see cref="DestinationBuffer"/>.
/// </summary>
public readonly Rectangle Rectangle;
public BufferArea(IBuffer2D<T> destinationBuffer, Rectangle rectangle)
@ -30,17 +33,41 @@ namespace SixLabors.ImageSharp.Memory
{
}
/// <summary>
/// Gets the <see cref="IBuffer2D{T}"/> being pointed by this instance.
/// </summary>
public IBuffer2D<T> DestinationBuffer { get; }
/// <summary>
/// Gets the size of the area.
/// </summary>
public Size Size => this.Rectangle.Size;
/// <summary>
/// Gets the pixel stride which is equal to the width of <see cref="DestinationBuffer"/>.
/// </summary>
public int Stride => this.DestinationBuffer.Width;
/// <summary>
/// Gets or sets a value at the given index.
/// </summary>
/// <param name="x">The position inside a row</param>
/// <param name="y">The row index</param>
/// <returns>The reference to the value</returns>
public ref T this[int x, int y] => ref this.DestinationBuffer.Span[this.GetIndexOf(x, y)];
/// <summary>
/// Gets a reference to the [0,0] element.
/// </summary>
/// <returns>The reference to the [0,0] element</returns>
public ref T GetReferenceToOrigo() =>
ref this.DestinationBuffer.Span[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X];
/// <summary>
/// Gets a span to row 'y' inside this area.
/// </summary>
/// <param name="y">The row index</param>
/// <returns>The span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> GetRowSpan(int y)
{
@ -51,6 +78,14 @@ namespace SixLabors.ImageSharp.Memory
return this.DestinationBuffer.Span.Slice(yy + xx, width);
}
/// <summary>
/// Returns a sub-area as <see cref="BufferArea{T}"/>. (Similar to <see cref="Span{T}.Slice(int, int)"/>.)
/// </summary>
/// <param name="x">The x index at the subarea origo</param>
/// <param name="y">The y index at the subarea origo</param>
/// <param name="width">The desired width of the subarea</param>
/// <param name="height">The desired height of the subarea</param>
/// <returns>The subarea</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferArea<T> GetSubArea(int x, int y, int width, int height)
{
@ -58,6 +93,11 @@ namespace SixLabors.ImageSharp.Memory
return this.GetSubArea(rectangle);
}
/// <summary>
/// Returns a sub-area as <see cref="BufferArea{T}"/>. (Similar to <see cref="Span{T}.Slice(int, int)"/>.)
/// </summary>
/// <param name="rectangle">The <see cref="Rectangle"/> specifying the boundaries of the subarea</param>
/// <returns>The subarea</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferArea<T> GetSubArea(Rectangle rectangle)
{

0
src/ImageSharp/Memory/Buffer.cs → src/ImageSharp/Memory/Buffer{T}.cs

0
src/ImageSharp/Memory/IBuffer2D.cs → src/ImageSharp/Memory/IBuffer2D{T}.cs

2
src/ImageSharp/Memory/IBuffer.cs → src/ImageSharp/Memory/IBuffer{T}.cs

@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Memory
{
/// <inheritdoc />
/// <summary>
/// Represents a contigous memory buffer of value-type items "promising" a <see cref="T:System.Span`1" />
/// Represents a contigous memory buffer of value-type items "promising" a <see cref="Span{T}"/>
/// </summary>
/// <typeparam name="T">The value type</typeparam>
internal interface IBuffer<T> : IDisposable

2
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs

@ -41,6 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
// TODO: This test occasionally fails. Don't get the reason, BufferArea.CopyTo() is totally OK.
[Fact]
public void Unscaled()
{
@ -61,6 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
// TODO: This test occasionally fails. Don't get the reason, BufferArea.CopyTo() is totally OK.
[Theory]
[InlineData(1, 1)]
[InlineData(1, 2)]

3
tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs

@ -13,6 +13,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
internal static partial class LibJpegTools
{
/// <summary>
/// Stores spectral blocks for jpeg components.
/// </summary>
public class ComponentData : IEquatable<ComponentData>, IJpegComponent
{
public ComponentData(int widthInBlocks, int heightInBlocks, int index)

3
tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs

@ -12,6 +12,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
internal static partial class LibJpegTools
{
/// <summary>
/// Stores spectral jpeg compoent data in libjpeg-compatible style.
/// </summary>
public class SpectralData : IEquatable<SpectralData>
{
public int ComponentCount { get; private set; }

20
tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs

@ -8,6 +8,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
using SixLabors.ImageSharp.Formats.Jpeg.Common;
/// <summary>
/// Utilities to read raw libjpeg data for reference conversion.
/// </summary>
internal static partial class LibJpegTools
{
public static (double total, double average) CalculateDifference(ComponentData expected, ComponentData actual)
@ -47,13 +50,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
TestEnvironment.ToolsDirectoryFullPath,
@"jpeg\dump-jpeg-coeffs.exe");
/// <summary>
/// Executes 'dump-jpeg-coeffs.exe' for the given jpeg image file, saving the libjpeg spectral data into 'destFile'. Windows only!
/// See:
/// <see>
/// <cref>https://github.com/SixLabors/Imagesharp.Tests.Images/blob/master/tools/jpeg/README.md</cref>
/// </see>
/// </summary>
public static void RunDumpJpegCoeffsTool(string sourceFile, string destFile)
{
if (!TestEnvironment.IsWindows)
{
throw new InvalidOperationException("Can't run dump-jpeg-coeffs.exe in non-Windows environment. Skip this test on Linux/Unix!");
}
string args = $@"""{sourceFile}"" ""{destFile}""";
var process = Process.Start(DumpToolFullPath, args);
process.WaitForExit();
}
/// <summary>
/// Extract libjpeg <see cref="SpectralData"/> from the given jpg file with 'dump-jpeg-coeffs.exe'. Windows only!
/// See:
/// https://github.com/SixLabors/Imagesharp.Tests.Images/blob/master/tools/jpeg/README.md
/// </summary>
public static SpectralData ExtractSpectralData(string inputFile)
{
TestFile testFile = TestFile.Create(inputFile);

3
tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs

@ -71,6 +71,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
return r;
}
#pragma warning disable 219
/// <summary>
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L200
/// </summary>
@ -82,6 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
float z0, z1, z2, z3, z4;
// see: PrintConstants()
float r0 = 1.41421354f;
float r1 = 1.3870399f;
float r2 = 1.306563f;

6
tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs

@ -8,6 +8,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
internal static partial class ReferenceImplementations
{
/// <summary>
/// TODO: produces really bad results for bigger values!
///
/// Contains the "original" golang based DCT/IDCT implementations as reference implementations.
/// 1. ===== Forward DCT =====
/// **** The original golang source claims:
@ -76,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
return result;
}
[Obsolete("Looks like this method produces really bad results for bigger values!")]
// [Obsolete("Looks like this method produces really bad results for bigger values!")]
public static Block8x8 TransformIDCT(ref Block8x8 block)
{
int[] temp = new int[Block8x8.Size];
@ -233,7 +235,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
/// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
/// </summary>
/// <param name="src">The source block of coefficients</param>
[Obsolete("Looks like this method produces really bad results for bigger values!")]
// [Obsolete("Looks like this method produces really bad results for bigger values!")]
public static void TransformIDCTInplace(Span<int> src)
{
// Horizontal 1-D IDCT.

Loading…
Cancel
Save