Exemplo n.º 1
0
        /// <summary>
        /// A Webp lossless image can go through four different types of transformation before being entropy encoded.
        /// This will reverse the transformations, if any are present.
        /// </summary>
        /// <param name="decoder">The decoder holding the transformation infos.</param>
        /// <param name="pixelData">The pixel data to apply the transformation.</param>
        /// <param name="memoryAllocator">The memory allocator is needed to allocate memory during the predictor transform.</param>
        public static void ApplyInverseTransforms(Vp8LDecoder decoder, Span <uint> pixelData, MemoryAllocator memoryAllocator)
        {
            List <Vp8LTransform> transforms = decoder.Transforms;

            for (int i = transforms.Count - 1; i >= 0; i--)
            {
                Vp8LTransform     transform     = transforms[i];
                Vp8LTransformType transformType = transform.TransformType;
                switch (transformType)
                {
                case Vp8LTransformType.PredictorTransform:
                    using (IMemoryOwner <uint> output = memoryAllocator.Allocate <uint>(pixelData.Length, AllocationOptions.Clean))
                    {
                        LosslessUtils.PredictorInverseTransform(transform, pixelData, output.GetSpan());
                    }

                    break;

                case Vp8LTransformType.SubtractGreen:
                    LosslessUtils.AddGreenToBlueAndRed(pixelData);
                    break;

                case Vp8LTransformType.CrossColorTransform:
                    LosslessUtils.ColorSpaceInverseTransform(transform, pixelData);
                    break;

                case Vp8LTransformType.ColorIndexingTransform:
                    LosslessUtils.ColorIndexInverseTransform(transform, pixelData);
                    break;
                }
            }
        }
Exemplo n.º 2
0
        public double GetDistanceCost(int distance)
        {
            int extraBits = 0;
            int code      = LosslessUtils.PrefixEncodeBits(distance, ref extraBits);

            return(this.Distance[code] + extraBits);
        }
Exemplo n.º 3
0
        private static void RunAddGreenToBlueAndRedTest()
        {
            uint[] pixelData =
            {
                4284188659, 4284254193, 4284318702, 4284187883, 4284318441, 4284383470, 4284318700, 4284124392,
                4283799012, 4283864546, 4284581610, 4285163264, 4284891926, 4284497945, 4284761620, 4284893965,
                4284828428, 4284959500, 4284959755, 4284828426, 4284960520, 4285289733, 4285159937, 4285292030,
                4285358077, 4285228030, 4284966398, 4285097213, 4285227773, 4285096956, 4285097470, 4285228540,
                4285163516, 4285425149, 4285294332, 4285228540, 4285228543, 4285163261, 4285163516, 4285032701,
                4284835841, 4284835584, 4284966140, 4285228029, 4284770300, 4285097214, 4285293819, 4285228795,
                4285163259, 4285228287, 4284901886
            };

            uint[] expectedOutput =
            {
                4293035898, 4293101432, 4292903793, 4292838511, 4292837995, 4292771950, 4292903791, 4293299316,
                4293563769, 4293629303, 4293363312, 4291913575, 4289282905, 4288692313, 4289349210, 4289809240,
                4289743703, 4289874775, 4289940567, 4289743701, 4290137943, 4290860378, 4291058267, 4291386715,
                4291583836, 4291715937, 4291585379, 4291650657, 4291650143, 4291584863, 4291716451, 4291847521,
                4291913571, 4292044130, 4291978850, 4291847521, 4291847524, 4291847779, 4291913571, 4291848293,
                4291651689, 4291585895, 4291519584, 4291715936, 4291520355, 4291650658, 4291847263, 4291913313,
                4291847777, 4291781731, 4291783015
            };

            LosslessUtils.AddGreenToBlueAndRed(pixelData);

            Assert.Equal(expectedOutput, pixelData);
        }
Exemplo n.º 4
0
        // Test image: Input\Png\Bike.png
        private static void RunColorSpaceTransformTestWithBikeImage()
        {
            // arrange
            uint[] expectedData =
            {
                4278714368, 4278192876, 4278198304, 4278198304, 4278190304, 4278190080, 4278190080, 4278198272,
                4278197760, 4278198816, 4278197794, 4278197774, 4278190080, 4278190080, 4278198816, 4278197281,
                4278197280, 4278197792, 4278200353, 4278191343, 4278190304, 4294713873, 4278198784, 4294844416,
                4278201578, 4278200044, 4278191343, 4278190288, 4294705200, 4294717139, 4278203628, 4278201064,
                4278201586, 4278197792, 4279240909
            };

            // Convert image pixels to bgra array.
            byte[] imgBytes = File.ReadAllBytes(TestImageFullPath(TestImages.Webp.Lossy.BikeSmall));
            using var image = Image.Load <Rgba32>(imgBytes, new WebpDecoder());
            uint[] bgra = ToBgra(image);

            int colorTransformBits = 4;
            int transformWidth     = LosslessUtils.SubSampleSize(image.Width, colorTransformBits);
            int transformHeight    = LosslessUtils.SubSampleSize(image.Height, colorTransformBits);

            uint[] transformData = new uint[transformWidth * transformHeight];
            int[]  scratch       = new int[256];

            // act
            PredictorEncoder.ColorSpaceTransform(image.Width, image.Height, colorTransformBits, 75, bgra, transformData, scratch);

            // assert
            Assert.Equal(expectedData, transformData);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Accumulate a token 'v' into a histogram.
        /// </summary>
        /// <param name="v">The token to add.</param>
        /// <param name="useDistanceModifier">Indicates whether to use the distance modifier.</param>
        /// <param name="xSize">xSize is only used when useDistanceModifier is true.</param>
        public void AddSinglePixOrCopy(PixOrCopy v, bool useDistanceModifier, int xSize = 0)
        {
            if (v.IsLiteral())
            {
                this.Alpha[v.Literal(3)]++;
                this.Red[v.Literal(2)]++;
                this.Literal[v.Literal(1)]++;
                this.Blue[v.Literal(0)]++;
            }
            else if (v.IsCacheIdx())
            {
                int literalIx = (int)(WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + v.CacheIdx());
                this.Literal[literalIx]++;
            }
            else
            {
                int extraBits = 0;
                int code      = LosslessUtils.PrefixEncodeBits(v.Length(), ref extraBits);
                this.Literal[WebpConstants.NumLiteralCodes + code]++;
                if (!useDistanceModifier)
                {
                    code = LosslessUtils.PrefixEncodeBits((int)v.Distance(), ref extraBits);
                }
                else
                {
                    code = LosslessUtils.PrefixEncodeBits(BackwardReferenceEncoder.DistanceToPlaneCode(xSize, (int)v.Distance()), ref extraBits);
                }

                this.Distance[code]++;
            }
        }
Exemplo n.º 6
0
        private static void RunTransformColorTest()
        {
            uint[] pixelData =
            {
                5998579,     65790,   130301, 16646653,   196350,   130565, 16712702, 16583164, 16452092,  65790,   782600,
                647446,   16571414, 16448771,   263931,   132601, 16711935,   131072,      511, 16711679, 132350,   329469,
                16647676,   132093,    66303, 16647169, 16515584,   196607,   196096, 16646655,      514, 131326, 16712192,
                327169,   16646655, 16776960,        3, 16712190,      511, 16646401, 16580612,    65535, 196092,   327425,16319743,
                392450,     196861, 16712192, 16711680,   130564, 16451071
            };

            var m = new Vp8LMultipliers()
            {
                GreenToBlue = 240,
                GreenToRed  = 232,
                RedToBlue   = 0
            };

            uint[] expectedOutput =
            {
                100279,      65790, 16710907, 16712190,   130813,    65028,   131840,   264449, 133377,    65790,  61697, 15917319,
                14801924, 16317698,   591614,   394748, 16711935,   131072,    65792, 16711679, 328704,   656896, 132607,
                328703,     197120,    66563, 16646657,   196607,   130815, 16711936,   131587, 131326,    66049, 261632, 16711936,
                16776960,        3,      511,    65792, 16711938, 16580612,    65535,    65019, 327425, 16516097, 261377,   196861,66049,
                16711680,    65027, 16712962
            };

            LosslessUtils.TransformColor(m, pixelData, pixelData.Length);

            Assert.Equal(expectedOutput, pixelData);
        }
Exemplo n.º 7
0
        public double GetLengthCost(int length)
        {
            int extraBits = 0;
            int code      = LosslessUtils.PrefixEncodeBits(length, ref extraBits);

            return(this.Literal[ValuesInBytes + code] + extraBits);
        }
Exemplo n.º 8
0
        private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, uint[] populationCounts, double[] output)
        {
            uint sum      = 0;
            int  nonzeros = 0;

            for (int i = 0; i < numSymbols; i++)
            {
                sum += populationCounts[i];
                if (populationCounts[i] > 0)
                {
                    nonzeros++;
                }
            }

            if (nonzeros <= 1)
            {
                output.AsSpan(0, numSymbols).Clear();
            }
            else
            {
                double logsum = LosslessUtils.FastLog2(sum);
                for (int i = 0; i < numSymbols; i++)
                {
                    output[i] = logsum - LosslessUtils.FastLog2(populationCounts[i]);
                }
            }
        }
