/// <summary> /// Writes data to "Define Quantization Tables" block for QuantIndex /// </summary> /// <param name="dqt">The "Define Quantization Tables" block</param> /// <param name="offset">Offset in "Define Quantization Tables" block</param> /// <param name="i">The quantization index</param> /// <param name="quant">The quantization table to copy data from</param> private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F quant) { dqt[offset++] = (byte)i; for (int j = 0; j < Block8x8F.Size; j++) { dqt[offset++] = (byte)quant[j]; } }
public static void IDCT8x4_LeftPart(ref Block8x8F s, ref Block8x8F d) { Vector4 my1 = s.V1L; Vector4 my7 = s.V7L; Vector4 mz0 = my1 + my7; Vector4 my3 = s.V3L; Vector4 mz2 = my3 + my7; Vector4 my5 = s.V5L; Vector4 mz1 = my3 + my5; Vector4 mz3 = my1 + my5; Vector4 mz4 = (mz0 + mz1) * C_1_175876; mz2 = (mz2 * C_1_961571) + mz4; mz3 = (mz3 * C_0_390181) + mz4; mz0 = mz0 * C_0_899976; mz1 = mz1 * C_2_562915; Vector4 mb3 = (my7 * C_0_298631) + mz0 + mz2; Vector4 mb2 = (my5 * C_2_053120) + mz1 + mz3; Vector4 mb1 = (my3 * C_3_072711) + mz1 + mz2; Vector4 mb0 = (my1 * C_1_501321) + mz0 + mz3; Vector4 my2 = s.V2L; Vector4 my6 = s.V6L; mz4 = (my2 + my6) * C_0_541196; Vector4 my0 = s.V0L; Vector4 my4 = s.V4L; mz0 = my0 + my4; mz1 = my0 - my4; mz2 = mz4 + (my6 * C_1_847759); mz3 = mz4 + (my2 * C_0_765367); my0 = mz0 + mz3; my3 = mz0 - mz3; my1 = mz1 + mz2; my2 = mz1 - mz2; d.V0L = my0 + mb0; d.V7L = my0 - mb0; d.V1L = my1 + mb1; d.V6L = my1 - mb1; d.V2L = my2 + mb2; d.V5L = my2 - mb2; d.V3L = my3 + mb3; d.V4L = my3 - mb3; }
/// <summary> /// Writes a block of pixel data using the given quantization table, /// returning the post-quantized DC value of the DCT-transformed block. /// The block is in natural (not zig-zag) order. /// </summary> /// <param name="index">The quantization table index.</param> /// <param name="prevDC">The previous DC value.</param> /// <param name="src">Source block</param> /// <param name="tempDest1">Temporal block to be used as FDCT Destination</param> /// <param name="tempDest2">Temporal block 2</param> /// <param name="quant">Quantization table</param> /// <param name="unzigPtr">The 8x8 Unzig block pointer</param> /// <returns> /// The <see cref="int"/> /// </returns> private int WriteBlock( QuantIndex index, int prevDC, Block8x8F *src, Block8x8F *tempDest1, Block8x8F *tempDest2, Block8x8F *quant, byte *unzigPtr) { FastFloatingPointDCT.TransformFDCT(ref *src, ref *tempDest1, ref *tempDest2); Block8x8F.Quantize(tempDest1, tempDest2, quant, unzigPtr); float *unziggedDestPtr = (float *)tempDest2; int dc = (int)unziggedDestPtr[0]; // Emit the DC delta. this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC); // Emit the AC components. HuffIndex h = (HuffIndex)((2 * (int)index) + 1); int runLength = 0; for (int zig = 1; zig < Block8x8F.Size; zig++) { int ac = (int)unziggedDestPtr[zig]; if (ac == 0) { runLength++; } else { while (runLength > 15) { this.EmitHuff(h, 0xf0); runLength -= 16; } this.EmitHuffRLE(h, runLength, ac); runLength = 0; } } if (runLength > 0) { this.EmitHuff(h, 0x00); } return(dc); }
/// <summary> /// Encodes the image with no subsampling. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="pixels">The pixel accessor providing access to the image pixels.</param> private void Encode444 <TPixel>(Image <TPixel> pixels) where TPixel : struct, IPixel <TPixel> { // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // (Partially done with YCbCrForwardConverter<TPixel>) Block8x8F temp1 = default(Block8x8F); Block8x8F temp2 = default(Block8x8F); Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; ZigZag unzig = ZigZag.CreateUnzigTable(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; YCbCrForwardConverter <TPixel> pixelConverter = YCbCrForwardConverter <TPixel> .Create(); for (int y = 0; y < pixels.Height; y += 8) { for (int x = 0; x < pixels.Width; x += 8) { pixelConverter.Convert(pixels.Frames.RootFrame, x, y); prevDCY = this.WriteBlock( QuantIndex.Luminance, prevDCY, &pixelConverter.Y, &temp1, &temp2, &onStackLuminanceQuantTable, unzig.Data); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, &pixelConverter.Cb, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, &pixelConverter.Cr, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); } } }
/// <summary> /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization). /// Ported from https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239 /// </summary> /// <param name="src">Source</param> /// <param name="dest">Destination</param> /// <param name="temp">Temporary block provided by the caller</param> public static void TransformIDCT(ref Block8x8F src, ref Block8x8F dest, ref Block8x8F temp) { // TODO: Transpose is a bottleneck now. We need full AVX support to optimize it: // https://github.com/dotnet/corefx/issues/22940 src.TransposeInto(ref temp); IDCT8x4_LeftPart(ref temp, ref dest); IDCT8x4_RightPart(ref temp, ref dest); dest.TransposeInto(ref temp); IDCT8x4_LeftPart(ref temp, ref dest); IDCT8x4_RightPart(ref temp, ref dest); // TODO: What if we leave the blocks in a scaled-by-x8 state until final color packing? dest.MultiplyInplace(C_0_125); }
/// <summary> /// Initializes quantization table. /// </summary> /// <param name="i">The quantization index.</param> /// <param name="scale">The scaling factor.</param> /// <param name="quant">The quantization table.</param> private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant) { for (int j = 0; j < Block8x8F.Size; j++) { int x = UnscaledQuant[i, j]; x = ((x * scale) + 50) / 100; if (x < 1) { x = 1; } if (x > 255) { x = 255; } quant[j] = x; } }
/// <summary> /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization) /// </summary> /// <param name="src">Source</param> /// <param name="dest">Destination</param> /// <param name="temp">Temporary block provided by the caller</param> /// <param name="offsetSourceByNeg128">If true, a constant -128.0 offset is applied for all values before FDCT </param> public static void TransformFDCT( ref Block8x8F src, ref Block8x8F dest, ref Block8x8F temp, bool offsetSourceByNeg128 = true) { src.TransposeInto(ref temp); if (offsetSourceByNeg128) { temp.AddToAllInplace(new Vector4(-128)); } FDCT8x4_LeftPart(ref temp, ref dest); FDCT8x4_RightPart(ref temp, ref dest); dest.TransposeInto(ref temp); FDCT8x4_LeftPart(ref temp, ref dest); FDCT8x4_RightPart(ref temp, ref dest); dest.MultiplyInplace(C_0_125); }
public void TransposeInto(ref Block8x8F d) { d.V0L.X = V0L.X; d.V1L.X = V0L.Y; d.V2L.X = V0L.Z; d.V3L.X = V0L.W; d.V4L.X = V0R.X; d.V5L.X = V0R.Y; d.V6L.X = V0R.Z; d.V7L.X = V0R.W; d.V0L.Y = V1L.X; d.V1L.Y = V1L.Y; d.V2L.Y = V1L.Z; d.V3L.Y = V1L.W; d.V4L.Y = V1R.X; d.V5L.Y = V1R.Y; d.V6L.Y = V1R.Z; d.V7L.Y = V1R.W; d.V0L.Z = V2L.X; d.V1L.Z = V2L.Y; d.V2L.Z = V2L.Z; d.V3L.Z = V2L.W; d.V4L.Z = V2R.X; d.V5L.Z = V2R.Y; d.V6L.Z = V2R.Z; d.V7L.Z = V2R.W; d.V0L.W = V3L.X; d.V1L.W = V3L.Y; d.V2L.W = V3L.Z; d.V3L.W = V3L.W; d.V4L.W = V3R.X; d.V5L.W = V3R.Y; d.V6L.W = V3R.Z; d.V7L.W = V3R.W; d.V0R.X = V4L.X; d.V1R.X = V4L.Y; d.V2R.X = V4L.Z; d.V3R.X = V4L.W; d.V4R.X = V4R.X; d.V5R.X = V4R.Y; d.V6R.X = V4R.Z; d.V7R.X = V4R.W; d.V0R.Y = V5L.X; d.V1R.Y = V5L.Y; d.V2R.Y = V5L.Z; d.V3R.Y = V5L.W; d.V4R.Y = V5R.X; d.V5R.Y = V5R.Y; d.V6R.Y = V5R.Z; d.V7R.Y = V5R.W; d.V0R.Z = V6L.X; d.V1R.Z = V6L.Y; d.V2R.Z = V6L.Z; d.V3R.Z = V6L.W; d.V4R.Z = V6R.X; d.V5R.Z = V6R.Y; d.V6R.Z = V6R.Z; d.V7R.Z = V6R.W; d.V0R.W = V7L.X; d.V1R.W = V7L.Y; d.V2R.W = V7L.Z; d.V3R.W = V7L.W; d.V4R.W = V7R.X; d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W; }
/// <summary> /// Encodes the image with subsampling. The Cb and Cr components are each subsampled /// at a factor of 2 both horizontally and vertically. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="pixels">The pixel accessor providing access to the image pixels.</param> private void Encode420 <TPixel>(Image <TPixel> pixels) where TPixel : struct, IPixel <TPixel> { // TODO: Need a JpegScanEncoder<TPixel> class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default(Block8x8F); BlockQuad cb = default(BlockQuad); BlockQuad cr = default(BlockQuad); Block8x8F *cbPtr = (Block8x8F *)cb.Data; Block8x8F *crPtr = (Block8x8F *)cr.Data; Block8x8F temp1 = default(Block8x8F); Block8x8F temp2 = default(Block8x8F); Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; ZigZag unzig = ZigZag.CreateUnzigTable(); var pixelConverter = YCbCrForwardConverter <TPixel> .Create(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; for (int y = 0; y < pixels.Height; y += 16) { for (int x = 0; x < pixels.Width; x += 16) { for (int i = 0; i < 4; i++) { int xOff = (i & 1) * 8; int yOff = (i & 2) * 4; pixelConverter.Convert(pixels.Frames.RootFrame, x + xOff, y + yOff); cbPtr[i] = pixelConverter.Cb; crPtr[i] = pixelConverter.Cr; prevDCY = this.WriteBlock( QuantIndex.Luminance, prevDCY, &pixelConverter.Y, &temp1, &temp2, &onStackLuminanceQuantTable, unzig.Data); } Block8x8F.Scale16X16To8X8(&b, cbPtr); prevDCCb = this.WriteBlock( QuantIndex.Chrominance, prevDCCb, &b, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); Block8x8F.Scale16X16To8X8(&b, crPtr); prevDCCr = this.WriteBlock( QuantIndex.Chrominance, prevDCCr, &b, &temp1, &temp2, &onStackChrominanceQuantTable, unzig.Data); } } }