DrawingContextFlattener.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Print / Reach / Serialization / DrawingContextFlattener.cs / 1 / DrawingContextFlattener.cs

                            //------------------------------------------------------------------------------ 
//  Microsoft Avalon
//  Copyright (c) Microsoft Corporation, all rights reserved
//
//  File:       DrawingContextFlattener.cs 
//
//  History: 
//      [....]: 01/25/2005  Split from VisualSerializer.cs 
//
//----------------------------------------------------------------------------- 

using System;
using System.Diagnostics;
using System.Collections; 
using System.Collections.Specialized;
using System.Collections.Generic; 
using System.Text; 
using System.ComponentModel;
 
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging; 
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation; 
using Microsoft.Internal.AlphaFlattener; 

namespace System.Windows.Xps.Serialization 
{
    #region internal class DrawingContextFlattener
    /// 
    /// DrawingContext flattening filter 
    /// 1) Convert animation to static value
    /// 2) Rasterize video/Scene3D to bitmap 
    /// 3) Convert shapes to Geometry 
    /// 4) Convert VisualBrush to DrawingBrush
    /// 5) Convert DrawingImage to ImageBrush 
    /// 6) Flatten DrawDrawing
    /// 
    internal class DrawingContextFlattener
    { 
        #region Constants
 
        ///  
        /// Percentage to inflate rasterization clip rectangle to prevent prematurely clipping
        /// Visuals at page edges. 
        /// 
        private const double RasterizationClipInflate = 0.2;

        #endregion 

        #region Private Fields 
 
        private IMetroDrawingContext _dc;
 
        // Stores pushed transforms. Each transform contains all prior transforms.
        private List _fullTransform = new List();

        // Pushed combined clipping in world space. 
        private List _fullClip = new List();
 
        private Size     _pageSize; 

        #endregion 

        #region Constructors

        internal DrawingContextFlattener(IMetroDrawingContext dc, Size pageSize) 
        {
            _dc       = dc; 
            _pageSize = pageSize; 
        }
 
        #endregion

        #region Public State
 
        public void Push(
            Transform transform, 
            Geometry clip, 
            double opacity,
            Brush opacityMask, 
            Rect maskBounds,
            bool onePrimitive,
            String nameAttr,
            Visual node, 
            Uri navigateUri,
            EdgeMode edgeMode) 
        { 
            Debug.Assert(Utility.IsValid(opacity), "Invalid opacity should clip subtree");
 
            Matrix mat = Matrix.Identity;

            if (transform != null)
            { 
                mat = transform.Value;
            } 
 
            // opacity mask might be VisualBrush, hence ReduceBrush to reduce to DrawingBrush
            Debug.Assert(!BrushProxy.IsEmpty(opacityMask), "empty opacity mask should not result in Push"); 
            _dc.Push(
                mat,
                clip,
                opacity, 
                ReduceBrush(opacityMask, maskBounds),
                maskBounds, 
                onePrimitive, 
                nameAttr,
                node, 
                navigateUri,
                edgeMode
                );
 
            // prepend to transforms and clipping stack
            mat.Append(Transform); 
            _fullTransform.Add(mat); 

            // transform clip to world space, intersect with current clip, and push 
            if (clip == null)
            {
                // push current clipping
                clip = Clip; 
            }
            else 
            { 
                clip = Utility.TransformGeometry(clip, Transform);
 
                bool empty;
                clip = Utility.Intersect(clip, Clip, Matrix.Identity, out empty);

                if (empty) 
                {
                    clip = Geometry.Empty; 
                } 
            }
 
            _fullClip.Add(clip);
        }

        ///  
        /// Pop the most recent Push operation
        ///  
        public void Pop() 
        {
            _dc.Pop(); 

            Debug.Assert(_fullTransform.Count == _fullClip.Count);

            int lastIndex = _fullTransform.Count - 1; 

            _fullTransform.RemoveAt(lastIndex); 
            _fullClip.RemoveAt(lastIndex); 
        }
 
