Primitive.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 / AlphaFlattener / Primitive.cs / 1 / Primitive.cs

                            //------------------------------------------------------------------------------ 
//  Microsoft Printing
//  Copyright (c) Microsoft Corporation, 2004
//
//  File:       Primitive.cs 
//
//  History: 
//      [....]: 04/20/2004  Created 
//-----------------------------------------------------------------------------
 
using System;
using System.Collections;              // for ArrayList
using System.Diagnostics;
 
using System.Windows;                  // for Rect                        WindowsBase.dll
using System.Windows.Media;            // for Geometry, Brush, BitmapSource. PresentationCore.dll 
using System.Windows.Media.Effects; 
using System.Windows.Media.Imaging;
using System.Windows.Controls;         // for Image 
using System.Windows.Shapes;           // for Glyphs

using System.Globalization;
using System.Text; 
using System.Collections.Generic;
 
namespace Microsoft.Internal.AlphaFlattener 
{
    ///  
    /// Base Primitive class
    /// 
    internal abstract class Primitive
    { 
        #region Private Fields
 
        private Geometry   _clip;      // changes coordinate spaces throughout printing 

        ///  
        /// Primitive opacity, possibly pushed from parent primitives.
        /// For example, parent Canvas opacity is pushed to children primitives when possible.
        /// 
        private double     _opacity; 

        private BrushProxy _opacityMask; 
        private Matrix     _transform; 

        // 
        // Fix bug 1308518: OpacityMask with DrawingBrush results in gaps
        //
        // We convert each tile of DrawingBrush into primitive. If the tile edges don't fall
        // on pixel boundary, they are anti-aliased by Avalon, which results in "gaps". We 
        // fix by setting pixel-snapping guidelines on geometry bounds when requested.
        // 
        private bool       _pixelSnapBounds; 

        #endregion 

        #region Constructors

        public Primitive() 
        {
            Opacity     = 1.0; 
            Transform   = Matrix.Identity; 
        }
 
        #endregion

        #region Public Abstract Methods
 
        /// 
        /// Render to a DrawingContext 
        ///  
        /// 
        public abstract void OnRender(DrawingContext ctx); 

        /// 
        /// Get the exact outline of drawing with Transform applied
        ///  
        /// 
        public abstract Geometry GetShapeGeometry(); 
 
        /// 
        /// Exclude area covered by a Geometry or clip to it 
        /// 
        /// 
        public abstract void Exclude(Geometry g);
 
        public abstract BrushProxy BlendBrush(BrushProxy brush);
 
        public abstract void BlendOverImage(ImageProxy image, Matrix trans); 

        ///  
        /// Blend a Drawing which is used as OpacityMask with a solid color
        /// 
        /// 
        ///  
        public abstract Primitive BlendOpacityMaskWithColor(BrushProxy color);
 
        #endregion 

        #region Public Abstract Properties 

        public abstract bool IsOpaque
        {
            get; 
        }
 
        public abstract bool IsTransparent 
        {
            get; 
        }

        #endregion
 
        #region Public Virtual Methods
 
        public virtual void ApplyTransform() 
        {
        } 

        /// True if not empty
        public virtual bool Optimize()
        { 
            return true;
        } 
 
        /// 
        /// Gets primitive-wide opacity, which is applied to entire primitive when rendering. 
        /// 
        public virtual double GetOpacity()
        {
            return Opacity; 
        }
 
        public virtual void PushOpacity(double opacity, BrushProxy opacityMask) 
        {
        } 

        #endregion

        #region Public Methods 

        ///  
        /// Returns a shallow copy of the primitive. 
        /// 
        ///  
        public Primitive Clone()
        {
            return MemberwiseClone() as Primitive;
        } 

        ///  
        /// Returns a deep copy of the primitive. 
        /// 
        ///  
        /// Also clones the primitive tree.
        /// 
        /// 
        public Primitive DeepClone() 
        {
            Primitive clone = Clone(); 
 
            clone.CloneMembers();
 
            return clone;
        }

        ///  
        /// Get exact outline of drawing after clipping
        ///  
        ///  
        public Geometry GetClippedShapeGeometry()
        { 
            Geometry shape = GetShapeGeometry();

            if (shape != null && Clip != null)
            { 
                bool empty;
 
                shape = Utility.Intersect(shape, Clip, Matrix.Identity, out empty); 

                if (empty) 
                {
                    shape = null;
                }
            } 

            return shape; 
        } 

        ///  
        /// Gets bounding box in world space.
        /// 
        /// 
        public Rect GetRectBounds(bool needed) 
        {
            Rect rect = Rect.Empty; 
 
            if (needed)
            { 
                rect = GetBoundsCore();

                if (!Utility.IsValid(rect))
                { 
                    // transformations may've made rectangle invalid
                    rect = Rect.Empty; 
                } 
            }
 
            return rect;
        }

        ///  
        /// Estimates the cost of flattening and GDIExporter rasterization.
        ///  
        /// Transformation from primitive space to world. 
        /// Returns 0 if primitive incurs no cost whatsoever.
        public double GetDrawingCost(Matrix worldTransform) 
        {
            if (Utility.IsTransparent(Opacity))
            {
                return 0; 
            }
            else 
            { 
                // Calculate cost multiplier due to opacity: Translucency can cause brushes
                // to be rasterized and blended together. 
                double factor = 1.0;

                if (!Utility.IsOpaque(Opacity))
                { 
                    factor = Utility.TransparencyCostFactor;
                } 
 
                return factor * GetBaseDrawingCost(worldTransform);
            } 
        }

        #endregion
 
        #region Public Properties
 
        ///  
        /// Primitive clip geometry.
        ///  
        /// 
        /// Prior to tree flattening, Clip is in primitive space. The flattening process transforms
        /// Clip to world space.
        ///  
        public Geometry Clip
        { 
            get 
            {
                return _clip; 
            }
            set
            {
                _clip = value; 
            }
        } 
 
        public double Opacity
        { 
            get
            {
                return _opacity;
            } 
            set
            { 
                // Validate during tree walking so that calculating children opacity through multiplication 
                // is done correctly.
                Debug.Assert(Utility.NormalizeOpacity(value) == value, "Opacity should be normalized during tree walking"); 

                _opacity = value;
            }
        } 

        public BrushProxy OpacityMask 
        { 
            get
            { 
                return _opacityMask;
            }
            set
            { 
                _opacityMask = value;
            } 
        } 

        public Matrix Transform 
        {
            get
            {
                return _transform; 
            }
            set 
            { 
                _transform = value;
            } 
        }

        public bool PixelSnapBounds
        { 
            get
            { 
                return _pixelSnapBounds; 
            }
            set 
            {
                _pixelSnapBounds = value;
            }
        } 

        #endregion 
 
        #region Internal Static Methods
 
