Пример #1
0
        /// <summary>
        ///     BoxBlurHorizontal is a private helper method for the BoxBlur, only for IFastBitmaps with alpha channel
        /// </summary>
        /// <param name="unmanagedBitmap">UnmanagedBitmap</param>
        /// <param name="range">Range must be odd!</param>
        private static void BoxBlurHorizontal(UnmanagedBitmap <Bgr32> unmanagedBitmap, int range)
        {
            var halfRange = range / 2;

            Parallel.For(0, unmanagedBitmap.Height, y =>
            {
                unsafe {
                    var rgb32             = unmanagedBitmap[y];
                    Span <Bgr32> averages = stackalloc Bgr32[range];
                    var r    = 0;
                    var g    = 0;
                    var b    = 0;
                    var hits = halfRange;
                    for (var x = 0; x < halfRange; x++)
                    {
                        ref Bgr32 color = ref rgb32[x];

                        r += color.R;
                        g += color.G;
                        b += color.B;
                    }
                    for (var x = 0; x < unmanagedBitmap.Width; x++)
                    {
                        var leftSide = x - halfRange - 1;
                        if (leftSide >= 0)
                        {
                            // Get value at the left side of range
                            ref Bgr32 color = ref rgb32[leftSide];
                            r -= color.R;
                            g -= color.G;
                            b -= color.B;
                            hits--;
                        }

                        var rightSide = x + halfRange;
                        if (rightSide < unmanagedBitmap.Width)
                        {
                            ref Bgr32 color = ref rgb32[rightSide];
                            r += color.R;
                            g += color.G;
                            b += color.B;
                            hits++;
                        }

                        ref Bgr32 average = ref averages[x % range];
                        average.R         = (byte)(r / hits);
                        average.G         = (byte)(g / hits);
                        average.B         = (byte)(b / hits);

                        if (leftSide >= 0)
                        {
                            // Now we can write the value from the calculated avarages
                            var readLocation = (leftSide % range);

                            rgb32[leftSide] = averages[readLocation];
                        }
                    }
Пример #2
0
 /// <summary>
 ///     Apply BoxBlur to the UnmanagedBitmap
 /// </summary>
 /// <param name="unmanagedBitmap">UnmanagedBitmap</param>
 /// <param name="range">Must be even!</param>
 public static void ApplyBoxBlur(this UnmanagedBitmap <Bgra32> unmanagedBitmap, int range)
 {
     // Range must be odd!
     if ((range & 1) == 0)
     {
         range++;
     }
     if (range <= 1)
     {
         return;
     }
     // Box blurs are frequently used to approximate a Gaussian blur.
     // By the central limit theorem, if applied 3 times on the same image, a box blur approximates the Gaussian kernel to within about 3%, yielding the same result as a quadratic convolution kernel.
     // This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur.
     // (Might also be a mistake in our blur, but for now it looks great)
     BoxBlurHorizontal(unmanagedBitmap, range);
     BoxBlurVertical(unmanagedBitmap, range);
     BoxBlurHorizontal(unmanagedBitmap, range);
     BoxBlurVertical(unmanagedBitmap, range);
 }
Пример #3
0
        /// <summary>
        /// Use "Scale2x" algorithm to produce bitmap from the original.
        /// Every pixel from input texture produces 4 output pixels, for more details check out http://scale2x.sourceforge.net/algorithm.html
        /// </summary>
        /// <param name="sourceBitmap">UnmanagedBitmap to scale 2x</param>
        /// <returns>UnmanagedBitmap</returns>
        public static UnmanagedBitmap <TPixelLayout> Scale2X <TPixelLayout>(this UnmanagedBitmap <TPixelLayout> sourceBitmap) where TPixelLayout : unmanaged
        {
            if (Marshal.SizeOf <TPixelLayout>() != 4)
            {
                throw new NotSupportedException("Only 4 byte unmanaged structs are supported.");
            }
            var destinationBitmap = new UnmanagedBitmap <TPixelLayout>(sourceBitmap.Width << 1, sourceBitmap.Height << 1);

            var sourceWidth                = sourceBitmap.Width;
            var destinationWidth           = destinationBitmap.Width;
            ReadOnlySpan <uint> sourceSpan = MemoryMarshal.Cast <TPixelLayout, uint>(sourceBitmap.Span);
            var destinationSpan            = MemoryMarshal.Cast <TPixelLayout, uint>(destinationBitmap.Span);

            unsafe
            {
                for (var y = 0; y < sourceBitmap.Height; y++)
                {
                    var sourceYOffset      = y * sourceWidth;
                    var destinationYOffset = (y << 1) * destinationWidth;
                    for (var x = 0; x < sourceWidth; x++)
                    {
                        var sourceOffset         = sourceYOffset + x;
                        ref readonly uint colorE = ref sourceSpan[sourceOffset];
        /// <summary>
        /// Use "Scale2x" algorithm to produce bitmap from the original.
        /// Every pixel from input texture produces 4 output pixels, for more details check out http://scale2x.sourceforge.net/algorithm.html
        /// </summary>
        /// <param name="sourceBitmap">UnmanagedBitmap to scale 2x</param>
        /// <returns>UnmanagedBitmap</returns>
        public static UnmanagedBitmap <TPixelLayout> Scale2XReference <TPixelLayout>(this UnmanagedBitmap <TPixelLayout> sourceBitmap) where TPixelLayout : unmanaged
        {
            if (Marshal.SizeOf <TPixelLayout>() != 4)
            {
                throw new NotSupportedException("Only 4 byte unmanaged structs are supported.");
            }
            var destinationBitmap = new UnmanagedBitmap <TPixelLayout>(sourceBitmap.Width << 1, sourceBitmap.Height << 1);

            var colorBOffset = -sourceBitmap.Width;
            var colorHOffset = sourceBitmap.Width;
            ReadOnlySpan <int> sourceSpan = MemoryMarshal.Cast <TPixelLayout, int>(sourceBitmap.Span);
            var destinationSpan           = MemoryMarshal.Cast <TPixelLayout, int>(destinationBitmap.Span);

            unsafe
            {
                var colors  = stackalloc int[5];
                var colorsE = stackalloc int[4];

                for (var y = 0; y < sourceBitmap.Height; y++)
                {
                    var offset = y * sourceBitmap.Width;
                    for (var x = 0; x < sourceBitmap.Width; x++)
                    {
                        var xOffset = offset + x;
                        colors[ColorE] = sourceSpan[xOffset];

                        if (y != 0)
                        {
                            colors[ColorB] = sourceSpan[xOffset + colorBOffset];
                        }
                        else
                        {
                            colors[ColorB] = colors[ColorE];
                        }

                        if (y != sourceBitmap.Height - 1)
                        {
                            colors[ColorH] = sourceSpan[xOffset + colorHOffset];
                        }
                        else
                        {
                            colors[ColorH] = colors[ColorE];
                        }
                        if (x > 0)
                        {
                            colors[ColorD] = sourceSpan[xOffset - 1];
                        }
                        else
                        {
                            colors[ColorD] = colors[ColorE];
                        }
                        if (x < sourceBitmap.Width - 1)
                        {
                            colors[ColorF] = sourceSpan[xOffset + 1];
                        }
                        else
                        {
                            colors[ColorF] = colors[ColorE];
                        }

                        if (colors[ColorB] != colors[ColorH] && colors[ColorD] != colors[ColorF])
                        {
                            colorsE[0] = colors[ColorD] == colors[ColorB] ? colors[ColorD] : colors[ColorE];
                            colorsE[1] = colors[ColorB] == colors[ColorF] ? colors[ColorF] : colors[ColorE];
                            colorsE[2] = colors[ColorD] == colors[ColorH] ? colors[ColorD] : colors[ColorE];
                            colorsE[3] = colors[ColorH] == colors[ColorF] ? colors[ColorF] : colors[ColorE];
                        }
                        else
                        {
                            colorsE[0] = colors[ColorE];
                            colorsE[1] = colors[ColorE];
                            colorsE[2] = colors[ColorE];
                            colorsE[3] = colors[ColorE];
                        }

                        var destinationOffset = (x << 1) + ((y << 1) * destinationBitmap.Width);
                        destinationSpan[destinationOffset]     = colorsE[0];
                        destinationSpan[destinationOffset + 1] = colorsE[1];
                        destinationSpan[destinationOffset + destinationBitmap.Width]     = colorsE[2];
                        destinationSpan[destinationOffset + 1 + destinationBitmap.Width] = colorsE[3];
                    }
                }
            }
            return(destinationBitmap);
        }