/// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            float  threshold   = this.Threshold * 255F;
            Rgba32 rgba        = default;
            bool   isAlphaOnly = typeof(TPixel) == typeof(Alpha8);

            var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
            int startY   = interest.Y;
            int endY     = interest.Bottom;
            int startX   = interest.X;
            int endX     = interest.Right;

            // Collect the values before looping so we can reduce our calculation count for identical sibling pixels
            TPixel             sourcePixel   = source[startX, startY];
            TPixel             previousPixel = sourcePixel;
            PixelPair <TPixel> pair          = this.GetClosestPixelPair(ref sourcePixel);

            sourcePixel.ToRgba32(ref rgba);

            // Convert to grayscale using ITU-R Recommendation BT.709 if required
            float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B);

            for (int y = startY; y < endY; y++)
            {
                Span <TPixel> row = source.GetPixelRowSpan(y);

                for (int x = startX; x < endX; x++)
                {
                    sourcePixel = row[x];

                    // Check if this is the same as the last pixel. If so use that value
                    // rather than calculating it again. This is an inexpensive optimization.
                    if (!previousPixel.Equals(sourcePixel))
                    {
                        pair = this.GetClosestPixelPair(ref sourcePixel);

                        // No error to spread, exact match.
                        if (sourcePixel.Equals(pair.First))
                        {
                            continue;
                        }

                        sourcePixel.ToRgba32(ref rgba);
                        luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B);

                        // Setup the previous pointer
                        previousPixel = sourcePixel;
                    }

                    TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First;
                    this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY);
                }
            }
        }
Beispiel #2
0
        protected PixelPair <TPixel> GetClosestPixelPair(ref TPixel pixel, TPixel[] colorPalette)
        {
            // Check if the color is in the lookup table
            if (this.cache.ContainsKey(pixel))
            {
                return(this.cache[pixel]);
            }

            // Not found - loop through the palette and find the nearest match.
            float leastDistance       = int.MaxValue;
            float secondLeastDistance = int.MaxValue;
            var   vector = pixel.ToVector4();

            var closest       = default(TPixel);
            var secondClosest = default(TPixel);

            for (int index = 0; index < colorPalette.Length; index++)
            {
                TPixel temp     = colorPalette[index];
                float  distance = Vector4.DistanceSquared(vector, temp.ToVector4());

                if (distance < leastDistance)
                {
                    leastDistance = distance;
                    secondClosest = closest;
                    closest       = temp;
                }
                else if (distance < secondLeastDistance)
                {
                    secondLeastDistance = distance;
                    secondClosest       = temp;
                }
            }

            // Pop it into the cache for next time
            var pair = new PixelPair <TPixel>(closest, secondClosest);

            this.cache.Add(pixel, pair);

            return(pair);
        }
Beispiel #3
0
 /// <inheritdoc/>
 public bool Equals(PixelPair <TPixel> other)
 => this.First.Equals(other.First) && this.Second.Equals(other.Second);