Skip to main content
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

Size
Size
Gets the size of the bitmap in device-independent pixels, taking DPI into account.
PixelSize
PixelSize
Gets the size of the bitmap in actual pixels.
Dpi
Vector
Gets the DPI (dots per inch) of the bitmap as a vector (X, Y).
Format
PixelFormat?
Gets the pixel format of the bitmap (e.g., Rgba8888, Bgra8888).
AlphaFormat
AlphaFormat?
Gets the alpha format (e.g., Premul for premultiplied, Unpremul for straight alpha, Opaque for no alpha).

Static Methods

DecodeToWidth
static Bitmap
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)
DecodeToHeight
static Bitmap
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

CreateScaledBitmap
Bitmap
Creates a new bitmap scaled to the specified size.
var scaled = bitmap.CreateScaledBitmap(
    destinationSize: new PixelSize(800, 600),
    interpolationMode: BitmapInterpolationMode.HighQuality
);
Save
void
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.
CopyPixels
void
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();
}
Dispose
void
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

Supported Image Formats

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)

PixelFormat

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

AlphaFormat

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

// In XAML
<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

  1. Always Dispose: Bitmaps hold native resources. Always use using statements or call Dispose()
  2. Use DecodeToWidth/Height: When loading images for display at smaller sizes, decode directly to target size
  3. Choose Appropriate Format: Use PNG for UI elements, JPEG for photos
  4. Avoid Blocking UI: Load large images asynchronously on background threads
  5. Cache Bitmaps: Reuse bitmap instances rather than reloading from disk
  6. Handle Disposal in ViewModels: When storing bitmaps in ViewModels, implement IDisposable
  7. Set Quality: Use appropriate BitmapInterpolationMode - HighQuality for static images, LowQuality for animations
  8. Consider Memory: Large bitmaps consume significant memory. Dispose promptly when no longer needed
  9. Use Asset URLs: In XAML, use avares:// URLs for embedded resources

Build docs developers (and LLMs) love