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/SkDevice.h"
9#include "third_party/skia/include/core/SkDraw.h"
10#include "third_party/skia/include/core/SkRRect.h"
11#include "third_party/skia/include/core/SkShader.h"
12#include "third_party/skia/src/core/SkRasterClip.h"
13#include "ui/gfx/rect_conversions.h"
14
15namespace {
16
17const int kNoLayer = -1;
18
19bool IsSolidColorPaint(const SkPaint& paint) {
20  SkXfermode::Mode xfermode;
21
22  // getXfermode can return a NULL, but that is handled
23  // gracefully by AsMode (NULL turns into kSrcOver mode).
24  SkXfermode::AsMode(paint.getXfermode(), &xfermode);
25
26  // Paint is solid color if the following holds:
27  // - Alpha is 1.0, style is fill, and there are no special effects
28  // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent
29  //   to kSrc if source alpha is 1.0, which is already checked).
30  return (paint.getAlpha() == 255 &&
31          !paint.getShader() &&
32          !paint.getLooper() &&
33          !paint.getMaskFilter() &&
34          !paint.getColorFilter() &&
35          paint.getStyle() == SkPaint::kFill_Style &&
36          (xfermode == SkXfermode::kSrc_Mode ||
37           xfermode == SkXfermode::kSrcOver_Mode));
38}
39
40bool IsFullQuad(const SkDraw& draw,
41                const SkRect& canvas_rect,
42                const SkRect& drawn_rect) {
43
44  // If the transform results in a non-axis aligned
45  // rect, then be conservative and return false.
46  if (!draw.fMatrix->rectStaysRect())
47    return false;
48
49  SkRect draw_bitmap_rect;
50  draw.fBitmap->getBounds(&draw_bitmap_rect);
51  SkRect clip_rect = SkRect::Make(draw.fRC->getBounds());
52  SkRect device_rect;
53  draw.fMatrix->mapRect(&device_rect, drawn_rect);
54
55  // The drawn rect covers the full canvas, if the following conditions hold:
56  // - Clip rect is an actual rectangle.
57  // - The rect we're drawing (post-transform) contains the clip rect.
58  //   That is, all of clip rect will be colored by the rect.
59  // - Clip rect contains the canvas rect.
60  //   That is, we're not clipping to a portion of this canvas.
61  // - The bitmap into which the draw call happens is at least as
62  //   big as the canvas rect
63  return draw.fRC->isRect() &&
64         device_rect.contains(clip_rect) &&
65         clip_rect.contains(canvas_rect) &&
66         draw_bitmap_rect.contains(canvas_rect);
67}
68
69} // namespace
70
71namespace skia {
72
73AnalysisDevice::AnalysisDevice(const SkBitmap& bitmap)
74    : INHERITED(bitmap),
75      is_forced_not_solid_(false),
76      is_forced_not_transparent_(false),
77      is_solid_color_(true),
78      is_transparent_(true),
79      has_text_(false) {}
80
81AnalysisDevice::~AnalysisDevice() {}
82
83bool AnalysisDevice::GetColorIfSolid(SkColor* color) const {
84  if (is_transparent_) {
85    *color = SK_ColorTRANSPARENT;
86    return true;
87  }
88  if (is_solid_color_) {
89    *color = color_;
90    return true;
91  }
92  return false;
93}
94
95bool AnalysisDevice::HasText() const {
96  return has_text_;
97}
98
99void AnalysisDevice::SetForceNotSolid(bool flag) {
100  is_forced_not_solid_ = flag;
101  if (is_forced_not_solid_)
102    is_solid_color_ = false;
103}
104
105void AnalysisDevice::SetForceNotTransparent(bool flag) {
106  is_forced_not_transparent_ = flag;
107  if (is_forced_not_transparent_)
108    is_transparent_ = false;
109}
110
111void AnalysisDevice::clear(SkColor color) {
112  is_transparent_ = (!is_forced_not_transparent_ && SkColorGetA(color) == 0);
113  has_text_ = false;
114
115  if (!is_forced_not_solid_ && SkColorGetA(color) == 255) {
116    is_solid_color_ = true;
117    color_ = color;
118  } else {
119    is_solid_color_ = false;
120  }
121}
122
123void AnalysisDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
124  is_solid_color_ = false;
125  is_transparent_ = false;
126}
127
128void AnalysisDevice::drawPoints(const SkDraw& draw,
129                                SkCanvas::PointMode mode,
130                                size_t count,
131                                const SkPoint points[],
132                                const SkPaint& paint) {
133  is_solid_color_ = false;
134  is_transparent_ = false;
135}
136
137void AnalysisDevice::drawRect(const SkDraw& draw,
138                              const SkRect& rect,
139                              const SkPaint& paint) {
140  bool does_cover_canvas =
141      IsFullQuad(draw, SkRect::MakeWH(width(), height()), rect);
142
143  SkXfermode::Mode xfermode;
144  SkXfermode::AsMode(paint.getXfermode(), &xfermode);
145
146  // This canvas will become transparent if the following holds:
147  // - The quad is a full tile quad
148  // - We're not in "forced not transparent" mode
149  // - Transfer mode is clear (0 color, 0 alpha)
150  //
151  // If the paint alpha is not 0, or if the transfrer mode is
152  // not src, then this canvas will not be transparent.
153  //
154  // In all other cases, we keep the current transparent value
155  if (does_cover_canvas &&
156      !is_forced_not_transparent_ &&
157      xfermode == SkXfermode::kClear_Mode) {
158    is_transparent_ = true;
159    has_text_ = false;
160  } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
161    is_transparent_ = false;
162  }
163
164  // This bitmap is solid if and only if the following holds.
165  // Note that this might be overly conservative:
166  // - We're not in "forced not solid" mode
167  // - Paint is solid color
168  // - The quad is a full tile quad
169  if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
170    is_solid_color_ = true;
171    color_ = paint.getColor();
172    has_text_ = false;
173  } else {
174    is_solid_color_ = false;
175  }
176}
177
178void AnalysisDevice::drawOval(const SkDraw& draw,
179                              const SkRect& oval,
180                              const SkPaint& paint) {
181  is_solid_color_ = false;
182  is_transparent_ = false;
183}
184
185void AnalysisDevice::drawRRect(const SkDraw& draw,
186                               const SkRRect& rr,
187                               const SkPaint& paint) {
188  // This should add the SkRRect to an SkPath, and call
189  // drawPath, but since drawPath ignores the SkPath, just
190  // do the same work here.
191  is_solid_color_ = false;
192  is_transparent_ = false;
193}
194
195void AnalysisDevice::drawPath(const SkDraw& draw,
196                              const SkPath& path,
197                              const SkPaint& paint,
198                              const SkMatrix* pre_path_matrix,
199                              bool path_is_mutable) {
200  is_solid_color_ = false;
201  is_transparent_ = false;
202}
203
204void AnalysisDevice::drawBitmap(const SkDraw& draw,
205                                const SkBitmap& bitmap,
206                                const SkMatrix& matrix,
207                                const SkPaint& paint) {
208  is_solid_color_ = false;
209  is_transparent_ = false;
210}
211
212void AnalysisDevice::drawSprite(const SkDraw& draw,
213                                const SkBitmap& bitmap,
214                                int x,
215                                int y,
216                                const SkPaint& paint) {
217  is_solid_color_ = false;
218  is_transparent_ = false;
219}
220
221void AnalysisDevice::drawBitmapRect(const SkDraw& draw,
222                                    const SkBitmap& bitmap,
223                                    const SkRect* src_or_null,
224                                    const SkRect& dst,
225                                    const SkPaint& paint,
226                                    SkCanvas::DrawBitmapRectFlags flags) {
227  // Call drawRect to determine transparency,
228  // but reset solid color to false.
229  drawRect(draw, dst, paint);
230  is_solid_color_ = false;
231}
232
233void AnalysisDevice::drawText(const SkDraw& draw,
234                              const void* text,
235                              size_t len,
236                              SkScalar x,
237                              SkScalar y,
238                              const SkPaint& paint) {
239  is_solid_color_ = false;
240  is_transparent_ = false;
241  has_text_ = true;
242}
243
244void AnalysisDevice::drawPosText(const SkDraw& draw,
245                                 const void* text,
246                                 size_t len,
247                                 const SkScalar pos[],
248                                 SkScalar const_y,
249                                 int scalars_per_pos,
250                                 const SkPaint& paint) {
251  is_solid_color_ = false;
252  is_transparent_ = false;
253  has_text_ = true;
254}
255
256void AnalysisDevice::drawTextOnPath(const SkDraw& draw,
257                                    const void* text,
258                                    size_t len,
259                                    const SkPath& path,
260                                    const SkMatrix* matrix,
261                                    const SkPaint& paint) {
262  is_solid_color_ = false;
263  is_transparent_ = false;
264  has_text_ = true;
265}
266
267#ifdef SK_BUILD_FOR_ANDROID
268void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw,
269                                       const void* text,
270                                       size_t len,
271                                       const SkPoint pos[],
272                                       const SkPaint& paint,
273                                       const SkPath& path,
274                                       const SkMatrix* matrix) {
275  is_solid_color_ = false;
276  is_transparent_ = false;
277  has_text_ = true;
278}
279#endif
280
281void AnalysisDevice::drawVertices(const SkDraw& draw,
282                                  SkCanvas::VertexMode,
283                                  int vertex_count,
284                                  const SkPoint verts[],
285                                  const SkPoint texs[],
286                                  const SkColor colors[],
287                                  SkXfermode* xmode,
288                                  const uint16_t indices[],
289                                  int index_count,
290                                  const SkPaint& paint) {
291  is_solid_color_ = false;
292  is_transparent_ = false;
293}
294
295void AnalysisDevice::drawDevice(const SkDraw& draw,
296                                SkBaseDevice* device,
297                                int x,
298                                int y,
299                                const SkPaint& paint) {
300  is_solid_color_ = false;
301  is_transparent_ = false;
302}
303
304AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device)
305    : INHERITED(device),
306      saved_stack_size_(0),
307      force_not_solid_stack_level_(kNoLayer),
308      force_not_transparent_stack_level_(kNoLayer) {}
309
310AnalysisCanvas::~AnalysisCanvas() {}
311
312bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
313  return (static_cast<AnalysisDevice*>(getDevice()))->GetColorIfSolid(color);
314}
315
316bool AnalysisCanvas::HasText() const {
317  return (static_cast<AnalysisDevice*>(getDevice()))->HasText();
318}
319
320bool AnalysisCanvas::abortDrawing() {
321  // Early out as soon as we have detected that the tile has text.
322  return HasText();
323}
324
325bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool do_aa) {
326  return INHERITED::clipRect(rect, op, do_aa);
327}
328
329bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool do_aa) {
330  // clipPaths can make our calls to IsFullQuad invalid (ie have false
331  // positives). As a precaution, force the setting to be non-solid
332  // and non-transparent until we pop this
333  if (force_not_solid_stack_level_ == kNoLayer) {
334    force_not_solid_stack_level_ = saved_stack_size_;
335    (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
336  }
337  if (force_not_transparent_stack_level_ == kNoLayer) {
338    force_not_transparent_stack_level_ = saved_stack_size_;
339    (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
340  }
341
342  return INHERITED::clipRect(path.getBounds(), op, do_aa);
343}
344
345bool AnalysisCanvas::clipRRect(const SkRRect& rrect,
346                               SkRegion::Op op,
347                               bool do_aa) {
348  // clipRRect can make our calls to IsFullQuad invalid (ie have false
349  // positives). As a precaution, force the setting to be non-solid
350  // and non-transparent until we pop this
351  if (force_not_solid_stack_level_ == kNoLayer) {
352    force_not_solid_stack_level_ = saved_stack_size_;
353    (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
354  }
355  if (force_not_transparent_stack_level_ == kNoLayer) {
356    force_not_transparent_stack_level_ = saved_stack_size_;
357    (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
358  }
359
360  return INHERITED::clipRect(rrect.getBounds(), op, do_aa);
361}
362
363int AnalysisCanvas::save(SkCanvas::SaveFlags flags) {
364  ++saved_stack_size_;
365  return INHERITED::save(flags);
366}
367
368int AnalysisCanvas::saveLayer(const SkRect* bounds,
369                              const SkPaint* paint,
370                              SkCanvas::SaveFlags flags) {
371  ++saved_stack_size_;
372
373  // If after we draw to the saved layer, we have to blend with the current
374  // layer, then we can conservatively say that the canvas will not be of
375  // solid color.
376  if ((paint && !IsSolidColorPaint(*paint)) ||
377      (bounds && !bounds->contains(SkRect::MakeWH(getDevice()->width(),
378                                                  getDevice()->height())))) {
379    if (force_not_solid_stack_level_ == kNoLayer) {
380      force_not_solid_stack_level_ = saved_stack_size_;
381      (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
382    }
383  }
384
385  // If after we draw to the save layer, we have to blend with the current
386  // layer using any part of the current layer's alpha, then we can
387  // conservatively say that the canvas will not be transparent.
388  SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
389  if (paint)
390    SkXfermode::AsMode(paint->getXfermode(), &xfermode);
391  if (xfermode != SkXfermode::kSrc_Mode) {
392    if (force_not_transparent_stack_level_ == kNoLayer) {
393      force_not_transparent_stack_level_ = saved_stack_size_;
394      (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
395    }
396  }
397
398  // Actually saving a layer here could cause a new bitmap to be created
399  // and real rendering to occur.
400  int count = INHERITED::save(flags);
401  if (bounds) {
402    INHERITED::clipRectBounds(bounds, flags, NULL);
403  }
404  return count;
405}
406
407void AnalysisCanvas::restore() {
408  INHERITED::restore();
409
410  DCHECK(saved_stack_size_);
411  if (saved_stack_size_) {
412    --saved_stack_size_;
413    if (saved_stack_size_ < force_not_solid_stack_level_) {
414      (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(false);
415      force_not_solid_stack_level_ = kNoLayer;
416    }
417    if (saved_stack_size_ < force_not_transparent_stack_level_) {
418      (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(
419          false);
420      force_not_transparent_stack_level_ = kNoLayer;
421    }
422  }
423}
424
425}  // namespace skia
426
427
428