Beispiel #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ScanDecoder"/> class.
 /// </summary>
 /// <param name="stream">The input stream.</param>
 /// <param name="frame">The image frame.</param>
 /// <param name="dcHuffmanTables">The DC Huffman tables.</param>
 /// <param name="acHuffmanTables">The AC Huffman tables.</param>
 /// <param name="fastACTables">The fast AC decoding tables.</param>
 /// <param name="componentsLength">The length of the components. Different to the array length.</param>
 /// <param name="restartInterval">The reset interval.</param>
 /// <param name="spectralStart">The spectral selection start.</param>
 /// <param name="spectralEnd">The spectral selection end.</param>
 /// <param name="successiveHigh">The successive approximation bit high end.</param>
 /// <param name="successiveLow">The successive approximation bit low end.</param>
 public ScanDecoder(
     DoubleBufferedStreamReader stream,
     JpegFrame frame,
     HuffmanTables dcHuffmanTables,
     HuffmanTables acHuffmanTables,
     FastACTables fastACTables,
     int componentsLength,
     int restartInterval,
     int spectralStart,
     int spectralEnd,
     int successiveHigh,
     int successiveLow)
 {
     this.dctZigZag        = ZigZag.CreateUnzigTable();
     this.stream           = stream;
     this.frame            = frame;
     this.dcHuffmanTables  = dcHuffmanTables;
     this.acHuffmanTables  = acHuffmanTables;
     this.fastACTables     = fastACTables;
     this.components       = frame.Components;
     this.marker           = JpegConstants.Markers.XFF;
     this.markerPosition   = 0;
     this.componentsLength = componentsLength;
     this.restartInterval  = restartInterval;
     this.spectralStart    = spectralStart;
     this.spectralEnd      = spectralEnd;
     this.successiveHigh   = successiveHigh;
     this.successiveLow    = successiveLow;
 }
Beispiel #2
0
        public unsafe void Quantize(int seed)
        {
            var block = new Block8x8F();

            block.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed));

            var qt = new Block8x8F();

            qt.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed));

            var unzig = ZigZag.CreateUnzigTable();

            int *expectedResults = stackalloc int[Block8x8F.Size];

            ReferenceImplementations.QuantizeRational(&block, expectedResults, &qt, unzig.Data);

            var actualResults = default(Block8x8F);

            Block8x8F.Quantize(&block, &actualResults, &qt, unzig.Data);

            for (int i = 0; i < Block8x8F.Size; i++)
            {
                int expected = expectedResults[i];
                int actual   = (int)actualResults[i];

                Assert.Equal(expected, actual);
            }
        }
Beispiel #3
0
        public void ZigZagCanHandleAllPossibleCoefficients()
        {
            // Mimic the behaviour of the huffman scan decoder using all possible byte values
            short[] block  = new short[64];
            var     zigzag = ZigZag.CreateUnzigTable();

            for (int h = 0; h < 255; h++)
            {
                for (int i = 1; i < 64; i++)
                {
                    int s = h;
                    int r = s >> 4;
                    s &= 15;

                    if (s != 0)
                    {
                        i += r;
                        block[zigzag[i++]] = (short)s;
                    }
                    else
                    {
                        if (r == 0)
                        {
                            break;
                        }

                        i += 16;
                    }
                }
            }
        }
Beispiel #4
0
            /// <summary>
            /// Creates and initializes a new <see cref="ComputationData"/> instance
            /// </summary>
            /// <returns>The <see cref="ComputationData"/></returns>
            public static ComputationData Create()
            {
                ComputationData data = default;

                data.Unzig = ZigZag.CreateUnzigTable();
                return(data);
            }
 /// <summary>
 /// Initializes a new instance of the <see cref="HuffmanScanDecoder"/> class.
 /// </summary>
 /// <param name="stream">The input stream.</param>
 /// <param name="frame">The image frame.</param>
 /// <param name="dcHuffmanTables">The DC Huffman tables.</param>
 /// <param name="acHuffmanTables">The AC Huffman tables.</param>
 /// <param name="componentsLength">The length of the components. Different to the array length.</param>
 /// <param name="restartInterval">The reset interval.</param>
 /// <param name="spectralStart">The spectral selection start.</param>
 /// <param name="spectralEnd">The spectral selection end.</param>
 /// <param name="successiveHigh">The successive approximation bit high end.</param>
 /// <param name="successiveLow">The successive approximation bit low end.</param>
 public HuffmanScanDecoder(
     DoubleBufferedStreamReader stream,
     JpegFrame frame,
     HuffmanTable[] dcHuffmanTables,
     HuffmanTable[] acHuffmanTables,
     int componentsLength,
     int restartInterval,
     int spectralStart,
     int spectralEnd,
     int successiveHigh,
     int successiveLow)
 {
     this.dctZigZag        = ZigZag.CreateUnzigTable();
     this.stream           = stream;
     this.scanBuffer       = new HuffmanScanBuffer(stream);
     this.frame            = frame;
     this.dcHuffmanTables  = dcHuffmanTables;
     this.acHuffmanTables  = acHuffmanTables;
     this.components       = frame.Components;
     this.componentsLength = componentsLength;
     this.restartInterval  = restartInterval;
     this.todo             = restartInterval;
     this.spectralStart    = spectralStart;
     this.spectralEnd      = spectralEnd;
     this.successiveHigh   = successiveHigh;
     this.successiveLow    = successiveLow;
 }
