Index: src/core/SkRecordOpts.cpp |
diff --git a/src/core/SkRecordOpts.cpp b/src/core/SkRecordOpts.cpp |
index df29b1baba88efdf792ffcd89d808c55fecab55a..83512926da7f30e28207943185c56152a474a041 100644 |
--- a/src/core/SkRecordOpts.cpp |
+++ b/src/core/SkRecordOpts.cpp |
@@ -21,6 +21,7 @@ void SkRecordOptimize(SkRecord* record) { |
//SkRecordNoopSaveRestores(record); |
SkRecordNoopSaveLayerDrawRestores(record); |
+ SkRecordMergeSvgOpacityAndFilterLayers(record); |
} |
// Most of the optimizations in this file are pattern-based. These are all defined as structs with: |
@@ -56,6 +57,64 @@ struct SaveOnlyDrawsRestoreNooper { |
return true; |
} |
}; |
+ |
+static bool fold_opacity_layer_color_to_paint(const SkPaint& layerPaint, |
+ bool isSaveLayer, |
+ SkPaint* paint) { |
+ // We assume layerPaint is always from a saveLayer. If isSaveLayer is |
+ // true, we assume paint is too. |
+ |
+ // The alpha folding can proceed if the filter layer paint does not have properties which cause |
+ // the resulting filter layer to be "blended" in complex ways to the parent layer. For example, |
+ // looper drawing unmodulated filter layer twice and then modulating the result produces |
+ // different image to drawing modulated filter layer twice. |
+ // TODO: most likely the looper and only some xfer modes are the hard constraints |
+ if (paint->getXfermode() || paint->getLooper()) { |
+ return false; |
+ } |
+ |
+ if (!isSaveLayer && paint->getImageFilter()) { |
+ // For normal draws, the paint color is used as one input for the color for the draw. Image |
+ // filter will operate on the result, and thus we can not change the input. |
+ // For layer saves, the image filter is applied to the layer contents. The layer is then |
+ // modulated with the paint color, so it's fine to proceed with the fold for saveLayer |
+ // paints with image filters. |
+ return false; |
+ } |
+ |
+ if (paint->getColorFilter()) { |
+ // Filter input depends on the paint color. |
+ |
+ // Here we could filter the color if we knew the draw is going to be uniform color. This |
+ // should be detectable as drawPath/drawRect/.. without a shader being uniform, while |
+ // drawBitmap/drawSprite or a shader being non-uniform. However, current matchers don't |
+ // give the type out easily, so just do not optimize that at the moment. |
+ return false; |
+ } |
+ |
+ const uint32_t layerColor = layerPaint.getColor(); |
+ // The layer paint color must have only alpha component. |
+ if (SK_ColorTRANSPARENT != SkColorSetA(layerColor, SK_AlphaTRANSPARENT)) { |
+ return false; |
+ } |
+ |
+ // The layer paint can not have any effects. |
+ if (layerPaint.getPathEffect() || |
+ layerPaint.getShader() || |
+ layerPaint.getXfermode() || |
+ layerPaint.getMaskFilter() || |
+ layerPaint.getColorFilter() || |
+ layerPaint.getRasterizer() || |
+ layerPaint.getLooper() || |
+ layerPaint.getImageFilter()) { |
+ return false; |
+ } |
+ |
+ paint->setAlpha(SkMulDiv255Round(paint->getAlpha(), SkColorGetA(layerColor))); |
+ |
+ return true; |
+} |
+ |
// Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops. |
struct SaveNoDrawsRestoreNooper { |
// Star matches greedily, so we also have to exclude Save and Restore. |
@@ -104,14 +163,10 @@ struct SaveLayerDrawRestoreNooper { |
return false; |
} |
- const uint32_t layerColor = layerPaint->getColor(); |
- const uint32_t drawColor = drawPaint->getColor(); |
- if (!IsOnlyAlpha(layerColor) || HasAnyEffect(*layerPaint) || CantFoldAlpha(*drawPaint)) { |
- // Too fancy for us. |
+ if (!fold_opacity_layer_color_to_paint(*layerPaint, false /*isSaveLayer*/, drawPaint)) { |
return false; |
} |
- drawPaint->setAlpha(SkMulDiv255Round(SkColorGetA(drawColor), SkColorGetA(layerColor))); |
return KillSaveLayerAndRestore(record, begin); |
} |
@@ -120,35 +175,58 @@ struct SaveLayerDrawRestoreNooper { |
record->replace<NoOp>(saveLayerIndex+2); // Restore |
return true; |
} |
+}; |
+void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) { |
+ SaveLayerDrawRestoreNooper pass; |
+ apply(&pass, record); |
+} |
- static bool HasAnyEffect(const SkPaint& paint) { |
- return paint.getPathEffect() || |
- paint.getShader() || |
- paint.getXfermode() || |
- paint.getMaskFilter() || |
- paint.getColorFilter() || |
- paint.getRasterizer() || |
- paint.getLooper() || |
- paint.getImageFilter(); |
- } |
- // The alpha folding can proceed if the single draw's paint has a shader, |
- // path effect, mask filter and/or rasterizer. |
- // TODO: most likely the looper and only some xfer modes are the hard |
- // constraints |
- static bool CantFoldAlpha(const SkPaint& paint) { |
- return paint.getXfermode() || |
- paint.getColorFilter() || |
- paint.getLooper() || |
- paint.getImageFilter(); |
+/* For SVG generated: |
+ SaveLayer (non-opaque, typically for CSS opacity) |
+ Save |
+ ClipRect |
+ SaveLayer (typically for SVG filter) |
+ Restore |
+ Restore |
+ Restore |
+*/ |
+struct SvgOpacityAndFilterLayerMergePass { |
+ typedef Pattern7<Is<SaveLayer>, Is<Save>, Is<ClipRect>, Is<SaveLayer>, |
+ Is<Restore>, Is<Restore>, Is<Restore> > Pattern; |
+ |
+ bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) { |
+ SkPaint* opacityPaint = pattern->first<SaveLayer>()->paint; |
+ if (NULL == opacityPaint) { |
+ // There wasn't really any point to this SaveLayer at all. |
+ return KillSaveLayerAndRestore(record, begin); |
+ } |
+ |
+ // This layer typically contains a filter, but this should work for layers with for other |
+ // purposes too. |
+ SkPaint* filterLayerPaint = pattern->fourth<SaveLayer>()->paint; |
+ if (filterLayerPaint == NULL) { |
+ // We can just give the inner SaveLayer the paint of the outer SaveLayer. |
+ // TODO(mtklein): figure out how to do this clearly |
+ return false; |
+ } |
+ |
+ if (!fold_opacity_layer_color_to_paint(*opacityPaint, true /*isSaveLayer*/, |
+ filterLayerPaint)) { |
+ return false; |
+ } |
+ |
+ return KillSaveLayerAndRestore(record, begin); |
} |
- static bool IsOnlyAlpha(SkColor color) { |
- return SK_ColorTRANSPARENT == SkColorSetA(color, SK_AlphaTRANSPARENT); |
+ static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerIndex) { |
+ record->replace<NoOp>(saveLayerIndex); // SaveLayer |
+ record->replace<NoOp>(saveLayerIndex + 6); // Restore |
+ return true; |
} |
}; |
-void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) { |
- SaveLayerDrawRestoreNooper pass; |
+ |
+void SkRecordMergeSvgOpacityAndFilterLayers(SkRecord* record) { |
+ SvgOpacityAndFilterLayerMergePass pass; |
apply(&pass, record); |
} |
- |