Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / skia / ext / analysis_canvas.cc
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
14 namespace {
15
16 const int kNoLayer = -1;
17
18 bool 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).
42 bool 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
67 namespace skia {
68
69 void AnalysisCanvas::SetForceNotSolid(bool flag) {
70   is_forced_not_solid_ = flag;
71   if (is_forced_not_solid_)
72     is_solid_color_ = false;
73 }
74
75 void AnalysisCanvas::SetForceNotTransparent(bool flag) {
76   is_forced_not_transparent_ = flag;
77   if (is_forced_not_transparent_)
78     is_transparent_ = false;
79 }
80
81 void 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
92 void 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
103 void 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
112 void AnalysisCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
113   // This recreates the early-exit logic in SkCanvas.cpp, which aborts early
114   // if the paint will "draw nothing".
115   if (paint.nothingToDraw())
116     return;
117
118   bool does_cover_canvas = IsFullQuad(this, rect);
119
120   SkXfermode::Mode xfermode;
121   SkXfermode::AsMode(paint.getXfermode(), &xfermode);
122
123   // This canvas will become transparent if the following holds:
124   // - The quad is a full tile quad
125   // - We're not in "forced not transparent" mode
126   // - Transfer mode is clear (0 color, 0 alpha)
127   //
128   // If the paint alpha is not 0, or if the transfrer mode is
129   // not src, then this canvas will not be transparent.
130   //
131   // In all other cases, we keep the current transparent value
132   if (does_cover_canvas &&
133       !is_forced_not_transparent_ &&
134       xfermode == SkXfermode::kClear_Mode) {
135     is_transparent_ = true;
136   } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
137     is_transparent_ = false;
138   }
139
140   // This bitmap is solid if and only if the following holds.
141   // Note that this might be overly conservative:
142   // - We're not in "forced not solid" mode
143   // - Paint is solid color
144   // - The quad is a full tile quad
145   if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
146     is_solid_color_ = true;
147     color_ = paint.getColor();
148   } else {
149     is_solid_color_ = false;
150   }
151   ++draw_op_count_;
152 }
153
154 void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
155   is_solid_color_ = false;
156   is_transparent_ = false;
157   ++draw_op_count_;
158 }
159
160 void AnalysisCanvas::drawRRect(const SkRRect& rr, const SkPaint& paint) {
161   // This should add the SkRRect to an SkPath, and call
162   // drawPath, but since drawPath ignores the SkPath, just
163   // do the same work here.
164   is_solid_color_ = false;
165   is_transparent_ = false;
166   ++draw_op_count_;
167 }
168
169 void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
170   is_solid_color_ = false;
171   is_transparent_ = false;
172   ++draw_op_count_;
173 }
174
175 void AnalysisCanvas::drawBitmap(const SkBitmap& bitmap,
176                                 SkScalar left,
177                                 SkScalar top,
178                                 const SkPaint*) {
179   is_solid_color_ = false;
180   is_transparent_ = false;
181   ++draw_op_count_;
182 }
183
184 void AnalysisCanvas::drawBitmapRectToRect(const SkBitmap&,
185                                           const SkRect* src,
186                                           const SkRect& dst,
187                                           const SkPaint* paint,
188                                           DrawBitmapRectFlags flags) {
189   // Call drawRect to determine transparency,
190   // but reset solid color to false.
191   SkPaint tmpPaint;
192   if (!paint)
193     paint = &tmpPaint;
194   drawRect(dst, *paint);
195   is_solid_color_ = false;
196   ++draw_op_count_;
197 }
198
199 void AnalysisCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
200                                       const SkMatrix& matrix,
201                                       const SkPaint* paint) {
202   is_solid_color_ = false;
203   is_transparent_ = false;
204   ++draw_op_count_;
205 }
206
207 void AnalysisCanvas::drawBitmapNine(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_;
214 }
215
216 void AnalysisCanvas::drawSprite(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_;
223 }
224
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_;
233 }
234
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_;
242 }
243
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_;
252 }
253
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_;
262 }
263
264 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer,
265                                   const SkRRect& inner,
266                                   const SkPaint& paint) {
267   is_solid_color_ = false;
268   is_transparent_ = false;
269   ++draw_op_count_;
270 }
271
272 void AnalysisCanvas::drawVertices(SkCanvas::VertexMode,
273                                   int vertex_count,
274                                   const SkPoint verts[],
275                                   const SkPoint texs[],
276                                   const SkColor colors[],
277                                   SkXfermode* xmode,
278                                   const uint16_t indices[],
279                                   int index_count,
280                                   const SkPaint& paint) {
281   is_solid_color_ = false;
282   is_transparent_ = false;
283   ++draw_op_count_;
284 }
285
286 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
287 // by any pixels
288 static SkBitmap MakeEmptyBitmap(int width, int height) {
289   SkBitmap bitmap;
290   bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
291   return bitmap;
292 }
293
294 AnalysisCanvas::AnalysisCanvas(int width, int height)
295     : INHERITED(MakeEmptyBitmap(width, height)),
296       saved_stack_size_(0),
297       force_not_solid_stack_level_(kNoLayer),
298       force_not_transparent_stack_level_(kNoLayer),
299       is_forced_not_solid_(false),
300       is_forced_not_transparent_(false),
301       is_solid_color_(true),
302       is_transparent_(true),
303       draw_op_count_(0) {}
304
305 AnalysisCanvas::~AnalysisCanvas() {}
306
307 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
308   if (is_transparent_) {
309     *color = SK_ColorTRANSPARENT;
310     return true;
311   }
312   if (is_solid_color_) {
313     *color = color_;
314     return true;
315   }
316   return false;
317 }
318
319 bool AnalysisCanvas::abortDrawing() {
320   // Early out as soon as we have more than one draw op.
321   // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to
322   // balance the amount of time we spend analyzing vs how many tiles would be
323   // solid if the number was higher.
324   if (draw_op_count_ > 1) {
325     // We have to reset solid/transparent state to false since we don't
326     // know whether consequent operations will make this false.
327     is_solid_color_ = false;
328     is_transparent_ = false;
329     return true;
330   }
331   return false;
332 }
333
334 void AnalysisCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, 
335                                 ClipEdgeStyle edge_style) {
336
337   INHERITED::onClipRect(rect, op, edge_style);
338 }
339
340 void AnalysisCanvas::onClipPath(const SkPath& path, SkRegion::Op op,
341                                 ClipEdgeStyle edge_style) {
342   // clipPaths can make our calls to IsFullQuad invalid (ie have false
343   // positives). As a precaution, force the setting to be non-solid
344   // and non-transparent until we pop this
345   if (force_not_solid_stack_level_ == kNoLayer) {
346     force_not_solid_stack_level_ = saved_stack_size_;
347     SetForceNotSolid(true);
348   }
349   if (force_not_transparent_stack_level_ == kNoLayer) {
350     force_not_transparent_stack_level_ = saved_stack_size_;
351     SetForceNotTransparent(true);
352   }
353
354   INHERITED::onClipRect(path.getBounds(), op, edge_style);
355 }
356
357 void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
358                                  SkRegion::Op op,
359                                  ClipEdgeStyle edge_style) {
360   // clipRRect can make our calls to IsFullQuad invalid (ie have false
361   // positives). As a precaution, force the setting to be non-solid
362   // and non-transparent until we pop this
363   if (force_not_solid_stack_level_ == kNoLayer) {
364     force_not_solid_stack_level_ = saved_stack_size_;
365     SetForceNotSolid(true);
366   }
367   if (force_not_transparent_stack_level_ == kNoLayer) {
368     force_not_transparent_stack_level_ = saved_stack_size_;
369     SetForceNotTransparent(true);
370   }
371
372   INHERITED::onClipRect(rrect.getBounds(), op, edge_style);
373 }
374
375 void AnalysisCanvas::willSave() {
376   ++saved_stack_size_;
377   INHERITED::willSave();
378 }
379
380 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
381     const SkRect* bounds,
382     const SkPaint* paint,
383     SkCanvas::SaveFlags flags) {
384
385   ++saved_stack_size_;
386
387   SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
388   SkRect canvas_bounds;
389   canvas_bounds.set(canvas_ibounds);
390
391   // If after we draw to the saved layer, we have to blend with the current
392   // layer, then we can conservatively say that the canvas will not be of
393   // solid color.
394   if ((paint && !IsSolidColorPaint(*paint)) ||
395       (bounds && !bounds->contains(canvas_bounds))) {
396     if (force_not_solid_stack_level_ == kNoLayer) {
397       force_not_solid_stack_level_ = saved_stack_size_;
398       SetForceNotSolid(true);
399     }
400   }
401
402   // If after we draw to the save layer, we have to blend with the current
403   // layer using any part of the current layer's alpha, then we can
404   // conservatively say that the canvas will not be transparent.
405   SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
406   if (paint)
407     SkXfermode::AsMode(paint->getXfermode(), &xfermode);
408   if (xfermode != SkXfermode::kSrc_Mode) {
409     if (force_not_transparent_stack_level_ == kNoLayer) {
410       force_not_transparent_stack_level_ = saved_stack_size_;
411       SetForceNotTransparent(true);
412     }
413   }
414
415   INHERITED::willSaveLayer(bounds, paint, flags);
416   // Actually saving a layer here could cause a new bitmap to be created
417   // and real rendering to occur.
418   return kNoLayer_SaveLayerStrategy;
419 }
420
421 void AnalysisCanvas::willRestore() {
422   DCHECK(saved_stack_size_);
423   if (saved_stack_size_) {
424     --saved_stack_size_;
425     if (saved_stack_size_ < force_not_solid_stack_level_) {
426       SetForceNotSolid(false);
427       force_not_solid_stack_level_ = kNoLayer;
428     }
429     if (saved_stack_size_ < force_not_transparent_stack_level_) {
430       SetForceNotTransparent(false);
431       force_not_transparent_stack_level_ = kNoLayer;
432     }
433   }
434
435   INHERITED::willRestore();
436 }
437
438 }  // namespace skia
439
440