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/rect_conversions.h"
16 const int kNoLayer = -1;
18 bool IsSolidColorPaint(const SkPaint& paint) {
19 SkXfermode::Mode xfermode;
21 // getXfermode can return a NULL, but that is handled
22 // gracefully by AsMode (NULL turns into kSrcOver mode).
23 SkXfermode::AsMode(paint.getXfermode(), &xfermode);
25 // Paint is solid color if the following holds:
26 // - Alpha is 1.0, style is fill, and there are no special effects
27 // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent
28 // to kSrc if source alpha is 1.0, which is already checked).
29 return (paint.getAlpha() == 255 &&
32 !paint.getMaskFilter() &&
33 !paint.getColorFilter() &&
34 !paint.getImageFilter() &&
35 paint.getStyle() == SkPaint::kFill_Style &&
36 (xfermode == SkXfermode::kSrc_Mode ||
37 xfermode == SkXfermode::kSrcOver_Mode));
40 // Returns true if the specified drawn_rect will cover the entire canvas, and
41 // that the canvas is not clipped (i.e. it covers ALL of the canvas).
42 bool IsFullQuad(SkCanvas* canvas, const SkRect& drawn_rect) {
43 if (!canvas->isClipRect())
47 canvas->getClipDeviceBounds(&clip_irect);
48 // if the clip is smaller than the canvas, we're partly clipped, so abort.
49 if (!clip_irect.contains(SkIRect::MakeSize(canvas->getDeviceSize())))
52 const SkMatrix& matrix = canvas->getTotalMatrix();
53 // If the transform results in a non-axis aligned
54 // rect, then be conservative and return false.
55 if (!matrix.rectStaysRect())
59 matrix.mapRect(&device_rect, drawn_rect);
61 clip_rect.set(clip_irect);
62 return device_rect.contains(clip_rect);
69 void AnalysisCanvas::SetForceNotSolid(bool flag) {
70 is_forced_not_solid_ = flag;
71 if (is_forced_not_solid_)
72 is_solid_color_ = false;
75 void AnalysisCanvas::SetForceNotTransparent(bool flag) {
76 is_forced_not_transparent_ = flag;
77 if (is_forced_not_transparent_)
78 is_transparent_ = false;
81 void AnalysisCanvas::clear(SkColor color) {
82 is_transparent_ = (!is_forced_not_transparent_ && SkColorGetA(color) == 0);
84 if (!is_forced_not_solid_ && SkColorGetA(color) == 255) {
85 is_solid_color_ = true;
88 is_solid_color_ = false;
92 void AnalysisCanvas::drawPaint(const SkPaint& paint) {
93 // This check is in SkCanvas::drawPaint(), and some of our unittests rely on
94 // on this, so we reproduce it here.
98 is_solid_color_ = false;
99 is_transparent_ = false;
103 void AnalysisCanvas::drawPoints(SkCanvas::PointMode mode,
105 const SkPoint points[],
106 const SkPaint& paint) {
107 is_solid_color_ = false;
108 is_transparent_ = false;
112 void AnalysisCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
113 // This recreates the early-exit logic in SkCanvas.cpp, which aborts early
114 // if the paint will "draw nothing".
115 if (paint.nothingToDraw())
118 bool does_cover_canvas = IsFullQuad(this, rect);
120 SkXfermode::Mode xfermode;
121 SkXfermode::AsMode(paint.getXfermode(), &xfermode);
123 // This canvas will become transparent if the following holds:
124 // - The quad is a full tile quad
125 // - We're not in "forced not transparent" mode
126 // - Transfer mode is clear (0 color, 0 alpha)
128 // If the paint alpha is not 0, or if the transfrer mode is
129 // not src, then this canvas will not be transparent.
131 // In all other cases, we keep the current transparent value
132 if (does_cover_canvas &&
133 !is_forced_not_transparent_ &&
134 xfermode == SkXfermode::kClear_Mode) {
135 is_transparent_ = true;
136 } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
137 is_transparent_ = false;
140 // This bitmap is solid if and only if the following holds.
141 // Note that this might be overly conservative:
142 // - We're not in "forced not solid" mode
143 // - Paint is solid color
144 // - The quad is a full tile quad
145 if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
146 is_solid_color_ = true;
147 color_ = paint.getColor();
149 is_solid_color_ = false;
154 void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
155 is_solid_color_ = false;
156 is_transparent_ = false;
160 void AnalysisCanvas::drawRRect(const SkRRect& rr, const SkPaint& paint) {
161 // This should add the SkRRect to an SkPath, and call
162 // drawPath, but since drawPath ignores the SkPath, just
163 // do the same work here.
164 is_solid_color_ = false;
165 is_transparent_ = false;
169 void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
170 is_solid_color_ = false;
171 is_transparent_ = false;
175 void AnalysisCanvas::drawBitmap(const SkBitmap& bitmap,
179 is_solid_color_ = false;
180 is_transparent_ = false;
184 void AnalysisCanvas::drawBitmapRectToRect(const SkBitmap&,
187 const SkPaint* paint,
188 DrawBitmapRectFlags flags) {
189 // Call drawRect to determine transparency,
190 // but reset solid color to false.
194 drawRect(dst, *paint);
195 is_solid_color_ = false;
199 void AnalysisCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
200 const SkMatrix& matrix,
201 const SkPaint* paint) {
202 is_solid_color_ = false;
203 is_transparent_ = false;
207 void AnalysisCanvas::drawBitmapNine(const SkBitmap& bitmap,
208 const SkIRect& center,
210 const SkPaint* paint) {
211 is_solid_color_ = false;
212 is_transparent_ = false;
216 void AnalysisCanvas::drawSprite(const SkBitmap& bitmap,
219 const SkPaint* paint) {
220 is_solid_color_ = false;
221 is_transparent_ = false;
225 void AnalysisCanvas::onDrawText(const void* text,
229 const SkPaint& paint) {
230 is_solid_color_ = false;
231 is_transparent_ = false;
235 void AnalysisCanvas::onDrawPosText(const void* text,
238 const SkPaint& paint) {
239 is_solid_color_ = false;
240 is_transparent_ = false;
244 void AnalysisCanvas::onDrawPosTextH(const void* text,
246 const SkScalar xpos[],
248 const SkPaint& paint) {
249 is_solid_color_ = false;
250 is_transparent_ = false;
254 void AnalysisCanvas::onDrawTextOnPath(const void* text,
257 const SkMatrix* matrix,
258 const SkPaint& paint) {
259 is_solid_color_ = false;
260 is_transparent_ = false;
264 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer,
265 const SkRRect& inner,
266 const SkPaint& paint) {
267 is_solid_color_ = false;
268 is_transparent_ = false;
272 void AnalysisCanvas::drawVertices(SkCanvas::VertexMode,
274 const SkPoint verts[],
275 const SkPoint texs[],
276 const SkColor colors[],
278 const uint16_t indices[],
280 const SkPaint& paint) {
281 is_solid_color_ = false;
282 is_transparent_ = false;
286 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
288 static SkBitmap MakeEmptyBitmap(int width, int height) {
290 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
294 AnalysisCanvas::AnalysisCanvas(int width, int height)
295 : INHERITED(MakeEmptyBitmap(width, height)),
296 saved_stack_size_(0),
297 force_not_solid_stack_level_(kNoLayer),
298 force_not_transparent_stack_level_(kNoLayer),
299 is_forced_not_solid_(false),
300 is_forced_not_transparent_(false),
301 is_solid_color_(true),
302 is_transparent_(true),
305 AnalysisCanvas::~AnalysisCanvas() {}
307 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
308 if (is_transparent_) {
309 *color = SK_ColorTRANSPARENT;
312 if (is_solid_color_) {
319 bool AnalysisCanvas::abortDrawing() {
320 // Early out as soon as we have more than one draw op.
321 // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to
322 // balance the amount of time we spend analyzing vs how many tiles would be
323 // solid if the number was higher.
324 if (draw_op_count_ > 1) {
325 // We have to reset solid/transparent state to false since we don't
326 // know whether consequent operations will make this false.
327 is_solid_color_ = false;
328 is_transparent_ = false;
334 void AnalysisCanvas::onClipRect(const SkRect& rect, SkRegion::Op op,
335 ClipEdgeStyle edge_style) {
337 INHERITED::onClipRect(rect, op, edge_style);
340 void AnalysisCanvas::onClipPath(const SkPath& path, SkRegion::Op op,
341 ClipEdgeStyle edge_style) {
342 // clipPaths can make our calls to IsFullQuad invalid (ie have false
343 // positives). As a precaution, force the setting to be non-solid
344 // and non-transparent until we pop this
345 if (force_not_solid_stack_level_ == kNoLayer) {
346 force_not_solid_stack_level_ = saved_stack_size_;
347 SetForceNotSolid(true);
349 if (force_not_transparent_stack_level_ == kNoLayer) {
350 force_not_transparent_stack_level_ = saved_stack_size_;
351 SetForceNotTransparent(true);
354 INHERITED::onClipRect(path.getBounds(), op, edge_style);
357 void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
359 ClipEdgeStyle edge_style) {
360 // clipRRect can make our calls to IsFullQuad invalid (ie have false
361 // positives). As a precaution, force the setting to be non-solid
362 // and non-transparent until we pop this
363 if (force_not_solid_stack_level_ == kNoLayer) {
364 force_not_solid_stack_level_ = saved_stack_size_;
365 SetForceNotSolid(true);
367 if (force_not_transparent_stack_level_ == kNoLayer) {
368 force_not_transparent_stack_level_ = saved_stack_size_;
369 SetForceNotTransparent(true);
372 INHERITED::onClipRect(rrect.getBounds(), op, edge_style);
375 void AnalysisCanvas::willSave() {
377 INHERITED::willSave();
380 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
381 const SkRect* bounds,
382 const SkPaint* paint,
383 SkCanvas::SaveFlags flags) {
387 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
388 SkRect canvas_bounds;
389 canvas_bounds.set(canvas_ibounds);
391 // If after we draw to the saved layer, we have to blend with the current
392 // layer, then we can conservatively say that the canvas will not be of
394 if ((paint && !IsSolidColorPaint(*paint)) ||
395 (bounds && !bounds->contains(canvas_bounds))) {
396 if (force_not_solid_stack_level_ == kNoLayer) {
397 force_not_solid_stack_level_ = saved_stack_size_;
398 SetForceNotSolid(true);
402 // If after we draw to the save layer, we have to blend with the current
403 // layer using any part of the current layer's alpha, then we can
404 // conservatively say that the canvas will not be transparent.
405 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
407 SkXfermode::AsMode(paint->getXfermode(), &xfermode);
408 if (xfermode != SkXfermode::kSrc_Mode) {
409 if (force_not_transparent_stack_level_ == kNoLayer) {
410 force_not_transparent_stack_level_ = saved_stack_size_;
411 SetForceNotTransparent(true);
415 INHERITED::willSaveLayer(bounds, paint, flags);
416 // Actually saving a layer here could cause a new bitmap to be created
417 // and real rendering to occur.
418 return kNoLayer_SaveLayerStrategy;
421 void AnalysisCanvas::willRestore() {
422 DCHECK(saved_stack_size_);
423 if (saved_stack_size_) {
425 if (saved_stack_size_ < force_not_solid_stack_level_) {
426 SetForceNotSolid(false);
427 force_not_solid_stack_level_ = kNoLayer;
429 if (saved_stack_size_ < force_not_transparent_stack_level_) {
430 SetForceNotTransparent(false);
431 force_not_transparent_stack_level_ = kNoLayer;
435 INHERITED::willRestore();