Exemple #1
0
        public override bool OnReadValue(Style styleBlock, Css.Value value, int start, out int size)
        {
            if (value != null)
            {
                // Get a quick ref to the actual value to check:
                value = value[start];

                                #if NETFX_CORE
                if (
                    (RawType != null && RawType.GetTypeInfo().IsAssignableFrom(value.GetType().GetTypeInfo())) ||
                    value.Type == Type
                    )
                {
                    size = 1;
                    return(true);
                }
                                #else
                if (
                    (RawType != null && RawType.IsAssignableFrom(value.GetType())) ||
                    value.Type == Type
                    )
                {
                    size = 1;
                    return(true);
                }
                                #endif
            }

            size = 0;
            return(false);
        }
        /// <summary>
        /// Sets the clipping region of the specified <see cref="RenderContext"/>.
        /// </summary>
        /// <param name="renderer">The <see cref="RenderContext"/> to have its clipping region set.</param>
        protected virtual void SetClip(RenderContext renderer)
        {
            Css.Value clip = ClipPath;

            if (clip == null)
            {
                return;
            }

            _previousClip = renderer.ClipRegion;

            // Get the shape from the clip value:
            Css.Value     nextValue;
            ShapeProvider shape = clip.ToShape(this, renderer, out nextValue);

            if (clip != nextValue)
            {
                // Overwrite the raw clip var:
                ClipPath = nextValue;
            }

            if (shape != null)
            {
                // Get the region now:
                renderer.SetClip(shape.GetRegion(this, renderer), false);
            }
        }
        /// <summary>
        /// Sets a time curve which progresses the overall slides animation.
        /// Almost always linear but change it for advanced effects.</summary>
        public void setTimingFunction(Css.Value timingFunc)
        {
            // The function to use:
            Blaze.VectorPath path = null;

            if (timingFunc != null)
            {
                // Get the defined vector path:
                path = timingFunc.GetPath(
                    style == null? null : style.RenderData,
                    Css.Properties.TimelineTimingFunction.GlobalProperty
                    );
            }

            if (path == null)
            {
                progressSampler = null;
            }
            else
            {
                if (!(path is Blaze.RasterVectorPath))
                {
                    Blaze.RasterVectorPath rvp = new Blaze.RasterVectorPath();
                    path.CopyInto(rvp);
                    rvp.ToStraightLines();
                    path = rvp;
                }

                progressSampler = new Blaze.CurveSampler(path);
                progressSampler.Reset();
            }
        }
        /// <summary>Gets all child elements with the given tag.</summary>
        /// <param name="selector">The selector string to match.</param>
        /// <returns>The set of all tags with this tag.</returns>
        public HTMLCollection querySelectorAll(string selector, bool one)
        {
            // Create results set:
            HTMLCollection results = new HTMLCollection();

            if (string.IsNullOrEmpty(selector))
            {
                // Empty set:
                return(results);
            }

            // Create the lexer:
            Css.CssLexer lexer = new Css.CssLexer(selector, this);

            // Read a value:
            Css.Value value = lexer.ReadValue();

            // Read the selectors from the value:
            List <Selector> selectors = new List <Selector>();

            Css.CssLexer.ReadSelectors(null, value, selectors);

            // Create a blank event to store the targets, if any:
            CssEvent e = new CssEvent();

            // Perform the selection process:
            querySelectorAll(selectors.ToArray(), results, e, false);

            return(results);
        }
        public override bool OnAttributeChange(string property)
        {
            // Global CSS properties. Width, height, fill, stroke, visibility, font-family,font-style etc are handled here.
            Css.CssProperty cssProperty = Css.CssProperties.Get(property);

            // Style refresh:
            if (Style.Computed.FirstMatch != null)
            {
                // This is a runtime attribute change.
                // We must consider if it's affecting the style or not:
                Style.Computed.AttributeChanged(property);
            }

            if (cssProperty != null)
            {
                // It's a CSS property! Apply to style:
                Css.Value value = ValueHelpers.Get(getAttribute(property));
                style[cssProperty] = value;
            }
            else if (property == "x")
            {
                style.left = getAttribute("x");
            }
            else if (property == "y")
            {
                style.top = getAttribute("y");
            }
            else if (!base.OnAttributeChange(property))
            {
                return(false);
            }

            return(true);
        }
        /// <summary>Animates this property now.</summary>
        /// <param name="animation">The animation that this property is a part of.</param>
        /// <param name="targetValue">The parsed value that this property will be when the animation is over.</param>
        /// <param name="timeCurve">Optional curve used to describe the progression of the animation.
        /// X is time, Y is value. Null acts like linear (a line from 0,0 to 1,1).</param>
        /// <param name="updateCss">True if this particular property should flush its changes to css/the screen.</param>
        public void Animate(UIAnimation animation, Css.Value targetValue, Blaze.VectorPath timeCurve, bool updateCss)
        {
            Animation   = animation;
            CurrentTime = 0f;
            UpdateCss   = updateCss;
            RawTarget   = targetValue;

            if (timeCurve == null)
            {
                ProgressSampler = null;
            }
            else
            {
                if (!(timeCurve is Blaze.RasterVectorPath))
                {
                    Blaze.RasterVectorPath rvp = new Blaze.RasterVectorPath();
                    timeCurve.CopyInto(rvp);
                    rvp.ToStraightLines();
                    timeCurve = rvp;
                }

                ProgressSampler = new Blaze.CurveSampler(timeCurve);
                ProgressSampler.Reset();
            }
        }
