root/cc/layers/painted_scrollbar_layer.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CreateLayerImpl
  2. Create
  3. has_thumb_
  4. ScrollLayerId
  5. SetScrollLayer
  6. SetClipLayer
  7. OpacityCanAnimateOnImplThread
  8. orientation
  9. MaxTextureSize
  10. ClampScaleToMaxTextureSize
  11. CalculateContentsScale
  12. PushPropertiesTo
  13. ToScrollbarLayer
  14. PushScrollClipPropertiesTo
  15. SetLayerTreeHost
  16. ScrollbarLayerRectToContentRect
  17. OriginThumbRect
  18. UpdateThumbAndTrackGeometry
  19. Update
  20. RasterizeScrollbarPart

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/layers/painted_scrollbar_layer.h"

#include "base/auto_reset.h"
#include "base/basictypes.h"
#include "base/debug/trace_event.h"
#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/resources/ui_resource_bitmap.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
#include "skia/ext/platform_canvas.h"
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkSize.h"
#include "ui/gfx/skia_util.h"

namespace cc {

scoped_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl(
    LayerTreeImpl* tree_impl) {
  return PaintedScrollbarLayerImpl::Create(
      tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>();
}

scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create(
    scoped_ptr<Scrollbar> scrollbar,
    int scroll_layer_id) {
  return make_scoped_refptr(
      new PaintedScrollbarLayer(scrollbar.Pass(), scroll_layer_id));
}

PaintedScrollbarLayer::PaintedScrollbarLayer(scoped_ptr<Scrollbar> scrollbar,
                                             int scroll_layer_id)
    : scrollbar_(scrollbar.Pass()),
      scroll_layer_id_(scroll_layer_id),
      clip_layer_id_(Layer::INVALID_ID),
      thumb_thickness_(scrollbar_->ThumbThickness()),
      thumb_length_(scrollbar_->ThumbLength()),
      is_overlay_(scrollbar_->IsOverlay()),
      has_thumb_(scrollbar_->HasThumb()) {
  if (!scrollbar_->IsOverlay())
    SetShouldScrollOnMainThread(true);
}

PaintedScrollbarLayer::~PaintedScrollbarLayer() {}

int PaintedScrollbarLayer::ScrollLayerId() const {
  return scroll_layer_id_;
}

void PaintedScrollbarLayer::SetScrollLayer(int layer_id) {
  if (layer_id == scroll_layer_id_)
    return;

  scroll_layer_id_ = layer_id;
  SetNeedsFullTreeSync();
}

void PaintedScrollbarLayer::SetClipLayer(int layer_id) {
  if (layer_id == clip_layer_id_)
    return;

  clip_layer_id_ = layer_id;
  SetNeedsFullTreeSync();
}

bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
  return scrollbar_->IsOverlay();
}

ScrollbarOrientation PaintedScrollbarLayer::orientation() const {
  return scrollbar_->Orientation();
}

int PaintedScrollbarLayer::MaxTextureSize() {
  DCHECK(layer_tree_host());
  return layer_tree_host()->GetRendererCapabilities().max_texture_size;
}

float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) {
  // If the scaled content_bounds() is bigger than the max texture size of the
  // device, we need to clamp it by rescaling, since content_bounds() is used
  // below to set the texture size.
  gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale);
  if (scaled_bounds.width() > MaxTextureSize() ||
      scaled_bounds.height() > MaxTextureSize()) {
    if (scaled_bounds.width() > scaled_bounds.height())
      return (MaxTextureSize() - 1) / static_cast<float>(bounds().width());
    else
      return (MaxTextureSize() - 1) / static_cast<float>(bounds().height());
  }
  return scale;
}

void PaintedScrollbarLayer::CalculateContentsScale(
    float ideal_contents_scale,
    float device_scale_factor,
    float page_scale_factor,
    bool animating_transform_to_screen,
    float* contents_scale_x,
    float* contents_scale_y,
    gfx::Size* content_bounds) {
  ContentsScalingLayer::CalculateContentsScale(
      ClampScaleToMaxTextureSize(ideal_contents_scale),
      device_scale_factor,
      page_scale_factor,
      animating_transform_to_screen,
      contents_scale_x,
      contents_scale_y,
      content_bounds);
}

void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
  ContentsScalingLayer::PushPropertiesTo(layer);

  PushScrollClipPropertiesTo(layer);

  PaintedScrollbarLayerImpl* scrollbar_layer =
      static_cast<PaintedScrollbarLayerImpl*>(layer);

  scrollbar_layer->SetThumbThickness(thumb_thickness_);
  scrollbar_layer->SetThumbLength(thumb_length_);
  if (orientation() == HORIZONTAL) {
    scrollbar_layer->SetTrackStart(
        track_rect_.x() - location_.x());
    scrollbar_layer->SetTrackLength(track_rect_.width());
  } else {
    scrollbar_layer->SetTrackStart(
        track_rect_.y() - location_.y());
    scrollbar_layer->SetTrackLength(track_rect_.height());
  }

  if (track_resource_.get())
    scrollbar_layer->set_track_ui_resource_id(track_resource_->id());
  if (thumb_resource_.get())
    scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id());

  scrollbar_layer->set_is_overlay_scrollbar(is_overlay_);
}

ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() {
  return this;
}

