/// <summary> /// Optimization: If a transparent primitive is covered underneath by an opaque primitive, cut its ties with all primitives before it /// </summary> /// <param name="pi"></param> /// <param name="commands"></param> /// <param name="i"></param> private static void ReduceTie(PrimitiveInfo pi, List <PrimitiveInfo> commands, int i) { if ((pi.underlay != null) && !pi.primitive.IsOpaque) { int len = pi.underlay.Count; for (int j = len - 1; j >= 0; j--) { PrimitiveInfo qi = commands[pi.underlay[j]]; if (qi.primitive.IsOpaque && qi.FullyCovers(pi)) { for (int k = j - 1; k >= 0; k--) { int under = pi.underlay[k]; commands[under].overlap.Remove(i); commands[under].overlapHasTransparency--; pi.underlay.Remove(under); } break; } } } }
/// <summary> /// Bug 1687865 /// Special optimization for annotation type of visual: lots of glyph runs covered by a single transparency geometry /// t1 ... tn g => t1 ... tn-1 g tn' /// => g t1' ... tn' /// </summary> /// <param name="commands"></param> /// <param name="j"></param> private static void PushTransparencyDown(List <PrimitiveInfo> commands, int j) { PrimitiveInfo pj = commands[j]; GeometryPrimitive gj = pj.primitive as GeometryPrimitive; if ((gj == null) || (pj.underlay == null) || (pj.underlay.Count == 0)) { return; } for (int n = pj.underlay.Count - 1; n >= 0; n--) { int i = pj.underlay[n]; PrimitiveInfo pi = commands[i]; if (pi == null) { continue; } GeometryPrimitive gi = pi.primitive as GeometryPrimitive; if ((gi != null) && (gi.Pen == null) && (pi.overlap.Count == 1) && pj.FullyCovers(pi)) { // c[i] ... c[j] => ... c[j] c[i]' if (BlendCommands(pi, pj)) // pi.Brush = Blend(pi.Brush, pj.Brush) { pj.underlay.Remove(i); pi.overlap = null; pi.overlapHasTransparency = 0; while (i < j) { SwitchCommands(commands, i, commands[i], i + 1, commands[i + 1], false); i++; } j--; } } } }
/// <summary> /// Optimization phase /// </summary> /// <param name="commands"></param> /// <param name="count"></param> /// <param name="disjoint"></param> private void DisplayListOptimization(List <PrimitiveInfo> commands, int count, bool disjoint) { #if DEBUG Console.WriteLine(); Console.WriteLine("Start 3: Display list optimization"); Console.WriteLine(); #endif List <int> [] oldUnderlay = null; if (!disjoint) // If not in a subtree which needs full flattening { // The following optimization may change PrimitiveInfo.underlay, but this is needed // for cluster calcuation. So we make a copy of it for use within this routine only oldUnderlay = CopyUnderlay(count, commands); // These optimizations need to run in a seperate pass, because they may affect other primitives for (int i = 0; i < count; i++) { repeat: PrimitiveInfo pi = commands[i]; if (pi == null) { continue; } // Optimization: If a primitive is covered by an opaque primtive, delete it if (pi.overlap != null) { bool deleted = false; for (int j = 0; j < pi.overlap.Count; j++) { PrimitiveInfo qi = commands[pi.overlap[j]]; if (qi.primitive.IsOpaque && qi.FullyCovers(pi)) { DeleteCommand(i); deleted = true; break; } } if (deleted) { continue; } } // Optimization: If a primitive is covered by overlap[0], blend brush and switch order // This results in smaller area being rendered as blending of two brushes. if ((pi.overlap != null) && (pi.overlap.Count != 0)) { int j = pi.overlap[0]; // first overlapping primitive PrimitiveInfo pj = commands[j]; // Do not attempt to blend if both primitives cover exactly same area, since blending // one into the other provides no benefits. if ((pj.underlay[pj.underlay.Count - 1] == i) && pj.FullyCovers(pi) && !pi.FullyCovers(pj)) { if (BlendCommands(pi, pj)) { SwitchCommands(commands, i, pi, j, pj, true); goto repeat; // pj at position i needs to be processed } } } // Optimization: Delete white primitives with nothing underneath if ((pi.underlay == null) && DisplayList.IsWhitePrimitive(pi.primitive)) { DeleteCommand(i); continue; } // Optimization: If a transparent primitive is covered underneath by an opaque primitive, cut its ties with all primitives before it ReduceTie(pi, commands, i); // Transparent primitive if (!pi.primitive.IsOpaque) { // Optimization: If a transparent primitive is covered underneath immediately by an opaque primitive, // or has nothing underneath, convert it to opaque primitive if (!ConvertTransparentOnOpaque(commands, i)) { PushTransparencyDown(commands, i); } } } for (int i = 0; i < count; i++) { PrimitiveInfo pi = commands[i]; if (pi == null) { continue; } // Optimization: If a primitive is covered by all opaque primitives, cut its ties with primitive on top of it. // This check is also implemented in PrimitiveRender.FindIntersection, // in which it is on a remaing items in overlapping list. // With overlapHasTransparency flag, it can be moved forward. if ((pi.overlap != null) && (pi.overlapHasTransparency == 0)) { foreach (int j in pi.overlap) { commands[j].underlay.Remove(i); } pi.overlap = null; } // Optimization: If an opaque primitive is covered by all opaque primitives, cut its ties with primitives under it. if ((pi.underlay != null) && (pi.overlapHasTransparency == 0) && pi.primitive.IsOpaque) { foreach (int j in pi.underlay) { commands[j].overlap.Remove(i); } pi.underlay = null; } } } List <Cluster> transparentCluster = Cluster.CalculateCluster(commands, count, disjoint, oldUnderlay); Cluster.CheckForRasterization(transparentCluster, commands); #if DEBUG if (HasUnmanagedCodePermission()) { LogInterestingPrimitives(commands, count, transparentCluster); SaveInterestingPrimitives(commands, count, transparentCluster); } #endif }
/// <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); }