示例#1
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]++;
            }
        }
        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++;
                }
            }
        }
        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++;
                }
            }
        }
示例#4
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++;
                }
            }
        }
        /// <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 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);
        }
        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;
            }
        }
        /// <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);
        }