        /// 
        /// Transformation representing all pushed transformations.
        /// 
        public Matrix Transform 
        {
            get 
            { 
                if (_fullTransform.Count == 0)
                { 
                    return Matrix.Identity;
                }
                else
                { 
                    return _fullTransform[_fullTransform.Count - 1];
                } 
            } 
        }
 
        /// 
        /// Intersection of all pushed clipping in world space.
        /// 
        public Geometry Clip 
        {
            get 
            { 
                if (_fullClip.Count == 0)
                { 
                    return null;
                }
                else
                { 
                    return _fullClip[_fullClip.Count - 1];
                } 
            } 
        }
 
        #endregion

        #region Public Methods
 
#if DEBUG
        ///  
        /// Add comment to output, as debugging aid or add extra information like document structure 
        /// 
        ///  
        public void Comment(String str)
        {
            _dc.Comment(str);
        } 
#endif
 
        ///  
        /// Simplifies brush so we don't have to handle as many cases.
        ///  
        /// 
        /// Brush fill bounds; must not be empty if VisualBrush
        /// 
        ///  
        /// Cases simplified:
        /// - A lot of empty brush cases. See BrushProxy.IsEmpty. 
        /// - GradientBrush where gradient colors are similar enough to be SolidColorBrush. 
        /// - Reduce VisualBrush to DrawingBrush.
        /// - Reduce ImageBrush with DrawingImage to DrawingBrush. 
        /// 
        private Brush ReduceBrush(Brush brush, Rect bounds)
        {
            return BrushProxy.ReduceBrush(brush, bounds, Transform, _pageSize); 
        }
 
        private Pen ReducePen(Pen pen, Rect bounds) 
        {
            if (PenProxy.IsNull(pen)) 
            {
                return null;
            }
 
            Brush b = ReduceBrush(pen.Brush, bounds);
 
            if (b == null) 
            {
                return null; 
            }

            if (! Object.ReferenceEquals(b, pen.Brush))
            { 
                pen = pen.CloneCurrentValue();
 
                pen.Brush = b; 
            }
 
            return pen;
        }

        ///  
        /// Draw a Geometry with the provided Brush and/or Pen.
        ///  
        public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) 
        {
            if (geometry != null) 
            {
                if (brush != null)
                {
                    Rect bounds = geometry.Bounds; 

                    brush = ReduceBrush(brush, bounds); 
                } 

                if ((pen != null) && (pen.Brush != null)) 
                {
                    Rect bounds = Rect.Empty;

                    if (VisualSerializer.NeedBounds(pen.Brush)) 
                    {
                        bounds = geometry.GetRenderBounds(pen); 
                    } 

                    pen = ReducePen(pen, bounds); 
                }
                else
                {
                    pen = null; 
                }
 
                // Draw even if brush/pen is null since geometry may be a hyperlink. 
                // _dc should cull invisible geometries when necessary.
                _dc.DrawGeometry(brush, pen, geometry); 
            }
        }

        ///  
        /// Draw an Image into the region specified by the Rect.
        ///  
        public void DrawImage(ImageSource image, Rect rectangle) 
        {
            if (image != null) 
            {
                bool handled = false;
                BitmapSource bitmapImage = image as BitmapSource;
 
                if (bitmapImage != null)
                { 
                    // we can handle BitmapSource 
                    handled = true;
                    _dc.DrawImage(image, rectangle); 
                }

                DrawingImage drawingImage = image as DrawingImage;
 
                if (drawingImage != null)
                { 
                    // convert DrawingImage to geometry fill with DrawingBrush 
                    handled = true;
 
                    DrawingBrush brush = new DrawingBrush(drawingImage.Drawing);

                    DrawGeometry(brush, null, new RectangleGeometry(rectangle));
                } 

                Debug.Assert(handled, "Unhandled Image.ImageSource type"); 
            } 
            else
            { 
                // no image, but may still be a hyperlink
                _dc.DrawImage(null, rectangle);
            }
        } 

