public MandelProps(MainForm mainForm, MandelbrotPosition mandelbrotWindow)
 {
     this.mandelbrotWindow = mandelbrotWindow;
     this.mainForm = mainForm;
 }
        /// <summary>Renders a mandelbrot fractal.</summary>
        /// <param name="position">The MandelbrotPosition representing the fractal boundaries to be rendered.</param>
        /// <param name="imageWidth">The width in pixels of the image to create.</param>
        /// <param name="imageHeight">The height in pixels of the image to create.</param>
        /// <param name="parallelRendering">Whether to render the image in parallel.</param>
        /// <returns>The rendered Bitmap.</returns>
        public static unsafe Bitmap Create(MandelProps mandelProps, MandelbrotPosition position, int imageWidth, int imageHeight, CancellationToken cancellationToken, bool parallelRendering)
        {
            // The maximum number of iterations to perform for each pixel.  Higher number means better
            // quality but also slower.
            int maxIterations = mandelProps.max_iter;
            _paletteColors = Palettes.CreatePaletteColors(mandelProps.cmap);

            // In order to use the Bitmap ctor that accepts a stride, the stride must be divisible by four.
            // We're using imageWidth as the stride, so shift it to be divisible by 4 as necessary.
            if (imageWidth % 4 != 0) imageWidth = (imageWidth / 4) * 4;

            // Based on the fractal bounds, determine its upper right coordinate
            double right = position.CenterX - (position.Width / 2);
            double top = position.CenterY + (position.Height / 2);

            // Get the factors that can be multiplied by row and col to arrive at specific x and y values
            double colToXTranslation = position.Width / (double)imageWidth;
            double rowToYTranslation = -position.Height / (double)imageHeight;

            // Create the byte array that will store the rendered color indices
            int pixels = imageWidth * imageHeight;
            byte[] data = new byte[pixels]; // initialized to all 0s, which equates to all black based on the default palette

            // Generate the fractal using the mandelbrot formula : z = z^2 + c
            try
            {
                // Parallel implementation
                Evaluator evaluator = Evaluators.evaluatorFactory(mandelProps);
                if (parallelRendering)
                {
                    var options = new ParallelOptions { CancellationToken = cancellationToken };
                    Parallel.For(0, imageHeight, options, row =>
                    {
                        double initialY = row * rowToYTranslation + top;
                        fixed (byte* ptr = data)
                        {
                            byte* currentPixel = &ptr[row * imageWidth];
                            for (int col = 0; col < imageWidth; col++, currentPixel++)
                            {
                                double initialX = col * colToXTranslation + right;
                                byte iteration = (byte) evaluator.eval(initialX, initialY);
                                *currentPixel =  iteration;
                            }
                        }
                    });
                }
                // Sequential implementation
                else
                {
                    for (int row = 0; row < imageHeight; row++)
                    {
                        // if (cancellationToken.IsCancellationRequested) break;
                        cancellationToken.ThrowIfCancellationRequested();

                        double initialY = row * rowToYTranslation + top;
                        fixed (byte* ptr = data)
                        {
                            byte* currentPixel = &ptr[row * imageWidth];
                            for (int col = 0; col < imageWidth; col++, currentPixel++)
                            {
                                double initialX = col * colToXTranslation + right;
                                byte iteration = (byte) evaluator.eval(initialX, initialY);
                                *currentPixel = iteration;
                            }
                        }
                    };
                }
            }
            catch (Exception)
            {
            }

            // Produce a Bitmap from the byte array of color indices and return it
            fixed (byte* ptr = data)
            {
                using (Bitmap tempBitmap = new Bitmap(imageWidth, imageHeight, imageWidth, PixelFormat.Format8bppIndexed, (IntPtr)ptr))
                {
                    Bitmap bitmap = tempBitmap.Clone(new Rectangle(0, 0, tempBitmap.Width, tempBitmap.Height), PixelFormat.Format8bppIndexed);
                    UpdatePalette(bitmap);
                    return bitmap;
                }
            }
        }
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (e.KeyCode == Keys.R)
            {
                _mandelbrotWindow = MandelbrotPosition.Default;

                using (MainForm tempForm = new MainForm())
                {
                    double xFactor = Size.Width / (double)tempForm.Width, yFactor = Size.Height / (double)tempForm.Height;
                    _mandelbrotWindow.Width *= xFactor;
                    _mandelbrotWindow.Height *= yFactor;
                }

                UpdateImageAsync();
            }
            else if (e.KeyCode == Keys.S)
            {
                _parallelRendering = false;
                UpdateImageAsync();
            }
            else if (e.KeyCode == Keys.P)
            {
                _parallelRendering = true;
                UpdateImageAsync();
            }
        }