        /// 
        /// Converts a Drawing object to a Primitive.
        /// 
        ///  
        /// Drawing-to-world transformation;
        /// used to estimate world bounds of Drawing objects for use as rasterization bitmap dimensions. 
        ///  
        internal static Primitive DrawingToPrimitive(System.Windows.Media.Drawing d, Matrix drawingToWorldTransformHint)
        { 
            if (d == null || !Utility.IsRenderVisible(d.Bounds))
            {
                return null;
            } 

            { 
                GeometryDrawing gd = d as GeometryDrawing; 

                if (gd != null) 
                {
                    GeometryPrimitive gp = null;

                    if (gd.Geometry != null) 
                    {
                        gp = new GeometryPrimitive(); 
 
                        Rect bounds = gd.Geometry.Bounds;
 
                        if (gd.Brush != null)
                        {
                            // gd.Brush comes directly from user-provided objects, so we need to perform
                            // brush reduction 
                            gp.Brush = BrushProxy.CreateUserBrush(gd.Brush, bounds, drawingToWorldTransformHint);
                        } 
 
                        if ((gd.Pen != null) && (gd.Pen.Brush != null))
                        { 
                            // pen needs to be made absolute relative to widened geometry bounds
                            Rect renderBounds = gd.Geometry.GetRenderBounds(gd.Pen);

                            gp.Pen = PenProxy.CreateUserPen(gd.Pen, renderBounds, drawingToWorldTransformHint); 
                        }
 
                        if ((gp.Brush == null) && (gp.Pen == null)) 
                        {
                            return null; 
                        }

                        gp.Geometry = gd.Geometry;
 
                        if ((gp.Brush != null) && (gp.Pen != null)) // split
                        { 
                            CanvasPrimitive cp = new CanvasPrimitive(); 

                            PenProxy pen = gp.Pen; 

                            gp.Pen = null;

                            cp.Children.Add(gp); 

                            gp = new GeometryPrimitive(); 
 
                            gp.Pen = pen;
                            gp.Geometry = gd.Geometry; 

                            cp.Children.Add(gp);

                            return cp; 
                        }
                    } 
 
                    return gp;
                } 
            }

            {
                GlyphRunDrawing gd = d as GlyphRunDrawing; 

                if (gd != null) 
                { 
                    GlyphPrimitive gp = null;
 
                    if ((gd.GlyphRun != null) && (gd.ForegroundBrush != null))
                    {
                        gp = new GlyphPrimitive();
 
                        gp.GlyphRun = gd.GlyphRun;
                        gp.Brush = BrushProxy.CreateUserBrush( 
                            gd.ForegroundBrush, 
                            gd.GlyphRun.BuildGeometry().Bounds,
                            drawingToWorldTransformHint 
                            );
                    }

                    return gp; 
                }
            } 
            { 
                ImageDrawing id = d as ImageDrawing;
 
                if (id != null)
                {
                    if (id.ImageSource != null)
                    { 
                        DrawingImage di = id.ImageSource as DrawingImage;
 
                        // Convert DrawimgImage to DrawingBrush, 
                        // ImageDrawing with DrawingImage to geometry filled with DrawingBrush
                        if (di != null) 
                        {
                            DrawingBrush db = Utility.CreateNonInheritingDrawingBrush(di.Drawing);

                            BrushProxy bp = BrushProxy.CreateBrush(db, id.Rect); 

                            if (bp == null) 
                            { 
                                return null;
                            } 
                            else
                            {
                                GeometryPrimitive gp = new GeometryPrimitive();
 
                                gp.Brush = BrushProxy.CreateBrush(db, id.Rect);
                                gp.Geometry = new RectangleGeometry(id.Rect); 
 
                                return gp;
                            } 
                        }

                        ImagePrimitive ip = new ImagePrimitive();
                        BitmapSource bs = (BitmapSource)id.ImageSource; 

                        ip.Image = new ImageProxy(bs); 
                        ip.DstRect = id.Rect; 

                        return ip; 
                    }

                    return null;
                } 
            }
 
            DrawingGroup dg = d as DrawingGroup; 

            if (dg != null) 
            {
                Primitive primitive = null;

                if (Utility.IsRenderVisible(dg)) 
                {
                    if (dg.Transform != null) 
                    { 
                        drawingToWorldTransformHint.Prepend(dg.Transform.Value);
                    } 

                    BitmapEffect effect = dg.BitmapEffect;

                    if (effect == null) 
                    {
                        // convert drawinggroup subtree into primitive subtree 
                        CanvasPrimitive cp = new CanvasPrimitive(); 

                        DrawingCollection children = dg.Children; 

                        for (int i = 0; i < children.Count; i++)
                        {
                            Primitive p = DrawingToPrimitive(children[i], drawingToWorldTransformHint); 

                            if (p != null) 
                            { 
                                cp.Children.Add(p);
                            } 
                        }

                        if (cp.Children.Count > 0)
                        { 
                            primitive = cp;
                        } 
                    } 
                    else
                    { 
                        // DrawingGroup has bitmap effect. Rasterize entire subtree to bitmap; Avalon
                        // will handle the application of bitmap effect for us.
                        Matrix bitmapToDrawingTransform;
 
                        BitmapSource bitmap = Utility.RasterizeDrawing(
                            dg, 
                            dg.Bounds, 
                            drawingToWorldTransformHint,
                            out bitmapToDrawingTransform 
                            );

                        if (bitmap != null)
                        { 
                            // bitmap may be null if bounds too small/invalid
                            ImagePrimitive ip = new ImagePrimitive(); 
 
                            ip.Image = new ImageProxy(bitmap);
                            ip.DstRect = new Rect(0, 0, bitmap.Width, bitmap.Height); 
                            ip.Transform = bitmapToDrawingTransform;

                            primitive = ip;
                        } 
                    }
 
                    if (primitive != null) 
                    {
                        if (dg.Transform != null) 
                        {
                            primitive.Transform *= dg.Transform.Value;
                        }
 
                        primitive.Clip = dg.ClipGeometry;
                        primitive.Opacity = Utility.NormalizeOpacity(dg.Opacity); 
 
                        if (dg.OpacityMask != null)
                        { 
                            primitive.OpacityMask = BrushProxy.CreateOpacityMaskBrush(dg.OpacityMask, dg.Bounds);
                        }
                    }
                } 

                return primitive; 
            } 

            Console.WriteLine("Drawing of type '" + d.GetType() + "' not handled."); 

            return null;
        }
 
        #endregion
 
        #region Protected Methods 

        protected int PushAll(DrawingContext dc) 
        {
            int level = 0;

            // Clipping already in device coordinate space, so it needs to be pushed first 
            if (Clip != null)
            { 
                dc.PushClip(Clip); 
                level++;
            } 

            // Fix bug 1308518: Perform bounds pixel-snapping if requested to fix bug.
            if (PixelSnapBounds)
            { 
                Rect bounds = GetRectBounds(true);
 
                double[] snapx = new double[] { bounds.Left, bounds.Right }; 
                double[] snapy = new double[] { bounds.Top, bounds.Bottom };
 
                dc.PushGuidelineSet(new GuidelineSet(snapx, snapy));
                level++;
            }
 
            if (!Transform.IsIdentity)
            { 
                dc.PushTransform(new MatrixTransform(Transform)); 
                level++;
            } 

            double opacity = GetOpacity();
            if (!Utility.IsOpaque(opacity))
            { 
                dc.PushOpacity(opacity);
                level++; 
            } 

            return level; 
        }

