private void LoadImage(WriteableBitmap bitmap)
        {
            if (bitmap.PixelWidth != 0 && bitmap.PixelHeight != 0)
            {
                this.loadedBitmap = bitmap;

                SketchGenTaskData task_data = new SketchGenTaskData();

                task_data.radius = this.gaussianRadius;
                task_data.width  = bitmap.PixelWidth;
                task_data.height = bitmap.PixelHeight;
                task_data.pixels = bitmap.Pixels;

                this.sketchGeneratorWorker.RunWorkerAsync(task_data);

                this.GenerationProgressIndicator.IsVisible = true;
            }
        }
        private void StartSketchGenerator()
        {
            if (this.scaledBitmap != null)
            {
                try
                {
                    SketchGenTaskData task_data = new SketchGenTaskData();

                    task_data.radius = this.gaussianRadius;
                    task_data.width  = this.scaledBitmap.PixelWidth;
                    task_data.height = this.scaledBitmap.PixelHeight;
                    task_data.pixels = this.scaledBitmap.Pixels;

                    this.sketchGeneratorWorker.RunWorkerAsync(task_data);

                    this.GenerationProgressIndicator.IsVisible = true;
                }
                catch (Exception)
                {
                    // Ignore
                }
            }
        }
        private void sketchGeneratorWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int radius = (e.Argument as SketchGenTaskData).radius;
            int width  = (e.Argument as SketchGenTaskData).width;
            int height = (e.Argument as SketchGenTaskData).height;

            int[] pixels = (e.Argument as SketchGenTaskData).pixels;

            // Make Gaussian blur of original image

            int[] sketch_pixels = pixels.Clone() as int[];

            int[] tab   = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
            int   alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius - 1];

            int r1 = 0;
            int r2 = height - 1;
            int c1 = 0;
            int c2 = width - 1;

            int[] rgba = new int[4];

            for (int col = c1; col <= c2; col++)
            {
                int s = r1 * width + col;

                for (int i = 0; i < 4; i++)
                {
                    rgba[i] = ((sketch_pixels[s] >> (i * 8)) & 0xFF) << 4;
                }

                s += width;

                for (int j = r1; j < r2; j++, s += width)
                {
                    int p = 0;

                    for (int i = 0; i < 4; i++)
                    {
                        p = p | ((((rgba[i] += ((((sketch_pixels[s] >> (i * 8)) & 0xFF) << 4) - rgba[i]) * alpha / 16) >> 4) & 0xFF) << (i * 8));
                    }

                    sketch_pixels[s] = p;
                }
            }

            for (int row = r1; row <= r2; row++)
            {
                int s = row * width + c1;

                for (int i = 0; i < 4; i++)
                {
                    rgba[i] = ((sketch_pixels[s] >> (i * 8)) & 0xFF) << 4;
                }

                s++;

                for (int j = c1; j < c2; j++, s++)
                {
                    int p = 0;

                    for (int i = 0; i < 4; i++)
                    {
                        p = p | ((((rgba[i] += ((((sketch_pixels[s] >> (i * 8)) & 0xFF) << 4) - rgba[i]) * alpha / 16) >> 4) & 0xFF) << (i * 8));
                    }

                    sketch_pixels[s] = p;
                }
            }

            for (int col = c1; col <= c2; col++)
            {
                int s = r2 * width + col;

                for (int i = 0; i < 4; i++)
                {
                    rgba[i] = ((sketch_pixels[s] >> (i * 8)) & 0xFF) << 4;
                }

                s -= width;

                for (int j = r1; j < r2; j++, s -= width)
                {
                    int p = 0;

                    for (int i = 0; i < 4; i++)
                    {
                        p = p | ((((rgba[i] += ((((sketch_pixels[s] >> (i * 8)) & 0xFF) << 4) - rgba[i]) * alpha / 16) >> 4) & 0xFF) << (i * 8));
                    }

                    sketch_pixels[s] = p;
                }
            }

            for (int row = r1; row <= r2; row++)
            {
                int s = row * width + c2;

                for (int i = 0; i < 4; i++)
                {
                    rgba[i] = ((sketch_pixels[s] >> (i * 8)) & 0xFF) << 4;
                }

                s--;

                for (int j = c1; j < c2; j++, s--)
                {
                    int p = 0;

                    for (int i = 0; i < 4; i++)
                    {
                        p = p | ((((rgba[i] += ((((sketch_pixels[s] >> (i * 8)) & 0xFF) << 4) - rgba[i]) * alpha / 16) >> 4) & 0xFF) << (i * 8));
                    }

                    sketch_pixels[s] = p;
                }
            }

            // Make grayscale image from original image, inverted grayscale image from blurred image
            // Then apply Color Dodge mixing for sketch

            int[] grayscale_pixels = pixels.Clone() as int[];

            for (int i = 0; i < sketch_pixels.Length; i++)
            {
                byte[] b_color = BitConverter.GetBytes(sketch_pixels[i]);
                byte[] g_color = BitConverter.GetBytes(grayscale_pixels[i]);

                byte top_gray = (byte)(255 - (byte)((b_color[0] * 11 + b_color[1] * 16 + b_color[2] * 5) / 32));
                byte btm_gray = (byte)((g_color[0] * 11 + g_color[1] * 16 + g_color[2] * 5) / 32);

                byte res_gray = (byte)(top_gray >= 255 ? 255 : Math.Min(btm_gray * 255 / (255 - top_gray), 255));

                sketch_pixels[i] = (b_color[3] << 24) | (res_gray << 16) | (res_gray << 8) | res_gray;
            }

            SketchGenTaskData task_data = new SketchGenTaskData();

            task_data.radius = radius;
            task_data.width  = width;
            task_data.height = height;
            task_data.pixels = sketch_pixels;

            e.Result = task_data;
        }