Exemplo n.º 9
0
        private void UpdateDecoder(Vp8LDecoder decoder, int width, int height)
        {
            int numBits = decoder.Metadata.HuffmanSubSampleBits;

            decoder.Width  = width;
            decoder.Height = height;
            decoder.Metadata.HuffmanXSize = LosslessUtils.SubSampleSize(width, numBits);
            decoder.Metadata.HuffmanMask  = numBits == 0 ? ~0 : (1 << numBits) - 1;
        }
Exemplo n.º 10
0
        // Test image: Input\Webp\peak.png
        private static void RunColorSpaceTransformTestWithPeakImage()
        {
            // arrange
            uint[] expectedData =
            {
                4278191104, 4278191104, 4278191104, 4278191104, 4278191104, 4278191104, 4278191104, 4294577152,
                4294707200, 4294707200, 4294707200, 4294707200, 4294837248, 4294837248, 4293926912, 4294316544,
                4278191104, 4278191104, 4294837248, 4294837248, 4280287232, 4280350720, 4294447104, 4294707200,
                4294838272, 4278516736, 4294837248, 4294837248, 4278516736, 4294707200, 4279298048, 4294837248,
                4294837248, 4294837248, 4294837248, 4280287232, 4280287232, 4292670464, 4279633408, 4294838272,
                4294837248, 4278516736, 4278516736, 4278516736, 4278516736, 4278516736, 4278778880, 4278193152,
                4278191104, 4280287232, 4280287232, 4280287232, 4280287232, 4293971968, 4280612864, 4292802560,
                4294837760, 4278516736, 4278516736, 4294837760, 4294707712, 4278516736, 4294837248, 4278193152,
                4280287232, 4278984704, 4280287232, 4278243328, 4280287232, 4278244352, 4280287232, 4280025088,
                4280025088, 4294837760, 4278192128, 4294838784, 4294837760, 4294707712, 4278778880, 4278324224,
                4280287232, 4280287232, 4278202368, 4279115776, 4280287232, 4278243328, 4280287232, 4280287232,
                4280025088, 4280287232, 4278192128, 4294838272, 4294838272, 4294837760, 4278190592, 4278778880,
                4280875008, 4280287232, 4279896576, 4281075712, 4281075712, 4280287232, 4280287232, 4280287232,
                4280287232, 4280287232, 4278190592, 4294709248, 4278516736, 4278516736, 4278584832, 4278909440,
                4280287232, 4280287232, 4294367744, 4294621184, 4279115776, 4280287232, 4280287232, 4280351744,
                4280287232, 4280287232, 4280287232, 4278513664, 4278516736, 4278716416, 4278584832, 4280291328,
                4293062144, 4280287232, 4280287232, 4280287232, 4294456320, 4280291328, 4280287232, 4280287232,
                4280287232, 4280287232, 4280287232, 4280287232, 4278513152, 4278716416, 4278584832, 4280291328,
                4278198272, 4278198272, 4278589952, 4278198272, 4278198272, 4280287232, 4278765568, 4280287232,
                4280287232, 4280287232, 4280287232, 4294712832, 4278513152, 4278716640, 4279300608, 4278584832,
                4280156672, 4279373312, 4278589952, 4279373312, 4278328832, 4278328832, 4278328832, 4279634432,
                4280287232, 4280287232, 4280287232, 4280287232, 4278457344, 4280483328, 4278584832, 4278385664,
                4279634432, 4279373312, 4279634432, 4280287232, 4280287232, 4280156672, 4278589952, 4278328832,
                4278198272, 4280156672, 4280483328, 4294363648, 4280287232, 4278376448, 4280287232, 4278647808,
                4280287232, 4280287232, 4279373312, 4280287232, 4280287232, 4280156672, 4280287232, 4278198272,
                4278198272, 4280156672, 4280287232, 4280287232, 4293669888, 4278765568, 4278765568, 4280287232,
                4280287232, 4280287232, 4279634432, 4279634432, 4280287232, 4280287232, 4280287232, 4280287232,
                4280287232, 4280287232, 4280287232, 4280287232, 4279373312, 4279764992, 4293539328, 4279896576,
                4280287232, 4280287232, 4280287232, 4279634432, 4278198272, 4279634432, 4280287232, 4280287232,
                4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4279503872, 4279503872, 4280288256,
                4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232,
                4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232, 4280287232
            };

            // Convert image pixels to bgra array.
            byte[] imgBytes = File.ReadAllBytes(TestImageFullPath(TestImages.Webp.Peak));
            using var image = Image.Load <Rgba32>(imgBytes);
            uint[] bgra = ToBgra(image);

            int colorTransformBits = 3;
            int transformWidth     = LosslessUtils.SubSampleSize(image.Width, colorTransformBits);
            int transformHeight    = LosslessUtils.SubSampleSize(image.Height, colorTransformBits);

            uint[] transformData = new uint[transformWidth * transformHeight];
            int[]  scratch       = new int[256];

            // act
            PredictorEncoder.ColorSpaceTransform(image.Width, image.Height, colorTransformBits, 75, bgra, transformData, scratch);

            // assert
            Assert.Equal(expectedData, transformData);
        }