        protected static void PopAll(DrawingContext dc, int levels)
        { 
            for (int i = 0; i < levels; i++)
            { 
                dc.Pop(); 
            }
        } 

        /// 
        /// Extract opacity from OpacityMask to Opacity when possible
        ///  
        protected void ExtractOpacity()
        { 
            if ((OpacityMask != null) && (OpacityMask.Brush != null)) 
            {
                SolidColorBrush sb = OpacityMask.Brush as SolidColorBrush; 

                if (sb != null)
                {
                    Opacity *= Utility.NormalizeOpacity(sb.Color.ScA) * OpacityMask.Opacity; 

                    OpacityMask = null; 
                } 
            }
        } 

        #endregion

        #region Protected Virtual/Abstract Methods 

        ///  
        /// Clones instance members of this Primitive. 
        /// 
        ///  
        /// Derived implementations must cal base implementation.
        /// 
        protected virtual void CloneMembers()
        { 
            if (_opacityMask != null)
            { 
                _opacityMask = _opacityMask.Clone(); 
            }
        } 

        /// 
        /// Gets primitive bounds in world space.
        ///  
        protected abstract Rect GetBoundsCore();
 
        ///  
        /// Gets base cost of drawing this primitive, which should include flattening and GDI
        /// rasterization cost. 
        /// 
        /// Transformation from primitive space to world.
        /// 
        protected abstract double GetBaseDrawingCost(Matrix worldTransform); 

        #endregion 
    } // end of class Primitive 

 
    /// 
    /// Geometry Primitive
    /// 
    internal class GeometryPrimitive : Primitive 
    {
        #region Private Fields 
 
        private BrushProxy _brush;
        private PenProxy _pen; 
        private Geometry _geometry;
        private Geometry _widenGeometry;

        #endregion 

        #region Constants 
 
        private const double MyFlatteningTolerance = 0.08; // 1200 dpi
 
        #endregion

        #region Public Methods
 
        /// 
        /// Change stroking to filling widened path 
        ///  
        public void Widen()
        { 
            if (Pen != null)
            {
                Debug.Assert(Brush == null, "no brush expected");
 
                Brush = Pen.StrokeBrush;
 
                Geometry = WidenGeometry; 
                _widenGeometry = null;
                Pen = null; 
            }
        }

        ///  
        /// Gets information for tiling the content of a TileBrush.
        ///  
        /// Brush whose content we're manually tiling 
        /// Bounds of brush fill region
        /// Receives bounds of first tile 
        /// x-scaling factor for start tile; used to flip the tile
        /// y-scaling factor for start tile; used to flip the tile
        /// Factor to multiply scaling factor by whenever moving in x-direction
        /// Factor to multiply scaling factor by whenever moving in y-direction 
        /// Number of rows in tiling
        /// Number of columns in tiling 
        private static void GetTilingInformation( 
            TileBrush brush,
            Rect bounds, 
            out Rect startTile,
            out int startScaleX,
            out int startScaleY,
            out int scaleFlipX, 
            out int scaleFlipY,
            out int rowCount, 
            out int columnCount) 
        {
            // 
            // We calculate scaling and start tile bounds by moving in -x -y direction until
            // we obtain tile bounds that cover top-left corner of bounds.
            //
 
            // Calculate starting flipping scale multipliers.
            scaleFlipX = scaleFlipY = 1; 
 
            switch (brush.TileMode)
            { 
                case TileMode.None:
                    scaleFlipX = 0;
                    scaleFlipY = 0;
                    break; 

                case TileMode.FlipX: 
                    scaleFlipX = -1; 
                    break;
 
                case TileMode.FlipY:
                    scaleFlipY = -1;
                    break;
 
                case TileMode.FlipXY:
                    scaleFlipX = -1; 
                    scaleFlipY = -1; 
                    break;
 
                case TileMode.Tile:
                default:
                    scaleFlipX = 1;
                    scaleFlipY = 1; 
                    break;
            } 
 
            // Starting at current tile, move to top-left tile, adjusting scaling factors
            // and bounds along the way. 
            startScaleX = 1;
            startScaleY = 1;

            startTile = brush.Viewport; 
            Debug.Assert(brush.ViewportUnits == BrushMappingMode.Absolute);
 
            if (brush.TileMode == TileMode.None) 
            {
                // no tiling 
                rowCount = columnCount = 1;
            }
            else
            { 
                // move to left-most column
                while (startTile.Left > bounds.Left) 
                { 
                    startTile.Offset(-startTile.Width, 0);
                    startScaleX *= scaleFlipX; 
                }

                columnCount = (int)Math.Ceiling((bounds.Right - startTile.Left) / startTile.Width);
 
                // move to top-most row
                while (startTile.Top > bounds.Top) 
                { 
                    startTile.Offset(0, -startTile.Height);
                    startScaleY *= scaleFlipY; 
                }

                rowCount = (int)Math.Ceiling((bounds.Bottom - startTile.Top) / startTile.Height);
            } 
        }
 
