diff --git a/src/ImageSharp46/Formats/Jpg/Components/Block.cs b/src/ImageSharp46/Formats/Jpg/Components/Block.cs index f41e615b5a..d9b16a44ed 100644 --- a/src/ImageSharp46/Formats/Jpg/Components/Block.cs +++ b/src/ImageSharp46/Formats/Jpg/Components/Block.cs @@ -12,9 +12,9 @@ namespace ImageSharp.Formats /// /// Represents an 8x8 block of coefficients to transform and encode. /// - public struct Block : IDisposable + internal struct Block : IDisposable { - private static ArrayPool IntArrayPool = ArrayPool.Create(BlockSize, 50); + private static readonly ArrayPool ArrayPool = ArrayPool.Create(BlockSize, 50); /// /// Gets the size of the block. @@ -37,7 +37,7 @@ namespace ImageSharp.Formats public void Init() { //this.Data = new int[BlockSize]; - this.Data = IntArrayPool.Rent(BlockSize); + this.Data = ArrayPool.Rent(BlockSize); } public static Block Create() @@ -79,7 +79,7 @@ namespace ImageSharp.Formats { if (Data != null) { - IntArrayPool.Return(Data, true); + ArrayPool.Return(Data, true); Data = null; } } @@ -108,4 +108,103 @@ namespace ImageSharp.Formats return clone; } } + + internal struct BlockF : IDisposable + { + private static readonly ArrayPool ArrayPool = ArrayPool.Create(BlockSize, 50); + + /// + /// Gets the size of the block. + /// + public const int BlockSize = 64; + + /// + /// The array of block data. + /// + public float[] Data; + + /// + /// Initializes a new instance of the class. + /// + //public Block() + //{ + // this.data = new int[BlockSize]; + //} + + public void Init() + { + //this.Data = new int[BlockSize]; + this.Data = ArrayPool.Rent(BlockSize); + } + + public static BlockF Create() + { + var block = new BlockF(); + block.Init(); + return block; + } + + public static BlockF[] CreateArray(int size) + { + BlockF[] result = new BlockF[size]; + for (int i = 0; i < result.Length; i++) + { + result[i].Init(); + } + return result; + } + + public bool IsInitialized => this.Data != null; + + /// + /// Gets the pixel data at the given block index. + /// + /// The index of the data to return. + /// + /// The . + /// + public float this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return this.Data[index]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set { this.Data[index] = value; } + } + + // TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement! + public void Dispose() + { + if (Data != null) + { + ArrayPool.Return(Data, true); + Data = null; + } + } + + public static void DisposeAll(BlockF[] blocks) + { + for (int i = 0; i < blocks.Length; i++) + { + blocks[i].Dispose(); + } + } + + + public void Clear() + { + for (int i = 0; i < Data.Length; i++) + { + Data[i] = 0; + } + } + + public BlockF Clone() + { + BlockF clone = Create(); + Array.Copy(Data, clone.Data, BlockSize); + return clone; + } + } + + } diff --git a/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs index 123b2efe56..4e7bef5368 100644 --- a/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs +++ b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs @@ -189,6 +189,15 @@ namespace ImageSharp.Formats dest.MultiplyAllInplace(_0_125); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IDCTInplace() + { + Block8x8 result = new Block8x8(); + Block8x8 temp = new Block8x8(); + IDCTInto(ref result, ref temp); + this = result; + } + private static readonly Vector4 _1_175876 = new Vector4(1.175876f); private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); @@ -416,8 +425,8 @@ namespace ImageSharp.Formats for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } */ } - - public static void SuchIDCT(ref Block block) + + internal static void SuchIDCT(ref Block block) { Block8x8 source = new Block8x8(); source.LoadFrom(block.Data); @@ -429,6 +438,18 @@ namespace ImageSharp.Formats dest.CopyTo(block.Data); } + internal static void SuchIDCT(ref BlockF block) + { + Block8x8 source = new Block8x8(); + source.LoadFrom(block.Data); + + Block8x8 dest = new Block8x8(); + Block8x8 temp = new Block8x8(); + + source.IDCTInto(ref dest, ref temp); + dest.CopyTo(block.Data); + } + public unsafe float this[int idx] { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp46/Formats/Jpg/Components/ReferenceDCT.cs b/src/ImageSharp46/Formats/Jpg/Components/ReferenceDCT.cs index a83ec5d4c1..595a3e54a7 100644 --- a/src/ImageSharp46/Formats/Jpg/Components/ReferenceDCT.cs +++ b/src/ImageSharp46/Formats/Jpg/Components/ReferenceDCT.cs @@ -215,7 +215,7 @@ namespace ImageSharp.Formats } } - public static void IDCT(ref Block block) + internal static void IDCT(ref Block block) { Span src = Span.RentFromPool(64); diff --git a/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs index 25e0a0d1b3..e66d42d80c 100644 --- a/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs @@ -84,7 +84,7 @@ namespace ImageSharp.Formats /// /// Saved state between progressive-mode scans. /// - private readonly Block[][] progCoeffs; + private readonly BlockF[][] progCoeffs; /// /// The huffman trees @@ -96,7 +96,7 @@ namespace ImageSharp.Formats /// /// Quantization tables, in zigzag order. /// - private readonly Block[] quantizationTables; + private readonly BlockF[] quantizationTables; /// /// A temporary buffer for holding pixels @@ -201,12 +201,12 @@ namespace ImageSharp.Formats public JpegDecoderCore() { //this.huffmanTrees = new Huffman[MaxTc + 1, MaxTh + 1]; - this.huffmanTrees = new Huffman[(MaxTc + 1)*(MaxTh + 1)]; + this.huffmanTrees = new Huffman[(MaxTc + 1) * (MaxTh + 1)]; - this.quantizationTables = Block.CreateArray(MaxTq + 1); - this.temp = new byte[2 * Block.BlockSize]; + this.quantizationTables = BlockF.CreateArray(MaxTq + 1); + this.temp = new byte[2 * BlockF.BlockSize]; this.componentArray = new Component[MaxComponents]; - this.progCoeffs = new Block[MaxComponents][]; + this.progCoeffs = new BlockF[MaxComponents][]; this.bits = new Bits(); this.bytes = new Bytes(); @@ -217,7 +217,7 @@ namespace ImageSharp.Formats for (int j = 0; j < MaxTh + 1; j++) { //this.huffmanTrees[i, j].Init(LutSize, MaxNCodes, MaxCodeLength); - this.huffmanTrees[i* ThRowSize + j].Init(LutSize, MaxNCodes, MaxCodeLength); + this.huffmanTrees[i * ThRowSize + j].Init(LutSize, MaxNCodes, MaxCodeLength); } } @@ -515,7 +515,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Bad Th value"); } - ProcessDefineHuffmanTablesMarkerLoop(ref this.huffmanTrees[tc* ThRowSize + th], ref remaining); + ProcessDefineHuffmanTablesMarkerLoop(ref this.huffmanTrees[tc * ThRowSize + th], ref remaining); } } @@ -571,8 +571,8 @@ namespace ImageSharp.Formats // whose codeLength's high bits matches code. // The high 8 bits of lutValue are the encoded value. // The low 8 bits are 1 plus the codeLength. - byte base2 = (byte) (code << (7 - i)); - ushort lutValue = (ushort) ((huffman.Values[x] << 8) | (2 + i)); + byte base2 = (byte)(code << (7 - i)); + ushort lutValue = (ushort)((huffman.Values[x] << 8) | (2 + i)); for (int k = 0; k < 1 << (7 - i); k++) { @@ -1117,32 +1117,32 @@ namespace ImageSharp.Formats switch (x >> 4) { case 0: - if (remaining < Block.BlockSize) + if (remaining < BlockF.BlockSize) { done = true; break; } - remaining -= Block.BlockSize; - this.ReadFull(this.temp, 0, Block.BlockSize); + remaining -= BlockF.BlockSize; + this.ReadFull(this.temp, 0, BlockF.BlockSize); - for (int i = 0; i < Block.BlockSize; i++) + for (int i = 0; i < BlockF.BlockSize; i++) { this.quantizationTables[tq][i] = this.temp[i]; } break; case 1: - if (remaining < 2 * Block.BlockSize) + if (remaining < 2 * BlockF.BlockSize) { done = true; break; } - remaining -= 2 * Block.BlockSize; - this.ReadFull(this.temp, 0, 2 * Block.BlockSize); + remaining -= 2 * BlockF.BlockSize; + this.ReadFull(this.temp, 0, 2 * BlockF.BlockSize); - for (int i = 0; i < Block.BlockSize; i++) + for (int i = 0; i < BlockF.BlockSize; i++) { this.quantizationTables[tq][i] = (this.temp[2 * i] << 8) | this.temp[(2 * i) + 1]; } @@ -1471,7 +1471,7 @@ namespace ImageSharp.Formats } } - private Block scanWorkerBlock = Block.Create(); + private BlockF scanWorkerBlock = BlockF.Create(); /// /// Processes the SOS (Start of scan marker). @@ -1535,7 +1535,7 @@ namespace ImageSharp.Formats // significant bit. // For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. int zigStart = 0; - int zigEnd = Block.BlockSize - 1; + int zigEnd = BlockF.BlockSize - 1; int ah = 0; int al = 0; @@ -1546,7 +1546,7 @@ namespace ImageSharp.Formats ah = this.temp[3 + scanComponentCountX2] >> 4; al = this.temp[3 + scanComponentCountX2] & 0x0f; - if ((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || Block.BlockSize <= zigEnd) + if ((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || BlockF.BlockSize <= zigEnd) { throw new ImageFormatException("Bad spectral selection bounds"); } @@ -1580,7 +1580,7 @@ namespace ImageSharp.Formats int compIndex = scan[i].Index; if (this.progCoeffs[compIndex] == null) { - this.progCoeffs[compIndex] = Block.CreateArray(mxx * myy * this.componentArray[compIndex].HorizontalFactor * this.componentArray[compIndex].VerticalFactor); + this.progCoeffs[compIndex] = BlockF.CreateArray(mxx * myy * this.componentArray[compIndex].HorizontalFactor * this.componentArray[compIndex].VerticalFactor); for (int j = 0; j < this.progCoeffs[compIndex].Length; j++) { @@ -1612,7 +1612,7 @@ namespace ImageSharp.Formats int compIndex = scan[i].Index; int hi = this.componentArray[compIndex].HorizontalFactor; int vi = this.componentArray[compIndex].VerticalFactor; - + for (int j = 0; j < hi * vi; j++) { @@ -1656,12 +1656,12 @@ namespace ImageSharp.Formats } var qtIndex = this.componentArray[compIndex].Selector; - + if (this.isProgressive) // Load the previous partially decoded coefficients, if applicable. { blockIndex = ((@by * mxx) * hi) + bx; - ProcessBlockImpl(ah, - ref this.progCoeffs[compIndex][blockIndex], + ProcessBlockImpl(ah, + ref this.progCoeffs[compIndex][blockIndex], scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, bx, ref this.quantizationTables[qtIndex] ); @@ -1670,11 +1670,11 @@ namespace ImageSharp.Formats { //var b = Block.Create(); scanWorkerBlock.Clear(); - + ProcessBlockImpl(ah, ref scanWorkerBlock, scan, i, zigStart, zigEnd, al, dc, compIndex, @by, mxx, hi, bx, ref this.quantizationTables[qtIndex] ); - + //b.Dispose(); } } @@ -1718,8 +1718,8 @@ namespace ImageSharp.Formats // for my } - private void ProcessBlockImpl(int ah, ref Block b, Scan[] scan, int i, int zigStart, int zigEnd, int al, - int[] dc, int compIndex, int @by, int mxx, int hi, int bx, ref Block qt) + private void ProcessBlockImpl(int ah, ref BlockF b, Scan[] scan, int i, int zigStart, int zigEnd, int al, + int[] dc, int compIndex, int @by, int mxx, int hi, int bx, ref BlockF qt) { if (ah != 0) { @@ -1755,8 +1755,8 @@ namespace ImageSharp.Formats for (; zig <= zigEnd; zig++) { byte value = this.DecodeHuffman(ref this.huffmanTrees[AcTable * ThRowSize + scan[i].AcTableSelector]); - byte val0 = (byte) (value >> 4); - byte val1 = (byte) (value & 0x0f); + byte val0 = (byte)(value >> 4); + byte val1 = (byte)(value & 0x0f); if (val1 != 0) { zig += val0; @@ -1772,10 +1772,10 @@ namespace ImageSharp.Formats { if (val0 != 0x0f) { - this.eobRun = (ushort) (1 << val0); + this.eobRun = (ushort)(1 << val0); if (val0 != 0) { - this.eobRun |= (ushort) this.DecodeBits(val0); + this.eobRun |= (ushort)this.DecodeBits(val0); } this.eobRun--; @@ -1790,11 +1790,11 @@ namespace ImageSharp.Formats if (this.isProgressive) { - if (zigEnd != Block.BlockSize - 1 || al != 0) + if (zigEnd != BlockF.BlockSize - 1 || al != 0) { // We haven't completely decoded this 8x8 block. Save the coefficients. - - this.progCoeffs[compIndex][((@by*mxx)*hi) + bx] = b.Clone(); + + this.progCoeffs[compIndex][((@by * mxx) * hi) + bx] = b.Clone(); // At this point, we could execute the rest of the loop body to dequantize and // perform the inverse DCT, to save early stages of a progressive image to the @@ -1806,7 +1806,7 @@ namespace ImageSharp.Formats } // Dequantize, perform the inverse DCT and store the block to the image. - for (int zig = 0; zig < Block.BlockSize; zig++) + for (int zig = 0; zig < BlockF.BlockSize; zig++) { b[Unzig[zig]] *= qt[zig]; } @@ -1824,7 +1824,7 @@ namespace ImageSharp.Formats { dst = this.grayImage.Pixels; stride = this.grayImage.Stride; - offset = this.grayImage.Offset + (8*((@by*this.grayImage.Stride) + bx)); + offset = this.grayImage.Offset + (8 * ((@by * this.grayImage.Stride) + bx)); } else { @@ -1833,26 +1833,26 @@ namespace ImageSharp.Formats case 0: dst = this.ycbcrImage.YChannel; stride = this.ycbcrImage.YStride; - offset = this.ycbcrImage.YOffset + (8*((@by*this.ycbcrImage.YStride) + bx)); + offset = this.ycbcrImage.YOffset + (8 * ((@by * this.ycbcrImage.YStride) + bx)); break; case 1: dst = this.ycbcrImage.CbChannel; stride = this.ycbcrImage.CStride; - offset = this.ycbcrImage.COffset + (8*((@by*this.ycbcrImage.CStride) + bx)); + offset = this.ycbcrImage.COffset + (8 * ((@by * this.ycbcrImage.CStride) + bx)); break; case 2: dst = this.ycbcrImage.CrChannel; stride = this.ycbcrImage.CStride; - offset = this.ycbcrImage.COffset + (8*((@by*this.ycbcrImage.CStride) + bx)); + offset = this.ycbcrImage.COffset + (8 * ((@by * this.ycbcrImage.CStride) + bx)); break; case 3: dst = this.blackPixels; stride = this.blackStride; - offset = 8*((@by*this.blackStride) + bx); + offset = 8 * ((@by * this.blackStride) + bx); break; default: @@ -1863,12 +1863,12 @@ namespace ImageSharp.Formats // Level shift by +128, clip to [0, 255], and write to dst. for (int y = 0; y < 8; y++) { - int y8 = y*8; - int yStride = y*stride; + int y8 = y * 8; + int yStride = y * stride; for (int x = 0; x < 8; x++) { - int c = b[y8 + x]; + float c = b[y8 + x]; if (c < -128) { c = 0; @@ -1882,7 +1882,7 @@ namespace ImageSharp.Formats c += 128; } - dst[yStride + x + offset] = (byte) c; + dst[yStride + x + offset] = (byte)c; } } } @@ -1927,15 +1927,15 @@ namespace ImageSharp.Formats } - totalHv += currentComponent.HorizontalFactor*currentComponent.VerticalFactor; + totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; - currentScan.DcTableSelector = (byte) (this.temp[2 + (2*i)] >> 4); + currentScan.DcTableSelector = (byte)(this.temp[2 + (2 * i)] >> 4); if (currentScan.DcTableSelector > MaxTh) { throw new ImageFormatException("Bad DC table selector value"); } - currentScan.AcTableSelector = (byte) (this.temp[2 + (2*i)] & 0x0f); + currentScan.AcTableSelector = (byte)(this.temp[2 + (2 * i)] & 0x0f); if (currentScan.AcTableSelector > MaxTh) { throw new ImageFormatException("Bad AC table selector value"); @@ -1950,7 +1950,7 @@ namespace ImageSharp.Formats /// The zig-zag start index /// The zig-zag end index /// The low transform offset - private void Refine(ref Block b, ref Huffman h, int zigStart, int zigEnd, int delta) + private void Refine(ref BlockF b, ref Huffman h, int zigStart, int zigEnd, int delta) { // Refining a DC component is trivial. if (zigStart == 0) @@ -1963,7 +1963,9 @@ namespace ImageSharp.Formats bool bit = this.DecodeBit(); if (bit) { - b[0] |= delta; + int stuff = (int)b[0]; + stuff |= delta; + b[0] = stuff; } return; @@ -2044,7 +2046,7 @@ namespace ImageSharp.Formats /// The non-zero entry /// The low transform offset /// The - private int RefineNonZeroes(ref Block b, int zig, int zigEnd, int nz, int delta) + private int RefineNonZeroes(ref BlockF b, int zig, int zigEnd, int nz, int delta) { for (; zig <= zigEnd; zig++) { @@ -2264,13 +2266,13 @@ namespace ImageSharp.Formats public void Dispose() { scanWorkerBlock.Dispose(); - Block.DisposeAll(this.quantizationTables); + BlockF.DisposeAll(this.quantizationTables); - foreach (Block[] blocks in progCoeffs) + foreach (BlockF[] blocks in progCoeffs) { if (blocks != null) { - Block.DisposeAll(blocks); + BlockF.DisposeAll(blocks); } } diff --git a/tests/ImageSharp.Tests46/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests46/Formats/Jpg/Block8x8Tests.cs index ae4c32c485..289e390d1b 100644 --- a/tests/ImageSharp.Tests46/Formats/Jpg/Block8x8Tests.cs +++ b/tests/ImageSharp.Tests46/Formats/Jpg/Block8x8Tests.cs @@ -73,11 +73,11 @@ namespace ImageSharp.Tests.Formats.Jpg { float sum = 0; - float[] block = new float[64]; + Measure(Times, () => { //Block8x8 block = new Block8x8(); - + float[] block = new float[64]; for (int i = 0; i < Block8x8.ScalarCount; i++) { block[i] = i;