void PaintedScrollbarLayer::PushScrollClipPropertiesTo(LayerImpl* layer) {
  PaintedScrollbarLayerImpl* scrollbar_layer =
      static_cast<PaintedScrollbarLayerImpl*>(layer);

  scrollbar_layer->SetScrollLayerById(scroll_layer_id_);
  scrollbar_layer->SetClipLayerById(clip_layer_id_);
}

void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
  // When the LTH is set to null or has changed, then this layer should remove
  // all of its associated resources.
  if (!host || host != layer_tree_host()) {
    track_resource_.reset();
    thumb_resource_.reset();
  }

  ContentsScalingLayer::SetLayerTreeHost(host);
}

gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect(
    const gfx::Rect& layer_rect) const {
  // Don't intersect with the bounds as in LayerRectToContentRect() because
  // layer_rect here might be in coordinates of the containing layer.
  gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect(
      layer_rect, contents_scale_x(), contents_scale_y());
  // We should never return a rect bigger than the content_bounds().
  gfx::Size clamped_size = expanded_rect.size();
  clamped_size.SetToMin(content_bounds());
  expanded_rect.set_size(clamped_size);
  return expanded_rect;
}

gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const {
  gfx::Size thumb_size;
  if (orientation() == HORIZONTAL) {
    thumb_size =
        gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness());
  } else {
    thumb_size =
        gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength());
  }
  return gfx::Rect(thumb_size);
}

void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
  UpdateProperty(scrollbar_->TrackRect(), &track_rect_);
  UpdateProperty(scrollbar_->Location(), &location_);
  UpdateProperty(scrollbar_->IsOverlay(), &is_overlay_);
  UpdateProperty(scrollbar_->HasThumb(), &has_thumb_);
  if (has_thumb_) {
    UpdateProperty(scrollbar_->ThumbThickness(), &thumb_thickness_);
    UpdateProperty(scrollbar_->ThumbLength(), &thumb_length_);
  }
}

bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue,
                                   const OcclusionTracker<Layer>* occlusion) {
  UpdateThumbAndTrackGeometry();

  gfx::Rect track_layer_rect = gfx::Rect(location_, bounds());
  gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect(
      track_layer_rect);

  if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty())
    return false;

  {
    base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
                                                  true);
    ContentsScalingLayer::Update(queue, occlusion);
  }

  if (update_rect_.IsEmpty() && track_resource_)
    return false;

  track_resource_ = ScopedUIResource::Create(
      layer_tree_host(),
      RasterizeScrollbarPart(track_layer_rect, scaled_track_rect, TRACK));

  gfx::Rect thumb_layer_rect = OriginThumbRect();
  gfx::Rect scaled_thumb_rect =
      ScrollbarLayerRectToContentRect(thumb_layer_rect);
  if (has_thumb_ && !scaled_thumb_rect.IsEmpty()) {
    thumb_resource_ = ScopedUIResource::Create(
        layer_tree_host(),
        RasterizeScrollbarPart(thumb_layer_rect, scaled_thumb_rect, THUMB));
  }

  // UI resources changed so push properties is needed.
  SetNeedsPushProperties();
  return true;
}

UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart(
    const gfx::Rect& layer_rect,
    const gfx::Rect& content_rect,
    ScrollbarPart part) {
  DCHECK(!content_rect.size().IsEmpty());
  DCHECK(!layer_rect.size().IsEmpty());

  SkBitmap skbitmap;
  skbitmap.allocN32Pixels(content_rect.width(), content_rect.height());
  SkCanvas skcanvas(skbitmap);

  float scale_x =
      content_rect.width() / static_cast<float>(layer_rect.width());
  float scale_y =
      content_rect.height() / static_cast<float>(layer_rect.height());

  skcanvas.scale(SkFloatToScalar(scale_x),
                 SkFloatToScalar(scale_y));
  skcanvas.translate(SkFloatToScalar(-layer_rect.x()),
                     SkFloatToScalar(-layer_rect.y()));

  SkRect layer_skrect = RectToSkRect(layer_rect);
  SkPaint paint;
  paint.setAntiAlias(false);
  paint.setXfermodeMode(SkXfermode::kClear_Mode);
  skcanvas.drawRect(layer_skrect, paint);
  skcanvas.clipRect(layer_skrect);

  scrollbar_->PaintPart(&skcanvas, part, layer_rect);
  // Make sure that the pixels are no longer mutable to unavoid unnecessary
  // allocation and copying.
  skbitmap.setImmutable();

  return UIResourceBitmap(skbitmap);
}

}  // namespace cc

/* [<][>][^][v][top][bottom][index][help] */