        ///  
        /// Unfolds DrawingBrush tiles into one or more primitives.
        ///  
        /// Returns a primitive that replaces the current GeometryPrimitive, or null if not visible.
        public Primitive UnfoldDrawingBrush()
        {
            if (_pen != null && _pen.StrokeBrush != null && _pen.StrokeBrush.Brush is DrawingBrush) 
            {
                // Treat DrawingBrush stroke as fill so that we can unfold it. 
                Widen(); 
            }
 
            if (_brush == null)
            {
                return this;
            } 

            // Force rebuild of underlying DrawingBrush in case BrushProxy has attributes that necesitate 
            // a DrawingBrush (i.e. before/after fill, drawing Primitive has changed). 
            DrawingBrush drawingBrush = _brush.GetRealBrush() as DrawingBrush;
 
            // Bug: 1691872 We can't handle transformation well in unfolding yet

            if ((drawingBrush == null) || ! Utility.IsIdentity(drawingBrush.Transform))
            { 
                // No DrawingBrush to unfold; keep current primitive.
                return this; 
            } 

            // 
            // Unfolding a DrawingBrush occurs in brush space and involves the following steps:
            //
            // 1) Get this primitive's clipped geometry in brush space and use as tiling bounds.
            // 2) Generate tiling information from brush and tiling bounds. 
            // 2) Create canvas primitive that mirror's this primitive's properties. It'll be the
            //    parent of children tile primitives. 
            // 3) For each tile, clone brush's primitive tree, transform tile primitive to tile bounds, 
            //    and add as child of canvas.
            // 
            Matrix brushTransform = drawingBrush.Transform == null ? Matrix.Identity : drawingBrush.Transform.Value;
            Matrix brushToWorldTransform = brushTransform * Transform;

            Primitive brushPrimitive = _brush.GetDrawingPrimitive(); 

            if (brushPrimitive == null) // nothing to draw 
            { 
                return null;
            } 

            //
            // Get primitive geometry in brush space.
            // 
            Geometry worldGeometry = GetClippedShapeGeometry();
            Geometry brushGeometry; 
 
            if (worldGeometry == null)
            { 
                // nothing visible
                return null;
            }
            else 
            {
                Matrix worldToBrushTransform = brushToWorldTransform; 
                worldToBrushTransform.Invert(); 

                brushGeometry = Utility.TransformGeometry(worldGeometry, worldToBrushTransform); 
            }

            Rect brushGeometryBounds = brushGeometry.Bounds;
 
            //
            // Get information for tiling brush content within brushGeometryBounds. 
            // 
            Rect startTileBounds;
            int startScaleX, startScaleY, scaleFlipX, scaleFlipY, rowCount, columnCount; 

            GetTilingInformation(
                drawingBrush,
                brushGeometryBounds, 
                out startTileBounds,
                out startScaleX, 
                out startScaleY, 
                out scaleFlipX,
                out scaleFlipY, 
                out rowCount,
                out columnCount
                );
 
            //
            // Compare cost of rasterizing entire primitive to the cost of GDI rasterizing the 
            // unfolded primitives. If GDI is more expensive, then abort and rasterize everything. 
            //
            // Note that transparency and overlaps may cause unfolding to be even more expensive, 
            // but this is a good enough metric.
            //
            double primitiveRasterizeCost = Configuration.RasterizationCost(
                worldGeometry.Bounds.Width, 
                worldGeometry.Bounds.Height
                ); 
 
            Matrix viewboxToViewportTransform = Utility.CreateViewboxToViewportTransform(
                drawingBrush, 
                drawingBrush.Viewbox,   // absolute coordinates
                startTileBounds         // absolute coordinates
                );
 
            double gdiRasterizeCost = rowCount * columnCount * brushPrimitive.GetDrawingCost(viewboxToViewportTransform);
 
            if (OpacityMask != null) 
            {
                gdiRasterizeCost *= Utility.TransparencyCostFactor; 
            }

            if (primitiveRasterizeCost < gdiRasterizeCost)
            { 
                return this;
            } 
 
            //
            // Create canvas primitive that'll serve as parent to tile primitives. 
            //
            CanvasPrimitive canvas = new CanvasPrimitive();

            canvas.Opacity = Opacity * _brush.Opacity; 
            canvas.OpacityMask = BrushProxy.BlendBrush(OpacityMask, _brush.OpacityMask);
 
            canvas.Clip = worldGeometry; 

            // 
            // Compute per-tile clipping if drawing content exceeds viewbox bounds.
            //
            bool tileClip = false;
 
            if (!drawingBrush.Viewbox.Contains(brushPrimitive.GetRectBounds(true)))
            { 
                tileClip = true; 
            }
 
            //
            // Generate children tile primitives.
            //
            int tileScaleX = startScaleX; 
            int tileScaleY = startScaleY;
 
            for (int y = 0; y < rowCount; y++) 
            {
                tileScaleX = startScaleX; 

                for (int x = 0; x < columnCount; x++)
                {
                    // Compute tile bounds. 
                    Rect tileBounds = startTileBounds;
                    tileBounds.Offset(x * startTileBounds.Width, y * startTileBounds.Height); 
 
                    // Transform tile primitive to brush space, taking into account stretching, flipping.
                    viewboxToViewportTransform = Utility.CreateViewboxToViewportTransform( 
                        drawingBrush,
                        drawingBrush.Viewbox,   // absolute
                        tileBounds              // absolute
                        ); 

                    Matrix tileTransform = brushPrimitive.Transform; 
                    tileTransform.Append(viewboxToViewportTransform); 

                    if (tileScaleX == -1 || tileScaleY == -1) 
                    {
                        // apply tile flipping
                        double centerX = tileBounds.X + tileBounds.Width / 2.0;
                        double centerY = tileBounds.Y + tileBounds.Height / 2.0; 

                        tileTransform.ScaleAt(tileScaleX, tileScaleY, centerX, centerY); 
                    } 

                    // Transform tile primitive to world space. 
                    tileTransform.Append(brushToWorldTransform);

                    // Add as child of unfolded canvas.
                    Primitive tilePrimitive = brushPrimitive.DeepClone(); 
                    tilePrimitive.Transform = tileTransform;
                    tilePrimitive.ApplyTransform(); 
 
                    if (tileClip)
                    { 
                        //
                        // Need to clip tile primitive to viewbox. Since this is pre-tree-flattening,
                        // Primitive.Clip is in primitive space. We transform world-space tileBounds to
                        // primitive space via Primitive.Transform (which may differ from tileTransform 
                        // at this point due to ApplyTransform call).
                        // 
                        if (tilePrimitive.Transform.IsIdentity) 
                        {
                            tilePrimitive.Clip = new RectangleGeometry(tileBounds); 
                        }
                        else
                        {
                            Matrix inverseTileTransform = tilePrimitive.Transform; 
                            inverseTileTransform.Invert();
 
                            tilePrimitive.Clip = new RectangleGeometry( 
                                tileBounds,
                                0, 
                                0,
                                new MatrixTransform(inverseTileTransform)
                                );
                        } 
                    }
 
                    canvas.Children.Add(tilePrimitive); 

                    // next column 
                    tileScaleX *= scaleFlipX;
                }

                // next row 
                tileScaleY *= scaleFlipY;
            } 
 
            return canvas;
        } 

        #endregion

        #region Public Properties 

        ///  
        /// Geometry fill brush. 
        /// 
        ///  
        /// Must be transformed by Transform to get world-space brush.
        /// 
        public BrushProxy Brush
        { 
            get
            { 
                return _brush; 
            }
            set 
            {
                _brush = value;
            }
        } 

        public PenProxy Pen 
        { 
            get
            { 
                return _pen;
            }
            set
            { 
                _pen = value;
            } 
        } 

        public Geometry Geometry 
        {
            get
            {
                return _geometry; 
            }
            set 
            { 
                _geometry = value;
            } 
        }

        public virtual Geometry WidenGeometry
        { 
            get
            { 
                if (Pen != null) 
                {
                    if (_widenGeometry == null) 
                    {
                        Pen p = Pen.GetPen(true);

                        double scale = Utility.GetScale(Transform); 

                        _widenGeometry = _geometry.GetWidenedPathGeometry(p, MyFlatteningTolerance / scale, ToleranceType.Absolute); 
                        _widenGeometry.Transform = System.Windows.Media.Transform.Identity; 
                    }
 
                    return _widenGeometry;
                }

                return Geometry; 
            }
        } 
 
