/// <summary>
        /// Initializes <see cref="SpectralBlocks"/>
        /// </summary>
        /// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
        /// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
        public void InitializeDerivedData(MemoryManager memoryManager, GolangJpegDecoderCore decoder)
        {
            // For 4-component images (either CMYK or YCbCrK), we only support two
            // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
            // Theoretically, 4-component JPEG images could mix and match hv values
            // but in practice, those two combinations are the only ones in use,
            // and it simplifies the applyBlack code below if we can assume that:
            // - for CMYK, the C and K channels have full samples, and if the M
            // and Y channels subsample, they subsample both horizontally and
            // vertically.
            // - for YCbCrK, the Y and K channels have full samples.
            this.SizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(this.SamplingFactors);

            if (this.Index == 0 || this.Index == 3)
            {
                this.SubSamplingDivisors = new Size(1, 1);
            }
            else
            {
                GolangComponent c0 = decoder.Components[0];
                this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
            }

            this.SpectralBlocks = memoryManager.Allocate2D <Block8x8>(this.SizeInBlocks.Width, this.SizeInBlocks.Height, true);
        }
            public static SpectralData LoadFromImageSharpDecoder(GolangJpegDecoderCore decoder)
            {
                GolangComponent[]            srcComponents  = decoder.Components;
                LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray();

                return(new SpectralData(destComponents));
            }
        /// <summary>
        /// Read Huffman data from Jpeg scans in <see cref="GolangJpegDecoderCore.InputStream"/>,
        /// and decode it as <see cref="Block8x8"/> into <see cref="GolangComponent.SpectralBlocks"/>.
        ///
        /// The blocks are traversed one MCU at a time. For 4:2:0 chroma
        /// subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
        /// For a baseline 32x16 pixel image, the Y blocks visiting order is:
        /// 0 1 4 5
        /// 2 3 6 7
        /// For progressive images, the interleaved scans (those with component count &gt; 1)
        /// are traversed as above, but non-interleaved scans are traversed left
        /// to right, top to bottom:
        /// 0 1 2 3
        /// 4 5 6 7
        /// Only DC scans (zigStart == 0) can be interleave AC scans must have
        /// only one component.
        /// To further complicate matters, for non-interleaved scans, there is no
        /// data for any blocks that are inside the image at the MCU level but
        /// outside the image at the pixel level. For example, a 24x16 pixel 4:2:0
        /// progressive image consists of two 16x16 MCUs. The interleaved scans
        /// will process 8 Y blocks:
        /// 0 1 4 5
        /// 2 3 6 7
        /// The non-interleaved scans will process only 6 Y blocks:
        /// 0 1 2
        /// 3 4 5
        /// </summary>
        /// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
        public void DecodeBlocks(GolangJpegDecoderCore decoder)
        {
            decoder.InputProcessor.ResetErrorState();

            this.blockCounter = 0;
            this.mcuCounter   = 0;
            this.expectedRst  = JpegConstants.Markers.RST0;

            for (int my = 0; my < decoder.MCUCountY; my++)
            {
                for (int mx = 0; mx < decoder.MCUCountX; mx++)
                {
                    this.DecodeBlocksAtMcuIndex(decoder, mx, my);

                    this.mcuCounter++;

                    // Handling restart intervals
                    // Useful info: https://stackoverflow.com/a/8751802
                    if (decoder.IsAtRestartInterval(this.mcuCounter))
                    {
                        this.ProcessRSTMarker(decoder);
                        this.Reset(decoder);
                    }
                }
            }
        }
        public void ColorSpace_IsDeducedCorrectlyGolang(string imageFile, object expectedColorSpaceValue)
        {
            var expecteColorSpace = (JpegColorSpace)expectedColorSpaceValue;

            using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile))
            {
                Assert.Equal(expecteColorSpace, decoder.ColorSpace);
            }
        }
Exemple #5
0
 internal static GolangJpegDecoderCore ParseGolangStream(string testFileName, bool metaDataOnly = false)
 {
     byte[] bytes = TestFile.Create(testFileName).Bytes;
     using (var ms = new MemoryStream(bytes))
     {
         var decoder = new GolangJpegDecoderCore(Configuration.Default, new JpegDecoder());
         decoder.ParseStream(ms, metaDataOnly);
         return(decoder);
     }
 }
