Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / cc / resources / picture_pile_impl.cc
blob2655d24bb7c034104e04b38705d8ff1b9bbef5ce
1 // Copyright 2012 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 <algorithm>
6 #include <limits>
8 #include "base/debug/trace_event.h"
9 #include "cc/base/region.h"
10 #include "cc/debug/debug_colors.h"
11 #include "cc/resources/picture_pile_impl.h"
12 #include "skia/ext/analysis_canvas.h"
13 #include "third_party/skia/include/core/SkCanvas.h"
14 #include "third_party/skia/include/core/SkPictureRecorder.h"
15 #include "ui/gfx/geometry/rect_conversions.h"
17 namespace {
19 #ifdef NDEBUG
20 const bool kDefaultClearCanvasSetting = false;
21 #else
22 const bool kDefaultClearCanvasSetting = true;
23 #endif
25 } // namespace
27 namespace cc {
29 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
30 return make_scoped_refptr(new PicturePileImpl);
33 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromPicturePile(
34 const PicturePile* other) {
35 return make_scoped_refptr(new PicturePileImpl(other));
38 PicturePileImpl::PicturePileImpl()
39 : background_color_(SK_ColorTRANSPARENT),
40 requires_clear_(true),
41 can_use_lcd_text_(false),
42 is_solid_color_(false),
43 solid_color_(SK_ColorTRANSPARENT),
44 has_any_recordings_(false),
45 clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
46 min_contents_scale_(0.f),
47 slow_down_raster_scale_factor_for_debug_(0),
48 should_attempt_to_use_distance_field_text_(false) {
51 PicturePileImpl::PicturePileImpl(const PicturePile* other)
52 : picture_map_(other->picture_map_),
53 tiling_(other->tiling_),
54 background_color_(SK_ColorTRANSPARENT),
55 requires_clear_(true),
56 can_use_lcd_text_(other->can_use_lcd_text_),
57 is_solid_color_(other->is_solid_color_),
58 solid_color_(other->solid_color_),
59 recorded_viewport_(other->recorded_viewport_),
60 has_any_recordings_(other->has_any_recordings_),
61 clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
62 min_contents_scale_(other->min_contents_scale_),
63 slow_down_raster_scale_factor_for_debug_(
64 other->slow_down_raster_scale_factor_for_debug_),
65 should_attempt_to_use_distance_field_text_(false) {
68 PicturePileImpl::~PicturePileImpl() {
71 void PicturePileImpl::PlaybackToSharedCanvas(SkCanvas* canvas,
72 const gfx::Rect& canvas_rect,
73 float contents_scale) const {
74 RasterCommon(canvas,
75 NULL,
76 canvas_rect,
77 contents_scale,
78 false);
81 void PicturePileImpl::RasterForAnalysis(skia::AnalysisCanvas* canvas,
82 const gfx::Rect& canvas_rect,
83 float contents_scale) const {
84 RasterCommon(canvas, canvas, canvas_rect, contents_scale, true);
87 void PicturePileImpl::PlaybackToCanvas(SkCanvas* canvas,
88 const gfx::Rect& canvas_rect,
89 float contents_scale) const {
90 canvas->discard();
91 if (clear_canvas_with_debug_color_) {
92 // Any non-painted areas in the content bounds will be left in this color.
93 canvas->clear(DebugColors::NonPaintedFillColor());
96 // If this picture has opaque contents, it is guaranteeing that it will
97 // draw an opaque rect the size of the layer. If it is not, then we must
98 // clear this canvas ourselves.
99 if (requires_clear_) {
100 TRACE_EVENT_INSTANT0("cc", "SkCanvas::clear", TRACE_EVENT_SCOPE_THREAD);
101 // Clearing is about ~4x faster than drawing a rect even if the content
102 // isn't covering a majority of the canvas.
103 canvas->clear(SK_ColorTRANSPARENT);
104 } else {
105 // Even if completely covered, for rasterizations that touch the edge of the
106 // layer, we also need to raster the background color underneath the last
107 // texel (since the recording won't cover it) and outside the last texel
108 // (due to linear filtering when using this texture).
109 gfx::Rect content_tiling_rect = gfx::ToEnclosingRect(
110 gfx::ScaleRect(gfx::Rect(tiling_.tiling_size()), contents_scale));
112 // The final texel of content may only be partially covered by a
113 // rasterization; this rect represents the content rect that is fully
114 // covered by content.
115 gfx::Rect deflated_content_tiling_rect = content_tiling_rect;
116 deflated_content_tiling_rect.Inset(0, 0, 1, 1);
117 if (!deflated_content_tiling_rect.Contains(canvas_rect)) {
118 if (clear_canvas_with_debug_color_) {
119 // Any non-painted areas outside of the content bounds are left in
120 // this color. If this is seen then it means that cc neglected to
121 // rerasterize a tile that used to intersect with the content rect
122 // after the content bounds grew.
123 canvas->save();
124 canvas->translate(-canvas_rect.x(), -canvas_rect.y());
125 canvas->clipRect(gfx::RectToSkRect(content_tiling_rect),
126 SkRegion::kDifference_Op);
127 canvas->drawColor(DebugColors::MissingResizeInvalidations(),
128 SkXfermode::kSrc_Mode);
129 canvas->restore();
132 // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
133 // faster than clearing, so special case this.
134 canvas->save();
135 canvas->translate(-canvas_rect.x(), -canvas_rect.y());
136 gfx::Rect inflated_content_tiling_rect = content_tiling_rect;
137 inflated_content_tiling_rect.Inset(0, 0, -1, -1);
138 canvas->clipRect(gfx::RectToSkRect(inflated_content_tiling_rect),
139 SkRegion::kReplace_Op);
140 canvas->clipRect(gfx::RectToSkRect(deflated_content_tiling_rect),
141 SkRegion::kDifference_Op);
142 canvas->drawColor(background_color_, SkXfermode::kSrc_Mode);
143 canvas->restore();
147 RasterCommon(canvas,
148 NULL,
149 canvas_rect,
150 contents_scale,
151 false);
154 void PicturePileImpl::CoalesceRasters(const gfx::Rect& canvas_rect,
155 const gfx::Rect& content_rect,
156 float contents_scale,
157 PictureRegionMap* results) const {
158 DCHECK(results);
159 // Rasterize the collection of relevant picture piles.
160 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
161 content_rect, 1.f / contents_scale);
163 // Make sure pictures don't overlap by keeping track of previous right/bottom.
164 int min_content_left = -1;
165 int min_content_top = -1;
166 int last_row_index = -1;
167 int last_col_index = -1;
168 gfx::Rect last_content_rect;
170 // Coalesce rasters of the same picture into different rects:
171 // - Compute the clip of each of the pile chunks,
172 // - Subtract it from the canvas rect to get difference region
173 // - Later, use the difference region to subtract each of the comprising
174 // rects from the canvas.
175 // Note that in essence, we're trying to mimic clipRegion with intersect op
176 // that also respects the current canvas transform and clip. In order to use
177 // the canvas transform, we must stick to clipRect operations (clipRegion
178 // ignores the transform). Intersect then can be written as subtracting the
179 // negation of the region we're trying to intersect. Luckily, we know that all
180 // of the rects will have to fit into |content_rect|, so we can start with
181 // that and subtract chunk rects to get the region that we need to subtract
182 // from the canvas. Then, we can use clipRect with difference op to subtract
183 // each rect in the region.
184 bool include_borders = true;
185 for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
186 tile_iter;
187 ++tile_iter) {
188 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
189 if (map_iter == picture_map_.end())
190 continue;
191 const PictureInfo& info = map_iter->second;
192 const Picture* picture = info.GetPicture();
193 if (!picture)
194 continue;
196 // This is intentionally *enclosed* rect, so that the clip is aligned on
197 // integral post-scale content pixels and does not extend past the edges
198 // of the picture chunk's layer rect. The min_contents_scale enforces that
199 // enough buffer pixels have been added such that the enclosed rect
200 // encompasses all invalidated pixels at any larger scale level.
201 gfx::Rect chunk_rect = PaddedRect(tile_iter.index());
202 gfx::Rect content_clip =
203 gfx::ScaleToEnclosedRect(chunk_rect, contents_scale);
204 DCHECK(!content_clip.IsEmpty()) << "Layer rect: "
205 << picture->LayerRect().ToString()
206 << "Contents scale: " << contents_scale;
207 content_clip.Intersect(canvas_rect);
209 // Make sure iterator goes top->bottom.
210 DCHECK_GE(tile_iter.index_y(), last_row_index);
211 if (tile_iter.index_y() > last_row_index) {
212 // First tile in a new row.
213 min_content_left = content_clip.x();
214 min_content_top = last_content_rect.bottom();
215 } else {
216 // Make sure iterator goes left->right.
217 DCHECK_GT(tile_iter.index_x(), last_col_index);
218 min_content_left = last_content_rect.right();
219 min_content_top = last_content_rect.y();
222 last_col_index = tile_iter.index_x();
223 last_row_index = tile_iter.index_y();
225 // Only inset if the content_clip is less than then previous min.
226 int inset_left = std::max(0, min_content_left - content_clip.x());
227 int inset_top = std::max(0, min_content_top - content_clip.y());
228 content_clip.Inset(inset_left, inset_top, 0, 0);
230 PictureRegionMap::iterator it = results->find(picture);
231 Region* clip_region;
232 if (it == results->end()) {
233 // The clip for a set of coalesced pictures starts out clipping the entire
234 // canvas. Each picture added to the set must subtract its own bounds
235 // from the clip region, poking a hole so that the picture is unclipped.
236 clip_region = &(*results)[picture];
237 *clip_region = canvas_rect;
238 } else {
239 clip_region = &it->second;
242 DCHECK(clip_region->Contains(content_clip))
243 << "Content clips should not overlap.";
244 clip_region->Subtract(content_clip);
245 last_content_rect = content_clip;
249 void PicturePileImpl::RasterCommon(
250 SkCanvas* canvas,
251 SkDrawPictureCallback* callback,
252 const gfx::Rect& canvas_rect,
253 float contents_scale,
254 bool is_analysis) const {
255 DCHECK(contents_scale >= min_contents_scale_);
257 canvas->translate(-canvas_rect.x(), -canvas_rect.y());
258 gfx::Rect content_tiling_rect = gfx::ToEnclosingRect(
259 gfx::ScaleRect(gfx::Rect(tiling_.tiling_size()), contents_scale));
260 content_tiling_rect.Intersect(canvas_rect);
262 canvas->clipRect(gfx::RectToSkRect(content_tiling_rect),
263 SkRegion::kIntersect_Op);
265 PictureRegionMap picture_region_map;
266 CoalesceRasters(
267 canvas_rect, content_tiling_rect, contents_scale, &picture_region_map);
269 #ifndef NDEBUG
270 Region total_clip;
271 #endif // NDEBUG
273 // Iterate the coalesced map and use each picture's region
274 // to clip the canvas.
275 for (PictureRegionMap::iterator it = picture_region_map.begin();
276 it != picture_region_map.end();
277 ++it) {
278 const Picture* picture = it->first;
279 Region negated_clip_region = it->second;
281 #ifndef NDEBUG
282 Region positive_clip = content_tiling_rect;
283 positive_clip.Subtract(negated_clip_region);
284 // Make sure we never rasterize the same region twice.
285 DCHECK(!total_clip.Intersects(positive_clip));
286 total_clip.Union(positive_clip);
287 #endif // NDEBUG
289 int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
291 for (int j = 0; j < repeat_count; ++j)
292 picture->Raster(canvas, callback, negated_clip_region, contents_scale);
295 #ifndef NDEBUG
296 // Fill the clip with debug color. This allows us to
297 // distinguish between non painted areas and problems with missing
298 // pictures.
299 SkPaint paint;
300 for (Region::Iterator it(total_clip); it.has_rect(); it.next())
301 canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
302 paint.setColor(DebugColors::MissingPictureFillColor());
303 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
304 canvas->drawPaint(paint);
305 #endif // NDEBUG
308 skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() {
309 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
311 gfx::Rect tiling_rect(tiling_.tiling_size());
312 SkPictureRecorder recorder;
313 SkCanvas* canvas =
314 recorder.beginRecording(tiling_rect.width(), tiling_rect.height());
315 if (!tiling_rect.IsEmpty())
316 PlaybackToCanvas(canvas, tiling_rect, 1.0);
317 skia::RefPtr<SkPicture> picture = skia::AdoptRef(recorder.endRecording());
319 return picture;
322 void PicturePileImpl::PerformSolidColorAnalysis(
323 const gfx::Rect& content_rect,
324 float contents_scale,
325 RasterSource::SolidColorAnalysis* analysis) const {
326 DCHECK(analysis);
327 TRACE_EVENT0("cc", "PicturePileImpl::PerformSolidColorAnalysis");
329 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
330 content_rect, 1.0f / contents_scale);
332 layer_rect.Intersect(gfx::Rect(tiling_.tiling_size()));
334 skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height());
336 RasterForAnalysis(&canvas, layer_rect, 1.0f);
338 analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color);
341 void PicturePileImpl::GatherPixelRefs(
342 const gfx::Rect& content_rect,
343 float contents_scale,
344 std::vector<SkPixelRef*>* pixel_refs) const {
345 DCHECK_EQ(0u, pixel_refs->size());
346 for (PixelRefIterator iter(content_rect, contents_scale, this); iter;
347 ++iter) {
348 pixel_refs->push_back(*iter);
352 bool PicturePileImpl::CoversRect(const gfx::Rect& content_rect,
353 float contents_scale) const {
354 if (tiling_.tiling_size().IsEmpty())
355 return false;
356 gfx::Rect layer_rect =
357 gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale);
358 layer_rect.Intersect(gfx::Rect(tiling_.tiling_size()));
360 // Common case inside of viewport to avoid the slower map lookups.
361 if (recorded_viewport_.Contains(layer_rect)) {
362 // Sanity check that there are no false positives in recorded_viewport_.
363 DCHECK(CanRasterSlowTileCheck(layer_rect));
364 return true;
367 return CanRasterSlowTileCheck(layer_rect);
370 gfx::Size PicturePileImpl::GetSize() const {
371 return tiling_.tiling_size();
374 bool PicturePileImpl::IsSolidColor() const {
375 return is_solid_color_;
378 SkColor PicturePileImpl::GetSolidColor() const {
379 DCHECK(IsSolidColor());
380 return solid_color_;
383 bool PicturePileImpl::HasRecordings() const {
384 return has_any_recordings_;
387 gfx::Rect PicturePileImpl::PaddedRect(const PictureMapKey& key) const {
388 gfx::Rect padded_rect = tiling_.TileBounds(key.first, key.second);
389 padded_rect.Inset(-buffer_pixels(), -buffer_pixels(), -buffer_pixels(),
390 -buffer_pixels());
391 return padded_rect;
394 bool PicturePileImpl::CanRasterSlowTileCheck(
395 const gfx::Rect& layer_rect) const {
396 bool include_borders = false;
397 for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
398 tile_iter; ++tile_iter) {
399 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
400 if (map_iter == picture_map_.end())
401 return false;
402 if (!map_iter->second.GetPicture())
403 return false;
405 return true;
408 void PicturePileImpl::SetShouldAttemptToUseDistanceFieldText() {
409 should_attempt_to_use_distance_field_text_ = true;
412 void PicturePileImpl::SetBackgoundColor(SkColor background_color) {
413 background_color_ = background_color;
416 void PicturePileImpl::SetRequiresClear(bool requires_clear) {
417 requires_clear_ = requires_clear;
420 bool PicturePileImpl::ShouldAttemptToUseDistanceFieldText() const {
421 return should_attempt_to_use_distance_field_text_;
424 void PicturePileImpl::AsValueInto(base::debug::TracedValue* pictures) const {
425 gfx::Rect tiling_rect(tiling_.tiling_size());
426 std::set<const void*> appended_pictures;
427 bool include_borders = true;
428 for (TilingData::Iterator tile_iter(&tiling_, tiling_rect, include_borders);
429 tile_iter; ++tile_iter) {
430 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
431 if (map_iter == picture_map_.end())
432 continue;
434 const Picture* picture = map_iter->second.GetPicture();
435 if (picture && (appended_pictures.count(picture) == 0)) {
436 appended_pictures.insert(picture);
437 TracedValue::AppendIDRef(picture, pictures);
442 bool PicturePileImpl::CanUseLCDText() const {
443 return can_use_lcd_text_;
446 PicturePileImpl::PixelRefIterator::PixelRefIterator(
447 const gfx::Rect& content_rect,
448 float contents_scale,
449 const PicturePileImpl* picture_pile)
450 : picture_pile_(picture_pile),
451 layer_rect_(
452 gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale)),
453 tile_iterator_(&picture_pile_->tiling_,
454 layer_rect_,
455 false /* include_borders */) {
456 // Early out if there isn't a single tile.
457 if (!tile_iterator_)
458 return;
460 AdvanceToTilePictureWithPixelRefs();
463 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
466 PicturePileImpl::PixelRefIterator&
467 PicturePileImpl::PixelRefIterator::operator++() {
468 ++pixel_ref_iterator_;
469 if (pixel_ref_iterator_)
470 return *this;
472 ++tile_iterator_;
473 AdvanceToTilePictureWithPixelRefs();
474 return *this;
477 void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() {
478 for (; tile_iterator_; ++tile_iterator_) {
479 PictureMap::const_iterator it =
480 picture_pile_->picture_map_.find(tile_iterator_.index());
481 if (it == picture_pile_->picture_map_.end())
482 continue;
484 const Picture* picture = it->second.GetPicture();
485 if (!picture || (processed_pictures_.count(picture) != 0) ||
486 !picture->WillPlayBackBitmaps())
487 continue;
489 processed_pictures_.insert(picture);
490 pixel_ref_iterator_ = Picture::PixelRefIterator(layer_rect_, picture);
491 if (pixel_ref_iterator_)
492 break;
496 void PicturePileImpl::DidBeginTracing() {
497 std::set<const void*> processed_pictures;
498 for (PictureMap::iterator it = picture_map_.begin();
499 it != picture_map_.end();
500 ++it) {
501 const Picture* picture = it->second.GetPicture();
502 if (picture && (processed_pictures.count(picture) == 0)) {
503 picture->EmitTraceSnapshot();
504 processed_pictures.insert(picture);
509 } // namespace cc