In this article, we will learn about generating images in C# using SkiaSharp.

To download the source code for this article, you can visit our GitHub repository.

Let’s dive in.

What is SkiaSharp?

The SkiaSharp library is a cross-platform 2D graphics API for .NET platforms based on Google’s Skia Graphics library, a comprehensive library for drawing text, geometries, and images. This library is the graphics engine used in various Google products, such as Google Chrome, ChromeOS, Android, and Flutter.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

In this article, we will explore using SkiaSharp to create a blank image and then draw a square in the middle of the created image.

Let’s start by navigating to our project directory on the terminal to import the NuGet package:

dotnet add package SkiaSharp

Creating an Image

Next, let’s create an ImageService static class where we will define some helper methods, beginning with creating a blank image:

public static SKBitmap CreateBlankImage(int width, int height)
{
    var bitmap = new SKBitmap(width, height);
    using var canvas = new SKCanvas(bitmap);
    canvas.Clear(SKColors.White);

    return bitmap;
}

Here we see the definition of our CreateBlankImage() static method, which takes width and height as parameters and returns a bitmap of type SKBitmap, which represents an image in SkiaSharp.

We begin by creating an SKBitmap object with the given width and height. Following that, we initialize a new SKCanvas object with our SKBitmap. By this, we link the canvas to the bitmap. The SKCanvas class represents the canvas or drawing surface in SkiaSharp. It provides methods for drawing graphics, text, and images.

When we initially create a bitmap, its pixel data consists of undefined random values. In attaching the bitmap to a canvas using the SKCanvas() constructor, we create a means to manipulate the image pixel data. Without the canvas, the bitmap will remain uninitialized random values.

Note that SKCanvas objects are disposable, thus the addition of a using declaration to ensure proper cleanup.

Next, we clear the entire canvas using the SKCanvas.Clear(SKColor) method. The Clear() method sets all the pixels within the canvas to a single color, which in our case is SKColors.White. If we call the parameterless overload of Clear() all pixels will be set to SKColor.Empty (#00000000).

Manipulating an Image

Now, let’s create a method to draw a square at the center of our image:

public static void DrawSquareOnImage(SKBitmap bitmap, int squareSize, int startX, int startY)
{
    if (squareSize <= 0 || startX <= 0 || startY <= 0)
    { 
        throw new ArgumentException("Square size and coordinates must be greater than zero.");
    }
    using var canvas = new SKCanvas(bitmap);
    using var paint = new SKPaint();
    paint.Color = SKColors.Red;
    var square = new SKRect(startX, startY, startX + squareSize, startY + squareSize);
    canvas.DrawRect(square, paint);
}

Here we define our DrawSquareOnImage() method, which will draw a square on the provided image. It takes four parameters namely the SKBitmap object we’ll draw on, squareSize, startX and startY. squareSize represents the size of the square we will draw, while startX and startY represent the X and Y coordinates of the square’s starting point.

First, we validate that all of the int parameter values are greater than zero.

Next, we create a new SKCanvas object for drawing on our bitmap. Following that, we instantiate a new SKPaint object and set its Color to SKColors.Red. We use SKPaint to define the style and color information for drawing objects: geometries, text, and bitmaps.

Next, we create an instance of SKRect, a struct that holds the four coordinates for a rectangle, which in our case, is a square. Then finally, we draw the square on the canvas using the DrawRect() method, which takes both an SKRect parameter (square) and an SKPaint (paint) parameter.

Saving an Image

Now, let’s create a method to save an image:

public static void SaveImage(SKBitmap bitmap, string outputPath)
{
    using var stream = new FileStream(outputPath, FileMode.Create, FileAccess.Write);
    using var image = SKImage.FromBitmap(bitmap); 
    using var encodedImage = image.Encode(); 
    encodedImage.SaveTo(stream);
}

Here we define our SaveImage() static method, which takes two parameters: bitmapandoutputPath.

We first instantiate a FileStream object for file creation, setting the FileMode to FileMode.Create to create or overwrite a file if it already exists, and the FileAccess to FileAccess.Write.

Next, we convert our bitmap to an SKImage, then call the parameterless Encode() method, which by default encodes our SKImage into PNG format. We then finally save it to the FileStream.

The using declarations ensure that the Dispose() method is called on each disposable object after use, to ensure proper resource management.

Generating Sample Images

Now, let’s bring our previously created methods into our Program class and create a new image:

string outputPath = @"outputImage.png";

int imageWidth = 400;
int imageHeight = 300;
int squareSize = 120;
int startX = (imageWidth - squareSize) / 2;
int startY = (imageHeight - squareSize) / 2;
var bitmap = ImageService.CreateBlankImage(imageWidth, imageHeight);
ImageService.DrawSquareOnImage(bitmap, squareSize, startX, startY);
ImageService.SaveImage(bitmap, outputPath);

Console.WriteLine("Image generated and saved successfully.");

We specify a path to the directory, where our generated image outputImage.png is saved. Our desired directory is the folder containing our .exe file, i.e. in our “net8.0” folder.

We set the imageWidth, imageHeight and squareSize to 400px, 300px and 120px, respectively.

Next, we compute the starting point for our square with respect to the x-axis by providing the imageWidth and squareSize. Similarly, we provide the imageHeight and squareSize to compute our square’s y-axis starting point.

Afterwards, we invoke the CreateBlankImage() method from our ImageService class, providing the imageWidth and imageHeight, to create our bitmap.

With our image created, we then call our DrawSquareOnImage() method, providing our bitmap and the values we computed earlier: squareSize, startX, startY. With this, the square is drawn on our bitmap.

Finally, we save our modified bitmap to our earlier defined outputPath, with a confirmation message printed on the console.

Let’s test it out:

Image generated and saved successfully.

As expected, we get a success message. Now let’s check our image:

Image generated using SkiaSharp library

Conclusion

In this article, we have looked at generating and manipulating images in C# using the SkiaSharp library by manipulating some images through examples.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!