Exemple #6
0
        public void OriginalDecoder_ParseStream_SaveSpectralResult <TPixel>(TestImageProvider <TPixel> provider)
            where TPixel : struct, IPixel <TPixel>
        {
            var decoder = new GolangJpegDecoderCore(Configuration.Default, new JpegDecoder());

            byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes;

            using (var ms = new MemoryStream(sourceBytes))
            {
                decoder.ParseStream(ms, false);

                var data = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder);
                VerifyJpeg.SaveSpectralImage(provider, data);
            }
        }
        public void ComponentScalingIsCorrect_1ChannelJpegGolang()
        {
            using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(TestImages.Jpeg.Baseline.Jpeg400))
            {
                Assert.Equal(1, decoder.ComponentCount);
                Assert.Equal(1, decoder.Components.Length);

                Size expectedSizeInBlocks = decoder.ImageSizeInPixels.DivideRoundUp(8);

                Assert.Equal(expectedSizeInBlocks, decoder.ImageSizeInMCU);

                var             uniform1 = new Size(1, 1);
                GolangComponent c0       = decoder.Components[0];
                VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1);
            }
        }
        public void PrintComponentDataGolang(string imageFile)
        {
            var sb = new StringBuilder();

            using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile))
            {
                sb.AppendLine(imageFile);
                sb.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}");
                GolangComponent c0 = decoder.Components[0];
                GolangComponent c1 = decoder.Components[1];

                sb.AppendLine($"Luma: SAMP: {c0.SamplingFactors} BLOCKS: {c0.SizeInBlocks}");
                sb.AppendLine($"Chroma: {c1.SamplingFactors} BLOCKS: {c1.SizeInBlocks}");
            }
            this.Output.WriteLine(sb.ToString());
        }
        private void DecodeBlocksAtMcuIndex(GolangJpegDecoderCore decoder, int mx, int my)
        {
            for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++)
            {
                this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex;
                GolangComponent component = decoder.Components[this.ComponentIndex];

                this.hi = component.HorizontalSamplingFactor;
                int vi = component.VerticalSamplingFactor;

                for (int j = 0; j < this.hi * vi; j++)
                {
                    if (this.componentScanCount != 1)
                    {
                        this.bx = (this.hi * mx) + (j % this.hi);
                        this.by = (vi * my) + (j / this.hi);
                    }
                    else
                    {
                        int q = decoder.MCUCountX * this.hi;
                        this.bx = this.blockCounter % q;
                        this.by = this.blockCounter / q;
                        this.blockCounter++;
                        if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight)
                        {
                            continue;
                        }
                    }

                    // Find the block at (bx,by) in the component's buffer:
                    ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by);

                    // Copy block to stack
                    this.data.Block = blockRefOnHeap;

                    if (!decoder.InputProcessor.ReachedEOF)
                    {
                        this.DecodeBlock(decoder, scanIndex);
                    }

                    // Store the result block:
                    blockRefOnHeap = this.data.Block;
                }
            }
