Exemple #1
0
 /// <summary>
 /// Collect all the references into a histogram (without reset).
 /// </summary>
 /// <param name="refs">The backward references.</param>
 public void StoreRefs(Vp8LBackwardRefs refs)
 {
     using List <PixOrCopy> .Enumerator c = refs.Refs.GetEnumerator();
     while (c.MoveNext())
     {
         this.AddSinglePixOrCopy(c.Current, false);
     }
 }
        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);
        }
 private static void BackwardReferences2DLocality(int xSize, Vp8LBackwardRefs refs)
 {
     using List <PixOrCopy> .Enumerator c = refs.Refs.GetEnumerator();
     while (c.MoveNext())
     {
         if (c.Current.IsCopy())
         {
             int dist            = (int)c.Current.BgraOrDistance;
             int transformedDist = DistanceToPlaneCode(xSize, dist);
             c.Current.BgraOrDistance = (uint)transformedDist;
         }
     }
 }
Exemple #4
0
        public void Build(int xSize, int cacheBits, Vp8LBackwardRefs backwardRefs)
        {
            var histogram = new Vp8LHistogram(cacheBits);

            using System.Collections.Generic.List <PixOrCopy> .Enumerator refsEnumerator = backwardRefs.Refs.GetEnumerator();

            // The following code is similar to HistogramCreate but converts the distance to plane code.
            while (refsEnumerator.MoveNext())
            {
                histogram.AddSinglePixOrCopy(refsEnumerator.Current, true, xSize);
            }

            ConvertPopulationCountTableToBitEstimates(histogram.NumCodes(), histogram.Literal, this.Literal);
            ConvertPopulationCountTableToBitEstimates(ValuesInBytes, histogram.Red, this.Red);
            ConvertPopulationCountTableToBitEstimates(ValuesInBytes, histogram.Blue, this.Blue);
            ConvertPopulationCountTableToBitEstimates(ValuesInBytes, histogram.Alpha, this.Alpha);
            ConvertPopulationCountTableToBitEstimates(WebpConstants.NumDistanceCodes, histogram.Distance, this.Distance);
        }
        /// <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++;
                }
            }
        }
        private static void BackwardReferencesTraceBackwards(
            int xSize,
            int ySize,
            MemoryAllocator memoryAllocator,
            ReadOnlySpan <uint> bgra,
            int cacheBits,
            Vp8LHashChain hashChain,
            Vp8LBackwardRefs refsSrc,
            Vp8LBackwardRefs refsDst)
        {
            int distArraySize = xSize * ySize;

            using IMemoryOwner <ushort> distArrayBuffer = memoryAllocator.Allocate <ushort>(distArraySize);
            Span <ushort> distArray = distArrayBuffer.GetSpan();

            BackwardReferencesHashChainDistanceOnly(xSize, ySize, memoryAllocator, bgra, cacheBits, hashChain, refsSrc, distArrayBuffer);
            int           chosenPathSize = TraceBackwards(distArray, distArraySize);
            Span <ushort> chosenPath     = distArray.Slice(distArraySize - chosenPathSize);

            BackwardReferencesHashChainFollowChosenPath(bgra, cacheBits, chosenPath, chosenPathSize, hashChain, refsDst);
        }
        private static void AddSingleLiteral(uint pixel, bool useColorCache, ColorCache colorCache, Vp8LBackwardRefs refs)
        {
            PixOrCopy v;

            if (useColorCache)
            {
                int key = colorCache.GetIndex(pixel);
                if (colorCache.Lookup(key) == pixel)
                {
                    v = PixOrCopy.CreateCacheIdx(key);
                }
                else
                {
                    v = PixOrCopy.CreateLiteral(pixel);
                    colorCache.Set((uint)key, pixel);
                }
            }
            else
            {
                v = PixOrCopy.CreateLiteral(pixel);
            }

            refs.Add(v);
        }
        /// <summary>
        /// Update (in-place) backward references for the specified cacheBits.
        /// </summary>
        private static void BackwardRefsWithLocalCache(ReadOnlySpan <uint> bgra, int cacheBits, Vp8LBackwardRefs refs)
        {
            int pixelIndex = 0;
            var colorCache = new ColorCache();

            colorCache.Init(cacheBits);
            for (int idx = 0; idx < refs.Refs.Count; idx++)
            {
                PixOrCopy v = refs.Refs[idx];
                if (v.IsLiteral())
                {
                    uint bgraLiteral = v.BgraOrDistance;
                    int  ix          = colorCache.Contains(bgraLiteral);
                    if (ix >= 0)
                    {
                        // Color cache contains bgraLiteral
                        v.Mode           = PixOrCopyMode.CacheIdx;
                        v.BgraOrDistance = (uint)ix;
                        v.Len            = 1;
                    }
                    else
                    {
                        colorCache.Insert(bgraLiteral);
                    }

                    pixelIndex++;
                }
                else
                {
                    // refs was created without local cache, so it can not have cache indexes.
                    for (int k = 0; k < v.Len; ++k)
                    {
                        colorCache.Insert(bgra[pixelIndex++]);
                    }
                }
            }
        }
        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++;
                }
            }
        }
        /// <summary>
        /// Compute an LZ77 by forcing matches to happen within a given distance cost.
        /// We therefore limit the algorithm to the lowest 32 values in the PlaneCode definition.
        /// </summary>
        private static void BackwardReferencesLz77Box(int xSize, int ySize, ReadOnlySpan <uint> bgra, int cacheBits, Vp8LHashChain hashChainBest, Vp8LHashChain hashChain, Vp8LBackwardRefs refs)
        {
            int pixelCount = xSize * ySize;

            int[] windowOffsets        = new int[WindowOffsetsSizeMax];
            int[] windowOffsetsNew     = new int[WindowOffsetsSizeMax];
            int   windowOffsetsSize    = 0;
            int   windowOffsetsNewSize = 0;

            short[] counts         = new short[xSize * ySize];
            int     bestOffsetPrev = -1;
            int     bestLengthPrev = -1;

            // counts[i] counts how many times a pixel is repeated starting at position i.
            int i         = pixelCount - 2;
            int countsPos = i;

            counts[countsPos + 1] = 1;
            for (; i >= 0; --i, --countsPos)
            {
                if (bgra[i] == bgra[i + 1])
                {
                    // Max out the counts to MaxLength.
                    counts[countsPos] = counts[countsPos + 1];
                    if (counts[countsPos + 1] != MaxLength)
                    {
                        counts[countsPos]++;
                    }
                }
                else
                {
                    counts[countsPos] = 1;
                }
            }

            // Figure out the window offsets around a pixel. They are stored in a
            // spiraling order around the pixel as defined by DistanceToPlaneCode.
            for (int y = 0; y <= 6; y++)
            {
                for (int x = -6; x <= 6; x++)
                {
                    int offset = (y * xSize) + x;

                    // Ignore offsets that bring us after the pixel.
                    if (offset <= 0)
                    {
                        continue;
                    }

                    int planeCode = DistanceToPlaneCode(xSize, offset) - 1;
                    if (planeCode >= WindowOffsetsSizeMax)
                    {
                        continue;
                    }

                    windowOffsets[planeCode] = offset;
                }
            }

            // For narrow images, not all plane codes are reached, so remove those.
            for (i = 0; i < WindowOffsetsSizeMax; i++)
            {
                if (windowOffsets[i] == 0)
                {
                    continue;
                }

                windowOffsets[windowOffsetsSize++] = windowOffsets[i];
            }

            // Given a pixel P, find the offsets that reach pixels unreachable from P-1
            // with any of the offsets in windowOffsets[].
            for (i = 0; i < windowOffsetsSize; i++)
            {
                bool isReachable = false;
                for (int j = 0; j < windowOffsetsSize && !isReachable; j++)
                {
                    isReachable |= windowOffsets[i] == windowOffsets[j] + 1;
                }

                if (!isReachable)
                {
                    windowOffsetsNew[windowOffsetsNewSize] = windowOffsets[i];
                    ++windowOffsetsNewSize;
                }
            }

            Span <uint> hashChainOffsetLength = hashChain.OffsetLength.GetSpan();

            hashChainOffsetLength[0] = 0;
            for (i = 1; i < pixelCount; i++)
            {
                int  ind;
                int  bestLength = hashChainBest.FindLength(i);
                int  bestOffset = 0;
                bool doCompute  = true;

                if (bestLength >= MaxLength)
                {
                    // Do not recompute the best match if we already have a maximal one in the window.
                    bestOffset = hashChainBest.FindOffset(i);
                    for (ind = 0; ind < windowOffsetsSize; ind++)
                    {
                        if (bestOffset == windowOffsets[ind])
                        {
                            doCompute = false;
                            break;
                        }
                    }
                }

                if (doCompute)
                {
                    // Figure out if we should use the offset/length from the previous pixel
                    // as an initial guess and therefore only inspect the offsets in windowOffsetsNew[].
                    bool usePrev = bestLengthPrev is > 1 and < MaxLength;
                    int  numInd  = usePrev ? windowOffsetsNewSize : windowOffsetsSize;
                    bestLength = usePrev ? bestLengthPrev - 1 : 0;
                    bestOffset = usePrev ? bestOffsetPrev : 0;

                    // Find the longest match in a window around the pixel.
                    for (ind = 0; ind < numInd; ind++)
                    {
                        int currLength = 0;
                        int j          = i;
                        int jOffset    = usePrev ? i - windowOffsetsNew[ind] : i - windowOffsets[ind];
                        if (jOffset < 0 || bgra[jOffset] != bgra[i])
                        {
                            continue;
                        }

                        // The longest match is the sum of how many times each pixel is repeated.
                        do
                        {
                            int countsJOffset = counts[jOffset];
                            int countsJ       = counts[j];
                            if (countsJOffset != countsJ)
                            {
                                currLength += countsJOffset < countsJ ? countsJOffset : countsJ;
                                break;
                            }

                            // The same color is repeated counts_pos times at jOffset and j.
                            currLength += countsJOffset;
                            jOffset    += countsJOffset;
                            j          += countsJOffset;
                        }while (currLength <= MaxLength && j < pixelCount && bgra[jOffset] == bgra[j]);

                        if (bestLength < currLength)
                        {
                            bestOffset = usePrev ? windowOffsetsNew[ind] : windowOffsets[ind];
                            if (currLength >= MaxLength)
                            {
                                bestLength = MaxLength;
                                break;
                            }
                            else
                            {
                                bestLength = currLength;
                            }
                        }
                    }
                }

                if (bestLength <= MinLength)
                {
                    hashChainOffsetLength[i] = 0;
                    bestOffsetPrev           = 0;
                    bestLengthPrev           = 0;
                }
                else
                {
                    hashChainOffsetLength[i] = (uint)((bestOffset << MaxLengthBits) | bestLength);
                    bestOffsetPrev           = bestOffset;
                    bestLengthPrev           = bestLength;
                }
            }

            hashChainOffsetLength[0] = 0;
            BackwardReferencesLz77(xSize, ySize, bgra, cacheBits, hashChain, refs);
        }
        private static void BackwardReferencesLz77(int xSize, int ySize, ReadOnlySpan <uint> bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refs)
        {
            int  iLastCheck    = -1;
            bool useColorCache = cacheBits > 0;
            int  pixCount      = xSize * ySize;
            var  colorCache    = new ColorCache();

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

            refs.Refs.Clear();
            for (int i = 0; i < pixCount;)
            {
                // Alternative #1: Code the pixels starting at 'i' using backward reference.
                int j;
                int offset = hashChain.FindOffset(i);
                int len    = hashChain.FindLength(i);
                if (len >= MinLength)
                {
                    int lenIni   = len;
                    int maxReach = 0;
                    int jMax     = i + lenIni >= pixCount ? pixCount - 1 : i + lenIni;

                    // Only start from what we have not checked already.
                    iLastCheck = i > iLastCheck ? i : iLastCheck;

                    // We know the best match for the current pixel but we try to find the
                    // best matches for the current pixel AND the next one combined.
                    // The naive method would use the intervals:
                    // [i,i+len) + [i+len, length of best match at i+len)
                    // while we check if we can use:
                    // [i,j) (where j<=i+len) + [j, length of best match at j)
                    for (j = iLastCheck + 1; j <= jMax; j++)
                    {
                        int lenJ  = hashChain.FindLength(j);
                        int reach = j + (lenJ >= MinLength ? lenJ : 1); // 1 for single literal.
                        if (reach > maxReach)
                        {
                            len      = j - i;
                            maxReach = reach;
                            if (maxReach >= pixCount)
                            {
                                break;
                            }
                        }
                    }
                }
                else
                {
                    len = 1;
                }

                // Go with literal or backward reference.
                if (len == 1)
                {
                    AddSingleLiteral(bgra[i], useColorCache, colorCache, refs);
                }
                else
                {
                    refs.Add(PixOrCopy.CreateCopy((uint)offset, (ushort)len));
                    if (useColorCache)
                    {
                        for (j = i; j < i + len; j++)
                        {
                            colorCache.Insert(bgra[j]);
                        }
                    }
                }

                i += len;
            }
        }
        private static void BackwardReferencesHashChainFollowChosenPath(ReadOnlySpan <uint> bgra, int cacheBits, Span <ushort> chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs)
        {
            bool useColorCache = cacheBits > 0;
            var  colorCache    = new ColorCache();
            int  i             = 0;

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

            backwardRefs.Refs.Clear();
            for (int ix = 0; ix < chosenPathSize; ix++)
            {
                int len = chosenPath[ix];
                if (len != 1)
                {
                    int offset = hashChain.FindOffset(i);
                    backwardRefs.Add(PixOrCopy.CreateCopy((uint)offset, (ushort)len));

                    if (useColorCache)
                    {
                        for (int k = 0; k < len; k++)
                        {
                            colorCache.Insert(bgra[i + k]);
                        }
                    }

                    i += len;
                }
                else
                {
                    PixOrCopy v;
                    int       idx = useColorCache ? colorCache.Contains(bgra[i]) : -1;
                    if (idx >= 0)
                    {
                        // useColorCache is true and color cache contains bgra[i]
                        // Push pixel as a color cache index.
                        v = PixOrCopy.CreateCacheIdx(idx);
                    }
                    else
                    {
                        if (useColorCache)
                        {
                            colorCache.Insert(bgra[i]);
                        }

                        v = PixOrCopy.CreateLiteral(bgra[i]);
                    }

                    backwardRefs.Add(v);
                    i++;
                }
            }
        }
        /// <summary>
        /// Evaluates best possible backward references for specified quality. The input cacheBits to 'GetBackwardReferences'
        /// sets the maximum cache bits to use (passing 0 implies disabling the local color cache).
        /// The optimal cache bits is evaluated and set for the cacheBits parameter.
        /// The return value is the pointer to the best of the two backward refs viz, refs[0] or refs[1].
        /// </summary>
        public static Vp8LBackwardRefs GetBackwardReferences(
            int width,
            int height,
            ReadOnlySpan <uint> bgra,
            int quality,
            int lz77TypesToTry,
            ref int cacheBits,
            MemoryAllocator memoryAllocator,
            Vp8LHashChain hashChain,
            Vp8LBackwardRefs best,
            Vp8LBackwardRefs worst)
        {
            int           lz77TypeBest     = 0;
            double        bitCostBest      = -1;
            int           cacheBitsInitial = cacheBits;
            Vp8LHashChain hashChainBox     = null;
            var           stats            = new Vp8LStreaks();
            var           bitsEntropy      = new Vp8LBitEntropy();

            for (int lz77Type = 1; lz77TypesToTry > 0; lz77TypesToTry &= ~lz77Type, lz77Type <<= 1)
            {
                int cacheBitsTmp = cacheBitsInitial;
                if ((lz77TypesToTry & lz77Type) == 0)
                {
                    continue;
                }

                switch ((Vp8LLz77Type)lz77Type)
                {
                case Vp8LLz77Type.Lz77Rle:
                    BackwardReferencesRle(width, height, bgra, 0, worst);
                    break;

                case Vp8LLz77Type.Lz77Standard:
                    // Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color cache is not that different in practice.
                    BackwardReferencesLz77(width, height, bgra, 0, hashChain, worst);
                    break;

                case Vp8LLz77Type.Lz77Box:
                    hashChainBox = new Vp8LHashChain(memoryAllocator, width * height);
                    BackwardReferencesLz77Box(width, height, bgra, 0, hashChain, hashChainBox, worst);
                    break;
                }

                // Next, try with a color cache and update the references.
                cacheBitsTmp = CalculateBestCacheSize(bgra, quality, worst, cacheBitsTmp);
                if (cacheBitsTmp > 0)
                {
                    BackwardRefsWithLocalCache(bgra, cacheBitsTmp, worst);
                }

                // Keep the best backward references.
                var    histo   = new Vp8LHistogram(worst, cacheBitsTmp);
                double bitCost = histo.EstimateBits(stats, bitsEntropy);

                if (lz77TypeBest == 0 || bitCost < bitCostBest)
                {
                    Vp8LBackwardRefs tmp = worst;
                    worst        = best;
                    best         = tmp;
                    bitCostBest  = bitCost;
                    cacheBits    = cacheBitsTmp;
                    lz77TypeBest = lz77Type;
                }
            }

            // Improve on simple LZ77 but only for high quality (TraceBackwards is costly).
            if ((lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard || lz77TypeBest == (int)Vp8LLz77Type.Lz77Box) && quality >= 25)
            {
                Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox;
                BackwardReferencesTraceBackwards(width, height, memoryAllocator, bgra, cacheBits, hashChainTmp, best, worst);
                var    histo        = new Vp8LHistogram(worst, cacheBits);
                double bitCostTrace = histo.EstimateBits(stats, bitsEntropy);
                if (bitCostTrace < bitCostBest)
                {
                    best = worst;
                }
            }

            BackwardReferences2DLocality(width, best);

            hashChainBox?.Dispose();

            return(best);
        }
        private static void BackwardReferencesHashChainDistanceOnly(
            int xSize,
            int ySize,
            MemoryAllocator memoryAllocator,
            ReadOnlySpan <uint> bgra,
            int cacheBits,
            Vp8LHashChain hashChain,
            Vp8LBackwardRefs refs,
            IMemoryOwner <ushort> distArrayBuffer)
        {
            int    pixCount              = xSize * ySize;
            bool   useColorCache         = cacheBits > 0;
            int    literalArraySize      = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + (cacheBits > 0 ? 1 << cacheBits : 0);
            var    costModel             = new CostModel(literalArraySize);
            int    offsetPrev            = -1;
            int    lenPrev               = -1;
            double offsetCost            = -1;
            int    firstOffsetIsConstant = -1; // initialized with 'impossible' value.
            int    reach      = 0;
            var    colorCache = new ColorCache();

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

            costModel.Build(xSize, cacheBits, refs);
            using var costManager = new CostManager(memoryAllocator, distArrayBuffer, pixCount, costModel);
            Span <float>  costManagerCosts = costManager.Costs.GetSpan();
            Span <ushort> distArray        = distArrayBuffer.GetSpan();

            // We loop one pixel at a time, but store all currently best points to non-processed locations from this point.
            distArray[0] = 0;

            // Add first pixel as literal.
            AddSingleLiteralWithCostModel(bgra, colorCache, costModel, 0, useColorCache, 0.0f, costManagerCosts, distArray);

            for (int i = 1; i < pixCount; i++)
            {
                float prevCost = costManagerCosts[i - 1];
                int   offset   = hashChain.FindOffset(i);
                int   len      = hashChain.FindLength(i);

                // Try adding the pixel as a literal.
                AddSingleLiteralWithCostModel(bgra, colorCache, costModel, i, useColorCache, prevCost, costManagerCosts, distArray);

                // If we are dealing with a non-literal.
                if (len >= 2)
                {
                    if (offset != offsetPrev)
                    {
                        int code = DistanceToPlaneCode(xSize, offset);
                        offsetCost            = costModel.GetDistanceCost(code);
                        firstOffsetIsConstant = 1;
                        costManager.PushInterval(prevCost + offsetCost, i, len);
                    }
                    else
                    {
                        // Instead of considering all contributions from a pixel i by calling:
                        // costManager.PushInterval(prevCost + offsetCost, i, len);
                        // we optimize these contributions in case offsetCost stays the same
                        // for consecutive pixels. This describes a set of pixels similar to a
                        // previous set (e.g. constant color regions).
                        if (firstOffsetIsConstant != 0)
                        {
                            reach = i - 1 + lenPrev - 1;
                            firstOffsetIsConstant = 0;
                        }

                        if (i + len - 1 > reach)
                        {
                            int lenJ = 0;
                            int j;
                            for (j = i; j <= reach; j++)
                            {
                                int offsetJ = hashChain.FindOffset(j + 1);
                                lenJ = hashChain.FindLength(j + 1);
                                if (offsetJ != offset)
                                {
                                    lenJ = hashChain.FindLength(j);
                                    break;
                                }
                            }

                            // Update the cost at j - 1 and j.
                            costManager.UpdateCostAtIndex(j - 1, false);
                            costManager.UpdateCostAtIndex(j, false);

                            costManager.PushInterval(costManagerCosts[j - 1] + offsetCost, j, lenJ);
                            reach = j + lenJ - 1;
                        }
                    }
                }

                costManager.UpdateCostAtIndex(i, true);
                offsetPrev = offset;
                lenPrev    = len;
            }
        }
        /// <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);
        }
Exemple #16
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Vp8LHistogram"/> class.
 /// </summary>
 /// <param name="refs">The backward references to initialize the histogram with.</param>
 /// <param name="paletteCodeBits">The palette code bits.</param>
 public Vp8LHistogram(Vp8LBackwardRefs refs, int paletteCodeBits)
     : this(paletteCodeBits) => this.StoreRefs(refs);