3
\$\begingroup\$

I'm currently trying to optimize this class I have for fractal generation. The equation is meant to be pluggable; I have used z => z*z + c so that it's not using the Complex.Pow method (eliminating a method call per iteration). Without changing it from the Complex data type, how else can I optimize this method, and is there a better way I can set the colors in the bitmap? Methods shown below.

    public void Calculate()
    {
        var maxIterations = MaxIterations;
        var stopwatch = new Stopwatch();
        var histogram = new int[maxIterations];
        var width = Bitmap.Width;
        var height = Bitmap.Height;
        var iterationCount = new int[width, height];
        var colors = new Color[width, height];
        var epsilon = new Vector2((MaxBounds.X - MinBounds.X) / (width-1), (MaxBounds.Y - MinBounds.Y) / (height-1));
        var scale = new Vector2(1 / epsilon.X, 1 / epsilon.Y);
        var escapeRadiusSquared = EscapeRadius*EscapeRadius;

        stopwatch.Start();
        Parallel.ForEach(Multithreading.Iterate(MinBounds.X, MaxBounds.X, epsilon.X), cx =>
        {
            for (var cy = MinBounds.Y; cy < MaxBounds.Y; cy += epsilon.Y)
            {
                var iterations = 0;
                var c = new Complex(cx, cy);
                var z = StartingPoint(c);

                while (z.Real*z.Real + z.Imaginary*z.Imaginary < escapeRadiusSquared && iterations < maxIterations)
                {
                    z = Algorithm(z, c);
                    iterations++;
                }
                var xx = (int) Math.Round((cx - MinBounds.X)*scale.X);
                var yy = height - 1 - (int) Math.Round((cy - MinBounds.Y)*scale.Y);
                if (iterations >= maxIterations)
                {
                    colors[xx, yy] = Color.Black;
                }
                else if (SmoothingType == SmoothingType.Continuous || SmoothingType == SmoothingType.Smooth)
                {
                    for (var i = 0; i < 3; i++)
                    {
                        z = Algorithm(z, c);
                        iterations++;
                    }

                    var mu = iterations + 1 - Math.Log(Math.Log(z.Magnitude))/Math.Log(2);
                    if (SmoothingType == SmoothingType.Continuous)
                    {
                        mu = mu/ maxIterations * ColorPalette.Count;
                    }
                    colors[xx, yy] = ColorUtils.GetColor(mu, ColorPalette);
                }
                else if (SmoothingType == SmoothingType.None)
                {
                    colors[xx, yy] = iterations == maxIterations
                        ? Color.Black
                        : ColorPalette[iterations%ColorPalette.Count];
                }
                else if (SmoothingType == SmoothingType.Histogram)
                {
                    iterationCount[xx, yy] = iterations;
                    histogram[iterations]++;
                }
                else
                {
                    throw new ArgumentOutOfRangeException();
                }
            }
        });

        stopwatch.Stop();
        TimeElapsed = stopwatch.Elapsed;
        if (SmoothingType == SmoothingType.Histogram)
        {
            Parallel.ForEach(Multithreading.Iterate(MinBounds.X, MaxBounds.X, epsilon.X), cx =>
            {
                for (var cy = MinBounds.Y; cy < MaxBounds.Y; cy += epsilon.Y)
                {
                    var xx = (int)Math.Round((cx - MinBounds.X) * scale.X);
                    var yy = height - 1 - (int)Math.Round((cy - MinBounds.Y) * scale.Y);
                    var total = 0;
                    for (var i = 0; i < MaxIterations; i++)
                    {
                        total += histogram[i];
                    }

                    var hue = 0.0;
                    for (var i = 0; i < iterationCount[xx, yy]; i += 1)
                    {
                        hue += ((double)histogram[i])/total;
                    }
                    hue = Math.Round(hue*(ColorPalette.Count - 1));
                    colors[xx, yy] = ColorPalette[(int)hue];
                }
            });
        }
        FastBitmap.SetPixels(colors);
    }

And the FastBitmap class.

    public unsafe void SetPixels(Color[,] colors)
    {
        var data = Bitmap.LockBits(new Rectangle(0, 0, Bitmap.Width, Bitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
        var scan0 = data.Scan0;
        var width = colors.GetLength(0);
        var height = colors.GetLength(1);

        Parallel.For(0, width, x =>
        {
            for (var y = 0; y < height; y++)
            {
                var color = colors[x, y];
                var imagePointer = (byte*) scan0.ToPointer(); // Pointer to first pixel of image
                var offset = y*data.Stride + 3*x; // 3x because we have 24bits/px = 3bytes/px
                var px = imagePointer + offset; // pointer to the pixel we want
                px[0] = color.B; // Red component
                px[1] = color.G; // Green component
                px[2] = color.R; // Blue component
            }
        });

        Bitmap.UnlockBits(data); // Set the data again
    }

Multithreading.Iterate:

public static class Multithreading
{
    public static IEnumerable<double> Iterate(
        double fromInclusive, double toExclusive, double step)
    {
        for (var d = fromInclusive; d < toExclusive; d += step) yield return d;
    }
}
\$\endgroup\$
1
\$\begingroup\$

Some ideas :

  • use a switch case around the if smoothingtype statement, you gain clarity (and the poor reader that I am too by the way)

  • the following line is a constant inside the for loop, put it outside

var xx = (int) Math.Round((cx - MinBounds.X)*scale.X);

  • have you use a profiler ? even a basic one can give you usefull hints on where this code spends its CPU time.

  • there are many calls to Algorithm(z, c); but I don't see the method in your code

Hope this helps !

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.