        ///  
        /// Clip visual bounds to rasterization clip rectangle. 
        /// 
        ///  
        /// 
        /// 
        private Rect PerformRasterizationClip(Rect visualBounds, Matrix visualToWorldTransform)
        { 
            if (! _pageSize.IsEmpty)
            { 
                Rect pageBox = new Rect(0, 0, _pageSize.Width, _pageSize.Height); 

                pageBox.Inflate( 
                    RasterizationClipInflate * pageBox.Width,
                    RasterizationClipInflate * pageBox.Height
                    );
 
                visualBounds.Intersect(pageBox);
            } 
 
            return visualBounds;
        } 

        /// 
        /// Rasterizes Visual and its descendents with optional bitmap effect. Also handles Visual opacity.
        ///  
        /// 
        /// Preserve Visual name attribute 
        /// Preserve FixedPage.NavigateUri 
        /// Preserve RenderOptions.EdgeMode
        /// Visual.Transform 
        /// Full transform from Visual to world space, including visual transform prepended
        /// 
        /// Transformation above VisualTreeFlattener instance. This is needed if we're reducing VisualBrush to
        /// DrawingBrush to increase rasterization fidelity if brush is small, but will eventually fill large region. 
        /// 
        /// Clip in world space 
        /// Optional bitmap effect 
        public void DrawRasterizedVisual(
            Visual visual, 
            string nameAttr,
            Uri navigateUri,
            EdgeMode edgeMode,
            Transform visualTransform, 
            Matrix visualToWorldTransform,
            Matrix inheritedTransformHint, 
            Geometry clip, 
            BitmapEffect effect
            ) 
        {
            Debug.Assert(visual != null);

            // Compute the bounding box of the visual and its descendants 
            Rect bounds = VisualTreeHelper.GetContentBounds(visual);
            bounds.Union(VisualTreeHelper.GetDescendantBounds(visual)); 
 
            if (!Utility.IsRenderVisible(bounds))
                return; 

            // transform clip to visual space
            if (clip != null)
            { 
                Matrix worldToVisualTransform = visualToWorldTransform;
                worldToVisualTransform.Invert(); 
 
                clip = Utility.TransformGeometry(clip, worldToVisualTransform);
            } 

            //
            // Clip visual bounds to rasterization clipping geometry.
            // We can't clip to Visual clipping geometry, since bitmap effects are applied 
            // without clipping. For example, the blur effect looks different if you clip first
            // then apply effect, compared to applying the effect and then clipping. 
            // 
            bounds = PerformRasterizationClip(bounds, visualToWorldTransform);
 
            if (!Utility.IsRenderVisible(bounds))
                return;

            // 
            // Rasterize Visual to IMetroDrawingContext with optional banding, depending
            // on whether bitmap effect is present. We can Push/Pop/Draw directly on _dc 
            // since the input we provide it is already normalized (no DrawingImage, for example). 
            // We also don't need Transform or Clip to be updated.
            // 
            _dc.Push(
                visualTransform == null ? Matrix.Identity : visualTransform.Value,
                clip,
                1.0, 
                null,
                Rect.Empty, 
                /*onePrimitive=*/false, // we Push and DrawImage below, which counts as 2 primitives 
                nameAttr,
                visual, 
                navigateUri,
                edgeMode
                );
 
            Matrix bitmapToVisualTransform;
            BitmapSource bitmap; 
 
            //
            // Rasterize visual in its entirety. Banding is not useful at this point since 
            // the resulting bands are all kept in memory anyway. Plus transformation is applied
            // to the band, which causes gaps between bands if they're rotated (bug 1562237).
            //
            // Banding is performed at GDIExporter layer just prior to sending images to the printer. 
            //
            if (effect == null) 
            { 
                bitmap = Utility.RasterizeVisual(
                    visual, 
                    bounds,
                    visualToWorldTransform * inheritedTransformHint,
                    out bitmapToVisualTransform
                    ); 
            }
            else 
            { 
                BitmapEffectInput effectInput = VisualTreeHelper.GetBitmapEffectInput(visual);
 
                bitmap = Utility.RasterizeVisualWithEffect(
                    visual,
                    bounds,
                    visualToWorldTransform * inheritedTransformHint, 
                    effect,
                    effectInput, 
                    out bitmapToVisualTransform 
                    );
            } 

            if (bitmap != null)
            {
                _dc.Push(bitmapToVisualTransform, null, 1.0, null, Rect.Empty, /*onePrimitive=*/true, null, null, null, EdgeMode.Unspecified); 
                _dc.DrawImage(bitmap, new Rect(0, 0, bitmap.Width, bitmap.Height));
                _dc.Pop(); 
            } 

            _dc.Pop(); 
        }

