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 "skia/ext/analysis_canvas.h"
7 #include "third_party/skia/include/core/SkDevice.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
.getStyle() == SkPaint::kFill_Style
&&
35 (xfermode
== SkXfermode::kSrc_Mode
||
36 xfermode
== SkXfermode::kSrcOver_Mode
));
39 bool IsFullQuad(const SkDraw
& draw
,
40 const SkRect
& canvas_rect
,
41 const SkRect
& drawn_rect
) {
43 // If the transform results in a non-axis aligned
44 // rect, then be conservative and return false.
45 if (!draw
.fMatrix
->rectStaysRect())
48 SkRect draw_bitmap_rect
;
49 draw
.fBitmap
->getBounds(&draw_bitmap_rect
);
50 SkRect clip_rect
= SkRect::Make(draw
.fRC
->getBounds());
52 draw
.fMatrix
->mapRect(&device_rect
, drawn_rect
);
54 // The drawn rect covers the full canvas, if the following conditions hold:
55 // - Clip rect is an actual rectangle.
56 // - The rect we're drawing (post-transform) contains the clip rect.
57 // That is, all of clip rect will be colored by the rect.
58 // - Clip rect contains the canvas rect.
59 // That is, we're not clipping to a portion of this canvas.
60 // - The bitmap into which the draw call happens is at least as
61 // big as the canvas rect
62 return draw
.fRC
->isRect() &&
63 device_rect
.contains(clip_rect
) &&
64 clip_rect
.contains(canvas_rect
) &&
65 draw_bitmap_rect
.contains(canvas_rect
);
72 AnalysisDevice::AnalysisDevice(const SkBitmap
& bitmap
)
74 is_forced_not_solid_(false),
75 is_forced_not_transparent_(false),
76 is_solid_color_(true),
77 is_transparent_(true),
80 AnalysisDevice::~AnalysisDevice() {}
82 bool AnalysisDevice::GetColorIfSolid(SkColor
* color
) const {
83 if (is_transparent_
) {
84 *color
= SK_ColorTRANSPARENT
;
87 if (is_solid_color_
) {
94 bool AnalysisDevice::HasText() const {
98 void AnalysisDevice::SetForceNotSolid(bool flag
) {
99 is_forced_not_solid_
= flag
;
100 if (is_forced_not_solid_
)
101 is_solid_color_
= false;
104 void AnalysisDevice::SetForceNotTransparent(bool flag
) {
105 is_forced_not_transparent_
= flag
;
106 if (is_forced_not_transparent_
)
107 is_transparent_
= false;
110 void AnalysisDevice::clear(SkColor color
) {
111 is_transparent_
= (!is_forced_not_transparent_
&& SkColorGetA(color
) == 0);
114 if (!is_forced_not_solid_
&& SkColorGetA(color
) == 255) {
115 is_solid_color_
= true;
118 is_solid_color_
= false;
122 void AnalysisDevice::drawPaint(const SkDraw
& draw
, const SkPaint
& paint
) {
123 is_solid_color_
= false;
124 is_transparent_
= false;
127 void AnalysisDevice::drawPoints(const SkDraw
& draw
,
128 SkCanvas::PointMode mode
,
130 const SkPoint points
[],
131 const SkPaint
& paint
) {
132 is_solid_color_
= false;
133 is_transparent_
= false;
136 void AnalysisDevice::drawRect(const SkDraw
& draw
,
138 const SkPaint
& paint
) {
139 bool does_cover_canvas
=
140 IsFullQuad(draw
, SkRect::MakeWH(width(), height()), rect
);
142 SkXfermode::Mode xfermode
;
143 SkXfermode::AsMode(paint
.getXfermode(), &xfermode
);
145 // This canvas will become transparent if the following holds:
146 // - The quad is a full tile quad
147 // - We're not in "forced not transparent" mode
148 // - Transfer mode is clear (0 color, 0 alpha)
150 // If the paint alpha is not 0, or if the transfrer mode is
151 // not src, then this canvas will not be transparent.
153 // In all other cases, we keep the current transparent value
154 if (does_cover_canvas
&&
155 !is_forced_not_transparent_
&&
156 xfermode
== SkXfermode::kClear_Mode
) {
157 is_transparent_
= true;
159 } else if (paint
.getAlpha() != 0 || xfermode
!= SkXfermode::kSrc_Mode
) {
160 is_transparent_
= false;
163 // This bitmap is solid if and only if the following holds.
164 // Note that this might be overly conservative:
165 // - We're not in "forced not solid" mode
166 // - Paint is solid color
167 // - The quad is a full tile quad
168 if (!is_forced_not_solid_
&& IsSolidColorPaint(paint
) && does_cover_canvas
) {
169 is_solid_color_
= true;
170 color_
= paint
.getColor();
173 is_solid_color_
= false;
177 void AnalysisDevice::drawOval(const SkDraw
& draw
,
179 const SkPaint
& paint
) {
180 is_solid_color_
= false;
181 is_transparent_
= false;
184 void AnalysisDevice::drawPath(const SkDraw
& draw
,
186 const SkPaint
& paint
,
187 const SkMatrix
* pre_path_matrix
,
188 bool path_is_mutable
) {
189 is_solid_color_
= false;
190 is_transparent_
= false;
193 void AnalysisDevice::drawBitmap(const SkDraw
& draw
,
194 const SkBitmap
& bitmap
,
195 const SkIRect
* src_rect_or_null
,
196 const SkMatrix
& matrix
,
197 const SkPaint
& paint
) {
198 is_solid_color_
= false;
199 is_transparent_
= false;
202 void AnalysisDevice::drawSprite(const SkDraw
& draw
,
203 const SkBitmap
& bitmap
,
206 const SkPaint
& paint
) {
207 is_solid_color_
= false;
208 is_transparent_
= false;
211 void AnalysisDevice::drawBitmapRect(const SkDraw
& draw
,
212 const SkBitmap
& bitmap
,
213 const SkRect
* src_or_null
,
215 const SkPaint
& paint
) {
216 // Call drawRect to determine transparency,
217 // but reset solid color to false.
218 drawRect(draw
, dst
, paint
);
219 is_solid_color_
= false;
222 void AnalysisDevice::drawText(const SkDraw
& draw
,
227 const SkPaint
& paint
) {
228 is_solid_color_
= false;
229 is_transparent_
= false;
233 void AnalysisDevice::drawPosText(const SkDraw
& draw
,
236 const SkScalar pos
[],
239 const SkPaint
& paint
) {
240 is_solid_color_
= false;
241 is_transparent_
= false;
245 void AnalysisDevice::drawTextOnPath(const SkDraw
& draw
,
249 const SkMatrix
* matrix
,
250 const SkPaint
& paint
) {
251 is_solid_color_
= false;
252 is_transparent_
= false;
256 #ifdef SK_BUILD_FOR_ANDROID
257 void AnalysisDevice::drawPosTextOnPath(const SkDraw
& draw
,
261 const SkPaint
& paint
,
263 const SkMatrix
* matrix
) {
264 is_solid_color_
= false;
265 is_transparent_
= false;
270 void AnalysisDevice::drawVertices(const SkDraw
& draw
,
271 SkCanvas::VertexMode
,
273 const SkPoint verts
[],
274 const SkPoint texs
[],
275 const SkColor colors
[],
277 const uint16_t indices
[],
279 const SkPaint
& paint
) {
280 is_solid_color_
= false;
281 is_transparent_
= false;
284 void AnalysisDevice::drawDevice(const SkDraw
& draw
,
288 const SkPaint
& paint
) {
289 is_solid_color_
= false;
290 is_transparent_
= false;
293 AnalysisCanvas::AnalysisCanvas(AnalysisDevice
* device
)
295 saved_stack_size_(0),
296 force_not_solid_stack_level_(kNoLayer
),
297 force_not_transparent_stack_level_(kNoLayer
) {}
299 AnalysisCanvas::~AnalysisCanvas() {}
301 bool AnalysisCanvas::GetColorIfSolid(SkColor
* color
) const {
302 return (static_cast<AnalysisDevice
*>(getDevice()))->GetColorIfSolid(color
);
305 bool AnalysisCanvas::HasText() const {
306 return (static_cast<AnalysisDevice
*>(getDevice()))->HasText();
309 bool AnalysisCanvas::abortDrawing() {
310 // Early out as soon as we have detected that the tile has text.
314 bool AnalysisCanvas::clipRect(const SkRect
& rect
, SkRegion::Op op
, bool do_aa
) {
315 return INHERITED::clipRect(rect
, op
, do_aa
);
318 bool AnalysisCanvas::clipPath(const SkPath
& path
, SkRegion::Op op
, bool do_aa
) {
319 // clipPaths can make our calls to IsFullQuad invalid (ie have false
320 // positives). As a precaution, force the setting to be non-solid
321 // and non-transparent until we pop this
322 if (force_not_solid_stack_level_
== kNoLayer
) {
323 force_not_solid_stack_level_
= saved_stack_size_
;
324 (static_cast<AnalysisDevice
*>(getDevice()))->SetForceNotSolid(true);
326 if (force_not_transparent_stack_level_
== kNoLayer
) {
327 force_not_transparent_stack_level_
= saved_stack_size_
;
328 (static_cast<AnalysisDevice
*>(getDevice()))->SetForceNotTransparent(true);
331 return INHERITED::clipRect(path
.getBounds(), op
, do_aa
);
334 bool AnalysisCanvas::clipRRect(const SkRRect
& rrect
,
337 // clipRRect can make our calls to IsFullQuad invalid (ie have false
338 // positives). As a precaution, force the setting to be non-solid
339 // and non-transparent until we pop this
340 if (force_not_solid_stack_level_
== kNoLayer
) {
341 force_not_solid_stack_level_
= saved_stack_size_
;
342 (static_cast<AnalysisDevice
*>(getDevice()))->SetForceNotSolid(true);
344 if (force_not_transparent_stack_level_
== kNoLayer
) {
345 force_not_transparent_stack_level_
= saved_stack_size_
;
346 (static_cast<AnalysisDevice
*>(getDevice()))->SetForceNotTransparent(true);
349 return INHERITED::clipRect(rrect
.getBounds(), op
, do_aa
);
352 int AnalysisCanvas::save(SkCanvas::SaveFlags flags
) {
354 return INHERITED::save(flags
);
357 int AnalysisCanvas::saveLayer(const SkRect
* bounds
,
358 const SkPaint
* paint
,
359 SkCanvas::SaveFlags flags
) {
362 // If after we draw to the saved layer, we have to blend with the current
363 // layer, then we can conservatively say that the canvas will not be of
365 if ((paint
&& !IsSolidColorPaint(*paint
)) ||
366 (bounds
&& !bounds
->contains(SkRect::MakeWH(getDevice()->width(),
367 getDevice()->height())))) {
368 if (force_not_solid_stack_level_
== kNoLayer
) {
369 force_not_solid_stack_level_
= saved_stack_size_
;
370 (static_cast<AnalysisDevice
*>(getDevice()))->SetForceNotSolid(true);
374 // If after we draw to the save layer, we have to blend with the current
375 // layer using any part of the current layer's alpha, then we can
376 // conservatively say that the canvas will not be transparent.
377 SkXfermode::Mode xfermode
= SkXfermode::kSrc_Mode
;
379 SkXfermode::AsMode(paint
->getXfermode(), &xfermode
);
380 if (xfermode
!= SkXfermode::kSrc_Mode
) {
381 if (force_not_transparent_stack_level_
== kNoLayer
) {
382 force_not_transparent_stack_level_
= saved_stack_size_
;
383 (static_cast<AnalysisDevice
*>(getDevice()))->SetForceNotTransparent(true);
387 // Actually saving a layer here could cause a new bitmap to be created
388 // and real rendering to occur.
389 int count
= INHERITED::save(flags
);
391 INHERITED::clipRectBounds(bounds
, flags
, NULL
);
396 void AnalysisCanvas::restore() {
397 INHERITED::restore();
399 DCHECK(saved_stack_size_
);
400 if (saved_stack_size_
) {
402 if (saved_stack_size_
< force_not_solid_stack_level_
) {
403 (static_cast<AnalysisDevice
*>(getDevice()))->SetForceNotSolid(false);
404 force_not_solid_stack_level_
= kNoLayer
;
406 if (saved_stack_size_
< force_not_transparent_stack_level_
) {
407 (static_cast<AnalysisDevice
*>(getDevice()))->SetForceNotTransparent(
409 force_not_transparent_stack_level_
= kNoLayer
;