• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsSolidColorPaint(const SkPaint & paint)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).
IsFullQuad(SkCanvas * canvas,const SkRect & drawn_rect)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 
SetForceNotSolid(bool flag)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 
SetForceNotTransparent(bool flag)75 void AnalysisCanvas::SetForceNotTransparent(bool flag) {
76   is_forced_not_transparent_ = flag;
77   if (is_forced_not_transparent_)
78     is_transparent_ = false;
79 }
80 
clear(SkColor color)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 
drawPaint(const SkPaint & paint)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 
drawPoints(SkCanvas::PointMode mode,size_t count,const SkPoint points[],const SkPaint & paint)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 
drawRect(const SkRect & rect,const SkPaint & paint)112 void 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 
drawOval(const SkRect & oval,const SkPaint & paint)160 void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
161   is_solid_color_ = false;
162   is_transparent_ = false;
163   ++draw_op_count_;
164 }
165 
drawRRect(const SkRRect & rr,const SkPaint & paint)166 void 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 
drawPath(const SkPath & path,const SkPaint & paint)175 void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
176   is_solid_color_ = false;
177   is_transparent_ = false;
178   ++draw_op_count_;
179 }
180 
drawBitmap(const SkBitmap & bitmap,SkScalar left,SkScalar top,const SkPaint *)181 void 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 
drawBitmapRectToRect(const SkBitmap &,const SkRect * src,const SkRect & dst,const SkPaint * paint,DrawBitmapRectFlags flags)190 void 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 
drawBitmapMatrix(const SkBitmap & bitmap,const SkMatrix & matrix,const SkPaint * paint)205 void 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 
drawBitmapNine(const SkBitmap & bitmap,const SkIRect & center,const SkRect & dst,const SkPaint * paint)213 void 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 
drawSprite(const SkBitmap & bitmap,int left,int top,const SkPaint * paint)222 void 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 
onDrawText(const void * text,size_t len,SkScalar x,SkScalar y,const SkPaint & paint)231 void 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 
onDrawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkPaint & paint)241 void 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 
onDrawPosTextH(const void * text,size_t byteLength,const SkScalar xpos[],SkScalar constY,const SkPaint & paint)250 void 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 
onDrawTextOnPath(const void * text,size_t len,const SkPath & path,const SkMatrix * matrix,const SkPaint & paint)260 void 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 
onDrawTextBlob(const SkTextBlob * blob,SkScalar x,SkScalar y,const SkPaint & paint)270 void 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 
onDrawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)279 void 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 
drawVertices(SkCanvas::VertexMode,int vertex_count,const SkPoint verts[],const SkPoint texs[],const SkColor colors[],SkXfermode * xmode,const uint16_t indices[],int index_count,const SkPaint & paint)287 void 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
MakeEmptyBitmap(int width,int height)303 static SkBitmap MakeEmptyBitmap(int width, int height) {
304   SkBitmap bitmap;
305   bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
306   return bitmap;
307 }
308 
AnalysisCanvas(int width,int height)309 AnalysisCanvas::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 
~AnalysisCanvas()322 AnalysisCanvas::~AnalysisCanvas() {}
323 
GetColorIfSolid(SkColor * color) const324 bool 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 
abortDrawing()336 bool 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 
OnComplexClip()351 void 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 
onClipRect(const SkRect & rect,SkRegion::Op op,ClipEdgeStyle edge_style)365 void AnalysisCanvas::onClipRect(const SkRect& rect,
366                                 SkRegion::Op op,
367                                 ClipEdgeStyle edge_style) {
368   INHERITED::onClipRect(rect, op, edge_style);
369 }
370 
onClipPath(const SkPath & path,SkRegion::Op op,ClipEdgeStyle edge_style)371 void 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 
onClipRRect(const SkRRect & rrect,SkRegion::Op op,ClipEdgeStyle edge_style)378 void 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 
onClipRegion(const SkRegion & deviceRgn,SkRegion::Op op)385 void 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 
willSave()396 void AnalysisCanvas::willSave() {
397   ++saved_stack_size_;
398   INHERITED::willSave();
399 }
400 
willSaveLayer(const SkRect * bounds,const SkPaint * paint,SkCanvas::SaveFlags flags)401 SkCanvas::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 
willRestore()442 void 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