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.
115 if (paint
.canComputeFastBounds() &&
116 quickReject(paint
.computeFastBounds(rect
, &scratch
))) {
120 // An extra no-op check SkCanvas.cpp doesn't do.
121 if (paint
.nothingToDraw())
124 bool does_cover_canvas
= IsFullQuad(this, rect
);
126 SkXfermode::Mode xfermode
;
127 SkXfermode::AsMode(paint
.getXfermode(), &xfermode
);
129 // This canvas will become transparent if the following holds:
130 // - The quad is a full tile quad
131 // - We're not in "forced not transparent" mode
132 // - Transfer mode is clear (0 color, 0 alpha)
134 // If the paint alpha is not 0, or if the transfrer mode is
135 // not src, then this canvas will not be transparent.
137 // In all other cases, we keep the current transparent value
138 if (does_cover_canvas
&&
139 !is_forced_not_transparent_
&&
140 xfermode
== SkXfermode::kClear_Mode
) {
141 is_transparent_
= true;
142 } else if (paint
.getAlpha() != 0 || xfermode
!= SkXfermode::kSrc_Mode
) {
143 is_transparent_
= false;
146 // This bitmap is solid if and only if the following holds.
147 // Note that this might be overly conservative:
148 // - We're not in "forced not solid" mode
149 // - Paint is solid color
150 // - The quad is a full tile quad
151 if (!is_forced_not_solid_
&& IsSolidColorPaint(paint
) && does_cover_canvas
) {
152 is_solid_color_
= true;
153 color_
= paint
.getColor();
155 is_solid_color_
= false;
160 void AnalysisCanvas::drawOval(const SkRect
& oval
, const SkPaint
& paint
) {
161 is_solid_color_
= false;
162 is_transparent_
= false;
166 void AnalysisCanvas::drawRRect(const SkRRect
& rr
, const SkPaint
& paint
) {
167 // This should add the SkRRect to an SkPath, and call
168 // drawPath, but since drawPath ignores the SkPath, just
169 // do the same work here.
170 is_solid_color_
= false;
171 is_transparent_
= false;
175 void AnalysisCanvas::drawPath(const SkPath
& path
, const SkPaint
& paint
) {
176 is_solid_color_
= false;
177 is_transparent_
= false;
181 void AnalysisCanvas::drawBitmap(const SkBitmap
& bitmap
,
185 is_solid_color_
= false;
186 is_transparent_
= false;
190 void AnalysisCanvas::drawBitmapRectToRect(const SkBitmap
&,
193 const SkPaint
* paint
,
194 DrawBitmapRectFlags flags
) {
195 // Call drawRect to determine transparency,
196 // but reset solid color to false.
200 drawRect(dst
, *paint
);
201 is_solid_color_
= false;
205 void AnalysisCanvas::drawBitmapMatrix(const SkBitmap
& bitmap
,
206 const SkMatrix
& matrix
,
207 const SkPaint
* paint
) {
208 is_solid_color_
= false;
209 is_transparent_
= false;
213 void AnalysisCanvas::drawBitmapNine(const SkBitmap
& bitmap
,
214 const SkIRect
& center
,
216 const SkPaint
* paint
) {
217 is_solid_color_
= false;
218 is_transparent_
= false;
222 void AnalysisCanvas::drawSprite(const SkBitmap
& bitmap
,
225 const SkPaint
* paint
) {
226 is_solid_color_
= false;
227 is_transparent_
= false;
231 void AnalysisCanvas::onDrawText(const void* text
,
235 const SkPaint
& paint
) {
236 is_solid_color_
= false;
237 is_transparent_
= false;
241 void AnalysisCanvas::onDrawPosText(const void* text
,
244 const SkPaint
& paint
) {
245 is_solid_color_
= false;
246 is_transparent_
= false;
250 void AnalysisCanvas::onDrawPosTextH(const void* text
,
252 const SkScalar xpos
[],
254 const SkPaint
& paint
) {
255 is_solid_color_
= false;
256 is_transparent_
= false;
260 void AnalysisCanvas::onDrawTextOnPath(const void* text
,
263 const SkMatrix
* matrix
,
264 const SkPaint
& paint
) {
265 is_solid_color_
= false;
266 is_transparent_
= false;
270 void AnalysisCanvas::onDrawDRRect(const SkRRect
& outer
,
271 const SkRRect
& inner
,
272 const SkPaint
& paint
) {
273 is_solid_color_
= false;
274 is_transparent_
= false;
278 void AnalysisCanvas::drawVertices(SkCanvas::VertexMode
,
280 const SkPoint verts
[],
281 const SkPoint texs
[],
282 const SkColor colors
[],
284 const uint16_t indices
[],
286 const SkPaint
& paint
) {
287 is_solid_color_
= false;
288 is_transparent_
= false;
292 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
294 static SkBitmap
MakeEmptyBitmap(int width
, int height
) {
296 bitmap
.setInfo(SkImageInfo::MakeUnknown(width
, height
));
300 AnalysisCanvas::AnalysisCanvas(int width
, int height
)
301 : INHERITED(MakeEmptyBitmap(width
, height
)),
302 saved_stack_size_(0),
303 force_not_solid_stack_level_(kNoLayer
),
304 force_not_transparent_stack_level_(kNoLayer
),
305 is_forced_not_solid_(false),
306 is_forced_not_transparent_(false),
307 is_solid_color_(true),
308 is_transparent_(true),
311 AnalysisCanvas::~AnalysisCanvas() {}
313 bool AnalysisCanvas::GetColorIfSolid(SkColor
* color
) const {
314 if (is_transparent_
) {
315 *color
= SK_ColorTRANSPARENT
;
318 if (is_solid_color_
) {
325 bool AnalysisCanvas::abortDrawing() {
326 // Early out as soon as we have more than one draw op.
327 // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to
328 // balance the amount of time we spend analyzing vs how many tiles would be
329 // solid if the number was higher.
330 if (draw_op_count_
> 1) {
331 // We have to reset solid/transparent state to false since we don't
332 // know whether consequent operations will make this false.
333 is_solid_color_
= false;
334 is_transparent_
= false;
340 void AnalysisCanvas::OnComplexClip() {
341 // complex clips can make our calls to IsFullQuad invalid (ie have false
342 // positives). As a precaution, force the setting to be non-solid
343 // and non-transparent until we pop this
344 if (force_not_solid_stack_level_
== kNoLayer
) {
345 force_not_solid_stack_level_
= saved_stack_size_
;
346 SetForceNotSolid(true);
348 if (force_not_transparent_stack_level_
== kNoLayer
) {
349 force_not_transparent_stack_level_
= saved_stack_size_
;
350 SetForceNotTransparent(true);
354 void AnalysisCanvas::onClipRect(const SkRect
& rect
,
356 ClipEdgeStyle edge_style
) {
357 INHERITED::onClipRect(rect
, op
, edge_style
);
360 void AnalysisCanvas::onClipPath(const SkPath
& path
,
362 ClipEdgeStyle edge_style
) {
364 INHERITED::onClipRect(path
.getBounds(), op
, edge_style
);
367 void AnalysisCanvas::onClipRRect(const SkRRect
& rrect
,
369 ClipEdgeStyle edge_style
) {
371 INHERITED::onClipRect(rrect
.getBounds(), op
, edge_style
);
374 void AnalysisCanvas::onClipRegion(const SkRegion
& deviceRgn
, SkRegion::Op op
) {
375 const ClipEdgeStyle edge_style
= kHard_ClipEdgeStyle
;
376 if (deviceRgn
.isRect()) {
377 onClipRect(SkRect::MakeFromIRect(deviceRgn
.getBounds()), op
, edge_style
);
381 INHERITED::onClipRect(
382 SkRect::MakeFromIRect(deviceRgn
.getBounds()), op
, edge_style
);
385 void AnalysisCanvas::willSave() {
387 INHERITED::willSave();
390 SkCanvas::SaveLayerStrategy
AnalysisCanvas::willSaveLayer(
391 const SkRect
* bounds
,
392 const SkPaint
* paint
,
393 SkCanvas::SaveFlags flags
) {
397 SkIRect canvas_ibounds
= SkIRect::MakeSize(this->getDeviceSize());
398 SkRect canvas_bounds
;
399 canvas_bounds
.set(canvas_ibounds
);
401 // If after we draw to the saved layer, we have to blend with the current
402 // layer, then we can conservatively say that the canvas will not be of
404 if ((paint
&& !IsSolidColorPaint(*paint
)) ||
405 (bounds
&& !bounds
->contains(canvas_bounds
))) {
406 if (force_not_solid_stack_level_
== kNoLayer
) {
407 force_not_solid_stack_level_
= saved_stack_size_
;
408 SetForceNotSolid(true);
412 // If after we draw to the save layer, we have to blend with the current
413 // layer using any part of the current layer's alpha, then we can
414 // conservatively say that the canvas will not be transparent.
415 SkXfermode::Mode xfermode
= SkXfermode::kSrc_Mode
;
417 SkXfermode::AsMode(paint
->getXfermode(), &xfermode
);
418 if (xfermode
!= SkXfermode::kSrc_Mode
) {
419 if (force_not_transparent_stack_level_
== kNoLayer
) {
420 force_not_transparent_stack_level_
= saved_stack_size_
;
421 SetForceNotTransparent(true);
425 INHERITED::willSaveLayer(bounds
, paint
, flags
);
426 // Actually saving a layer here could cause a new bitmap to be created
427 // and real rendering to occur.
428 return kNoLayer_SaveLayerStrategy
;
431 void AnalysisCanvas::willRestore() {
432 DCHECK(saved_stack_size_
);
433 if (saved_stack_size_
) {
435 if (saved_stack_size_
< force_not_solid_stack_level_
) {
436 SetForceNotSolid(false);
437 force_not_solid_stack_level_
= kNoLayer
;
439 if (saved_stack_size_
< force_not_transparent_stack_level_
) {
440 SetForceNotTransparent(false);
441 force_not_transparent_stack_level_
= kNoLayer
;
445 INHERITED::willRestore();