Share via


Renderers

In the rendering stage, the processing set up by the pipeline is actually performed, and a resulting image is created. The developer can choose how to receive the result by selecting a renderer class from the provided ones: BitmapRenderer, GifRenderer, JpegRenderer, and WriteableBitmapRenderer.

Bitmap Renderer

Use a BitmapRenderer when the target image is an intermediate Bitmap to be used in further processing, or you need to access the pixels efficiently. The application can either create the bitmap beforehand in a desired size, and then pass that in, or let the bitmap renderer output a new bitmap based on the processed image.

GIF Renderer

Note: Available since version 1.2 beta

Use a GifRenderer to render GIF images, both normal and animated. To render a single image GIF, use it in the same way as the other renderers.

When rendering an animated GIF, you assign a list of sources to the Sources property. You can set the default duration for a frame to be shown with the Duration property, and the number of times that the animation will loop when displayed with the property NumberOfAnimationLoops.

If the Size property has been set, the GIF output image will have that size, and all input images will be stretched to fit it. If size has not been set, the GIF output image will have the same size as the first image in the sources list. Other images in the list will then not be resized.

The sample below renders an animated GIF with a frame duration of 100ms.

using (var gifRenderer = new GifRenderer(sources))
{
    gifRenderer.Size = new Size(320, 180);
    gifRenderer.Duration = 100;
    var buffer = await gifRenderer.RenderAsync();
}
Dn859605.gif_renderer_source_1(en-us,WIN.10).jpg Dn859605.gif_renderer_source_2(en-us,WIN.10).jpg
Source 1 Source 2
Dn859605.gif_renderer_source_3(en-us,WIN.10).jpg Dn859605.gif_renderer(en-us,WIN.10).gif
Source 3 Rendered GIF image

The GIF renderer encodes each of the IImageProviders in the Sources collection one at the time, thus conserving resources. Not all of the sources need to be stored in memory at the same time, allowing for greater complexity of each individual frame. This allows you to render a GIF animation with many frames, without running out of memory.

Here is an example that produces a gif animation of a blurring image. It contains 256 frames. Rendering and encoding such a large number of filter effects would consume an enormous amount of resources. Depending on the size of the source file, it just might demand all and more of devices resources. Because the GIF renderer doesn't demand all of the sources at the same time it empowers the user to be more creative.

using (var source = new StorageFileImageSource(sourceFile))  using (var renderer = new GifRenderer())
{
    var sources = new List<IImageProvider>();

    for (int i = 1; i < 257; i++ )
    {
        sources.Add(new FilterEffect(source) { Filters = new [] { new BlurFilter(i) } });
    }

    renderer.Sources = sources;
    renderer.NumberOfAnimationLoops = 0;
    renderer.Duration = 30;

    var buffer = await renderer.RenderAsync();
}

Dn859605.gif_many_sources(en-us,WIN.10).gif

Knowing this can have additional uses. The sample below demonstrates the rendering of a simple GIF animation by implementing just one custom effect. By knowing that only one frame will be encoded at the same time, you can introduce a state into the custom effect and apply a different effect for each frame.

The example below shows this by sliding a black square along the width of the canvas. Additionally, it also shows that there is always at most one rendering operation in process.

class SlidingSquareEffect : CustomEffectBase
{
    private int m_currentFrame = 0;
    private readonly int m_numberOfFrames;
    private int m_ongoingOperations;
    private int m_numSimultaneousRenderOperations = 0;

    private const int SquareSideLength = 19;

    public SlidingSquareEffect(IImageProvider source, int numberOfFrames)
        : base(source, true)
    {
         m_numberOfFrames = numberOfFrames;
    }

    protected override void OnProcess(PixelRegion sourcePixelRegion, PixelRegion targetPixelRegion)
    {
        try
        {
            m_ongoingOperations = Interlocked.Increment(ref m_numSimultaneousRenderOperations);

            if (ManySimultaneousOperations())
                throw new InvalidOperationException();

            int sidewayStepSize = (int)((sourcePixelRegion.ImageSize.Width - SquareSideLength) / (m_numberOfFrames - 1));
            var location = new Point(m_currentFrame * sidewayStepSize, 10);

            DrawSquareAtLocation(sourcePixelRegion, location);

            m_currentFrame = (m_currentFrame + 1) % m_numberOfFrames;
        }
        finally
        {
            Interlocked.Decrement(ref m_numSimultaneousRenderOperations);
        }
    }

    private static void DrawSquareAtLocation(PixelRegion sourcePixelRegion, Point location)
    {
        for (int row = (int)location.Y; row < location.Y + SquareSideLength; row++)
        {
            int startOfLineIndex = row * (int)sourcePixelRegion.Bounds.Width;
            for (int col = (int)location.X; col < location.X + SquareSideLength; col++)
            {
                int index = startOfLineIndex + col;

                uint blackPixel = (255u << 24) + (0u << 16) + (0u << 8) + (0u << 0);
                sourcePixelRegion.ImagePixels[index] = blackPixel;

            }
        }
    }

    private bool ManySimultaneousOperations()
    {
        return m_ongoingOperations > 1;
    }
};

To render the animation:

int numberOfStepsInTheAnimation = 10;

using (var source = new ColorImageSource(new Size(100, 100), Colors.Green))
using (var animationFrame = new SlidingSquareEffect(source, numberOfStepsInTheAnimation))
using (var renderer = new GifRenderer())
{
    renderer.Sources = new List<IImageProvider>(Enumerable.Repeat(animationFrame, numberOfStepsInTheAnimation));
    var buffer = await renderer.RenderAsync();
}

Dn859605.GifMovingSquare(en-us,WIN.10).gif

Jpeg Renderer

Use a JpegRenderer when the target image should be a JPEG/JFIF with optional EXIF metadata. The result is an IBuffer which can be saved to a stream, file, and so on using normal Windows Phone application practices.

Writeable Bitmap Renderer

Use a WriteableBitmapRenderer when the target image needs to be a WriteableBitmap, for display purposes. However, it is not recommended to use WriteableBitmap as an intermediate bitmap format for further processing. Instead, in that case use a BitmapRenderer.

An example of how to use the resulting WriteableBitmap in an XAML UI can be seen in the Quick Start.