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   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 
     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 
    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 
    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 
    154 void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
    155   is_solid_color_ = false;
    156   is_transparent_ = false;
    157 }
    158 
    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 
    167 void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
    168   is_solid_color_ = false;
    169   is_transparent_ = false;
    170 }
    171 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    278 static SkBitmap MakeEmptyBitmap(int width, int height) {
    279   SkBitmap bitmap;
    280   bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
    281   return bitmap;
    282 }
    283 
    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 
    295 AnalysisCanvas::~AnalysisCanvas() {}
    296 
    297 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 
    309 bool AnalysisCanvas::HasText() const { return has_text_; }
    310 
    311 bool AnalysisCanvas::abortDrawing() {
    312   // Early out as soon as we have detected that the tile has text.
    313   return HasText();
    314 }
    315 
    316 void AnalysisCanvas::onClipRect(const SkRect& rect, SkRegion::Op op,
    317                                 ClipEdgeStyle edge_style) {
    318 
    319   INHERITED::onClipRect(rect, op, edge_style);
    320 }
    321 
    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 
    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 
    357 void AnalysisCanvas::willSave() {
    358   ++saved_stack_size_;
    359   INHERITED::willSave();
    360 }
    361 
    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 
    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