Home | History | Annotate | Download | only in layers
      1 // Copyright 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 "cc/layers/painted_scrollbar_layer.h"
      6 
      7 #include "base/auto_reset.h"
      8 #include "base/basictypes.h"
      9 #include "base/debug/trace_event.h"
     10 #include "cc/layers/painted_scrollbar_layer_impl.h"
     11 #include "cc/resources/ui_resource_bitmap.h"
     12 #include "cc/trees/layer_tree_host.h"
     13 #include "cc/trees/layer_tree_impl.h"
     14 #include "skia/ext/platform_canvas.h"
     15 #include "skia/ext/refptr.h"
     16 #include "third_party/skia/include/core/SkBitmap.h"
     17 #include "third_party/skia/include/core/SkCanvas.h"
     18 #include "third_party/skia/include/core/SkSize.h"
     19 #include "ui/gfx/skia_util.h"
     20 
     21 namespace cc {
     22 
     23 scoped_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl(
     24     LayerTreeImpl* tree_impl) {
     25   return PaintedScrollbarLayerImpl::Create(
     26       tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>();
     27 }
     28 
     29 scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create(
     30     scoped_ptr<Scrollbar> scrollbar,
     31     int scroll_layer_id) {
     32   return make_scoped_refptr(
     33       new PaintedScrollbarLayer(scrollbar.Pass(), scroll_layer_id));
     34 }
     35 
     36 PaintedScrollbarLayer::PaintedScrollbarLayer(scoped_ptr<Scrollbar> scrollbar,
     37                                              int scroll_layer_id)
     38     : scrollbar_(scrollbar.Pass()),
     39       scroll_layer_id_(scroll_layer_id),
     40       clip_layer_id_(Layer::INVALID_ID),
     41       thumb_thickness_(scrollbar_->ThumbThickness()),
     42       thumb_length_(scrollbar_->ThumbLength()),
     43       is_overlay_(scrollbar_->IsOverlay()),
     44       has_thumb_(scrollbar_->HasThumb()) {
     45   if (!scrollbar_->IsOverlay())
     46     SetShouldScrollOnMainThread(true);
     47 }
     48 
     49 PaintedScrollbarLayer::~PaintedScrollbarLayer() {}
     50 
     51 int PaintedScrollbarLayer::ScrollLayerId() const {
     52   return scroll_layer_id_;
     53 }
     54 
     55 void PaintedScrollbarLayer::SetScrollLayer(int layer_id) {
     56   if (layer_id == scroll_layer_id_)
     57     return;
     58 
     59   scroll_layer_id_ = layer_id;
     60   SetNeedsFullTreeSync();
     61 }
     62 
     63 void PaintedScrollbarLayer::SetClipLayer(int layer_id) {
     64   if (layer_id == clip_layer_id_)
     65     return;
     66 
     67   clip_layer_id_ = layer_id;
     68   SetNeedsFullTreeSync();
     69 }
     70 
     71 bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
     72   return scrollbar_->IsOverlay();
     73 }
     74 
     75 ScrollbarOrientation PaintedScrollbarLayer::orientation() const {
     76   return scrollbar_->Orientation();
     77 }
     78 
     79 int PaintedScrollbarLayer::MaxTextureSize() {
     80   DCHECK(layer_tree_host());
     81   return layer_tree_host()->GetRendererCapabilities().max_texture_size;
     82 }
     83 
     84 float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) {
     85   // If the scaled content_bounds() is bigger than the max texture size of the
     86   // device, we need to clamp it by rescaling, since content_bounds() is used
     87   // below to set the texture size.
     88   gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale);
     89   if (scaled_bounds.width() > MaxTextureSize() ||
     90       scaled_bounds.height() > MaxTextureSize()) {
     91     if (scaled_bounds.width() > scaled_bounds.height())
     92       return (MaxTextureSize() - 1) / static_cast<float>(bounds().width());
     93     else
     94       return (MaxTextureSize() - 1) / static_cast<float>(bounds().height());
     95   }
     96   return scale;
     97 }
     98 
     99 void PaintedScrollbarLayer::CalculateContentsScale(
    100     float ideal_contents_scale,
    101     float* contents_scale_x,
    102     float* contents_scale_y,
    103     gfx::Size* content_bounds) {
    104   ContentsScalingLayer::CalculateContentsScale(
    105       ClampScaleToMaxTextureSize(ideal_contents_scale),
    106       contents_scale_x,
    107       contents_scale_y,
    108       content_bounds);
    109 }
    110 
    111 void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
    112   ContentsScalingLayer::PushPropertiesTo(layer);
    113 
    114   PushScrollClipPropertiesTo(layer);
    115 
    116   PaintedScrollbarLayerImpl* scrollbar_layer =
    117       static_cast<PaintedScrollbarLayerImpl*>(layer);
    118 
    119   scrollbar_layer->SetThumbThickness(thumb_thickness_);
    120   scrollbar_layer->SetThumbLength(thumb_length_);
    121   if (orientation() == HORIZONTAL) {
    122     scrollbar_layer->SetTrackStart(
    123         track_rect_.x() - location_.x());
    124     scrollbar_layer->SetTrackLength(track_rect_.width());
    125   } else {
    126     scrollbar_layer->SetTrackStart(
    127         track_rect_.y() - location_.y());
    128     scrollbar_layer->SetTrackLength(track_rect_.height());
    129   }
    130 
    131   if (track_resource_.get())
    132     scrollbar_layer->set_track_ui_resource_id(track_resource_->id());
    133   else
    134     scrollbar_layer->set_track_ui_resource_id(0);
    135   if (thumb_resource_.get())
    136     scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id());
    137   else
    138     scrollbar_layer->set_thumb_ui_resource_id(0);
    139 
    140   scrollbar_layer->set_is_overlay_scrollbar(is_overlay_);
    141 }
    142 
    143 ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() {
    144   return this;
    145 }
    146 
    147 void PaintedScrollbarLayer::PushScrollClipPropertiesTo(LayerImpl* layer) {
    148   PaintedScrollbarLayerImpl* scrollbar_layer =
    149       static_cast<PaintedScrollbarLayerImpl*>(layer);
    150 
    151   scrollbar_layer->SetScrollLayerAndClipLayerByIds(scroll_layer_id_,
    152                                                    clip_layer_id_);
    153 }
    154 
    155 void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
    156   // When the LTH is set to null or has changed, then this layer should remove
    157   // all of its associated resources.
    158   if (!host || host != layer_tree_host()) {
    159     track_resource_.reset();
    160     thumb_resource_.reset();
    161   }
    162 
    163   ContentsScalingLayer::SetLayerTreeHost(host);
    164 }
    165 
    166 gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect(
    167     const gfx::Rect& layer_rect) const {
    168   // Don't intersect with the bounds as in LayerRectToContentRect() because
    169   // layer_rect here might be in coordinates of the containing layer.
    170   gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect(
    171       layer_rect, contents_scale_x(), contents_scale_y());
    172   // We should never return a rect bigger than the content_bounds().
    173   gfx::Size clamped_size = expanded_rect.size();
    174   clamped_size.SetToMin(content_bounds());
    175   expanded_rect.set_size(clamped_size);
    176   return expanded_rect;
    177 }
    178 
    179 gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const {
    180   gfx::Size thumb_size;
    181   if (orientation() == HORIZONTAL) {
    182     thumb_size =
    183         gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness());
    184   } else {
    185     thumb_size =
    186         gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength());
    187   }
    188   return gfx::Rect(thumb_size);
    189 }
    190 
    191 void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
    192   UpdateProperty(scrollbar_->TrackRect(), &track_rect_);
    193   UpdateProperty(scrollbar_->Location(), &location_);
    194   UpdateProperty(scrollbar_->IsOverlay(), &is_overlay_);
    195   UpdateProperty(scrollbar_->HasThumb(), &has_thumb_);
    196   if (has_thumb_) {
    197     UpdateProperty(scrollbar_->ThumbThickness(), &thumb_thickness_);
    198     UpdateProperty(scrollbar_->ThumbLength(), &thumb_length_);
    199   } else {
    200     UpdateProperty(0, &thumb_thickness_);
    201     UpdateProperty(0, &thumb_length_);
    202   }
    203 }
    204 
    205 bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue,
    206                                    const OcclusionTracker<Layer>* occlusion) {
    207   UpdateThumbAndTrackGeometry();
    208 
    209   gfx::Rect track_layer_rect = gfx::Rect(location_, bounds());
    210   gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect(
    211       track_layer_rect);
    212 
    213   bool updated = false;
    214 
    215   if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty()) {
    216     if (track_resource_) {
    217       track_resource_.reset();
    218       if (thumb_resource_)
    219         thumb_resource_.reset();
    220       SetNeedsPushProperties();
    221       updated = true;
    222     }
    223     return updated;
    224   }
    225 
    226   if (!has_thumb_ && thumb_resource_) {
    227     thumb_resource_.reset();
    228     SetNeedsPushProperties();
    229   }
    230 
    231   {
    232     base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
    233                                                   true);
    234     ContentsScalingLayer::Update(queue, occlusion);
    235   }
    236 
    237   if (update_rect_.IsEmpty() && track_resource_)
    238     return updated;
    239 
    240   track_resource_ = ScopedUIResource::Create(
    241       layer_tree_host(),
    242       RasterizeScrollbarPart(track_layer_rect, scaled_track_rect, TRACK));
    243 
    244   gfx::Rect thumb_layer_rect = OriginThumbRect();
    245   gfx::Rect scaled_thumb_rect =
    246       ScrollbarLayerRectToContentRect(thumb_layer_rect);
    247   if (has_thumb_ && !scaled_thumb_rect.IsEmpty()) {
    248     thumb_resource_ = ScopedUIResource::Create(
    249         layer_tree_host(),
    250         RasterizeScrollbarPart(thumb_layer_rect, scaled_thumb_rect, THUMB));
    251   }
    252 
    253   // UI resources changed so push properties is needed.
    254   SetNeedsPushProperties();
    255   updated = true;
    256   return updated;
    257 }
    258 
    259 UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart(
    260     const gfx::Rect& layer_rect,
    261     const gfx::Rect& content_rect,
    262     ScrollbarPart part) {
    263   DCHECK(!content_rect.size().IsEmpty());
    264   DCHECK(!layer_rect.size().IsEmpty());
    265 
    266   SkBitmap skbitmap;
    267   skbitmap.allocN32Pixels(content_rect.width(), content_rect.height());
    268   SkCanvas skcanvas(skbitmap);
    269 
    270   float scale_x =
    271       content_rect.width() / static_cast<float>(layer_rect.width());
    272   float scale_y =
    273       content_rect.height() / static_cast<float>(layer_rect.height());
    274 
    275   skcanvas.scale(SkFloatToScalar(scale_x),
    276                  SkFloatToScalar(scale_y));
    277   skcanvas.translate(SkFloatToScalar(-layer_rect.x()),
    278                      SkFloatToScalar(-layer_rect.y()));
    279 
    280   SkRect layer_skrect = RectToSkRect(layer_rect);
    281   SkPaint paint;
    282   paint.setAntiAlias(false);
    283   paint.setXfermodeMode(SkXfermode::kClear_Mode);
    284   skcanvas.drawRect(layer_skrect, paint);
    285   skcanvas.clipRect(layer_skrect);
    286 
    287   scrollbar_->PaintPart(&skcanvas, part, layer_rect);
    288   // Make sure that the pixels are no longer mutable to unavoid unnecessary
    289   // allocation and copying.
    290   skbitmap.setImmutable();
    291 
    292   return UIResourceBitmap(skbitmap);
    293 }
    294 
    295 }  // namespace cc
    296