void IProxyDrawingContext.Push(double opacity, BrushProxy opacityMask) { _pushedStack.Push(_opacity); _pushedStack.Push(_opacityMask); _opacity *= opacity; _opacityMask = BrushProxy.BlendBrush(_opacityMask, opacityMask); }
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; }
public DisplayListDrawingContext( Flattener flattener, double opacity, BrushProxy opacityMask, Matrix trans, Geometry clip) { _flattener = flattener; _opacity = opacity; _opacityMask = opacityMask; _transform = trans; _clip = clip; }
bool IProxyDrawingContext.DrawGlyphs(GlyphRun glyphrun, Geometry clip, Matrix trans, BrushProxy foreground) { Debug.Assert(!_costing, "in costing mode DrawyGlyphs"); BrushProxy bp = BrushProxy.BlendColorWithBrush(false, Colors.White, foreground, false); Brush b = bp.Brush; if ((b == null) || (b is DrawingBrush)) { return(false); } if (clip != null) { _dc.PushClip(clip); } if (!trans.IsIdentity) { _dc.PushTransform(trans); } #if DEBUG _seq++; _dc.Comment("-> DrawGlyphRun " + _seq); #endif _dc.DrawGlyphRun(b, glyphrun); #if DEBUG _dc.Comment("<- DrawGlyphRun " + _seq); if (Configuration.Verbose >= 2) { Console.WriteLine(" DrawGlyphRun(" + _comment + ")"); } #endif if (!trans.IsIdentity) { _dc.PopTransform(); } if (clip != null) { _dc.PopClip(); } return(true); }
/// <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 BlendUnderBrush(bool opacityOnly, BrushProxy brush, Matrix trans) { if (brush.Brush is SolidColorBrush) { SolidColorBrush sb = brush.Brush as SolidColorBrush; BlendUnderColor(Utility.Scale(sb.Color, brush.Opacity), 1, opacityOnly); } else { Byte[] brushPixels = RasterizeBrush(brush, trans); Decode(); Utility.BlendPixels(_pixels, opacityOnly, brushPixels, brush.OpacityOnly, _pixelWidth * _pixelHeight, _pixels); } }
private static bool BlendCommands(PrimitiveInfo pi, PrimitiveInfo pj) { GeometryPrimitive gi = pi.primitive as GeometryPrimitive; GeometryPrimitive gj = pj.primitive as GeometryPrimitive; if ((gi != null) && (gi.Brush != null) && (gj != null) && (gj.Brush != null)) { // get brushes in world space BrushProxy bi = gi.Brush.ApplyTransformCopy(gi.Transform); BrushProxy bj = gj.Brush.ApplyTransformCopy(gj.Transform); gi.Brush = bi.BlendBrush(bj); return(true); } return(false); }
public void BlendOverBrush(bool opacityOnly, BrushProxy brush, Matrix trans) { if (IsOpaque()) { Debug.Assert(!opacityOnly, "Opaque image OpacityMask should not be blended with brush"); return; } if (brush.Brush is SolidColorBrush) { SolidColorBrush sb = brush.Brush as SolidColorBrush; BlendOverColor(Utility.Scale(sb.Color, brush.Opacity), 1.0, opacityOnly); } else { Byte[] brushPixels = RasterizeBrush(brush, trans); Decode(); Utility.BlendPixels(brushPixels, brush.OpacityOnly, _pixels, opacityOnly, _pixelWidth * _pixelHeight, _pixels); } }
void IProxyDrawingContext.Push(double opacity, BrushProxy opacityMask) { // BrushProxyDecomposer sends output directly to GDI, so opacity // is invalid by this point. Debug.Assert(false, "Opacity invalid at BrushProxyDecomposer"); }
/// <summary> /// Fill a geometry using a list of brushes /// </summary> /// <param name="one">First brush to use</param> /// <param name="brushes">Brush list</param> /// <param name="from">Index to get the second brush</param> /// <param name="geometry">Geometry to fill</param> private bool FillGeometry(BrushProxy one, ArrayList brushes, int from, Geometry geometry) { Debug.Assert(one != null); BrushProxy two = null; if (one.BrushList == null) { if (from >= brushes.Count) // Only single brush left { ((IProxyDrawingContext)this).DrawGeometry(one, null, geometry, null, Matrix.Identity, ProxyDrawingFlags.None); return(true); } two = brushes[from] as BrushProxy; from++; // Move to next brush } else { Debug.Assert(one.BrushList.Count == 2, "Only two brushes allowed here"); two = one.BrushList[1] as BrushProxy; one = one.BrushList[0] as BrushProxy; } BrushProxy.BrushTypes typeOne = one.BrushType; BrushProxy.BrushTypes typeTwo = two.BrushType; bool pre = true; // Try to break the brush with higher BrushTypes first, and then the other one for (int i = 0; i < 2; i++) { // swap to higher one first iteration, then swap for the second loop if ((typeOne < typeTwo) || (i == 1)) { BrushProxy t = one; one = two; two = t; BrushProxy.BrushTypes bt = typeOne; typeOne = typeTwo; typeTwo = bt; pre = !pre; } if ((typeOne & BrushProxy.BrushTypes.RadialGradientBrush) != 0) { return(RadialFillGeometry(one, two, pre, brushes, from, geometry)); } if ((typeOne & BrushProxy.BrushTypes.LinearGradientBrush) != 0) { return(LinearFillGeometry(one, two, pre, brushes, from, geometry)); } if ((typeOne & BrushProxy.BrushTypes.HasOpacityMask) != 0) { BrushProxy.BrushTypes opacityType = one.OpacityMask.BrushType; if ((opacityType & BrushProxy.BrushTypes.RadialGradientBrush) != 0) { return(RadialFillGeometry(one, two, pre, brushes, from, geometry)); } if ((opacityType & BrushProxy.BrushTypes.LinearGradientBrush) != 0) { return(LinearFillGeometry(one, two, pre, brushes, from, geometry)); } } } #if DEBUG if (Configuration.Verbose >= 2) { Debug.WriteLine("FillGeometry not implemented " + one + " " + two); } #endif return(false); }
/// <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); } } }
// Breaking RadialGradientBrush apart to simplify drawing private bool RadialFillGeometry(BrushProxy radial, BrushProxy other, bool pre, ArrayList brushes, int from, Geometry shape) { bool opacityOnly = false; RadialGradientBrush b = null; double opacity = 0; b = radial.Brush as RadialGradientBrush; BrushProxy saveMask = radial.OpacityMask; double saveOpacity = radial.Opacity; if (b != null) { opacity = radial.Opacity; } else { b = saveMask.Brush as RadialGradientBrush; opacity = saveMask.Opacity; opacityOnly = true; Debug.Assert(b != null, "RadialGradientBrush expected"); } radial.OpacityMask = null; // Need to give flattener BrushProxy's opacity, which includes Brush's opacity and // opacity pushed from parent primitives. RadialGradientFlattener rf = new RadialGradientFlattener(b, shape, opacity); int steps = rf.Steps; if (_costing) { if (steps > Configuration.MaxGradientSteps) // Avoid decomposition if there are too many steps { _cost = 1; return(true); } } else { _dc.PushClip(shape); } bool result = true; for (int i = steps; i > 0; i--) { Color color; Geometry slice = rf.GetSlice(i, out color); BrushProxy blend = null; if (opacityOnly) { radial.Opacity = saveOpacity * Utility.NormalizeOpacity(color.ScA); if (pre) { blend = radial.BlendBrush(other); } else { blend = other.BlendBrush(radial); } } else { if (saveMask == null) { blend = BrushProxy.BlendColorWithBrush(false, color, other, !pre); } else { blend = BrushProxy.BlendColorWithBrush(false, color, saveMask, false); if (pre) { blend = blend.BlendBrush(other); } else { blend = other.BlendBrush(blend); } } } result = FillGeometry(blend, brushes, from, slice); if (!result) { break; } // Break when we already know decomposition of gradient is more costly if (_costing && (_cost > 0)) { break; } } radial.OpacityMask = saveMask; radial.Opacity = saveOpacity; if (!_costing) { _dc.PopClip(); } return(result); }
bool IProxyDrawingContext.DrawGlyphs(GlyphRun glyphrun, Geometry clip, Matrix trans, BrushProxy foreground) { bool empty; clip = Utility.Intersect(_clip, Utility.TransformGeometry(clip, _transform), Matrix.Identity, out empty); if (empty) { return(true); } GlyphPrimitive gp = new GlyphPrimitive(); gp.GlyphRun = glyphrun; gp.Clip = clip; gp.Transform = trans * _transform; gp.Brush = foreground; gp.PushOpacity(_opacity, _opacityMask); _flattener.AddPrimitive(gp); return(true); }
public void Push( Transform transform, Geometry clip, double opacity, Brush opacityMask, Rect maskBounds, bool onePrimitive, String nameAttr, Visual node, Uri navigateUri, EdgeMode edgeMode) { Debug.Assert(Utility.IsValid(opacity), "Invalid opacity should clip subtree"); Matrix mat = Matrix.Identity; if (transform != null) { mat = transform.Value; } // opacity mask might be VisualBrush, hence ReduceBrush to reduce to DrawingBrush Debug.Assert(!BrushProxy.IsEmpty(opacityMask), "empty opacity mask should not result in Push"); _dc.Push( mat, clip, opacity, ReduceBrush(opacityMask, maskBounds), maskBounds, onePrimitive, nameAttr, node, navigateUri, edgeMode ); // prepend to transforms and clipping stack mat.Append(Transform); _fullTransform.Add(mat); // transform clip to world space, intersect with current clip, and push if (clip == null) { // push current clipping clip = Clip; } else { clip = Utility.TransformGeometry(clip, Transform); bool empty; clip = Utility.Intersect(clip, Clip, Matrix.Identity, out empty); if (empty) { clip = Geometry.Empty; } } _fullClip.Add(clip); }
void IProxyDrawingContext.DrawGeometry(BrushProxy brush, PenProxy pen, Geometry geometry, Geometry clip, Matrix brushTrans, ProxyDrawingFlags flags) { if ((brush != null) && (pen != null)) // Split fill & stroke into two { ((IProxyDrawingContext)(this)).DrawGeometry(brush, null, geometry, clip, brushTrans, flags); brush = null; } bool empty; clip = Utility.Intersect(_clip, Utility.TransformGeometry(clip, _transform), Matrix.Identity, out empty); if (empty) { return; } GeometryPrimitive geo = new GeometryPrimitive(); // apply drawing flags to primitive if ((flags & ProxyDrawingFlags.PixelSnapBounds) != 0) { geo.PixelSnapBounds = true; } if (pen != null) { if (!brushTrans.IsIdentity) { double scale; bool uniform = Utility.HasUniformScale(brushTrans, out scale); if (uniform) { geo.Pen = pen.Clone(); geo.Pen.Scale(scale); geo.Pen.StrokeBrush.ApplyTransform(brushTrans); } else { // relative may not be good enough geometry = Utility.InverseTransformGeometry(geometry, brushTrans); geometry = geometry.GetWidenedPathGeometry(pen.GetPen(true), 0.0001, ToleranceType.Relative); geometry = Utility.TransformGeometry(geometry, brushTrans); brush = pen.StrokeBrush; pen = null; } } else { geo.Pen = pen.Clone(); } } if (brush != null) { geo.Brush = brush.ApplyTransformCopy(brushTrans); } geo.Geometry = geometry; geo.Clip = clip; geo.Transform = _transform; geo.PushOpacity(_opacity, _opacityMask); _flattener.AddPrimitive(geo); }
public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) { // Ignore total transparent primitive if (Utility.IsTransparent(_opacity) || ((brush == null) && (pen == null || pen.Brush == null)) || (geometry == null)) { return; } // Split if having both pen and brush if ((brush != null) && (pen != null)) // if (!Utility.IsOpaque(_opacity) || (_opacityMask != null)) { // Push a canvas to handle geometry with brush + pen properly Push(Matrix.Identity, null, 1.0, null, Rect.Empty, false); DrawGeometry(brush, null, geometry); DrawGeometry(null, pen, geometry); Pop(); return; } AssertState(DeviceState.PageStarted, DeviceState.NoChange); GeometryPrimitive g = new GeometryPrimitive(); g.Geometry = geometry; g.Clip = _clip; g.Opacity = _opacity; g.OpacityMask = _opacityMask; int needBounds = 0; // 1 for fill, 2 for stroke if (brush != null) { // Fix bug 1427695: Need bounds for non-SolidColorBrushes to enable rebuilding Brush from BrushProxy. if (!(brush is SolidColorBrush)) { needBounds |= 1; } } if ((pen != null) && (pen.Brush != null)) { if (!(pen.Brush is SolidColorBrush)) { needBounds |= 2; } } if (g.OpacityMask != null) { if (g.OpacityMask.BrushList == null && !(g.OpacityMask.Brush is SolidColorBrush)) { if (pen != null) { needBounds |= 2; } else { needBounds |= 1; } } } Rect bounds = g.GetRectBounds((needBounds & 1) != 0); if (brush != null) { g.Brush = BrushProxy.CreateBrush(brush, bounds); } if ((needBounds & 2) != 0) { bounds = geometry.GetRenderBounds(pen); } if ((pen != null) && (pen.Brush != null)) { g.Pen = PenProxy.CreatePen(pen, bounds); } if (g.OpacityMask != null) { if (!g.OpacityMask.MakeBrushAbsolute(bounds)) { // Fix bug 1463955: Brush has become empty; replace with transparent brush. g.OpacityMask = BrushProxy.CreateColorBrush(Colors.Transparent); } } // Optimization: Unfold primitive DrawingBrush when possible to avoid rasterizing it. Primitive primitive = g.UnfoldDrawingBrush(); if (primitive != null) { _root.Children.Add(primitive); } }
/// <summary> /// Optimization: If a transparent primitive is covered underneath immediately by an opaque primitive, /// or has nothing underneath, convert it to opaque primitive /// </summary> /// <param name="commands"></param> /// <param name="i"></param> private static bool ConvertTransparentOnOpaque(List <PrimitiveInfo> commands, int i) { PrimitiveInfo pi = commands[i]; GeometryPrimitive gp = pi.primitive as GeometryPrimitive; if (gp != null) { PrimitiveInfo qi = null; if ((pi.underlay != null) && (pi.underlay.Count != 0)) { qi = commands[pi.underlay[pi.underlay.Count - 1]]; } if ((qi == null) || (qi.primitive.IsOpaque && qi.FullyCovers(pi))) { BrushProxy under = BrushProxy.CreateColorBrush(Colors.White); if (qi != null) { GeometryPrimitive qp = qi.primitive as GeometryPrimitive; if (qp != null) { under = qp.Brush; } } if (under != null) { // Blend it with brush underneath BrushProxy blendedBrush = gp.Brush; BrushProxy blendedPenBrush = gp.Pen == null ? null : gp.Pen.StrokeBrush; if (blendedBrush != null) { blendedBrush = under.BlendBrush(blendedBrush); } else if (blendedPenBrush != null) { blendedPenBrush = under.BlendBrush(blendedPenBrush); } // // Fix bug 1293500: // Allow blending to proceed only if we did not generate pen stroke // brush that is a brush list. Reason: Such a case would have to be // handled during rendering by stroking the object with each brush // in the list. But we're already rendering brushes of underlying // objects, so the optimization is pointless. // bool proceedBlending = true; if (blendedPenBrush != null && blendedPenBrush.BrushList != null) { proceedBlending = false; } if (proceedBlending) { gp.Brush = blendedBrush; if (gp.Pen != null) { gp.Pen.StrokeBrush = blendedPenBrush; } } if (proceedBlending && pi.primitive.IsOpaque) { #if DEBUG Console.WriteLine("Make {0} opaque", i); #endif if (pi.underlay != null) { for (int k = 0; k < pi.underlay.Count; k++) { commands[pi.underlay[k]].overlapHasTransparency--; } } return(true); } } } } return(false); }
private Byte[] RasterizeBrush(BrushProxy brush, Matrix trans) { return(brush.CreateBrushImage(trans, _pixelWidth, _pixelHeight)); }
public void PushOpacity(double opacity, BrushProxy opacityMask, Rect rect, Matrix trans) { if (opacityMask != null) { rect.Transform(trans); // // Blend this image on top of opacity mask. // // Calculate scaling factor from opacity mask to this image. TileBrush opacityBrush = opacityMask.Brush as TileBrush; Rect viewport; if (opacityBrush != null) { Debug.Assert(opacityBrush.ViewportUnits == BrushMappingMode.Absolute, "TileBrush must have absolute viewport by this point"); viewport = opacityBrush.Viewport; } else { // viewport covers entire image viewport = rect; } // Fix for 1689025: double scaleX = _pixelWidth / rect.Width; double scaleY = _pixelHeight / rect.Height; // If current image is too small, magnify it to match opacity mask's size, // otherwise we lose the detail in opacity mask. if ((scaleX < MinimumBlendRatio || scaleY < MinimumBlendRatio) && (rect.Width <= MaximumOpacityMaskViewport) && (rect.Height <= MaximumOpacityMaskViewport)) // Avoiding generate huge bitmap { Scale(rect.Width / _pixelWidth, rect.Height / _pixelHeight); scaleX = 1.0; scaleY = 1.0; } // Transform brush to image space. Matrix transform = new Matrix(); transform.Translate(-rect.Left, -rect.Top); transform.Scale(scaleX, scaleY); // Blend opacity mask into image. BlendUnderBrush(false, opacityMask, transform); } int op = Utility.OpacityToByte(opacity); if (op <= 0) { _image = null; _pixels = null; return; } else if (op >= 255) { return; } Decode(); Byte[] map = new Byte[256]; for (int i = 0; i < 256; i++) { map[i] = (Byte)(i * op / 255); } int count = _pixelWidth * _pixelHeight * 4; for (int i = 0; i < count; i++) { _pixels[i] = map[_pixels[i]]; } }
/// <summary> /// Check if the whole cluster is complex enough such that rasterizing the whole thing is better than flattening it /// </summary> /// <param name="commands"></param> /// <returns></returns> private bool BetterRasterize(List <PrimitiveInfo> commands) { double clustersize = m_bounds.Width * m_bounds.Height; double diff = -Configuration.RasterizationCost(m_bounds.Width, m_bounds.Height); // Estimate cost of geometry operations (intersecting) double pathComplexity = 1; foreach (int i in m_primitives) { PrimitiveInfo pi = commands[i]; Primitive p = pi.primitive; GeometryPrimitive gp = p as GeometryPrimitive; Rect bounds = pi.GetClippedBounds(); bool rasterize = true; if (gp != null) { double complexity = 1; Geometry geo = gp.Geometry; if (geo != null) { complexity = Utility.GetGeometryPointCount(geo); // weight down the complexity of small region complexity *= bounds.Width * bounds.Height / clustersize; } BrushProxy bp = gp.Brush; if (bp == null) { bp = gp.Pen.StrokeBrush; // Widen path would at least double the points complexity *= 3; } if (complexity > 1) { pathComplexity *= complexity; if (pathComplexity > 100000) // 333 x 333 { return(true); } } if (bp != null) { Brush b = bp.Brush; if ((b != null) && ((b is SolidColorBrush) || (b is LinearGradientBrush))) { // SolidColorBrush does not need full rasterization // Vertical/Horizontal linear gradient brush does not need full rasterization rasterize = false; } } } if (rasterize) { diff += Configuration.RasterizationCost(bounds.Width, bounds.Height); if (diff > 0) { break; } } } return(diff > 0); }
void IProxyDrawingContext.Pop() { _opacityMask = (BrushProxy)_pushedStack.Pop(); _opacity = (double)_pushedStack.Pop(); }
void IProxyDrawingContext.DrawGeometry(BrushProxy brush, PenProxy pen, Geometry geometry, Geometry clip, Matrix brushTrans, ProxyDrawingFlags flags) { Debug.Assert(brushTrans.IsIdentity, "brushTrans not supported"); if ((brush == null) && (pen == null)) { return; } if (!_costing && (clip != null)) { _dc.PushClip(clip); } if (brush != null) { brush = BrushProxy.BlendColorWithBrush(false, Colors.White, brush, false); } // Simplification, pushing transformation if (geometry is LineGeometry) { LineGeometry line = geometry.CloneCurrentValue() as LineGeometry; line.StartPoint = geometry.Transform.Value.Transform(line.StartPoint); line.EndPoint = geometry.Transform.Value.Transform(line.EndPoint); line.Transform = Transform.Identity; } if ((brush != null) && (brush.BrushList != null)) // List of brushes { Debug.Assert(pen == null, "no pen"); if (_costing) { FillGeometry(brush.BrushList[0] as BrushProxy, brush.BrushList, 1, geometry); } else { bool rasterize = BetterRasterize(brush, geometry); if (!rasterize) { rasterize = !FillGeometry(brush.BrushList[0] as BrushProxy, brush.BrushList, 1, geometry); } if (rasterize) { bool empty = false; if (clip != null) { // Fix bug 1506957: Clip geometry prior to rasterizing to prevent excessive // rasterization bitmap size. geometry = Utility.Intersect(geometry, clip, Matrix.Identity, out empty); } if (!empty) { RasterizeGeometry(brush, geometry); } } } } else // Single Avalon brush or pen { Pen p = null; BrushProxy strokeBrush = null; if (pen != null) // Blend pen with White { p = pen.GetPen(true); strokeBrush = pen.StrokeBrush; if (!strokeBrush.IsOpaque()) { strokeBrush = BrushProxy.BlendColorWithBrush(false, Colors.White, strokeBrush, false); } } Brush b = null; if (_costing) { if (brush != null) { // DrawingBrush is always rasterized onward from this stage. // Avoid the cost of creating new DrawingBrush in GetRealBrush during costing if ((brush.Brush != null) && (brush.Brush is DrawingBrush)) { b = brush.Brush; } else { b = brush.GetRealBrush(); } } _cost += DrawGeometryCost(b, p, geometry); } else { if (brush != null) { b = brush.GetRealBrush(); } #if DEBUG _seq++; _dc.Comment("-> DrawGeometry " + _seq + ' ' + _comment); #endif if (p == null) { _dc.DrawGeometry(b, null, null, geometry); } else { _dc.DrawGeometry(b, p, strokeBrush.GetRealBrush(), geometry); } #if DEBUG _dc.Comment("<- DrawGeometry" + _seq + ' ' + _comment); if (Configuration.Verbose >= 2) { Console.WriteLine(" DrawGeometry(" + _comment + ")"); } #endif } } if (!_costing && (clip != null)) { _dc.PopClip(); } }
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; } }
// Recursive // _brush must be in world space private void FillGeometry( PrimitiveInfo topPI, Geometry cur, string desp, Geometry curAlt, string despAlt, int start, Geometry inter, Geometry topBounds ) { Primitive p = topPI.primitive; Geometry diff = Utility.Exclude(cur, topBounds, Matrix.Identity); if (diff != null) { // Render cur [- topBounds] using original brush if (_disjoint) { #if DEBUG FillGeometry(diff, Oper(desp, '-', topPI.id), null, null, start + 1); #else FillGeometry(diff, null, null, null, start + 1); #endif } else { // Only diff = cur - topBounds need to be rendered. But it may generate more // complicated path and gaps between objects if (curAlt != null) { #if DEBUG FillGeometry(diff, Oper(desp, '-', topPI.id), curAlt, despAlt, start + 1); #else FillGeometry(diff, null, curAlt, despAlt, start + 1); #endif } else { #if DEBUG FillGeometry(diff, Oper(desp, '-', topPI.id), cur, desp, start + 1); #else FillGeometry(diff, null, cur, desp, start + 1); #endif } } } //if (_disjoint || ! p.IsOpaque) { if (topPI.primitive is ImagePrimitive) { // If primitve on the top is ImagePrimitive, change it to DrawImage with blended image. // An alternative will be generating an image brush ImagePrimitive ip = topPI.primitive as ImagePrimitive; bool empty; double imageWidth = ip.Image.Image.Width; double imageHeight = ip.Image.Image.Height; // Get clip in world space. Geometry clip = Utility.Intersect(inter, Utility.TransformGeometry(new RectangleGeometry(ip.DstRect), ip.Transform), ip.Transform, out empty); if (!empty) { // Get clip bounds in image space. Geometry clipImageSpace = Utility.TransformGeometry(clip, ReverseMap(ip.Transform, ip.DstRect, imageWidth, imageHeight)); Rect drawBounds = clipImageSpace.Bounds; // Clip image data to the intersection. Resulting draw bounds are in image space. BitmapSource clippedImage = ip.Image.GetClippedImage(drawBounds, out drawBounds); if (clippedImage != null) { // Transform draw bounds back to world space. drawBounds.Scale(ip.DstRect.Width / imageWidth, ip.DstRect.Height / imageHeight); drawBounds.Offset(ip.DstRect.Left, ip.DstRect.Top); ImageProxy image = new ImageProxy(clippedImage); // Blend image with other brush, then render composited image. image.BlendOverBrush(false, _brush, ReverseMap(ip.Transform, drawBounds, image.PixelWidth, image.PixelHeight)); #if DEBUG RenderImage(image, drawBounds, clip, true, start + 1, ip.Transform, Oper(desp, '*', topPI.id)); #else RenderImage(image, drawBounds, clip, true, start + 1, ip.Transform, null); #endif } } } else { // -- If top primitive opaque, skip the intersection // -- If current primitive is completely covered by an opaque object, skip the intersection if (p.IsOpaque) // && Utility.Covers(topBounds, cur)) { cur = null; } else { // Render the intersection using blended brush BrushProxy oldbrush = _brush; _brush = p.BlendBrush(_brush); #if DEBUG FillGeometry(inter, Oper(desp, '*', topPI.id), null, null, start + 1); #else FillGeometry(inter, null, null, null, start + 1); #endif _brush = oldbrush; } } if (cur != null) { bool empty; Geometry geo = Utility.Intersect(cur, _clip, Matrix.Identity, out empty); if (geo != null) { topPI.primitive.Exclude(geo); // exclude cur & _clip #if DEBUG topPI.id = Oper(topPI.id, '-', Oper(desp, '*', Oper(desp, '.', "c"))); #endif } } } }
/// <summary> /// Simplifies brush so we don't have to handle as many cases. /// </summary> /// <param name="brush"></param> /// <param name="bounds">Brush fill bounds; must not be empty if VisualBrush</param> /// <returns></returns> /// <remarks> /// Cases simplified: /// - A lot of empty brush cases. See BrushProxy.IsEmpty. /// - GradientBrush where gradient colors are similar enough to be SolidColorBrush. /// - Reduce VisualBrush to DrawingBrush. /// - Reduce ImageBrush with DrawingImage to DrawingBrush. /// </remarks> private Brush ReduceBrush(Brush brush, Rect bounds) { return(BrushProxy.ReduceBrush(brush, bounds, Transform, _pageSize, _treeWalkProgress)); }