Exemplo n.º 11
0
        private static void RunCombinedShannonEntropyTest()
        {
            int[] x        = { 3, 5, 2, 5, 3, 1, 2, 2, 3, 3, 1, 2, 1, 2, 1, 1, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1, 1, 0, 0, 2, 1, 1, 0, 3, 1, 2, 3, 2, 3 };
            int[] y        = { 11, 12, 8, 3, 4, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 1, 1, 2, 4, 6, 4 };
            float expected = 884.7585f;

            float actual = LosslessUtils.CombinedShannonEntropy(x, y);

            Assert.Equal(expected, actual, 5);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Reads the transformations, if any are present.
        /// </summary>
        /// <param name="xSize">The width of the image.</param>
        /// <param name="ySize">The height of the image.</param>
        /// <param name="decoder">Vp8LDecoder where the transformations will be stored.</param>
        private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder)
        {
            var transformType = (Vp8LTransformType)this.bitReader.ReadValue(2);
            var transform     = new Vp8LTransform(transformType, xSize, ySize);

            // Each transform is allowed to be used only once.
            foreach (Vp8LTransform decoderTransform in decoder.Transforms)
            {
                if (decoderTransform.TransformType == transform.TransformType)
                {
                    WebpThrowHelper.ThrowImageFormatException("Each transform can only be present once");
                }
            }

            switch (transformType)
            {
            case Vp8LTransformType.SubtractGreen:
                // There is no data associated with this transform.
                break;

            case Vp8LTransformType.ColorIndexingTransform:
                // The transform data contains color table size and the entries in the color table.
                // 8 bit value for color table size.
                uint numColors = this.bitReader.ReadValue(8) + 1;
                int  bits      = numColors > 16 ? 0
                                     : numColors > 4 ? 1
                                     : numColors > 2 ? 2
                                     : 3;
                transform.Bits = bits;
                using (IMemoryOwner <uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false))
                {
                    int finalNumColors = 1 << (8 >> transform.Bits);
                    IMemoryOwner <uint> newColorMap = this.memoryAllocator.Allocate <uint>(finalNumColors, AllocationOptions.Clean);
                    LosslessUtils.ExpandColorMap((int)numColors, colorMap.GetSpan(), newColorMap.GetSpan());
                    transform.Data = newColorMap;
                }

                break;

            case Vp8LTransformType.PredictorTransform:
            case Vp8LTransformType.CrossColorTransform:
            {
                // The first 3 bits of prediction data define the block width and height in number of bits.
                transform.Bits = (int)this.bitReader.ReadValue(3) + 2;
                int blockWidth  = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits);
                int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits);
                IMemoryOwner <uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);
                transform.Data = transformData;
                break;
            }
            }

            decoder.Transforms.Add(transform);
        }
Exemplo n.º 13
0
        public static void GetHistoImageSymbols(int xSize, int ySize, Vp8LBackwardRefs refs, int quality, int histoBits, int cacheBits, List <Vp8LHistogram> imageHisto, Vp8LHistogram tmpHisto, ushort[] histogramSymbols)
        {
            int histoXSize            = histoBits > 0 ? LosslessUtils.SubSampleSize(xSize, histoBits) : 1;
            int histoYSize            = histoBits > 0 ? LosslessUtils.SubSampleSize(ySize, histoBits) : 1;
            int imageHistoRawSize     = histoXSize * histoYSize;
            int entropyCombineNumBins = BinSize;

            ushort[] mapTmp          = new ushort[imageHistoRawSize];
            ushort[] clusterMappings = new ushort[imageHistoRawSize];
            var      origHisto       = new List <Vp8LHistogram>(imageHistoRawSize);

            for (int i = 0; i < imageHistoRawSize; i++)
            {
                origHisto.Add(new Vp8LHistogram(cacheBits));
            }

            // Construct the histograms from the backward references.
            HistogramBuild(xSize, histoBits, refs, origHisto);

            // Copies the histograms and computes its bitCost. histogramSymbols is optimized.
            int numUsed = HistogramCopyAndAnalyze(origHisto, imageHisto, histogramSymbols);

            bool entropyCombine = numUsed > entropyCombineNumBins * 2 && quality < 100;

            if (entropyCombine)
            {
                ushort[] binMap            = mapTmp;
                int      numClusters       = numUsed;
                double   combineCostFactor = GetCombineCostFactor(imageHistoRawSize, quality);
                HistogramAnalyzeEntropyBin(imageHisto, binMap);

                // Collapse histograms with similar entropy.
                HistogramCombineEntropyBin(imageHisto, histogramSymbols, clusterMappings, tmpHisto, binMap, entropyCombineNumBins, combineCostFactor);

                OptimizeHistogramSymbols(clusterMappings, numClusters, mapTmp, histogramSymbols);
            }

            float x = quality / 100.0f;

            // Cubic ramp between 1 and MaxHistoGreedy:
            int  thresholdSize = (int)(1 + (x * x * x * (MaxHistoGreedy - 1)));
            bool doGreedy      = HistogramCombineStochastic(imageHisto, thresholdSize);

            if (doGreedy)
            {
                RemoveEmptyHistograms(imageHisto);
                HistogramCombineGreedy(imageHisto);
            }

            // Find the optimal map from original histograms to the final ones.
            RemoveEmptyHistograms(imageHisto);
            HistogramRemap(origHisto, imageHisto, histogramSymbols);
        }
Exemplo n.º 14
0
        private static void BackwardReferencesRle(int xSize, int ySize, ReadOnlySpan <uint> bgra, int cacheBits, Vp8LBackwardRefs refs)
        {
            int  pixelCount    = xSize * ySize;
            bool useColorCache = cacheBits > 0;
            var  colorCache    = new ColorCache();

            if (useColorCache)
            {
                colorCache.Init(cacheBits);
            }

            refs.Refs.Clear();

            // Add first pixel as literal.
            AddSingleLiteral(bgra[0], useColorCache, colorCache, refs);
            int i = 1;

            while (i < pixelCount)
            {
                int maxLen     = LosslessUtils.MaxFindCopyLength(pixelCount - i);
                int rleLen     = LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - 1), 0, maxLen);
                int prevRowLen = i < xSize ? 0 : LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - xSize), 0, maxLen);
                if (rleLen >= prevRowLen && rleLen >= MinLength)
                {
                    refs.Add(PixOrCopy.CreateCopy(1, (ushort)rleLen));

                    // We don't need to update the color cache here since it is always the
                    // same pixel being copied, and that does not change the color cache state.
                    i += rleLen;
                }
                else if (prevRowLen >= MinLength)
                {
                    refs.Add(PixOrCopy.CreateCopy((uint)xSize, (ushort)prevRowLen));
                    if (useColorCache)
                    {
                        for (int k = 0; k < prevRowLen; ++k)
                        {
                            colorCache.Insert(bgra[i + k]);
                        }
                    }

                    i += prevRowLen;
                }
                else
                {
                    AddSingleLiteral(bgra[i], useColorCache, colorCache, refs);
                    i++;
                }
            }
        }
Exemplo n.º 15
0
        private static void RunPredictor12Test()
        {
            // arrange
            uint[] topData        = { 4294844413, 4294779388 };
            uint   left           = 4294844413;
            uint   expectedResult = 4294779388;

            // act
            unsafe
            {
                fixed(uint *top = &topData[1])
                {
                    uint actual = LosslessUtils.Predictor12(left, top);

                    // assert
                    Assert.Equal(expectedResult, actual);
                }
            }
        }
