GPU workaround to simulate Out of Memory errors with large textures
[chromium-blink-merge.git] / cc / layers / painted_scrollbar_layer.cc
blob36c9f82f5a810e0bb424d64258462d313e49b6ee
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.
5 #include "cc/layers/painted_scrollbar_layer.h"
7 #include <algorithm>
9 #include "base/auto_reset.h"
10 #include "base/basictypes.h"
11 #include "base/trace_event/trace_event.h"
12 #include "cc/base/math_util.h"
13 #include "cc/layers/painted_scrollbar_layer_impl.h"
14 #include "cc/resources/ui_resource_bitmap.h"
15 #include "cc/trees/layer_tree_host.h"
16 #include "cc/trees/layer_tree_impl.h"
17 #include "skia/ext/platform_canvas.h"
18 #include "skia/ext/refptr.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
20 #include "third_party/skia/include/core/SkCanvas.h"
21 #include "third_party/skia/include/core/SkSize.h"
22 #include "ui/gfx/geometry/size_conversions.h"
23 #include "ui/gfx/skia_util.h"
25 namespace cc {
27 scoped_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl(
28 LayerTreeImpl* tree_impl) {
29 return PaintedScrollbarLayerImpl::Create(
30 tree_impl, id(), scrollbar_->Orientation());
33 scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create(
34 scoped_ptr<Scrollbar> scrollbar,
35 int scroll_layer_id) {
36 return make_scoped_refptr(
37 new PaintedScrollbarLayer(scrollbar.Pass(), scroll_layer_id));
40 PaintedScrollbarLayer::PaintedScrollbarLayer(scoped_ptr<Scrollbar> scrollbar,
41 int scroll_layer_id)
42 : scrollbar_(scrollbar.Pass()),
43 scroll_layer_id_(scroll_layer_id),
44 clip_layer_id_(Layer::INVALID_ID),
45 internal_contents_scale_(0.f),
46 thumb_thickness_(scrollbar_->ThumbThickness()),
47 thumb_length_(scrollbar_->ThumbLength()),
48 is_overlay_(scrollbar_->IsOverlay()),
49 has_thumb_(scrollbar_->HasThumb()) {
50 if (!scrollbar_->IsOverlay())
51 SetShouldScrollOnMainThread(true);
54 PaintedScrollbarLayer::~PaintedScrollbarLayer() {}
56 int PaintedScrollbarLayer::ScrollLayerId() const {
57 return scroll_layer_id_;
60 void PaintedScrollbarLayer::SetScrollLayer(int layer_id) {
61 if (layer_id == scroll_layer_id_)
62 return;
64 scroll_layer_id_ = layer_id;
65 SetNeedsFullTreeSync();
68 void PaintedScrollbarLayer::SetClipLayer(int layer_id) {
69 if (layer_id == clip_layer_id_)
70 return;
72 clip_layer_id_ = layer_id;
73 SetNeedsFullTreeSync();
76 bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
77 return scrollbar_->IsOverlay();
80 ScrollbarOrientation PaintedScrollbarLayer::orientation() const {
81 return scrollbar_->Orientation();
84 int PaintedScrollbarLayer::MaxTextureSize() {
85 DCHECK(layer_tree_host());
86 return layer_tree_host()->GetRendererCapabilities().max_texture_size;
89 float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) {
90 // If the scaled bounds() is bigger than the max texture size of the
91 // device, we need to clamp it by rescaling, since this is used
92 // below to set the texture size.
93 gfx::Size scaled_bounds = gfx::ToCeiledSize(gfx::ScaleSize(bounds(), scale));
94 if (scaled_bounds.width() > MaxTextureSize() ||
95 scaled_bounds.height() > MaxTextureSize()) {
96 if (scaled_bounds.width() > scaled_bounds.height())
97 return (MaxTextureSize() - 1) / static_cast<float>(bounds().width());
98 else
99 return (MaxTextureSize() - 1) / static_cast<float>(bounds().height());
101 return scale;
104 void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
105 Layer::PushPropertiesTo(layer);
107 PushScrollClipPropertiesTo(layer);
109 PaintedScrollbarLayerImpl* scrollbar_layer =
110 static_cast<PaintedScrollbarLayerImpl*>(layer);
112 scrollbar_layer->set_internal_contents_scale_and_bounds(
113 internal_contents_scale_, internal_content_bounds_);
115 scrollbar_layer->SetThumbThickness(thumb_thickness_);
116 scrollbar_layer->SetThumbLength(thumb_length_);
117 if (orientation() == HORIZONTAL) {
118 scrollbar_layer->SetTrackStart(
119 track_rect_.x() - location_.x());
120 scrollbar_layer->SetTrackLength(track_rect_.width());
121 } else {
122 scrollbar_layer->SetTrackStart(
123 track_rect_.y() - location_.y());
124 scrollbar_layer->SetTrackLength(track_rect_.height());
127 if (track_resource_.get())
128 scrollbar_layer->set_track_ui_resource_id(track_resource_->id());
129 else
130 scrollbar_layer->set_track_ui_resource_id(0);
131 if (thumb_resource_.get())
132 scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id());
133 else
134 scrollbar_layer->set_thumb_ui_resource_id(0);
136 scrollbar_layer->set_is_overlay_scrollbar(is_overlay_);
139 ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() {
140 return this;
143 void PaintedScrollbarLayer::PushScrollClipPropertiesTo(LayerImpl* layer) {
144 PaintedScrollbarLayerImpl* scrollbar_layer =
145 static_cast<PaintedScrollbarLayerImpl*>(layer);
147 scrollbar_layer->SetScrollLayerAndClipLayerByIds(scroll_layer_id_,
148 clip_layer_id_);
151 void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
152 // When the LTH is set to null or has changed, then this layer should remove
153 // all of its associated resources.
154 if (!host || host != layer_tree_host()) {
155 track_resource_ = nullptr;
156 thumb_resource_ = nullptr;
159 Layer::SetLayerTreeHost(host);
162 gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect(
163 const gfx::Rect& layer_rect) const {
164 // Don't intersect with the bounds as in LayerRectToContentRect() because
165 // layer_rect here might be in coordinates of the containing layer.
166 gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect(
167 layer_rect, internal_contents_scale_, internal_contents_scale_);
168 // We should never return a rect bigger than the content bounds.
169 gfx::Size clamped_size = expanded_rect.size();
170 clamped_size.SetToMin(internal_content_bounds_);
171 expanded_rect.set_size(clamped_size);
172 return expanded_rect;
175 gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const {
176 gfx::Size thumb_size;
177 if (orientation() == HORIZONTAL) {
178 thumb_size =
179 gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness());
180 } else {
181 thumb_size =
182 gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength());
184 return gfx::Rect(thumb_size);
187 void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
188 UpdateProperty(scrollbar_->TrackRect(), &track_rect_);
189 UpdateProperty(scrollbar_->Location(), &location_);
190 UpdateProperty(scrollbar_->IsOverlay(), &is_overlay_);
191 UpdateProperty(scrollbar_->HasThumb(), &has_thumb_);
192 if (has_thumb_) {
193 UpdateProperty(scrollbar_->ThumbThickness(), &thumb_thickness_);
194 UpdateProperty(scrollbar_->ThumbLength(), &thumb_length_);
195 } else {
196 UpdateProperty(0, &thumb_thickness_);
197 UpdateProperty(0, &thumb_length_);
201 void PaintedScrollbarLayer::UpdateInternalContentScale() {
202 float scale = layer_tree_host()->device_scale_factor();
203 if (layer_tree_host()
204 ->settings()
205 .layer_transforms_should_scale_layer_contents) {
206 gfx::Vector2dF transform_scales =
207 MathUtil::ComputeTransform2dScaleComponents(draw_transform(), scale);
208 scale = std::max(transform_scales.x(), transform_scales.y());
210 bool changed = false;
211 changed |= UpdateProperty(ClampScaleToMaxTextureSize(scale),
212 &internal_contents_scale_);
213 changed |= UpdateProperty(
214 gfx::ToCeiledSize(gfx::ScaleSize(bounds(), internal_contents_scale_)),
215 &internal_content_bounds_);
216 if (changed) {
217 // If the content scale or bounds change, repaint.
218 SetNeedsDisplay();
222 bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue,
223 const OcclusionTracker<Layer>* occlusion) {
225 base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
226 true);
227 Layer::Update(queue, occlusion);
228 UpdateInternalContentScale();
231 UpdateThumbAndTrackGeometry();
233 gfx::Rect track_layer_rect = gfx::Rect(location_, bounds());
234 gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect(
235 track_layer_rect);
237 bool updated = false;
239 if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty()) {
240 if (track_resource_) {
241 track_resource_ = nullptr;
242 thumb_resource_ = nullptr;
243 SetNeedsPushProperties();
244 updated = true;
246 return updated;
249 if (!has_thumb_ && thumb_resource_) {
250 thumb_resource_ = nullptr;
251 SetNeedsPushProperties();
252 updated = true;
255 if (update_rect_.IsEmpty() && track_resource_)
256 return updated;
258 track_resource_ = ScopedUIResource::Create(
259 layer_tree_host(),
260 RasterizeScrollbarPart(track_layer_rect, scaled_track_rect, TRACK));
262 gfx::Rect thumb_layer_rect = OriginThumbRect();
263 gfx::Rect scaled_thumb_rect =
264 ScrollbarLayerRectToContentRect(thumb_layer_rect);
265 if (has_thumb_ && !scaled_thumb_rect.IsEmpty()) {
266 thumb_resource_ = ScopedUIResource::Create(
267 layer_tree_host(),
268 RasterizeScrollbarPart(thumb_layer_rect, scaled_thumb_rect, THUMB));
271 // UI resources changed so push properties is needed.
272 SetNeedsPushProperties();
273 updated = true;
274 return updated;
277 UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart(
278 const gfx::Rect& layer_rect,
279 const gfx::Rect& content_rect,
280 ScrollbarPart part) {
281 DCHECK(!content_rect.size().IsEmpty());
282 DCHECK(!layer_rect.size().IsEmpty());
284 SkBitmap skbitmap;
285 skbitmap.allocN32Pixels(content_rect.width(), content_rect.height());
286 SkCanvas skcanvas(skbitmap);
288 float scale_x =
289 content_rect.width() / static_cast<float>(layer_rect.width());
290 float scale_y =
291 content_rect.height() / static_cast<float>(layer_rect.height());
293 skcanvas.scale(SkFloatToScalar(scale_x),
294 SkFloatToScalar(scale_y));
295 skcanvas.translate(SkFloatToScalar(-layer_rect.x()),
296 SkFloatToScalar(-layer_rect.y()));
298 SkRect layer_skrect = RectToSkRect(layer_rect);
299 SkPaint paint;
300 paint.setAntiAlias(false);
301 paint.setXfermodeMode(SkXfermode::kClear_Mode);
302 skcanvas.drawRect(layer_skrect, paint);
303 skcanvas.clipRect(layer_skrect);
305 scrollbar_->PaintPart(&skcanvas, part, layer_rect);
306 // Make sure that the pixels are no longer mutable to unavoid unnecessary
307 // allocation and copying.
308 skbitmap.setImmutable();
310 return UIResourceBitmap(skbitmap);
313 } // namespace cc