/// <summary>
        /// Creates a new WriteableBitmap which is the grayscaled version of this one and returns it. The gray values are equal to the brightness values.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <returns>The new gray WriteableBitmap.</returns>
        public static BitmapBuffer Gray(this BitmapBuffer bmp)
        {
            using (var context = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                int          nWidth  = context.Width;
                int          nHeight = context.Height;
                int[]        px      = context.Pixels;
                BitmapBuffer result  = BitmapBufferFactory.New(nWidth, nHeight);

                using (var dest = result.GetBitmapContext())
                {
                    int[] rp  = dest.Pixels;
                    int   len = context.Length;
                    for (int i = 0; i < len; i++)
                    {
                        // Extract
                        int c = px[i];
                        int a = (c >> 24) & 0xff;
                        int r = (c >> 16) & 0xff;
                        int g = (c >> 8) & 0xff;
                        int b = (c) & 0xff;

                        // Convert to gray with constant factors 0.2126, 0.7152, 0.0722
                        r = g = b = ((r * 6966 + g * 23436 + b * 2366) >> 15);

                        // Set
                        rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
                    }
                }

                return(result);
            }
        }
        /// <summary>
        /// Creates a new inverted WriteableBitmap and returns it.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <returns>The new inverted WriteableBitmap.</returns>
        public static BitmapBuffer Invert(this BitmapBuffer bmp)
        {
            using (var srcContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                var result = BitmapBufferFactory.New(srcContext.Width, srcContext.Height);
                using (var resultContext = result.GetBitmapContext())
                {
                    int[] rp     = resultContext.Pixels;
                    int[] p      = srcContext.Pixels;
                    int   length = srcContext.Length;

                    for (int i = 0; i < length; i++)
                    {
                        // Extract
                        int c = p[i];
                        int a = (c >> 24) & 0xff;
                        int r = (c >> 16) & 0xff;
                        int g = (c >> 8) & 0xff;
                        int b = (c) & 0xff;

                        // Invert
                        r = 255 - r;
                        g = 255 - g;
                        b = 255 - b;

                        // Set
                        rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
                    }

                    return(result);
                }
            }
        }
