public DisplayListDrawingContext( Flattener flattener, double opacity, BrushProxy opacityMask, Matrix trans, Geometry clip) { _flattener = flattener; _opacity = opacity; _opacityMask = opacityMask; _transform = trans; _clip = clip; }
/// <summary> /// Start Tree/alpha flattening and send result to ILegacyDevice interface /// </summary> public void FlushPage(ILegacyDevice sink, double width, double height, Nullable <OutputQuality> outputQuality) { AssertState(DeviceState.PageStarted, DeviceState.DocStarted); if (_stack.Count != 0) { throw new InvalidOperationException(); } if (sink != null) { Flattener.Convert(_root, sink, width, height, 96, 96, outputQuality); } }
/// <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); } } }
/// <summary> /// Flatten Primitive to ILegacyDevice /// </summary> public static void Convert(Primitive tree, ILegacyDevice dc, double width, double height, double dpix, double dpiy, Nullable <OutputQuality> quality) { // Change Flattener quality setting based on OutputQualityValue if (quality != null) { switch (quality) { case OutputQuality.Unknown: case OutputQuality.Automatic: break; case OutputQuality.Draft: case OutputQuality.Fax: case OutputQuality.Text: Configuration.RasterizationDPI = 96; Configuration.MaximumTransparencyLayer = 8; Configuration.GradientDecompositionDensity = 0.75; Configuration.DecompositionDepth = 2; break; case OutputQuality.Normal: Configuration.RasterizationDPI = 150; Configuration.MaximumTransparencyLayer = 12; Configuration.GradientDecompositionDensity = 1; Configuration.DecompositionDepth = 3; break; case OutputQuality.High: Configuration.RasterizationDPI = 300; Configuration.MaximumTransparencyLayer = 16; Configuration.GradientDecompositionDensity = 1.25; Configuration.DecompositionDepth = 4; break; case OutputQuality.Photographic: uint dcDpi = Math.Max(PrintQueue.GetDpiX(dc), PrintQueue.GetDpiY(dc)); Configuration.RasterizationDPI = (int)(Math.Max(dcDpi, 300)); Configuration.MaximumTransparencyLayer = 16; Configuration.GradientDecompositionDensity = 1.25; Configuration.DecompositionDepth = 4; break; } } #if DEBUG if (Configuration.Verbose >= 2) { Console.WriteLine(); Console.WriteLine("\r\nStage 1: Tree Flattening"); Console.WriteLine(); } #endif // Paper dimension as clipping Geometry clip = null; if ((width != 0) && (height != 0)) { clip = new RectangleGeometry(new Rect(0, 0, width, height)); } // Transform to device resolution Matrix transform = Matrix.Identity; transform.Scale(dpix / 96, dpiy / 96); Flattener fl = new Flattener(false, width, height); Toolbox.EmitEvent(EventTrace.Event.WClientDRXTreeFlattenBegin); fl.TreeFlatten(tree, clip, transform, 1.0, null); Toolbox.EmitEvent(EventTrace.Event.WClientDRXTreeFlattenEnd); Toolbox.EmitEvent(EventTrace.Event.WClientDRXAlphaFlattenBegin); fl.AlphaFlatten(new BrushProxyDecomposer(dc), false); Toolbox.EmitEvent(EventTrace.Event.WClientDRXAlphaFlattenEnd); #if DEBUG if (Configuration.Verbose >= 2) { Console.WriteLine(); Console.WriteLine("End AlphaFlattening"); Console.WriteLine(); } #endif }