        #endregion
 
        #region Protected Properties

        protected Geometry WidenGeometryCore
        { 
            get
            { 
                return _widenGeometry; 
            }
            set 
            {
                _widenGeometry = value;
            }
        } 

        #endregion 
 
        #region Private Methods
 
        private void AbsorbOpacity()
        {
            if (!Utility.IsOpaque(Opacity) || (OpacityMask != null))
            { 
                if (Brush != null)
                { 
                    Brush = Brush.Clone(); 

                    Brush = Brush.PushOpacity(Opacity, OpacityMask); 
                }

                if (Pen != null)
                { 
                    Pen.PushOpacity(Opacity, OpacityMask);
                } 
 
                Opacity = 1;
                OpacityMask = null; 
            }
        }

        #endregion 

        #region Primitive Members 
 
        public override void OnRender(DrawingContext dc)
        { 
            if ((Geometry != null) && (Pen != null) || (Brush != null))
            {
                //
                // Fix bug 1308518: Gaps in DrawingBrush opacity mask 
                //
                // The gaps are due to anti-aliasing, and we fix by snapping clipped primitive bounds 
                // to pixels. The decision to clip the geometry is simply because DrawingBrush unfolding 
                // uses clipping to determine which tile is rendered. The geometry drawn for each tile
                // is identical. Therefore, the geometry needs to be clipped prior to calculating pixel- 
                // snapping.
                //
                bool empty = false;
                Geometry saveGeometry = Geometry; 
                Geometry saveClip = Clip;
 
                if (PixelSnapBounds) 
                {
                    // intersect clip with geometry 
                    Geometry = Utility.Intersect(Geometry, Clip, Matrix.Identity, out empty);
                    Clip = null;
                }
 
                if (!empty)
                { 
                    int level = PushAll(dc); 

                    Pen p = null; 

                    if (Pen != null)
                    {
                        p = Pen.GetPen(false); 
                    }
 
                    if (Brush == null) 
                    {
                        dc.DrawGeometry(null, p, Geometry); 
                    }
                    else
                    {
                        Brush.DrawGeometry(dc, p, Geometry); // BrushProxy.GetRealBrush can't handle OpacityMask 
                    }
 
                    PopAll(dc, level); 
                }
 
                // restore
                Geometry = saveGeometry;
                Clip = saveClip;
            } 
        }
 
        public override Geometry GetShapeGeometry() 
        {
            Geometry g = Utility.TransformGeometry(WidenGeometry, Transform); 

            return g;
        }
 
        public override void Exclude(Geometry g)
        { 
            if (g != null) 
            {
                Widen(); 

                ApplyTransform();

                Geometry = Utility.Exclude(Geometry, Utility.InverseTransformGeometry(g, Transform), Transform); 
            }
        } 
 
        public override BrushProxy BlendBrush(BrushProxy brushA)
        { 
            BrushProxy b = Brush;

            if (b == null)
            { 
                b = Pen.StrokeBrush;
            } 
 
            // Transform is not pushed in GlyphPrimitive.
            // Need to apply transform to Brush when returning to outside 
            if (b != null)
            {
                b = b.ApplyTransformCopy(Transform);
            } 

            return brushA.BlendBrush(b); 
        } 

        public override void BlendOverImage(ImageProxy image, Matrix trans) 
        {
            BrushProxy b = Brush;

            if (b == null) 
            {
                b = Pen.StrokeBrush; 
            } 

            // Transform is not pushed in GlyphPrimitive. 
            // Need to apply transform to Brush when returning to outside
            if (b != null)
            {
                b = b.ApplyTransformCopy(Transform); 
            }
 
            image.BlendUnderBrush(false, b, trans); 
        }
 
        public override Primitive BlendOpacityMaskWithColor(BrushProxy color)
        {
            GeometryPrimitive g = Clone() as GeometryPrimitive;
 
            //
            // Fix bug 1308518: OpacityMask from DrawingBrush ignored 
            // 
            // The original code blended the DrawingBrush on the bottom, and color on top.
            // We need to reverse the order, since the DrawingBrush is the mask applied to 
            // the color.
            //
            if (g.Brush != null)
            { 
                g.Brush = g.Brush.Clone();
                g.Brush.OpacityOnly = true; 
 
                g.Brush = color.BlendBrush(g.Brush);
            } 

            if (g.Pen != null)
            {
                g.Pen = g.Pen.Clone(); 
                g.Pen.StrokeBrush.OpacityOnly = true;
 
                g.Pen.StrokeBrush = color.BlendBrush(g.Pen.StrokeBrush); 
            }
 
            return g;
        }

        public override bool IsOpaque 
        {
            get 
            { 
                if ((Brush == null) && (Pen == null))
                { 
                    return false;
                }

                if (!Utility.IsOpaque(Opacity)) 
                {
                    return false; 
                } 

                if (Brush == null) 
                {
                    return Pen.IsOpaque();
                }
 
                if (Pen == null)
                { 
                    return Brush.IsOpaque(); 
                }
 
                return Pen.IsOpaque() && Brush.IsOpaque();
            }
        }
 
        public override bool IsTransparent
        { 
            get 
            {
                if (Utility.IsTransparent(Opacity)) 
                {
                    return true;
                }
 
                if ((Brush == null) && (Pen == null))
                { 
                    return true; 
                }
 
                if (Brush == null)
                {
                    return Pen.IsTransparent();
                } 

                if (Pen == null) 
                { 
                    return Brush.IsTransparent();
                } 

                return Pen.IsTransparent() && Brush.IsTransparent();
            }
        } 

        ///  
        /// Push transform to geometry 
        /// 
        public override void ApplyTransform() 
        {
            Debug.Assert((Brush != null) || (Pen != null), "empty primitive");

            if (!Transform.IsIdentity) 
            {
                if (Pen != null) 
                { 
                    double scale;
 
                    // If transformation is unfirm scaling, just change pen thickness
                    if (Utility.HasUniformScale(Transform, out scale))
                    {
                        Pen.Scale(scale); 
                        Pen.StrokeBrush.ApplyTransform(Transform);
                    } 
                    else 
                    {
                        Widen(); // We need to widen it because Pen does not have transformation 
                    }
                }

                // Clip     = Utility.TransformGeometry(Clip, Transform); 
                Geometry = Utility.TransformGeometry(Geometry, Transform);
 
                if (Brush != null) 
                {
                    Brush.ApplyTransform(Transform); 
                }

                Transform = Matrix.Identity; // Reset transform
                _widenGeometry = null;  // Reset cached widen geometry if any 
            }
        } 
 
