Pages

Drawing lines and polylines

I need to create a PNG file where lines are drawn on a background that could be of a solid color or transparent. It's an easy job, but it requires to use a few Magick++ classes. As we are going to see, the Image class has a central role in it.

Background color

There are many ways of creating a Magick++ Color. Actually, there are even many Magick++ Color flavors, rigorized as classes that all derives from Magick::Color. Here my requirements are not very demanding, so I can easily settle for using the base class, and just a couple of its constructors.

For simple standard colors, ImageMagick uses the X11 name convention, so that I can define a very light orange background in this way:
Magick::Color background("papaya whip");
A full list of X11 color names is on wikipedia.

We can specify the level of transparency of a color at its creation time, calling the Color constructor that expects in input red, green, blue, and alpha (AKA, its transparency) quantums. That's how I create a fully transparent color:
Magick::Color background(0, 0, 0, QuantumRange);
I have already written something about the relation about Quantum, QuantumRange and Imagick++ in a previous post, so here I just assume you know what I am talking about.
Being fully transparent, it doesn't matter the quantum level for the first three components. A fully transparent black (that the one I picked up) does not differ from any other fully transparent color.

Creating an image

Once I have a background color, I can create an 50x50 Image object in this way:
Magick::Image image(Magick::Geometry(50, 50), background);
It is usually a smart idea to specify just after creation which kind of image we are interested in:
image.magick("PNG");
Another common early setting is about the kind of stroke I want to use. Here I set its width and the line cap shape:
image.strokeWidth(4);
image.strokeLineCap(Magick::RoundCap);
Drawing lines

I am about to draw the red/blue asterisk on transparent background that you see here aside.

Firstly I draw the red X, than I switch to blue and I draw a plus. In both case, what I actually draw are simple lines, two red ones and two blue ones, with no relation one to the others. It is all pretty easy:
image.strokeColor(Magick::Color("red")); // 1
image.draw(Magick::DrawableLine(10, 10, 40, 40)); // 2
image.draw(Magick::DrawableLine(40, 10, 10, 40));

image.strokeColor(Magick::Color("blue")); // 3
image.draw(Magick::DrawableLine(5, 25, 45, 25));
image.draw(Magick::DrawableLine(25, 5, 25, 45));
1. Select the red color.
2. Draw a first line from (10, 10) to (40, 40), then a second one from (40, 10) to (10, 40).
3. Then I switch to blue, and I draw the other couple of lines.

Drawing polylines

Things get a bit more complicated in case of polylines. If a line has just a beginning and an end, a polyline is defined by a number of points, each point describing an object vertex. I need to call the Image::draw() method on the entire collection of points. To do that, we use a STL list container parametrized for the Magick Coordinate class.

Before filling the list, I set a few other stroke options, and the filling color. More details below the code:
image.strokeAntiAlias(true); // 1
image.strokeLineJoin(Magick::RoundJoin); // 2
image.strokeColor(Magick::Color("red")); // 3
image.fillColor(background); // 4

std::list<Magick::Coordinate> line; // 5
line.push_back(Magick::Coordinate(11,4));
line.push_back(Magick::Coordinate(41,23));
line.push_back(Magick::Coordinate(27,42));
line.push_back(Magick::Coordinate(59,91));

image.draw(Magick::DrawablePolyline(line)); // 6
1. Activate the anti-aliasing. This means that Magick will do its best to avoid jaggies, trying to smoothing the drawing.
2. I say to Magick to round the join in the connection between parts of the polyline.
3. The foreground color is the bright standard X11 red.
4. If I don't say to Magick to use a specific color as filler, the stroke color will be used. Not what I want to get here.
5. Instantiate a list of Coordinates, and fill it with the points I need.
6. And finally, I convert the list in a drawable polyline, and I draw it.

Saving the image in a file

We need a last step, writing the image to a file:
image.write("/tmp/test.png");
Exceptions

The Magick++ exceptions are derived by the STL exception. So you could try/catch your Magick++ code just for the generic standard exception. If you want finer management you can add more catch blocks to your code. For instance, here I was interested in giving a specific message for the ErrorBlob exception:
try
{
  // Magick++ code goes here
}
catch(Magick::ErrorBlob& ex)
{
  std::cout << "Magick ErrorBlob: " << ex.what() << std::endl;
}
catch(std::exception& ex)
{
  std::cout << "Error: " << ex.what() << std::endl;
}

No comments:

Post a Comment