Share via


Renderers

In the rendering stage, the image processing graph is actually "executed" and the resulting image is generated.

The rendering is performed by a specific renderer class, and knowing which one to use depends on the type of output object that is desired.

  • BitmapRenderer
  • Direct3DSurfaceRenderer
  • GifRenderer
  • JpegRenderer
  • SoftwareBitmapRenderer
  • SwapChainPanelRenderer
  • WriteableBitmapRenderer

BitmapRenderer

Use a BitmapRenderer when the target image should be written into a Bitmap and be consumed by further image processing or be otherwise accessed by app code.

This is the preferred representation for "temporary" bitmaps.

The app can supply an existing Bitmap or let the renderer create a new Bitmap. Supplying an existing Bitmap helps with memory reuse, as a new allocation is avoided. It also means the renderer will adapt the image to fit the existing Bitmap in terms of size and color mode.

Direct3DSurfaceRenderer (Windows 10 Only)

Use a Direct3DSurfaceRenderer in interop scenarios where the target image should be written into an IDirect3DSurface. This can be useful in certain GPU rendering scenarios. The Direct2D image processing graph in the SDK can then render directly into a hardware surface and avoid copying to/from CPU memory.

GifRenderer

Use a GifRenderer to render to the GIF format. This renderer supports generating both single-image and animated GIFs.

When rendering an animated GIF, you assign a list of sources to the Sources property. You can set the number of times that the animation will loop when displayed using the NumberOfAnimationLoops property.

The duration that each frame should be shown can be set globally using the Duration property on the renderer. The duration can also be controlled per frame, by appending AnimationFrame effects to specific sources.

If the Size property has been set on the renderer, the GIF will be that size and all input images will be stretched to fit. If Size has not been set, the GIF will have the size of the first source. The other images in the list will then not be stretched.

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

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

The GIF renderer encodes each of the IEffect objects in Sources one at a time. This sequential processing conserves memory. 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 an image that becomes gradually more blurred. It contains 256 frames.

Rendering and encoding such a large number of frames would be impractical and consume an enormous amount of memory if they were prepared all at once. Depending on the size of the original image, this would probably consume more memory than is available on any given device.

Because the GIF renderer renders each source in turn, the memory consumption is largely independent of the frame count, which enables many interesting use cases.

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 BlurEffect(source, i));
    }

    renderer.Sources = sources;
    renderer.NumberOfAnimationLoops = 0;
    renderer.Duration = 30;
    
    var buffer = await renderer.RenderAsync();
}

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

JpegRenderer

Use a JpegRenderer when the target image should be a JPEG/JFIF with optional EXIF metadata.

The result is an IBuffer which could be saved to a stream, a file, and so on using normal app practices.

SoftwareBitmapRenderer (Windows 10 Only)

Use a SoftwareBitmapRenderer when the target image should be written into a SoftwareBitmap.

The app can supply an existing SoftwareBitmap or let the renderer create a new SoftwareBitmap. Supplying an existing SoftwareBitmap helps with memory reuse, as a new allocation is avoided. It also means the renderer will adapt the image to fit the existing SoftwareBitmap in terms of size and color mode.

SwapChainPanelRenderer

This is the preferred renderer to use when the output image should primarily be displayed on-screen.

It is especially useful in interactive scenarios as it can display images with much lower latency compared to the alternatives.

The app must have a SwapChainPanel control in its XAML user interface and pass in that control when creating the renderer.

WriteableBitmapRenderer

Use a WriteableBitmapRenderer when the target needs to be a WriteableBitmap in order to interoperate with another API.

It is not recommended to use WriteableBitmapRenderer in other scenarios. For general rendering to intermediate images, prefer BitmapRenderer or SoftwareBitmapRenderer instead.

One problem arises from the fact that WriteableBitmap objects are associated with the UI thread (CoreDispatcher). This app must be careful to avoid performance loss or deadlock, and may risk spending time on the UI thread that wasn't intended.

Another problem is that WriteableBitmap uses premultiplied alpha. This may impact image quality if a WriteableBitmap is used for intermediate results.

An example of how to render to and display a WriteableBitmap in a XAML UI can be seen in the Quick Start.