        /// 
        /// Remove gaps whened filled by a TileBrush with no tiling and None/Uniform stretch 
        /// 
        public override bool Optimize()
        {
            AbsorbOpacity(); 

            // Widen stroken with TileBrush, so that gap can be removed 
            if ((Pen != null) && (Pen.StrokeBrush.Brush is TileBrush)) 
            {
                Widen(); 
            }
            // Fix for  1688277. The code below will operate on Geometry, which starts as empty for
            // GlyphPrimitive until GlyphPrimitive.WidenGeometry is called. So skip for GlyphPrimitive
            else if (this is GlyphPrimitive) 
            {
                return true; 
            } 

            // Remove gaps whened filled by a TileBrush with no tiling and None/Uniform stretch 
            if (Brush != null)
            {
                TileBrush tb = Brush.Brush as TileBrush;
 
                if (tb != null)
                { 
                    // get geometry bounds in world space. can't touch Geometry directly since 
                    // we might be a GlyphPrimitive, which has Geometry == null
                    Rect geometryBounds = GetRectBounds(true); 

                    if (!Brush.IsTiled(geometryBounds))
                    {
                        Brush.CloneRealBrush(); 

                        tb = Brush.Brush as TileBrush; 
 
                        // content top-left not necessarily at (0, 0) for DrawingBrush
                        Rect content = Utility.GetTileContentBounds(tb); 

                        Rect viewbox = tb.Viewbox;      // absolute viewbox
                        Debug.Assert(tb.ViewboxUnits == BrushMappingMode.Absolute, "Absolute Viewbox expected");
 
                        //
                        // Calculate transform from absolute viewbox to geometry space. 
                        // We then use transformed content bounds as viewport to remove gaps. 
                        // Content bounds are used over viewbox since viewbox determines placement
                        // of content, but does not clip the content. 
                        //
                        Matrix viewboxTransform = Utility.CreateViewboxToViewportTransform(tb);

                        // clip to geometry 
                        bool empty;
                        Clip = Utility.Intersect(Clip, Geometry, Matrix.Identity, out empty); 
 
                        // set viewport to be transformed viewbox
                        if (!empty) 
                        {
                            Rect geometryViewbox = content;
                            geometryViewbox.Transform(viewboxTransform);
 
                            if (!tb.Viewport.Contains(geometryViewbox))
                            { 
                                // New viewport larger than original viewport, clip to original viewport. 
                                // This can occur if content is larger than viewport and stretch is none.
                                // Fix bug 1395406: Clip is in world space, also need to apply Primitive.Transform. 
                                RectangleGeometry viewportGeometry = new RectangleGeometry(tb.Viewport);
                                viewportGeometry.Transform = Utility.MultiplyTransform(tb.Transform, new MatrixTransform(Transform));

                                Clip = Utility.Intersect(Clip, viewportGeometry, Matrix.Identity, out empty); 
                            }
 
                            // Include entire content in viewbox. The viewbox is essentially pushed to 
                            // the viewport.
                            tb.Viewbox = content; 
                            tb.ViewboxUnits = BrushMappingMode.Absolute;
                            tb.Viewport = geometryViewbox;
                        }
 
                        //
                        // Fix bug 1376514: MGC: Black fill introduced printing ImageBrush with rotation 
                        // 
                        // Clip to brush content if it doesn't cover viewport in case we're rasterized,
                        // otherwise unfilled areas of rasterization bitmap will show through. 
                        //
                        bool clipContent = false;

                        if (!empty && tb.Transform != null && !Utility.IsScaleTranslate(tb.Transform.Value)) 
                        {
                            // Rotation results in content not completely filling rasterization bitmap. 
                            clipContent = true; 
                        }
 
                        if (!empty && !Brush.IsViewportCoverBounds(geometryBounds))
                        {
                            // Viewport no longer covers geometry. During rasterization only the viewport
                            // will be filled, leaving the rest black. Thus, we need to clip to viewport. 
                            clipContent = true;
                        } 
 
                        if (!empty && clipContent)
                        { 
                            // Intersect with entire content, then transform to world space.
                            // Fix bug 1395406: Clip is in world space, also need to apply Primitive.Transform.
                            content.Transform(viewboxTransform);
 
                            RectangleGeometry contentGeometry = new RectangleGeometry(content);
                            contentGeometry.Transform = Utility.MultiplyTransform(tb.Transform, new MatrixTransform(Transform)); 
 
                            Clip = Utility.Intersect(Clip, contentGeometry, Matrix.Identity, out empty);
                        } 

                        if (empty)
                        {
                            Geometry = null; 
                        }
                        else 
                        { 
                            Geometry = new RectangleGeometry(tb.Viewport);
                            Geometry.Transform = tb.Transform; 
                        }
                    }
                }
            } 

            // Optimize: Absorb clip into geometry to avoid converting two Geometry objects to GDI paths 
            // in GDIExporter. In the future we may want to absorb clip only if intersection is simple. 
            if (Clip != null && Geometry != null && Pen == null)
            { 
                // By this point clip should be in world space. Transform back to geometry space before
                // performing intersection.
                Geometry geometryClip = Clip;
 
                if (!Transform.IsIdentity)
                { 
                    Matrix inverseTransform = Transform; 
                    inverseTransform.Invert();
 
                    geometryClip = Utility.TransformGeometry(geometryClip, inverseTransform);
                }

                bool empty; 
                Geometry = Utility.Intersect(Geometry, geometryClip, Matrix.Identity, out empty);
 
                if (empty) 
                {
                    Geometry = null; 
                }

                Clip = null;
            } 

            return Geometry != null; 
        } 

        // Override since opacity may be pushed into BrushProxy. Need to strip opacity from BrushProxy. 
        public override double GetOpacity()
        {
            double opacity = Opacity;
 
            if (Brush != null)
            { 
                // Push only inherited opacity; strip BrushProxy.Brush.Opacity from BrushProxy.Opacity. 
                // Get real brush first in case it rebuilds the brush and modifies opacity.
                Brush realBrush = Brush.GetRealBrush(); 

                opacity *= Brush.Opacity;

                if (realBrush != null) 
                {
                    double realOpacity = Utility.NormalizeOpacity(realBrush.Opacity); 
 
                    if (realOpacity != 0)
                    { 
                        opacity /= realOpacity;
                    }
                }
            } 

            if (Pen != null) 
            { 
                // Like above, remove BrushProxy.Brush.Opacity since geometry rendering will
                // apply it. 
                Brush realBrush = Pen.StrokeBrush.GetRealBrush();

                opacity *= Pen.StrokeBrush.Opacity;
 
                if (realBrush != null)
                { 
                    double realOpacity = Utility.NormalizeOpacity(realBrush.Opacity); 

                    if (realOpacity != 0) 
                    {
                        opacity /= realOpacity;
                    }
                } 
            }
 
            return opacity; 
        }
 
        public override void PushOpacity(double opacity, BrushProxy opacityMask)
        {
            if (Utility.IsOpaque(opacity) && (opacityMask == null))
            { 
                return;
            } 
 
            OpacityMask = BrushProxy.BlendBrush(OpacityMask, opacityMask);
 
            ExtractOpacity();

            Opacity *= opacity;
 
            AbsorbOpacity();
        } 
 
