/// <summary>
 /// Return negative of Bitmap
 /// </summary>
 /// <param name="sourceBitmap">Bitmap to create a negative off</param>
 /// <returns>Negative bitmap</returns>
 public static Bitmap CreateNegative(Bitmap sourceBitmap)
 {
     using (BitmapBuffer bb = new BitmapBuffer(sourceBitmap, true)) {
         bb.Lock();
         for (int y = 0; y < bb.Height; y++) {
             for (int x = 0; x < bb.Width; x++) {
                 Color color = bb.GetColorAt(x, y);
                 Color invertedColor = Color.FromArgb(color.A, color.R ^ 255, color.G ^ 255, color.B ^ 255);
                 bb.SetColorAt(x, y, invertedColor);
             }
         }
         bb.Unlock();
         return bb.Bitmap;
     }
 }
        /// <summary>
        /// Create a new bitmap with the sourceBitmap blurred
        /// </summary>
        /// <param name="sourceBitmap">Source to blur</param>
        /// <param name="applyRect">Area to blur</param>
        /// <param name="useExportQuality">Use best quality</param>
        /// <param name="blurRadius">Radius of the blur</param>
        /// <param name="previewQuality">Quality, use 1d for normal anything less skipps calculations</param>
        /// <param name="invert">true if the blur needs to occur outside of the area</param>
        /// <param name="parentBounds">Rectangle limiting the area when using invert</param>
        public static unsafe Bitmap CreateBlur(Bitmap sourceBitmap, Rectangle applyRect, bool useExportQuality, int blurRadius, double previewQuality, bool invert, Rectangle parentBounds)
        {
            if (applyRect.Height <= 0 || applyRect.Width <= 0) {
                return null;
            }
            // do nothing when nothing can be done!
            if (blurRadius < 1) {
                return null;
            }

            byte[] nullColor = new byte[] { 255, 255, 255, 255 };
            if (sourceBitmap.PixelFormat == PixelFormat.Format32bppArgb) {
                nullColor = new byte[] { 0, 0, 0, 0 };
            }
            byte[] settingColor = new byte[4];
            byte[] readingColor = new byte[4];

            using (BitmapBuffer bbbDest = new BitmapBuffer(sourceBitmap, applyRect, true)) {
                bbbDest.Lock();
                using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, applyRect, false)) {
                    bbbSrc.Lock();
                    Random rand = new Random();
                    unchecked {
                        int r = blurRadius;
                        int[] w = CreateGaussianBlurRow(r);
                        int wlen = w.Length;
                        long* waSums = stackalloc long[wlen];
                        long* wcSums = stackalloc long[wlen];
                        long* aSums = stackalloc long[wlen];
                        long* rSums = stackalloc long[wlen];
                        long* gSums = stackalloc long[wlen];
                        long* bSums = stackalloc long[wlen];
                        for (int y = 0; y < applyRect.Height; ++y) {
                            long waSum = 0;
                            long wcSum = 0;
                            long aSum = 0;
                            long rSum = 0;
                            long gSum = 0;
                            long bSum = 0;

                            for (int wx = 0; wx < wlen; ++wx) {
                                int srcX = wx - r;
                                waSums[wx] = 0;
                                wcSums[wx] = 0;
                                aSums[wx] = 0;
                                rSums[wx] = 0;
                                gSums[wx] = 0;
                                bSums[wx] = 0;

                                if (srcX >= 0 && srcX < bbbDest.Width) {
                                    for (int wy = 0; wy < wlen; ++wy) {
                                        int srcY = y + wy - r;

                                        if (srcY >= 0 && srcY < bbbDest.Height) {
                                            bbbSrc.GetColorIn(srcX, srcY, readingColor);
                                            int wp = w[wy];

                                            waSums[wx] += wp;
                                            wp *= readingColor[0] + (readingColor[0] >> 7);
                                            wcSums[wx] += wp;
                                            wp >>= 8;

                                            aSums[wx] += wp * readingColor[0];
                                            rSums[wx] += wp * readingColor[1];
                                            gSums[wx] += wp * readingColor[2];
                                            bSums[wx] += wp * readingColor[3];
                                        }
                                    }

                                    int wwx = w[wx];
                                    waSum += wwx * waSums[wx];
                                    wcSum += wwx * wcSums[wx];
                                    aSum += wwx * aSums[wx];
                                    rSum += wwx * rSums[wx];
                                    gSum += wwx * gSums[wx];
                                    bSum += wwx * bSums[wx];
                                }
                            }

                            wcSum >>= 8;

                            if (parentBounds.Contains(applyRect.Left, applyRect.Top + y) ^ invert) {
                                if (waSum == 0 || wcSum == 0) {
                                    bbbDest.SetUncheckedColorArrayAt(0, y, nullColor);
                                } else {
                                    settingColor[0] = (byte)(aSum / waSum);
                                    settingColor[1] = (byte)(rSum / wcSum);
                                    settingColor[2] = (byte)(gSum / wcSum);
                                    settingColor[3] = (byte)(bSum / wcSum);
                                    bbbDest.SetUncheckedColorArrayAt(0, y, settingColor);
                                }
                            }

                            for (int x = 1; x < applyRect.Width; ++x) {
                                for (int i = 0; i < wlen - 1; ++i) {
                                    waSums[i] = waSums[i + 1];
                                    wcSums[i] = wcSums[i + 1];
                                    aSums[i] = aSums[i + 1];
                                    rSums[i] = rSums[i + 1];
                                    gSums[i] = gSums[i + 1];
                                    bSums[i] = bSums[i + 1];
                                }

                                waSum = 0;
                                wcSum = 0;
                                aSum = 0;
                                rSum = 0;
                                gSum = 0;
                                bSum = 0;

                                int wx;
                                for (wx = 0; wx < wlen - 1; ++wx) {
                                    long wwx = (long)w[wx];
                                    waSum += wwx * waSums[wx];
                                    wcSum += wwx * wcSums[wx];
                                    aSum += wwx * aSums[wx];
                                    rSum += wwx * rSums[wx];
                                    gSum += wwx * gSums[wx];
                                    bSum += wwx * bSums[wx];
                                }

                                wx = wlen - 1;

                                waSums[wx] = 0;
                                wcSums[wx] = 0;
                                aSums[wx] = 0;
                                rSums[wx] = 0;
                                gSums[wx] = 0;
                                bSums[wx] = 0;

                                int srcX = x + wx - r;

                                if (srcX >= 0 && srcX < applyRect.Width) {
                                    for (int wy = 0; wy < wlen; ++wy) {
                                        int srcY = y + wy - r;
                                        // only when in EDIT mode, ignore some pixels depending on preview quality
                                        if ((useExportQuality || rand.NextDouble() < previewQuality) && srcY >= 0 && srcY < applyRect.Height) {
                                            int wp = w[wy];
                                            waSums[wx] += wp;
                                            bbbSrc.GetColorIn(srcX, srcY, readingColor);
                                            wp *= readingColor[0] + (readingColor[0] >> 7);
                                            wcSums[wx] += wp;
                                            wp >>= 8;

                                            aSums[wx] += wp * readingColor[0];
                                            rSums[wx] += wp * readingColor[1];
                                            gSums[wx] += wp * readingColor[2];
                                            bSums[wx] += wp * readingColor[3];
                                        }
                                    }

                                    int wr = w[wx];
                                    waSum += wr * waSums[wx];
                                    wcSum += wr * wcSums[wx];
                                    aSum += wr * aSums[wx];
                                    rSum += wr * rSums[wx];
                                    gSum += wr * gSums[wx];
                                    bSum += wr * bSums[wx];
                                }

                                wcSum >>= 8;
                                if (parentBounds.Contains(applyRect.Left + x, applyRect.Top + y) ^ invert) {
                                    if (waSum == 0 || wcSum == 0) {
                                        bbbDest.SetUncheckedColorArrayAt(x, y, nullColor);
                                    } else {
                                        settingColor[0] = (byte)(aSum / waSum);
                                        settingColor[1] = (byte)(rSum / wcSum);
                                        settingColor[2] = (byte)(gSum / wcSum);
                                        settingColor[3] = (byte)(bSum / wcSum);
                                        bbbDest.SetUncheckedColorArrayAt(x, y, settingColor);
                                    }
                                }
                            }
                        }
                    }
                }
                bbbDest.Unlock();
                return bbbDest.Bitmap;
            }
        }