        /// 
        /// Rasterizes a Drawing to IMetroDrawingContext. 
        /// 
        ///  
        ///  
        /// See RasterizeVisual.
        public void RasterizeDrawing( 
            DrawingGroup drawing,
            Matrix drawingToWorldTransform,
            Matrix inheritedTransformHint
            ) 
        {
            Rect bounds = drawing.Bounds; 
 
            if (!Utility.IsRenderVisible(bounds))
                return; 

            bounds = PerformRasterizationClip(bounds, drawingToWorldTransform);

            if (!Utility.IsRenderVisible(bounds)) 
                return;
 
            Matrix bitmapToDrawingTransform; 

            BitmapSource bitmap = Utility.RasterizeDrawing( 
                drawing,
                bounds,
                drawingToWorldTransform * inheritedTransformHint,
                out bitmapToDrawingTransform 
                );
 
            if (bitmap != null) 
            {
                _dc.Push(bitmapToDrawingTransform * drawingToWorldTransform, null, 1.0, null, Rect.Empty, true, null, null, null, EdgeMode.Unspecified); 
                _dc.DrawImage(bitmap, new Rect(0, 0, bitmap.Width, bitmap.Height));
                _dc.Pop();
            }
        } 

        ///  
        /// Draw a GlyphRun. 
        /// 
        public void DrawGlyphRun(Brush foreground, GlyphRun glyphRun) 
        {
            if (glyphRun != null)
            {
                foreground = ReduceBrush(foreground, glyphRun.ComputeInkBoundingBox()); 

                // foreground may be null, but glyphrun may still be a hyperlink 
                _dc.DrawGlyphRun(foreground, glyphRun); 
            }
        } 