Exemple #7
0
        /// <summary>Sets the defaults for one or more transform functions.</summary>
        private void SetDefaults(Css.Value set)
        {
            // Get as a function:
            Css.Functions.Transformation tf = set as Css.Functions.Transformation;

            if (tf != null)
            {
                tf.SetDefaults();
                return;
            }

            // Should be a set:
            if (set is Css.ValueSet)
            {
                for (int i = 0; i < set.Count; i++)
                {
                    // Get as a function:
                    tf = set[i] as Css.Functions.Transformation;

                    if (tf != null)
                    {
                        tf.SetDefaults();
                    }
                }
            }
        }
        private void Complete()
        {
            // Remove from the update queue:
            Stop();

            // Set target to the result.

            Css.Value target = RawTarget.Copy();

            // Write it straight out to the computed style:
            // (Internally handles any aliases for us)
            Animation.ComputedStyle[InnerPropertyInfo] = target;

            // If it's not an alias, update hostValue:
            if (!InnerPropertyInfo.IsAlias)
            {
                PropertyValueObject = RawTarget;
            }

            if (UpdateCss)
            {
                // If we're the main animation (updateCss is true), tell the style it changed:
                // Note that in grouped properties, only the last one actually runs the update.
                Animation.ComputedStyle.ChangeProperty(PropertyInfo, PropertyValueObject);

                // And call the done function:
                Animation.Finished();
            }
        }
Exemple #9
0
        /// <summary>Resolves the marker for the given computed style.</summary>
        public static string GetOrdinal(ComputedStyle style, bool prefixed)
        {
            // Get the list style type for this element:
            Css.Value value = style[Css.Properties.ListStyleType.GlobalProperty];

            if (value == null)
            {
                // Disc is the default:
                return(style.reflowDocument.GetOrdinal(style.Element.childElementIndex + 1, "disc", prefixed) + " ");
            }
            else if (value.IsType(typeof(Css.Keywords.None)))
            {
                return("");
            }

            // Get textual name:
            string name = value.Text;

            // Is it a named set?
            string result = style.reflowDocument.GetOrdinal(style.Element.childElementIndex + 1, name, prefixed);

            if (result == null)
            {
                // It's possibly the symbols() function or alternatively it's just some text.
                // Assume it's some text for now!
                result = value.Text;
            }

            return(result);
        }
        /// <summary>Gets a CSS standard value. Typically e.g. "2in".</summary>
        public static Css.Value Get(string valueText)
        {
            // Load it (never null):
            Css.Value value = Css.Value.Load(valueText);

            return(value);
        }
