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/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"
15 const int kNoLayer
= -1;
17 bool ActsLikeClear(SkXfermode::Mode mode
, unsigned src_alpha
) {
19 case SkXfermode::kClear_Mode
:
21 case SkXfermode::kSrc_Mode
:
22 case SkXfermode::kSrcIn_Mode
:
23 case SkXfermode::kDstIn_Mode
:
24 case SkXfermode::kSrcOut_Mode
:
25 case SkXfermode::kDstATop_Mode
:
26 return src_alpha
== 0;
27 case SkXfermode::kDstOut_Mode
:
28 return src_alpha
== 0xFF;
34 bool IsSolidColorPaint(const SkPaint
& paint
) {
35 SkXfermode::Mode xfermode
;
37 // getXfermode can return a NULL, but that is handled
38 // gracefully by AsMode (NULL turns into kSrcOver mode).
39 if (!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 &&
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())
64 if (!canvas
->getClipDeviceBounds(&clip_irect
))
67 // if the clip is smaller than the canvas, we're partly clipped, so abort.
68 if (!clip_irect
.contains(SkIRect::MakeSize(canvas
->getDeviceSize())))
71 const SkMatrix
& matrix
= canvas
->getTotalMatrix();
72 // If the transform results in a non-axis aligned
73 // rect, then be conservative and return false.
74 if (!matrix
.rectStaysRect())
78 matrix
.mapRect(&device_rect
, drawn_rect
);
80 clip_rect
.set(clip_irect
);
81 return device_rect
.contains(clip_rect
);
88 void AnalysisCanvas::SetForceNotSolid(bool flag
) {
89 is_forced_not_solid_
= flag
;
90 if (is_forced_not_solid_
)
91 is_solid_color_
= false;
94 void AnalysisCanvas::SetForceNotTransparent(bool flag
) {
95 is_forced_not_transparent_
= flag
;
96 if (is_forced_not_transparent_
)
97 is_transparent_
= false;
100 void AnalysisCanvas::onDrawPaint(const SkPaint
& paint
) {
102 if (getClipBounds(&rect
))
103 drawRect(rect
, paint
);
106 void AnalysisCanvas::onDrawPoints(SkCanvas::PointMode mode
,
108 const SkPoint points
[],
109 const SkPaint
& paint
) {
110 is_solid_color_
= false;
111 is_transparent_
= false;
115 void AnalysisCanvas::onDrawRect(const SkRect
& rect
, const SkPaint
& paint
) {
116 // This recreates the early-exit logic in SkCanvas.cpp.
118 if (paint
.canComputeFastBounds() &&
119 quickReject(paint
.computeFastBounds(rect
, &scratch
))) {
123 // An extra no-op check SkCanvas.cpp doesn't do.
124 if (paint
.nothingToDraw())
127 bool does_cover_canvas
= IsFullQuad(this, rect
);
129 SkXfermode::Mode xfermode
;
130 SkXfermode::AsMode(paint
.getXfermode(), &xfermode
);
132 // This canvas will become transparent if the following holds:
133 // - The quad is a full tile quad
134 // - We're not in "forced not transparent" mode
135 // - Transfer mode is clear (0 color, 0 alpha)
137 // If the paint alpha is not 0, or if the transfrer mode is
138 // not src, then this canvas will not be transparent.
140 // In all other cases, we keep the current transparent value
141 if (does_cover_canvas
&&
142 !is_forced_not_transparent_
&&
143 ActsLikeClear(xfermode
, paint
.getAlpha())) {
144 is_transparent_
= true;
145 } else if (paint
.getAlpha() != 0 || xfermode
!= SkXfermode::kSrc_Mode
) {
146 is_transparent_
= false;
149 // This bitmap is solid if and only if the following holds.
150 // Note that this might be overly conservative:
151 // - We're not in "forced not solid" mode
152 // - Paint is solid color
153 // - The quad is a full tile quad
154 if (!is_forced_not_solid_
&& IsSolidColorPaint(paint
) && does_cover_canvas
) {
155 is_solid_color_
= true;
156 color_
= paint
.getColor();
158 is_solid_color_
= false;
163 void AnalysisCanvas::onDrawOval(const SkRect
& oval
, const SkPaint
& paint
) {
164 is_solid_color_
= false;
165 is_transparent_
= false;
169 void AnalysisCanvas::onDrawRRect(const SkRRect
& rr
, const SkPaint
& paint
) {
170 // This should add the SkRRect to an SkPath, and call
171 // drawPath, but since drawPath ignores the SkPath, just
172 // do the same work here.
173 is_solid_color_
= false;
174 is_transparent_
= false;
178 void AnalysisCanvas::onDrawPath(const SkPath
& path
, const SkPaint
& paint
) {
179 is_solid_color_
= false;
180 is_transparent_
= false;
184 void AnalysisCanvas::onDrawBitmap(const SkBitmap
& bitmap
,
188 is_solid_color_
= false;
189 is_transparent_
= false;
193 void AnalysisCanvas::onDrawBitmapRect(const SkBitmap
&,
196 const SkPaint
* paint
,
198 // Call drawRect to determine transparency,
199 // but reset solid color to false.
203 drawRect(dst
, *paint
);
204 is_solid_color_
= false;
208 void AnalysisCanvas::onDrawBitmapNine(const SkBitmap
& bitmap
,
209 const SkIRect
& center
,
211 const SkPaint
* paint
) {
212 is_solid_color_
= false;
213 is_transparent_
= false;
217 void AnalysisCanvas::onDrawImage(const SkImage
*,
221 is_solid_color_
= false;
222 is_transparent_
= false;
226 void AnalysisCanvas::onDrawImageRect(const SkImage
*,
229 const SkPaint
* paint
,
231 // Call drawRect to determine transparency,
232 // but reset solid color to false.
236 drawRect(dst
, *paint
);
237 is_solid_color_
= false;
241 void AnalysisCanvas::onDrawSprite(const SkBitmap
& bitmap
,
244 const SkPaint
* paint
) {
245 is_solid_color_
= false;
246 is_transparent_
= false;
250 void AnalysisCanvas::onDrawText(const void* text
,
254 const SkPaint
& paint
) {
255 is_solid_color_
= false;
256 is_transparent_
= false;
260 void AnalysisCanvas::onDrawPosText(const void* text
,
263 const SkPaint
& paint
) {
264 is_solid_color_
= false;
265 is_transparent_
= false;
269 void AnalysisCanvas::onDrawPosTextH(const void* text
,
271 const SkScalar xpos
[],
273 const SkPaint
& paint
) {
274 is_solid_color_
= false;
275 is_transparent_
= false;
279 void AnalysisCanvas::onDrawTextOnPath(const void* text
,
282 const SkMatrix
* matrix
,
283 const SkPaint
& paint
) {
284 is_solid_color_
= false;
285 is_transparent_
= false;
289 void AnalysisCanvas::onDrawTextBlob(const SkTextBlob
* blob
,
292 const SkPaint
&paint
) {
293 is_solid_color_
= false;
294 is_transparent_
= false;
298 void AnalysisCanvas::onDrawDRRect(const SkRRect
& outer
,
299 const SkRRect
& inner
,
300 const SkPaint
& paint
) {
301 is_solid_color_
= false;
302 is_transparent_
= false;
306 void AnalysisCanvas::onDrawVertices(SkCanvas::VertexMode
,
308 const SkPoint verts
[],
309 const SkPoint texs
[],
310 const SkColor colors
[],
312 const uint16_t indices
[],
314 const SkPaint
& paint
) {
315 is_solid_color_
= false;
316 is_transparent_
= false;
320 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
322 static SkBitmap
MakeEmptyBitmap(int width
, int height
) {
324 bitmap
.setInfo(SkImageInfo::MakeUnknown(width
, height
));
328 AnalysisCanvas::AnalysisCanvas(int width
, int height
)
329 : INHERITED(MakeEmptyBitmap(width
, height
)),
330 saved_stack_size_(0),
331 force_not_solid_stack_level_(kNoLayer
),
332 force_not_transparent_stack_level_(kNoLayer
),
333 is_forced_not_solid_(false),
334 is_forced_not_transparent_(false),
335 is_solid_color_(true),
336 color_(SK_ColorTRANSPARENT
),
337 is_transparent_(true),
341 AnalysisCanvas::~AnalysisCanvas() {}
343 bool AnalysisCanvas::GetColorIfSolid(SkColor
* color
) const {
344 if (is_transparent_
) {
345 *color
= SK_ColorTRANSPARENT
;
348 if (is_solid_color_
) {
355 bool AnalysisCanvas::abort() {
356 // Early out as soon as we have more than one draw op.
357 // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to
358 // balance the amount of time we spend analyzing vs how many tiles would be
359 // solid if the number was higher.
360 if (draw_op_count_
> 1) {
361 // We have to reset solid/transparent state to false since we don't
362 // know whether consequent operations will make this false.
363 is_solid_color_
= false;
364 is_transparent_
= false;
370 void AnalysisCanvas::OnComplexClip() {
371 // complex clips can make our calls to IsFullQuad invalid (ie have false
372 // positives). As a precaution, force the setting to be non-solid
373 // and non-transparent until we pop this
374 if (force_not_solid_stack_level_
== kNoLayer
) {
375 force_not_solid_stack_level_
= saved_stack_size_
;
376 SetForceNotSolid(true);
378 if (force_not_transparent_stack_level_
== kNoLayer
) {
379 force_not_transparent_stack_level_
= saved_stack_size_
;
380 SetForceNotTransparent(true);
384 void AnalysisCanvas::onClipRect(const SkRect
& rect
,
386 ClipEdgeStyle edge_style
) {
387 INHERITED::onClipRect(rect
, op
, edge_style
);
390 void AnalysisCanvas::onClipPath(const SkPath
& path
,
392 ClipEdgeStyle edge_style
) {
394 INHERITED::onClipRect(path
.getBounds(), op
, edge_style
);
397 void AnalysisCanvas::onClipRRect(const SkRRect
& rrect
,
399 ClipEdgeStyle edge_style
) {
401 INHERITED::onClipRect(rrect
.getBounds(), op
, edge_style
);
404 void AnalysisCanvas::onClipRegion(const SkRegion
& deviceRgn
, SkRegion::Op op
) {
405 const ClipEdgeStyle edge_style
= kHard_ClipEdgeStyle
;
406 if (deviceRgn
.isRect()) {
407 onClipRect(SkRect::MakeFromIRect(deviceRgn
.getBounds()), op
, edge_style
);
411 INHERITED::onClipRect(
412 SkRect::MakeFromIRect(deviceRgn
.getBounds()), op
, edge_style
);
415 void AnalysisCanvas::willSave() {
417 INHERITED::willSave();
420 SkCanvas::SaveLayerStrategy
AnalysisCanvas::willSaveLayer(
421 const SkRect
* bounds
,
422 const SkPaint
* paint
,
423 SkCanvas::SaveFlags flags
) {
427 SkIRect canvas_ibounds
= SkIRect::MakeSize(this->getDeviceSize());
428 SkRect canvas_bounds
;
429 canvas_bounds
.set(canvas_ibounds
);
431 // If after we draw to the saved layer, we have to blend with the current
432 // layer, then we can conservatively say that the canvas will not be of
434 if ((paint
&& !IsSolidColorPaint(*paint
)) ||
435 (bounds
&& !bounds
->contains(canvas_bounds
))) {
436 if (force_not_solid_stack_level_
== kNoLayer
) {
437 force_not_solid_stack_level_
= saved_stack_size_
;
438 SetForceNotSolid(true);
442 // If after we draw to the save layer, we have to blend with the current
443 // layer using any part of the current layer's alpha, then we can
444 // conservatively say that the canvas will not be transparent.
445 SkXfermode::Mode xfermode
= SkXfermode::kSrc_Mode
;
447 SkXfermode::AsMode(paint
->getXfermode(), &xfermode
);
448 if (xfermode
!= SkXfermode::kDst_Mode
) {
449 if (force_not_transparent_stack_level_
== kNoLayer
) {
450 force_not_transparent_stack_level_
= saved_stack_size_
;
451 SetForceNotTransparent(true);
455 INHERITED::willSaveLayer(bounds
, paint
, flags
);
456 // Actually saving a layer here could cause a new bitmap to be created
457 // and real rendering to occur.
458 return kNoLayer_SaveLayerStrategy
;
461 void AnalysisCanvas::willRestore() {
462 DCHECK(saved_stack_size_
);
463 if (saved_stack_size_
) {
465 if (saved_stack_size_
< force_not_solid_stack_level_
) {
466 SetForceNotSolid(false);
467 force_not_solid_stack_level_
= kNoLayer
;
469 if (saved_stack_size_
< force_not_transparent_stack_level_
) {
470 SetForceNotTransparent(false);
471 force_not_transparent_stack_level_
= kNoLayer
;
475 INHERITED::willRestore();