Beispiel #6
0
        /// <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>
        /// <param name="cancellationToken">The token to monitor for cancellation.</param>
        private void Encode444 <TPixel>(Image <TPixel> pixels, CancellationToken cancellationToken)
            where TPixel : unmanaged, 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 temp2 = default;

            Block8x8F onStackLuminanceQuantTable   = this.luminanceQuantTable;
            Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;

            var unzig = ZigZag.CreateUnzigTable();

            // ReSharper disable once InconsistentNaming
            int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;

            var pixelConverter = YCbCrForwardConverter <TPixel> .Create();

            ImageFrame <TPixel> frame       = pixels.Frames.RootFrame;
            Buffer2D <TPixel>   pixelBuffer = frame.PixelBuffer;

            for (int y = 0; y < pixels.Height; y += 8)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var currentRows = new RowOctet <TPixel>(pixelBuffer, y);

                for (int x = 0; x < pixels.Width; x += 8)
                {
                    pixelConverter.Convert(frame, x, y, currentRows);

                    prevDCY = this.WriteBlock(
                        QuantIndex.Luminance,
                        prevDCY,
                        ref pixelConverter.Y,
                        ref temp1,
                        ref temp2,
                        ref onStackLuminanceQuantTable,
                        ref unzig);
                    prevDCCb = this.WriteBlock(
                        QuantIndex.Chrominance,
                        prevDCCb,
                        ref pixelConverter.Cb,
                        ref temp1,
                        ref temp2,
                        ref onStackChrominanceQuantTable,
                        ref unzig);
                    prevDCCr = this.WriteBlock(
                        QuantIndex.Chrominance,
                        prevDCCr,
                        ref pixelConverter.Cr,
                        ref temp1,
                        ref temp2,
                        ref onStackChrominanceQuantTable,
                        ref unzig);
                }
            }
        }
        /// <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 temp2 = default;

            Block8x8F onStackLuminanceQuantTable   = this.luminanceQuantTable;
            Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;

            var unzig = ZigZag.CreateUnzigTable();

            // ReSharper disable once InconsistentNaming
            int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;

            var 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);
                }
            }
        }
Beispiel #8
0
        public unsafe void DequantizeBlock(int seed)
        {
            Block8x8F original = CreateRandomFloatBlock(-500, 500, seed);
            Block8x8F qt       = CreateRandomFloatBlock(0, 10, seed + 42);

            var unzig = ZigZag.CreateUnzigTable();

            Block8x8F expected = original;
            Block8x8F actual   = original;

            ReferenceImplementations.DequantizeBlock(&expected, &qt, unzig.Data);
            Block8x8F.DequantizeBlock(&actual, &qt, unzig.Data);

            this.CompareBlocks(expected, actual, 0);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="HuffmanScanDecoder"/> class.
        /// </summary>
        /// <param name="stream">The input stream.</param>
        /// <param name="converter">Spectral to pixel converter.</param>
        /// <param name="cancellationToken">The token to monitor cancellation.</param>
        public HuffmanScanDecoder(
            BufferedReadStream stream,
            SpectralConverter converter,
            CancellationToken cancellationToken)
        {
            this.dctZigZag         = ZigZag.CreateUnzigTable();
            this.stream            = stream;
            this.spectralConverter = converter;
            this.cancellationToken = cancellationToken;

            // TODO: this is actually a variable value depending on component count
            const int maxTables = 4;

            this.dcHuffmanTables = new HuffmanTable[maxTables];
            this.acHuffmanTables = new HuffmanTable[maxTables];
        }
Beispiel #10
0
        public unsafe void ZigZag_CreateDequantizationTable_MultiplicationShouldQuantize(int seed)
        {
            Block8x8F original = CreateRandomFloatBlock(-500, 500, seed);
            Block8x8F qt       = CreateRandomFloatBlock(0, 10, seed + 42);

            var       unzig = ZigZag.CreateUnzigTable();
            Block8x8F zigQt = ZigZag.CreateDequantizationTable(ref qt);

            Block8x8F expected = original;
            Block8x8F actual   = original;

            ReferenceImplementations.DequantizeBlock(&expected, &qt, unzig.Data);

            actual.MultiplyInplace(ref zigQt);

            this.CompareBlocks(expected, actual, 0);
        }
Beispiel #11
0
        /// <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;

            BlockQuad cb    = default;
            BlockQuad cr    = default;
            var       cbPtr = (Block8x8F *)cb.Data;
            var       crPtr = (Block8x8F *)cr.Data;

            Block8x8F temp1 = default;
            Block8x8F temp2 = default;

            Block8x8F onStackLuminanceQuantTable   = this.luminanceQuantTable;
            Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable;

            var 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);
                }
            }
        }