Exemple #11
0
        public override bool OnReadValue(Style styleBlock, Css.Value value, int start, out int size)
        {
            int setCount = Set.Length;
            int usedUp   = 0;

            size = 0;

            // Unfortunately this is required to be worst-case n^2, but n is usually very small (typically just 2).
            bool somethingWasSpent = true;

            while (somethingWasSpent)
            {
                // Clear the flag:
                somethingWasSpent = false;

                for (int b = 0; b < setCount; b++)
                {
                    // If it has been 'spent' then ignore this one.
                    if (Used[b])
                    {
                        continue;
                    }

                    int currentSize;
                    if (Set[b].OnReadValue(styleBlock, value, start, out currentSize))
                    {
                        // 'b' has now been used up.
                        Used[b] = true;

                        // Move start and size along:
                        start += currentSize;
                        size  += currentSize;

                        // We now go again if there's any that have not been used up.
                        somethingWasSpent = true;
                        usedUp++;
                        break;
                    }
                }

                if (usedUp == setCount)
                {
                    // Used them all up.
                    break;
                }
            }

            // Clear used:
            for (int i = 0; i < setCount; i++)
            {
                Used[i] = false;
            }

            // Valid if at least 1 matched.
            return(size != 0);
        }
Exemple #12
0
        public override bool OnReadValue(Style styleBlock, Css.Value value, int start, out int size)
        {
            if (!Value.OnReadValue(styleBlock, value, start, out size))
            {
                size = 0;
            }

            // Ok either way:
            return(true);
        }
Exemple #13
0
        /// <summary>Starts animating the named property and target value.</summary>
        /// <param name="property">The property to update. May be an alias or composite.</param>
        /// <param name="value">The target value of the property.</param>
        /// <param name="updateCss">True if this property should update CSS/ the screen when it's progressed.</param>
        private void Animate(CssProperty property, Css.Value value, bool updateCss)
        {
            // Check if this property is already animated - if so, interrupt it and override with our new values.
            // There won't be many actively animated properties, so looping through the update queue is fast anyway.
            AnimatedProperty animProperty = GetAnimatedProperty(Animating, property);

            if (animProperty != null)
            {
                animProperty.Animate(this, value, TimeCurve, updateCss);
            }
            else
            {
                // Otherwise we want to create one or more AnimatedProperties and stick them into the queue.

                // Get or create the initial value:
                Css.Value hostValue;
                Css.Value rawValue = property.GetOrCreateValue(Animating, ComputedStyle, false, out hostValue);

                if (rawValue is Css.ValueSet)
                {
                    // A special case is when animating a value set. Each one needs to actually animate separately.
                    // E.g. padding:40px 20px; then animating to just padding:30px;

                    for (int i = 0; i < rawValue.Count; i++)
                    {
                        // Create it now:
                        animProperty = new AnimatedProperty(this, property.GetAliased(i, true));

                        // Setup the value:
                        animProperty.SetupValue(hostValue, rawValue[i]);

                        // Get the target value:
                        Css.Value target = (value is Css.ValueSet) ? value[i] : value;

                        // Animate it now:
                        animProperty.Animate(this, target, TimeCurve, updateCss);

                        animProperty.AddToQueue();
                    }
                }
                else
                {
                    // Create it now:
                    animProperty = new AnimatedProperty(this, property);

                    // Setup the value:
                    animProperty.SetupValue(hostValue, rawValue);

                    // Animate it now:
                    animProperty.Animate(this, value, TimeCurve, updateCss);

                    animProperty.AddToQueue();
                }
            }
        }
Exemple #14
0
        public override bool OnReadValue(Style styleBlock, Css.Value value, int start, out int size)
        {
            if (value[start].GetRawDecimal() == RawLiteral)
            {
                size = 1;
                return(true);
            }

            size = 0;
            return(false);
        }
