public static Visual ApplyDropShadow( LayerContext context, Visual source, DropShadowEffect dropShadowEffect) { if (!context.ObjectFactory.IsUapApiAvailable(nameof(CompositionVisualSurface), versionDependentFeatureDescription: "Drop Shadow")) { // The effect can't be displayed on the targeted version. return(source); } Debug.Assert(dropShadowEffect.IsEnabled, "Precondition"); Debug.Assert(context is PreCompLayerContext || context is ShapeLayerContext, "Precondition"); // Shadow: // +------------------+ // | Container Visual | -- Has the final composited result. // +------------------+ < // ^ Child #1 \ Child #2 (original layer) // | (shadow layer) \ // | \ // +---------------------+ \ // | ApplyGaussianBlur() | \ // +---------------------+ +-----------------+ // ^ | ContainerVisual | - Original Visual node. // | +-----------------+ // +----------------+ . // | SpriteVisual | . // +----------------+ . // ^ Source . // | . // +--------------+ . // | MaskBrush | . // +--------------+ . // ^ Source ^ Mask . Source // | \ V // +----------+ +---------------+ // |ColorBrush| | VisualSurface | // +----------+ +---------------+ GaussianBlurEffect gaussianBlurEffect = new GaussianBlurEffect( name: dropShadowEffect.Name + "_blur", isEnabled: true, blurriness: dropShadowEffect.Softness, blurDimensions: new Animatable <Enum <BlurDimension> >(BlurDimension.HorizontalAndVertical), repeatEdgePixels: new Animatable <bool>(true), forceGpuRendering: true); var factory = context.ObjectFactory; var size = context.CompositionContext.Size; if (context is PreCompLayerContext) { size = ConvertTo.Vector2(((PreCompLayerContext)context).Layer.Width, ((PreCompLayerContext)context).Layer.Height); } var visualSurface = factory.CreateVisualSurface(); visualSurface.SourceSize = size; visualSurface.SourceVisual = source; var maskBrush = factory.CreateMaskBrush(); var colorBrush = factory.CreateColorBrush(dropShadowEffect.Color.InitialValue); var color = Optimizer.TrimAnimatable(context, dropShadowEffect.Color); if (color.IsAnimated) { Animate.Color(context, color, colorBrush, nameof(colorBrush.Color)); } else { colorBrush.Color = ConvertTo.Color(color.InitialValue); } maskBrush.Source = colorBrush; maskBrush.Mask = factory.CreateSurfaceBrush(visualSurface); var shadowSpriteVisual = factory.CreateSpriteVisual(); shadowSpriteVisual.Size = size; shadowSpriteVisual.Brush = maskBrush; var blurResult = ApplyGaussianBlur(context, shadowSpriteVisual, gaussianBlurEffect); var opacity = Optimizer.TrimAnimatable(context, dropShadowEffect.Opacity); if (opacity.IsAnimated) { Animate.Opacity(context, opacity, blurResult, nameof(blurResult.Opacity)); } else { blurResult.Opacity = (float)opacity.InitialValue.Value; } // Convert direction and distance to a Vector3. var direction = Optimizer.TrimAnimatable(context, dropShadowEffect.Direction); var distance = Optimizer.TrimAnimatable(context, dropShadowEffect.Distance); if (direction.IsAnimated) { if (distance.IsAnimated) { // Direction and distance are animated. // NOTE: we could support this in some cases. The worst cases are // where the keyframes don't line up, and/or the easings are different // between distance and direction. context.Issues.AnimatedLayerEffectParameters("drop shadow"); } else { // Only direction is animated. var distanceValue = distance.InitialValue; var keyFrames = direction.KeyFrames.Select( kf => new KeyFrame <Vector3>(kf.Frame, VectorFromRotationAndDistance(kf.Value, distanceValue), kf.Easing)).ToArray(); var directionAnimation = new TrimmedAnimatable <Vector3>(context, keyFrames[0].Value, keyFrames); Animate.Vector3(context, directionAnimation, blurResult, nameof(blurResult.Offset)); } } else if (distance.IsAnimated) { // Only distance is animated. var directionRadians = direction.InitialValue.Radians; var keyFrames = distance.KeyFrames.Select( kf => new KeyFrame <Vector3>(kf.Frame, VectorFromRotationAndDistance(directionRadians, kf.Value), kf.Easing)).ToArray(); var distanceAnimation = new TrimmedAnimatable <Vector3>(context, keyFrames[0].Value, keyFrames); Animate.Vector3(context, distanceAnimation, blurResult, nameof(blurResult.Offset)); } else { // Direction and distance are both not animated. var directionRadians = direction.InitialValue.Radians; var distanceValue = distance.InitialValue; blurResult.Offset = ConvertTo.Vector3(VectorFromRotationAndDistance(direction.InitialValue, distance.InitialValue)); } var result = factory.CreateContainerVisual(); result.Size = size; result.Children.Add(blurResult); // Check if ShadowOnly can be false if (!dropShadowEffect.IsShadowOnly.IsAlways(true)) { // Check if ShadowOnly can be true if (!dropShadowEffect.IsShadowOnly.IsAlways(false)) { var isVisible = FlipBoolAnimatable(dropShadowEffect.IsShadowOnly); // isVisible = !isShadowOnly source.IsVisible = isVisible.InitialValue; if (isVisible.IsAnimated) { Animate.Boolean( context, Optimizer.TrimAnimatable(context, isVisible), source, nameof(blurResult.IsVisible)); } } result.Children.Add(source); } return(result); }
static LayerVisual ApplyDropShadow(PreCompLayerContext context, Visual visual, DropShadowEffect dropShadowEffect) { Debug.Assert(dropShadowEffect.IsEnabled, "Precondition"); // Create a LayerVisual so we can add a drop shadow. var result = context.ObjectFactory.CreateLayerVisual(); result.Children.Add(visual); // TODO: Due to a Composition bug, LayerVisual currently must be given a size for the drop // shadow to show up correctly. And even then it is not reliable. result.Size = context.CompositionContext.Size; var shadow = context.ObjectFactory.CreateDropShadow(); result.Shadow = shadow; shadow.SourcePolicy = CompositionDropShadowSourcePolicy.InheritFromVisualContent; var isShadowOnly = Optimizer.TrimAnimatable(context, dropShadowEffect.IsShadowOnly); if (!isShadowOnly.IsAlways(true)) { context.Issues.ShadowOnlyShadowEffect(); } // TODO - it's not clear whether BlurRadius and Softness are equivalent. We may // need to scale Softness to convert it to BlurRadius. var blurRadius = Optimizer.TrimAnimatable(context, dropShadowEffect.Softness); if (blurRadius.IsAnimated) { Animate.Scalar(context, blurRadius, shadow, nameof(shadow.BlurRadius)); } else { shadow.BlurRadius = (float)blurRadius.InitialValue; } var color = Optimizer.TrimAnimatable(context, dropShadowEffect.Color); if (color.IsAnimated) { Animate.Color(context, color, shadow, nameof(shadow.Color)); } else { shadow.Color = ConvertTo.Color(color.InitialValue); } var opacity = Optimizer.TrimAnimatable(context, dropShadowEffect.Opacity); if (opacity.IsAnimated) { Animate.Opacity(context, opacity, shadow, nameof(shadow.Opacity)); } else { shadow.Opacity = (float)opacity.InitialValue.Value; } // Convert direction and distance to a Vector3. var direction = Optimizer.TrimAnimatable(context, dropShadowEffect.Direction); var distance = Optimizer.TrimAnimatable(context, dropShadowEffect.Distance); if (direction.IsAnimated) { if (distance.IsAnimated) { // Direction and distance are animated. // NOTE: we could support this in some cases. The worst cases are // where the keyframes don't line up, and/or the easings are different // between distance and direction. context.Issues.AnimatedLayerEffectParameters("drop shadow"); } else { // Only direction is animated. var distanceValue = distance.InitialValue; var keyFrames = direction.KeyFrames.Select( kf => new KeyFrame <Vector3>(kf.Frame, VectorFromRotationAndDistance(kf.Value, distanceValue), kf.Easing)).ToArray(); var directionAnimation = new TrimmedAnimatable <Vector3>(context, keyFrames[0].Value, keyFrames); Animate.Vector3(context, directionAnimation, shadow, nameof(shadow.Offset)); } } else if (distance.IsAnimated) { // Only distance is animated. var directionRadians = direction.InitialValue.Radians; var keyFrames = distance.KeyFrames.Select( kf => new KeyFrame <Vector3>(kf.Frame, VectorFromRotationAndDistance(directionRadians, kf.Value), kf.Easing)).ToArray(); var distanceAnimation = new TrimmedAnimatable <Vector3>(context, keyFrames[0].Value, keyFrames); Animate.Vector3(context, distanceAnimation, shadow, nameof(shadow.Offset)); } else { // Direction and distance are both not animated. var directionRadians = direction.InitialValue.Radians; var distanceValue = distance.InitialValue; shadow.Offset = ConvertTo.Vector3(VectorFromRotationAndDistance(direction.InitialValue, distance.InitialValue)); } return(result); }