Beispiel #3
0
 /// <summary>
 /// Clones the specified WriteableBitmap.
 /// </summary>
 /// <param name="bmp">The WriteableBitmap.</param>
 /// <returns>A copy of the WriteableBitmap.</returns>
 public static BitmapBuffer Clone(this BitmapBuffer bmp)
 {
     using (var srcContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
     {
         BitmapBuffer result = BitmapBufferFactory.New(srcContext.Width, srcContext.Height);
         using (var destContext = result.GetBitmapContext())
         {
             BitmapContext.BlockCopy(srcContext, 0, destContext, 0, srcContext.Length * ARGB_SIZE);
         }
         return(result);
     }
 }
        /// <summary>
        /// Creates a new resized WriteableBitmap.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <param name="width">The new desired width.</param>
        /// <param name="height">The new desired height.</param>
        /// <param name="interpolation">The interpolation method that should be used.</param>
        /// <returns>A new WriteableBitmap that is a resized version of the input.</returns>
        public static BitmapBuffer Resize(this BitmapBuffer bmp, int width, int height, Interpolation interpolation)
        {
            using (var srcContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                int[] pd = Resize(srcContext, srcContext.Width, srcContext.Height, width, height, interpolation);

                BitmapBuffer result = BitmapBufferFactory.New(width, height);
                using (var dstContext = result.GetBitmapContext())
                {
                    BitmapContext.BlockCopy(pd, 0, dstContext, 0, ARGB_SIZE * pd.Length);
                }
                return(result);
            }
        }
        /// <summary>
        /// Flips (reflects the image) either vertical or horizontal.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <param name="flipMode">The flip mode.</param>
        /// <returns>A new WriteableBitmap that is a flipped version of the input.</returns>
        public static BitmapBuffer Flip(this BitmapBuffer bmp, FlipMode flipMode)
        {
            using (var context = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                // Use refs for faster access (really important!) speeds up a lot!
                int          w      = context.Width;
                int          h      = context.Height;
                int[]        p      = context.Pixels;
                int          i      = 0;
                BitmapBuffer result = BitmapBufferFactory.New(w, h);

                if (flipMode == FlipMode.Horizontal)
                {
                    using (BitmapContext destContext = result.GetBitmapContext())
                    {
                        int[] rp = destContext.Pixels;
                        for (int y = h - 1; y >= 0; y--)
                        {
                            for (int x = 0; x < w; x++)
                            {
                                int srcInd = y * w + x;
                                rp[i] = p[srcInd];
                                i++;
                            }
                        }
                    }
                }
                else if (flipMode == FlipMode.Vertical)
                {
                    using (BitmapContext destContext = result.GetBitmapContext())
                    {
                        int[] rp = destContext.Pixels;
                        for (int y = 0; y < h; y++)
                        {
                            for (int x = w - 1; x >= 0; x--)
                            {
                                int srcInd = y * w + x;
                                rp[i] = p[srcInd];
                                i++;
                            }
                        }
                    }
                }

                return(result);
            }
        }
        /// <summary>
        /// Creates a new cropped WriteableBitmap.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <param name="x">The x coordinate of the rectangle that defines the crop region.</param>
        /// <param name="y">The y coordinate of the rectangle that defines the crop region.</param>
        /// <param name="width">The width of the rectangle that defines the crop region.</param>
        /// <param name="height">The height of the rectangle that defines the crop region.</param>
        /// <returns>A new WriteableBitmap that is a cropped version of the input.</returns>
        public static BitmapBuffer Crop(this BitmapBuffer bmp, int x, int y, int width, int height)
        {
            using (var srcContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                int srcWidth  = srcContext.Width;
                int srcHeight = srcContext.Height;

                // If the rectangle is completely out of the bitmap
                if (x > srcWidth || y > srcHeight)
                {
                    return(BitmapBufferFactory.New(0, 0));
                }

                // Clamp to boundaries
                if (x < 0)
                {
                    x = 0;
                }
                if (x + width > srcWidth)
                {
                    width = srcWidth - x;
                }
                if (y < 0)
                {
                    y = 0;
                }
                if (y + height > srcHeight)
                {
                    height = srcHeight - y;
                }

                // Copy the pixels line by line using fast BlockCopy
                BitmapBuffer result = BitmapBufferFactory.New(width, height);
                using (var destContext = result.GetBitmapContext())
                {
                    for (int line = 0; line < height; line++)
                    {
                        int srcOff = ((y + line) * srcWidth + x) * ARGB_SIZE;
                        int dstOff = line * width * ARGB_SIZE;
                        BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, width * ARGB_SIZE);
                    }

                    return(result);
                }
            }
        }
        /// <summary>
        /// Creates a new WriteableBitmap which is contrast adjusted version of this one and returns it.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <param name="level">Level of contrast as double. [-255.0, 255.0] </param>
        /// <returns>The new WriteableBitmap.</returns>
        public static BitmapBuffer AdjustContrast(this BitmapBuffer bmp, double level)
        {
            int factor = (int)((259.0 * (level + 255.0)) / (255.0 * (259.0 - level)) * 255.0);

            using (var context = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                int          nWidth  = context.Width;
                int          nHeight = context.Height;
                int[]        px      = context.Pixels;
                BitmapBuffer result  = BitmapBufferFactory.New(nWidth, nHeight);

                using (var dest = result.GetBitmapContext())
                {
                    int[] rp  = dest.Pixels;
                    int   len = context.Length;
                    for (int i = 0; i < len; i++)
                    {
                        // Extract
                        int c = px[i];
                        int a = (c >> 24) & 0xff;
                        int r = (c >> 16) & 0xff;
                        int g = (c >> 8) & 0xff;
                        int b = (c) & 0xff;

                        // Adjust contrast based on computed factor
                        //TODO: create lookup table for this
                        r = ((factor * (r - 128)) >> 8) + 128;
                        g = ((factor * (g - 128)) >> 8) + 128;
                        b = ((factor * (b - 128)) >> 8) + 128;

                        // Clamp
                        r = r < 0 ? 0 : r > 255 ? 255 : r;
                        g = g < 0 ? 0 : g > 255 ? 255 : g;
                        b = b < 0 ? 0 : b > 255 ? 255 : b;

                        // Set
                        rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
                    }
                }

                return(result);
            }
        }
        /// <summary>
        /// Creates a new WriteableBitmap which is gamma adjusted version of this one and returns it.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <param name="value">Value of gamma for adjustment. Original is 1.0.</param>
        /// <returns>The new WriteableBitmap.</returns>
        public static BitmapBuffer AdjustGamma(this BitmapBuffer bmp, double value)
        {
            using (var context = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                int          nWidth    = context.Width;
                int          nHeight   = context.Height;
                int[]        srcPixels = context.Pixels;
                BitmapBuffer result    = BitmapBufferFactory.New(nWidth, nHeight);

                using (var dest = result.GetBitmapContext())
                {
                    int[] rp = dest.Pixels;
                    var   gammaCorrection = 1.0 / value;
                    int   len             = context.Length;
                    for (int i = 0; i < len; i++)
                    {
                        // Extract
                        int c = srcPixels[i];
                        int a = (c >> 24) & 0xff;
                        int r = (c >> 16) & 0xff;
                        int g = (c >> 8) & 0xff;
                        int b = (c) & 0xff;

                        //Gamma adjustment
                        //TODO: create gamma-lookup table for this
                        r = (int)(255.0 * Math.Pow((r / 255.0), gammaCorrection));
                        g = (int)(255.0 * Math.Pow((g / 255.0), gammaCorrection));
                        b = (int)(255.0 * Math.Pow((b / 255.0), gammaCorrection));

                        // Clamps
                        r = r < 0 ? 0 : r > 255 ? 255 : r;
                        g = g < 0 ? 0 : g > 255 ? 255 : g;
                        b = b < 0 ? 0 : b > 255 ? 255 : b;

                        // Set
                        rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
                    }
                }

                return(result);
            }
        }
        /// <summary>
        /// Creates a new WriteableBitmap which is brightness adjusted version of this one and returns it.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <param name="nLevel">Level of contrast as double. [-255.0, 255.0] </param>
        /// <returns>The new WriteableBitmap.</returns>
        public static BitmapBuffer AdjustBrightness(this BitmapBuffer bmp, int nLevel)
        {
            using (var context = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                int          nWidth  = context.Width;
                int          nHeight = context.Height;
                int[]        px      = context.Pixels;
                BitmapBuffer result  = BitmapBufferFactory.New(nWidth, nHeight);

                using (var dest = result.GetBitmapContext())
                {
                    int[] rp  = dest.Pixels;
                    int   len = context.Length;
                    for (int i = 0; i < len; i++)
                    {
                        // Extract
                        int c = px[i];
                        int a = (c >> 24) & 0xff;
                        int r = (c >> 16) & 0xff;
                        int g = (c >> 8) & 0xff;
                        int b = (c) & 0xff;

                        // Brightness adjustment
                        //TODO: create lookup table for this
                        r += nLevel;
                        g += nLevel;
                        b += nLevel;

                        // Clamp
                        r = r < 0 ? 0 : r > 255 ? 255 : r;
                        g = g < 0 ? 0 : g > 255 ? 255 : g;
                        b = b < 0 ? 0 : b > 255 ? 255 : b;

                        // Set
                        rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
                    }
                }

                return(result);
            }
        }
        /// <summary>
        /// Rotates the bitmap in any degree returns a new rotated WriteableBitmap.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <param name="angle">Arbitrary angle in 360 Degrees (positive = clockwise).</param>
        /// <param name="crop">if true: keep the size, false: adjust canvas to new size</param>
        /// <returns>A new WriteableBitmap that is a rotated version of the input.</returns>
        public static BitmapBuffer RotateFree(this BitmapBuffer bmp, double angle, bool crop = true)
        {
            // rotating clockwise, so it's negative relative to Cartesian quadrants
            double cnAngle = -1.0 * (Math.PI / 180) * angle;

            // general iterators
            int i, j;
            // calculated indices in Cartesian coordinates
            int    x, y;
            double fDistance, fPolarAngle;
            // for use in neighboring indices in Cartesian coordinates
            int iFloorX, iCeilingX, iFloorY, iCeilingY;
            // calculated indices in Cartesian coordinates with trailing decimals
            double fTrueX, fTrueY;
            // for interpolation
            double fDeltaX, fDeltaY;

            // interpolated "top" pixels
            double fTopRed, fTopGreen, fTopBlue, fTopAlpha;

            // interpolated "bottom" pixels
            double fBottomRed, fBottomGreen, fBottomBlue, fBottomAlpha;

            // final interpolated color components
            int iRed, iGreen, iBlue, iAlpha;

            int iCentreX, iCentreY;
            int iDestCentreX, iDestCentreY;
            int iWidth, iHeight, newWidth, newHeight;

            using (var bmpContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                iWidth  = bmpContext.Width;
                iHeight = bmpContext.Height;

                if (crop)
                {
                    newWidth  = iWidth;
                    newHeight = iHeight;
                }
                else
                {
                    double rad = angle / (180 / Math.PI);
                    newWidth  = (int)Math.Ceiling(Math.Abs(Math.Sin(rad) * iHeight) + Math.Abs(Math.Cos(rad) * iWidth));
                    newHeight = (int)Math.Ceiling(Math.Abs(Math.Sin(rad) * iWidth) + Math.Abs(Math.Cos(rad) * iHeight));
                }


                iCentreX = iWidth / 2;
                iCentreY = iHeight / 2;

                iDestCentreX = newWidth / 2;
                iDestCentreY = newHeight / 2;

                BitmapBuffer bmBilinearInterpolation = BitmapBufferFactory.New(newWidth, newHeight);

                using (var bilinearContext = bmBilinearInterpolation.GetBitmapContext())
                {
                    int[] newp = bilinearContext.Pixels;
                    int[] oldp = bmpContext.Pixels;
                    int   oldw = bmpContext.Width;

                    // assigning pixels of destination image from source image
                    // with bilinear interpolation
                    for (i = 0; i < newHeight; ++i)
                    {
                        for (j = 0; j < newWidth; ++j)
                        {
                            // convert raster to Cartesian
                            x = j - iDestCentreX;
                            y = iDestCentreY - i;

                            // convert Cartesian to polar
                            fDistance = Math.Sqrt(x * x + y * y);
                            if (x == 0)
                            {
                                if (y == 0)
                                {
                                    // center of image, no rotation needed
                                    newp[i * newWidth + j] = oldp[iCentreY * oldw + iCentreX];
                                    continue;
                                }
                                if (y < 0)
                                {
                                    fPolarAngle = 1.5 * Math.PI;
                                }
                                else
                                {
                                    fPolarAngle = 0.5 * Math.PI;
                                }
                            }
                            else
                            {
                                fPolarAngle = Math.Atan2(y, x);
                            }

                            // the crucial rotation part
                            // "reverse" rotate, so minus instead of plus
                            fPolarAngle -= cnAngle;

                            // convert polar to Cartesian
                            fTrueX = fDistance * Math.Cos(fPolarAngle);
                            fTrueY = fDistance * Math.Sin(fPolarAngle);

                            // convert Cartesian to raster
                            fTrueX = fTrueX + iCentreX;
                            fTrueY = iCentreY - fTrueY;

                            iFloorX   = (int)(Math.Floor(fTrueX));
                            iFloorY   = (int)(Math.Floor(fTrueY));
                            iCeilingX = (int)(Math.Ceiling(fTrueX));
                            iCeilingY = (int)(Math.Ceiling(fTrueY));

                            // check bounds
                            if (iFloorX < 0 || iCeilingX < 0 || iFloorX >= iWidth || iCeilingX >= iWidth || iFloorY < 0 ||
                                iCeilingY < 0 || iFloorY >= iHeight || iCeilingY >= iHeight)
                            {
                                continue;
                            }

                            fDeltaX = fTrueX - iFloorX;
                            fDeltaY = fTrueY - iFloorY;

                            int clrTopLeft     = oldp[iFloorY * oldw + iFloorX];
                            int clrTopRight    = oldp[iFloorY * oldw + iCeilingX];
                            int clrBottomLeft  = oldp[iCeilingY * oldw + iFloorX];
                            int clrBottomRight = oldp[iCeilingY * oldw + iCeilingX];

                            fTopAlpha = (1 - fDeltaX) * ((clrTopLeft >> 24) & 0xFF) + fDeltaX * ((clrTopRight >> 24) & 0xFF);
                            fTopRed   = (1 - fDeltaX) * ((clrTopLeft >> 16) & 0xFF) + fDeltaX * ((clrTopRight >> 16) & 0xFF);
                            fTopGreen = (1 - fDeltaX) * ((clrTopLeft >> 8) & 0xFF) + fDeltaX * ((clrTopRight >> 8) & 0xFF);
                            fTopBlue  = (1 - fDeltaX) * (clrTopLeft & 0xFF) + fDeltaX * (clrTopRight & 0xFF);

                            // linearly interpolate horizontally between bottom neighbors
                            fBottomAlpha = (1 - fDeltaX) * ((clrBottomLeft >> 24) & 0xFF) + fDeltaX * ((clrBottomRight >> 24) & 0xFF);
                            fBottomRed   = (1 - fDeltaX) * ((clrBottomLeft >> 16) & 0xFF) + fDeltaX * ((clrBottomRight >> 16) & 0xFF);
                            fBottomGreen = (1 - fDeltaX) * ((clrBottomLeft >> 8) & 0xFF) + fDeltaX * ((clrBottomRight >> 8) & 0xFF);
                            fBottomBlue  = (1 - fDeltaX) * (clrBottomLeft & 0xFF) + fDeltaX * (clrBottomRight & 0xFF);

                            // linearly interpolate vertically between top and bottom interpolated results
                            iRed   = (int)(Math.Round((1 - fDeltaY) * fTopRed + fDeltaY * fBottomRed));
                            iGreen = (int)(Math.Round((1 - fDeltaY) * fTopGreen + fDeltaY * fBottomGreen));
                            iBlue  = (int)(Math.Round((1 - fDeltaY) * fTopBlue + fDeltaY * fBottomBlue));
                            iAlpha = (int)(Math.Round((1 - fDeltaY) * fTopAlpha + fDeltaY * fBottomAlpha));

                            // make sure color values are valid
                            if (iRed < 0)
                            {
                                iRed = 0;
                            }
                            if (iRed > 255)
                            {
                                iRed = 255;
                            }
                            if (iGreen < 0)
                            {
                                iGreen = 0;
                            }
                            if (iGreen > 255)
                            {
                                iGreen = 255;
                            }
                            if (iBlue < 0)
                            {
                                iBlue = 0;
                            }
                            if (iBlue > 255)
                            {
                                iBlue = 255;
                            }
                            if (iAlpha < 0)
                            {
                                iAlpha = 0;
                            }
                            if (iAlpha > 255)
                            {
                                iAlpha = 255;
                            }

                            int a = iAlpha + 1;
                            newp[i * newWidth + j] = (iAlpha << 24)
                                                     | ((byte)((iRed * a) >> 8) << 16)
                                                     | ((byte)((iGreen * a) >> 8) << 8)
                                                     | ((byte)((iBlue * a) >> 8));
                        }
                    }
                    return(bmBilinearInterpolation);
                }
            }
        }
        /// <summary>
        /// Rotates the bitmap in 90° steps clockwise and returns a new rotated WriteableBitmap.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <param name="angle">The angle in degrees the bitmap should be rotated in 90° steps clockwise.</param>
        /// <returns>A new WriteableBitmap that is a rotated version of the input.</returns>
        public static BitmapBuffer Rotate(this BitmapBuffer bmp, int angle)
        {
            using (var context = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                // Use refs for faster access (really important!) speeds up a lot!
                int          w = context.Width;
                int          h = context.Height;
                int[]        p = context.Pixels;
                int          i = 0;
                BitmapBuffer result;
                angle %= 360;

                if (angle > 0 && angle <= 90)
                {
                    result = BitmapBufferFactory.New(h, w);
                    using (var destContext = result.GetBitmapContext())
                    {
                        var rp = destContext.Pixels;
                        for (int x = 0; x < w; x++)
                        {
                            for (int y = h - 1; y >= 0; y--)
                            {
                                int srcInd = y * w + x;
                                rp[i] = p[srcInd];
                                i++;
                            }
                        }
                    }
                }
                else if (angle > 90 && angle <= 180)
                {
                    result = BitmapBufferFactory.New(w, h);
                    using (var destContext = result.GetBitmapContext())
                    {
                        var rp = destContext.Pixels;
                        for (int y = h - 1; y >= 0; y--)
                        {
                            for (int x = w - 1; x >= 0; x--)
                            {
                                int srcInd = y * w + x;
                                rp[i] = p[srcInd];
                                i++;
                            }
                        }
                    }
                }
                else if (angle > 180 && angle <= 270)
                {
                    result = BitmapBufferFactory.New(h, w);
                    using (var destContext = result.GetBitmapContext())
                    {
                        int[] rp = destContext.Pixels;
                        for (int x = w - 1; x >= 0; x--)
                        {
                            for (int y = 0; y < h; y++)
                            {
                                int srcInd = y * w + x;
                                rp[i] = p[srcInd];
                                i++;
                            }
                        }
                    }
                }
                else
                {
                    result = bmp.Clone();
                }
                return(result);
            }
        }
        /// <summary>
        /// Creates a new filtered WriteableBitmap.
        /// </summary>
        /// <param name="bmp">The WriteableBitmap.</param>
        /// <param name="kernel">The kernel used for convolution.</param>
        /// <param name="kernelFactorSum">The factor used for the kernel summing.</param>
        /// <param name="kernelOffsetSum">The offset used for the kernel summing.</param>
        /// <returns>A new WriteableBitmap that is a filtered version of the input.</returns>
        public static BitmapBuffer Convolute(this BitmapBuffer bmp, int[,] kernel, int kernelFactorSum, int kernelOffsetSum)
        {
            int kh = kernel.GetUpperBound(0) + 1;
            int kw = kernel.GetUpperBound(1) + 1;

            if ((kw & 1) == 0)
            {
                throw new System.InvalidOperationException("Kernel width must be odd!");
            }
            if ((kh & 1) == 0)
            {
                throw new System.InvalidOperationException("Kernel height must be odd!");
            }

            using (var srcContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
            {
                int          w      = srcContext.Width;
                int          h      = srcContext.Height;
                BitmapBuffer result = BitmapBufferFactory.New(w, h);

                using (var resultContext = result.GetBitmapContext())
                {
                    int[] pixels       = srcContext.Pixels;
                    int[] resultPixels = resultContext.Pixels;
                    int   index        = 0;
                    int   kwh          = kw >> 1;
                    int   khh          = kh >> 1;

                    for (int y = 0; y < h; y++)
                    {
                        for (int x = 0; x < w; x++)
                        {
                            int a = 0;
                            int r = 0;
                            int g = 0;
                            int b = 0;

                            for (int kx = -kwh; kx <= kwh; kx++)
                            {
                                int px = kx + x;
                                // Repeat pixels at borders
                                if (px < 0)
                                {
                                    px = 0;
                                }
                                else if (px >= w)
                                {
                                    px = w - 1;
                                }

                                for (int ky = -khh; ky <= khh; ky++)
                                {
                                    int py = ky + y;
                                    // Repeat pixels at borders
                                    if (py < 0)
                                    {
                                        py = 0;
                                    }
                                    else if (py >= h)
                                    {
                                        py = h - 1;
                                    }

                                    int col = pixels[py * w + px];
                                    int k   = kernel[ky + kwh, kx + khh];
                                    a += ((col >> 24) & 0x000000FF) * k;
                                    r += ((col >> 16) & 0x000000FF) * k;
                                    g += ((col >> 8) & 0x000000FF) * k;
                                    b += ((col) & 0x000000FF) * k;
                                }
                            }

                            int ta = ((a / kernelFactorSum) + kernelOffsetSum);
                            int tr = ((r / kernelFactorSum) + kernelOffsetSum);
                            int tg = ((g / kernelFactorSum) + kernelOffsetSum);
                            int tb = ((b / kernelFactorSum) + kernelOffsetSum);

                            // Clamp to byte boundaries
                            byte ba = (byte)((ta > 255) ? 255 : ((ta < 0) ? 0 : ta));
                            byte br = (byte)((tr > 255) ? 255 : ((tr < 0) ? 0 : tr));
                            byte bg = (byte)((tg > 255) ? 255 : ((tg < 0) ? 0 : tg));
                            byte bb = (byte)((tb > 255) ? 255 : ((tb < 0) ? 0 : tb));

                            resultPixels[index++] = (ba << 24) | (br << 16) | (bg << 8) | (bb);
                        }
                    }
                    return(result);
                }
            }
        }