Beispiel #12
0
        /// <summary>
        /// Decodes the spectral scan
        /// </summary>
        /// <param name="frame">The image frame</param>
        /// <param name="stream">The input stream</param>
        /// <param name="dcHuffmanTables">The DC Huffman tables</param>
        /// <param name="acHuffmanTables">The AC Huffman tables</param>
        /// <param name="components">The scan components</param>
        /// <param name="componentIndex">The component index within the array</param>
        /// <param name="componentsLength">The length of the components. Different to the array length</param>
        /// <param name="resetInterval">The reset interval</param>
        /// <param name="spectralStart">The spectral selection start</param>
        /// <param name="spectralEnd">The spectral selection end</param>
        /// <param name="successivePrev">The successive approximation bit high end</param>
        /// <param name="successive">The successive approximation bit low end</param>
        public void DecodeScan(
            PdfJsFrame frame,
            DoubleBufferedStreamReader stream,
            PdfJsHuffmanTables dcHuffmanTables,
            PdfJsHuffmanTables acHuffmanTables,
            PdfJsFrameComponent[] components,
            int componentIndex,
            int componentsLength,
            ushort resetInterval,
            int spectralStart,
            int spectralEnd,
            int successivePrev,
            int successive)
        {
            this.dctZigZag               = ZigZag.CreateUnzigTable();
            this.markerBuffer            = new byte[2];
            this.compIndex               = componentIndex;
            this.specStart               = spectralStart;
            this.specEnd                 = spectralEnd;
            this.successiveState         = successive;
            this.endOfStreamReached      = false;
            this.unexpectedMarkerReached = false;

            bool progressive = frame.Progressive;

            this.mcusPerLine = frame.McusPerLine;

            this.mcu = 0;
            int mcuExpected;

            if (componentsLength == 1)
            {
                mcuExpected = components[this.compIndex].WidthInBlocks * components[this.compIndex].HeightInBlocks;
            }
            else
            {
                mcuExpected = this.mcusPerLine * frame.McusPerColumn;
            }

            while (this.mcu < mcuExpected)
            {
                // Reset interval stuff
                this.mcuToRead = resetInterval != 0 ? Math.Min(mcuExpected - this.mcu, resetInterval) : mcuExpected;
                for (int i = 0; i < components.Length; i++)
                {
                    PdfJsFrameComponent c = components[i];
                    c.Pred = 0;
                }

                this.eobrun = 0;

                if (!progressive)
                {
                    this.DecodeScanBaseline(dcHuffmanTables, acHuffmanTables, components, componentsLength, stream);
                }
                else
                {
                    bool isAc    = this.specStart != 0;
                    bool isFirst = successivePrev == 0;
                    PdfJsHuffmanTables huffmanTables = isAc ? acHuffmanTables : dcHuffmanTables;
                    this.DecodeScanProgressive(huffmanTables, isAc, isFirst, components, componentsLength, stream);
                }

                // Reset
                // TODO: I do not understand why these values are reset? We should surely be tracking the bits across mcu's?
                this.bitsCount = 0;
                this.bitsData  = 0;
                this.unexpectedMarkerReached = false;

                // Some images include more scan blocks than expected, skip past those and
                // attempt to find the next valid marker
                PdfJsFileMarker fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream);
                byte            marker     = fileMarker.Marker;

                // RSTn - We've already read the bytes and altered the position so no need to skip
                if (marker >= PdfJsJpegConstants.Markers.RST0 && marker <= PdfJsJpegConstants.Markers.RST7)
                {
                    continue;
                }

                if (!fileMarker.Invalid)
                {
                    // We've found a valid marker.
                    // Rewind the stream to the position of the marker and break
                    stream.Position = fileMarker.Position;
                    break;
                }

#if DEBUG
                Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}");
#endif
            }
        }