Avalonia에서 이미지 다루기

1. 자산 로더를 통한 비트맵 생성

Avalonia에서 이미지를 표시하려면 AssetLoader를 사용하여 리소스를 불러와야 합니다.

var imageUri = new Uri("avares://MyApp/Assets/sample.png");
using var resourceStream = AssetLoader.Open(imageUri);
var bitmap = new Bitmap(resourceStream);
imageControl.Source = bitmap;

2. IImage 인터페이스 구현체들

Image 컨트롤의 Source 속성은 IImage 타입을 받습니다. 주요 구현체는 다음과 같습니다:

  • DrawingImage: 벡터 드로잉 기반 이미지
  • Bitmap: 정적 비트맵 이미지
  • CroppedBitmap: 비트맵 일부 영역 잘라내기
  • RenderTargetBitmap: 렌더링 대상으로 사용 가능한 비트맵
  • WriteableBitmap: 픽셀 단위 수정 가능한 비트맵

2.1 DrawingImage 활용

벡터 그래픽을 직접 구성하여 이미지를 만들 수 있습니다:

protected override void OnInitialized()
{
    var circleGeometry = new EllipseGeometry 
    { 
        Center = new Point(50, 50),
        RadiusX = 30,
        RadiusY = 30
    };
    
    var shapeDrawing = new GeometryDrawing
    {
        Geometry = circleGeometry,
        Brush = Brushes.Blue,
        Pen = new Pen(Brushes.Black, 2)
    };
    
    var vectorImage = new DrawingImage(shapeDrawing);
    imageView.Source = vectorImage;
}

2.2 CroppedBitmap 적용

이미지의 특정 영역만 표시할 때 유용합니다:

var originalBitmap = new Bitmap(AssetLoader.Open(new Uri("avares://App/Images/photo.jpg")));
var cropArea = new PixelRect(new PixelPoint(10, 10), new PixelSize(200, 150));
var croppedImage = new CroppedBitmap(originalBitmap, cropArea);
thumbnailView.Source = croppedImage;

2.3 RenderTargetBitmap 사용법

렌더링된 콘텐츠를 비트맵으로 캡처할 수 있습니다:

var visualElement = new TextBlock 
{ 
    Text = "캡처 대상", 
    FontSize = 24,
    Foreground = Brushes.DarkBlue
};

visualElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
visualElement.Arrange(new Rect(visualElement.DesiredSize));

var renderBitmap = new RenderTargetBitmap(
    new PixelSize(300, 100),
    new Vector(96, 96)
);

renderBitmap.Render(visualElement);
previewImage.Source = renderBitmap;

2.4 WriteableBitmap 픽셀 조작

실시간으로 픽셀 데이터를 수정해야 할 경우 사용됩니다:

// 픽셀 포맷 확장 메서드
public static class PixelFormatHelper
{
    public static int BytesPerPixel(this PixelFormat format) =>
        format switch
        {
            PixelFormat.Rgb565 => 2,
            PixelFormat.Rgba8888 => 4,
            PixelFormat.Bgra8888 => 4,
            _ => throw new NotSupportedException()
        };
}

// 프레임버퍼 확장 메서드
public static class FramebufferExtensions
{
    public static unsafe Span<byte> GetPixelData(this ILockedFramebuffer fb, int x, int y)
    {
        var pixelBytes = fb.Format.BytesPerPixel();
        var baseAddress = (byte*)fb.Address;
        var offset = fb.RowBytes * y + pixelBytes * x;
        return new Span<byte>(baseAddress + offset, pixelBytes);
    }

    public static void WritePixel(this ILockedFramebuffer fb, int x, int y, Color color)
    {
        var targetPixel = fb.GetPixelData(x, y);
        var alphaFactor = color.A / 255.0;

        switch (fb.Format)
        {
            case PixelFormat.Rgb565:
                ushort rgbValue = (ushort)(
                    ((color.R & 0xF8) << 8) |
                    ((color.G & 0xFC) << 3) |
                    (color.B >> 3)
                );
                targetPixel[0] = (byte)rgbValue;
                targetPixel[1] = (byte)(rgbValue >> 8);
                break;

            case PixelFormat.Rgba8888:
                targetPixel[0] = (byte)(color.R * alphaFactor);
                targetPixel[1] = (byte)(color.G * alphaFactor);
                targetPixel[2] = (byte)(color.B * alphaFactor);
                targetPixel[3] = color.A;
                break;

            case PixelFormat.Bgra8888:
                targetPixel[0] = (byte)(color.B * alphaFactor);
                targetPixel[1] = (byte)(color.G * alphaFactor);
                targetPixel[2] = (byte)(color.R * alphaFactor);
                targetPixel[3] = color.A;
                break;
        }
    }
}

픽셀 조작 예제:

var sourceUri = new Uri("avares://Application/Resources/background.png");
using var stream = AssetLoader.Open(sourceUri);
var editableBitmap = WriteableBitmap.Decode(stream);

using (var bufferLock = editableBitmap.Lock())
{
    // 좌측 상단 50x50 영역을 녹색으로 변경
    for (int row = 0; row < 50; row++)
    {
        for (int col = 0; col < 50; col++)
        {
            bufferLock.WritePixel(col, row, Colors.Green);
        }
    }
}

displayImage.Source = editableBitmap;

3. ImageBrush 응용

ImageBrushIImageBrushSource 인터페이스를 구현하는 객체를 소스로 받습니다:

var backgroundImage = new Bitmap(AssetLoader.Open(new Uri("avares://Project/Textures/tile.png")));
var brush = new ImageBrush(backgroundImage)
{
    Stretch = Stretch.UniformToFill,
    TileMode = TileMode.Tile
};

contentPanel.Background = brush;

태그: Avalonia Image Processing C# UI Framework Graphics

5월 30일 04:22에 게시됨