Exemplo n.º 16
0
        /// <summary>
        /// Construct the histograms from the backward references.
        /// </summary>
        private static void HistogramBuild(int xSize, int histoBits, Vp8LBackwardRefs backwardRefs, List <Vp8LHistogram> histograms)
        {
            int x = 0, y = 0;
            int histoXSize = LosslessUtils.SubSampleSize(xSize, histoBits);

            using List <PixOrCopy> .Enumerator backwardRefsEnumerator = backwardRefs.Refs.GetEnumerator();
            while (backwardRefsEnumerator.MoveNext())
            {
                PixOrCopy v  = backwardRefsEnumerator.Current;
                int       ix = ((y >> histoBits) * histoXSize) + (x >> histoBits);
                histograms[ix].AddSinglePixOrCopy(v, false);
                x += v.Len;
                while (x >= xSize)
                {
                    x -= xSize;
                    y++;
                }
            }
        }
Exemplo n.º 17
0
        private static void RunPredictor13Test()
        {
            // arrange
            uint[] topData        = { 4278193922, 4278193666 };
            uint   left           = 4278193410;
            uint   expectedResult = 4278193154;

            // act
            unsafe
            {
                fixed(uint *top = &topData[1])
                {
                    uint actual = LosslessUtils.Predictor13(left, top);

                    // assert
                    Assert.Equal(expectedResult, actual);
                }
            }
        }