        #endregion
    }
    #endregion 

    internal static class GeometryHelper 
    { 
        const double FUZZ = 1e-6;           // Relative 0
        const double PI_OVER_180 = Math.PI / 180;  // PI/180 

        //  Function: AcceptRadius
        //  Synopsis: Accept one radius
        //  Return:   false if the radius is too small compared to the chord length 
        public static bool
        AcceptRadius( 
            double rHalfChord2,    // (1/2 chord length)squared 
            double rFuzz2,         // Squared fuzz
            ref double rRadius)   // The radius to accept (or not) 
        {
            Debug.Assert(rHalfChord2 >= rFuzz2);   // Otherewise we have no guarantee that the radius is not 0,
            // and we need to divide by the radius
            bool fAccept = (rRadius * rRadius > rHalfChord2 * rFuzz2); 

            if (fAccept) 
            { 
                if (rRadius < 0)
                { 
                    rRadius = 0;
                }
            }
 
            return fAccept;
        } 
 
        public static Point Add(Point a, Point b)
        { 
            return new Point(a.X + b.X, a.Y + b.Y);
        }

        public static Point Sub(Point a, Point b) 
        {
            return new Point(a.X - b.X, a.Y - b.Y); 
        } 

        // Dot Product 
        public static double DotProduct(Point a, Point b)
        {
            return a.X * b.X + a.Y * b.Y;
        } 

        public static double Determinant(Point a, Point b) 
        { 
            return a.X * b.Y - a.Y * b.X;
        } 

        //+-------------------------------------------------------------------------------------------------

        // 
        //  Function: GetArcAngle
        // 
        //  Synopsis: Get the number of Bezier arcs, and sine & cosine of each 
        //
        //  Notes:    This is a private utility used by ArcToBezier 
        //            We break the arc into pieces so that no piece will span more than 90 degrees.
        //            The input points are on the unit circle
        //
        //------------------------------------------------------------------------------------------------- 
        public static void
        GetArcAngle( 
            Point ptStart,      // Start point 
            Point ptEnd,        // End point
            bool fIsLargeArc,     // Choose the larger of the 2 possible arcs if TRUE 
            SweepDirection eSweepDirection,      // Direction n which to sweep the arc.
            out double rCosArcAngle, // Cosine of a the sweep angle of one arc piece
            out double rSinArcAngle, // Sine of a the sweep angle of one arc piece
            out int cPieces)      // Out: The number of pieces 
        {
            double rAngle; 
 
            // The points are on the unit circle, so:
            rCosArcAngle = DotProduct(ptStart, ptEnd); 
            rSinArcAngle = Determinant(ptStart, ptEnd);

            if (rCosArcAngle >= 0)
            { 
                if (fIsLargeArc)
                { 
                    // The angle is between 270 and 360 degrees, so 
                    cPieces = 4;
                } 
                else
                {
                    // The angle is between 0 and 90 degrees, so
                    cPieces = 1; 
                    return; // We already have the cosine and sine of the angle
                } 
            } 
            else
            { 
                if (fIsLargeArc)
                {
                    // The angle is between 180 and 270 degrees, so
                    cPieces = 3; 
                }
                else 
                { 
                    // The angle is between 90 and 180 degrees, so
                    cPieces = 2; 
                }
            }

            // We have to chop the arc into the computed number of pieces.  For cPieces=2 and 4 we could 
            // have uses the half-angle trig formulas, but for cPieces=3 it requires solving a cubic
            // equation; the performance difference is not worth the extra code, so we'll get the angle, 
            // divide it, and get its sine and cosine. 

            Debug.Assert(cPieces > 0); 
            rAngle = Math.Atan2(rSinArcAngle, rCosArcAngle);

            if (eSweepDirection == SweepDirection.Clockwise)
            { 
                if (rAngle < 0)
                { 
                    rAngle += Math.PI * 2; 
                }
            } 
            else
            {
                if (rAngle > 0)
                { 
                    rAngle -= Math.PI * 2;
                } 
            } 

            rAngle /= cPieces; 
            rCosArcAngle = Math.Cos(rAngle);
            rSinArcAngle = Math.Sin(rAngle);
        }
 
        /******************************************************************************\
        * 
        * Function Description: 
        *
        * Get the distance from a circular arc's endpoints to the control points of the 
        * Bezier arc that approximates it, as a fraction of the arc's radius.
        *
        * Since the result is relative to the arc's radius, it depends strictly on the
        * arc's angle. The arc is assumed to be of 90 degrees of less, so the angle is 
        * determined by the cosine of that angle, which is derived from rDot = the dot
        * product of two radius vectors.  We need the Bezier curve that agrees with 
        * the arc's points and tangents at the ends and midpoint.  Here we compute the 
        * distance from the curve's endpoints to its control points.
        * 
        * Since we are looking for the relative distance, we can work on the unit
        * circle. Place the center of the circle at the origin, and put the X axis as
        * the bisector between the 2 vectors.  Let a be the angle between the vectors.
        * Then the X coordinates of the 1st & last points are cos(a/2).  Let x be the X 
        * coordinate of the 2nd & 3rd points.  At t=1/2 we have a point at (1,0).
        * But the terms of the polynomial there are all equal: 
        * 
        *           (1-t)^3 = t*(1-t)^2 = 2^2*(1-t) = t^3 = 1/8,
        * 
        * so from the Bezier formula there we have:
        *
        *           1 = (1/8) * (cos(a/2) + 3x + 3x + cos(a/2)),
        * hence 
        *           x = (1 - cos(a/2)) / 3
        * 
        * The X difference between that and the 1st point is: 
        *
        *           DX = x - cos(a/2) = 4(1 - cos(a/2)) / 3. 
        *
        * But DX = distance / sin(a/2), hence the distance is
        *
        *           dist = (4/3)*(1 - cos(a/2)) / sin(a/2). 
        *
        * Created:  5/29/2001 [....] 
        * 
        /*****************************************************************************/
        public static double 
        GetBezierDistance(  // Return the distance as a fraction of the radius
            double rDot,    // In: The dot product of the two radius vectors
            double rRadius) // In: The radius of the arc's circle (optional=1)
        { 
            double rRadSquared = rRadius * rRadius;  // Squared radius
 
            Debug.Assert(rDot >= -rRadSquared * .1);  // angle < 90 degrees 
            Debug.Assert(rDot <= rRadSquared * 1.1);  // as dot product of 2 radius vectors
 
            double rDist = 0;   // Acceptable fallback value

            /* Rather than the angle a, we are given rDot = R^2 * cos(a), so we
                multiply top and bottom by R: 

                                dist = (4/3)*(R - Rcos(a/2)) / Rsin(a/2) 
 
                and use some trig:
                                    __________ 
                        cos(a/2) = \/1 + cos(a) / 2
                                        ________________         __________
                        R*cos(a/2) = \/R^2 + R^2 cos(a) / 2 = \/R^2 + rDot / 2 */
 
            double rCos = (rRadSquared + rDot) / 2;   // =(R*cos(a))^2
            if (rCos < 0)   // Shouldn't happen but dist=0 will work 
            { 
                return rDist;
            } 
            //                 __________________
            //  R*sin(a/2) = \/R^2 - R^2 cos(a/2)

            double rSin = rRadSquared - rCos;         // =(R*sin(a))^2 
            if (rSin <= 0)
                // 0 angle, we shouldn't be rounding the corner, but dist=0 is OK 
                return rDist; 

            rSin = Math.Sqrt(rSin); //   = R*cos(a) 
            rCos = Math.Sqrt(rCos); //   = R*sin(a)

            rDist = 4 * (rRadius - rCos) / 3;
            if (rDist <= rSin * FUZZ) 
            {
                rDist = 0; 
            } 
            else
            { 
                rDist = 4 * (rRadius - rCos) / rSin / 3;
            }

            return rDist; 
        }
 
        //+------------------------------------------------------------------------------------------------- 
        //
        //  Function: ArcToBezier 
        //
        //  Synopsis: Compute the Bezier approximation of an arc
        //
        //  Notes:    This utilitycomputes the Bezier approximation for an elliptical arc as it is defined 
        //            in the SVG arc spec. The ellipse from which the arc is carved is axis-aligned in its
        //            own coordinates, and defined there by its x and y radii. The rotation angle defines 
        //            how the ellipse's axes are rotated relative to our x axis. The start and end points 
        //            define one of 4 possible arcs; the sweep and large-arc flags determine which one of
        //            these arcs will be chosen. See SVG spec for details. 
        //
        //            Returning cPieces = 0 indicates a line instead of an arc
        //                      cPieces = -1 indicates that the arc degenerates to a point
        // 
        //--------------------------------------------------------------------------------------------------
        [MS.Internal.ReachFramework.FriendAccessAllowed] 
        public static PointCollection ArcToBezier( 
            double xStart,     // X coordinate of the last point
            double yStart,     // Y coordinate of the last point 
            double xRadius,    // The ellipse's X radius
            double yRadius,    // The ellipse's Y radius
            double rRotation,  // Rotation angle of the ellipse's x axis
            bool fIsLargeArc,  // Choose the larger of the 2 possible arcs if TRUE 
            SweepDirection eSweepDirection,   // Sweep the arc while increasing the angle if TRUE
            double xEnd,       // X coordinate of the last point 
            double yEnd,       // Y coordinate of the last point 
            out int cPieces)    // The number of output Bezier curves
        { 
            double rCosArcAngle, rSinArcAngle, xCenter, yCenter, r, rBezDist;
            Point vecToBez1, vecToBez2;
            Matrix matToEllipse;
 
            double rFuzz2 = FUZZ * FUZZ;
            bool fZeroCenter = false; 
 
            cPieces = -1;
 
            // In the following, the line segment between between the arc's start and
            // end points is referred to as "the chord".

            // Transform 1: Shift the origin to the chord's midpoint 
            double x = (xEnd - xStart) / 2;
            double y = (yEnd - yStart) / 2; 
 
            double rHalfChord2 = x * x + y * y;     // (half chord length)^2
 
            // Degenerate case: single point
            if (rHalfChord2 < rFuzz2)
            {
                // The chord degeneartes to a point, the arc will be ignored 
                return null;
            } 
 
            // Degenerate case: straight line
            if (!AcceptRadius(rHalfChord2, rFuzz2, ref xRadius) || 
                !AcceptRadius(rHalfChord2, rFuzz2, ref yRadius))
            {
                // We have a zero radius, add a straight line segment instead of an arc
                cPieces = 0; 
                return null;
            } 
 
            // Transform 2: Rotate to the ellipse's coordinate system
            rRotation = -rRotation * PI_OVER_180; 

            double rCos = Math.Cos(rRotation);
            double rSin = Math.Sin(rRotation);
 
            r = x * rCos - y * rSin;
            y = x * rSin + y * rCos; 
            x = r; 

            // Transform 3: Scale so that the ellipse will become a unit circle 
            x /= xRadius;
            y /= yRadius;

            // We get to the center of that circle along a verctor perpendicular to the chord 
            // from the origin, which is the chord's midpoint. By Pythagoras, the length of that
            // vector is sqrt(1 - (half chord)^2). 
 
            rHalfChord2 = x * x + y * y;   // now in the circle coordinates
 
            if (rHalfChord2 > 1)
            {
                // The chord is longer than the circle's diameter; we scale the radii uniformly so
                // that the chord will be a diameter. The center will then be the chord's midpoint, 
                // which is now the origin.
                r = Math.Sqrt(rHalfChord2); 
                xRadius *= r; 
                yRadius *= r;
                xCenter = yCenter = 0; 
                fZeroCenter = true;

                // Adjust the unit-circle coordinates x and y
                x /= r; 
                y /= r;
            } 
            else 
            {
                // The length of (-y,x) or (x,-y) is sqrt(rHalfChord2), and we want a vector 
                // of length sqrt(1 - rHalfChord2), so we'll multiply it by:
                r = Math.Sqrt((1 - rHalfChord2) / rHalfChord2);
                if (fIsLargeArc != (eSweepDirection == SweepDirection.Clockwise))
                // Going to the center from the origin=chord-midpoint 
                {
                    // in the direction of (-y, x) 
                    xCenter = -r * y; 
                    yCenter = r * x;
                } 
                else
                {
                    // in the direction of (y, -x)
                    xCenter = r * y; 
                    yCenter = -r * x;
                } 
            } 

            // Transformation 4: shift the origin to the center of the circle, which then becomes 
            // the unit circle. Since the chord's midpoint is the origin, the start point is (-x, -y)
            // and the endpoint is (x, y).
            Point ptStart = new Point(-x - xCenter, -y - yCenter);
            Point ptEnd = new Point(x - xCenter, y - yCenter); 

            // Set up the matrix that will take us back to our coordinate system.  This matrix is 
            // the inverse of the combination of transformation 1 thru 4. 
            matToEllipse = new Matrix(rCos * xRadius, -rSin * xRadius,
                                      rSin * yRadius, rCos * yRadius, 
                                      (xEnd + xStart) / 2, (yEnd + yStart) / 2);

            if (!fZeroCenter)
            { 
                // Prepend the translation that will take the origin to the circle's center
                matToEllipse.OffsetX += (matToEllipse.M11 * xCenter + matToEllipse.M21 * yCenter); 
                matToEllipse.OffsetY += (matToEllipse.M12 * xCenter + matToEllipse.M22 * yCenter); 
            }
 
            // Get the sine & cosine of the angle that will generate the arc pieces
            GetArcAngle(ptStart, ptEnd, fIsLargeArc, eSweepDirection, out rCosArcAngle, out rSinArcAngle, out cPieces);

            // Get the vector to the first Bezier control point 
            rBezDist = GetBezierDistance(rCosArcAngle, 1);
 
            if (eSweepDirection == SweepDirection.Counterclockwise) 
            {
                rBezDist = -rBezDist; 
            }

            vecToBez1 = new Point(-rBezDist * ptStart.Y, rBezDist * ptStart.X);
 
            PointCollection rslt = new PointCollection();
 
            // Add the arc pieces, except for the last 
            for (int i = 1; i < cPieces; i++)
            { 
                // Get the arc piece's endpoint
                Point ptPieceEnd = new Point(ptStart.X * rCosArcAngle - ptStart.Y * rSinArcAngle,
                                    ptStart.X * rSinArcAngle + ptStart.Y * rCosArcAngle);
                vecToBez2 = new Point(-rBezDist * ptPieceEnd.Y, rBezDist * ptPieceEnd.X); 

                rslt.Add(matToEllipse.Transform(Add(ptStart, vecToBez1))); 
                rslt.Add(matToEllipse.Transform(Sub(ptPieceEnd, vecToBez2))); 
                rslt.Add(matToEllipse.Transform(ptPieceEnd));
 
                // Move on to the next arc
                ptStart = ptPieceEnd;
                vecToBez1 = vecToBez2;
            } 

            // Last arc - we know the endpoint 
            vecToBez2 = new Point(-rBezDist * ptEnd.Y, rBezDist * ptEnd.X); 

            rslt.Add(matToEllipse.Transform(Add(ptStart, vecToBez1))); 
            rslt.Add(matToEllipse.Transform(Sub(ptEnd, vecToBez2)));
            rslt.Add(new Point(xEnd, yEnd));

            return rslt; 
        }
    }; 
 
    /// 
    /// IMetroDrawingContext implementation to convert VisualBrush to DrawingBrush to 
    /// reduce the number of Brush types we need to handle.
    /// 
    internal class DrawingFlattenDrawingContext : IMetroDrawingContext
    { 
        #region Public Properties
 
        private DrawingContext _context = null; 

        // Records number of DrawingContext.Push calls part of each DrawingFlattenDrawingContext.Push call 
        // for use in stack popping.
        private Stack _push = new Stack();

        #endregion 

        #region Constructors 
 
        public DrawingFlattenDrawingContext(DrawingContext context)
        { 
            Debug.Assert(context != null);

            _context = context;
        } 

        #endregion 
 
        #region IMetroDrawingContext Members
 
        public void DrawGeometry(Brush brush, Pen pen, Geometry geometry)
        {
            _context.DrawGeometry(brush, pen, geometry);
        } 

        public void DrawImage(ImageSource image, Rect rectangle) 
        { 
            _context.DrawImage(image, rectangle);
        } 

        public void DrawGlyphRun(Brush foreground, GlyphRun glyphRun)
        {
            _context.DrawGlyphRun(foreground, glyphRun); 
        }
 
        public void Push( 
            Matrix transform,
            Geometry clip, 
            double opacity,
            Brush opacityMask,
            Rect maskBounds,
            bool onePrimitive, 

            // serialization attributes 
            String nameAttr, 
            Visual node,
            Uri navigateUri, 
            EdgeMode edgeMode
            )
        {
            opacity = Utility.NormalizeOpacity(opacity); 

            int pushCount = 0; 
 
            if (!transform.IsIdentity)
            { 
                _context.PushTransform(new MatrixTransform(transform));
                pushCount++;
            }
 
            if (clip != null)
            { 
                _context.PushClip(clip); 
                pushCount++;
            } 

            if (!Utility.IsOpaque(opacity))
            {
                _context.PushOpacity(opacity); 
                pushCount++;
            } 
 
            if (opacityMask != null)
            { 
                _context.PushOpacityMask(opacityMask);
                pushCount++;
            }
 
            _push.Push(pushCount);
        } 
 
        public void Pop()
        { 
            int popCount = (int)_push.Pop();

            for (int index = 0; index < popCount; index++)
            { 
                _context.Pop();
            } 
        } 

        public void Comment(string message) 
        {
        }

        #endregion 
    }
} 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK