Regira Drawing is a .NET image processing library that provides a consistent abstraction over image manipulation, format conversion, and multi-layer composition. All operations are available through a single IImageService interface, implemented by both backends.
| Project | Package | Purpose |
|---|---|---|
Common.Media |
Regira.Media |
Shared abstractions, models, DTOs, and ImageBuilder |
Drawing.SkiaSharp |
Regira.Drawing.SkiaSharp |
Preferred — cross-platform (SkiaSharp) |
Drawing.GDI |
Regira.Drawing.GDI |
Windows-only alternative (GDI+) |
<!-- Preferred (cross-platform) -->
<PackageReference Include="Regira.Drawing.SkiaSharp" Version="6.*" />
<!-- Windows-only alternative -->
<PackageReference Include="Regira.Drawing.GDI" Version="6.*" />
```csharp no-compile // Register services.AddSingleton<IImageService, Regira.Drawing.SkiaSharp.Services.ImageService>();
// Use using var image = (await imageService.Parse(inputBytes))!; using var resized = await imageService.Resize(image, new ImageSize(200, 200)); using var webp = await imageService.ChangeFormat(resized, ImageFormat.Webp); return webp.GetBytes()!;
## Core Models
### IImageFile / ImageFile
Represents an image held in memory. Implements `IDisposable`.
| Property | Type | Description |
|----------|------|-------------|
| `Bytes` | `byte[]?` | Raw encoded image bytes |
| `Stream` | `Stream?` | Stream-based access |
| `Size` | `ImageSize?` | Width × height |
| `Format` | `ImageFormat?` | Detected or set format |
| `ContentType` | `string?` | MIME type |
### ImageSize
```csharp
var size = new ImageSize(800, 600);
var half = size / 2; // (400, 300)
var square = (ImageSize)128; // (128, 128) — implicit from int
| Member | Description |
|---|---|
Width, Height |
Integer dimensions |
Empty |
(0, 0) sentinel |
*, / operators |
Scale by integer factor |
Implicit from int |
Creates a square of that side |
Implicit from int[] |
[width, height] |
RGBA struct with hex string support.
| Format | Example | Alpha |
|---|---|---|
#RGB |
#F00 |
255 (opaque) |
#RGBA |
#F008 |
from hex |
#RRGGBB |
#FF0000 |
255 (opaque) |
#RRGGBBAA |
#FF000080 |
from hex |
| Static constants | Color.White, Color.Black, Color.Transparent |
Color c = "#FF000080"; // implicit from string
string rgb = c.Hex; // "#FF0000"
string rgba = c.HexA; // "#FF000080"
Png Jpeg Webp Gif Bmp Tiff Ico Heif Tga Wbmp …
CSS-style distance from each edge.
```csharp no-compile new ImageEdgeOffset(top: 10, left: 20, bottom: 10, right: 20) new ImageEdgeOffset(10, 20) // top + left; Bottom and Right stay null
### ImagePosition
Flags enum for layer alignment. Combine with `|`.
| Value | Description |
|-------|-------------|
| `Absolute` | Use `Offset` coordinates directly |
| `Left` / `Right` | Horizontal edge alignment |
| `Top` / `Bottom` | Vertical edge alignment |
| `HCenter` | Horizontal center |
| `VCenter` | Vertical center |
### ImageLayerOptions
Controls how a layer is positioned and rendered when composited.
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `Size` | `ImageSize?` | *(natural size)* | Override layer dimensions |
| `Margin` | `int` | `0` | Inset from the position anchor |
| `Position` | `ImagePosition` | `Absolute` | Alignment within the canvas |
| `Offset` | `ImageEdgeOffset?` | `(0, 0)` | Pixel offset for `Absolute` positioning |
| `Rotation` | `int` | `0` | Clockwise rotation in degrees |
| `Opacity` | `float` | `1.0` | Transparency (0 = invisible, 1 = opaque) |
## IImageService — Image Operations
`IImageService` is a composite of five focused sub-interfaces. Every method returns a `Task` and accepts an optional trailing `CancellationToken` (omitted below for brevity).
### Parsing
```csharp no-compile
Task<IImageFile?> Parse(Stream? stream)
Task<IImageFile?> Parse(byte[]? bytes)
Task<IImageFile?> Parse(byte[] rawBytes, ImageSize size, ImageFormat? format = null)
Task<IImageFile?> Parse(IMemoryFile file)
The third overload accepts unencoded pixel data together with explicit dimensions and format.
```csharp no-compile
Task
### Transform
```csharp no-compile
Task<ImageSize> GetDimensions(IImageFile input)
Task<IImageFile> Resize(IImageFile input, ImageSize wantedSize, int quality = 100) // preserves aspect ratio
Task<IImageFile> ResizeFixed(IImageFile input, ImageSize size, int quality = 100) // ignores aspect ratio
Task<IImageFile> CropRectangle(IImageFile input, ImageEdgeOffset rect)
Task<IImageFile> Rotate(IImageFile input, int degrees, Color? background = null)
Task<IImageFile> FlipHorizontal(IImageFile input)
Task<IImageFile> FlipVertical(IImageFile input)
SkiaSharp default quality: 80. GDI default quality: 100.
```csharp no-compile
Task
### Draw / Create
```csharp no-compile
Task<IImageFile> Create(ImageSize size, Color? backgroundColor = null, ImageFormat? format = null)
Task<IImageFile> CreateTextImage(LabelImageOptions? options = null)
Task<IImageFile> Draw(IEnumerable<ImageLayer> items, IImageFile? target = null)
ImageBuilder composes multiple layers onto a single canvas using a fluent API.
```csharp no-compile
services.AddSingleton<IImageService, Regira.Drawing.SkiaSharp.Services.ImageService>();
services.AddSingleton<IImageCreator, CanvasImageCreator>();
services.AddSingleton<IImageCreator, LabelImageCreator>();
services.AddSingleton
### Fluent API
```csharp no-compile
var result = await new ImageBuilder(imageService, imageCreators)
.SetBaseLayer(new CanvasImageOptions { Size = new ImageSize(800, 600), BackgroundColor = Color.White })
.Add(layer1, layer2, layer3)
.Build();
| Overload | Description |
|---|---|
SetBaseLayer(IImageFile target) |
Existing image as canvas |
SetBaseLayer(CanvasImageOptions options) |
Create a blank canvas |
SetBaseLayer(IImageLayer layer) |
Any resolved IImageLayer |
If no base layer is set, Build() auto-calculates a canvas that fits all added layers.
Three generic types let you add image files, canvases, or labels as layers:
```csharp no-compile // Existing image — pin to bottom-right new ImageLayer { Source = imageFile, Options = new() { Position = ImagePosition.Right | ImagePosition.Bottom, Margin = 10 } }
// Blank colored rectangle — absolute position
new ImageLayer
// Text label — centered with rotation and opacity
new ImageLayer
### Custom IImageCreator
Implement `IImageCreator<T>` to make `ImageBuilder` understand any source type:
```csharp no-compile
public class QrCodeCreator(IQrService qr) : ImageCreatorBase<QrCodeOptions>
{
public override async Task<IImageFile?> Create(QrCodeOptions input, CancellationToken cancellationToken = default) =>
new ImageFile { Bytes = await qr.Generate(input.Content, input.Size), Format = ImageFormat.Png };
}
services.AddSingleton<IImageCreator, QrCodeCreator>();
IImageService imageService = new Regira.Drawing.SkiaSharp.Services.ImageService();
using var img = await imageService.CreateTextImage("Hello World"); // implicit string shorthand
| Property | Type | Default | Description |
|---|---|---|---|
Text |
string |
(required) | Content to render |
FontName |
string? |
"Arial" |
Font family |
FontSize |
int? |
15 |
Size in points |
Padding |
int? |
0 |
Padding in pixels |
TextColor |
Color? |
#000000FF |
Foreground color |
BackgroundColor |
Color? |
#FFFFFFFF |
Background fill |
Use
Color.Transparentas background when compositing the label over another image.
| Feature | Drawing.SkiaSharp |
Drawing.GDI |
|---|---|---|
| Recommended | ✓ | – |
| Cross-platform | ✓ (Win / Linux / macOS) | Windows only |
| Default resize quality | 80 | 100 |
| EXIF auto-rotate | – | ✓ |
| Printing support | – | ✓ (PrintUtility) |
| Engine | Google Skia | GDI+ (System.Drawing.Common) |
Both implement IImageService and are interchangeable in consuming code.
Common.Media ships DTO types for JSON API contracts. DtoExtensions converts them to domain objects.
| DTO | Description |
|---|---|
ImageLayerDto |
Image bytes + draw options |
ImageLayerOptionsDto |
Draw options: unit, size, position, rotation, opacity |
CanvasImageDto |
Blank canvas definition |
CanvasImageLayerDto |
Canvas with draw positioning |
LabelImageLayerDto |
Text content + label style + draw options |
All measurement properties (Width, Height, Top, Left, …) are float and interpreted according to the DTO’s DimensionUnit property, of type LengthUnit:
LengthUnit |
Description |
|---|---|
Points |
Points / pixels (default) |
Inches |
Physical inches |
Millimeters |
Physical millimeters |
Percent |
Relative to canvas size |
A live demo is available at services.regira.com/office — endpoint /drawing/create, samples at /drawing/samples/**.
Apache License 2.0 — this package contains no license validation and no runtime limits. See LICENSE. A few companion packages are commercially licensed with a free tier; see the licensing overview.