        protected override void CloneMembers()
        { 
            base.CloneMembers();

            if (_brush != null)
            { 
                _brush = _brush.Clone();
            } 
 
            if (_pen != null)
            { 
                _pen = _pen.Clone();
            }
        }
 
        protected override Rect GetBoundsCore()
        { 
            Rect result; 

            if (Transform.IsIdentity) 
            {
                if (Pen != null)
                {
                    result = Geometry.GetRenderBounds(Pen.GetPen(true)); 
                }
                else 
                { 
                    result = Geometry.Bounds;
                } 
            }
            else
            {
                Geometry g = Utility.TransformGeometry(WidenGeometry, Transform); 

                if (g == null) 
                { 
                    result = Rect.Empty;
                } 
                else
                {
                    result = g.Bounds;
                } 
            }
 
            return result; 
        }
 
        protected override double GetBaseDrawingCost(Matrix worldTransform)
        {
            Rect bounds = GetRectBounds(true);
            bounds.Transform(worldTransform); 

            double cost = 0; 
 
            if (_brush != null)
            { 
                cost += _brush.GetDrawingCost(bounds.Size);
            }

            if (_pen != null && _pen.StrokeBrush != null) 
            {
                cost += _pen.StrokeBrush.GetDrawingCost(bounds.Size); 
            } 

            return cost; 
        }

        #endregion
    } // end of class GeometryPrimitive 

    internal class GlyphPrimitive : GeometryPrimitive 
    { 
        #region Private Fields
 
        private GlyphRun _glyphRun;
        private Geometry _bounds;   // glyph bounds in world space (transformation has been applied)

        #endregion 

        #region Public Properties 
 
        public GlyphRun GlyphRun
        { 
            get
            {
                return _glyphRun;
            } 
            set
            { 
                _glyphRun = value; 
            }
        } 

        #endregion

        #region GeometryPrimitive Members 

        public override Geometry WidenGeometry 
        { 
            get
            { 
                if (WidenGeometryCore == null)
                {
                    WidenGeometryCore = GlyphRun.BuildGeometry();
                } 

                return WidenGeometryCore; 
            } 
        }
 
        #endregion

        #region Primitive Members
 
        public override void OnRender(DrawingContext dc)
        { 
            if ((GlyphRun != null) && (Pen != null) || (Brush != null)) 
            {
                int level; 

                level = PushAll(dc);

                dc.DrawGlyphRun(Brush.GetRealBrush(), GlyphRun); 

                PopAll(dc, level); 
            } 
        }
 
        public override Geometry GetShapeGeometry()
        {
            Geometry g = Utility.TransformGeometry(WidenGeometry, Transform);
 
            return g; //  new RectangleGeometry(g.Bounds);
        } 
 
        public override void Exclude(Geometry g)
        { 
            if ((GlyphRun != null) && (g != null))
            {
                if (_bounds == null)
                { 
                    // bounds are in world space
                    _bounds = new RectangleGeometry(GetRectBounds(true)); 
                } 

                if (Utility.Covers(g, _bounds)) 
                {
                    GlyphRun = null;
                }
                else 
                {
 
                    if (Clip == null) 
                    {
                        Clip = _bounds; 
                    }

                    Clip = Utility.Exclude(Clip, g, Matrix.Identity);
 
                    if (Clip == null)
                    { 
                        GlyphRun = null; 
                    }
                } 
            }
        }

        public override void ApplyTransform() 
        {
        } 
 
        public override bool Optimize()
        { 
            base.Optimize();

            return GlyphRun != null;
        } 

        protected override Rect GetBoundsCore() 
        { 
            Rect bounds = Rect.Empty;
 
            if (GlyphRun != null)
            {
                bounds = GlyphRun.ComputeInkBoundingBox();
 
                if (!bounds.IsEmpty)
                { 
                    bounds = new Rect(bounds.X + GlyphRun.BaselineOrigin.X, 
                                      bounds.Y + GlyphRun.BaselineOrigin.Y,
                                      bounds.Width, 
                                      bounds.Height);

                    bounds = Utility.TransformRect(bounds, Transform);
                } 
            }
 
            return bounds; 
        }
 
        #endregion
    } // end of class GlyphPrimitive

    internal class ImagePrimitive : Primitive 
    {
        #region Private Fields 
 
        private ImageProxy _image;
 
        private Rect _destRect;

        #endregion
 
        #region Public Properties
 
        public ImageProxy Image 
        {
            get 
            {
                return _image;
            }
            set 
            {
                _image = value; 
            } 
        }
 
        public Rect DstRect
        {
            get
            { 
                return _destRect;
            } 
            set 
            {
                _destRect = value; 
            }
        }

        #endregion 

        #region Private Methods 
 
        private void AbsorbOpacity()
        { 
            Image.PushOpacity(Opacity, OpacityMask, DstRect, Transform);

            Opacity = 1;
            OpacityMask = null; 
        }
 
        #endregion 

        #region Primitive Members 

        public override void OnRender(DrawingContext dc)
        {
            if (Image != null) 
            {
                int level; 
 
                level = PushAll(dc);
 
                dc.DrawImage(Image.GetImage(), DstRect);

                PopAll(dc, level);
            } 
        }
 
        public override Geometry GetShapeGeometry() 
        {
            return Utility.TransformGeometry(new RectangleGeometry(DstRect), Transform); 
        }

        public override void Exclude(Geometry g)
        { 
            if (Image != null)
            { 
                if (Clip == null) 
                {
                    Clip = Utility.TransformGeometry(new RectangleGeometry(DstRect), Transform); 
                }

                Clip = Utility.Exclude(Clip, g, Matrix.Identity);
 
                if (Clip == null) // nothing is visible
                { 
                    Image = null; // nothing to draw 
                }
            } 
        }

        public override BrushProxy BlendBrush(BrushProxy brush)
        { 
            Debug.Assert(false, "Image over Brush?");
 
            return brush; 
        }
 
        public override void BlendOverImage(ImageProxy image, Matrix trans)
        {
            if (IsOpaque)
            { 
                return;
            } 
 
            ImageBrush brush = new ImageBrush();
 
            brush.CanBeInheritanceContext = false;              // Opt-out of inheritance
            brush.ImageSource             = Image.GetImage();
            brush.ViewportUnits           = BrushMappingMode.Absolute;
            brush.Viewport                = DstRect; 
            brush.Transform               = new MatrixTransform(Transform);
 
            BrushProxy b = BrushProxy.CreateBrush(brush, DstRect); 

            image.BlendUnderBrush(false, b, trans); 
        }

        public override Primitive BlendOpacityMaskWithColor(BrushProxy color)
        { 
            // blend color into image opacity mask
            ImagePrimitive primitive = (ImagePrimitive)DeepClone(); 
 
            Rect imageBounds = new Rect(0, 0, primitive.Image.PixelWidth, primitive.Image.PixelHeight);
 
            primitive.Image.PushOpacity(1.0, color, imageBounds, primitive.Transform);

            return primitive;
        } 

