In this article, we will learn about generating images in C# using SkiaSharp.
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.
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: bitmap
andoutputPath
.
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:
Conclusion
In this article, we have looked at generating and manipulating images in C# using the SkiaSharp library by manipulating some images through examples.