/// <summary> /// Appends the entire input DrawingCollection, while only firing a single set of /// public events. If an exception is thrown from the public events, the /// Append operation is rolled back. /// </summary> internal void TransactionalAppend(DrawingCollection collectionToAppend) { // Use appendCount to avoid inconsistencies & runaway loops when // this == collectionToAppend, and to ensure collectionToAppend.Count // is only evaluated once. int appendCount = collectionToAppend.Count; // First, append the collection for (int i = 0; i < appendCount; i++) { AddWithoutFiringPublicEvents(collectionToAppend.Internal_GetItem(i)); } // Fire the public Changed event after all the elements have been added. // // If an exception is thrown, then the Append operation is rolled-back without // firing additional events. try { FireChanged(); } catch (Exception) { // Compute the number of elements that existed before the append int beforeAppendCount = Count - appendCount; // Remove the appended elements in reverse order without firing Changed events. for (int i = Count - 1; // Start at the current last index i >= beforeAppendCount; // Until the previous last index i-- // Move to the preceding index ) { RemoveAtWithoutFiringPublicEvents(i); } // Avoid firing WritePostscript events (e.g., OnChanged) after rolling-back // the current operation. // // This ensures that only a single set of events is fired for both exceptional & // typical cases, and it's likely that firing events would cause another exception. throw; } }
internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel) { if (_duceResource.CreateOrAddRefOnChannel(this, channel, System.Windows.Media.Composition.DUCE.ResourceType.TYPE_DRAWINGGROUP)) { Geometry vClipGeometry = ClipGeometry; if (vClipGeometry != null) { ((DUCE.IResource)vClipGeometry).AddRefOnChannel(channel); } Brush vOpacityMask = OpacityMask; if (vOpacityMask != null) { ((DUCE.IResource)vOpacityMask).AddRefOnChannel(channel); } Transform vTransform = Transform; if (vTransform != null) { ((DUCE.IResource)vTransform).AddRefOnChannel(channel); } GuidelineSet vGuidelineSet = GuidelineSet; if (vGuidelineSet != null) { ((DUCE.IResource)vGuidelineSet).AddRefOnChannel(channel); } DrawingCollection vChildren = Children; if (vChildren != null) { int count = vChildren.Count; for (int i = 0; i < count; i++) { ((DUCE.IResource)vChildren.Internal_GetItem(i)).AddRefOnChannel(channel); } } AddRefOnChannelAnimations(channel); UpdateResource(channel, true /* skip "on channel" check - we already know that we're on channel */); } return(_duceResource.GetHandle(channel)); }
internal override void ReleaseOnChannelCore(DUCE.Channel channel) { Debug.Assert(_duceResource.IsOnChannel(channel)); if (_duceResource.ReleaseOnChannel(channel)) { Geometry vClipGeometry = ClipGeometry; if (vClipGeometry != null) { ((DUCE.IResource)vClipGeometry).ReleaseOnChannel(channel); } Brush vOpacityMask = OpacityMask; if (vOpacityMask != null) { ((DUCE.IResource)vOpacityMask).ReleaseOnChannel(channel); } Transform vTransform = Transform; if (vTransform != null) { ((DUCE.IResource)vTransform).ReleaseOnChannel(channel); } GuidelineSet vGuidelineSet = GuidelineSet; if (vGuidelineSet != null) { ((DUCE.IResource)vGuidelineSet).ReleaseOnChannel(channel); } DrawingCollection vChildren = Children; if (vChildren != null) { int count = vChildren.Count; for (int i = 0; i < count; i++) { ((DUCE.IResource)vChildren.Internal_GetItem(i)).ReleaseOnChannel(channel); } } ReleaseOnChannelAnimations(channel); } }
/// <summary> /// Calls methods on the DrawingContext that are equivalent to the /// Drawing with the Drawing's current value. /// </summary> internal override void WalkCurrentValue(DrawingContextWalker ctx) { int popCount = 0; // We avoid unneccessary ShouldStopWalking checks based on assumptions // about when ShouldStopWalking is set. Guard that assumption with an // assertion. // // ShouldStopWalking is currently only set during a hit-test walk after // an object has been hit. Because a DrawingGroup can't be hit until after // the first Drawing is tested, this method doesn't check ShouldStopWalking // until after the first child. // // We don't need to add this check to other Drawing subclasses for // the same reason -- if the Drawing being tested isn't a DrawingGroup, // they are always the 'first child'. // // If this assumption is ever broken then the ShouldStopWalking // check should be done on the first child -- including in the // WalkCurrentValue method of other Drawing subclasses. Debug.Assert(!ctx.ShouldStopWalking); // // Draw the transform property // // Avoid calling PushTransform if the base value is set to the default and // no animations have been set on the property. if (!IsBaseValueDefault(DrawingGroup.TransformProperty) || (null != AnimationStorage.GetStorage(this, DrawingGroup.TransformProperty))) { ctx.PushTransform(Transform); popCount++; } // // Draw the clip property // // Avoid calling PushClip if the base value is set to the default and // no animations have been set on the property. if (!IsBaseValueDefault(DrawingGroup.ClipGeometryProperty) || (null != AnimationStorage.GetStorage(this, DrawingGroup.ClipGeometryProperty))) { ctx.PushClip(ClipGeometry); popCount++; } // // Draw the opacity property // // Avoid calling PushOpacity if the base value is set to the default and // no animations have been set on the property. if (!IsBaseValueDefault(DrawingGroup.OpacityProperty) || (null != AnimationStorage.GetStorage(this, DrawingGroup.OpacityProperty))) { // Push the current value of the opacity property, which // is what Opacity returns. ctx.PushOpacity(Opacity); popCount++; } // Draw the opacity mask property // if (OpacityMask != null) { ctx.PushOpacityMask(OpacityMask); popCount++; } // // Draw the effect property // // Push the current value of the effect property, which // is what BitmapEffect returns. if (BitmapEffect != null) { // Disable warning about obsolete method. This code must remain active // until we can remove the public BitmapEffect APIs. #pragma warning disable 0618 ctx.PushEffect(BitmapEffect, BitmapEffectInput); #pragma warning restore 0618 popCount++; } // // Draw the Children collection // // Get the current value of the children collection DrawingCollection collection = Children; // Call Walk on each child if (collection != null) { for (int i = 0; i < collection.Count; i++) { Drawing drawing = collection.Internal_GetItem(i); if (drawing != null) { drawing.WalkCurrentValue(ctx); // Don't visit the remaining children if the previous // child caused us to stop walking. if (ctx.ShouldStopWalking) { break; } } } } // // Call Pop() for every Push // // Avoid placing this logic in a finally block because if an exception is // thrown, the Walk is simply aborted. There is no requirement to Walk // through Pop instructions when an exception is thrown. // for (int i = 0; i < popCount; i++) { ctx.Pop(); } }
//------------------------------------------------------ // // Public Properties // //------------------------------------------------------ private static void ChildrenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // The first change to the default value of a mutable collection property (e.g. GeometryGroup.Children) // will promote the property value from a default value to a local value. This is technically a sub-property // change because the collection was changed and not a new collection set (GeometryGroup.Children. // Add versus GeometryGroup.Children = myNewChildrenCollection). However, we never marshalled // the default value to the compositor. If the property changes from a default value, the new local value // needs to be marshalled to the compositor. We detect this scenario with the second condition // e.OldValueSource != e.NewValueSource. Specifically in this scenario the OldValueSource will be // Default and the NewValueSource will be Local. if (e.IsASubPropertyChange && (e.OldValueSource == e.NewValueSource)) { return; } DrawingGroup target = ((DrawingGroup)d); // If this is both non-null and mutable, we need to unhook the Changed event. DrawingCollection oldCollection = null; DrawingCollection newCollection = null; if ((e.OldValueSource != BaseValueSourceInternal.Default) || e.IsOldValueModified) { oldCollection = (DrawingCollection)e.OldValue; if ((oldCollection != null) && !oldCollection.IsFrozen) { oldCollection.ItemRemoved -= target.ChildrenItemRemoved; oldCollection.ItemInserted -= target.ChildrenItemInserted; } } // If this is both non-null and mutable, we need to hook the Changed event. if ((e.NewValueSource != BaseValueSourceInternal.Default) || e.IsNewValueModified) { newCollection = (DrawingCollection)e.NewValue; if ((newCollection != null) && !newCollection.IsFrozen) { newCollection.ItemInserted += target.ChildrenItemInserted; newCollection.ItemRemoved += target.ChildrenItemRemoved; } } if (oldCollection != newCollection && target.Dispatcher != null) { using (CompositionEngineLock.Acquire()) { DUCE.IResource targetResource = (DUCE.IResource)target; int channelCount = targetResource.GetChannelCount(); for (int channelIndex = 0; channelIndex < channelCount; channelIndex++) { DUCE.Channel channel = targetResource.GetChannel(channelIndex); Debug.Assert(!channel.IsOutOfBandChannel); Debug.Assert(!targetResource.GetHandle(channel).IsNull); // resource shouldn't be null because // 1) If the field is one of our collections, we don't allow null elements // 2) Codegen already made sure the collection contains DUCE.IResources // ... so we'll Assert it if (newCollection != null) { int count = newCollection.Count; for (int i = 0; i < count; i++) { DUCE.IResource resource = newCollection.Internal_GetItem(i) as DUCE.IResource; Debug.Assert(resource != null); resource.AddRefOnChannel(channel); } } if (oldCollection != null) { int count = oldCollection.Count; for (int i = 0; i < count; i++) { DUCE.IResource resource = oldCollection.Internal_GetItem(i) as DUCE.IResource; Debug.Assert(resource != null); resource.ReleaseOnChannel(channel); } } } } } target.PropertyChanged(ChildrenProperty); }
//------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods internal override void UpdateResource(DUCE.Channel channel, bool skipOnChannelCheck) { // If we're told we can skip the channel check, then we must be on channel Debug.Assert(!skipOnChannelCheck || _duceResource.IsOnChannel(channel)); if (skipOnChannelCheck || _duceResource.IsOnChannel(channel)) { base.UpdateResource(channel, skipOnChannelCheck); // Read values of properties into local variables DrawingCollection vChildren = Children; Geometry vClipGeometry = ClipGeometry; Brush vOpacityMask = OpacityMask; Transform vTransform = Transform; GuidelineSet vGuidelineSet = GuidelineSet; // Obtain handles for properties that implement DUCE.IResource DUCE.ResourceHandle hClipGeometry = vClipGeometry != null ? ((DUCE.IResource)vClipGeometry).GetHandle(channel) : DUCE.ResourceHandle.Null; DUCE.ResourceHandle hOpacityMask = vOpacityMask != null ? ((DUCE.IResource)vOpacityMask).GetHandle(channel) : DUCE.ResourceHandle.Null; DUCE.ResourceHandle hTransform; if (vTransform == null || Object.ReferenceEquals(vTransform, Transform.Identity) ) { hTransform = DUCE.ResourceHandle.Null; } else { hTransform = ((DUCE.IResource)vTransform).GetHandle(channel); } DUCE.ResourceHandle hGuidelineSet = vGuidelineSet != null ? ((DUCE.IResource)vGuidelineSet).GetHandle(channel) : DUCE.ResourceHandle.Null; // Obtain handles for animated properties DUCE.ResourceHandle hOpacityAnimations = GetAnimationResourceHandle(OpacityProperty, channel); // Store the count of this resource's contained collections in local variables. int ChildrenCount = (vChildren == null) ? 0 : vChildren.Count; // Pack & send command packet DUCE.MILCMD_DRAWINGGROUP data; unsafe { data.Type = MILCMD.MilCmdDrawingGroup; data.Handle = _duceResource.GetHandle(channel); data.ChildrenSize = (uint)(sizeof(DUCE.ResourceHandle) * ChildrenCount); data.hClipGeometry = hClipGeometry; if (hOpacityAnimations.IsNull) { data.Opacity = Opacity; } data.hOpacityAnimations = hOpacityAnimations; data.hOpacityMask = hOpacityMask; data.hTransform = hTransform; data.hGuidelineSet = hGuidelineSet; data.EdgeMode = (EdgeMode)GetValue(RenderOptions.EdgeModeProperty); data.bitmapScalingMode = (BitmapScalingMode)GetValue(RenderOptions.BitmapScalingModeProperty); data.ClearTypeHint = (ClearTypeHint)GetValue(RenderOptions.ClearTypeHintProperty); channel.BeginCommand( (byte *)&data, sizeof(DUCE.MILCMD_DRAWINGGROUP), (int)(data.ChildrenSize) ); // Copy this collection's elements (or their handles) to reserved data for (int i = 0; i < ChildrenCount; i++) { DUCE.ResourceHandle resource = ((DUCE.IResource)vChildren.Internal_GetItem(i)).GetHandle(channel);; channel.AppendCommandData( (byte *)&resource, sizeof(DUCE.ResourceHandle) ); } channel.EndCommand(); } } }