System.Drawing package is added back to .NET 5. It provides common GDI+ graphics to draw images: System.Drawing.Bitmap, System.Drawing.BitmapData, System.Drawing.Brush, System.Drawing.Font, System.Drawing.Graphics, System.Drawing.Icon and etc. Previously we have to use third-party libraries to draw images in .NET Core 2.x/3.x as article Graphics Programming and Image Processing in .NET Core 2.x shows. This article provides detailed steps to implement the same in ASP.NET 5 applications.
Prerequisites
.NET 5 SDK is required to create projects through command line. Refer to page .NET 5 is Officially Released for more details about .NET.
Create sample ASP.NET project
Use the following commands to create a sample project:
mkdir dotnet5-drawing
cd dotnet5-drawing
dotnet new webapp
The project structure looks like the following screenshot:
Add package reference
The package required is available here NuGet Gallery | System.Drawing.Common 5.0.1.
Use the following command to add reference to the package:
dotnet add package System.Drawing.Common --version 5.0.1
After this, the project file will be updated accordingly:
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="5.0.1" />
</ItemGroup>
Add a controller
Let's add a controller class to draw images. Follow these steps:
- Add package reference
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
Or upgrade it already exists:
dotnet tool update --global dotnet-aspnet-codegenerator
- Add a controller class
Generate a controller class using template:
dotnet aspnet-codegenerator --project . controller -name CaptchaController
A new class file named CaptchaController.cs will be generated.
Update controller
Update the controller implementation to draw a captcha image in the response.
Replace the content with the following:
using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace dotnet5_drawing
{
[Route("/api/[controller]")]
public class CaptchaController : Controller
{
// Background color
private readonly Color bgColor = Color.FromArgb(0xe9, 0xec, 0xef);
// Code color
private readonly Color codeColor = Color.FromArgb(0x00, 0x69, 0xd9);
// Obstruction color
private readonly Color obsColor = Color.FromArgb(0x28, 0xa7, 0x45);
[HttpGet]
[AllowAnonymous]
public async Task IndexAsync()
{
var (content, contentType) = GenerateCaptchaImage();
HttpContext.Response.ContentType = contentType;
await HttpContext.Response.BodyWriter.WriteAsync(content);
}
private (byte[] content, string contentType) GenerateCaptchaImage()
{
// Setup output format
var contentType = "image/png";
// Image width
const int imageWidth = 150;
// Image height
const int imageHeight = 50;
// Captcha code length
const int captchaCodeLength = 4;
// Captcha code string, all the possible chars that can appear in the image.
const string captchaCodeString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
// Generate random characters
StringBuilder s = new StringBuilder();
using var ms = new MemoryStream();
// Create the image
using Bitmap bitmap = new Bitmap(imageWidth, imageHeight);
// Create the graphics
using Graphics graphics = Graphics.FromImage(bitmap);
// Write bg color
graphics.FillRectangle(new SolidBrush(bgColor), 0, 0, imageWidth, imageHeight);
// Add obstructions
using (Pen pen = new Pen(new SolidBrush(obsColor), 2))
{
for (int i = 0; i < 10; i++)
{
graphics.DrawLine(pen, new Point(random.Next(0, imageWidth - 1), random.Next(0, imageHeight - 1)), new Point(random.Next(0, imageWidth - 1), random.Next(0, imageHeight - 1)));
}
}
for (int i = 0; i < 100; i++)
{
bitmap.SetPixel(random.Next(imageWidth), random.Next(imageHeight), Color.FromArgb(random.Next()));
}
// Font
using (Font font = new Font(FontFamily.GenericMonospace, 32, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Pixel))
{
for (int i = 0; i < captchaCodeLength; i++)
{
s.Append(captchaCodeString.Substring(random.Next(0, captchaCodeString.Length - 1), 1));
// Write char to the graphic
graphics.DrawString(s[s.Length - 1].ToString(), font, new SolidBrush(codeColor), i * 32, random.Next(0, 24));
}
}
// Save image, image format type is consistent with response content type.
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return (ms.ToArray(), contentType);
}
}
}
The controller will be exposed via route /api/captcha.
The most important logic is implemented in function GenerateCaptchaImage. This function does the following:
- Use namespace System.Drawing.
- Instantiate a Bitmap object which is then used to instantiate Graphics object.
- The Graphics object is used to draw lines, dots and characters. The characters are randomly selected from a predefined string. The color and formats of these drawings are defined by the pens/brushes. The position of the characters are randomly decided too.
- At last the bitmap binary content is save to a memory stream which is eventually output to the HTTP response stream.
Update Startup.cs
We need to update Startup.cs file to ensure controller routes are mapped and exposed:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});
}
Build and run the application
Now we can build and run the application using the following command:
dotnet build
dotnet run
The output of the last command looks something like the following:
Building...
info: Microsoft.Hosting.Lifetime[0]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: F:\Projects\dotnet5-drawing
The image can be viewed via opening the following URL in your browser:
https://localhost:5001/api/captcha
The captcha image looks like the following screenshot:
Add image to Razor pages
You can also add the image in other pages. For example, the following code snippet displays the image in Index page:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
<p><img src="/api/captcha" /></p>
</div>
The index page looks like the following screenshot:
Summarize
It's very easy to use System.Drawing.Common package to draw images in ASP.NET Core or .NET core. For more information about GDI+, refer to GDI+ - Win32 apps | Microsoft Docs.