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.
4
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"
13
14namespace {
15
16const int kNoLayer = -1;
17
18bool IsSolidColorPaint(const SkPaint& paint) {
19  SkXfermode::Mode xfermode;
20
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);
24
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 &&
30          !paint.getShader() &&
31          !paint.getLooper() &&
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));
38}
39
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).
42bool IsFullQuad(SkCanvas* canvas, const SkRect& drawn_rect) {
43  if (!canvas->isClipRect())
44    return false;
45
46  SkIRect clip_irect;
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())))
50    return false;
51
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())
56    return false;
57
58  SkRect device_rect;
59  matrix.mapRect(&device_rect, drawn_rect);
60  SkRect clip_rect;
61  clip_rect.set(clip_irect);
62  return device_rect.contains(clip_rect);
63}
64
65} // namespace
66
67namespace skia {
68
69void AnalysisCanvas::SetForceNotSolid(bool flag) {
70  is_forced_not_solid_ = flag;
71  if (is_forced_not_solid_)
72    is_solid_color_ = false;
73}
74
75void AnalysisCanvas::SetForceNotTransparent(bool flag) {
76  is_forced_not_transparent_ = flag;
77  if (is_forced_not_transparent_)
78    is_transparent_ = false;
79}
80
81void AnalysisCanvas::clear(SkColor color) {
82  is_transparent_ = (!is_forced_not_transparent_ && SkColorGetA(color) == 0);
83
84  if (!is_forced_not_solid_ && SkColorGetA(color) == 255) {
85    is_solid_color_ = true;
86    color_ = color;
87  } else {
88    is_solid_color_ = false;
89  }
90}
91
92void 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.
95  if (isClipEmpty())
96    return;
97
98  is_solid_color_ = false;
99  is_transparent_ = false;
100  ++draw_op_count_;
101}
102
103void AnalysisCanvas::drawPoints(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_;
110}
111
112void AnalysisCanvas::drawRect(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;
118  }
119
120  // An extra no-op check SkCanvas.cpp doesn't do.
121  if (paint.nothingToDraw())
122    return;
123
124  bool does_cover_canvas = IsFullQuad(this, rect);
125
126  SkXfermode::Mode xfermode;
127  SkXfermode::AsMode(paint.getXfermode(), &xfermode);
128
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)
133  //
134  // If the paint alpha is not 0, or if the transfrer mode is
135  // not src, then this canvas will not be transparent.
136  //
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;
144  }
145
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;
156  }
157  ++draw_op_count_;
158}
159
160void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
161  is_solid_color_ = false;
162  is_transparent_ = false;
163  ++draw_op_count_;
164}
165
166void 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;
172  ++draw_op_count_;
173}
174
175void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
176  is_solid_color_ = false;
177  is_transparent_ = false;
178  ++draw_op_count_;
179}
180
181void AnalysisCanvas::drawBitmap(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_;
188}
189
190void AnalysisCanvas::drawBitmapRectToRect(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_;
203}
204
205void AnalysisCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
206                                      const SkMatrix& matrix,
207                                      const SkPaint* paint) {
208  is_solid_color_ = false;
209  is_transparent_ = false;
210  ++draw_op_count_;
211}
212
213void AnalysisCanvas::drawBitmapNine(const SkBitmap& bitmap,
214                                    const SkIRect& center,
215                                    const SkRect& dst,
216                                    const SkPaint* paint) {
217  is_solid_color_ = false;
218  is_transparent_ = false;
219  ++draw_op_count_;
220}
221
222void AnalysisCanvas::drawSprite(const SkBitmap& bitmap,
223                                int left,
224                                int top,
225                                const SkPaint* paint) {
226  is_solid_color_ = false;
227  is_transparent_ = false;
228  ++draw_op_count_;
229}
230
231void AnalysisCanvas::onDrawText(const void* text,
232                                size_t len,
233                                SkScalar x,
234                                SkScalar y,
235                                const SkPaint& paint) {
236  is_solid_color_ = false;
237  is_transparent_ = false;
238  ++draw_op_count_;
239}
240
241void AnalysisCanvas::onDrawPosText(const void* text,
242                                   size_t byteLength,
243                                   const SkPoint pos[],
244                                   const SkPaint& paint) {
245  is_solid_color_ = false;
246  is_transparent_ = false;
247  ++draw_op_count_;
248}
249
250void AnalysisCanvas::onDrawPosTextH(const void* text,
251                                    size_t byteLength,
252                                    const SkScalar xpos[],
253                                    SkScalar constY,
254                                    const SkPaint& paint) {
255  is_solid_color_ = false;
256  is_transparent_ = false;
257  ++draw_op_count_;
258}
259
260void AnalysisCanvas::onDrawTextOnPath(const void* text,
261                                      size_t len,
262                                      const SkPath& path,
263                                      const SkMatrix* matrix,
264                                      const SkPaint& paint) {
265  is_solid_color_ = false;
266  is_transparent_ = false;
267  ++draw_op_count_;
268}
269
270void AnalysisCanvas::onDrawTextBlob(const SkTextBlob* blob,
271                                    SkScalar x,
272                                    SkScalar y,
273                                    const SkPaint &paint) {
274  is_solid_color_ = false;
275  is_transparent_ = false;
276  ++draw_op_count_;
277}
278
279void AnalysisCanvas::onDrawDRRect(const SkRRect& outer,
280                                  const SkRRect& inner,
281                                  const SkPaint& paint) {
282  is_solid_color_ = false;
283  is_transparent_ = false;
284  ++draw_op_count_;
285}
286
287void AnalysisCanvas::drawVertices(SkCanvas::VertexMode,
288                                  int vertex_count,
289                                  const SkPoint verts[],
290                                  const SkPoint texs[],
291                                  const SkColor colors[],
292                                  SkXfermode* xmode,
293                                  const uint16_t indices[],
294                                  int index_count,
295                                  const SkPaint& paint) {
296  is_solid_color_ = false;
297  is_transparent_ = false;
298  ++draw_op_count_;
299}
300
301// Needed for now, since SkCanvas requires a bitmap, even if it is not backed
302// by any pixels
303static SkBitmap MakeEmptyBitmap(int width, int height) {
304  SkBitmap bitmap;
305  bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
306  return bitmap;
307}
308
309AnalysisCanvas::AnalysisCanvas(int width, int height)
310    : INHERITED(MakeEmptyBitmap(width, height)),
311      saved_stack_size_(0),
312      force_not_solid_stack_level_(kNoLayer),
313      force_not_transparent_stack_level_(kNoLayer),
314      is_forced_not_solid_(false),
315      is_forced_not_transparent_(false),
316      is_solid_color_(true),
317      color_(SK_ColorTRANSPARENT),
318      is_transparent_(true),
319      draw_op_count_(0) {
320}
321
322AnalysisCanvas::~AnalysisCanvas() {}
323
324bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
325  if (is_transparent_) {
326    *color = SK_ColorTRANSPARENT;
327    return true;
328  }
329  if (is_solid_color_) {
330    *color = color_;
331    return true;
332  }
333  return false;
334}
335
336bool AnalysisCanvas::abortDrawing() {
337  // Early out as soon as we have more than one draw op.
338  // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to
339  // balance the amount of time we spend analyzing vs how many tiles would be
340  // solid if the number was higher.
341  if (draw_op_count_ > 1) {
342    // We have to reset solid/transparent state to false since we don't
343    // know whether consequent operations will make this false.
344    is_solid_color_ = false;
345    is_transparent_ = false;
346    return true;
347  }
348  return false;
349}
350
351void AnalysisCanvas::OnComplexClip() {
352  // complex clips can make our calls to IsFullQuad invalid (ie have false
353  // positives). As a precaution, force the setting to be non-solid
354  // and non-transparent until we pop this
355  if (force_not_solid_stack_level_ == kNoLayer) {
356    force_not_solid_stack_level_ = saved_stack_size_;
357    SetForceNotSolid(true);
358  }
359  if (force_not_transparent_stack_level_ == kNoLayer) {
360    force_not_transparent_stack_level_ = saved_stack_size_;
361    SetForceNotTransparent(true);
362  }
363}
364
365void AnalysisCanvas::onClipRect(const SkRect& rect,
366                                SkRegion::Op op,
367                                ClipEdgeStyle edge_style) {
368  INHERITED::onClipRect(rect, op, edge_style);
369}
370
371void AnalysisCanvas::onClipPath(const SkPath& path,
372                                SkRegion::Op op,
373                                ClipEdgeStyle edge_style) {
374  OnComplexClip();
375  INHERITED::onClipRect(path.getBounds(), op, edge_style);
376}
377
378void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
379                                 SkRegion::Op op,
380                                 ClipEdgeStyle edge_style) {
381  OnComplexClip();
382  INHERITED::onClipRect(rrect.getBounds(), op, edge_style);
383}
384
385void AnalysisCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
386  const ClipEdgeStyle edge_style = kHard_ClipEdgeStyle;
387  if (deviceRgn.isRect()) {
388    onClipRect(SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
389    return;
390  }
391  OnComplexClip();
392  INHERITED::onClipRect(
393      SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
394}
395
396void AnalysisCanvas::willSave() {
397  ++saved_stack_size_;
398  INHERITED::willSave();
399}
400
401SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
402    const SkRect* bounds,
403    const SkPaint* paint,
404    SkCanvas::SaveFlags flags) {
405
406  ++saved_stack_size_;
407
408  SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
409  SkRect canvas_bounds;
410  canvas_bounds.set(canvas_ibounds);
411
412  // If after we draw to the saved layer, we have to blend with the current
413  // layer, then we can conservatively say that the canvas will not be of
414  // solid color.
415  if ((paint && !IsSolidColorPaint(*paint)) ||
416      (bounds && !bounds->contains(canvas_bounds))) {
417    if (force_not_solid_stack_level_ == kNoLayer) {
418      force_not_solid_stack_level_ = saved_stack_size_;
419      SetForceNotSolid(true);
420    }
421  }
422
423  // If after we draw to the save layer, we have to blend with the current
424  // layer using any part of the current layer's alpha, then we can
425  // conservatively say that the canvas will not be transparent.
426  SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
427  if (paint)
428    SkXfermode::AsMode(paint->getXfermode(), &xfermode);
429  if (xfermode != SkXfermode::kSrc_Mode) {
430    if (force_not_transparent_stack_level_ == kNoLayer) {
431      force_not_transparent_stack_level_ = saved_stack_size_;
432      SetForceNotTransparent(true);
433    }
434  }
435
436  INHERITED::willSaveLayer(bounds, paint, flags);
437  // Actually saving a layer here could cause a new bitmap to be created
438  // and real rendering to occur.
439  return kNoLayer_SaveLayerStrategy;
440}
441
442void AnalysisCanvas::willRestore() {
443  DCHECK(saved_stack_size_);
444  if (saved_stack_size_) {
445    --saved_stack_size_;
446    if (saved_stack_size_ < force_not_solid_stack_level_) {
447      SetForceNotSolid(false);
448      force_not_solid_stack_level_ = kNoLayer;
449    }
450    if (saved_stack_size_ < force_not_transparent_stack_level_) {
451      SetForceNotTransparent(false);
452      force_not_transparent_stack_level_ = kNoLayer;
453    }
454  }
455
456  INHERITED::willRestore();
457}
458
459}  // namespace skia
460
461
462