Exemple #10
0
        public void VerifySpectralCorrectness_Golang <TPixel>(TestImageProvider <TPixel> provider)
            where TPixel : struct, IPixel <TPixel>
        {
            if (!TestEnvironment.IsWindows)
            {
                return;
            }

            var decoder = new GolangJpegDecoderCore(Configuration.Default, new GolangJpegDecoder());

            byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes;

            using (var ms = new MemoryStream(sourceBytes))
            {
                decoder.ParseStream(ms);
                var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder);

                this.VerifySpectralCorrectness <TPixel>(provider, imageSharpData);
            }
        }
        public void ComponentScalingIsCorrect_MultiChannelJpegGolang(
            string imageFile,
            int componentCount,
            object expectedLumaFactors,
            object expectedChromaFactors)
        {
            var fLuma   = (Size)expectedLumaFactors;
            var fChroma = (Size)expectedChromaFactors;

            using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile))
            {
                Assert.Equal(componentCount, decoder.ComponentCount);
                Assert.Equal(componentCount, decoder.Components.Length);

                GolangComponent c0 = decoder.Components[0];
                GolangComponent c1 = decoder.Components[1];
                GolangComponent c2 = decoder.Components[2];

                var uniform1 = new Size(1, 1);

                Size expectedLumaSizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(fLuma);

                Size divisor = fLuma.DivideBy(fChroma);

                Size expectedChromaSizeInBlocks = expectedLumaSizeInBlocks.DivideRoundUp(divisor);

                VerifyJpeg.VerifyComponent(c0, expectedLumaSizeInBlocks, fLuma, uniform1);
                VerifyJpeg.VerifyComponent(c1, expectedChromaSizeInBlocks, fChroma, divisor);
                VerifyJpeg.VerifyComponent(c2, expectedChromaSizeInBlocks, fChroma, divisor);

                if (componentCount == 4)
                {
                    GolangComponent c3 = decoder.Components[2];
                    VerifyJpeg.VerifyComponent(c3, expectedLumaSizeInBlocks, fLuma, uniform1);
                }
            }
        }
        /// <summary>
        /// Initializes all component data except <see cref="SpectralBlocks"/>.
        /// </summary>
        /// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
        public void InitializeCoreData(GolangJpegDecoderCore decoder)
        {
            // Section B.2.2 states that "the value of C_i shall be different from
            // the values of C_1 through C_(i-1)".
            int i = this.Index;

            for (int j = 0; j < this.Index; j++)
            {
                if (this.Identifier == decoder.Components[j].Identifier)
                {
                    throw new ImageFormatException("Repeated component identifier");
                }
            }

            this.QuantizationTableIndex = decoder.Temp[8 + (3 * i)];
            if (this.QuantizationTableIndex > GolangJpegDecoderCore.MaxTq)
            {
                throw new ImageFormatException("Bad Tq value");
            }

            byte hv = decoder.Temp[7 + (3 * i)];
            int  h  = hv >> 4;
            int  v  = hv & 0x0f;

            if (h < 1 || h > 4 || v < 1 || v > 4)
            {
                throw new ImageFormatException("Unsupported Luma/chroma subsampling ratio");
            }

            if (h == 3 || v == 3)
            {
                throw new ImageFormatException("Lnsupported subsampling ratio");
            }

            switch (decoder.ComponentCount)
            {
            case 1:

                // If a JPEG image has only one component, section A.2 says "this data
                // is non-interleaved by definition" and section A.2.2 says "[in this
                // case...] the order of data units within a scan shall be left-to-right
                // and top-to-bottom... regardless of the values of H_1 and V_1". Section
                // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be
                // one data unit". Similarly, section A.1.1 explains that it is the ratio
                // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale
                // images, H_1 is the maximum H_j for all components j, so that ratio is
                // always 1. The component's (h, v) is effectively always (1, 1): even if
                // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8
                // MCUs, not two 16x8 MCUs.
                h = 1;
                v = 1;
                break;

            case 3:

                // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0,
                // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the
                // (h, v) values for the Y component are either (1, 1), (1, 2),
                // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values
                // must be a multiple of the Cb and Cr component's values. We also
                // assume that the two chroma components have the same subsampling
                // ratio.
                switch (i)
                {
                case 0:
                {
                    // Y.
                    // We have already verified, above, that h and v are both
                    // either 1, 2 or 4, so invalid (h, v) combinations are those
                    // with v == 4.
                    if (v == 4)
                    {
                        throw new ImageFormatException("Unsupported subsampling ratio");
                    }

                    break;
                }

                case 1:
                {
                    // Cb.
                    Size s0 = decoder.Components[0].SamplingFactors;

                    if (s0.Width % h != 0 || s0.Height % v != 0)
                    {
                        throw new ImageFormatException("Unsupported subsampling ratio");
                    }

                    break;
                }

                case 2:
                {
                    // Cr.
                    Size s1 = decoder.Components[1].SamplingFactors;

                    if (s1.Width != h || s1.Height != v)
                    {
                        throw new ImageFormatException("Unsupported subsampling ratio");
                    }

                    break;
                }
                }

                break;

            case 4:

                // For 4-component images (either CMYK or YCbCrK), we only support two
                // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
                // Theoretically, 4-component JPEG images could mix and match hv values
                // but in practice, those two combinations are the only ones in use,
                // and it simplifies the applyBlack code below if we can assume that:
                // - for CMYK, the C and K channels have full samples, and if the M
                // and Y channels subsample, they subsample both horizontally and
                // vertically.
                // - for YCbCrK, the Y and K channels have full samples.
                switch (i)
                {
                case 0:
                    if (hv != 0x11 && hv != 0x22)
                    {
                        throw new ImageFormatException("Unsupported subsampling ratio");
                    }

                    break;

                case 1:
                case 2:
                    if (hv != 0x11)
                    {
                        throw new ImageFormatException("Unsupported subsampling ratio");
                    }

                    break;

                case 3:
                    Size s0 = decoder.Components[0].SamplingFactors;

                    if (s0.Width != h || s0.Height != v)
                    {
                        throw new ImageFormatException("Unsupported subsampling ratio");
                    }

                    break;
                }

                break;
            }

            this.SamplingFactors = new Size(h, v);
        }
 /// <summary>
 /// Initializes a default-constructed <see cref="GolangJpegScanDecoder"/> instance for reading data from <see cref="GolangJpegDecoderCore"/>-s stream.
 /// </summary>
 /// <param name="p">Pointer to <see cref="GolangJpegScanDecoder"/> on the stack</param>
 /// <param name="decoder">The <see cref="GolangJpegDecoderCore"/> instance</param>
 /// <param name="remaining">The remaining bytes in the segment block.</param>
 public static void InitStreamReading(GolangJpegScanDecoder *p, GolangJpegDecoderCore decoder, int remaining)
 {
     p->data     = ComputationData.Create();
     p->pointers = new DataPointers(&p->data);
     p->InitStreamReadingImpl(decoder, remaining);
 }