in ExampleGallery/EffectRegionMapping.xaml.cs [132:250]
void canvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
// We animate the source image by changing which character is highlighted in yellow.
// Therefore there can be two changed regions: the highlighted character has changed from
// white to yellow, while the previous highlight has changed from yellow back to white.
// Look up the bounds of the two changed characters.
var highlightBounds = GetCharacterBounds(highlightedCharacter);
var previousBounds = GetCharacterBounds(previousHighlight);
// Tell our effects that the highlighted character region has changed.
blurEffect.InvalidateSourceRectangle(args.DrawingSession, 0, highlightBounds);
shadowEffect.InvalidateSourceRectangle(args.DrawingSession, 0, highlightBounds);
// Query what part of the output image will change as a result.
var highlightInvalidRects = compositeEffect.GetInvalidRectangles(args.DrawingSession);
var highlightInvalidUnion = GetRectangleUnion(highlightInvalidRects);
// Also tell the effects about the previously highlighted character.
blurEffect.InvalidateSourceRectangle(args.DrawingSession, 0, previousBounds);
shadowEffect.InvalidateSourceRectangle(args.DrawingSession, 0, previousBounds);
// Query the output region again. This will return a superset of highlightInvalidRects,
// as it now accounts for the change to previousBounds as well as highlightBounds.
var totalInvalidRects = compositeEffect.GetInvalidRectangles(args.DrawingSession);
var totalInvalidUnion = GetRectangleUnion(totalInvalidRects);
// We can also look up in the opposite direction: given that we are going to redraw only
// the totalInvalidUnion area, what portion of each source image is needed to do that?
// When using filter kernels like blur, this will be larger than just highlightBounds+previousBounds.
var requiredSourceRects = compositeEffect.GetRequiredSourceRectangles(args.DrawingSession,
totalInvalidUnion,
new ICanvasEffect[] { blurEffect, shadowEffect },
new uint[] { 0, 0 },
new Rect[2] { sourceRenderTarget.Bounds, sourceRenderTarget.Bounds });
// How about if we were going to redraw only highlightBounds, skipping previousBounds?
// (we don't actually do this, but do display what source regions it would require).
var blurSourceRect = compositeEffect.GetRequiredSourceRectangle(args.DrawingSession, highlightInvalidUnion, blurEffect, 0, sourceRenderTarget.Bounds);
var shadowSourceRect = compositeEffect.GetRequiredSourceRectangle(args.DrawingSession, highlightInvalidUnion, shadowEffect, 0, sourceRenderTarget.Bounds);
// Draw text into the source rendertarget.
using (var drawingSession = sourceRenderTarget.CreateDrawingSession())
{
// To make sure the correct requiredSourceRects were reported, we clear the background outside
// that region to magenta. If everything is working correctly, this should never be picked up by
// effect drawing, as we only leave magenta in the areas we don't expect the effects to read from.
drawingSession.Clear(Colors.Magenta);
// Clear the requiredSourceRects to transparent.
drawingSession.Blend = CanvasBlend.Copy;
foreach (var r in requiredSourceRects)
{
drawingSession.FillRectangle(r, Colors.Transparent);
}
// Draw the text characters.
drawingSession.Blend = CanvasBlend.SourceOver;
for (int i = 0; i < characterLayouts.Count; i++)
{
var color = (i == highlightedCharacter) ? Colors.Yellow : Colors.White;
drawingSession.DrawTextLayout(characterLayouts[i], characterPositions[i], color);
}
}
// Draw the effect graph (which reads from sourceRenderTarget) into destRenderTarget.
using (var drawingSession = destRenderTarget.CreateDrawingSession())
{
// Slightly darken down the existing contents of the output rendertarget. This causes everything
// except totalInvalidUnion to gradually fade out, so we can see which areas are getting redrawn.
// If this FillRectangle was removed, the result of redrawing only the changed region would be
// identical to if we redrew the whole thing every time (by removing the CreateLayer call).
drawingSession.FillRectangle(destRenderTarget.Bounds, Color.FromArgb(16, 0, 0, 0));
// Clip our drawing to the totalInvalidUnion rectangle,
// which should be the only part of the output that has changed.
using (var layer = drawingSession.CreateLayer(1, totalInvalidUnion))
{
drawingSession.Clear(Colors.CornflowerBlue);
drawingSession.DrawImage(compositeEffect, totalInvalidUnion, totalInvalidUnion);
}
}
if (!ThumbnailGenerator.IsDrawingThumbnail)
{
args.DrawingSession.Transform = Matrix3x2.CreateTranslation(gap, gap);
// Display sourceRenderTarget.
args.DrawingSession.DrawImage(sourceRenderTarget);
// Display highlightBounds, blurSourceRect, and shadowSourceRect.
args.DrawingSession.DrawRectangle(highlightBounds, Colors.Gray);
args.DrawingSession.DrawRectangle(blurSourceRect, Colors.Blue);
args.DrawingSession.DrawRectangle(shadowSourceRect, Colors.Blue);
}
args.DrawingSession.Transform = Matrix3x2.CreateTranslation(gap, gap * 2 + height);
// Display destRenderTarget.
args.DrawingSession.DrawImage(destRenderTarget);
// Display highlightInvalidRects.
foreach (var i in highlightInvalidRects)
{
args.DrawingSession.DrawRectangle(i, Colors.DarkBlue);
}
previousHighlight = highlightedCharacter;
// When generating thumbnails, repeat the first draw a bunch of times to reach a more interesting image.
if (ThumbnailGenerator.IsDrawingThumbnail && highlightedCharacter < characterLayouts.Count * 5 / 6)
{
highlightedCharacter++;
canvas_Draw(sender, args);
}
}