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/SkDevice.h"
      9 #include "third_party/skia/include/core/SkDraw.h"
     10 #include "third_party/skia/include/core/SkRRect.h"
     11 #include "third_party/skia/include/core/SkShader.h"
     12 #include "third_party/skia/src/core/SkRasterClip.h"
     13 #include "ui/gfx/rect_conversions.h"
     14 
     15 namespace {
     16 
     17 const int kNoLayer = -1;
     18 
     19 bool IsSolidColorPaint(const SkPaint& paint) {
     20   SkXfermode::Mode xfermode;
     21 
     22   // getXfermode can return a NULL, but that is handled
     23   // gracefully by AsMode (NULL turns into kSrcOver mode).
     24   SkXfermode::AsMode(paint.getXfermode(), &xfermode);
     25 
     26   // Paint is solid color if the following holds:
     27   // - Alpha is 1.0, style is fill, and there are no special effects
     28   // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent
     29   //   to kSrc if source alpha is 1.0, which is already checked).
     30   return (paint.getAlpha() == 255 &&
     31           !paint.getShader() &&
     32           !paint.getLooper() &&
     33           !paint.getMaskFilter() &&
     34           !paint.getColorFilter() &&
     35           paint.getStyle() == SkPaint::kFill_Style &&
     36           (xfermode == SkXfermode::kSrc_Mode ||
     37            xfermode == SkXfermode::kSrcOver_Mode));
     38 }
     39 
     40 bool IsFullQuad(const SkDraw& draw,
     41                 const SkRect& canvas_rect,
     42                 const SkRect& drawn_rect) {
     43 
     44   // If the transform results in a non-axis aligned
     45   // rect, then be conservative and return false.
     46   if (!draw.fMatrix->rectStaysRect())
     47     return false;
     48 
     49   SkRect draw_bitmap_rect;
     50   draw.fBitmap->getBounds(&draw_bitmap_rect);
     51   SkRect clip_rect = SkRect::Make(draw.fRC->getBounds());
     52   SkRect device_rect;
     53   draw.fMatrix->mapRect(&device_rect, drawn_rect);
     54 
     55   // The drawn rect covers the full canvas, if the following conditions hold:
     56   // - Clip rect is an actual rectangle.
     57   // - The rect we're drawing (post-transform) contains the clip rect.
     58   //   That is, all of clip rect will be colored by the rect.
     59   // - Clip rect contains the canvas rect.
     60   //   That is, we're not clipping to a portion of this canvas.
     61   // - The bitmap into which the draw call happens is at least as
     62   //   big as the canvas rect
     63   return draw.fRC->isRect() &&
     64          device_rect.contains(clip_rect) &&
     65          clip_rect.contains(canvas_rect) &&
     66          draw_bitmap_rect.contains(canvas_rect);
     67 }
     68 
     69 } // namespace
     70 
     71 namespace skia {
     72 
     73 AnalysisDevice::AnalysisDevice(const SkBitmap& bitmap)
     74     : INHERITED(bitmap),
     75       is_forced_not_solid_(false),
     76       is_forced_not_transparent_(false),
     77       is_solid_color_(true),
     78       is_transparent_(true),
     79       has_text_(false) {}
     80 
     81 AnalysisDevice::~AnalysisDevice() {}
     82 
     83 bool AnalysisDevice::GetColorIfSolid(SkColor* color) const {
     84   if (is_transparent_) {
     85     *color = SK_ColorTRANSPARENT;
     86     return true;
     87   }
     88   if (is_solid_color_) {
     89     *color = color_;
     90     return true;
     91   }
     92   return false;
     93 }
     94 
     95 bool AnalysisDevice::HasText() const {
     96   return has_text_;
     97 }
     98 
     99 void AnalysisDevice::SetForceNotSolid(bool flag) {
    100   is_forced_not_solid_ = flag;
    101   if (is_forced_not_solid_)
    102     is_solid_color_ = false;
    103 }
    104 
    105 void AnalysisDevice::SetForceNotTransparent(bool flag) {
    106   is_forced_not_transparent_ = flag;
    107   if (is_forced_not_transparent_)
    108     is_transparent_ = false;
    109 }
    110 
    111 void AnalysisDevice::clear(SkColor color) {
    112   is_transparent_ = (!is_forced_not_transparent_ && SkColorGetA(color) == 0);
    113   has_text_ = false;
    114 
    115   if (!is_forced_not_solid_ && SkColorGetA(color) == 255) {
    116     is_solid_color_ = true;
    117     color_ = color;
    118   } else {
    119     is_solid_color_ = false;
    120   }
    121 }
    122 
    123 void AnalysisDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
    124   is_solid_color_ = false;
    125   is_transparent_ = false;
    126 }
    127 
    128 void AnalysisDevice::drawPoints(const SkDraw& draw,
    129                                 SkCanvas::PointMode mode,
    130                                 size_t count,
    131                                 const SkPoint points[],
    132                                 const SkPaint& paint) {
    133   is_solid_color_ = false;
    134   is_transparent_ = false;
    135 }
    136 
    137 void AnalysisDevice::drawRect(const SkDraw& draw,
    138                               const SkRect& rect,
    139                               const SkPaint& paint) {
    140   bool does_cover_canvas =
    141       IsFullQuad(draw, SkRect::MakeWH(width(), height()), rect);
    142 
    143   SkXfermode::Mode xfermode;
    144   SkXfermode::AsMode(paint.getXfermode(), &xfermode);
    145 
    146   // This canvas will become transparent if the following holds:
    147   // - The quad is a full tile quad
    148   // - We're not in "forced not transparent" mode
    149   // - Transfer mode is clear (0 color, 0 alpha)
    150   //
    151   // If the paint alpha is not 0, or if the transfrer mode is
    152   // not src, then this canvas will not be transparent.
    153   //
    154   // In all other cases, we keep the current transparent value
    155   if (does_cover_canvas &&
    156       !is_forced_not_transparent_ &&
    157       xfermode == SkXfermode::kClear_Mode) {
    158     is_transparent_ = true;
    159     has_text_ = false;
    160   } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
    161     is_transparent_ = false;
    162   }
    163 
    164   // This bitmap is solid if and only if the following holds.
    165   // Note that this might be overly conservative:
    166   // - We're not in "forced not solid" mode
    167   // - Paint is solid color
    168   // - The quad is a full tile quad
    169   if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
    170     is_solid_color_ = true;
    171     color_ = paint.getColor();
    172     has_text_ = false;
    173   } else {
    174     is_solid_color_ = false;
    175   }
    176 }
    177 
    178 void AnalysisDevice::drawOval(const SkDraw& draw,
    179                               const SkRect& oval,
    180                               const SkPaint& paint) {
    181   is_solid_color_ = false;
    182   is_transparent_ = false;
    183 }
    184 
    185 void AnalysisDevice::drawRRect(const SkDraw& draw,
    186                                const SkRRect& rr,
    187                                const SkPaint& paint) {
    188   // This should add the SkRRect to an SkPath, and call
    189   // drawPath, but since drawPath ignores the SkPath, just
    190   // do the same work here.
    191   is_solid_color_ = false;
    192   is_transparent_ = false;
    193 }
    194 
    195 void AnalysisDevice::drawPath(const SkDraw& draw,
    196                               const SkPath& path,
    197                               const SkPaint& paint,
    198                               const SkMatrix* pre_path_matrix,
    199                               bool path_is_mutable) {
    200   is_solid_color_ = false;
    201   is_transparent_ = false;
    202 }
    203 
    204 void AnalysisDevice::drawBitmap(const SkDraw& draw,
    205                                 const SkBitmap& bitmap,
    206                                 const SkMatrix& matrix,
    207                                 const SkPaint& paint) {
    208   is_solid_color_ = false;
    209   is_transparent_ = false;
    210 }
    211 
    212 void AnalysisDevice::drawSprite(const SkDraw& draw,
    213                                 const SkBitmap& bitmap,
    214                                 int x,
    215                                 int y,
    216                                 const SkPaint& paint) {
    217   is_solid_color_ = false;
    218   is_transparent_ = false;
    219 }
    220 
    221 void AnalysisDevice::drawBitmapRect(const SkDraw& draw,
    222                                     const SkBitmap& bitmap,
    223                                     const SkRect* src_or_null,
    224                                     const SkRect& dst,
    225                                     const SkPaint& paint,
    226                                     SkCanvas::DrawBitmapRectFlags flags) {
    227   // Call drawRect to determine transparency,
    228   // but reset solid color to false.
    229   drawRect(draw, dst, paint);
    230   is_solid_color_ = false;
    231 }
    232 
    233 void AnalysisDevice::drawText(const SkDraw& draw,
    234                               const void* text,
    235                               size_t len,
    236                               SkScalar x,
    237                               SkScalar y,
    238                               const SkPaint& paint) {
    239   is_solid_color_ = false;
    240   is_transparent_ = false;
    241   has_text_ = true;
    242 }
    243 
    244 void AnalysisDevice::drawPosText(const SkDraw& draw,
    245                                  const void* text,
    246                                  size_t len,
    247                                  const SkScalar pos[],
    248                                  SkScalar const_y,
    249                                  int scalars_per_pos,
    250                                  const SkPaint& paint) {
    251   is_solid_color_ = false;
    252   is_transparent_ = false;
    253   has_text_ = true;
    254 }
    255 
    256 void AnalysisDevice::drawTextOnPath(const SkDraw& draw,
    257                                     const void* text,
    258                                     size_t len,
    259                                     const SkPath& path,
    260                                     const SkMatrix* matrix,
    261                                     const SkPaint& paint) {
    262   is_solid_color_ = false;
    263   is_transparent_ = false;
    264   has_text_ = true;
    265 }
    266 
    267 #ifdef SK_BUILD_FOR_ANDROID
    268 void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw,
    269                                        const void* text,
    270                                        size_t len,
    271                                        const SkPoint pos[],
    272                                        const SkPaint& paint,
    273                                        const SkPath& path,
    274                                        const SkMatrix* matrix) {
    275   is_solid_color_ = false;
    276   is_transparent_ = false;
    277   has_text_ = true;
    278 }
    279 #endif
    280 
    281 void AnalysisDevice::drawVertices(const SkDraw& draw,
    282                                   SkCanvas::VertexMode,
    283                                   int vertex_count,
    284                                   const SkPoint verts[],
    285                                   const SkPoint texs[],
    286                                   const SkColor colors[],
    287                                   SkXfermode* xmode,
    288                                   const uint16_t indices[],
    289                                   int index_count,
    290                                   const SkPaint& paint) {
    291   is_solid_color_ = false;
    292   is_transparent_ = false;
    293 }
    294 
    295 void AnalysisDevice::drawDevice(const SkDraw& draw,
    296                                 SkBaseDevice* device,
    297                                 int x,
    298                                 int y,
    299                                 const SkPaint& paint) {
    300   is_solid_color_ = false;
    301   is_transparent_ = false;
    302 }
    303 
    304 AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device)
    305     : INHERITED(device),
    306       saved_stack_size_(0),
    307       force_not_solid_stack_level_(kNoLayer),
    308       force_not_transparent_stack_level_(kNoLayer) {}
    309 
    310 AnalysisCanvas::~AnalysisCanvas() {}
    311 
    312 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
    313   return (static_cast<AnalysisDevice*>(getDevice()))->GetColorIfSolid(color);
    314 }
    315 
    316 bool AnalysisCanvas::HasText() const {
    317   return (static_cast<AnalysisDevice*>(getDevice()))->HasText();
    318 }
    319 
    320 bool AnalysisCanvas::abortDrawing() {
    321   // Early out as soon as we have detected that the tile has text.
    322   return HasText();
    323 }
    324 
    325 bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool do_aa) {
    326   return INHERITED::clipRect(rect, op, do_aa);
    327 }
    328 
    329 bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool do_aa) {
    330   // clipPaths can make our calls to IsFullQuad invalid (ie have false
    331   // positives). As a precaution, force the setting to be non-solid
    332   // and non-transparent until we pop this
    333   if (force_not_solid_stack_level_ == kNoLayer) {
    334     force_not_solid_stack_level_ = saved_stack_size_;
    335     (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
    336   }
    337   if (force_not_transparent_stack_level_ == kNoLayer) {
    338     force_not_transparent_stack_level_ = saved_stack_size_;
    339     (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
    340   }
    341 
    342   return INHERITED::clipRect(path.getBounds(), op, do_aa);
    343 }
    344 
    345 bool AnalysisCanvas::clipRRect(const SkRRect& rrect,
    346                                SkRegion::Op op,
    347                                bool do_aa) {
    348   // clipRRect can make our calls to IsFullQuad invalid (ie have false
    349   // positives). As a precaution, force the setting to be non-solid
    350   // and non-transparent until we pop this
    351   if (force_not_solid_stack_level_ == kNoLayer) {
    352     force_not_solid_stack_level_ = saved_stack_size_;
    353     (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
    354   }
    355   if (force_not_transparent_stack_level_ == kNoLayer) {
    356     force_not_transparent_stack_level_ = saved_stack_size_;
    357     (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
    358   }
    359 
    360   return INHERITED::clipRect(rrect.getBounds(), op, do_aa);
    361 }
    362 
    363 int AnalysisCanvas::save(SkCanvas::SaveFlags flags) {
    364   ++saved_stack_size_;
    365   return INHERITED::save(flags);
    366 }
    367 
    368 int AnalysisCanvas::saveLayer(const SkRect* bounds,
    369                               const SkPaint* paint,
    370                               SkCanvas::SaveFlags flags) {
    371   ++saved_stack_size_;
    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(SkRect::MakeWH(getDevice()->width(),
    378                                                   getDevice()->height())))) {
    379     if (force_not_solid_stack_level_ == kNoLayer) {
    380       force_not_solid_stack_level_ = saved_stack_size_;
    381       (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
    382     }
    383   }
    384 
    385   // If after we draw to the save layer, we have to blend with the current
    386   // layer using any part of the current layer's alpha, then we can
    387   // conservatively say that the canvas will not be transparent.
    388   SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
    389   if (paint)
    390     SkXfermode::AsMode(paint->getXfermode(), &xfermode);
    391   if (xfermode != SkXfermode::kSrc_Mode) {
    392     if (force_not_transparent_stack_level_ == kNoLayer) {
    393       force_not_transparent_stack_level_ = saved_stack_size_;
    394       (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
    395     }
    396   }
    397 
    398   // Actually saving a layer here could cause a new bitmap to be created
    399   // and real rendering to occur.
    400   int count = INHERITED::save(flags);
    401   if (bounds) {
    402     INHERITED::clipRectBounds(bounds, flags, NULL);
    403   }
    404   return count;
    405 }
    406 
    407 void AnalysisCanvas::restore() {
    408   INHERITED::restore();
    409 
    410   DCHECK(saved_stack_size_);
    411   if (saved_stack_size_) {
    412     --saved_stack_size_;
    413     if (saved_stack_size_ < force_not_solid_stack_level_) {
    414       (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(false);
    415       force_not_solid_stack_level_ = kNoLayer;
    416     }
    417     if (saved_stack_size_ < force_not_transparent_stack_level_) {
    418       (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(
    419           false);
    420       force_not_transparent_stack_level_ = kNoLayer;
    421     }
    422   }
    423 }
    424 
    425 }  // namespace skia
    426 
    427 
    428