Until now, the images generated by the app follow a simplified coloring scheme:
the pixels are white or black depending if they are outside or inside the Mandelbrot set.
But, how can we add colors to these images?
A basic technique to colorize the Mandelbrot set is the “escape time” algorithm, which selects
a color for a point using the number of iterations taken to check if the point is part of the set.
Detecting if a pixel is inside the set is not enough for this algorithm, that’s why we’ll need to
update the Verifierstruct to return a custom Verification object. It’s a simple change, but many things
are going to break inside the app. Luckily, Golang’s type system and our tests will help us in this task!
Now take a look at the Representation of the set;
you’ll notice that every pixel is represented with a boolean, which is not useful anymore.
We will replace this boolean with our brand new Verificationstruct:
To be fair, we are already using colors to represent the set: black and white. It’s the most basic palette,
but this should give us a clue of how to handle different colors. Let’s create a new Palettestruct that
will be in charge of the colors used inside the image:
A palette should be responsible for returning colors, but the logic of choosing which color to use depending
on the Verification object should be handled by a different struct.
We will call it Coloring, and we will build it with a basic implementation of the “escape time” algorithm.
Now let’s edit the Exporterstruct to make use of Coloring:
This change makes it simpler to separate the color election from “painting” the image. Now we can add as many
color palettes as we want without the need of changing the exportation process. To put into practice
this idea, we will add our first range of colors: the Bob Ross palette. In case you don’t know Mr Ross,
he was an American painter that hosted The joy of painting, a widely famous
TV show where he taught how to paint taking simple steps
and using a limited palette of colors:
I have also added a 256 color palette called “Plan 9” which is based on the
one inside the official palette package (image/color/palette).
First colorized image
Now that everything is ready to generate our first colorized version of the Mandelbrot set, let’s
improve the main method by letting the user select the palette and the coloring strategy:
As you can see in the image above, the “escape time” algorithm creates bands of color. Read more about
how other algorithms deal with this problem. I tried to code the “continuous coloring”
but gave up after failing to use it correctly :-(
When looking with detail at the resulting images, you’ll easily notice pixel artifacts (aliasing)
especially in dense areas like the one shown in the next image:
To fix this we can use sampling, which means selecting various random points inside every pixel and averaging
the sum of the color values. This is equivalent to rendering the image at a higher resolution and scaling it down.
For example, if the size of the final image must be 200x200 and the chosen antialiasing value is 4,
we will need to generate a 800x800 image and then resize it back to 200x200 while using
an interpolation method (“linear”, “cubic”, “lanczos3”, …)
The new antialiasing parameter is going to affect the size of our representation. That’s why
instead of working directly with width, height and antialiasing factor values,
a new Sizestruct will handle all theses parameters:
This new Sizestruct is going to be used inside Config:
It’s also going to affect the size of our Representation, because the size of the set
needs to be multiplied by the antialiasing factor:
The final step of the antialiasing process is resizing the raw image to the original size
using an interpolation function.
To simplify this process we are going to use Resize; the “import” feature of Golang
allows the use third party libraries very easily!
Note: we have defined Lanczos3 as the interpolation function because,
even though not being the fastest method, it gives very good results.
Don’t forget to run the next command; it downloads all the external tools
imported inside our source code:
After adding the antialiasing parameter to the main method, the app will be ready to generate improved images;
checkout the improved code and generate a new image:
Thanks to the antialiasing (-aa=4), our resulting image has smoother colors than the previous version.
Check the difference in a highly zoomed section:
Our next goal is to generate multiple images while zooming to a custom point in the set. This will give us
the content to generate animations in future posts, but before that we will need to improve the
poor performance of our code. See you in the next posts!