System.Drawing in ASP .NET ASP.NET Core

There are a lot of questions dedicated to the usage of System.Drawing library in ASP.NET services. The most common answer is that Microsoft does not recommend the usage of System.Drawing in ASP.NET services.

In this article, you will learn details about using the interface of System.Drawing library in ASP.NET services with Aspose.Drawing for .NET. The following sections structure all the relevant information:

Why Aspose.Drawing?

Microsoft does not recommend the usage of its System.Drawing library in ASP.NET services. The home page of System.Drawing documentation reads:

Classes within the System.Drawing namespace are not supported for use within a Windows or ASP.NET service. Attempting to use these classes from within one of these application types may result in run-time exceptions and diminished service performance.

There are two basic problems that compel Microsoft to write above caution. The first problem is the usage of GDI+ native library and, as result, usage of GDI handles. While the second concern is concurrency issues. For instance, a process-wide lock occurs during any DrawImage() operation. Therefore, you can not create fast and scalable ASP.NET services using the System.Drawing library from Microsoft.

Installing Aspose.Drawing for .NET API

You can easily download the DLL file from the Downloads section, or configure it via NuGet package Manager with the following command:

PM> Install-Package Aspose.Drawing

Aspose.Drawing for .NET API - Introduction

We understand that System.Drawing is very popular among developers because of its image manipulation features. That is why we have created a similar and compatible API without any compatibility issues. For your convenience and adaptability, the API contains the same names of classes, functions, enums, and interfaces. You can simply change the project reference from System.Drawing to Aspose.Drawing and recompile the program.

Moreover, Aspose.Drawing is a managed library available for NET Framework 2.0 and NET Core 2.0. Unlike System.Drawing, it has no dependencies on any platform like Linux, Azure, etc.

Using Aspose.Drawing in ASP.NET Services

Aspose.Drawing implementation does not depend on GDI or GDI+. It doesn’t use GDI handles and rarely uses process-wide locks.

Demonstration for using Aspose.Drawing in ASP.NET Services

Let us create a simple application that explains the benefits of using Aspose.Drawing library:

Imagine that you decide to create an entirely new web service or application. The application will generate a set of thumbnails with filters attached to them from the user’s image.

You can start by introducing few new entities:

/// <summary>
/// Available types of the filters.
/// </summary>
public enum FilterType
{
/// <summary>
/// Filter for generation of image saturated with red color.
/// </summary>
Red,
/// <summary>
/// Filter for generation of image saturated with green color.
/// </summary>
Green,
/// <summary>
/// Filter for generation of image saturated with blue color.
/// </summary>
Blue,
/// <summary>
/// Filter for generation of a grayscale image.
/// </summary>
Grayscale,
/// <summary>
/// Filter for generation of a negative image.
/// </summary>
Negative,
/// <summary>
/// Filter for generation of a sepia image.
/// </summary>
Sepia,
}
view raw FilterType.cs hosted with ❤ by GitHub
/// <summary>
/// The result of filtered thumbnail generation.
/// </summary>
public class FilterThumbnailResult
{
/// <summary>
/// Initializes a new instance of the FilterThumbnailResult class.
/// </summary>
/// <param name="filterType">The type of filter applied to the thumbnail.</param>
/// <param name="pngData">The image data in PNG format.</param>
/// <param name="width">The width of the thumbnail.</param>
/// <param name="height">The height of the thumbnail.</param>
public FilterThumbnailResult(FilterType filterType, byte[] pngData, int width, int height)
{
this.FilterType = filterType;
this.PngData = pngData;
this.Width = width;
this.Height = height;
}
/// <summary>
/// Gets the type of filter applied to the thumbnail.
/// </summary>
public FilterType FilterType { get; }
/// <summary>
/// Gets the image data in PNG format.
/// </summary>
public byte[] PngData { get; }
/// <summary>
/// Gets the width of the thumbnail.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the height of the thumbnail.
/// </summary>
public int Height { get; }
}

Now you can define the interface for the generation of thumbnails with attached filters:

using System.Collections.Generic;
/// <summary>
/// The interface for thumbnails images generation.
/// </summary>
public interface IThumbnailGenerator
{
/// <summary>
/// Generates a set of thumbnails for all available filters.
/// </summary>
/// <param name="sourceImage">Bytes of the source image.</param>
/// <param name="width">Requested width of the preview images.</param>
/// <param name="height">Requested height of the preview images.</param>
/// <returns>A set of thumbnails.</returns>
IEnumerable<FilterThumbnailResult> GenerateAllThumbnails(byte[] sourceImage, int width, int height);
}

Moving on to the implementation, assume implementing the IThumbnailGenerator interface using System.Drawing library. It will look like the next code snippet:

/// <summary>
/// IThumbnailGenerator interface implementation using System.Drawing.
/// </summary>
public class ThumbnailGenerator
: IThumbnailGenerator
{
/// <summary>
/// A set of thumbnail processors.
/// </summary>
private static readonly FilterThumbnailProcessor[] Processors = new FilterThumbnailProcessor[]
{
ApplyRedFilter,
ApplyGreenFilter,
ApplyBlueFilter,
ApplyGrayFilter,
ApplyNegativeFilter,
ApplySepiaFilter,
};
/// <summary>
/// Delegate for thumbnail processing.
/// </summary>
/// <param name="thumbnail">Thumbnail of the original image.</param>
/// <returns>Result of filtered thumbnail generation.</returns>
private delegate FilterThumbnailResult FilterThumbnailProcessor(Bitmap thumbnail);
/// <summary>
/// Generates a set of thumbnails for all available filters.
/// </summary>
/// <param name="sourceImage">Bytes of the source image.</param>
/// <param name="width">Requested width of the preview images.</param>
/// <param name="height">Requested height of the preview images.</param>
/// <returns>A set of thumbnails.</returns>
public IEnumerable<FilterThumbnailResult> GenerateAllThumbnails(byte[] sourceImage, int width, int height)
{
using (MemoryStream ms = new MemoryStream(sourceImage))
using (Bitmap bitmap = new Bitmap(ms))
{
foreach (var processor in Processors)
{
using (Bitmap previewBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
using (Graphics graphics = Graphics.FromImage(previewBitmap))
{
graphics.DrawImage(bitmap, 0, 0, width, height);
yield return processor(previewBitmap);
}
}
}
}
/// <summary>
/// Applies a red filter to thumbnail.
/// </summary>
/// <param name="thumbnail">Thumbnail of the original image.</param>
/// <returns>Result of filtered thumbnail generation.</returns>
private static FilterThumbnailResult ApplyRedFilter(Bitmap thumbnail)
{
...
}
/// <summary>
/// Applies a green filter to thumbnail.
/// </summary>
/// <param name="thumbnail">Thumbnail of the original image.</param>
/// <returns>Result of filtered thumbnail generation.</returns>
private static FilterThumbnailResult ApplyGreenFilter(Bitmap thumbnail)
{
...
}
/// <summary>
/// Applies a blue filter to thumbnail.
/// </summary>
/// <param name="thumbnail">Thumbnail of the original image.</param>
/// <returns>Result of filtered thumbnail generation.</returns>
private static FilterThumbnailResult ApplyBlueFilter(Bitmap thumbnail)
{
...
}
/// <summary>
/// Applies a gray filter to thumbnail.
/// </summary>
/// <param name="thumbnail">Thumbnail of the original image.</param>
/// <returns>Result of filtered thumbnail generation.</returns>
private static FilterThumbnailResult ApplyGrayFilter(Bitmap thumbnail)
{
...
}
/// <summary>
/// Applies a negative filter to thumbnail.
/// </summary>
/// <param name="thumbnail">Thumbnail of the original image.</param>
/// <returns>Result of filtered thumbnail generation.</returns>
private static FilterThumbnailResult ApplyNegativeFilter(Bitmap thumbnail)
{
...
}
/// <summary>
/// Applies a sepia filter to thumbnail.
/// </summary>
/// <param name="thumbnail">Thumbnail of the original image.</param>
/// <returns>Result of filtered thumbnail generation.</returns>
private static FilterThumbnailResult ApplySepiaFilter(Bitmap thumbnail)
{
...
}
}

The code will work fine but it has one disadvantage - it generates all thumbnails sequentially. System.Drawing library is based on quite old GDI+ library which was designed as single thread library so it can waste resources when there are multiple CPU cores.

Using Aspose.Drawing can help to significantly improve the performance because it does not has any limitations related to single thread. Please try changing only one function:

/// <summary>
/// Generates a set of thumbnails for all available filters.
/// </summary>
/// <param name="sourceImage">Bytes of the source image.</param>
/// <param name="width">Requested width of the preview images.</param>
/// <param name="height">Requested height of the preview images.</param>
/// <returns>A set of thumbnails.</returns>
public IEnumerable<FilterThumbnailResult> GenerateAllThumbnails(byte[] sourceImage, int width, int height)
{
using (MemoryStream ms = new MemoryStream(sourceImage))
using (Bitmap bitmap = new Bitmap(ms))
{
ConcurrentBag<FilterThumbnailResult> collection = new ConcurrentBag<FilterThumbnailResult>();
Parallel.ForEach(Processors, (processor) =>
{
using (Bitmap previewBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
using (Graphics graphics = Graphics.FromImage(previewBitmap))
{
graphics.DrawImage(bitmap, 0, 0, width, height);
collection.Add(processor(previewBitmap));
}
});
return collection;
}
}

This code can now use multiple CPU cores without any significant changes in the original source code. Replacing System.Drawing with Aspose.Drawing expedites the performance of your applications and services.

Download Source Code

You may download the Source Code related to this example for your reference.

Free API Evaluation License

You can request a Free Temporary License to test the API in its full capacity.

Conclusion

In conclusion, you have learned with example that how simple it is to use Aspose.Drawing library in your ASP.NET and ASP.NET Core services. Moreover, Aspose.Drawing has no problems or external dependencies that exist in System.Drawing. Furthermore, you can explore the API in detail by going through the Documentation. Please feel free to contact us anytime via Free Support Forum for any of your queries!

See Also