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

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ContentDisposition.cs
- ServiceModelPerformanceCounters.cs
- SecurityTokenProviderContainer.cs
- StatusBarItem.cs
- IndexerNameAttribute.cs
- CheckPair.cs
- PersonalizationStateInfo.cs
- ExpressionEvaluator.cs
- CalendarAutomationPeer.cs
- ReachPrintTicketSerializer.cs
- XmlAttributeCollection.cs
- WindowsScrollBarBits.cs
- PageThemeParser.cs
- XamlTreeBuilderBamlRecordWriter.cs
- HwndSubclass.cs
- MouseButtonEventArgs.cs
- QuerySettings.cs
- DrawingBrush.cs
- X509Certificate2.cs
- Parameter.cs
- TitleStyle.cs
- EntityDataReader.cs
- NameValuePermission.cs
- PreviewPageInfo.cs
- TypeBrowserDialog.cs
- JavaScriptSerializer.cs
- Selection.cs
- PersonalizationDictionary.cs
- Dictionary.cs
- CodeRegionDirective.cs
- ListBindingHelper.cs
- TextElementCollection.cs
- ObjectParameter.cs
- ConfigurationHandlersInstallComponent.cs
- JpegBitmapDecoder.cs
- RegexCode.cs
- HealthMonitoringSectionHelper.cs
- ModelFunction.cs
- DecimalAverageAggregationOperator.cs
- QuaternionAnimation.cs
- RowParagraph.cs
- PropertyDescriptorCollection.cs
- DesignerMetadata.cs
- MemoryMappedFile.cs
- GridViewColumn.cs
- MethodBuilderInstantiation.cs
- MediaPlayer.cs
- basecomparevalidator.cs
- TargetInvocationException.cs
- StylusButtonCollection.cs
- InstanceData.cs
- ToolStripDropTargetManager.cs
- PropertyNames.cs
- DateTimeStorage.cs
- ControlPropertyNameConverter.cs
- _AcceptOverlappedAsyncResult.cs
- JulianCalendar.cs
- TimestampInformation.cs
- CustomAttributeFormatException.cs
- ProtectedConfiguration.cs
- SatelliteContractVersionAttribute.cs
- AddInControllerImpl.cs
- GrammarBuilder.cs
- X509Certificate2Collection.cs
- Interlocked.cs
- ServiceOperation.cs
- SqlTopReducer.cs
- SQLDateTime.cs
- ProviderCollection.cs
- _DigestClient.cs
- AutomationElement.cs
- FormsAuthenticationModule.cs
- SmtpMail.cs
- RegionInfo.cs
- IERequestCache.cs
- FrameworkTextComposition.cs
- AccessDataSourceDesigner.cs
- AsymmetricCryptoHandle.cs
- RelationshipConstraintValidator.cs
- PathParser.cs
- ActivityCollectionMarkupSerializer.cs
- DataSourceHelper.cs
- EventSinkHelperWriter.cs
- Error.cs
- EmbossBitmapEffect.cs
- PositiveTimeSpanValidator.cs
- ContextMenuStrip.cs
- ScriptControl.cs
- TraceProvider.cs
- ControlType.cs
- Scheduler.cs
- StylusPointDescription.cs
- OdbcStatementHandle.cs
- TableRowGroup.cs
- OpCodes.cs
- ToolStripCollectionEditor.cs
- DynamicDataManager.cs
- ChangesetResponse.cs
- XamlTypeMapper.cs
- TitleStyle.cs