Exemplo n.º 18
0
        public static void CollectColorBlueTransforms(Span <uint> bgra, int stride, int tileWidth, int tileHeight, int greenToBlue, int redToBlue, Span <int> histo)
        {
#if SUPPORTS_RUNTIME_INTRINSICS
            if (Avx2.IsSupported && tileWidth >= 16)
            {
                const int     span   = 16;
                Span <ushort> values = stackalloc ushort[span];
                var           multsr = Vector256.Create(LosslessUtils.Cst5b(redToBlue));
                var           multsg = Vector256.Create(LosslessUtils.Cst5b(greenToBlue));
                for (int y = 0; y < tileHeight; y++)
                {
                    Span <uint> srcSpan  = bgra.Slice(y * stride);
                    ref uint    inputRef = ref MemoryMarshal.GetReference(srcSpan);
                    for (nint x = 0; x <= tileWidth - span; x += span)
                    {
                        nint               input0Idx = x;
                        nint               input1Idx = x + (span / 2);
                        Vector256 <byte>   input0    = Unsafe.As <uint, Vector256 <uint> >(ref Unsafe.Add(ref inputRef, input0Idx)).AsByte();
                        Vector256 <byte>   input1    = Unsafe.As <uint, Vector256 <uint> >(ref Unsafe.Add(ref inputRef, input1Idx)).AsByte();
                        Vector256 <byte>   r0        = Avx2.Shuffle(input0, CollectColorBlueTransformsShuffleLowMask256);
                        Vector256 <byte>   r1        = Avx2.Shuffle(input1, CollectColorBlueTransformsShuffleHighMask256);
                        Vector256 <byte>   r         = Avx2.Or(r0, r1);
                        Vector256 <byte>   gb0       = Avx2.And(input0, CollectColorBlueTransformsGreenBlueMask256);
                        Vector256 <byte>   gb1       = Avx2.And(input1, CollectColorBlueTransformsGreenBlueMask256);
                        Vector256 <ushort> gb        = Avx2.PackUnsignedSaturate(gb0.AsInt32(), gb1.AsInt32());
                        Vector256 <byte>   g         = Avx2.And(gb.AsByte(), CollectColorBlueTransformsGreenMask256);
                        Vector256 <short>  a         = Avx2.MultiplyHigh(r.AsInt16(), multsr);
                        Vector256 <short>  b         = Avx2.MultiplyHigh(g.AsInt16(), multsg);
                        Vector256 <byte>   c         = Avx2.Subtract(gb.AsByte(), b.AsByte());
                        Vector256 <byte>   d         = Avx2.Subtract(c, a.AsByte());
                        Vector256 <byte>   e         = Avx2.And(d, CollectColorBlueTransformsBlueMask256);

                        ref ushort outputRef = ref MemoryMarshal.GetReference(values);
                        Unsafe.As <ushort, Vector256 <ushort> >(ref outputRef) = e.AsUInt16();

                        for (int i = 0; i < span; i++)
                        {
                            ++histo[values[i]];
                        }
                    }
                }
Exemplo n.º 19
0
        public void BitsEntropyUnrefined(Span <uint> array, int n)
        {
            this.Init();

            for (int i = 0; i < n; i++)
            {
                if (array[i] != 0)
                {
                    this.Sum         += array[i];
                    this.NoneZeroCode = (uint)i;
                    this.NoneZeros++;
                    this.Entropy -= LosslessUtils.FastSLog2(array[i]);
                    if (this.MaxVal < array[i])
                    {
                        this.MaxVal = array[i];
                    }
                }
            }

            this.Entropy += LosslessUtils.FastSLog2(this.Sum);
        }
Exemplo n.º 20
0
        private static void RunPredictor11Test()
        {
            // arrange
            uint[] topData = { 4278258949, 4278258949 };
            uint   left    = 4294839812;

            short[] scratch        = new short[8];
            uint    expectedResult = 4294839812;

            // act
            unsafe
            {
                fixed(uint *top = &topData[1])
                {
                    uint actual = LosslessUtils.Predictor11(left, top, scratch);

                    // assert
                    Assert.Equal(expectedResult, actual);
                }
            }
        }
Exemplo n.º 21
0
        public void GetEntropyUnrefined(uint[] x, int length, Vp8LStreaks stats)
        {
            int  i;
            int  iPrev = 0;
            uint xPrev = x[0];

            this.Init();

            for (i = 1; i < length; i++)
            {
                uint xi = x[i];
                if (xi != xPrev)
                {
                    this.GetEntropyUnrefined(xi, i, ref xPrev, ref iPrev, stats);
                }
            }

            this.GetEntropyUnrefined(0, i, ref xPrev, ref iPrev, stats);

            this.Entropy += LosslessUtils.FastSLog2(this.Sum);
        }
Exemplo n.º 22
0
        public static void ApplyNearLossless(int xSize, int ySize, int quality, Span <uint> argbSrc, Span <uint> argbDst, int stride)
        {
            uint[] copyBuffer = new uint[xSize * 3];
            int    limitBits  = LosslessUtils.NearLosslessBits(quality);

            // For small icon images, don't attempt to apply near-lossless compression.
            if ((xSize < MinDimForNearLossless && ySize < MinDimForNearLossless) || ySize < 3)
            {
                for (int i = 0; i < ySize; i++)
                {
                    argbSrc.Slice(i * stride, xSize).CopyTo(argbDst.Slice(i * xSize, xSize));
                }

                return;
            }

            NearLossless(xSize, ySize, argbSrc, stride, limitBits, copyBuffer, argbDst);
            for (int i = limitBits - 1; i != 0; i--)
            {
                NearLossless(xSize, ySize, argbDst, xSize, i, copyBuffer, argbDst);
            }
        }
Exemplo n.º 23
0
#pragma warning restore SA1503 // Braces should not be omitted

        /// <summary>
        /// Quantize every component of the difference between the actual pixel value and
        /// its prediction to a multiple of a quantization (a power of 2, not larger than
        /// maxQuantization which is a power of 2, smaller than maxDiff). Take care if
        /// value and predict have undergone subtract green, which means that red and
        /// blue are represented as offsets from green.
        /// </summary>
        private static uint NearLossless(uint value, uint predict, int maxQuantization, int maxDiff, bool usedSubtractGreen)
        {
            byte newGreen  = 0;
            byte greenDiff = 0;
            byte a;

            if (maxDiff <= 2)
            {
                return(LosslessUtils.SubPixels(value, predict));
            }

            int quantization = maxQuantization;

            while (quantization >= maxDiff)
            {
                quantization >>= 1;
            }

            if (value >> 24 is 0 or 0xff)
            {
                // Preserve transparency of fully transparent or fully opaque pixels.
                a = NearLosslessDiff((byte)((value >> 24) & 0xff), (byte)((predict >> 24) & 0xff));
            }
Exemplo n.º 24
0
        private void GetEntropyUnrefined(uint val, int i, ref uint valPrev, ref int iPrev, Vp8LStreaks stats)
        {
            int streak = i - iPrev;

            // Gather info for the bit entropy.
            if (valPrev != 0)
            {
                this.Sum         += (uint)(valPrev * streak);
                this.NoneZeros   += streak;
                this.NoneZeroCode = (uint)iPrev;
                this.Entropy     -= LosslessUtils.FastSLog2(valPrev) * streak;
                if (this.MaxVal < valPrev)
                {
                    this.MaxVal = valPrev;
                }
            }

            // Gather info for the Huffman cost.
            stats.Counts[valPrev != 0 ? 1 : 0] += streak > 3 ? 1 : 0;
            stats.Streaks[valPrev != 0 ? 1 : 0][streak > 3 ? 1 : 0] += streak;

            valPrev = val;
            iPrev   = i;
        }
Exemplo n.º 25
0
        /// <summary>
        /// Evaluate optimal cache bits for the local color cache.
        /// The input bestCacheBits sets the maximum cache bits to use (passing 0 implies disabling the local color cache).
        /// The local color cache is also disabled for the lower (smaller then 25) quality.
        /// </summary>
        /// <returns>Best cache size.</returns>
        private static int CalculateBestCacheSize(ReadOnlySpan <uint> bgra, int quality, Vp8LBackwardRefs refs, int bestCacheBits)
        {
            int cacheBitsMax = quality <= 25 ? 0 : bestCacheBits;

            if (cacheBitsMax == 0)
            {
                // Local color cache is disabled.
                return(0);
            }

            double entropyMin = MaxEntropy;
            int    pos        = 0;
            var    colorCache = new ColorCache[WebpConstants.MaxColorCacheBits + 1];
            var    histos     = new Vp8LHistogram[WebpConstants.MaxColorCacheBits + 1];

            for (int i = 0; i <= WebpConstants.MaxColorCacheBits; i++)
            {
                histos[i]     = new Vp8LHistogram(paletteCodeBits: i);
                colorCache[i] = new ColorCache();
                colorCache[i].Init(i);
            }

            // Find the cacheBits giving the lowest entropy.
            for (int idx = 0; idx < refs.Refs.Count; idx++)
            {
                PixOrCopy v = refs.Refs[idx];
                if (v.IsLiteral())
                {
                    uint pix = bgra[pos++];
                    uint a   = (pix >> 24) & 0xff;
                    uint r   = (pix >> 16) & 0xff;
                    uint g   = (pix >> 8) & 0xff;
                    uint b   = (pix >> 0) & 0xff;

                    // The keys of the caches can be derived from the longest one.
                    int key = ColorCache.HashPix(pix, 32 - cacheBitsMax);

                    // Do not use the color cache for cacheBits = 0.
                    ++histos[0].Blue[b];
                    ++histos[0].Literal[g];
                    ++histos[0].Red[r];
                    ++histos[0].Alpha[a];

                    // Deal with cacheBits > 0.
                    for (int i = cacheBitsMax; i >= 1; --i, key >>= 1)
                    {
                        if (colorCache[i].Lookup(key) == pix)
                        {
                            ++histos[i].Literal[WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + key];
                        }
                        else
                        {
                            colorCache[i].Set((uint)key, pix);
                            ++histos[i].Blue[b];
                            ++histos[i].Literal[g];
                            ++histos[i].Red[r];
                            ++histos[i].Alpha[a];
                        }
                    }
                }
                else
                {
                    // We should compute the contribution of the (distance, length)
                    // histograms but those are the same independently from the cache size.
                    // As those constant contributions are in the end added to the other
                    // histogram contributions, we can ignore them, except for the length
                    // prefix that is part of the literal_ histogram.
                    int  len      = v.Len;
                    uint bgraPrev = bgra[pos] ^ 0xffffffffu;

                    int extraBits = 0, extraBitsValue = 0;
                    int code = LosslessUtils.PrefixEncode(len, ref extraBits, ref extraBitsValue);
                    for (int i = 0; i <= cacheBitsMax; i++)
                    {
                        ++histos[i].Literal[WebpConstants.NumLiteralCodes + code];
                    }

                    // Update the color caches.
                    do
                    {
                        if (bgra[pos] != bgraPrev)
                        {
                            // Efficiency: insert only if the color changes.
                            int key = ColorCache.HashPix(bgra[pos], 32 - cacheBitsMax);
                            for (int i = cacheBitsMax; i >= 1; --i, key >>= 1)
                            {
                                colorCache[i].Colors[key] = bgra[pos];
                            }

                            bgraPrev = bgra[pos];
                        }

                        pos++;
                    }while (--len != 0);
                }
            }

            var stats       = new Vp8LStreaks();
            var bitsEntropy = new Vp8LBitEntropy();

            for (int i = 0; i <= cacheBitsMax; i++)
            {
                double entropy = histos[i].EstimateBits(stats, bitsEntropy);
                if (i == 0 || entropy < entropyMin)
                {
                    entropyMin    = entropy;
                    bestCacheBits = i;
                }
            }

            return(bestCacheBits);
        }
Exemplo n.º 26
0
        public void Fill(ReadOnlySpan <uint> bgra, int quality, int xSize, int ySize, bool lowEffort)
        {
            int size       = xSize * ySize;
            int iterMax    = GetMaxItersForQuality(quality);
            int windowSize = GetWindowSizeForHashChain(quality, xSize);
            int pos;

            if (size <= 2)
            {
                this.OffsetLength.GetSpan()[0] = 0;
                return;
            }

            using IMemoryOwner <int> hashToFirstIndexBuffer = this.memoryAllocator.Allocate <int>(HashSize);
            using IMemoryOwner <int> chainBuffer            = this.memoryAllocator.Allocate <int>(size, AllocationOptions.Clean);
            Span <int> hashToFirstIndex = hashToFirstIndexBuffer.GetSpan();
            Span <int> chain            = chainBuffer.GetSpan();

            // Initialize hashToFirstIndex array to -1.
            hashToFirstIndex.Fill(-1);

            // Fill the chain linking pixels with the same hash.
            bool        bgraComp = bgra.Length > 1 && bgra[0] == bgra[1];
            Span <uint> tmp      = stackalloc uint[2];

            for (pos = 0; pos < size - 2;)
            {
                uint hashCode;
                bool bgraCompNext = bgra[pos + 1] == bgra[pos + 2];
                if (bgraComp && bgraCompNext)
                {
                    // Consecutive pixels with the same color will share the same hash.
                    // We therefore use a different hash: the color and its repetition length.
                    tmp.Clear();
                    uint len = 1;
                    tmp[0] = bgra[pos];

                    // Figure out how far the pixels are the same. The last pixel has a different 64 bit hash,
                    // as its next pixel does not have the same color, so we just need to get to
                    // the last pixel equal to its follower.
                    while (pos + (int)len + 2 < size && bgra[(int)(pos + len + 2)] == bgra[pos])
                    {
                        ++len;
                    }

                    if (len > BackwardReferenceEncoder.MaxLength)
                    {
                        // Skip the pixels that match for distance=1 and length>MaxLength
                        // because they are linked to their predecessor and we automatically
                        // check that in the main for loop below. Skipping means setting no
                        // predecessor in the chain, hence -1.
                        pos += (int)(len - BackwardReferenceEncoder.MaxLength);
                        len  = BackwardReferenceEncoder.MaxLength;
                    }

                    // Process the rest of the hash chain.
                    while (len > 0)
                    {
                        tmp[1]     = len--;
                        hashCode   = GetPixPairHash64(tmp);
                        chain[pos] = hashToFirstIndex[(int)hashCode];
                        hashToFirstIndex[(int)hashCode] = pos++;
                    }

                    bgraComp = false;
                }
                else
                {
                    // Just move one pixel forward.
                    hashCode   = GetPixPairHash64(bgra.Slice(pos));
                    chain[pos] = hashToFirstIndex[(int)hashCode];
                    hashToFirstIndex[(int)hashCode] = pos++;
                    bgraComp = bgraCompNext;
                }
            }

            // Process the penultimate pixel.
            chain[pos] = hashToFirstIndex[(int)GetPixPairHash64(bgra.Slice(pos))];

            // Find the best match interval at each pixel, defined by an offset to the
            // pixel and a length. The right-most pixel cannot match anything to the right
            // (hence a best length of 0) and the left-most pixel nothing to the left (hence an offset of 0).
            Span <uint> offsetLength = this.OffsetLength.GetSpan();

            offsetLength[0] = offsetLength[size - 1] = 0;
            for (int basePosition = size - 2; basePosition > 0;)
            {
                int  maxLen       = LosslessUtils.MaxFindCopyLength(size - 1 - basePosition);
                int  bgraStart    = basePosition;
                int  iter         = iterMax;
                int  bestLength   = 0;
                uint bestDistance = 0;
                int  minPos       = basePosition > windowSize ? basePosition - windowSize : 0;
                int  lengthMax    = maxLen < 256 ? maxLen : 256;
                pos = chain[basePosition];
                int currLength;

                if (!lowEffort)
                {
                    // Heuristic: use the comparison with the above line as an initialization.
                    if (basePosition >= (uint)xSize)
                    {
                        currLength = LosslessUtils.FindMatchLength(bgra.Slice(bgraStart - xSize), bgra.Slice(bgraStart), bestLength, maxLen);
                        if (currLength > bestLength)
                        {
                            bestLength   = currLength;
                            bestDistance = (uint)xSize;
                        }

                        iter--;
                    }

                    // Heuristic: compare to the previous pixel.
                    currLength = LosslessUtils.FindMatchLength(bgra.Slice(bgraStart - 1), bgra.Slice(bgraStart), bestLength, maxLen);
                    if (currLength > bestLength)
                    {
                        bestLength   = currLength;
                        bestDistance = 1;
                    }

                    iter--;

                    // Skip the for loop if we already have the maximum.
                    if (bestLength == BackwardReferenceEncoder.MaxLength)
                    {
                        pos = minPos - 1;
                    }
                }

                uint bestBgra = bgra.Slice(bgraStart)[bestLength];

                for (; pos >= minPos && (--iter > 0); pos = chain[pos])
                {
                    if (bgra[pos + bestLength] != bestBgra)
                    {
                        continue;
                    }

                    currLength = LosslessUtils.VectorMismatch(bgra.Slice(pos), bgra.Slice(bgraStart), maxLen);
                    if (bestLength < currLength)
                    {
                        bestLength   = currLength;
                        bestDistance = (uint)(basePosition - pos);
                        bestBgra     = bgra.Slice(bgraStart)[bestLength];

                        // Stop if we have reached a good enough length.
                        if (bestLength >= lengthMax)
                        {
                            break;
                        }
                    }
                }

                // We have the best match but in case the two intervals continue matching
                // to the left, we have the best matches for the left-extended pixels.
                uint maxBasePosition = (uint)basePosition;
                while (true)
                {
                    offsetLength[basePosition] = (bestDistance << BackwardReferenceEncoder.MaxLengthBits) | (uint)bestLength;
                    --basePosition;

                    // Stop if we don't have a match or if we are out of bounds.
                    if (bestDistance == 0 || basePosition == 0)
                    {
                        break;
                    }

                    // Stop if we cannot extend the matching intervals to the left.
                    if (basePosition < bestDistance || bgra[(int)(basePosition - bestDistance)] != bgra[basePosition])
                    {
                        break;
                    }

                    // Stop if we are matching at its limit because there could be a closer
                    // matching interval with the same maximum length. Then again, if the
                    // matching interval is as close as possible (best_distance == 1), we will
                    // never find anything better so let's continue.
                    if (bestLength == BackwardReferenceEncoder.MaxLength && bestDistance != 1 && basePosition + BackwardReferenceEncoder.MaxLength < maxBasePosition)
                    {
                        break;
                    }

                    if (bestLength < BackwardReferenceEncoder.MaxLength)
                    {
                        bestLength++;
                        maxBasePosition = (uint)basePosition;
                    }
                }
            }
        }
Exemplo n.º 27
0
        /// <summary>
        /// Stores the difference between the pixel and its prediction in "output".
        /// In case of a lossy encoding, updates the source image to avoid propagating
        /// the deviation further to pixels which depend on the current pixel for their
        /// predictions.
        /// </summary>
        private static void GetResidual(
            int width,
            int height,
            Span <uint> upperRowSpan,
            Span <uint> currentRowSpan,
            Span <byte> maxDiffs,
            int mode,
            int xStart,
            int xEnd,
            int y,
            int maxQuantization,
            WebpTransparentColorMode transparentColorMode,
            bool usedSubtractGreen,
            bool nearLossless,
            Span <uint> output,
            Span <short> scratch)
        {
            if (transparentColorMode == WebpTransparentColorMode.Preserve)
            {
                PredictBatch(mode, xStart, y, xEnd - xStart, currentRowSpan, upperRowSpan, output, scratch);
            }
            else
            {
#pragma warning disable SA1503 // Braces should not be omitted
                fixed(uint *currentRow = currentRowSpan)
                fixed(uint *upperRow = upperRowSpan)
                {
                    for (int x = xStart; x < xEnd; x++)
                    {
                        uint predict = 0;
                        uint residual;
                        if (y == 0)
                        {
                            predict = x == 0 ? WebpConstants.ArgbBlack : currentRow[x - 1];  // Left.
                        }
                        else if (x == 0)
                        {
                            predict = upperRow[x];  // Top.
                        }
                        else
                        {
                            switch (mode)
                            {
                            case 0:
                                predict = WebpConstants.ArgbBlack;
                                break;

                            case 1:
                                predict = currentRow[x - 1];
                                break;

                            case 2:
                                predict = LosslessUtils.Predictor2(currentRow[x - 1], upperRow + x);
                                break;

                            case 3:
                                predict = LosslessUtils.Predictor3(currentRow[x - 1], upperRow + x);
                                break;

                            case 4:
                                predict = LosslessUtils.Predictor4(currentRow[x - 1], upperRow + x);
                                break;

                            case 5:
                                predict = LosslessUtils.Predictor5(currentRow[x - 1], upperRow + x);
                                break;

                            case 6:
                                predict = LosslessUtils.Predictor6(currentRow[x - 1], upperRow + x);
                                break;

                            case 7:
                                predict = LosslessUtils.Predictor7(currentRow[x - 1], upperRow + x);
                                break;

                            case 8:
                                predict = LosslessUtils.Predictor8(currentRow[x - 1], upperRow + x);
                                break;

                            case 9:
                                predict = LosslessUtils.Predictor9(currentRow[x - 1], upperRow + x);
                                break;

                            case 10:
                                predict = LosslessUtils.Predictor10(currentRow[x - 1], upperRow + x);
                                break;

                            case 11:
                                predict = LosslessUtils.Predictor11(currentRow[x - 1], upperRow + x, scratch);
                                break;

                            case 12:
                                predict = LosslessUtils.Predictor12(currentRow[x - 1], upperRow + x);
                                break;

                            case 13:
                                predict = LosslessUtils.Predictor13(currentRow[x - 1], upperRow + x);
                                break;
                            }
                        }

                        if (nearLossless)
                        {
                            if (maxQuantization == 1 || mode == 0 || y == 0 || y == height - 1 || x == 0 || x == width - 1)
                            {
                                residual = LosslessUtils.SubPixels(currentRow[x], predict);
                            }
                            else
                            {
                                residual = NearLossless(currentRow[x], predict, maxQuantization, maxDiffs[x], usedSubtractGreen);

                                // Update the source image.
                                currentRow[x] = LosslessUtils.AddPixels(predict, residual);

                                // x is never 0 here so we do not need to update upperRow like below.
                            }
                        }
                        else
                        {
                            residual = LosslessUtils.SubPixels(currentRow[x], predict);
                        }

                        if ((currentRow[x] & MaskAlpha) == 0)
                        {
                            // If alpha is 0, cleanup RGB. We can choose the RGB values of the
                            // residual for best compression. The prediction of alpha itself can be
                            // non-zero and must be kept though. We choose RGB of the residual to be
                            // 0.
                            residual &= MaskAlpha;

                            // Update the source image.
                            currentRow[x] = predict & ~MaskAlpha;

                            // The prediction for the rightmost pixel in a row uses the leftmost
                            // pixel
                            // in that row as its top-right context pixel. Hence if we change the
                            // leftmost pixel of current_row, the corresponding change must be
                            // applied
                            // to upperRow as well where top-right context is being read from.
                            if (x == 0 && y != 0)
                            {
                                upperRow[width] = currentRow[0];
                            }
                        }

                        output[x - xStart] = residual;
                    }
                }
            }
        }
Exemplo n.º 28
0
        /// <summary>
        /// Finds the best predictor for each tile, and converts the image to residuals
        /// with respect to predictions. If nearLosslessQuality &lt; 100, applies
        /// near lossless processing, shaving off more bits of residuals for lower qualities.
        /// </summary>
        public static void ResidualImage(
            int width,
            int height,
            int bits,
            Span <uint> bgra,
            Span <uint> bgraScratch,
            Span <uint> image,
            int[][] histoArgb,
            int[][] bestHisto,
            bool nearLossless,
            int nearLosslessQuality,
            WebpTransparentColorMode transparentColorMode,
            bool usedSubtractGreen,
            bool lowEffort)
        {
            int          tilesPerRow     = LosslessUtils.SubSampleSize(width, bits);
            int          tilesPerCol     = LosslessUtils.SubSampleSize(height, bits);
            int          maxQuantization = 1 << LosslessUtils.NearLosslessBits(nearLosslessQuality);
            Span <short> scratch         = stackalloc short[8];

            // TODO: Can we optimize this?
            int[][] histo = new int[4][];
            for (int i = 0; i < 4; i++)
            {
                histo[i] = new int[256];
            }

            if (lowEffort)
            {
                for (int i = 0; i < tilesPerRow * tilesPerCol; i++)
                {
                    image[i] = WebpConstants.ArgbBlack | (PredLowEffort << 8);
                }
            }
            else
            {
                for (int tileY = 0; tileY < tilesPerCol; tileY++)
                {
                    for (int tileX = 0; tileX < tilesPerRow; tileX++)
                    {
                        int pred = GetBestPredictorForTile(
                            width,
                            height,
                            tileX,
                            tileY,
                            bits,
                            histo,
                            bgraScratch,
                            bgra,
                            histoArgb,
                            bestHisto,
                            maxQuantization,
                            transparentColorMode,
                            usedSubtractGreen,
                            nearLossless,
                            image,
                            scratch);

                        image[(tileY * tilesPerRow) + tileX] = (uint)(WebpConstants.ArgbBlack | (pred << 8));
                    }
                }
            }

            CopyImageWithPrediction(
                width,
                height,
                bits,
                image,
                bgraScratch,
                bgra,
                maxQuantization,
                transparentColorMode,
                usedSubtractGreen,
                nearLossless,
                lowEffort);
        }
Exemplo n.º 29
0
        public IMemoryOwner <uint> DecodeImageStream(Vp8LDecoder decoder, int xSize, int ySize, bool isLevel0)
        {
            int transformXSize            = xSize;
            int transformYSize            = ySize;
            int numberOfTransformsPresent = 0;

            if (isLevel0)
            {
                decoder.Transforms = new List <Vp8LTransform>(WebpConstants.MaxNumberOfTransforms);

                // Next bit indicates, if a transformation is present.
                while (this.bitReader.ReadBit())
                {
                    if (numberOfTransformsPresent > WebpConstants.MaxNumberOfTransforms)
                    {
                        WebpThrowHelper.ThrowImageFormatException($"The maximum number of transforms of {WebpConstants.MaxNumberOfTransforms} was exceeded");
                    }

                    this.ReadTransformation(transformXSize, transformYSize, decoder);
                    if (decoder.Transforms[numberOfTransformsPresent].TransformType == Vp8LTransformType.ColorIndexingTransform)
                    {
                        transformXSize = LosslessUtils.SubSampleSize(transformXSize, decoder.Transforms[numberOfTransformsPresent].Bits);
                    }

                    numberOfTransformsPresent++;
                }
            }
            else
            {
                decoder.Metadata = new Vp8LMetadata();
            }

            // Color cache.
            bool isColorCachePresent = this.bitReader.ReadBit();
            int  colorCacheBits      = 0;
            int  colorCacheSize      = 0;

            if (isColorCachePresent)
            {
                colorCacheBits = (int)this.bitReader.ReadValue(4);

                // Note: According to webpinfo color cache bits of 11 are valid, even though 10 is defined in the source code as maximum.
                // That is why 11 bits is also considered valid here.
                bool colorCacheBitsIsValid = colorCacheBits is >= 1 and <= WebpConstants.MaxColorCacheBits + 1;
                if (!colorCacheBitsIsValid)
                {
                    WebpThrowHelper.ThrowImageFormatException("Invalid color cache bits found");
                }
            }

            // Read the Huffman codes (may recurse).
            this.ReadHuffmanCodes(decoder, transformXSize, transformYSize, colorCacheBits, isLevel0);
            decoder.Metadata.ColorCacheSize = colorCacheSize;

            // Finish setting up the color-cache.
            if (isColorCachePresent)
            {
                decoder.Metadata.ColorCache = new ColorCache();
                colorCacheSize = 1 << colorCacheBits;
                decoder.Metadata.ColorCacheSize = colorCacheSize;
                decoder.Metadata.ColorCache.Init(colorCacheBits);
            }
            else
            {
                decoder.Metadata.ColorCacheSize = 0;
            }

            this.UpdateDecoder(decoder, transformXSize, transformYSize);
            if (isLevel0)
            {
                // level 0 complete.
                return(null);
            }

            // Use the Huffman trees to decode the LZ77 encoded data.
            IMemoryOwner <uint> pixelData = this.memoryAllocator.Allocate <uint>(decoder.Width * decoder.Height, AllocationOptions.Clean);

            this.DecodeImageData(decoder, pixelData.GetSpan());

            return(pixelData);
        }
Exemplo n.º 30
0
        private void ReadHuffmanCodes(Vp8LDecoder decoder, int xSize, int ySize, int colorCacheBits, bool allowRecursion)
        {
            int maxAlphabetSize   = 0;
            int numHTreeGroups    = 1;
            int numHTreeGroupsMax = 1;

            // If the next bit is zero, there is only one meta Huffman code used everywhere in the image. No more data is stored.
            // If this bit is one, the image uses multiple meta Huffman codes. These meta Huffman codes are stored as an entropy image.
            if (allowRecursion && this.bitReader.ReadBit())
            {
                // Use meta Huffman codes.
                int huffmanPrecision = (int)(this.bitReader.ReadValue(3) + 2);
                int huffmanXSize     = LosslessUtils.SubSampleSize(xSize, huffmanPrecision);
                int huffmanYSize     = LosslessUtils.SubSampleSize(ySize, huffmanPrecision);
                int huffmanPixels    = huffmanXSize * huffmanYSize;

                IMemoryOwner <uint> huffmanImage     = this.DecodeImageStream(decoder, huffmanXSize, huffmanYSize, false);
                Span <uint>         huffmanImageSpan = huffmanImage.GetSpan();
                decoder.Metadata.HuffmanSubSampleBits = huffmanPrecision;

                // TODO: Isn't huffmanPixels the length of the span?
                for (int i = 0; i < huffmanPixels; i++)
                {
                    // The huffman data is stored in red and green bytes.
                    uint group = (huffmanImageSpan[i] >> 8) & 0xffff;
                    huffmanImageSpan[i] = group;
                    if (group >= numHTreeGroupsMax)
                    {
                        numHTreeGroupsMax = (int)group + 1;
                    }
                }

                numHTreeGroups = numHTreeGroupsMax;
                decoder.Metadata.HuffmanImage = huffmanImage;
            }

            // Find maximum alphabet size for the hTree group.
            for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++)
            {
                int alphabetSize = WebpConstants.AlphabetSize[j];
                if (j == 0 && colorCacheBits > 0)
                {
                    alphabetSize += 1 << colorCacheBits;
                }

                if (maxAlphabetSize < alphabetSize)
                {
                    maxAlphabetSize = alphabetSize;
                }
            }

            int tableSize     = TableSize[colorCacheBits];
            var huffmanTables = new HuffmanCode[numHTreeGroups * tableSize];
            var hTreeGroups   = new HTreeGroup[numHTreeGroups];
            Span <HuffmanCode> huffmanTable = huffmanTables.AsSpan();

            int[] codeLengths = new int[maxAlphabetSize];
            for (int i = 0; i < numHTreeGroupsMax; i++)
            {
                hTreeGroups[i] = new HTreeGroup(HuffmanUtils.HuffmanPackedTableSize);
                HTreeGroup hTreeGroup       = hTreeGroups[i];
                int        totalSize        = 0;
                bool       isTrivialLiteral = true;
                int        maxBits          = 0;
                codeLengths.AsSpan().Clear();
                for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++)
                {
                    int alphabetSize = WebpConstants.AlphabetSize[j];
                    if (j == 0 && colorCacheBits > 0)
                    {
                        alphabetSize += 1 << colorCacheBits;
                    }

                    int size = this.ReadHuffmanCode(alphabetSize, codeLengths, huffmanTable);
                    if (size == 0)
                    {
                        WebpThrowHelper.ThrowImageFormatException("Huffman table size is zero");
                    }

                    // TODO: Avoid allocation.
                    hTreeGroup.HTrees.Add(huffmanTable.Slice(0, size).ToArray());

                    HuffmanCode huffTableZero = huffmanTable[0];
                    if (isTrivialLiteral && LiteralMap[j] == 1)
                    {
                        isTrivialLiteral = huffTableZero.BitsUsed == 0;
                    }

                    totalSize   += huffTableZero.BitsUsed;
                    huffmanTable = huffmanTable.Slice(size);

                    if (j <= HuffIndex.Alpha)
                    {
                        int localMaxBits = codeLengths[0];
                        int k;
                        for (k = 1; k < alphabetSize; ++k)
                        {
                            int codeLengthK = codeLengths[k];
                            if (codeLengthK > localMaxBits)
                            {
                                localMaxBits = codeLengthK;
                            }
                        }

                        maxBits += localMaxBits;
                    }
                }

                hTreeGroup.IsTrivialLiteral = isTrivialLiteral;
                hTreeGroup.IsTrivialCode    = false;
                if (isTrivialLiteral)
                {
                    uint red   = hTreeGroup.HTrees[HuffIndex.Red][0].Value;
                    uint blue  = hTreeGroup.HTrees[HuffIndex.Blue][0].Value;
                    uint green = hTreeGroup.HTrees[HuffIndex.Green][0].Value;
                    uint alpha = hTreeGroup.HTrees[HuffIndex.Alpha][0].Value;
                    hTreeGroup.LiteralArb = (alpha << 24) | (red << 16) | blue;
                    if (totalSize == 0 && green < WebpConstants.NumLiteralCodes)
                    {
                        hTreeGroup.IsTrivialCode = true;
                        hTreeGroup.LiteralArb   |= green << 8;
                    }
                }

                hTreeGroup.UsePackedTable = !hTreeGroup.IsTrivialCode && maxBits < HuffmanUtils.HuffmanPackedBits;
                if (hTreeGroup.UsePackedTable)
                {
                    this.BuildPackedTable(hTreeGroup);
                }
            }

            decoder.Metadata.NumHTreeGroups = numHTreeGroups;
            decoder.Metadata.HTreeGroups    = hTreeGroups;
            decoder.Metadata.HuffmanTables  = huffmanTables;
        }