Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / skia / ext / analysis_canvas.cc
blob066f35f7e04c20598abff632272ff272493272e5
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/SkRRect.h"
10 #include "third_party/skia/include/core/SkShader.h"
11 #include "third_party/skia/src/core/SkRasterClip.h"
13 namespace {
15 const int kNoLayer = -1;
17 bool ActsLikeClear(SkXfermode::Mode mode, unsigned src_alpha) {
18 switch (mode) {
19 case SkXfermode::kClear_Mode:
20 return true;
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;
29 default:
30 return false;
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 SkXfermode::AsMode(paint.getXfermode(), &xfermode);
41 // Paint is solid color if the following holds:
42 // - Alpha is 1.0, style is fill, and there are no special effects
43 // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent
44 // to kSrc if source alpha is 1.0, which is already checked).
45 return (paint.getAlpha() == 255 &&
46 !paint.getShader() &&
47 !paint.getLooper() &&
48 !paint.getMaskFilter() &&
49 !paint.getColorFilter() &&
50 !paint.getImageFilter() &&
51 paint.getStyle() == SkPaint::kFill_Style &&
52 (xfermode == SkXfermode::kSrc_Mode ||
53 xfermode == SkXfermode::kSrcOver_Mode));
56 // Returns true if the specified drawn_rect will cover the entire canvas, and
57 // that the canvas is not clipped (i.e. it covers ALL of the canvas).
58 bool IsFullQuad(SkCanvas* canvas, const SkRect& drawn_rect) {
59 if (!canvas->isClipRect())
60 return false;
62 SkIRect clip_irect;
63 if (!canvas->getClipDeviceBounds(&clip_irect))
64 return false;
66 // if the clip is smaller than the canvas, we're partly clipped, so abort.
67 if (!clip_irect.contains(SkIRect::MakeSize(canvas->getDeviceSize())))
68 return false;
70 const SkMatrix& matrix = canvas->getTotalMatrix();
71 // If the transform results in a non-axis aligned
72 // rect, then be conservative and return false.
73 if (!matrix.rectStaysRect())
74 return false;
76 SkRect device_rect;
77 matrix.mapRect(&device_rect, drawn_rect);
78 SkRect clip_rect;
79 clip_rect.set(clip_irect);
80 return device_rect.contains(clip_rect);
83 } // namespace
85 namespace skia {
87 void AnalysisCanvas::SetForceNotSolid(bool flag) {
88 is_forced_not_solid_ = flag;
89 if (is_forced_not_solid_)
90 is_solid_color_ = false;
93 void AnalysisCanvas::SetForceNotTransparent(bool flag) {
94 is_forced_not_transparent_ = flag;
95 if (is_forced_not_transparent_)
96 is_transparent_ = false;
99 void AnalysisCanvas::onDrawPaint(const SkPaint& paint) {
100 SkRect rect;
101 if (getClipBounds(&rect))
102 drawRect(rect, paint);
105 void AnalysisCanvas::onDrawPoints(SkCanvas::PointMode mode,
106 size_t count,
107 const SkPoint points[],
108 const SkPaint& paint) {
109 is_solid_color_ = false;
110 is_transparent_ = false;
111 ++draw_op_count_;
114 void AnalysisCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
115 // This recreates the early-exit logic in SkCanvas.cpp.
116 SkRect scratch;
117 if (paint.canComputeFastBounds() &&
118 quickReject(paint.computeFastBounds(rect, &scratch))) {
119 return;
122 // An extra no-op check SkCanvas.cpp doesn't do.
123 if (paint.nothingToDraw())
124 return;
126 bool does_cover_canvas = IsFullQuad(this, rect);
128 SkXfermode::Mode xfermode;
129 SkXfermode::AsMode(paint.getXfermode(), &xfermode);
131 // This canvas will become transparent if the following holds:
132 // - The quad is a full tile quad
133 // - We're not in "forced not transparent" mode
134 // - Transfer mode is clear (0 color, 0 alpha)
136 // If the paint alpha is not 0, or if the transfrer mode is
137 // not src, then this canvas will not be transparent.
139 // In all other cases, we keep the current transparent value
140 if (does_cover_canvas &&
141 !is_forced_not_transparent_ &&
142 ActsLikeClear(xfermode, paint.getAlpha())) {
143 is_transparent_ = true;
144 } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
145 is_transparent_ = false;
148 // This bitmap is solid if and only if the following holds.
149 // Note that this might be overly conservative:
150 // - We're not in "forced not solid" mode
151 // - Paint is solid color
152 // - The quad is a full tile quad
153 if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
154 is_solid_color_ = true;
155 color_ = paint.getColor();
156 } else {
157 is_solid_color_ = false;
159 ++draw_op_count_;
162 void AnalysisCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
163 is_solid_color_ = false;
164 is_transparent_ = false;
165 ++draw_op_count_;
168 void AnalysisCanvas::onDrawRRect(const SkRRect& rr, const SkPaint& paint) {
169 // This should add the SkRRect to an SkPath, and call
170 // drawPath, but since drawPath ignores the SkPath, just
171 // do the same work here.
172 is_solid_color_ = false;
173 is_transparent_ = false;
174 ++draw_op_count_;
177 void AnalysisCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
178 is_solid_color_ = false;
179 is_transparent_ = false;
180 ++draw_op_count_;
183 void AnalysisCanvas::onDrawBitmap(const SkBitmap& bitmap,
184 SkScalar left,
185 SkScalar top,
186 const SkPaint*) {
187 is_solid_color_ = false;
188 is_transparent_ = false;
189 ++draw_op_count_;
192 void AnalysisCanvas::onDrawBitmapRect(const SkBitmap&,
193 const SkRect* src,
194 const SkRect& dst,
195 const SkPaint* paint,
196 DrawBitmapRectFlags flags) {
197 // Call drawRect to determine transparency,
198 // but reset solid color to false.
199 SkPaint tmpPaint;
200 if (!paint)
201 paint = &tmpPaint;
202 drawRect(dst, *paint);
203 is_solid_color_ = false;
204 ++draw_op_count_;
207 void AnalysisCanvas::onDrawBitmapNine(const SkBitmap& bitmap,
208 const SkIRect& center,
209 const SkRect& dst,
210 const SkPaint* paint) {
211 is_solid_color_ = false;
212 is_transparent_ = false;
213 ++draw_op_count_;
216 void AnalysisCanvas::onDrawSprite(const SkBitmap& bitmap,
217 int left,
218 int top,
219 const SkPaint* paint) {
220 is_solid_color_ = false;
221 is_transparent_ = false;
222 ++draw_op_count_;
225 void AnalysisCanvas::onDrawText(const void* text,
226 size_t len,
227 SkScalar x,
228 SkScalar y,
229 const SkPaint& paint) {
230 is_solid_color_ = false;
231 is_transparent_ = false;
232 ++draw_op_count_;
235 void AnalysisCanvas::onDrawPosText(const void* text,
236 size_t byteLength,
237 const SkPoint pos[],
238 const SkPaint& paint) {
239 is_solid_color_ = false;
240 is_transparent_ = false;
241 ++draw_op_count_;
244 void AnalysisCanvas::onDrawPosTextH(const void* text,
245 size_t byteLength,
246 const SkScalar xpos[],
247 SkScalar constY,
248 const SkPaint& paint) {
249 is_solid_color_ = false;
250 is_transparent_ = false;
251 ++draw_op_count_;
254 void AnalysisCanvas::onDrawTextOnPath(const void* text,
255 size_t len,
256 const SkPath& path,
257 const SkMatrix* matrix,
258 const SkPaint& paint) {
259 is_solid_color_ = false;
260 is_transparent_ = false;
261 ++draw_op_count_;
264 void AnalysisCanvas::onDrawTextBlob(const SkTextBlob* blob,
265 SkScalar x,
266 SkScalar y,
267 const SkPaint &paint) {
268 is_solid_color_ = false;
269 is_transparent_ = false;
270 ++draw_op_count_;
273 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer,
274 const SkRRect& inner,
275 const SkPaint& paint) {
276 is_solid_color_ = false;
277 is_transparent_ = false;
278 ++draw_op_count_;
281 void AnalysisCanvas::onDrawVertices(SkCanvas::VertexMode,
282 int vertex_count,
283 const SkPoint verts[],
284 const SkPoint texs[],
285 const SkColor colors[],
286 SkXfermode* xmode,
287 const uint16_t indices[],
288 int index_count,
289 const SkPaint& paint) {
290 is_solid_color_ = false;
291 is_transparent_ = false;
292 ++draw_op_count_;
295 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
296 // by any pixels
297 static SkBitmap MakeEmptyBitmap(int width, int height) {
298 SkBitmap bitmap;
299 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
300 return bitmap;
303 AnalysisCanvas::AnalysisCanvas(int width, int height)
304 : INHERITED(MakeEmptyBitmap(width, height)),
305 saved_stack_size_(0),
306 force_not_solid_stack_level_(kNoLayer),
307 force_not_transparent_stack_level_(kNoLayer),
308 is_forced_not_solid_(false),
309 is_forced_not_transparent_(false),
310 is_solid_color_(true),
311 color_(SK_ColorTRANSPARENT),
312 is_transparent_(true),
313 draw_op_count_(0) {
316 AnalysisCanvas::~AnalysisCanvas() {}
318 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
319 if (is_transparent_) {
320 *color = SK_ColorTRANSPARENT;
321 return true;
323 if (is_solid_color_) {
324 *color = color_;
325 return true;
327 return false;
330 bool AnalysisCanvas::abortDrawing() {
331 // Early out as soon as we have more than one draw op.
332 // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to
333 // balance the amount of time we spend analyzing vs how many tiles would be
334 // solid if the number was higher.
335 if (draw_op_count_ > 1) {
336 // We have to reset solid/transparent state to false since we don't
337 // know whether consequent operations will make this false.
338 is_solid_color_ = false;
339 is_transparent_ = false;
340 return true;
342 return false;
345 void AnalysisCanvas::OnComplexClip() {
346 // complex clips can make our calls to IsFullQuad invalid (ie have false
347 // positives). As a precaution, force the setting to be non-solid
348 // and non-transparent until we pop this
349 if (force_not_solid_stack_level_ == kNoLayer) {
350 force_not_solid_stack_level_ = saved_stack_size_;
351 SetForceNotSolid(true);
353 if (force_not_transparent_stack_level_ == kNoLayer) {
354 force_not_transparent_stack_level_ = saved_stack_size_;
355 SetForceNotTransparent(true);
359 void AnalysisCanvas::onClipRect(const SkRect& rect,
360 SkRegion::Op op,
361 ClipEdgeStyle edge_style) {
362 INHERITED::onClipRect(rect, op, edge_style);
365 void AnalysisCanvas::onClipPath(const SkPath& path,
366 SkRegion::Op op,
367 ClipEdgeStyle edge_style) {
368 OnComplexClip();
369 INHERITED::onClipRect(path.getBounds(), op, edge_style);
372 void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
373 SkRegion::Op op,
374 ClipEdgeStyle edge_style) {
375 OnComplexClip();
376 INHERITED::onClipRect(rrect.getBounds(), op, edge_style);
379 void AnalysisCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
380 const ClipEdgeStyle edge_style = kHard_ClipEdgeStyle;
381 if (deviceRgn.isRect()) {
382 onClipRect(SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
383 return;
385 OnComplexClip();
386 INHERITED::onClipRect(
387 SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
390 void AnalysisCanvas::willSave() {
391 ++saved_stack_size_;
392 INHERITED::willSave();
395 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
396 const SkRect* bounds,
397 const SkPaint* paint,
398 SkCanvas::SaveFlags flags) {
400 ++saved_stack_size_;
402 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
403 SkRect canvas_bounds;
404 canvas_bounds.set(canvas_ibounds);
406 // If after we draw to the saved layer, we have to blend with the current
407 // layer, then we can conservatively say that the canvas will not be of
408 // solid color.
409 if ((paint && !IsSolidColorPaint(*paint)) ||
410 (bounds && !bounds->contains(canvas_bounds))) {
411 if (force_not_solid_stack_level_ == kNoLayer) {
412 force_not_solid_stack_level_ = saved_stack_size_;
413 SetForceNotSolid(true);
417 // If after we draw to the save layer, we have to blend with the current
418 // layer using any part of the current layer's alpha, then we can
419 // conservatively say that the canvas will not be transparent.
420 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
421 if (paint)
422 SkXfermode::AsMode(paint->getXfermode(), &xfermode);
423 if (xfermode != SkXfermode::kDst_Mode) {
424 if (force_not_transparent_stack_level_ == kNoLayer) {
425 force_not_transparent_stack_level_ = saved_stack_size_;
426 SetForceNotTransparent(true);
430 INHERITED::willSaveLayer(bounds, paint, flags);
431 // Actually saving a layer here could cause a new bitmap to be created
432 // and real rendering to occur.
433 return kNoLayer_SaveLayerStrategy;
436 void AnalysisCanvas::willRestore() {
437 DCHECK(saved_stack_size_);
438 if (saved_stack_size_) {
439 --saved_stack_size_;
440 if (saved_stack_size_ < force_not_solid_stack_level_) {
441 SetForceNotSolid(false);
442 force_not_solid_stack_level_ = kNoLayer;
444 if (saved_stack_size_ < force_not_transparent_stack_level_) {
445 SetForceNotTransparent(false);
446 force_not_transparent_stack_level_ = kNoLayer;
450 INHERITED::willRestore();
453 } // namespace skia