Exemple #15
0
        public override bool OnReadValue(Style styleBlock, Css.Value value, int start, out int size)
        {
            // Check if it's inherit or initial:
            if (value is Css.Keywords.Initial || value is Css.Keywords.Inherit)
            {
                // Apply it!
                size = 1;

                if (SetInfo != null)
                {
                    // Indicate that we've been set:
                    SetInfo.Set = true;
                }

                styleBlock[RawProperty] = value;

                return(true);
            }

            Css.Spec.Value spec = RawProperty.Specification;

            if (spec.OnReadValue(styleBlock, value, start, out size))
            {
                // Apply it!
                if (SetInfo != null)
                {
                    // Indicate that we've been set:
                    SetInfo.Set = true;
                }

                if (size == 1)
                {
                    styleBlock[RawProperty] = value[start];
                }
                else
                {
                    // Chop out a segment:
                    Css.ValueSet set = new Css.ValueSet(new Css.Value[size]);

                    for (int i = 0; i < size; i++)
                    {
                        set[i] = value[start + i];
                    }

                    // Apply:
                    styleBlock[RawProperty] = set;
                }

                return(true);
            }

            size = 0;
            return(false);
        }
Exemple #16
0
        public override bool OnReadValue(Style styleBlock, Css.Value value, int start, out int size)
        {
            for (int i = 0; i < Set.Length; i++)
            {
                if (Set[i].OnReadValue(styleBlock, value, start, out size))
                {
                    return(true);
                }
            }

            size = 0;
            return(false);
        }
Exemple #17
0
        public override bool OnReadValue(Style styleBlock, Css.Value value, int start, out int size)
        {
            Css.CssFunction func = value[start] as Css.CssFunction;

            if (func != null && func.Name == Name)
            {
                size = 1;
                return(true);
            }

            size = 0;
            return(false);
        }
Exemple #18
0
        /// <summary>Gets the named CSS property as a colour. If it's not found, the given default is used.</summary>
        protected Color GetFilterColour(CssProperty prop, Color deflt)
        {
            // Resolve the property (it must know its own default):
            Css.Value value = ComputedStyle.Resolve(prop);

            if (value == null)
            {
                return(deflt);
            }

            // Get as a colour:
            return(value.GetColour(RenderData, prop));
        }
Exemple #19
0
        /// <summary>Resolves the named CSS property into a decimal value held in the computed style.</summary>
        protected float ResolveDecimal(Css.CssProperty prop)
        {
            // Reslove now:
            Css.Value val = ComputedStyle.Resolve(prop);

            if (val == null)
            {
                // Use initial value to avoid this one.
                return(0f);
            }

            return(val.GetDecimal(RenderData, prop));
        }
Exemple #20
0
        /// <summary>Animates css properties on this element.</summary>
        /// <param name="css">A set of target css properties, e.g. "rotate-x:45deg;scale-y:110%;".</param>
        /// <param name="duration">The time, in seconds, to take animating the properties.</param>
        /// <param name="cssTimeFunction">The timing function (CSS).
        /// Can be anything that's valid CSS; e.g. "steps(3)" or "ease". Note that "ease" is the default in CSS.</summary>
        /// <returns>An animation instance which can be used to track progress.</returns>
        public UIAnimation animate(string css, float duration, string cssTimeFunction)
        {
            // Load the CSS function:
            Css.Value value = Css.Value.Load(cssTimeFunction);

            Blaze.VectorPath timeFunc = null;

            if (value != null)
            {
                // Get as a path:
                timeFunc = value.GetPath(null, null);
            }

            return(new UIAnimation(this, css, duration, timeFunc));
        }
Exemple #21
0
        public override void OnComputeBox(Renderman renderer, Css.LayoutBox box, ref bool widthUndefined, ref bool heightUndefined)
        {
            RectangleProvider rect = Rectangle;

            // Check to see if w/h was updated via CSS:
            Css.Value newWidth  = Width;
            Css.Value newHeight = Height;

            if (rect.Width != newWidth || rect.Height != newHeight)
            {
                rect.Width  = newWidth;
                rect.Height = newHeight;
                RebuildPath();
            }
        }
