Chromecast ATV: distinguish actual shutdown vs. lifecycle from sleep.
[chromium-blink-merge.git] / skia / ext / analysis_canvas.cc
blobeefd8f3c516bb5082d2efe84d723007c2a0336e2
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.
5 #include "base/debug/trace_event.h"
6 #include "base/logging.h"
7 #include "skia/ext/analysis_canvas.h"
8 #include "third_party/skia/include/core/SkDraw.h"
9 #include "third_party/skia/include/core/SkRRect.h"
10 #include "third_party/skia/include/core/SkShader.h"
11 #include "third_party/skia/src/core/SkRasterClip.h"
12 #include "ui/gfx/geometry/rect_conversions.h"
14 namespace {
16 const int kNoLayer = -1;
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;
35 bool IsSolidColorPaint(const SkPaint& paint) {
36 SkXfermode::Mode xfermode;
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);
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));
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;
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;
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;
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);
82 } // namespace
84 namespace skia {
86 void AnalysisCanvas::SetForceNotSolid(bool flag) {
87 is_forced_not_solid_ = flag;
88 if (is_forced_not_solid_)
89 is_solid_color_ = false;
92 void AnalysisCanvas::SetForceNotTransparent(bool flag) {
93 is_forced_not_transparent_ = flag;
94 if (is_forced_not_transparent_)
95 is_transparent_ = false;
98 void AnalysisCanvas::onDrawPaint(const SkPaint& paint) {
99 SkRect rect;
100 getClipBounds(&rect);
101 drawRect(rect, paint);
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_;
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;
121 // An extra no-op check SkCanvas.cpp doesn't do.
122 if (paint.nothingToDraw())
123 return;
125 bool does_cover_canvas = IsFullQuad(this, rect);
127 SkXfermode::Mode xfermode;
128 SkXfermode::AsMode(paint.getXfermode(), &xfermode);
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)
135 // If the paint alpha is not 0, or if the transfrer mode is
136 // not src, then this canvas will not be transparent.
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;
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;
158 ++draw_op_count_;
161 void AnalysisCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
162 is_solid_color_ = false;
163 is_transparent_ = false;
164 ++draw_op_count_;
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_;
176 void AnalysisCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
177 is_solid_color_ = false;
178 is_transparent_ = false;
179 ++draw_op_count_;
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_;
191 void AnalysisCanvas::onDrawBitmapRect(const SkBitmap&,
192 const SkRect* src,
193 const SkRect& dst,
194 const SkPaint* paint,
195 DrawBitmapRectFlags 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_;
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_;
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_;
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_;
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_;
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_;
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_;
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_;
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_;
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_;
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;
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) {
315 AnalysisCanvas::~AnalysisCanvas() {}
317 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
318 if (is_transparent_) {
319 *color = SK_ColorTRANSPARENT;
320 return true;
322 if (is_solid_color_) {
323 *color = color_;
324 return true;
326 return false;
329 bool AnalysisCanvas::abortDrawing() {
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;
341 return false;
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);
352 if (force_not_transparent_stack_level_ == kNoLayer) {
353 force_not_transparent_stack_level_ = saved_stack_size_;
354 SetForceNotTransparent(true);
358 void AnalysisCanvas::onClipRect(const SkRect& rect,
359 SkRegion::Op op,
360 ClipEdgeStyle edge_style) {
361 INHERITED::onClipRect(rect, op, edge_style);
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);
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);
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;
384 OnComplexClip();
385 INHERITED::onClipRect(
386 SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
389 void AnalysisCanvas::willSave() {
390 ++saved_stack_size_;
391 INHERITED::willSave();
394 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
395 const SkRect* bounds,
396 const SkPaint* paint,
397 SkCanvas::SaveFlags flags) {
399 ++saved_stack_size_;
401 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
402 SkRect canvas_bounds;
403 canvas_bounds.set(canvas_ibounds);
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);
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::kSrc_Mode) {
423 if (force_not_transparent_stack_level_ == kNoLayer) {
424 force_not_transparent_stack_level_ = saved_stack_size_;
425 SetForceNotTransparent(true);
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;
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;
443 if (saved_stack_size_ < force_not_transparent_stack_level_) {
444 SetForceNotTransparent(false);
445 force_not_transparent_stack_level_ = kNoLayer;
449 INHERITED::willRestore();
452 } // namespace skia