OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkRecordOpts.h" | 8 #include "SkRecordOpts.h" |
9 | 9 |
10 #include "SkRecordPattern.h" | 10 #include "SkRecordPattern.h" |
11 #include "SkRecords.h" | 11 #include "SkRecords.h" |
12 #include "SkTDArray.h" | 12 #include "SkTDArray.h" |
13 | 13 |
14 using namespace SkRecords; | 14 using namespace SkRecords; |
15 | 15 |
16 void SkRecordOptimize(SkRecord* record) { | 16 void SkRecordOptimize(SkRecord* record) { |
17 // This might be useful as a first pass in the future if we want to weed | 17 // This might be useful as a first pass in the future if we want to weed |
18 // out junk for other optimization passes. Right now, nothing needs it, | 18 // out junk for other optimization passes. Right now, nothing needs it, |
19 // and the bounding box hierarchy will do the work of skipping no-op | 19 // and the bounding box hierarchy will do the work of skipping no-op |
20 // Save-NoDraw-Restore sequences better than we can here. | 20 // Save-NoDraw-Restore sequences better than we can here. |
21 //SkRecordNoopSaveRestores(record); | 21 //SkRecordNoopSaveRestores(record); |
22 | 22 |
23 SkRecordNoopSaveLayerDrawRestores(record); | 23 SkRecordNoopSaveLayerDrawRestores(record); |
| 24 SkRecordMergeSvgOpacityAndFilterLayers(record); |
24 } | 25 } |
25 | 26 |
26 // Most of the optimizations in this file are pattern-based. These are all defi
ned as structs with: | 27 // Most of the optimizations in this file are pattern-based. These are all defi
ned as structs with: |
27 // - a Pattern typedef | 28 // - a Pattern typedef |
28 // - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method, | 29 // - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method, |
29 // which returns true if it made changes and false if not. | 30 // which returns true if it made changes and false if not. |
30 | 31 |
31 // Run a pattern-based optimization once across the SkRecord, returning true if
it made any changes. | 32 // Run a pattern-based optimization once across the SkRecord, returning true if
it made any changes. |
32 // It looks for spans which match Pass::Pattern, and when found calls onMatch()
with the pattern, | 33 // It looks for spans which match Pass::Pattern, and when found calls onMatch()
with the pattern, |
33 // record, and [begin,end) span of the commands that matched. | 34 // record, and [begin,end) span of the commands that matched. |
(...skipping 15 matching lines...) Expand all Loading... |
49 Star<Or<Is<NoOp>, IsDraw> >, | 50 Star<Or<Is<NoOp>, IsDraw> >, |
50 Is<Restore> > | 51 Is<Restore> > |
51 Pattern; | 52 Pattern; |
52 | 53 |
53 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en
d) { | 54 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en
d) { |
54 record->replace<NoOp>(begin); // Save | 55 record->replace<NoOp>(begin); // Save |
55 record->replace<NoOp>(end-1); // Restore | 56 record->replace<NoOp>(end-1); // Restore |
56 return true; | 57 return true; |
57 } | 58 } |
58 }; | 59 }; |
| 60 |
| 61 static bool fold_opacity_layer_color_to_paint(const SkPaint& layerPaint, |
| 62 bool isSaveLayer, |
| 63 SkPaint* paint) { |
| 64 // We assume layerPaint is always from a saveLayer. If isSaveLayer is |
| 65 // true, we assume paint is too. |
| 66 |
| 67 // The alpha folding can proceed if the filter layer paint does not have pro
perties which cause |
| 68 // the resulting filter layer to be "blended" in complex ways to the parent
layer. For example, |
| 69 // looper drawing unmodulated filter layer twice and then modulating the res
ult produces |
| 70 // different image to drawing modulated filter layer twice. |
| 71 // TODO: most likely the looper and only some xfer modes are the hard constr
aints |
| 72 if (paint->getXfermode() || paint->getLooper()) { |
| 73 return false; |
| 74 } |
| 75 |
| 76 if (!isSaveLayer && paint->getImageFilter()) { |
| 77 // For normal draws, the paint color is used as one input for the color
for the draw. Image |
| 78 // filter will operate on the result, and thus we can not change the inp
ut. |
| 79 // For layer saves, the image filter is applied to the layer contents. T
he layer is then |
| 80 // modulated with the paint color, so it's fine to proceed with the fold
for saveLayer |
| 81 // paints with image filters. |
| 82 return false; |
| 83 } |
| 84 |
| 85 if (paint->getColorFilter()) { |
| 86 // Filter input depends on the paint color. |
| 87 |
| 88 // Here we could filter the color if we knew the draw is going to be uni
form color. This |
| 89 // should be detectable as drawPath/drawRect/.. without a shader being u
niform, while |
| 90 // drawBitmap/drawSprite or a shader being non-uniform. However, current
matchers don't |
| 91 // give the type out easily, so just do not optimize that at the moment. |
| 92 return false; |
| 93 } |
| 94 |
| 95 const uint32_t layerColor = layerPaint.getColor(); |
| 96 // The layer paint color must have only alpha component. |
| 97 if (SK_ColorTRANSPARENT != SkColorSetA(layerColor, SK_AlphaTRANSPARENT)) { |
| 98 return false; |
| 99 } |
| 100 |
| 101 // The layer paint can not have any effects. |
| 102 if (layerPaint.getPathEffect() || |
| 103 layerPaint.getShader() || |
| 104 layerPaint.getXfermode() || |
| 105 layerPaint.getMaskFilter() || |
| 106 layerPaint.getColorFilter() || |
| 107 layerPaint.getRasterizer() || |
| 108 layerPaint.getLooper() || |
| 109 layerPaint.getImageFilter()) { |
| 110 return false; |
| 111 } |
| 112 |
| 113 paint->setAlpha(SkMulDiv255Round(paint->getAlpha(), SkColorGetA(layerColor))
); |
| 114 |
| 115 return true; |
| 116 } |
| 117 |
59 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual
no-ops. | 118 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual
no-ops. |
60 struct SaveNoDrawsRestoreNooper { | 119 struct SaveNoDrawsRestoreNooper { |
61 // Star matches greedily, so we also have to exclude Save and Restore. | 120 // Star matches greedily, so we also have to exclude Save and Restore. |
62 // Nested SaveLayers need to be excluded, or we'll match their Restore! | 121 // Nested SaveLayers need to be excluded, or we'll match their Restore! |
63 typedef Pattern3<Is<Save>, | 122 typedef Pattern3<Is<Save>, |
64 Star<Not<Or4<Is<Save>, | 123 Star<Not<Or4<Is<Save>, |
65 Is<SaveLayer>, | 124 Is<SaveLayer>, |
66 Is<Restore>, | 125 Is<Restore>, |
67 IsDraw> > >, | 126 IsDraw> > >, |
68 Is<Restore> > | 127 Is<Restore> > |
(...skipping 28 matching lines...) Expand all Loading... |
97 return KillSaveLayerAndRestore(record, begin); | 156 return KillSaveLayerAndRestore(record, begin); |
98 } | 157 } |
99 | 158 |
100 SkPaint* drawPaint = pattern->second<SkPaint>(); | 159 SkPaint* drawPaint = pattern->second<SkPaint>(); |
101 if (drawPaint == NULL) { | 160 if (drawPaint == NULL) { |
102 // We can just give the draw the SaveLayer's paint. | 161 // We can just give the draw the SaveLayer's paint. |
103 // TODO(mtklein): figure out how to do this clearly | 162 // TODO(mtklein): figure out how to do this clearly |
104 return false; | 163 return false; |
105 } | 164 } |
106 | 165 |
107 const uint32_t layerColor = layerPaint->getColor(); | 166 if (!fold_opacity_layer_color_to_paint(*layerPaint, false /*isSaveLayer*
/, drawPaint)) { |
108 const uint32_t drawColor = drawPaint->getColor(); | |
109 if (!IsOnlyAlpha(layerColor) || HasAnyEffect(*layerPaint) || CantFoldAlp
ha(*drawPaint)) { | |
110 // Too fancy for us. | |
111 return false; | 167 return false; |
112 } | 168 } |
113 | 169 |
114 drawPaint->setAlpha(SkMulDiv255Round(SkColorGetA(drawColor), SkColorGetA
(layerColor))); | |
115 return KillSaveLayerAndRestore(record, begin); | 170 return KillSaveLayerAndRestore(record, begin); |
116 } | 171 } |
117 | 172 |
118 static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerInde
x) { | 173 static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerInde
x) { |
119 record->replace<NoOp>(saveLayerIndex); // SaveLayer | 174 record->replace<NoOp>(saveLayerIndex); // SaveLayer |
120 record->replace<NoOp>(saveLayerIndex+2); // Restore | 175 record->replace<NoOp>(saveLayerIndex+2); // Restore |
121 return true; | 176 return true; |
122 } | 177 } |
123 | |
124 static bool HasAnyEffect(const SkPaint& paint) { | |
125 return paint.getPathEffect() || | |
126 paint.getShader() || | |
127 paint.getXfermode() || | |
128 paint.getMaskFilter() || | |
129 paint.getColorFilter() || | |
130 paint.getRasterizer() || | |
131 paint.getLooper() || | |
132 paint.getImageFilter(); | |
133 } | |
134 | |
135 // The alpha folding can proceed if the single draw's paint has a shader, | |
136 // path effect, mask filter and/or rasterizer. | |
137 // TODO: most likely the looper and only some xfer modes are the hard | |
138 // constraints | |
139 static bool CantFoldAlpha(const SkPaint& paint) { | |
140 return paint.getXfermode() || | |
141 paint.getColorFilter() || | |
142 paint.getLooper() || | |
143 paint.getImageFilter(); | |
144 } | |
145 | |
146 static bool IsOnlyAlpha(SkColor color) { | |
147 return SK_ColorTRANSPARENT == SkColorSetA(color, SK_AlphaTRANSPARENT); | |
148 } | |
149 }; | 178 }; |
150 void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) { | 179 void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) { |
151 SaveLayerDrawRestoreNooper pass; | 180 SaveLayerDrawRestoreNooper pass; |
152 apply(&pass, record); | 181 apply(&pass, record); |
153 } | 182 } |
154 | 183 |
| 184 |
| 185 /* For SVG generated: |
| 186 SaveLayer (non-opaque, typically for CSS opacity) |
| 187 Save |
| 188 ClipRect |
| 189 SaveLayer (typically for SVG filter) |
| 190 Restore |
| 191 Restore |
| 192 Restore |
| 193 */ |
| 194 struct SvgOpacityAndFilterLayerMergePass { |
| 195 typedef Pattern7<Is<SaveLayer>, Is<Save>, Is<ClipRect>, Is<SaveLayer>, |
| 196 Is<Restore>, Is<Restore>, Is<Restore> > Pattern; |
| 197 |
| 198 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned en
d) { |
| 199 SkPaint* opacityPaint = pattern->first<SaveLayer>()->paint; |
| 200 if (NULL == opacityPaint) { |
| 201 // There wasn't really any point to this SaveLayer at all. |
| 202 return KillSaveLayerAndRestore(record, begin); |
| 203 } |
| 204 |
| 205 // This layer typically contains a filter, but this should work for laye
rs with for other |
| 206 // purposes too. |
| 207 SkPaint* filterLayerPaint = pattern->fourth<SaveLayer>()->paint; |
| 208 if (filterLayerPaint == NULL) { |
| 209 // We can just give the inner SaveLayer the paint of the outer SaveL
ayer. |
| 210 // TODO(mtklein): figure out how to do this clearly |
| 211 return false; |
| 212 } |
| 213 |
| 214 if (!fold_opacity_layer_color_to_paint(*opacityPaint, true /*isSaveLayer
*/, |
| 215 filterLayerPaint)) { |
| 216 return false; |
| 217 } |
| 218 |
| 219 return KillSaveLayerAndRestore(record, begin); |
| 220 } |
| 221 |
| 222 static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerInde
x) { |
| 223 record->replace<NoOp>(saveLayerIndex); // SaveLayer |
| 224 record->replace<NoOp>(saveLayerIndex + 6); // Restore |
| 225 return true; |
| 226 } |
| 227 }; |
| 228 |
| 229 void SkRecordMergeSvgOpacityAndFilterLayers(SkRecord* record) { |
| 230 SvgOpacityAndFilterLayerMergePass pass; |
| 231 apply(&pass, record); |
| 232 } |
OLD | NEW |