Exemple #22
0
        /// <summary>Sets up general information for a particular animation and starts running it.</summary>
        private void Setup(Node animating, Style style, float duration, Blaze.VectorPath timeCurve)
        {
            Animating     = animating;
            ComputedStyle = (animating as IRenderableNode).ComputedStyle;

            if (duration <= 0f)
            {
                duration = 0f;
            }

            Duration  = duration;
            TimeCurve = timeCurve;

            // Start animating the properties now.

            foreach (KeyValuePair <CssProperty, Css.Value> kvp in style.Properties)
            {
                // Get the value:
                Css.Value value = kvp.Value;

                // Get the property:
                CssProperty property = kvp.Key;

                // Grab the type:
                Css.ValueType type = value.Type;

                // Animate it (note that we don't need to copy it):
                if (property == Css.Properties.TransformProperty.GlobalProperty)
                {
                    // Special case for animating transform.
                    AnimateTransform(value);
                }
                else if (type == Css.ValueType.Set)
                {
                    int count = value.Count;

                    for (int i = 0; i < count; i++)
                    {
                        // Get the correct aliased property and animate it:
                        Animate(property.GetAliased(i, true), value[i], (i == (count - 1)));
                    }
                }
                else
                {
                    Animate(property, value, true);
                }
            }
        }
        private void BuildFilter(RenderContext renderer, bool withCssFilter)
        {
            // Get a path to draw:
            VectorPath path = GetPath(this, renderer);

            if (Visibility == VisibilityMode.Hidden || !PushTransforms(renderer))
            {
                return;
            }

            if (withCssFilter)
            {
                // Get the filter CSS property:
                Css.Value filterValue = style.Computed[Css.Properties.Filter.GlobalProperty];

                if (filterValue != null)
                {
                    string filterPath = filterValue.Text;

                    Element filter = document.getElementById(filterPath) as SVGFilterElement;

                    if (filter != null)
                    {
                        PopTransforms(renderer);

                        // filter.ApplyFilter(this, renderer, (r) => this.BuildFilter(r, false));

                        // Don't render normally.
                        return;
                    }
                }
            }

            SetClip(renderer);

            if (path != null)
            {
                BuildFill(path, renderer);
                BuildStroke(path, renderer);
            }
            else
            {
                BuildChildren(renderer);
            }

            ResetClip(renderer);
            PopTransforms(renderer);
        }
Exemple #24
0
        /// <summary>Scrolls the element by the given values.</summary>
        /// <param name="x">The change in x pixels.</param>
        /// <param name="y">The change in y pixels.</param>
        public void scrollBy(float x, float y)
        {
            if (x == 0f && y == 0f)
            {
                return;
            }

            // Get the scroll value (if there is one):
            Css.Value scroll = Style.Computed.Scroll;

            if (scroll == null)
            {
                scrollTo(x, y);
                return;
            }

            float top  = (scroll[0] == null) ? 0f : scroll[0].GetDecimal(RenderData, null);
            float left = (scroll[1] == null) ? 0f : scroll[1].GetDecimal(RenderData, null);

            scrollTo(left + x, top + y);
        }
