//Thread to generate a number of lines of a mandelbrot figure //Which lines is specified by the context that is passed through the arg parameter private unsafe void MandelbrotThread(object arg) { Thread.CurrentThread.Priority = ThreadPriority.Lowest; MandelbrotContext c = (MandelbrotContext)arg; //Make copies of a couple of settings to prevent problems //when they are changed during the generating int maxmandel = MandelbrotStyle == MandelbrotColorStyle.Paletted ? MandlebrotMaxCount : 512; Color[] paletteCopy = new Color[MandelbrotPalette.Count]; MandelbrotPalette.CopyTo(paletteCopy, 0); uint * pBitmap = c.bitmapPtr; double xStep = MandelbrotWidth / c.width; double yStep = MandelbrotHeight / c.height; double yreal = c.startLine * yStep + MandelbrotY; for (int y = c.startLine; y < c.startLine + c.nrLines; y++) { //make it possible to cancel generating if (mCancelMandelbrot) { return; } uint * curLine = pBitmap; double xreal = MandelbrotX; for (int x = 0; x < c.width; x++) { //calculate the mandelbrot number (count) double a = 0; double b = 0; int count = 0; do { double a_old = a; a = a * a - b * b + xreal; b = 2 * a_old * b + yreal; count++; } //instead of using sqrt (which is slow), simply square the other side of the equation while (a * a + b * b <= 2 * 2 && count < (maxmandel - 1)); if (MandelbrotStyle == MandelbrotColorStyle.Paletted) { int idx = count * (paletteCopy.Length - 1) / maxmandel; int rem = (count * (paletteCopy.Length - 1) % maxmandel) / (paletteCopy.Length - 1); Color col = paletteCopy[idx]; if (rem != 0) { //Interpolate between two palette colors Color col2 = paletteCopy[idx + 1]; col = Color.FromArgb( col.R + (col2.R - col.R) * rem / (maxmandel / (paletteCopy.Length - 1)), col.G + (col2.G - col.G) * rem / (maxmandel / (paletteCopy.Length - 1)), col.B + (col2.B - col.B) * rem / (maxmandel / (paletteCopy.Length - 1))); } *curLine++ = (uint)col.ToArgb(); } else { //Make the maximum value (which depicts infinity to some extend) black, //because it looks better if (count == 511) { count = 0; } //Extract some bits and use them as r, g and b values (after scaling) uint r = (uint)(count & 7) * 32; uint g = (uint)((count >> 3) & 7) * 32; uint b_ = (uint)((count >> 6) & 7) * 32; //Set the current pixel in ARGB format *curLine++ = 0xFF000000u | (r << 16) | (g << 8) | b_; } //next mandelbrot coordinate xreal += xStep; } //next line (sometimes there is padding at the end, so use the stride and divide by 4, because it's a uint pointer) pBitmap += c.stride / 4; yreal += yStep; } }
public unsafe void StartGenerateMandelbrot(bool generate2Times = false) { //When another mandelbrot is already being generated, //cancel it when a new mandelbrot is requested. //This is useful when a 2x version is generated in the bg, //while the use wants to zoom in already if (mIsGeneratingMandlebrot) { mCancelMandelbrot = true; //Wait till the currently being generated mandelbrot is canceled while (mIsGeneratingMandlebrot) { Thread.Sleep(1); //And sleep this thread for a little while, to give the mandelbrot threads a chance to cancel themselves } } mCancelMandelbrot = false; mIsGeneratingMandlebrot = true; //make sure the palette contains at least 2 entries when using the paletted mandelbrot style if (MandelbrotStyle == MandelbrotColorStyle.Paletted && MandelbrotPalette.Count < 2) { BeginUpdatePalette(); MandelbrotPalette.Clear(); MandelbrotPalette.Add(Color.DarkBlue); MandelbrotPalette.Add(Color.White); MandelbrotPalette.Add(Color.Orange); MandelbrotPalette.Add(Color.White); MandelbrotPalette.Add(Color.Black); EndUpdatePalette(); } //use a bitmap and pointers to it's data for much faster drawing int multiplier = generate2Times ? 2 : 1; Bitmap bitmap = new Bitmap(Width * multiplier, Height * multiplier); //Lock the bits of the bitmap to access the pixels using a pointer directly BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb); uint * pBitmap = (uint *)bitmapData.Scan0; //start multiple threads for much faster generating int nrthreads = 64; Task[] tasks = new Task[nrthreads]; //calculate the number of lines that are calculated by each thread int nrlinesperthread = bitmap.Height / nrthreads; int y = 0; for (int i = 0; i < nrthreads; i++) { //Setup the params for the mandelbrot thread MandelbrotContext c = new MandelbrotContext(); c.width = bitmap.Width; c.height = bitmap.Height; c.bitmapPtr = pBitmap; c.startLine = y; //In case of the last thread, make sure all lines left are being calculated, //because nrlinesperthread is always a whole number of lines. When the image //isn't a multiple of nrthreads, a couple of lines wouldn't be calculated otherwise if (i == nrthreads - 1) { c.nrLines = bitmap.Height - y; } else { c.nrLines = nrlinesperthread; } c.stride = bitmapData.Stride; c.multiplier = multiplier; y += nrlinesperthread; //Skip the number of lines that are calculated by this thread pBitmap += bitmapData.Stride / 4 * nrlinesperthread; //Start the thread (task) tasks[i] = Task.Factory.StartNew(MandelbrotThread, c); } //Wait for all tasks to be completed in a new thread, //to return from this method immediately new Thread((ThreadStart) delegate { Task.WaitAll(tasks); //Unlock the bitmap again (would cause a memory leak otherwise) bitmap.UnlockBits(bitmapData); if (!mCancelMandelbrot) { mIsGeneratingMandlebrot = false; //Invoke the ready event if (MandlebrotReady != null) { MandlebrotReady.Invoke(bitmap, generate2Times); } } else { mIsGeneratingMandlebrot = false; } }).Start(); }