Home | History | Annotate | Download | only in ext
      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.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    303 static SkBitmap MakeEmptyBitmap(int width, int height) {
    304   SkBitmap bitmap;
    305   bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
    306   return bitmap;
    307 }
    308 
    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 
    322 AnalysisCanvas::~AnalysisCanvas() {}
    323 
    324 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 
    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 
    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 
    365 void AnalysisCanvas::onClipRect(const SkRect& rect,
    366                                 SkRegion::Op op,
    367                                 ClipEdgeStyle edge_style) {
    368   INHERITED::onClipRect(rect, op, edge_style);
    369 }
    370 
    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 
    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 
    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 
    396 void AnalysisCanvas::willSave() {
    397   ++saved_stack_size_;
    398   INHERITED::willSave();
    399 }
    400 
    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 
    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