Updated on
To generate an image in C# with ImageMagick, we install the Magick.NET NuGet package, create a MagickImage with a size and a background color, draw on it with Drawables, and save it with Write().
Our example is a 480 by 300 pixel image with a green circle in the middle, built on Magick.NET 15 and .NET 10.
What Is ImageMagick and How Do We Use It in C#?
ImageMagick is a free and open-source cross-platform library for displaying, creating, converting, modifying, and editing raster images. It’s written in C, so a C# application doesn’t call it directly.
That’s the job of Magick.NET, the .NET wrapper around ImageMagick. We install it as a NuGet package, and the package already carries the native ImageMagick binaries for Windows, Linux and Apple Silicon macOS, so there’s nothing else to install on the machine.
The main type is MagickImage. It holds one image in memory, and we can create it from a size and a color or read it from a file or a stream.
To draw, we use Drawables, a builder that collects stroke, fill and shape commands and renders them onto an image in one Draw() call. Saving the result takes one Write() call with a path and a MagickFormat.
MagickImage wraps native memory, so we dispose of it when we’re done, usually with a using declaration.
The Magick.NET README says we can use ImageMagick in our application “without having to install ImageMagick on your server or desktop.”
We will use this library to create an image and draw a green circle in the middle of the image.
To start, let’s navigate to the project directory on the terminal to import the Magick.NET NuGet package into our project:
dotnet add package Magick.NET-Q8-AnyCPU
This version of Magick.NET package has a Quantum Depth of 8 (Q8). Thus, it uses 8 bits per channel for color representation. This means that it uses 256 levels of color intensity for each channel (red, green, blue). AnyCPU means one package carries the native ImageMagick binaries for every platform it supports (32-bit and 64-bit Windows, Linux and Apple Silicon macOS among them) and picks the right one when our application runs.
We use the Magick.NET-Q8-AnyCPU NuGet package because it’s a good balance between color precision and memory usage. This is in comparison to the other Magick.NET AnyCPU packages: Magick.NET-Q16-AnyCPU and Magick.NET-Q16-HDRI-AnyCPU.
Creating a Blank MagickImage
Next, let’s create a static ImageService class in which we define static methods for creating an image, drawing a circle on it, and finally saving the image to a file. We need two namespaces, because the drawing types live in ImageMagick.Drawing.
using ImageMagick;
using ImageMagick.Drawing;
namespace ImageMagickLibrary;
public static class ImageService
{
}
First, let’s define a method to create a blank image:
public static MagickImage CreateBlankImage(uint width, uint height, MagickColor color)
{
return new MagickImage(color, width, height);
}
In our CreateBlankImage() method, we create a blank image with a specified width, height, and color using the MagickImage class from the Magick.NET library. We then return the newly created MagickImage object. The width and height parameters are uint, because that’s what the MagickImage constructor expects. Since Magick.NET 14, sizes and other values that can’t be negative are unsigned.
Drawing on Images in ImageMagick
Rendering a shape onto an image in ImageMagick is a two-step process. First, we need to define a Drawables object that we desire to render onto a MagickImage. Second, using the MagickImage.Draw() we render the Drawables object on the image.Â
Creating Our Drawables Object
So let’s first define a method to create a circle Drawables:
public static Drawables CreateCircle(
int centerX,
int centerY,
int radius,
int strokeWidth,
MagickColor strokeColor,
MagickColor fillColor)
{
if (centerX <= 0 || centerY <= 0 || radius <= 0 || strokeWidth <= 0)
{
throw new ArgumentException(
"The values for the circle's center, radius, and stroke width must be greater than zero."
);
}
var drawables = new Drawables();
drawables.StrokeColor(strokeColor);
drawables.FillColor(fillColor);
drawables.StrokeWidth(strokeWidth);
drawables.Circle(centerX, centerY, centerX, centerY - radius);
return drawables;
}
The CreateCircle() method accepts six parameters:
- X and Y coordinates of the circle’s center:
centerXandcenterY - The radius of the circle:
radius - Width of the circle’s outline:
strokeWidth - Color of the circle’s outline:
strokeColor - Interior color of the circle:
fillColor
We check and throw an ArgumentException if the int parameter values are less than or equal to zero.
Next, using the Drawables class from Magick.NET, we create a drawables object. Drawables comes from the ImageMagick.Drawing namespace, which is why our class imports it. We then set the stroke color, fill color, and stroke width of our drawables object using the respective methods. Lastly, we call the Circle() method to create a circle using the provided properties. The circle’s center is set at (centerX, centerY) and the circle extends to (centerX, centerY - radius), defining its radius.
Finally, we return drawables object which represents the circle we desire to render onto a MagickImage.
Rendering Our Drawables Object
Now that we have a Drawables object representing the circle we wish to render onto our image, let’s create a new helper method to draw the circle:
public static void DrawOnImage(MagickImage image, Drawables drawables)
{
image.Draw(drawables);
}
Our DrawOnImage() method takes a MagickImage and a Drawables object as parameters.
Then we simply call the image.Draw() method with our drawables object as an argument. Draw() renders the provided drawable object onto the image.
Essentially, we modify the image by superimposing the drawable object onto it.
Saving Images in ImageMagick
We have seen how to both create and modify an image, but all of that would be in vain if we didn’t have a way to actually save the image:
public static void SaveImage(MagickImage image, string outputPath)
{
image.Write(outputPath, MagickFormat.Png);
}
Here we define our SaveImage() method. We provide both a MagickImage to save and an outputPath defining the destination for our saved image.
Next, with image.Write(outputPath, MagickFormat.Png), we write the image to our specified output path, in our desired png format.
Putting It All Together
Finally, let’s bring our previously created methods into our Program class and test them out:
using ImageMagick;
using ImageMagickLibrary;
string outputPath = @"outputImage.png";
using var image = ImageService.CreateBlankImage(480, 300, MagickColors.AliceBlue);
var strokeColor = MagickColors.YellowGreen;
var fillColor = MagickColors.Transparent;
var circle = ImageService.CreateCircle((int)image.Width / 2, (int)image.Height / 2, 100, 5, strokeColor, fillColor);
ImageService.DrawOnImage(image, circle);
ImageService.SaveImage(image, outputPath);
Console.WriteLine("Image generated and saved successfully.");
First, we define the path where our generated image outputImage.png will be saved.
Next, we create a blank image with a width of 480 pixels and a height of 300 pixels. We declare it with using var, so the image and its native memory are released when the program ends. We look at this pattern in more detail in disposing IDisposable objects. We set the fill color to MagickColors.AliceBlue using one of the predefined colors from the MagickColors class. MagickColors is a class within the Magick.NET library that provides predefined color objects.
As we prepare to draw our circle, we again use the MagickColors class to define our strokeColor as YellowGreen and our fillColor as Transparent.
Next, we call our CreateCircle() static method to generate a circle. We perform a simple calculation to place the circle at the center of our image and set the radius to 100 pixels. Since image.Width and image.Height are uint, we cast them to int with an explicit conversion first. CreateCircle() takes int values so it can reject negative ones. We set the strokeWidth at 5, and finally pass in our previously defined strokeColor and fillColor values.
With our circle created, we invoke our DrawOnImage() method to complete our image generation and modification.
Finally, we save our modified image to our earlier defined outputPath, with an affirmative message printed on the console:
Image generated and saved successfully.
Next, let’s have a look at our image:
Why Does Older Magick.NET Code Not Compile Anymore?
Code written for Magick.NET 13, like the first version of this article, stops compiling on Magick.NET 14 and 15. Two changes cause it, and a third decides where the package can run.
The drawing types moved. Drawables and the other drawing classes now live in the ImageMagick.Drawing namespace, so the old code fails with “The type or namespace name ‘Drawables’ could not be found” until we add using ImageMagick.Drawing;.
Sizes became unsigned. Magick.NET 14 changed values that can’t be negative from int to uint, so the MagickImage constructor takes uint sizes, and image.Width needs a cast before it goes into an int parameter.
Magick.NET 15 targets net10.0 and netstandard2.0. A .NET 8 project still installs it through the netstandard2.0 build. On an Intel Mac, though, we stay on Magick.NET 14, because version 15 no longer ships Intel macOS binaries.
| In Magick.NET 13 | In Magick.NET 15 | What we change |
|---|---|---|
Drawables in ImageMagick | Drawables in ImageMagick.Drawing | add using ImageMagick.Drawing; |
new MagickImage(color, int, int) | new MagickImage(color, uint, uint) | take uint width and height |
image.Width and image.Height are int | both are uint | cast with (int) before passing them to an int parameter |
targets netstandard2.0 and netstandard2.1 | targets net10.0 and netstandard2.0 | nothing on .NET 8, which gets the netstandard2.0 build; a net10.0 project gets the net10.0 build |
Conclusion
We’ve created a blank image with Magick.NET, drawn a circle on it with Drawables and saved it as a PNG, all on Magick.NET 15 and .NET 10.
SkiaSharp is another option, and we show how to generate an image with SkiaSharp in C# as well. If we’d rather avoid native binaries altogether, we can also generate images with ImageSharp, a fully managed library.
Tested with .NET 10.0.10 and Magick.NET-Q8-AnyCPU 15.0.0.

