Avalonia provides comprehensive image handling capabilities through the Bitmap class and related imaging APIs. The framework supports loading images from files, streams, and memory, as well as creating and manipulating bitmaps programmatically.
Bitmap Class
The primary class for working with bitmap images.
Constructors
// Load from file path
var bitmap = new Bitmap("image.png");
// Load from stream
using var stream = File.OpenRead("image.jpg");
var bitmap = new Bitmap(stream);
// Create from pixel data
var bitmap = new Bitmap(
format: PixelFormat.Rgba8888,
alphaFormat: AlphaFormat.Premul,
data: pixelDataPtr,
size: new PixelSize(width, height),
dpi: new Vector(96, 96),
stride: width * 4
);
Properties
Gets the size of the bitmap in device-independent pixels, taking DPI into account.
Gets the size of the bitmap in actual pixels.
Gets the DPI (dots per inch) of the bitmap as a vector (X, Y).
Gets the pixel format of the bitmap (e.g., Rgba8888, Bgra8888).
Gets the alpha format (e.g., Premul for premultiplied, Unpremul for straight alpha, Opaque for no alpha).
Static Methods
Loads and decodes a bitmap to a specific width, maintaining aspect ratio. More efficient than loading and then resizing.using var stream = File.OpenRead("large-image.jpg");
var thumbnail = Bitmap.DecodeToWidth(
stream,
width: 200,
interpolationMode: BitmapInterpolationMode.HighQuality
);
Parameters:
stream - The stream containing the image
width - Desired width in pixels
interpolationMode - Interpolation quality (optional, default is HighQuality)
Loads and decodes a bitmap to a specific height, maintaining aspect ratio.using var stream = File.OpenRead("large-image.jpg");
var thumbnail = Bitmap.DecodeToHeight(
stream,
height: 150,
interpolationMode: BitmapInterpolationMode.HighQuality
);
Instance Methods
Creates a new bitmap scaled to the specified size.var scaled = bitmap.CreateScaledBitmap(
destinationSize: new PixelSize(800, 600),
interpolationMode: BitmapInterpolationMode.HighQuality
);
Saves the bitmap to a file or stream.// Save to file
bitmap.Save("output.png", quality: 90);
// Save to stream
using var stream = File.Create("output.jpg");
bitmap.Save(stream, quality: 85);
The quality parameter (0-100) is used for JPEG compression. For PNG, it’s ignored.
Copies pixel data from the bitmap to a buffer.var pixels = new byte[bitmap.PixelSize.Width * bitmap.PixelSize.Height * 4];
var handle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
try
{
bitmap.CopyPixels(
sourceRect: new PixelRect(bitmap.PixelSize),
buffer: handle.AddrOfPinnedObject(),
bufferSize: pixels.Length,
stride: bitmap.PixelSize.Width * 4
);
// Process pixel data...
}
finally
{
handle.Free();
}
Releases the resources used by the bitmap. Always dispose bitmaps when done.using var bitmap = new Bitmap("image.png");
// Use bitmap...
// Automatically disposed at end of using block
Avalonia supports the following image formats:
- PNG - Portable Network Graphics (recommended for UI elements)
- JPEG/JPG - Joint Photographic Experts Group (good for photos)
- BMP - Bitmap (uncompressed)
- GIF - Graphics Interchange Format (animated GIFs supported)
- ICO - Icon format
- WEBP - WebP format (platform dependent)
BitmapInterpolationMode
Defines the quality of bitmap scaling:
Default - Platform default interpolation
LowQuality - Fastest, lowest quality (nearest neighbor)
MediumQuality - Balanced performance and quality (bilinear)
HighQuality - Best quality, slower (bicubic or better)
Defines pixel data organization:
Rgba8888 - 32-bit RGBA (8 bits per channel)
Bgra8888 - 32-bit BGRA (8 bits per channel)
Rgb565 - 16-bit RGB (5-6-5 bits)
Gray8 - 8-bit grayscale
Defines how alpha channel is stored:
Premul - Premultiplied alpha (RGB values already multiplied by alpha)
Unpremul - Straight alpha (RGB values independent of alpha)
Opaque - No alpha channel
Usage Examples
Loading and Displaying Images
<Image Source="/Assets/logo.png"
Stretch="Uniform" />
<Image Source="avares://MyApp/Assets/icon.png" />
// In code
public class MyViewModel : ViewModelBase
{
public Bitmap? Logo { get; }
public MyViewModel()
{
Logo = new Bitmap("Assets/logo.png");
}
}
Loading Images Asynchronously
public async Task<Bitmap?> LoadImageAsync(string url)
{
try
{
using var httpClient = new HttpClient();
var data = await httpClient.GetByteArrayAsync(url);
using var stream = new MemoryStream(data);
return new Bitmap(stream);
}
catch (Exception ex)
{
Console.WriteLine($"Failed to load image: {ex.Message}");
return null;
}
}
Creating Thumbnails
public Bitmap CreateThumbnail(string imagePath, int maxWidth, int maxHeight)
{
using var original = new Bitmap(imagePath);
// Calculate scaling to fit within max dimensions
var scale = Math.Min(
(double)maxWidth / original.PixelSize.Width,
(double)maxHeight / original.PixelSize.Height
);
var newWidth = (int)(original.PixelSize.Width * scale);
var newHeight = (int)(original.PixelSize.Height * scale);
return original.CreateScaledBitmap(
new PixelSize(newWidth, newHeight),
BitmapInterpolationMode.HighQuality
);
}
Efficient Image Loading
// Instead of loading full resolution and scaling:
// ❌ Don't do this
using var fullSize = new Bitmap("huge-image.jpg");
var thumbnail = fullSize.CreateScaledBitmap(
new PixelSize(200, 200),
BitmapInterpolationMode.HighQuality
);
// ✅ Do this instead - decode directly to target size
using var stream = File.OpenRead("huge-image.jpg");
var thumbnail = Bitmap.DecodeToWidth(stream, 200, BitmapInterpolationMode.HighQuality);
Manipulating Pixel Data
public Bitmap ApplyGrayscaleFilter(Bitmap source)
{
var pixelSize = source.PixelSize;
var pixels = new byte[pixelSize.Width * pixelSize.Height * 4];
var handle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
try
{
// Copy pixels from source
source.CopyPixels(
new PixelRect(pixelSize),
handle.AddrOfPinnedObject(),
pixels.Length,
pixelSize.Width * 4
);
// Convert to grayscale
for (int i = 0; i < pixels.Length; i += 4)
{
var gray = (byte)((pixels[i] * 0.299) +
(pixels[i + 1] * 0.587) +
(pixels[i + 2] * 0.114));
pixels[i] = gray; // R
pixels[i + 1] = gray; // G
pixels[i + 2] = gray; // B
// Keep alpha unchanged
}
// Create new bitmap from modified pixels
return new Bitmap(
PixelFormat.Rgba8888,
AlphaFormat.Unpremul,
handle.AddrOfPinnedObject(),
pixelSize,
source.Dpi,
pixelSize.Width * 4
);
}
finally
{
handle.Free();
}
}
Writing to Bitmaps
public class WriteableBitmapExample
{
public WriteableBitmap CreateDynamicBitmap(int width, int height)
{
var bitmap = new WriteableBitmap(
new PixelSize(width, height),
new Vector(96, 96),
PixelFormat.Bgra8888,
AlphaFormat.Premul
);
using (var fb = bitmap.Lock())
{
// Draw directly to pixel buffer
unsafe
{
var ptr = (uint*)fb.Address.ToPointer();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// Create a gradient
byte r = (byte)(x * 255 / width);
byte g = (byte)(y * 255 / height);
byte b = 128;
ptr[y * width + x] = 0xFF000000 |
((uint)b << 16) | ((uint)g << 8) | r;
}
}
}
}
return bitmap;
}
}
Rendering Bitmap from Visual
public RenderTargetBitmap RenderVisualToBitmap(Visual visual, Size size)
{
var bitmap = new RenderTargetBitmap(
new PixelSize((int)size.Width, (int)size.Height),
new Vector(96, 96)
);
using (var context = bitmap.CreateDrawingContext(null))
{
visual.Render(context);
}
return bitmap;
}
CroppedBitmap
Creates a cropped view of another bitmap without copying pixel data:
var source = new Bitmap("large-image.png");
var cropped = new CroppedBitmap(
source,
new PixelRect(100, 100, 200, 200) // x, y, width, height
);
Best Practices
- Always Dispose: Bitmaps hold native resources. Always use
using statements or call Dispose()
- Use DecodeToWidth/Height: When loading images for display at smaller sizes, decode directly to target size
- Choose Appropriate Format: Use PNG for UI elements, JPEG for photos
- Avoid Blocking UI: Load large images asynchronously on background threads
- Cache Bitmaps: Reuse bitmap instances rather than reloading from disk
- Handle Disposal in ViewModels: When storing bitmaps in ViewModels, implement
IDisposable
- Set Quality: Use appropriate
BitmapInterpolationMode - HighQuality for static images, LowQuality for animations
- Consider Memory: Large bitmaps consume significant memory. Dispose promptly when no longer needed
- Use Asset URLs: In XAML, use
avares:// URLs for embedded resources