Roll libvpx 861f35:1fff3e
[chromium-blink-merge.git] / skia / ext / analysis_canvas.cc
blob0ff16602d9cc1a8787c2f94a40f5bb7584c5fab7
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 canvas->getClipDeviceBounds(&clip_irect);
64 // if the clip is smaller than the canvas, we're partly clipped, so abort.
65 if (!clip_irect.contains(SkIRect::MakeSize(canvas->getDeviceSize())))
66 return false;
68 const SkMatrix& matrix = canvas->getTotalMatrix();
69 // If the transform results in a non-axis aligned
70 // rect, then be conservative and return false.
71 if (!matrix.rectStaysRect())
72 return false;
74 SkRect device_rect;
75 matrix.mapRect(&device_rect, drawn_rect);
76 SkRect clip_rect;
77 clip_rect.set(clip_irect);
78 return device_rect.contains(clip_rect);
81 } // namespace
83 namespace skia {
85 void AnalysisCanvas::SetForceNotSolid(bool flag) {
86 is_forced_not_solid_ = flag;
87 if (is_forced_not_solid_)
88 is_solid_color_ = false;
91 void AnalysisCanvas::SetForceNotTransparent(bool flag) {
92 is_forced_not_transparent_ = flag;
93 if (is_forced_not_transparent_)
94 is_transparent_ = false;
97 void AnalysisCanvas::onDrawPaint(const SkPaint& paint) {
98 SkRect rect;
99 getClipBounds(&rect);
100 drawRect(rect, paint);
103 void AnalysisCanvas::onDrawPoints(SkCanvas::PointMode mode,
104 size_t count,
105 const SkPoint points[],
106 const SkPaint& paint) {
107 is_solid_color_ = false;
108 is_transparent_ = false;
109 ++draw_op_count_;
112 void AnalysisCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
113 // This recreates the early-exit logic in SkCanvas.cpp.
114 SkRect scratch;
115 if (paint.canComputeFastBounds() &&
116 quickReject(paint.computeFastBounds(rect, &scratch))) {
117 return;
120 // An extra no-op check SkCanvas.cpp doesn't do.
121 if (paint.nothingToDraw())
122 return;
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 ActsLikeClear(xfermode, paint.getAlpha())) {
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();
154 } else {
155 is_solid_color_ = false;
157 ++draw_op_count_;
160 void AnalysisCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
161 is_solid_color_ = false;
162 is_transparent_ = false;
163 ++draw_op_count_;
166 void AnalysisCanvas::onDrawRRect(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;
172 ++draw_op_count_;
175 void AnalysisCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
176 is_solid_color_ = false;
177 is_transparent_ = false;
178 ++draw_op_count_;
181 void AnalysisCanvas::onDrawBitmap(const SkBitmap& bitmap,
182 SkScalar left,
183 SkScalar top,
184 const SkPaint*) {
185 is_solid_color_ = false;
186 is_transparent_ = false;
187 ++draw_op_count_;
190 void AnalysisCanvas::onDrawBitmapRect(const SkBitmap&,
191 const SkRect* src,
192 const SkRect& dst,
193 const SkPaint* paint,
194 DrawBitmapRectFlags flags) {
195 // Call drawRect to determine transparency,
196 // but reset solid color to false.
197 SkPaint tmpPaint;
198 if (!paint)
199 paint = &tmpPaint;
200 drawRect(dst, *paint);
201 is_solid_color_ = false;
202 ++draw_op_count_;
205 void AnalysisCanvas::onDrawBitmapNine(const SkBitmap& bitmap,
206 const SkIRect& center,
207 const SkRect& dst,
208 const SkPaint* paint) {
209 is_solid_color_ = false;
210 is_transparent_ = false;
211 ++draw_op_count_;
214 void AnalysisCanvas::onDrawSprite(const SkBitmap& bitmap,
215 int left,
216 int top,
217 const SkPaint* paint) {
218 is_solid_color_ = false;
219 is_transparent_ = false;
220 ++draw_op_count_;
223 void AnalysisCanvas::onDrawText(const void* text,
224 size_t len,
225 SkScalar x,
226 SkScalar y,
227 const SkPaint& paint) {
228 is_solid_color_ = false;
229 is_transparent_ = false;
230 ++draw_op_count_;
233 void AnalysisCanvas::onDrawPosText(const void* text,
234 size_t byteLength,
235 const SkPoint pos[],
236 const SkPaint& paint) {
237 is_solid_color_ = false;
238 is_transparent_ = false;
239 ++draw_op_count_;
242 void AnalysisCanvas::onDrawPosTextH(const void* text,
243 size_t byteLength,
244 const SkScalar xpos[],
245 SkScalar constY,
246 const SkPaint& paint) {
247 is_solid_color_ = false;
248 is_transparent_ = false;
249 ++draw_op_count_;
252 void AnalysisCanvas::onDrawTextOnPath(const void* text,
253 size_t len,
254 const SkPath& path,
255 const SkMatrix* matrix,
256 const SkPaint& paint) {
257 is_solid_color_ = false;
258 is_transparent_ = false;
259 ++draw_op_count_;
262 void AnalysisCanvas::onDrawTextBlob(const SkTextBlob* blob,
263 SkScalar x,
264 SkScalar y,
265 const SkPaint &paint) {
266 is_solid_color_ = false;
267 is_transparent_ = false;
268 ++draw_op_count_;
271 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer,
272 const SkRRect& inner,
273 const SkPaint& paint) {
274 is_solid_color_ = false;
275 is_transparent_ = false;
276 ++draw_op_count_;
279 void AnalysisCanvas::onDrawVertices(SkCanvas::VertexMode,
280 int vertex_count,
281 const SkPoint verts[],
282 const SkPoint texs[],
283 const SkColor colors[],
284 SkXfermode* xmode,
285 const uint16_t indices[],
286 int index_count,
287 const SkPaint& paint) {
288 is_solid_color_ = false;
289 is_transparent_ = false;
290 ++draw_op_count_;
293 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
294 // by any pixels
295 static SkBitmap MakeEmptyBitmap(int width, int height) {
296 SkBitmap bitmap;
297 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
298 return bitmap;
301 AnalysisCanvas::AnalysisCanvas(int width, int height)
302 : INHERITED(MakeEmptyBitmap(width, height)),
303 saved_stack_size_(0),
304 force_not_solid_stack_level_(kNoLayer),
305 force_not_transparent_stack_level_(kNoLayer),
306 is_forced_not_solid_(false),
307 is_forced_not_transparent_(false),
308 is_solid_color_(true),
309 color_(SK_ColorTRANSPARENT),
310 is_transparent_(true),
311 draw_op_count_(0) {
314 AnalysisCanvas::~AnalysisCanvas() {}
316 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
317 if (is_transparent_) {
318 *color = SK_ColorTRANSPARENT;
319 return true;
321 if (is_solid_color_) {
322 *color = color_;
323 return true;
325 return false;
328 bool AnalysisCanvas::abortDrawing() {
329 // Early out as soon as we have more than one draw op.
330 // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to
331 // balance the amount of time we spend analyzing vs how many tiles would be
332 // solid if the number was higher.
333 if (draw_op_count_ > 1) {
334 // We have to reset solid/transparent state to false since we don't
335 // know whether consequent operations will make this false.
336 is_solid_color_ = false;
337 is_transparent_ = false;
338 return true;
340 return false;
343 void AnalysisCanvas::OnComplexClip() {
344 // complex clips can make our calls to IsFullQuad invalid (ie have false
345 // positives). As a precaution, force the setting to be non-solid
346 // and non-transparent until we pop this
347 if (force_not_solid_stack_level_ == kNoLayer) {
348 force_not_solid_stack_level_ = saved_stack_size_;
349 SetForceNotSolid(true);
351 if (force_not_transparent_stack_level_ == kNoLayer) {
352 force_not_transparent_stack_level_ = saved_stack_size_;
353 SetForceNotTransparent(true);
357 void AnalysisCanvas::onClipRect(const SkRect& rect,
358 SkRegion::Op op,
359 ClipEdgeStyle edge_style) {
360 INHERITED::onClipRect(rect, op, edge_style);
363 void AnalysisCanvas::onClipPath(const SkPath& path,
364 SkRegion::Op op,
365 ClipEdgeStyle edge_style) {
366 OnComplexClip();
367 INHERITED::onClipRect(path.getBounds(), op, edge_style);
370 void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
371 SkRegion::Op op,
372 ClipEdgeStyle edge_style) {
373 OnComplexClip();
374 INHERITED::onClipRect(rrect.getBounds(), op, edge_style);
377 void AnalysisCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
378 const ClipEdgeStyle edge_style = kHard_ClipEdgeStyle;
379 if (deviceRgn.isRect()) {
380 onClipRect(SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
381 return;
383 OnComplexClip();
384 INHERITED::onClipRect(
385 SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
388 void AnalysisCanvas::willSave() {
389 ++saved_stack_size_;
390 INHERITED::willSave();
393 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
394 const SkRect* bounds,
395 const SkPaint* paint,
396 SkCanvas::SaveFlags flags) {
398 ++saved_stack_size_;
400 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
401 SkRect canvas_bounds;
402 canvas_bounds.set(canvas_ibounds);
404 // If after we draw to the saved layer, we have to blend with the current
405 // layer, then we can conservatively say that the canvas will not be of
406 // solid color.
407 if ((paint && !IsSolidColorPaint(*paint)) ||
408 (bounds && !bounds->contains(canvas_bounds))) {
409 if (force_not_solid_stack_level_ == kNoLayer) {
410 force_not_solid_stack_level_ = saved_stack_size_;
411 SetForceNotSolid(true);
415 // If after we draw to the save layer, we have to blend with the current
416 // layer using any part of the current layer's alpha, then we can
417 // conservatively say that the canvas will not be transparent.
418 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
419 if (paint)
420 SkXfermode::AsMode(paint->getXfermode(), &xfermode);
421 if (xfermode != SkXfermode::kDst_Mode) {
422 if (force_not_transparent_stack_level_ == kNoLayer) {
423 force_not_transparent_stack_level_ = saved_stack_size_;
424 SetForceNotTransparent(true);
428 INHERITED::willSaveLayer(bounds, paint, flags);
429 // Actually saving a layer here could cause a new bitmap to be created
430 // and real rendering to occur.
431 return kNoLayer_SaveLayerStrategy;
434 void AnalysisCanvas::willRestore() {
435 DCHECK(saved_stack_size_);
436 if (saved_stack_size_) {
437 --saved_stack_size_;
438 if (saved_stack_size_ < force_not_solid_stack_level_) {
439 SetForceNotSolid(false);
440 force_not_solid_stack_level_ = kNoLayer;
442 if (saved_stack_size_ < force_not_transparent_stack_level_) {
443 SetForceNotTransparent(false);
444 force_not_transparent_stack_level_ = kNoLayer;
448 INHERITED::willRestore();
451 } // namespace skia