• 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   has_text_ = false;
84 
85   if (!is_forced_not_solid_ && SkColorGetA(color) == 255) {
86     is_solid_color_ = true;
87     color_ = color;
88   } else {
89     is_solid_color_ = false;
90   }
91 }
92 
drawPaint(const SkPaint & paint)93 void AnalysisCanvas::drawPaint(const SkPaint& paint) {
94   // This check is in SkCanvas::drawPaint(), and some of our unittests rely on
95   // on this, so we reproduce it here.
96   if (isClipEmpty())
97     return;
98 
99   is_solid_color_ = false;
100   is_transparent_ = false;
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 }
110 
drawRect(const SkRect & rect,const SkPaint & paint)111 void AnalysisCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
112   // This recreates the early-exit logic in SkCanvas.cpp, which aborts early
113   // if the paint will "draw nothing".
114   if (paint.nothingToDraw())
115     return;
116 
117   bool does_cover_canvas = IsFullQuad(this, rect);
118 
119   SkXfermode::Mode xfermode;
120   SkXfermode::AsMode(paint.getXfermode(), &xfermode);
121 
122   // This canvas will become transparent if the following holds:
123   // - The quad is a full tile quad
124   // - We're not in "forced not transparent" mode
125   // - Transfer mode is clear (0 color, 0 alpha)
126   //
127   // If the paint alpha is not 0, or if the transfrer mode is
128   // not src, then this canvas will not be transparent.
129   //
130   // In all other cases, we keep the current transparent value
131   if (does_cover_canvas &&
132       !is_forced_not_transparent_ &&
133       xfermode == SkXfermode::kClear_Mode) {
134     is_transparent_ = true;
135     has_text_ = false;
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     has_text_ = false;
149   } else {
150     is_solid_color_ = false;
151   }
152 }
153 
drawOval(const SkRect & oval,const SkPaint & paint)154 void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
155   is_solid_color_ = false;
156   is_transparent_ = false;
157 }
158 
drawRRect(const SkRRect & rr,const SkPaint & paint)159 void AnalysisCanvas::drawRRect(const SkRRect& rr, const SkPaint& paint) {
160   // This should add the SkRRect to an SkPath, and call
161   // drawPath, but since drawPath ignores the SkPath, just
162   // do the same work here.
163   is_solid_color_ = false;
164   is_transparent_ = false;
165 }
166 
drawPath(const SkPath & path,const SkPaint & paint)167 void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
168   is_solid_color_ = false;
169   is_transparent_ = false;
170 }
171 
drawBitmap(const SkBitmap & bitmap,SkScalar left,SkScalar top,const SkPaint *)172 void AnalysisCanvas::drawBitmap(const SkBitmap& bitmap,
173                                 SkScalar left,
174                                 SkScalar top,
175                                 const SkPaint*) {
176   is_solid_color_ = false;
177   is_transparent_ = false;
178 }
179 
drawBitmapRectToRect(const SkBitmap &,const SkRect * src,const SkRect & dst,const SkPaint * paint,DrawBitmapRectFlags flags)180 void AnalysisCanvas::drawBitmapRectToRect(const SkBitmap&,
181                                           const SkRect* src,
182                                           const SkRect& dst,
183                                           const SkPaint* paint,
184                                           DrawBitmapRectFlags flags) {
185   // Call drawRect to determine transparency,
186   // but reset solid color to false.
187   SkPaint tmpPaint;
188   if (!paint)
189     paint = &tmpPaint;
190   drawRect(dst, *paint);
191   is_solid_color_ = false;
192 }
193 
drawBitmapMatrix(const SkBitmap & bitmap,const SkMatrix & matrix,const SkPaint * paint)194 void AnalysisCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
195                                       const SkMatrix& matrix,
196                                       const SkPaint* paint) {
197   is_solid_color_ = false;
198   is_transparent_ = false;
199 }
200 
drawBitmapNine(const SkBitmap & bitmap,const SkIRect & center,const SkRect & dst,const SkPaint * paint)201 void AnalysisCanvas::drawBitmapNine(const SkBitmap& bitmap,
202                                     const SkIRect& center,
203                                     const SkRect& dst,
204                                     const SkPaint* paint) {
205   is_solid_color_ = false;
206   is_transparent_ = false;
207 }
208 
drawSprite(const SkBitmap & bitmap,int left,int top,const SkPaint * paint)209 void AnalysisCanvas::drawSprite(const SkBitmap& bitmap,
210                                 int left,
211                                 int top,
212                                 const SkPaint* paint) {
213   is_solid_color_ = false;
214   is_transparent_ = false;
215 }
216 
onDrawText(const void * text,size_t len,SkScalar x,SkScalar y,const SkPaint & paint)217 void AnalysisCanvas::onDrawText(const void* text,
218                                 size_t len,
219                                 SkScalar x,
220                                 SkScalar y,
221                                 const SkPaint& paint) {
222   is_solid_color_ = false;
223   is_transparent_ = false;
224   has_text_ = true;
225 }
226 
onDrawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkPaint & paint)227 void AnalysisCanvas::onDrawPosText(const void* text,
228                                    size_t byteLength,
229                                    const SkPoint pos[],
230                                    const SkPaint& paint) {
231   is_solid_color_ = false;
232   is_transparent_ = false;
233   has_text_ = true;
234 }
235 
onDrawPosTextH(const void * text,size_t byteLength,const SkScalar xpos[],SkScalar constY,const SkPaint & paint)236 void AnalysisCanvas::onDrawPosTextH(const void* text,
237                                     size_t byteLength,
238                                     const SkScalar xpos[],
239                                     SkScalar constY,
240                                     const SkPaint& paint) {
241   is_solid_color_ = false;
242   is_transparent_ = false;
243   has_text_ = true;
244 }
245 
onDrawTextOnPath(const void * text,size_t len,const SkPath & path,const SkMatrix * matrix,const SkPaint & paint)246 void AnalysisCanvas::onDrawTextOnPath(const void* text,
247                                       size_t len,
248                                       const SkPath& path,
249                                       const SkMatrix* matrix,
250                                       const SkPaint& paint) {
251   is_solid_color_ = false;
252   is_transparent_ = false;
253   has_text_ = true;
254 }
255 
onDrawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)256 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer,
257                                   const SkRRect& inner,
258                                   const SkPaint& paint) {
259   is_solid_color_ = false;
260   is_transparent_ = false;
261 }
262 
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)263 void AnalysisCanvas::drawVertices(SkCanvas::VertexMode,
264                                   int vertex_count,
265                                   const SkPoint verts[],
266                                   const SkPoint texs[],
267                                   const SkColor colors[],
268                                   SkXfermode* xmode,
269                                   const uint16_t indices[],
270                                   int index_count,
271                                   const SkPaint& paint) {
272   is_solid_color_ = false;
273   is_transparent_ = false;
274 }
275 
276 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
277 // by any pixels
MakeEmptyBitmap(int width,int height)278 static SkBitmap MakeEmptyBitmap(int width, int height) {
279   SkBitmap bitmap;
280   bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
281   return bitmap;
282 }
283 
AnalysisCanvas(int width,int height)284 AnalysisCanvas::AnalysisCanvas(int width, int height)
285     : INHERITED(MakeEmptyBitmap(width, height)),
286       saved_stack_size_(0),
287       force_not_solid_stack_level_(kNoLayer),
288       force_not_transparent_stack_level_(kNoLayer),
289       is_forced_not_solid_(false),
290       is_forced_not_transparent_(false),
291       is_solid_color_(true),
292       is_transparent_(true),
293       has_text_(false) {}
294 
~AnalysisCanvas()295 AnalysisCanvas::~AnalysisCanvas() {}
296 
GetColorIfSolid(SkColor * color) const297 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
298   if (is_transparent_) {
299     *color = SK_ColorTRANSPARENT;
300     return true;
301   }
302   if (is_solid_color_) {
303     *color = color_;
304     return true;
305   }
306   return false;
307 }
308 
HasText() const309 bool AnalysisCanvas::HasText() const { return has_text_; }
310 
abortDrawing()311 bool AnalysisCanvas::abortDrawing() {
312   // Early out as soon as we have detected that the tile has text.
313   return HasText();
314 }
315 
onClipRect(const SkRect & rect,SkRegion::Op op,ClipEdgeStyle edge_style)316 void AnalysisCanvas::onClipRect(const SkRect& rect, SkRegion::Op op,
317                                 ClipEdgeStyle edge_style) {
318 
319   INHERITED::onClipRect(rect, op, edge_style);
320 }
321 
onClipPath(const SkPath & path,SkRegion::Op op,ClipEdgeStyle edge_style)322 void AnalysisCanvas::onClipPath(const SkPath& path, SkRegion::Op op,
323                                 ClipEdgeStyle edge_style) {
324   // clipPaths can make our calls to IsFullQuad invalid (ie have false
325   // positives). As a precaution, force the setting to be non-solid
326   // and non-transparent until we pop this
327   if (force_not_solid_stack_level_ == kNoLayer) {
328     force_not_solid_stack_level_ = saved_stack_size_;
329     SetForceNotSolid(true);
330   }
331   if (force_not_transparent_stack_level_ == kNoLayer) {
332     force_not_transparent_stack_level_ = saved_stack_size_;
333     SetForceNotTransparent(true);
334   }
335 
336   INHERITED::onClipRect(path.getBounds(), op, edge_style);
337 }
338 
onClipRRect(const SkRRect & rrect,SkRegion::Op op,ClipEdgeStyle edge_style)339 void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
340                                  SkRegion::Op op,
341                                  ClipEdgeStyle edge_style) {
342   // clipRRect 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(rrect.getBounds(), op, edge_style);
355 }
356 
willSave()357 void AnalysisCanvas::willSave() {
358   ++saved_stack_size_;
359   INHERITED::willSave();
360 }
361 
willSaveLayer(const SkRect * bounds,const SkPaint * paint,SkCanvas::SaveFlags flags)362 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
363     const SkRect* bounds,
364     const SkPaint* paint,
365     SkCanvas::SaveFlags flags) {
366 
367   ++saved_stack_size_;
368 
369   SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
370   SkRect canvas_bounds;
371   canvas_bounds.set(canvas_ibounds);
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(canvas_bounds))) {
378     if (force_not_solid_stack_level_ == kNoLayer) {
379       force_not_solid_stack_level_ = saved_stack_size_;
380       SetForceNotSolid(true);
381     }
382   }
383 
384   // If after we draw to the save layer, we have to blend with the current
385   // layer using any part of the current layer's alpha, then we can
386   // conservatively say that the canvas will not be transparent.
387   SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
388   if (paint)
389     SkXfermode::AsMode(paint->getXfermode(), &xfermode);
390   if (xfermode != SkXfermode::kSrc_Mode) {
391     if (force_not_transparent_stack_level_ == kNoLayer) {
392       force_not_transparent_stack_level_ = saved_stack_size_;
393       SetForceNotTransparent(true);
394     }
395   }
396 
397   INHERITED::willSaveLayer(bounds, paint, flags);
398   // Actually saving a layer here could cause a new bitmap to be created
399   // and real rendering to occur.
400   return kNoLayer_SaveLayerStrategy;
401 }
402 
willRestore()403 void AnalysisCanvas::willRestore() {
404   DCHECK(saved_stack_size_);
405   if (saved_stack_size_) {
406     --saved_stack_size_;
407     if (saved_stack_size_ < force_not_solid_stack_level_) {
408       SetForceNotSolid(false);
409       force_not_solid_stack_level_ = kNoLayer;
410     }
411     if (saved_stack_size_ < force_not_transparent_stack_level_) {
412       SetForceNotTransparent(false);
413       force_not_transparent_stack_level_ = kNoLayer;
414     }
415   }
416 
417   INHERITED::willRestore();
418 }
419 
420 }  // namespace skia
421 
422 
423