Exemple #25
0
        /// <summary>Special case when animating CSS transform.</summary>
        private void AnimateTransform(Css.Value value)
        {
            // Check if this property is already animated - if so, interrupt it and override with our new values.
            // There won't be many actively animated properties, so looping through the update queue is fast anyway.
            CssProperty property = Css.Properties.TransformProperty.GlobalProperty;

            AnimatedTransformProperty animProperty = GetAnimatedProperty(Animating, property) as AnimatedTransformProperty;

            bool isNew = (animProperty == null);

            if (isNew)
            {
                // Create it now:
                animProperty = new AnimatedTransformProperty(this);

                // Add to queue:
                animProperty.AddToQueue();
            }

            animProperty.Animate(this, value, TimeCurve, true);
        }
        /// <summary>Called when the thumb is being dragged.</summary>
        public override bool OnDrag(PowerUI.DragEvent mouseEvent)
        {
            // Get the amount of pixels the pointer moved by:
            float deltaX = AllowX ? mouseEvent.deltaX : 0f;
            float deltaY = AllowY ? mouseEvent.deltaY : 0f;

            if (deltaX == 0f && deltaY == 0f)
            {
                return(false);
            }

            // Resize now!
            ComputedStyle cs = ToResize_.Style.Computed;

            if (deltaX != 0f)
            {
                // Width is..
                Css.Value width = cs[Css.Properties.Width.GlobalProperty];

                // Update it:
                deltaX += width.GetDecimal(ToResize_.RenderData, Css.Properties.Width.GlobalProperty);

                // Write it back out:
                cs.ChangeProperty(Css.Properties.Width.GlobalProperty, new Css.Units.DecimalUnit(deltaX));
            }

            if (deltaY != 0f)
            {
                // Height is..
                Css.Value height = cs[Css.Properties.Height.GlobalProperty];

                // Update it:
                deltaY += height.GetDecimal(ToResize_.RenderData, Css.Properties.Height.GlobalProperty);

                // Write it back out:
                cs.ChangeProperty(Css.Properties.Height.GlobalProperty, new Css.Units.DecimalUnit(deltaY));
            }

            return(false);
        }
        /// <summary>Sets the clipping boundary from the given computed style.</summary>
        /// <param name="style">The computed style to find the clipping boundary from.</param>
        public void SetBoundary(ComputedStyle computed, LayoutBox box)
        {
            bool visibleX = (box.OverflowX == VisibilityMode.Visible);
            bool visibleY = (box.OverflowY == VisibilityMode.Visible);

            if (visibleX && visibleY)
            {
                return;
            }

            BoxRegion newBoundary = null;

            if (visibleX)
            {
                newBoundary = new BoxRegion(ClippingBoundary.X, box.Y + box.FixedStyleOffsetTop, ClippingBoundary.Width, box.InnerHeight);
            }
            else if (visibleY)
            {
                newBoundary = new BoxRegion(box.X + box.FixedStyleOffsetLeft, ClippingBoundary.Y, box.InnerWidth, ClippingBoundary.Height);
            }
            else
            {
                newBoundary = new BoxRegion(box.X + box.FixedStyleOffsetLeft, box.Y + box.FixedStyleOffsetTop, box.InnerWidth, box.InnerHeight);
            }

            // Should it be clipped?
            Css.Value clipValue = computed[Css.Properties.ClipMode.GlobalProperty];

            if (clipValue == null || clipValue.IsType(typeof(Css.Keywords.None)) || clipValue.GetBoolean(computed.RenderData, Css.Properties.ClipMode.GlobalProperty))
            {
                newBoundary.ClipBy(ClippingBoundary);
            }
            else
            {
                ScreenClip = false;
            }

            ClippingBoundary = newBoundary;
        }
Exemple #28
0
        public override bool OnReadValue(Style styleBlock, Css.Value value, int start, out int size)
        {
            int setCount = Set.Length;

            size = 0;

            for (int i = 0; i < setCount; i++)
            {
                int currentSize;
                if (Set[i].OnReadValue(styleBlock, value, start, out currentSize))
                {
                    start += currentSize;
                    size  += currentSize;
                }
                else
                {
                    size = 0;
                    return(false);
                }
            }

            // All ok!
            return(true);
        }
        /// <summary>Loads a timeline from the given JSON.</summary>
        public void load(Json.JSObject json)
        {
            if (started)
            {
                // Reset if needed:
                reset();
            }

            duration = null;

            // First, check for the 'tracks' field:
            Json.JSObject trackData = json["tracks"];

            if (trackData == null)
            {
                // Considered to be a single track.
                // Try loading it now:
                Track track = Track.loadFromJson(this, json);

                if (track == null)
                {
                    // Empty:
                    tracks = new Track[0];
                }
                else
                {
                    // We have at least 1 entry in a track.
                    tracks = new Track[1];

                    // We now need to detect what kind of track it is based on what it provides.
                    // If it's a style track, the first element
                    tracks[0] = track;
                }
            }
            else
            {
                // Optional default language (must be before tracks load):
                string lang = json.String("lang");

                if (!string.IsNullOrEmpty(lang))
                {
                    defaultLanguage = lang.Trim().ToLower();
                }

                // Must be an indexed array:
                var trackArray = trackData as Json.JSIndexedArray;

                if (trackArray == null)
                {
                    loadFailed("'tracks' must be an indexed array.", this);
                }

                // Fully defined timeline.
                int length = trackArray.length;

                // Create track set:
                tracks = new Track[length];

                // Load each one:
                for (int i = 0; i < length; i++)
                {
                    // Load the track now:
                    tracks[i] = Track.loadFromJson(this, trackArray[i]);
                }

                // Optional duration:
                string durationText = json.String("duration");

                if (!string.IsNullOrEmpty(durationText))
                {
                    // Load it as a CSS value:
                    duration = Css.Value.Load(durationText);
                }
            }

            status_ = TIMELINE_STARTED;
        }
