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> /// Refines non-zero entries of b in zig-zag order. /// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over. /// </summary> /// <param name="bp">The <see cref="InputProcessor"/></param> /// <param name="zig">The zig-zag start index</param> /// <param name="nz">The non-zero entry</param> /// <param name="delta">The low transform offset</param> /// <returns>The <see cref="int" /></returns> private int RefineNonZeroes(ref InputProcessor bp, int zig, int nz, int delta) { var b = this.pointers.Block; for (; zig <= this.zigEnd; zig++) { int u = this.pointers.Unzig[zig]; float bu = Block8x8F.GetScalarAt(b, u); // TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary? if (bu == 0) { if (nz == 0) { break; } nz--; continue; } bool bit; DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit); if (!bp.CheckEOFEnsureNoError(errorCode)) { return(int.MinValue); } if (!bit) { continue; } if (bu >= 0) { // b[u] += delta; Block8x8F.SetScalarAt(b, u, bu + delta); } else { // b[u] -= delta; Block8x8F.SetScalarAt(b, u, bu - delta); } } return(zig); }
/// <summary> /// Dequantize, perform the inverse DCT and store decodedBlock.Block to the into the corresponding <see cref="JpegPixelArea"/> instance. /// </summary> /// <param name="decoder">The <see cref="JpegDecoderCore"/></param> /// <param name="decodedBlock">The <see cref="DecodedBlock"/></param> private void ProcessBlockColors(JpegDecoderCore decoder, ref DecodedBlock decodedBlock) { this.data.Block = decodedBlock.Block; int qtIndex = decoder.ComponentArray[this.componentIndex].Selector; this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; Block8x8F *b = this.pointers.Block; Block8x8F.UnZig(b, this.pointers.QuantiazationTable, this.pointers.Unzig); DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2); JpegPixelArea destChannel = decoder.GetDestinationChannel(this.componentIndex); JpegPixelArea destArea = destChannel.GetOffsetedSubAreaForBlock(decodedBlock.Bx, decodedBlock.By); destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); }
private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b) { a.V0L = DivideRound(a.V0L, b.V0L); a.V0R = DivideRound(a.V0R, b.V0R); a.V1L = DivideRound(a.V1L, b.V1L); a.V1R = DivideRound(a.V1R, b.V1R); a.V2L = DivideRound(a.V2L, b.V2L); a.V2R = DivideRound(a.V2R, b.V2R); a.V3L = DivideRound(a.V3L, b.V3L); a.V3R = DivideRound(a.V3R, b.V3R); a.V4L = DivideRound(a.V4L, b.V4L); a.V4R = DivideRound(a.V4R, b.V4R); a.V5L = DivideRound(a.V5L, b.V5L); a.V5R = DivideRound(a.V5R, b.V5R); a.V6L = DivideRound(a.V6L, b.V6L); a.V6R = DivideRound(a.V6R, b.V6R); a.V7L = DivideRound(a.V7L, b.V7L); a.V7R = DivideRound(a.V7R, b.V7R); }
internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d) { d.V0L = Vector4.Clamp(V0L + COff4, CMin4, CMax4); d.V0R = Vector4.Clamp(V0R + COff4, CMin4, CMax4); d.V1L = Vector4.Clamp(V1L + COff4, CMin4, CMax4); d.V1R = Vector4.Clamp(V1R + COff4, CMin4, CMax4); d.V2L = Vector4.Clamp(V2L + COff4, CMin4, CMax4); d.V2R = Vector4.Clamp(V2R + COff4, CMin4, CMax4); d.V3L = Vector4.Clamp(V3L + COff4, CMin4, CMax4); d.V3R = Vector4.Clamp(V3R + COff4, CMin4, CMax4); d.V4L = Vector4.Clamp(V4L + COff4, CMin4, CMax4); d.V4R = Vector4.Clamp(V4R + COff4, CMin4, CMax4); d.V5L = Vector4.Clamp(V5L + COff4, CMin4, CMax4); d.V5R = Vector4.Clamp(V5R + COff4, CMin4, CMax4); d.V6L = Vector4.Clamp(V6L + COff4, CMin4, CMax4); d.V6R = Vector4.Clamp(V6R + COff4, CMin4, CMax4); d.V7L = Vector4.Clamp(V7L + COff4, CMin4, CMax4); d.V7R = Vector4.Clamp(V7R + COff4, CMin4, CMax4); }
/// <summary> /// Refines non-zero entries of b in zig-zag order. /// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over. /// </summary> /// <param name="decoder">The decoder</param> /// <param name="zig">The zig-zag start index</param> /// <param name="nz">The non-zero entry</param> /// <param name="delta">The low transform offset</param> /// <returns>The <see cref="int" /></returns> private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta) { var b = this.pointers.Block; for (; zig <= this.zigEnd; zig++) { int u = this.pointers.Unzig[zig]; float bu = Block8x8F.GetScalarAt(b, u); // TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary? if (bu == 0) { if (nz == 0) { break; } nz--; continue; } bool bit = decoder.DecodeBit(); if (!bit) { continue; } if (bu >= 0) { // b[u] += delta; Block8x8F.SetScalarAt(b, u, bu + delta); } else { // b[u] -= delta; Block8x8F.SetScalarAt(b, u, bu - delta); } } return(zig); }
/// <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.MultiplyAllInplace(C_0_125); }
/// <summary> /// Decodes a successive approximation refinement block, as specified in section G.1.2. /// </summary> /// <param name="bp">The <see cref="InputProcessor"/> instance</param> /// <param name="h">The Huffman tree</param> /// <param name="delta">The low transform offset</param> private void Refine(ref InputProcessor bp, ref HuffmanTree h, int delta) { Block8x8F *b = this.pointers.Block; // Refining a DC component is trivial. if (this.zigStart == 0) { if (this.zigEnd != 0) { throw new ImageFormatException("Invalid state for zig DC component"); } bool bit; DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit); if (!bp.CheckEOFEnsureNoError(errorCode)) { return; } if (bit) { int stuff = (int)Block8x8F.GetScalarAt(b, 0); // int stuff = (int)b[0]; stuff |= delta; // b[0] = stuff; Block8x8F.SetScalarAt(b, 0, stuff); } return; } // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. int zig = this.zigStart; if (this.eobRun == 0) { for (; zig <= this.zigEnd; zig++) { bool done = false; int z = 0; int val; DecoderErrorCode errorCode = bp.DecodeHuffmanUnsafe(ref h, out val); if (!bp.CheckEOF(errorCode)) { return; } int val0 = val >> 4; int val1 = val & 0x0f; switch (val1) { case 0: if (val0 != 0x0f) { this.eobRun = 1 << val0; if (val0 != 0) { errorCode = this.DecodeEobRun(val0, ref bp); if (!bp.CheckEOFEnsureNoError(errorCode)) { return; } } done = true; } break; case 1: z = delta; bool bit; errorCode = bp.DecodeBitUnsafe(out bit); if (!bp.CheckEOFEnsureNoError(errorCode)) { return; } if (!bit) { z = -z; } break; default: throw new ImageFormatException("Unexpected Huffman code"); } if (done) { break; } zig = this.RefineNonZeroes(ref bp, zig, val0, delta); if (bp.UnexpectedEndOfStreamReached) { return; } if (zig > this.zigEnd) { throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}"); } if (z != 0) { // b[Unzig[zig]] = z; Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], z); } } } if (this.eobRun > 0) { this.eobRun--; this.RefineNonZeroes(ref bp, zig, -1, delta); } }
/// <summary> /// Read the current the current block at (<see cref="bx"/>, <see cref="by"/>) from the decoders stream /// </summary> /// <param name="decoder">The decoder</param> /// <param name="scanIndex">The index of the scan</param> private void DecodeBlock(JpegDecoderCore decoder, int scanIndex) { var b = this.pointers.Block; int huffmannIdx = (HuffmanTree.AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; if (this.ah != 0) { this.Refine(ref decoder.InputProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); } else { int zig = this.zigStart; DecoderErrorCode errorCode; if (zig == 0) { zig++; // Decode the DC coefficient, as specified in section F.2.2.1. int value; int huffmanIndex = (HuffmanTree.DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; errorCode = decoder.InputProcessor.DecodeHuffmanUnsafe( ref decoder.HuffmanTrees[huffmanIndex], out value); if (!decoder.InputProcessor.CheckEOF(errorCode)) { return; } if (value > 16) { throw new ImageFormatException("Excessive DC component"); } int deltaDC; errorCode = decoder.InputProcessor.ReceiveExtendUnsafe(value, out deltaDC); if (!decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) { return; } this.pointers.Dc[this.ComponentIndex] += deltaDC; // b[0] = dc[compIndex] << al; Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[this.ComponentIndex] << this.al); } if (zig <= this.zigEnd && this.eobRun > 0) { this.eobRun--; } else { // Decode the AC coefficients, as specified in section F.2.2.2. for (; zig <= this.zigEnd; zig++) { int value; errorCode = decoder.InputProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); if (!decoder.InputProcessor.CheckEOF(errorCode)) { return; } int val0 = value >> 4; int val1 = value & 0x0f; if (val1 != 0) { zig += val0; if (zig > this.zigEnd) { break; } int ac; errorCode = decoder.InputProcessor.ReceiveExtendUnsafe(val1, out ac); if (!decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) { return; } // b[Unzig[zig]] = ac << al; Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al); } else { if (val0 != 0x0f) { this.eobRun = (ushort)(1 << val0); if (val0 != 0) { errorCode = this.DecodeEobRun(val0, ref decoder.InputProcessor); if (!decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) { return; } } this.eobRun--; break; } zig += 0x0f; } } } } }
/// <summary> /// Decodes a successive approximation refinement block, as specified in section G.1.2. /// </summary> /// <param name="decoder">The decoder instance</param> /// <param name="h">The Huffman tree</param> /// <param name="delta">The low transform offset</param> private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta) { Block8x8F *b = this.pointers.Block; // Refining a DC component is trivial. if (this.zigStart == 0) { if (this.zigEnd != 0) { throw new ImageFormatException("Invalid state for zig DC component"); } bool bit = decoder.DecodeBit(); if (bit) { int stuff = (int)Block8x8F.GetScalarAt(b, 0); // int stuff = (int)b[0]; stuff |= delta; // b[0] = stuff; Block8x8F.SetScalarAt(b, 0, stuff); } return; } // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. int zig = this.zigStart; if (this.eobRun == 0) { for (; zig <= this.zigEnd; zig++) { bool done = false; int z = 0; byte val = decoder.DecodeHuffman(ref h); int val0 = val >> 4; int val1 = val & 0x0f; switch (val1) { case 0: if (val0 != 0x0f) { this.eobRun = (ushort)(1 << val0); if (val0 != 0) { this.eobRun |= (ushort)decoder.DecodeBits(val0); } done = true; } break; case 1: z = delta; bool bit = decoder.DecodeBit(); if (!bit) { z = -z; } break; default: throw new ImageFormatException("Unexpected Huffman code"); } if (done) { break; } zig = this.RefineNonZeroes(decoder, zig, val0, delta); if (zig > this.zigEnd) { throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}"); } if (z != 0) { // b[Unzig[zig]] = z; Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], z); } } } if (this.eobRun > 0) { this.eobRun--; this.RefineNonZeroes(decoder, zig, -1, delta); } }
/// <summary> /// Process the current block at (<see cref="bx"/>, <see cref="by"/>) /// </summary> /// <param name="decoder">The decoder</param> /// <param name="i">The index of the scan</param> /// <param name="compIndex">The component index</param> /// <param name="hi">Horizontal sampling factor at the given component index</param> private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi) { var b = this.pointers.Block; int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.Scan[i].AcTableSelector; if (this.ah != 0) { this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); } else { int zig = this.zigStart; if (zig == 0) { zig++; // Decode the DC coefficient, as specified in section F.2.2.1. byte value = decoder.DecodeHuffman( ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.Scan[i].DcTableSelector]); if (value > 16) { throw new ImageFormatException("Excessive DC component"); } int deltaDC = decoder.Bits.ReceiveExtend(value, decoder); this.pointers.Dc[compIndex] += deltaDC; // b[0] = dc[compIndex] << al; Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[compIndex] << this.al); } if (zig <= this.zigEnd && this.eobRun > 0) { this.eobRun--; } else { // Decode the AC coefficients, as specified in section F.2.2.2. for (; zig <= this.zigEnd; zig++) { byte value = decoder.DecodeHuffman(ref decoder.HuffmanTrees[huffmannIdx]); byte val0 = (byte)(value >> 4); byte val1 = (byte)(value & 0x0f); if (val1 != 0) { zig += val0; if (zig > this.zigEnd) { break; } int ac = decoder.Bits.ReceiveExtend(val1, decoder); // b[Unzig[zig]] = ac << al; Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al); } else { if (val0 != 0x0f) { this.eobRun = (ushort)(1 << val0); if (val0 != 0) { this.eobRun |= (ushort)decoder.DecodeBits(val0); } this.eobRun--; break; } zig += 0x0f; } } } } if (decoder.IsProgressive) { if (this.zigEnd != Block8x8F.ScalarCount - 1 || this.al != 0) { // We haven't completely decoded this 8x8 block. Save the coefficients. // this.ProgCoeffs[compIndex][((@by * XNumberOfMCUs) * hi) + bx] = b.Clone(); decoder.ProgCoeffs[compIndex][((this.by * this.XNumberOfMCUs) * hi) + this.bx] = *b; // 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 // *image.YCbCr buffers (the whole point of progressive encoding), but in Go, // the jpeg.Decode function does not return until the entire image is decoded, // so we "continue" here to avoid wasted computation. return; } } // Dequantize, perform the inverse DCT and store the block to the image. Block8x8F.UnZig(b, this.pointers.QuantiazationTable, this.pointers.Unzig); DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2); var destChannel = decoder.GetDestinationChannel(compIndex); var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by); destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); }
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> /// Store the block data into a <see cref="DecodedBlock"/> /// </summary> /// <param name="bx">X coordinate of the block</param> /// <param name="by">Y coordinate of the block</param> /// <param name="block">The <see cref="Block8x8F"/></param> public void SaveBlock(int bx, int by, ref Block8x8F block) { this.Bx = bx; this.By = by; this.Block = block; }