public void Pop() { AssertState(DeviceState.PageStarted, DeviceState.NoChange); _opacityMask = _stack.Pop() as BrushProxy; _opacity = (double)_stack.Pop(); _clip = _stack.Pop() as Geometry; _root = _stack.Pop() as CanvasPrimitive; }
/// <summary> /// /// </summary> public bool StartPage() { AssertState(DeviceState.DocStarted, DeviceState.PageStarted); _page = new CanvasPrimitive(); _root = _page; _stack = new Stack(); _opacity = 1; _opacityMask = null; _clip = null; return(true); }
public void Push(Matrix transform, Geometry clip, double opacity, Brush opacityMask, Rect maskBounds, bool onePrimitive) { AssertState(DeviceState.PageStarted, DeviceState.NoChange); opacity = Utility.NormalizeOpacity(opacity); if (!Utility.IsValid(transform)) { // treat as invisible subtree opacity = 0.0; transform = Matrix.Identity; } _stack.Push(_root); _stack.Push(_clip); _stack.Push(_opacity); _stack.Push(_opacityMask); bool noTrans = transform.IsIdentity; if (onePrimitive && noTrans && (opacityMask == null)) { bool empty; _clip = Utility.Intersect(_clip, clip, Matrix.Identity, out empty); _opacity *= opacity; _opacityMask = null; if (empty) { _opacity = 0; } } else { CanvasPrimitive c = new CanvasPrimitive(); if (noTrans) { c.Transform = Matrix.Identity; } else { c.Transform = transform; Matrix invertTransform = transform; invertTransform.Invert(); _clip = Utility.TransformGeometry(_clip, invertTransform); // transform inherited clip to this level } bool empty; c.Clip = Utility.Intersect(clip, _clip, Matrix.Identity, out empty); // Combined with inherited attributes c.Opacity = opacity * _opacity; if (empty) { c.Opacity = 0; // Invisible } if (opacityMask != null) { double op; if (Utility.ExtractOpacityMaskOpacity(opacityMask, out op, maskBounds)) { c.Opacity = opacity * _opacity * op; } else { c.OpacityMask = BrushProxy.CreateOpacityMaskBrush(opacityMask, maskBounds); } } _root.Children.Add(c); _root = c; _clip = null; _opacity = 1.0; _opacityMask = null; } }
/// <summary> /// Flatten the structure of a primitive tree by push clip/transform/opacity onto each leaf node. /// Build an index in a DisplayList. /// </summary> /// <param name="tree"></param> /// <param name="clip"></param> /// <param name="transform"></param> /// <param name="opacity"></param> /// <param name="opacityMask"></param> public void TreeFlatten(Primitive tree, Geometry clip, Matrix transform, double opacity, BrushProxy opacityMask) { More: if (tree == null) { return; } Debug.Assert(Utility.IsValid(opacity) && Utility.IsValid(tree.Opacity), "Invalid opacity encountered, should've been normalized in conversion to Primitive"); // Non-invertible transforms may arise from brush unfolding, where brush content is huge but // we need to scale down significantly to fill target region. Allow such transforms. CanvasPrimitive canvas = tree as CanvasPrimitive; if (canvas != null) { ArrayList children = canvas.Children; // No children, nothing to do if ((children == null) || (children.Count == 0)) { return; } opacity *= tree.Opacity; // transform opacity mask into current primitive space if (opacityMask != null) { Matrix worldToPrimitiveTransform = tree.Transform; worldToPrimitiveTransform.Invert(); opacityMask.ApplyTransform(worldToPrimitiveTransform); } opacityMask = BrushProxy.BlendBrush(opacityMask, tree.OpacityMask); // Skip the subtree if it's transparent enough if (Utility.IsTransparent(opacity)) { return; } transform = tree.Transform * transform; Geometry transclip = Utility.TransformGeometry(tree.Clip, transform); bool empty; clip = Utility.Intersect(clip, transclip, Matrix.Identity, out empty); if (empty) { return; } // For single child, just push transform/clip/opacity onto it. if (children.Count == 1) { tree = children[0] as Primitive; // Save a recursive call goto More; } if (Configuration.BlendAlphaWithWhite || Configuration.ForceAlphaOpaque || (Utility.IsOpaque(opacity) && (opacityMask == null))) // For opaque subtree, just push trasform/clip into it. { foreach (Primitive p in children) { TreeFlatten(p, clip, transform, opacity, opacityMask); } } else { // A semi-transparent sub-tree with more than one child Flattener fl = new Flattener(true, _dl.m_width, _dl.m_height); Primitive ntree = tree; ntree.Clip = null; ntree.Transform = Matrix.Identity; ntree.Opacity = 1.0; ntree.OpacityMask = null; #if DEBUG if (Configuration.Verbose >= 2) { Console.WriteLine("TreeFlatten for subtree"); } #endif if (opacityMask != null) { opacityMask.ApplyTransform(transform); } // Flatten sub-tree structure into a new DisplayList fl.TreeFlatten(ntree, clip, transform, 1.0, null); // Remove alpha in the sub-tree and add to the current display list #if DEBUG if (Configuration.Verbose >= 2) { Console.WriteLine("end TreeFlatten for subtree"); Console.WriteLine("AlphaFlatten for subtree"); } #endif fl.AlphaFlatten(new DisplayListDrawingContext(this, opacity, opacityMask, Matrix.Identity, null), true); #if DEBUG if (Configuration.Verbose >= 2) { Console.WriteLine("end AlphaFlatten for subtree"); } #endif } } else { GeometryPrimitive gp = tree as GeometryPrimitive; if (gp != null && gp.Brush != null && gp.Pen != null && (!gp.IsOpaque || !Utility.IsOpaque(opacity))) { // // As an optimization we split fill from stroke, however doing so requires // an intermediate canvas to handle translucent fill/stroke, otherwise // the translucent stroke and fill will overlap. // CanvasPrimitive splitCanvas = new CanvasPrimitive(); GeometryPrimitive fill = (GeometryPrimitive)gp; GeometryPrimitive stroke = (GeometryPrimitive)gp.Clone(); fill.Pen = null; stroke.Brush = null; splitCanvas.Children.Add(fill); splitCanvas.Children.Add(stroke); tree = splitCanvas; goto More; } // Push transform/clip/opacity to leaf node tree.Transform = tree.Transform * transform; if (tree.Clip == null) { tree.Clip = clip; } else { Geometry transclip = Utility.TransformGeometry(tree.Clip, transform); bool empty; tree.Clip = Utility.Intersect(transclip, clip, Matrix.Identity, out empty); if (!empty) { empty = Utility.IsEmpty(tree.Clip, Matrix.Identity); } if (empty) { return; } } tree.PushOpacity(opacity, opacityMask); if (gp != null) { // Split fill and stroke into separate primitives if no opacity involved. // Intermediate Canvas not needed due to opaqueness. if ((gp.Brush != null) && (gp.Pen != null)) { GeometryPrimitive fill = gp.Clone() as GeometryPrimitive; fill.Pen = null; AddPrimitive(fill); // Fill only first // Stroke is flattend to fill only when needed gp.Brush = null; AddPrimitive(gp); // Followed by stroke only } else if ((gp.Pen != null) || (gp.Brush != null)) { AddPrimitive(gp); } } else { // Record it AddPrimitive(tree); } } }