OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/debug/trace_event.h" | 5 #include "base/debug/trace_event.h" |
6 #include "skia/ext/analysis_canvas.h" | 6 #include "skia/ext/analysis_canvas.h" |
7 #include "third_party/skia/include/core/SkDevice.h" | 7 #include "third_party/skia/include/core/SkDevice.h" |
8 #include "third_party/skia/include/core/SkDraw.h" | 8 #include "third_party/skia/include/core/SkDraw.h" |
9 #include "third_party/skia/include/core/SkRRect.h" | 9 #include "third_party/skia/include/core/SkRRect.h" |
10 #include "third_party/skia/src/core/SkRasterClip.h" | |
10 #include "ui/gfx/rect_conversions.h" | 11 #include "ui/gfx/rect_conversions.h" |
11 | 12 |
12 namespace { | 13 namespace { |
13 | 14 |
14 // FIXME: Arbitrary number. Requires tuning & experimentation. | 15 // FIXME: Arbitrary number. Requires tuning & experimentation. |
15 // Probably requires per-platform tuning; N10 average draw call takes | 16 // Probably requires per-platform tuning; N10 average draw call takes |
16 // 25x as long as Z620. | 17 // 25x as long as Z620. |
17 const int gPictureCostThreshold = 1000; | 18 const int gPictureCostThreshold = 1000; |
18 | 19 |
20 static bool isSolidColorPaint(const SkPaint& paint) { | |
Justin Novosad
2013/03/05 22:14:02
I think isPaintOpaque (SkPaintPriv.cpp in skia) co
| |
21 SkXfermode::Mode xferMode; | |
22 | |
23 // getXfermode can return a NULL, but that is handled | |
24 // gracefully by AsMode (NULL turns into kSrcOver mode). | |
25 SkXfermode::AsMode(paint.getXfermode(), &xferMode); | |
26 | |
27 // Paint is solid color if the following holds: | |
28 // - Alpha is 1.0, style is fill, and there are no special effects | |
29 // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent | |
30 // to kSrc if source alpha is 1.0, which is already checked). | |
31 return (paint.getAlpha() == 255 && | |
32 !paint.getShader() && | |
33 !paint.getLooper() && | |
34 !paint.getMaskFilter() && | |
35 !paint.getColorFilter() && | |
36 paint.getStyle() == SkPaint::kFill_Style && | |
37 (xferMode == SkXfermode::kSrc_Mode || | |
38 xferMode == SkXfermode::kSrcOver_Mode)); | |
19 } | 39 } |
20 | 40 |
41 static bool isFullQuad(const SkDraw& draw, | |
42 const SkRect& canvasRect, | |
43 const SkRect& drawnRect) { | |
44 SkRect drawBitmapRect; | |
45 draw.fBitmap->getBounds(&drawBitmapRect); | |
46 SkRect clipRect = SkRect::Make(draw.fRC->getBounds()); | |
47 SkRect deviceRect; | |
48 draw.fMatrix->mapRect(&deviceRect, drawnRect); | |
Justin Novosad
2013/03/05 22:14:02
The way this is implemented, you will sometimes ge
| |
49 | |
50 // The drawn rect covers the full canvas, if the following conditions hold: | |
51 // - Clip rect is an actual rectangle. | |
52 // - The rect we're drawing (post-transform) contains the clip rect. | |
53 // That is, all of clip rect will be colored by the rect. | |
54 // - Clip rect contains the canvas rect. | |
55 // That is, we're not clipping to a portion of this canvas. | |
56 // - The bitmap into which the draw call happens is at least as | |
57 // big as the canvas rect | |
58 return draw.fRC->isRect() && | |
Justin Novosad
2013/03/05 22:14:02
This is not safe. AnalysisCanvas converts clipPat
| |
59 deviceRect.contains(clipRect) && | |
60 clipRect.contains(canvasRect) && | |
61 drawBitmapRect.contains(canvasRect); | |
62 } | |
63 | |
64 } // namespace | |
65 | |
21 namespace skia { | 66 namespace skia { |
22 | 67 |
23 AnalysisDevice::AnalysisDevice(const SkBitmap& bm) | 68 AnalysisDevice::AnalysisDevice(const SkBitmap& bm) |
24 : INHERITED(bm) | 69 : INHERITED(bm) |
25 , estimatedCost_(0) { | 70 , estimatedCost_(0) |
71 , isForcedNotSolid_(false) | |
72 , isForcedNotTransparent_(false) | |
73 , isSolidColor_(false) | |
74 , isTransparent_(false) { | |
26 | 75 |
27 } | 76 } |
28 | 77 |
29 AnalysisDevice::~AnalysisDevice() { | 78 AnalysisDevice::~AnalysisDevice() { |
30 | 79 |
31 } | 80 } |
32 | 81 |
33 int AnalysisDevice::getEstimatedCost() const { | 82 int AnalysisDevice::getEstimatedCost() const { |
34 return estimatedCost_; | 83 return estimatedCost_; |
35 } | 84 } |
36 | 85 |
86 bool AnalysisDevice::getColorIfSolid(SkColor* color) const { | |
87 if (isSolidColor_) | |
88 *color = color_; | |
89 return isSolidColor_; | |
90 } | |
91 | |
92 bool AnalysisDevice::isTransparent() const { | |
93 return isTransparent_; | |
94 } | |
95 | |
96 void AnalysisDevice::setForceNotSolid(bool flag) { | |
97 isForcedNotSolid_ = flag; | |
98 if (isForcedNotSolid_) | |
99 isSolidColor_ = false; | |
100 } | |
101 | |
102 void AnalysisDevice::setForceNotTransparent(bool flag) { | |
103 isForcedNotTransparent_ = flag; | |
104 if (isForcedNotTransparent_) | |
105 isTransparent_ = false; | |
106 } | |
107 | |
37 void AnalysisDevice::clear(SkColor color) { | 108 void AnalysisDevice::clear(SkColor color) { |
38 ++estimatedCost_; | 109 ++estimatedCost_; |
110 | |
111 isTransparent_ = (!isForcedNotTransparent_ && SkColorGetA(color) == 0); | |
112 | |
113 if (!isForcedNotSolid_ && SkColorGetA(color) == 255) { | |
114 isSolidColor_ = true; | |
115 color_ = color; | |
116 } | |
117 else { | |
118 isSolidColor_ = false; | |
119 } | |
39 } | 120 } |
40 | 121 |
41 void AnalysisDevice::drawPaint(const SkDraw&, const SkPaint& paint) { | 122 void AnalysisDevice::drawPaint(const SkDraw&, const SkPaint& paint) { |
Justin Novosad
2013/03/05 22:14:02
This method could do more. it fills the entire cli
| |
42 ++estimatedCost_; | 123 ++estimatedCost_; |
124 isSolidColor_ = false; | |
125 isTransparent_ = false; | |
43 } | 126 } |
44 | 127 |
45 void AnalysisDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, | 128 void AnalysisDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, |
46 size_t count, const SkPoint[], | 129 size_t count, const SkPoint[], |
47 const SkPaint& paint) { | 130 const SkPaint& paint) { |
48 ++estimatedCost_; | 131 ++estimatedCost_; |
132 isSolidColor_ = false; | |
133 isTransparent_ = false; | |
49 } | 134 } |
50 | 135 |
51 void AnalysisDevice::drawRect(const SkDraw&, const SkRect& r, | 136 void AnalysisDevice::drawRect(const SkDraw& draw, const SkRect& rect, |
52 const SkPaint& paint) { | 137 const SkPaint& paint) { |
138 | |
53 // FIXME: if there's a pending image decode & resize, more expensive | 139 // FIXME: if there's a pending image decode & resize, more expensive |
54 if (paint.getMaskFilter()) { | 140 if (paint.getMaskFilter()) { |
55 estimatedCost_ += 300; | 141 estimatedCost_ += 300; |
56 } | 142 } |
57 ++estimatedCost_; | 143 ++estimatedCost_; |
144 | |
145 bool doesCoverCanvas = isFullQuad(draw, | |
146 SkRect::MakeWH(width(), height()), | |
147 rect); | |
148 | |
149 SkXfermode::Mode xferMode; | |
150 SkXfermode::AsMode(paint.getXfermode(), &xferMode); | |
151 | |
152 // This canvas will become transparent if the following holds: | |
153 // - The quad is a full tile quad | |
154 // - We're not in "forced not transparent" mode | |
155 // - Transfer mode is clear (0 color, 0 alpha) | |
156 // | |
157 // If the paint alpha is not 0, or if the transfrer mode is | |
158 // not src, then this canvas will not be transparent. | |
159 // | |
160 // In all other cases, we keep the current transparent value | |
161 if (doesCoverCanvas && | |
162 !isForcedNotTransparent_ && | |
163 xferMode == SkXfermode::kClear_Mode) { | |
164 isTransparent_ = true; | |
165 } | |
166 else if (paint.getAlpha() != 0 || | |
167 xferMode != SkXfermode::kSrc_Mode) { | |
168 isTransparent_ = false; | |
169 } | |
170 | |
171 // This bitmap is solid if and only if the following holds. | |
172 // Note that this might be overly conservative: | |
173 // - We're not in "forced not solid" mode | |
174 // - Paint is solid color | |
175 // - The quad is a full tile quad | |
176 if (!isForcedNotSolid_ && | |
177 isSolidColorPaint(paint) && | |
178 doesCoverCanvas) { | |
179 isSolidColor_ = true; | |
180 color_ = paint.getColor(); | |
181 } | |
182 else { | |
183 isSolidColor_ = false; | |
184 } | |
58 } | 185 } |
59 | 186 |
60 void AnalysisDevice::drawOval(const SkDraw&, const SkRect& oval, | 187 void AnalysisDevice::drawOval(const SkDraw&, const SkRect& oval, |
Justin Novosad
2013/03/05 22:14:02
FWIW: It is trivial to compute whether an oval con
| |
61 const SkPaint& paint) { | 188 const SkPaint& paint) { |
62 ++estimatedCost_; | 189 ++estimatedCost_; |
190 isSolidColor_ = false; | |
191 isTransparent_ = false; | |
63 } | 192 } |
64 | 193 |
65 void AnalysisDevice::drawPath(const SkDraw&, const SkPath& path, | 194 void AnalysisDevice::drawPath(const SkDraw&, const SkPath& path, |
66 const SkPaint& paint, | 195 const SkPaint& paint, |
67 const SkMatrix* prePathMatrix , | 196 const SkMatrix* prePathMatrix , |
68 bool pathIsMutable ) { | 197 bool pathIsMutable ) { |
69 // On Z620, every antialiased path costs us about 300us. | 198 // On Z620, every antialiased path costs us about 300us. |
70 // We've only seen this in practice on filled paths, but | 199 // We've only seen this in practice on filled paths, but |
71 // we expect it to apply to all path stroking modes. | 200 // we expect it to apply to all path stroking modes. |
72 if (paint.getMaskFilter()) { | 201 if (paint.getMaskFilter()) { |
73 estimatedCost_ += 300; | 202 estimatedCost_ += 300; |
74 } | 203 } |
75 ++estimatedCost_; | 204 ++estimatedCost_; |
205 isSolidColor_ = false; | |
206 isTransparent_ = false; | |
76 } | 207 } |
77 | 208 |
78 void AnalysisDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap, | 209 void AnalysisDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap, |
79 const SkIRect* srcRectOrNull, | 210 const SkIRect* srcRectOrNull, |
80 const SkMatrix& matrix, const SkPaint& paint) | 211 const SkMatrix& matrix, const SkPaint& paint) { |
81 { | |
82 ++estimatedCost_; | 212 ++estimatedCost_; |
213 isSolidColor_ = false; | |
214 isTransparent_ = false; | |
83 } | 215 } |
84 | 216 |
85 void AnalysisDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap, | 217 void AnalysisDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap, |
86 int x, int y, const SkPaint& paint) { | 218 int x, int y, const SkPaint& paint) { |
87 ++estimatedCost_; | 219 ++estimatedCost_; |
220 isSolidColor_ = false; | |
221 isTransparent_ = false; | |
88 } | 222 } |
89 | 223 |
90 void AnalysisDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, | 224 void AnalysisDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap&, |
91 const SkRect* srcOrNull, const SkRect& dst, | 225 const SkRect* srcOrNull, const SkRect& dst, |
92 const SkPaint& paint) { | 226 const SkPaint& paint) { |
93 ++estimatedCost_; | 227 ++estimatedCost_; |
228 | |
229 // Call drawRect to determine transparency, | |
230 // but reset solid color to false. | |
231 drawRect(draw, dst, paint); | |
232 isSolidColor_ = false; | |
94 } | 233 } |
95 | 234 |
96 | 235 |
97 void AnalysisDevice::drawText(const SkDraw&, const void* text, size_t len, | 236 void AnalysisDevice::drawText(const SkDraw&, const void* text, size_t len, |
98 SkScalar x, SkScalar y, const SkPaint& paint) | 237 SkScalar x, SkScalar y, const SkPaint& paint) { |
99 { | |
100 ++estimatedCost_; | 238 ++estimatedCost_; |
239 isSolidColor_ = false; | |
240 isTransparent_ = false; | |
101 } | 241 } |
102 | 242 |
103 void AnalysisDevice::drawPosText(const SkDraw& draw, const void* text, size_t le n, | 243 void AnalysisDevice::drawPosText(const SkDraw& draw, const void* text, size_t le n, |
104 const SkScalar pos[], SkScalar constY, | 244 const SkScalar pos[], SkScalar constY, |
105 int scalarsPerPos, const SkPaint& paint) { | 245 int scalarsPerPos, const SkPaint& paint) { |
106 // FIXME: On Z620, every glyph cache miss costs us about 10us. | 246 // FIXME: On Z620, every glyph cache miss costs us about 10us. |
107 // We don't have a good mechanism for predicting glyph cache misses. | 247 // We don't have a good mechanism for predicting glyph cache misses. |
108 ++estimatedCost_; | 248 ++estimatedCost_; |
249 isSolidColor_ = false; | |
250 isTransparent_ = false; | |
109 } | 251 } |
110 | 252 |
111 void AnalysisDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, | 253 void AnalysisDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, |
112 const SkPath& path, const SkMatrix* matrix, | 254 const SkPath& path, const SkMatrix* matrix, |
113 const SkPaint& paint) { | 255 const SkPaint& paint) { |
114 ++estimatedCost_; | 256 ++estimatedCost_; |
257 isSolidColor_ = false; | |
258 isTransparent_ = false; | |
115 } | 259 } |
116 | 260 |
117 #ifdef SK_BUILD_FOR_ANDROID | 261 #ifdef SK_BUILD_FOR_ANDROID |
118 void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw, const void* text, | 262 void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw, const void* text, |
119 size_t len, | 263 size_t len, |
120 const SkPoint pos[], const SkPaint& paint, | 264 const SkPoint pos[], const SkPaint& paint, |
121 const SkPath& path, const SkMatrix* matrix) | 265 const SkPath& path, const SkMatrix* matrix) { |
122 { | |
123 ++estimatedCost_; | 266 ++estimatedCost_; |
267 isSolidColor_ = false; | |
268 isTransparent_ = false; | |
124 } | 269 } |
125 #endif | 270 #endif |
126 | 271 |
127 void AnalysisDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, | 272 void AnalysisDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, |
128 int vertexCount, | 273 int vertexCount, |
129 const SkPoint verts[], const SkPoint texs[], | 274 const SkPoint verts[], const SkPoint texs[], |
130 const SkColor colors[], SkXfermode* xmode, | 275 const SkColor colors[], SkXfermode* xmode, |
131 const uint16_t indices[], int indexCount, | 276 const uint16_t indices[], int indexCount, |
132 const SkPaint& paint) { | 277 const SkPaint& paint) { |
133 ++estimatedCost_; | 278 ++estimatedCost_; |
279 isSolidColor_ = false; | |
280 isTransparent_ = false; | |
134 } | 281 } |
135 | 282 |
136 void AnalysisDevice::drawDevice(const SkDraw&, SkDevice*, int x, int y, | 283 void AnalysisDevice::drawDevice(const SkDraw&, SkDevice*, int x, int y, |
137 const SkPaint&) { | 284 const SkPaint&) { |
138 ++estimatedCost_; | 285 ++estimatedCost_; |
286 isSolidColor_ = false; | |
287 isTransparent_ = false; | |
139 } | 288 } |
140 | 289 |
141 | 290 |
142 | 291 |
143 | |
144 AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device) | 292 AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device) |
145 : INHERITED(device) { | 293 : INHERITED(device) |
146 | 294 , savedLayerStackSize_(0) { |
147 } | 295 } |
148 | 296 |
149 AnalysisCanvas::~AnalysisCanvas() { | 297 AnalysisCanvas::~AnalysisCanvas() { |
150 } | 298 } |
151 | 299 |
152 | 300 |
153 bool AnalysisCanvas::isCheap() const { | 301 bool AnalysisCanvas::isCheap() const { |
154 return getEstimatedCost() < gPictureCostThreshold; | 302 return getEstimatedCost() < gPictureCostThreshold; |
155 } | 303 } |
156 | 304 |
305 bool AnalysisCanvas::getColorIfSolid(SkColor* color) const { | |
306 return (static_cast<AnalysisDevice*>(getDevice()))->getColorIfSolid(color); | |
307 } | |
308 | |
309 bool AnalysisCanvas::isTransparent() const { | |
310 return (static_cast<AnalysisDevice*>(getDevice()))->isTransparent(); | |
311 } | |
312 | |
157 int AnalysisCanvas::getEstimatedCost() const { | 313 int AnalysisCanvas::getEstimatedCost() const { |
158 return (static_cast<AnalysisDevice*>(getDevice()))->getEstimatedCost(); | 314 return (static_cast<AnalysisDevice*>(getDevice()))->getEstimatedCost(); |
159 } | 315 } |
160 | 316 |
161 | |
162 bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, | 317 bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, |
163 bool doAA) { | 318 bool doAA) { |
164 return INHERITED::clipRect(rect, op, doAA); | 319 return INHERITED::clipRect(rect, op, doAA); |
165 } | 320 } |
166 | 321 |
167 bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, | 322 bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, |
168 bool doAA) { | 323 bool doAA) { |
169 return INHERITED::clipRect(path.getBounds(), op, doAA); | 324 return INHERITED::clipRect(path.getBounds(), op, doAA); |
170 } | 325 } |
171 | 326 |
172 bool AnalysisCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, | 327 bool AnalysisCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, |
173 bool doAA) { | 328 bool doAA) { |
174 return INHERITED::clipRect(rrect.getBounds(), op, doAA); | 329 return INHERITED::clipRect(rrect.getBounds(), op, doAA); |
175 } | 330 } |
176 | 331 |
177 int AnalysisCanvas::saveLayer(const SkRect* bounds, const SkPaint*, | 332 int AnalysisCanvas::save(SkCanvas::SaveFlags flags) { |
333 INHERITED::save(flags); | |
334 } | |
335 | |
336 int AnalysisCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, | |
178 SkCanvas::SaveFlags flags) { | 337 SkCanvas::SaveFlags flags) { |
338 // If after we draw to the saved layer, we have to blend with the current | |
339 // layer, then we can conservatively say that the canvas will not be of | |
340 // solid color. | |
341 if ((paint && !isSolidColorPaint(*paint)) || | |
342 (bounds && !bounds->contains( | |
343 SkRect::MakeWH(getDevice()->width(), getDevice()->height())))) | |
344 (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(true); | |
345 | |
346 // If after we draw to the save layer, we have to blend with the current | |
347 // layer using any part of the current layer's alpha, then we can | |
348 // conservatively say that the canvas will not be transparent. | |
349 SkXfermode::Mode xferMode = SkXfermode::kSrc_Mode; | |
350 if (paint) | |
351 SkXfermode::AsMode(paint->getXfermode(), &xferMode); | |
352 if (xferMode != SkXfermode::kSrc_Mode) | |
353 (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(true); | |
354 | |
355 ++savedLayerStackSize_; | |
356 | |
179 // Actually saving a layer here could cause a new bitmap to be created | 357 // Actually saving a layer here could cause a new bitmap to be created |
180 // and real rendering to occur. | 358 // and real rendering to occur. |
181 int count = SkCanvas::save(flags); | 359 int count = INHERITED::save(flags); |
182 if (bounds) { | 360 if (bounds) { |
183 INHERITED::clipRectBounds(bounds, flags, NULL); | 361 INHERITED::clipRectBounds(bounds, flags, NULL); |
184 } | 362 } |
185 return count; | 363 return count; |
186 } | 364 } |
187 | 365 |
366 void AnalysisCanvas::restore() { | |
367 INHERITED::restore(); | |
368 | |
369 if (--savedLayerStackSize_ == 0) { | |
Justin Novosad
2013/03/05 22:14:02
This is overly conservative. You could store the
| |
370 (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(false); | |
371 (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(false); | |
372 } | |
373 } | |
374 | |
188 } // namespace skia | 375 } // namespace skia |
189 | 376 |
190 | 377 |
OLD | NEW |