Browse Source

additional Block8x8F cleanup

af/merge-core
Anton Firszov 9 years ago
parent
commit
97dc26c01a
  1. 4
      src/ImageSharp/Common/Extensions/SimdUtils.cs
  2. 82
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  3. 88
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.cs
  4. 40
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.tt
  5. 74
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  6. 12
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs
  7. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
  8. 32
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  9. 4
      tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs
  10. 4
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs

4
src/ImageSharp/Common/Extensions/SimdUtils.cs

@ -15,9 +15,9 @@ namespace SixLabors.ImageSharp
internal static class SimdUtils
{
/// <summary>
/// Indicates AVX2 architecture where both float and integer registers are of size 256 byte.
/// Gets a value indicating whether the code is being executed on AVX2 CPU where both float and integer registers are of size 256 byte.
/// </summary>
public static readonly bool IsAvx2CompatibleArchitecture = Vector<float>.Count == 8 && Vector<int>.Count == 8;
public static bool IsAvx2CompatibleArchitecture => Vector<float>.Count == 8 && Vector<int>.Count == 8;
internal static void GuardAvx2(string operation)
{

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

@ -181,7 +181,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public Block8x8F AsFloatBlock()
{
var result = default(Block8x8F);
this.CopyToFloatBlock(ref result);
result.LoadFrom(ref this);
return result;
}
@ -297,85 +297,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result;
}
/// <summary>
/// Convert values into <see cref="Block8x8F"/> as <see cref="float"/>-s
/// </summary>
public void CopyToFloatBlock(ref Block8x8F dest)
{
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref this);
dest.V0L.X = Unsafe.Add(ref selfRef, 0);
dest.V0L.Y = Unsafe.Add(ref selfRef, 1);
dest.V0L.Z = Unsafe.Add(ref selfRef, 2);
dest.V0L.W = Unsafe.Add(ref selfRef, 3);
dest.V0R.X = Unsafe.Add(ref selfRef, 4);
dest.V0R.Y = Unsafe.Add(ref selfRef, 5);
dest.V0R.Z = Unsafe.Add(ref selfRef, 6);
dest.V0R.W = Unsafe.Add(ref selfRef, 7);
dest.V1L.X = Unsafe.Add(ref selfRef, 8);
dest.V1L.Y = Unsafe.Add(ref selfRef, 9);
dest.V1L.Z = Unsafe.Add(ref selfRef, 10);
dest.V1L.W = Unsafe.Add(ref selfRef, 11);
dest.V1R.X = Unsafe.Add(ref selfRef, 12);
dest.V1R.Y = Unsafe.Add(ref selfRef, 13);
dest.V1R.Z = Unsafe.Add(ref selfRef, 14);
dest.V1R.W = Unsafe.Add(ref selfRef, 15);
dest.V2L.X = Unsafe.Add(ref selfRef, 16);
dest.V2L.Y = Unsafe.Add(ref selfRef, 17);
dest.V2L.Z = Unsafe.Add(ref selfRef, 18);
dest.V2L.W = Unsafe.Add(ref selfRef, 19);
dest.V2R.X = Unsafe.Add(ref selfRef, 20);
dest.V2R.Y = Unsafe.Add(ref selfRef, 21);
dest.V2R.Z = Unsafe.Add(ref selfRef, 22);
dest.V2R.W = Unsafe.Add(ref selfRef, 23);
dest.V3L.X = Unsafe.Add(ref selfRef, 24);
dest.V3L.Y = Unsafe.Add(ref selfRef, 25);
dest.V3L.Z = Unsafe.Add(ref selfRef, 26);
dest.V3L.W = Unsafe.Add(ref selfRef, 27);
dest.V3R.X = Unsafe.Add(ref selfRef, 28);
dest.V3R.Y = Unsafe.Add(ref selfRef, 29);
dest.V3R.Z = Unsafe.Add(ref selfRef, 30);
dest.V3R.W = Unsafe.Add(ref selfRef, 31);
dest.V4L.X = Unsafe.Add(ref selfRef, 32);
dest.V4L.Y = Unsafe.Add(ref selfRef, 33);
dest.V4L.Z = Unsafe.Add(ref selfRef, 34);
dest.V4L.W = Unsafe.Add(ref selfRef, 35);
dest.V4R.X = Unsafe.Add(ref selfRef, 36);
dest.V4R.Y = Unsafe.Add(ref selfRef, 37);
dest.V4R.Z = Unsafe.Add(ref selfRef, 38);
dest.V4R.W = Unsafe.Add(ref selfRef, 39);
dest.V5L.X = Unsafe.Add(ref selfRef, 40);
dest.V5L.Y = Unsafe.Add(ref selfRef, 41);
dest.V5L.Z = Unsafe.Add(ref selfRef, 42);
dest.V5L.W = Unsafe.Add(ref selfRef, 43);
dest.V5R.X = Unsafe.Add(ref selfRef, 44);
dest.V5R.Y = Unsafe.Add(ref selfRef, 45);
dest.V5R.Z = Unsafe.Add(ref selfRef, 46);
dest.V5R.W = Unsafe.Add(ref selfRef, 47);
dest.V6L.X = Unsafe.Add(ref selfRef, 48);
dest.V6L.Y = Unsafe.Add(ref selfRef, 49);
dest.V6L.Z = Unsafe.Add(ref selfRef, 50);
dest.V6L.W = Unsafe.Add(ref selfRef, 51);
dest.V6R.X = Unsafe.Add(ref selfRef, 52);
dest.V6R.Y = Unsafe.Add(ref selfRef, 53);
dest.V6R.Z = Unsafe.Add(ref selfRef, 54);
dest.V6R.W = Unsafe.Add(ref selfRef, 55);
dest.V7L.X = Unsafe.Add(ref selfRef, 56);
dest.V7L.Y = Unsafe.Add(ref selfRef, 57);
dest.V7L.Z = Unsafe.Add(ref selfRef, 58);
dest.V7L.W = Unsafe.Add(ref selfRef, 59);
dest.V7R.X = Unsafe.Add(ref selfRef, 60);
dest.V7R.Y = Unsafe.Add(ref selfRef, 61);
dest.V7R.Z = Unsafe.Add(ref selfRef, 62);
dest.V7R.W = Unsafe.Add(ref selfRef, 63);
}
}
}

