Table of Contents

Image Processing (TextureCanvas)

Every combination of anchor and stretch that TextureCanvas can apply, drawn as a grid of thumbnails so the options can be compared rather than read about. Each cell blits a region of a source texture into a destination rectangle with one setting changed, and renders the result into a UI card. The canvases are disposed as they go - each one owns a GPU render target, and a grid of them left open would be an easy leak.

The Program.cs file shows how to:

  • Drawing into an offscreen target with CreateTextureCanvas
  • Blitting a source rectangle into a destination rectangle
  • How Anchor and Stretch interact when the aspect ratios differ
  • Choosing a SamplingPattern for the resample
  • Turning a canvas into a Texture for display
  • Disposing each canvas so its render target is released
  • Laying results out in a UniformGrid of UI cards

Image Processing (TextureCanvas)

View on GitHub.

using Stride.CommunityToolkit.Engine;
using Stride.CommunityToolkit.Rendering;
using Stride.Core.Mathematics;
using Stride.Engine;
using Stride.Graphics;
using Stride.Rendering.Images;
using Stride.Rendering.Sprites;
using Stride.UI;
using Stride.UI.Controls;
using Stride.UI.Panels;

using var game = new Game();

// A tall window for the 9 by 9 grid, which is 1000 pixels square. Set before the device exists:
// game.Window.SetSize in Start would show the default size first, and back-buffer sizes from
// game.UseGameSettings alone can shrink the 1280 by 720 window but never grow it.
game.SetWindowSize(1000, 1080);

game.Run(new SceneSetup3D { Camera = CameraSetup.Perspective(), Ground = null }, start: Start);

void Start(Scene rootScene)
{
    // sRGB and premultiplied, as the content pipeline would import it. Texture.Load with its defaults
    // reads it as linear, and every mid-tone comes out pale
    var texture = new TextureLoader(game.GraphicsDevice).Color("input.png");

    var grid = new UniformGrid
    {
        Width = 1000,
        Height = 1000,
        Columns = 9,
        Rows = 9,
        Margin = new Thickness(8, 8, 8, 8)
    };

    grid.Children.Add(CreateCard(texture));

    // Every anchor across the columns; every stretch twice down the rows, into a wide area with the
    // expanded sampling pattern and into a tall one with the default
    for (var a = 0; a < 9; a++)
    {
        for (var s = 0; s < 4; s++)
        {
            AddCard(a, s * 2 + 1, new Rectangle(128, 256, 768, 512), (Stretch)s, (Anchor)a, SamplingPattern.Expanded);
            AddCard(a, s * 2 + 2, new Rectangle(256, 128, 512, 768), (Stretch)s, (Anchor)a, SamplingPattern.Linear);
        }
    }

    var entity = new Entity { new UIComponent { Page = new UIPage { RootElement = grid } } };

    entity.Scene = rootScene;

    void AddCard(int column, int row, Rectangle destination, Stretch stretch, Anchor anchor, SamplingPattern sampling)
    {
        using var canvas = game.CreateTextureCanvas(new Size2(1024, 1024));

        canvas.DrawTexture(texture, new Rectangle(0, 128, 256, 256), destination, null, stretch, anchor, sampling);

        var card = CreateCard(canvas.ToTexture());

        card.SetGridColumn(column);
        card.SetGridRow(row);
        grid.Children.Add(card);
    }
}

static Border CreateCard(Texture texture) => new()
{
    BorderColor = new Color(25, 25, 25),
    BackgroundColor = new Color(120, 120, 120),
    BorderThickness = new Thickness(2, 2, 2, 2),
    Padding = new Thickness(8, 8, 8, 8),
    Margin = new Thickness(4, 4, 4, 4),
    Content = new StackPanel
    {
        Orientation = Orientation.Vertical,
        Children = { new ImageElement { Source = new SpriteFromTexture { Texture = texture } } },
    },
};