        public override bool IsOpaque 
        { 
            get
            { 
                if (Image == null)
                {
                    return false;
                } 

                return Image.IsOpaque(); 
            } 
        }
 
        public override bool IsTransparent
        {
            get
            { 
                if (Image == null)
                { 
                    return true; 
                }
 
                if (Utility.IsTransparent(Opacity))
                {
                    return true;
                } 

                return false; 
            } 
        }
 
        public override bool Optimize()
        {
            AbsorbOpacity();
 
            if ((Image != null) && (Clip != null))
            { 
                Geometry dest = Utility.TransformGeometry(new RectangleGeometry(DstRect), Transform); 

                if (Utility.Covers(Clip, dest)) // If dest is inside clipping region, ignore clipping 
                {
                    Clip = null;
                }
            } 

            return Image != null; 
        } 

        public override void PushOpacity(double opacity, BrushProxy opacityMask) 
        {
            OpacityMask = BrushProxy.BlendBrush(OpacityMask, opacityMask);

            ExtractOpacity(); 

            Opacity *= opacity; 
        } 

        protected override void CloneMembers() 
        {
            base.CloneMembers();

            if (_image != null) 
            {
                _image = _image.Clone(); 
            } 
        }
 
        protected override Rect GetBoundsCore()
        {
            return Utility.TransformRect(DstRect, Transform);
        } 

        protected override double GetBaseDrawingCost(Matrix worldTransform) 
        { 
            Rect bounds = GetRectBounds(true);
            bounds.Transform(worldTransform); 

            return Configuration.RasterizationCost(bounds.Width, bounds.Height);
        }
 
        #endregion
    } // end of class ImagePrimitive 
 
    /// 
    /// CanvasPrimitive 
    /// 
    internal class CanvasPrimitive : Primitive
    {
        #region Private Fields 

        private ArrayList _children; 
 
        #endregion
 
        #region Constructors

        public CanvasPrimitive() : base()
        { 
            _children = new ArrayList();
        } 
 
        #endregion
 
        #region Public Properties

        public ArrayList Children
        { 
            get
            { 
                return _children; 
            }
        } 

        #endregion

        #region Primitive Members 

        public override void OnRender(DrawingContext dc) 
        { 
            int level;
 
            level = PushAll(dc);

            foreach (Primitive p in Children)
            { 
                p.OnRender(dc);
            } 
 
            PopAll(dc, level);
        } 

        public override Geometry GetShapeGeometry()
        {
            Debug.Assert(false, "GetShapeGeometry on Canvas"); 
            return null;
        } 
 
        public override void Exclude(Geometry g)
        { 
            Debug.Assert(false, "Exclude on Canvas");
        }

        public override BrushProxy BlendBrush(BrushProxy brush) 
        {
            Debug.Assert(false, "BlendBrush on Canvas"); 
 
            return brush;
        } 

        public override void BlendOverImage(ImageProxy image, Matrix trans)
        {
            Debug.Assert(false, "BlendOverImage on Canvas"); 
        }
 
        public override Primitive BlendOpacityMaskWithColor(BrushProxy color) 
        {
            CanvasPrimitive c = this.Clone() as CanvasPrimitive; 

            c._children = new ArrayList();

            foreach (Primitive p in Children) 
            {
                c.Children.Add(p.BlendOpacityMaskWithColor(color)); 
            } 

            return c; 
        }

        public override bool IsOpaque
        { 
            get
            { 
                return false; 
            }
        } 

        public override bool IsTransparent
        {
            get 
            {
                if (Utility.IsTransparent(Opacity)) 
                { 
                    return true;
                } 

                return false;
            }
        } 

        protected override void CloneMembers() 
        { 
            base.CloneMembers();
 
            _children = (ArrayList)_children.Clone();

            for (int index = 0; index < _children.Count; index++)
            { 
                _children[index] = ((Primitive)_children[index]).DeepClone();
            } 
        } 

        protected override Rect GetBoundsCore() 
        {
            Rect bounds = new Rect();

            bool first = true; 

            foreach (Primitive p in Children) 
            { 
                Rect r = p.GetRectBounds(true);
 
                if (first)
                {
                    bounds = r;
                    first = false; 
                }
                else 
                { 
                    bounds.Union(r);
                } 
            }

            return Utility.TransformRect(bounds, Transform);
        } 

        protected override double GetBaseDrawingCost(Matrix worldTransform) 
        { 
            double cost = 1024;
 
            foreach (Primitive primitive in _children)
            {
                cost += primitive.GetDrawingCost(worldTransform);
            } 

            return cost; 
        } 

        #endregion 
    } // end of class CanvasPrimitive

    /// 
    /// Primitive related information during flattening 
    /// 
    internal class PrimitiveInfo       // >= 44 bytes 
    { 
#if DEBUG
        public string id;              //  4 bytes 
#endif

        public Primitive primitive;                 //  4 bytes
        public Rect      bounds;                    // 32 bytes 
        public List overlap;                   //  4 + N bytes, object on top of current object
        public List underlay;                  //  4 + N bytes, object under current object 
        public int       overlapHasTransparency;    //  4 bytes 
        public Cluster   m_cluster;
 
#if UNIT_TEST
        public PrimitiveInfo(Rect b)
        {
            bounds = b; 
        }
#endif 
 
        public PrimitiveInfo(Primitive p)
        { 
            primitive   = p;
            bounds      = p.GetRectBounds(true);
        }
 
#if DEBUG
        internal void SetID(int i) 
        { 
            id  = i.ToString(CultureInfo.InvariantCulture);
        } 
#endif

        /// 
        /// Gets approximate primitive bounds with clipping applied. 
        /// 
        public Rect GetClippedBounds() 
        { 
            Rect bounds = this.bounds;
 
            if (primitive.Clip != null)
            {
                bounds = Rect.Intersect(bounds, primitive.Clip.Bounds);
            } 

            return bounds; 
        } 

        ///  
        /// Current primitive is underneath p. Return true if current primitive fully contains p in shape and color
        /// 
        /// 
        ///  
        public bool FullyCovers(PrimitiveInfo p)
        { 
            if (bounds.Contains(p.bounds)) 
            {
                GeometryPrimitive gp = primitive as GeometryPrimitive; 

                if ((gp != null) && (gp.Brush != null) && (gp.Pen == null))
                {
                    BrushProxy bp = gp.Brush; 

                    if (!(bp.Brush is TileBrush)) 
                    { 
                        Geometry pshape = p.primitive.GetShapeGeometry();
 
                        if (primitive.Clip != null)
                        {
                            if (!Utility.FullyCovers(primitive.Clip, pshape))
                            { 
                                return false;
                            } 
                        } 

                        return Utility.FullyCovers(primitive.GetShapeGeometry(), pshape); 
                    }
                }
            }
 
            return false;
        } 
 
    } // end of class PrimitiveInfo
 
} // end of namespace

// 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