88
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.cs

@ -96,8 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <summary>
/// Level shift by +128, clip to [0, 255]
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void NormalizeColorsInplace()
public void NormalizeColorsInplace()
{
this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4);
this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4);
@ -117,11 +116,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
this.V7R = Vector4.Clamp(this.V7R + COff4, CMin4, CMax4);
}
/// <summary>
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void NormalizeColorsAndRoundInplaceAvx2()
{
Vector<float> off = new Vector<float>(128f);
Vector<float> max = new Vector<float>(255F);
ref Vector<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref this.V0L);
row0 = NormalizeAndRound(row0, off, max);
@ -148,5 +150,85 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
row7 = NormalizeAndRound(row7, off, max);
}
/// <summary>
/// Fill the block from 'source' doing short -> float conversion.
/// </summary>
public void LoadFrom(ref Block8x8 source)
{
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref source);
this.V0L.X = Unsafe.Add(ref selfRef, 0);
this.V0L.Y = Unsafe.Add(ref selfRef, 1);
this.V0L.Z = Unsafe.Add(ref selfRef, 2);
this.V0L.W = Unsafe.Add(ref selfRef, 3);
this.V0R.X = Unsafe.Add(ref selfRef, 4);
this.V0R.Y = Unsafe.Add(ref selfRef, 5);
this.V0R.Z = Unsafe.Add(ref selfRef, 6);
this.V0R.W = Unsafe.Add(ref selfRef, 7);
this.V1L.X = Unsafe.Add(ref selfRef, 8);
this.V1L.Y = Unsafe.Add(ref selfRef, 9);
this.V1L.Z = Unsafe.Add(ref selfRef, 10);
this.V1L.W = Unsafe.Add(ref selfRef, 11);
this.V1R.X = Unsafe.Add(ref selfRef, 12);
this.V1R.Y = Unsafe.Add(ref selfRef, 13);
this.V1R.Z = Unsafe.Add(ref selfRef, 14);
this.V1R.W = Unsafe.Add(ref selfRef, 15);
this.V2L.X = Unsafe.Add(ref selfRef, 16);
this.V2L.Y = Unsafe.Add(ref selfRef, 17);
this.V2L.Z = Unsafe.Add(ref selfRef, 18);
this.V2L.W = Unsafe.Add(ref selfRef, 19);
this.V2R.X = Unsafe.Add(ref selfRef, 20);
this.V2R.Y = Unsafe.Add(ref selfRef, 21);
this.V2R.Z = Unsafe.Add(ref selfRef, 22);
this.V2R.W = Unsafe.Add(ref selfRef, 23);
this.V3L.X = Unsafe.Add(ref selfRef, 24);
this.V3L.Y = Unsafe.Add(ref selfRef, 25);
this.V3L.Z = Unsafe.Add(ref selfRef, 26);
this.V3L.W = Unsafe.Add(ref selfRef, 27);
this.V3R.X = Unsafe.Add(ref selfRef, 28);
this.V3R.Y = Unsafe.Add(ref selfRef, 29);
this.V3R.Z = Unsafe.Add(ref selfRef, 30);
this.V3R.W = Unsafe.Add(ref selfRef, 31);
this.V4L.X = Unsafe.Add(ref selfRef, 32);
this.V4L.Y = Unsafe.Add(ref selfRef, 33);
this.V4L.Z = Unsafe.Add(ref selfRef, 34);
this.V4L.W = Unsafe.Add(ref selfRef, 35);
this.V4R.X = Unsafe.Add(ref selfRef, 36);
this.V4R.Y = Unsafe.Add(ref selfRef, 37);
this.V4R.Z = Unsafe.Add(ref selfRef, 38);
this.V4R.W = Unsafe.Add(ref selfRef, 39);
this.V5L.X = Unsafe.Add(ref selfRef, 40);
this.V5L.Y = Unsafe.Add(ref selfRef, 41);
this.V5L.Z = Unsafe.Add(ref selfRef, 42);
this.V5L.W = Unsafe.Add(ref selfRef, 43);
this.V5R.X = Unsafe.Add(ref selfRef, 44);
this.V5R.Y = Unsafe.Add(ref selfRef, 45);
this.V5R.Z = Unsafe.Add(ref selfRef, 46);
this.V5R.W = Unsafe.Add(ref selfRef, 47);
this.V6L.X = Unsafe.Add(ref selfRef, 48);
this.V6L.Y = Unsafe.Add(ref selfRef, 49);
this.V6L.Z = Unsafe.Add(ref selfRef, 50);
this.V6L.W = Unsafe.Add(ref selfRef, 51);
this.V6R.X = Unsafe.Add(ref selfRef, 52);
this.V6R.Y = Unsafe.Add(ref selfRef, 53);
this.V6R.Z = Unsafe.Add(ref selfRef, 54);
this.V6R.W = Unsafe.Add(ref selfRef, 55);
this.V7L.X = Unsafe.Add(ref selfRef, 56);
this.V7L.Y = Unsafe.Add(ref selfRef, 57);
this.V7L.Z = Unsafe.Add(ref selfRef, 58);
this.V7L.W = Unsafe.Add(ref selfRef, 59);
this.V7R.X = Unsafe.Add(ref selfRef, 60);
this.V7R.Y = Unsafe.Add(ref selfRef, 61);
this.V7R.Z = Unsafe.Add(ref selfRef, 62);
this.V7R.W = Unsafe.Add(ref selfRef, 63);
}
}
}