Exemple #30
0
        /// <summary>Figures out what kind of lerp is required with the given from/to values.</summary>
        public void ApplyValues(Css.Value from, Css.Value to)
        {
            // Get computed:
            if (from != null)
            {
                from = from.Computed;

                if (from.IsType(typeof(Css.Keywords.None)))
                {
                    from = null;
                }
            }

            if (to != null)
            {
                to = to.Computed;

                if (to.IsType(typeof(Css.Keywords.None)))
                {
                    to = null;
                }
            }

            // If both are null, do nothing.
            if (to == null && from == null)
            {
                return;
            }

            // If either is a cached transformValue, pull the origin:
            if (to != null && to is Css.Units.TransformValue)
            {
                Css.Units.TransformValue rawTo = to as Css.Units.TransformValue;
                to = rawTo.Origin;
            }

            if (from != null && from is Css.Units.TransformValue)
            {
                Css.Units.TransformValue rawFrom = from as Css.Units.TransformValue;
                from = rawFrom.Origin;
            }

            // If one or the other is null then it acts like the identity of the other.
            if (to != null && from != null)
            {
                // Check for functional equiv:
                if (!to.FunctionalEquals(from))
                {
                    // Matrix interpolation :'(

                    // Bake both matrices:
                    RenderableData rd = Animation.ComputedStyle.RenderData;

                    Matrix4x4 fromMatrix = Css.Properties.TransformProperty.Compute(from, rd);
                    Matrix4x4 toMatrix   = Css.Properties.TransformProperty.Compute(to, rd);

                    // If either is 3D then they both are treated as 3D values:
                    if (Css.Properties.TransformProperty.Is3D(from) || Css.Properties.TransformProperty.Is3D(to))
                    {
                        // 3D!

                        // Write out into this value (no prep required here):
                        FromFunctions = new InterpolationMatrix3D(fromMatrix);
                        ToFunctions   = new InterpolationMatrix3D(toMatrix);
                    }
                    else
                    {
                        // 2D!

                        // Write out into this value:
                        InterpolationMatrix fromInt = new InterpolationMatrix(fromMatrix);
                        InterpolationMatrix toInt   = new InterpolationMatrix(toMatrix);

                        // Prep for interpolation:
                        fromInt.PrepareForInterpolate(toInt);

                        ToFunctions   = toInt;
                        FromFunctions = fromInt;
                    }

                    // Copy the from value:
                    ActiveFunctions = FromFunctions.Copy();

                    // Write it back out:
                    WriteOut();
                    return;
                }
            }

            // Interpolate every parameter of each function (if the other is null, use 0)
            // Note! The same function can appear multiple times.
            ToFunctions   = to;
            FromFunctions = from;

            if (FromFunctions == null)
            {
                // Copy to (going from none) and set it all as zero:
                ActiveFunctions = ToFunctions.Copy();

                // Set it all to 'zero' (it's e.g. 1 for scale though!)
                SetDefaults(ActiveFunctions);
            }
            else
            {
                // Copy from:
                ActiveFunctions = FromFunctions.Copy();
            }

            // Write it back out:
            WriteOut();
        }