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.
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"
20 const bool kDefaultClearCanvasSetting
= false;
22 const bool kDefaultClearCanvasSetting
= true;
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 {
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 {
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
);
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.
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
);
132 // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
133 // faster than clearing, so special case this.
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
);
154 void PicturePileImpl::CoalesceRasters(const gfx::Rect
& canvas_rect
,
155 const gfx::Rect
& content_rect
,
156 float contents_scale
,
157 PictureRegionMap
* results
) const {
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
);
188 PictureMap::const_iterator map_iter
= picture_map_
.find(tile_iter
.index());
189 if (map_iter
== picture_map_
.end())
191 const PictureInfo
& info
= map_iter
->second
;
192 const Picture
* picture
= info
.GetPicture();
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();
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
);
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
;
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(
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
;
267 canvas_rect
, content_tiling_rect
, contents_scale
, &picture_region_map
);
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();
278 const Picture
* picture
= it
->first
;
279 Region negated_clip_region
= it
->second
;
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
);
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
);
296 // Fill the clip with debug color. This allows us to
297 // distinguish between non painted areas and problems with missing
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
);
308 skia::RefPtr
<SkPicture
> PicturePileImpl::GetFlattenedPicture() {
309 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
311 gfx::Rect
tiling_rect(tiling_
.tiling_size());
312 SkPictureRecorder recorder
;
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());
322 void PicturePileImpl::PerformSolidColorAnalysis(
323 const gfx::Rect
& content_rect
,
324 float contents_scale
,
325 RasterSource::SolidColorAnalysis
* analysis
) const {
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
;
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())
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
));
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());
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(),
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())
402 if (!map_iter
->second
.GetPicture())
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())
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
),
452 gfx::ScaleToEnclosingRect(content_rect
, 1.f
/ contents_scale
)),
453 tile_iterator_(&picture_pile_
->tiling_
,
455 false /* include_borders */) {
456 // Early out if there isn't a single tile.
460 AdvanceToTilePictureWithPixelRefs();
463 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
466 PicturePileImpl::PixelRefIterator
&
467 PicturePileImpl::PixelRefIterator::operator++() {
468 ++pixel_ref_iterator_
;
469 if (pixel_ref_iterator_
)
473 AdvanceToTilePictureWithPixelRefs();
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())
484 const Picture
* picture
= it
->second
.GetPicture();
485 if (!picture
|| (processed_pictures_
.count(picture
) != 0) ||
486 !picture
->WillPlayBackBitmaps())
489 processed_pictures_
.insert(picture
);
490 pixel_ref_iterator_
= Picture::PixelRefIterator(layer_rect_
, picture
);
491 if (pixel_ref_iterator_
)
496 void PicturePileImpl::DidBeginTracing() {
497 std::set
<const void*> processed_pictures
;
498 for (PictureMap::iterator it
= picture_map_
.begin();
499 it
!= picture_map_
.end();
501 const Picture
* picture
= it
->second
.GetPicture();
502 if (picture
&& (processed_pictures
.count(picture
) == 0)) {
503 picture
->EmitTraceSnapshot();
504 processed_pictures
.insert(picture
);