40
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.tt

@ -61,8 +61,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <summary>
/// Level shift by +128, clip to [0, 255]
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void NormalizeColorsInplace()
public void NormalizeColorsInplace()
{
<#
@ -80,11 +79,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
#>
}
/// <summary>
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void NormalizeColorsAndRoundInplaceAvx2()
{
Vector<float> off = new Vector<float>(128f);
Vector<float> max = new Vector<float>(255F);
<#
for (int i = 0; i < 8; i++)
@ -98,5 +100,37 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
#>
}
/// <summary>
/// Fill the block from 'source' doing short -> float conversion.
/// </summary>
public void LoadFrom(ref Block8x8 source)
{
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref source);
<#
PushIndent(" ");
for (int j = 0; j < 8; j++)
{
for (int i = 0; i < 8; i++)
{
char destCoord = coordz[i % 4];
char destSide = (i / 4) % 2 == 0 ? 'L' : 'R';
if(j > 0 && i == 0){
WriteLine("");
}
char srcCoord = coordz[j % 4];
char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R';
string expression = $"this.V{j}{destSide}.{destCoord} = Unsafe.Add(ref selfRef, {j*8+i});\r\n";
Write(expression);
}
}
PopIndent();
#>
}
}
}

74
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -16,16 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// </summary>
internal partial struct Block8x8F
{
// Most of the static methods of this struct are instance methods by actual semantics: they use Block8x8F* as their first parameter.
// Example: GetScalarAt() and SetScalarAt() are really just other (optimized) versions of the indexer.
// It's much cleaner, easier and safer to work with the code, if the methods with same semantics are next to each other.
#pragma warning disable SA1204 // StaticElementsMustAppearBeforeInstanceElements
/// <summary>
/// Vector count
/// </summary>
public const int VectorCount = 16;
/// <summary>
/// A number of scalar coefficients in a <see cref="Block8x8F"/>
/// </summary>
@ -156,36 +146,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result;
}
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="idx">Index</param>
/// <returns>The scaleVec value at the specified index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx)
{
GuardBlockIndex(idx);
float* fp = (float*)blockPtr;
return fp[idx];
}
/// <summary>
/// Pointer-based "Indexer" (setter part)
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="idx">Index</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value)
{
GuardBlockIndex(idx);
float* fp = (float*)blockPtr;
fp[idx] = value;
}
/// <summary>
/// Fill the block with defaults (zeroes)
/// </summary>
@ -409,6 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
}
/// <summary>
/// Quantize 'block' into 'dest' using the 'qt' quantization table:
/// Unzig the elements of block into dest, while dividing them by elements of qt and "pre-rounding" the values.
/// To finish the rounding it's enough to (int)-cast these values.
/// </summary>
@ -416,7 +377,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <param name="dest">Destination block</param>
/// <param name="qt">The quantization table</param>
/// <param name="unzigPtr">Pointer to elements of <see cref="ZigZag"/></param>
public static unsafe void UnzigDivRound(
public static unsafe void Quantize(
Block8x8F* block,
Block8x8F* dest,
Block8x8F* qt,
@ -505,15 +466,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector<float> NormalizeAndRound(Vector<float> row, Vector<float> off, Vector<float> max)
/// <summary>
/// Level shift by +128, clip to [0..255], and round all the values in the block.
/// </summary>
public void NormalizeColorsAndRoundInplace()
{
row += off;
row = Vector.Max(row, Vector<float>.Zero);
row = Vector.Min(row, max);
return row.FastRound();
if (SimdUtils.IsAvx2CompatibleArchitecture)
{
this.NormalizeColorsAndRoundInplaceAvx2();
}
else
{
this.NormalizeColorsInplace();
this.RoundInplace();
}
}
/// <summary>
/// Rounds all values in the block.
/// </summary>
public void RoundInplace()
{
for (int i = 0; i < Size; i++)
@ -540,6 +511,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return bld.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector<float> NormalizeAndRound(Vector<float> row, Vector<float> off, Vector<float> max)
{
row += off;
row = Vector.Max(row, Vector<float>.Zero);
row = Vector.Min(row, max);
return row.FastRound();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{

12
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs

@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
BufferArea<float> destArea)
{
ref Block8x8F b = ref this.SourceBlock;
sourceBlock.CopyToFloatBlock(ref b);
b.LoadFrom(ref sourceBlock);
// Dequantize:
b.MultiplyInplace(ref this.DequantiazationTable);
@ -74,15 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
// To conform better to libjpeg we actually NEED TO loose precision here.
// This is because they store blocks as Int16 between all the operations.
// To be "more accurate", we need to emulate this by rounding!
if (SimdUtils.IsAvx2CompatibleArchitecture)
{
this.WorkspaceBlock1.NormalizeColorsAndRoundInplaceAvx2();
}
else
{
this.WorkspaceBlock1.NormalizeColorsInplace();
this.WorkspaceBlock1.RoundInplace();
}
this.WorkspaceBlock1.NormalizeColorsAndRoundInplace();
this.WorkspaceBlock1.CopyTo(destArea, this.subSamplingDivisors.Width, this.subSamplingDivisors.Height);
}

2
src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs

@ -564,7 +564,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
FastFloatingPointDCT.TransformFDCT(ref *src, ref *tempDest1, ref *tempDest2);
Block8x8F.UnzigDivRound(tempDest1, tempDest2, quant, unzigPtr);
Block8x8F.Quantize(tempDest1, tempDest2, quant, unzigPtr);
float* unziggedDestPtr = (float*)tempDest2;
int dc = (int)unziggedDestPtr[0];

32
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -65,31 +65,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
});
Assert.Equal(sum, 64f * 63f * 0.5f);
}
[Fact]
public unsafe void Indexer_GetScalarAt_SetScalarAt()
{
float sum = 0;
this.Measure(
Times,
() =>
{
var block = new Block8x8F();
for (int i = 0; i < Block8x8F.Size; i++)
{
Block8x8F.SetScalarAt(&block, i, i);
}
sum = 0;
for (int i = 0; i < Block8x8F.Size; i++)
{
sum += Block8x8F.GetScalarAt(&block, i);
}
});
Assert.Equal(sum, 64f * 63f * 0.5f);
}
[Fact]
public void Indexer_ReferenceBenchmarkWithArray()
{
@ -296,7 +272,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Theory]
[InlineData(1)]
[InlineData(2)]
public unsafe void UnzigDivRound(int seed)
public unsafe void Quantize(int seed)
{
var block = new Block8x8F();
block.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed));
@ -307,11 +283,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var unzig = ZigZag.CreateUnzigTable();
int* expectedResults = stackalloc int[Block8x8F.Size];
ReferenceImplementations.UnZigDivRoundRational(&block, expectedResults, &qt, unzig.Data);
ReferenceImplementations.QuantizeRational(&block, expectedResults, &qt, unzig.Data);
var actualResults = default(Block8x8F);
Block8x8F.UnzigDivRound(&block, &actualResults, &qt, unzig.Data);
Block8x8F.Quantize(&block, &actualResults, &qt, unzig.Data);
for (int i = 0; i < Block8x8F.Size; i++)
{

4
tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs

@ -34,8 +34,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Baseline.Jpeg444,
};
// [Theory] // Benchmark, enable manually
// [MemberData(nameof(DecodeJpegData))]
//[Theory] // Benchmark, enable manually
//[MemberData(nameof(DecodeJpegData))]
public void DecodeJpeg_Original(string fileName)
{
this.DecodeJpegBenchmarkImpl(fileName, new OrigJpegDecoder());

4
tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs

@ -108,14 +108,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
}
/// <summary>
/// Reference implementation to test <see cref="Block8x8F.UnzigDivRound"/>.
/// Reference implementation to test <see cref="Block8x8F.Quantize"/>.
/// Rounding is done used an integer-based algorithm defined in <see cref="RationalRound(int,int)"/>.
/// </summary>
/// <param name="src">The input block</param>
/// <param name="dest">The destination block of integers</param>
/// <param name="qt">The quantization table</param>
/// <param name="unzigPtr">Pointer to <see cref="ZigZag.Data"/> </param>
public static unsafe void UnZigDivRoundRational(Block8x8F* src, int* dest, Block8x8F* qt, int* unzigPtr)
public static unsafe void QuantizeRational(Block8x8F* src, int* dest, Block8x8F* qt, int* unzigPtr)
{
float* s = (float*)src;
float* q = (float*)qt;

Loading…
Cancel
Save