Draw Images in ASP.NET Core 5

Raymond Raymond event 2021-02-16 visibility 6,376
more_vert
Draw Images in ASP.NET Core 5

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:

2021021674034-image.png

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:

1) Add package reference

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design  

Or upgrade it already exists:

dotnet tool update --global dotnet-aspnet-codegenerator 

2) 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:

2021021690638-image.png

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:
2021021691036-image.png

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.

More from Kontext
comment Comments
No comments yet.

Please log in or register to comment.

account_circle Log in person_add Register

Log in with external accounts