OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/logging.h" | |
6 #include "base/trace_event/trace_event.h" | |
7 #include "skia/ext/analysis_canvas.h" | |
8 #include "third_party/skia/include/core/SkDraw.h" | |
9 #include "third_party/skia/include/core/SkPath.h" | |
10 #include "third_party/skia/include/core/SkRRect.h" | |
11 #include "third_party/skia/include/core/SkShader.h" | |
12 #include "third_party/skia/src/core/SkRasterClip.h" | |
13 | |
14 namespace { | |
15 | |
16 const int kNoLayer = -1; | |
17 | |
18 bool ActsLikeClear(SkXfermode::Mode mode, unsigned src_alpha) { | |
19 switch (mode) { | |
20 case SkXfermode::kClear_Mode: | |
21 return true; | |
22 case SkXfermode::kSrc_Mode: | |
23 case SkXfermode::kSrcIn_Mode: | |
24 case SkXfermode::kDstIn_Mode: | |
25 case SkXfermode::kSrcOut_Mode: | |
26 case SkXfermode::kDstATop_Mode: | |
27 return src_alpha == 0; | |
28 case SkXfermode::kDstOut_Mode: | |
29 return src_alpha == 0xFF; | |
30 default: | |
31 return false; | |
32 } | |
33 } | |
34 | |
35 bool IsSolidColorPaint(const SkPaint& paint) { | |
36 SkXfermode::Mode xfermode; | |
37 | |
38 // getXfermode can return a NULL, but that is handled | |
39 // gracefully by AsMode (NULL turns into kSrcOver mode). | |
40 SkXfermode::AsMode(paint.getXfermode(), &xfermode); | |
41 | |
42 // Paint is solid color if the following holds: | |
43 // - Alpha is 1.0, style is fill, and there are no special effects | |
44 // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent | |
45 // to kSrc if source alpha is 1.0, which is already checked). | |
46 return (paint.getAlpha() == 255 && | |
47 !paint.getShader() && | |
48 !paint.getLooper() && | |
49 !paint.getMaskFilter() && | |
50 !paint.getColorFilter() && | |
51 !paint.getImageFilter() && | |
52 paint.getStyle() == SkPaint::kFill_Style && | |
53 (xfermode == SkXfermode::kSrc_Mode || | |
54 xfermode == SkXfermode::kSrcOver_Mode)); | |
55 } | |
56 | |
57 // Returns true if the specified drawn_rect will cover the entire canvas, and | |
58 // that the canvas is not clipped (i.e. it covers ALL of the canvas). | |
59 bool IsFullQuad(SkCanvas* canvas, const SkRect& drawn_rect) { | |
60 if (!canvas->isClipRect()) | |
61 return false; | |
62 | |
63 SkIRect clip_irect; | |
64 canvas->getClipDeviceBounds(&clip_irect); | |
65 // if the clip is smaller than the canvas, we're partly clipped, so abort. | |
66 if (!clip_irect.contains(SkIRect::MakeSize(canvas->getDeviceSize()))) | |
67 return false; | |
68 | |
69 const SkMatrix& matrix = canvas->getTotalMatrix(); | |
70 // If the transform results in a non-axis aligned | |
71 // rect, then be conservative and return false. | |
72 if (!matrix.rectStaysRect()) | |
73 return false; | |
74 | |
75 SkRect device_rect; | |
76 matrix.mapRect(&device_rect, drawn_rect); | |
77 SkRect clip_rect; | |
78 clip_rect.set(clip_irect); | |
79 return device_rect.contains(clip_rect); | |
80 } | |
81 | |
82 } // namespace | |
83 | |
84 namespace skia { | |
85 | |
86 void AnalysisCanvas::SetForceNotSolid(bool flag) { | |
87 is_forced_not_solid_ = flag; | |
88 if (is_forced_not_solid_) | |
89 is_solid_color_ = false; | |
90 } | |
91 | |
92 void AnalysisCanvas::SetForceNotTransparent(bool flag) { | |
93 is_forced_not_transparent_ = flag; | |
94 if (is_forced_not_transparent_) | |
95 is_transparent_ = false; | |
96 } | |
97 | |
98 void AnalysisCanvas::onDrawPaint(const SkPaint& paint) { | |
99 SkRect rect; | |
100 getClipBounds(&rect); | |
101 drawRect(rect, paint); | |
102 } | |
103 | |
104 void AnalysisCanvas::onDrawPoints(SkCanvas::PointMode mode, | |
105 size_t count, | |
106 const SkPoint points[], | |
107 const SkPaint& paint) { | |
108 is_solid_color_ = false; | |
109 is_transparent_ = false; | |
110 ++draw_op_count_; | |
111 } | |
112 | |
113 void AnalysisCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { | |
114 // This recreates the early-exit logic in SkCanvas.cpp. | |
115 SkRect scratch; | |
116 if (paint.canComputeFastBounds() && | |
117 quickReject(paint.computeFastBounds(rect, &scratch))) { | |
118 return; | |
119 } | |
120 | |
121 // An extra no-op check SkCanvas.cpp doesn't do. | |
122 if (paint.nothingToDraw()) | |
123 return; | |
124 | |
125 bool does_cover_canvas = IsFullQuad(this, rect); | |
126 | |
127 SkXfermode::Mode xfermode; | |
128 SkXfermode::AsMode(paint.getXfermode(), &xfermode); | |
129 | |
130 // This canvas will become transparent if the following holds: | |
131 // - The quad is a full tile quad | |
132 // - We're not in "forced not transparent" mode | |
133 // - Transfer mode is clear (0 color, 0 alpha) | |
134 // | |
135 // If the paint alpha is not 0, or if the transfrer mode is | |
136 // not src, then this canvas will not be transparent. | |
137 // | |
138 // In all other cases, we keep the current transparent value | |
139 if (does_cover_canvas && | |
140 !is_forced_not_transparent_ && | |
141 ActsLikeClear(xfermode, paint.getAlpha())) { | |
142 is_transparent_ = true; | |
143 } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) { | |
144 is_transparent_ = false; | |
145 } | |
146 | |
147 // This bitmap is solid if and only if the following holds. | |
148 // Note that this might be overly conservative: | |
149 // - We're not in "forced not solid" mode | |
150 // - Paint is solid color | |
151 // - The quad is a full tile quad | |
152 if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) { | |
153 is_solid_color_ = true; | |
154 color_ = paint.getColor(); | |
155 } else { | |
156 is_solid_color_ = false; | |
157 } | |
158 ++draw_op_count_; | |
159 } | |
160 | |
161 void AnalysisCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) { | |
162 is_solid_color_ = false; | |
163 is_transparent_ = false; | |
164 ++draw_op_count_; | |
165 } | |
166 | |
167 void AnalysisCanvas::onDrawRRect(const SkRRect& rr, const SkPaint& paint) { | |
168 // This should add the SkRRect to an SkPath, and call | |
169 // drawPath, but since drawPath ignores the SkPath, just | |
170 // do the same work here. | |
171 is_solid_color_ = false; | |
172 is_transparent_ = false; | |
173 ++draw_op_count_; | |
174 } | |
175 | |
176 void AnalysisCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { | |
177 is_solid_color_ = false; | |
178 is_transparent_ = false; | |
179 ++draw_op_count_; | |
180 } | |
181 | |
182 void AnalysisCanvas::onDrawBitmap(const SkBitmap& bitmap, | |
183 SkScalar left, | |
184 SkScalar top, | |
185 const SkPaint*) { | |
186 is_solid_color_ = false; | |
187 is_transparent_ = false; | |
188 ++draw_op_count_; | |
189 } | |
190 | |
191 void AnalysisCanvas::onDrawBitmapRect(const SkBitmap&, | |
192 const SkRect* src, | |
193 const SkRect& dst, | |
194 const SkPaint* paint, | |
195 SrcRectConstraint flags) { | |
196 // Call drawRect to determine transparency, | |
197 // but reset solid color to false. | |
198 SkPaint tmpPaint; | |
199 if (!paint) | |
200 paint = &tmpPaint; | |
201 drawRect(dst, *paint); | |
202 is_solid_color_ = false; | |
203 ++draw_op_count_; | |
204 } | |
205 | |
206 void AnalysisCanvas::onDrawBitmapNine(const SkBitmap& bitmap, | |
207 const SkIRect& center, | |
208 const SkRect& dst, | |
209 const SkPaint* paint) { | |
210 is_solid_color_ = false; | |
211 is_transparent_ = false; | |
212 ++draw_op_count_; | |
213 } | |
214 | |
215 void AnalysisCanvas::onDrawSprite(const SkBitmap& bitmap, | |
216 int left, | |
217 int top, | |
218 const SkPaint* paint) { | |
219 is_solid_color_ = false; | |
220 is_transparent_ = false; | |
221 ++draw_op_count_; | |
222 } | |
223 | |
224 void AnalysisCanvas::onDrawText(const void* text, | |
225 size_t len, | |
226 SkScalar x, | |
227 SkScalar y, | |
228 const SkPaint& paint) { | |
229 is_solid_color_ = false; | |
230 is_transparent_ = false; | |
231 ++draw_op_count_; | |
232 } | |
233 | |
234 void AnalysisCanvas::onDrawPosText(const void* text, | |
235 size_t byteLength, | |
236 const SkPoint pos[], | |
237 const SkPaint& paint) { | |
238 is_solid_color_ = false; | |
239 is_transparent_ = false; | |
240 ++draw_op_count_; | |
241 } | |
242 | |
243 void AnalysisCanvas::onDrawPosTextH(const void* text, | |
244 size_t byteLength, | |
245 const SkScalar xpos[], | |
246 SkScalar constY, | |
247 const SkPaint& paint) { | |
248 is_solid_color_ = false; | |
249 is_transparent_ = false; | |
250 ++draw_op_count_; | |
251 } | |
252 | |
253 void AnalysisCanvas::onDrawTextOnPath(const void* text, | |
254 size_t len, | |
255 const SkPath& path, | |
256 const SkMatrix* matrix, | |
257 const SkPaint& paint) { | |
258 is_solid_color_ = false; | |
259 is_transparent_ = false; | |
260 ++draw_op_count_; | |
261 } | |
262 | |
263 void AnalysisCanvas::onDrawTextBlob(const SkTextBlob* blob, | |
264 SkScalar x, | |
265 SkScalar y, | |
266 const SkPaint &paint) { | |
267 is_solid_color_ = false; | |
268 is_transparent_ = false; | |
269 ++draw_op_count_; | |
270 } | |
271 | |
272 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer, | |
273 const SkRRect& inner, | |
274 const SkPaint& paint) { | |
275 is_solid_color_ = false; | |
276 is_transparent_ = false; | |
277 ++draw_op_count_; | |
278 } | |
279 | |
280 void AnalysisCanvas::onDrawVertices(SkCanvas::VertexMode, | |
281 int vertex_count, | |
282 const SkPoint verts[], | |
283 const SkPoint texs[], | |
284 const SkColor colors[], | |
285 SkXfermode* xmode, | |
286 const uint16_t indices[], | |
287 int index_count, | |
288 const SkPaint& paint) { | |
289 is_solid_color_ = false; | |
290 is_transparent_ = false; | |
291 ++draw_op_count_; | |
292 } | |
293 | |
294 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed | |
295 // by any pixels | |
296 static SkBitmap MakeEmptyBitmap(int width, int height) { | |
297 SkBitmap bitmap; | |
298 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height)); | |
299 return bitmap; | |
300 } | |
301 | |
302 AnalysisCanvas::AnalysisCanvas(int width, int height) | |
303 : INHERITED(MakeEmptyBitmap(width, height)), | |
304 saved_stack_size_(0), | |
305 force_not_solid_stack_level_(kNoLayer), | |
306 force_not_transparent_stack_level_(kNoLayer), | |
307 is_forced_not_solid_(false), | |
308 is_forced_not_transparent_(false), | |
309 is_solid_color_(true), | |
310 color_(SK_ColorTRANSPARENT), | |
311 is_transparent_(true), | |
312 draw_op_count_(0) { | |
313 } | |
314 | |
315 AnalysisCanvas::~AnalysisCanvas() {} | |
316 | |
317 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const { | |
318 if (is_transparent_) { | |
319 *color = SK_ColorTRANSPARENT; | |
320 return true; | |
321 } | |
322 if (is_solid_color_) { | |
323 *color = color_; | |
324 return true; | |
325 } | |
326 return false; | |
327 } | |
328 | |
329 bool AnalysisCanvas::abort() { | |
330 // Early out as soon as we have more than one draw op. | |
331 // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to | |
332 // balance the amount of time we spend analyzing vs how many tiles would be | |
333 // solid if the number was higher. | |
334 if (draw_op_count_ > 1) { | |
335 // We have to reset solid/transparent state to false since we don't | |
336 // know whether consequent operations will make this false. | |
337 is_solid_color_ = false; | |
338 is_transparent_ = false; | |
339 return true; | |
340 } | |
341 return false; | |
342 } | |
343 | |
344 void AnalysisCanvas::OnComplexClip() { | |
345 // complex clips can make our calls to IsFullQuad invalid (ie have false | |
346 // positives). As a precaution, force the setting to be non-solid | |
347 // and non-transparent until we pop this | |
348 if (force_not_solid_stack_level_ == kNoLayer) { | |
349 force_not_solid_stack_level_ = saved_stack_size_; | |
350 SetForceNotSolid(true); | |
351 } | |
352 if (force_not_transparent_stack_level_ == kNoLayer) { | |
353 force_not_transparent_stack_level_ = saved_stack_size_; | |
354 SetForceNotTransparent(true); | |
355 } | |
356 } | |
357 | |
358 void AnalysisCanvas::onClipRect(const SkRect& rect, | |
359 SkRegion::Op op, | |
360 ClipEdgeStyle edge_style) { | |
361 INHERITED::onClipRect(rect, op, edge_style); | |
362 } | |
363 | |
364 void AnalysisCanvas::onClipPath(const SkPath& path, | |
365 SkRegion::Op op, | |
366 ClipEdgeStyle edge_style) { | |
367 OnComplexClip(); | |
368 INHERITED::onClipRect(path.getBounds(), op, edge_style); | |
369 } | |
370 | |
371 void AnalysisCanvas::onClipRRect(const SkRRect& rrect, | |
372 SkRegion::Op op, | |
373 ClipEdgeStyle edge_style) { | |
374 OnComplexClip(); | |
375 INHERITED::onClipRect(rrect.getBounds(), op, edge_style); | |
376 } | |
377 | |
378 void AnalysisCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) { | |
379 const ClipEdgeStyle edge_style = kHard_ClipEdgeStyle; | |
380 if (deviceRgn.isRect()) { | |
381 onClipRect(SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style); | |
382 return; | |
383 } | |
384 OnComplexClip(); | |
385 INHERITED::onClipRect( | |
386 SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style); | |
387 } | |
388 | |
389 void AnalysisCanvas::willSave() { | |
390 ++saved_stack_size_; | |
391 INHERITED::willSave(); | |
392 } | |
393 | |
394 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer( | |
395 const SkRect* bounds, | |
396 const SkPaint* paint, | |
397 SkCanvas::SaveFlags flags) { | |
398 | |
399 ++saved_stack_size_; | |
400 | |
401 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize()); | |
402 SkRect canvas_bounds; | |
403 canvas_bounds.set(canvas_ibounds); | |
404 | |
405 // If after we draw to the saved layer, we have to blend with the current | |
406 // layer, then we can conservatively say that the canvas will not be of | |
407 // solid color. | |
408 if ((paint && !IsSolidColorPaint(*paint)) || | |
409 (bounds && !bounds->contains(canvas_bounds))) { | |
410 if (force_not_solid_stack_level_ == kNoLayer) { | |
411 force_not_solid_stack_level_ = saved_stack_size_; | |
412 SetForceNotSolid(true); | |
413 } | |
414 } | |
415 | |
416 // If after we draw to the save layer, we have to blend with the current | |
417 // layer using any part of the current layer's alpha, then we can | |
418 // conservatively say that the canvas will not be transparent. | |
419 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode; | |
420 if (paint) | |
421 SkXfermode::AsMode(paint->getXfermode(), &xfermode); | |
422 if (xfermode != SkXfermode::kDst_Mode) { | |
423 if (force_not_transparent_stack_level_ == kNoLayer) { | |
424 force_not_transparent_stack_level_ = saved_stack_size_; | |
425 SetForceNotTransparent(true); | |
426 } | |
427 } | |
428 | |
429 INHERITED::willSaveLayer(bounds, paint, flags); | |
430 // Actually saving a layer here could cause a new bitmap to be created | |
431 // and real rendering to occur. | |
432 return kNoLayer_SaveLayerStrategy; | |
433 } | |
434 | |
435 void AnalysisCanvas::willRestore() { | |
436 DCHECK(saved_stack_size_); | |
437 if (saved_stack_size_) { | |
438 --saved_stack_size_; | |
439 if (saved_stack_size_ < force_not_solid_stack_level_) { | |
440 SetForceNotSolid(false); | |
441 force_not_solid_stack_level_ = kNoLayer; | |
442 } | |
443 if (saved_stack_size_ < force_not_transparent_stack_level_) { | |
444 SetForceNotTransparent(false); | |
445 force_not_transparent_stack_level_ = kNoLayer; | |
446 } | |
447 } | |
448 | |
449 INHERITED::willRestore(); | |
450 } | |
451 | |
452 } // namespace skia | |
453 | |
454 | |
OLD | NEW |