ash: Add missing include to test from r236852.
[chromium-blink-merge.git] / cc / resources / picture_pile_impl.cc
blob44a52b8afd71d20a0fd3158e6a5ba367f4a1af7f
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/SkSize.h"
15 #include "ui/gfx/rect_conversions.h"
16 #include "ui/gfx/size_conversions.h"
17 #include "ui/gfx/skia_util.h"
19 namespace cc {
21 PicturePileImpl::ClonesForDrawing::ClonesForDrawing(
22 const PicturePileImpl* pile, int num_threads) {
23 for (int i = 0; i < num_threads; i++) {
24 scoped_refptr<PicturePileImpl> clone =
25 PicturePileImpl::CreateCloneForDrawing(pile, i);
26 clones_.push_back(clone);
30 PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() {
33 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
34 return make_scoped_refptr(new PicturePileImpl);
37 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther(
38 const PicturePileBase* other) {
39 return make_scoped_refptr(new PicturePileImpl(other));
42 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateCloneForDrawing(
43 const PicturePileImpl* other, unsigned thread_index) {
44 return make_scoped_refptr(new PicturePileImpl(other, thread_index));
47 PicturePileImpl::PicturePileImpl()
48 : clones_for_drawing_(ClonesForDrawing(this, 0)) {
51 PicturePileImpl::PicturePileImpl(const PicturePileBase* other)
52 : PicturePileBase(other),
53 clones_for_drawing_(ClonesForDrawing(this, num_raster_threads())) {
56 PicturePileImpl::PicturePileImpl(
57 const PicturePileImpl* other, unsigned thread_index)
58 : PicturePileBase(other, thread_index),
59 clones_for_drawing_(ClonesForDrawing(this, 0)) {
62 PicturePileImpl::~PicturePileImpl() {
65 PicturePileImpl* PicturePileImpl::GetCloneForDrawingOnThread(
66 unsigned thread_index) const {
67 CHECK_GT(clones_for_drawing_.clones_.size(), thread_index);
68 return clones_for_drawing_.clones_[thread_index].get();
71 void PicturePileImpl::RasterDirect(
72 SkCanvas* canvas,
73 gfx::Rect canvas_rect,
74 float contents_scale,
75 RenderingStatsInstrumentation* rendering_stats_instrumentation) {
76 RasterCommon(canvas,
77 NULL,
78 canvas_rect,
79 contents_scale,
80 rendering_stats_instrumentation);
83 void PicturePileImpl::RasterForAnalysis(
84 skia::AnalysisCanvas* canvas,
85 gfx::Rect canvas_rect,
86 float contents_scale) {
87 RasterCommon(canvas, canvas, canvas_rect, contents_scale, NULL);
90 void PicturePileImpl::RasterToBitmap(
91 SkCanvas* canvas,
92 gfx::Rect canvas_rect,
93 float contents_scale,
94 RenderingStatsInstrumentation* rendering_stats_instrumentation) {
95 if (clear_canvas_with_debug_color_) {
96 // Any non-painted areas will be left in this color.
97 canvas->clear(DebugColors::NonPaintedFillColor());
100 // If this picture has opaque contents, it is guaranteeing that it will
101 // draw an opaque rect the size of the layer. If it is not, then we must
102 // clear this canvas ourselves.
103 if (!contents_opaque_) {
104 // Clearing is about ~4x faster than drawing a rect even if the content
105 // isn't covering a majority of the canvas.
106 canvas->clear(SK_ColorTRANSPARENT);
107 } else {
108 // Even if it is opaque, on any rasterizations that touch the edge of the
109 // layer, we also need to raster the background color underneath the last
110 // texel (since the recording won't cover it) and outside the last texel
111 // (due to linear filtering when using this texture).
112 gfx::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(),
113 contents_scale);
114 gfx::Rect content_rect(gfx::ToCeiledSize(total_content_size));
116 // The final texel of content may only be partially covered by a
117 // rasterization; this rect represents the content rect that is fully
118 // covered by content.
119 gfx::Rect deflated_content_rect = content_rect;
120 deflated_content_rect.Inset(0, 0, 1, 1);
121 if (!deflated_content_rect.Contains(canvas_rect)) {
122 // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
123 // faster than clearing, so special case this.
124 canvas->save();
125 canvas->translate(-canvas_rect.x(), -canvas_rect.y());
126 gfx::Rect inflated_content_rect = content_rect;
127 inflated_content_rect.Inset(0, 0, -1, -1);
128 canvas->clipRect(gfx::RectToSkRect(inflated_content_rect),
129 SkRegion::kReplace_Op);
130 canvas->clipRect(gfx::RectToSkRect(deflated_content_rect),
131 SkRegion::kDifference_Op);
132 canvas->drawColor(background_color_, SkXfermode::kSrc_Mode);
133 canvas->restore();
137 RasterCommon(canvas,
138 NULL,
139 canvas_rect,
140 contents_scale,
141 rendering_stats_instrumentation);
144 void PicturePileImpl::CoalesceRasters(gfx::Rect canvas_rect,
145 gfx::Rect content_rect,
146 float contents_scale,
147 PictureRegionMap* results) {
148 DCHECK(results);
149 // Rasterize the collection of relevant picture piles.
150 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
151 content_rect, 1.f / contents_scale);
153 // Coalesce rasters of the same picture into different rects:
154 // - Compute the clip of each of the pile chunks,
155 // - Subtract it from the canvas rect to get difference region
156 // - Later, use the difference region to subtract each of the comprising
157 // rects from the canvas.
158 // Note that in essence, we're trying to mimic clipRegion with intersect op
159 // that also respects the current canvas transform and clip. In order to use
160 // the canvas transform, we must stick to clipRect operations (clipRegion
161 // ignores the transform). Intersect then can be written as subtracting the
162 // negation of the region we're trying to intersect. Luckily, we know that all
163 // of the rects will have to fit into |content_rect|, so we can start with
164 // that and subtract chunk rects to get the region that we need to subtract
165 // from the canvas. Then, we can use clipRect with difference op to subtract
166 // each rect in the region.
167 for (TilingData::Iterator tile_iter(&tiling_, layer_rect);
168 tile_iter; ++tile_iter) {
169 PictureMap::iterator map_iter = picture_map_.find(tile_iter.index());
170 if (map_iter == picture_map_.end())
171 continue;
172 PictureInfo& info = map_iter->second;
173 if (!info.picture.get())
174 continue;
176 // This is intentionally *enclosed* rect, so that the clip is aligned on
177 // integral post-scale content pixels and does not extend past the edges
178 // of the picture chunk's layer rect. The min_contents_scale enforces that
179 // enough buffer pixels have been added such that the enclosed rect
180 // encompasses all invalidated pixels at any larger scale level.
181 gfx::Rect chunk_rect = PaddedRect(tile_iter.index());
182 gfx::Rect content_clip =
183 gfx::ScaleToEnclosedRect(chunk_rect, contents_scale);
184 DCHECK(!content_clip.IsEmpty()) << "Layer rect: "
185 << info.picture->LayerRect().ToString()
186 << "Contents scale: " << contents_scale;
187 content_clip.Intersect(canvas_rect);
189 PictureRegionMap::iterator it = results->find(info.picture.get());
190 if (it == results->end()) {
191 Region& region = (*results)[info.picture.get()];
192 region = content_rect;
193 region.Subtract(content_clip);
194 continue;
197 Region& region = it->second;
198 region.Subtract(content_clip);
202 void PicturePileImpl::RasterCommon(
203 SkCanvas* canvas,
204 SkDrawPictureCallback* callback,
205 gfx::Rect canvas_rect,
206 float contents_scale,
207 RenderingStatsInstrumentation* rendering_stats_instrumentation) {
208 DCHECK(contents_scale >= min_contents_scale_);
210 canvas->translate(-canvas_rect.x(), -canvas_rect.y());
211 gfx::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(),
212 contents_scale);
213 gfx::Rect total_content_rect(gfx::ToCeiledSize(total_content_size));
214 gfx::Rect content_rect = total_content_rect;
215 content_rect.Intersect(canvas_rect);
217 canvas->clipRect(gfx::RectToSkRect(content_rect),
218 SkRegion::kIntersect_Op);
220 PictureRegionMap picture_region_map;
221 CoalesceRasters(
222 canvas_rect, content_rect, contents_scale, &picture_region_map);
224 #ifndef NDEBUG
225 Region total_clip;
226 #endif // NDEBUG
228 // Iterate the coalesced map and use each picture's region
229 // to clip the canvas.
230 for (PictureRegionMap::iterator it = picture_region_map.begin();
231 it != picture_region_map.end();
232 ++it) {
233 Picture* picture = it->first;
234 Region negated_clip_region = it->second;
236 #ifndef NDEBUG
237 Region positive_clip = content_rect;
238 positive_clip.Subtract(negated_clip_region);
239 total_clip.Union(positive_clip);
240 #endif // NDEBUG
242 base::TimeDelta best_duration =
243 base::TimeDelta::FromInternalValue(std::numeric_limits<int64>::max());
244 int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
245 int rasterized_pixel_count = 0;
247 for (int j = 0; j < repeat_count; ++j) {
248 base::TimeTicks start_time;
249 if (rendering_stats_instrumentation)
250 start_time = rendering_stats_instrumentation->StartRecording();
252 rasterized_pixel_count = picture->Raster(
253 canvas, callback, negated_clip_region, contents_scale);
255 if (rendering_stats_instrumentation) {
256 base::TimeDelta duration =
257 rendering_stats_instrumentation->EndRecording(start_time);
258 best_duration = std::min(best_duration, duration);
262 if (rendering_stats_instrumentation) {
263 rendering_stats_instrumentation->AddRaster(best_duration,
264 rasterized_pixel_count);
268 #ifndef NDEBUG
269 // Fill the clip with debug color. This allows us to
270 // distinguish between non painted areas and problems with missing
271 // pictures.
272 SkPaint paint;
273 for (Region::Iterator it(total_clip); it.has_rect(); it.next())
274 canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
275 paint.setColor(DebugColors::MissingPictureFillColor());
276 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
277 canvas->drawPaint(paint);
278 #endif // NDEBUG
281 skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() {
282 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
284 gfx::Rect layer_rect(tiling_.total_size());
285 skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
286 if (layer_rect.IsEmpty())
287 return picture;
289 SkCanvas* canvas = picture->beginRecording(
290 layer_rect.width(),
291 layer_rect.height(),
292 SkPicture::kUsePathBoundsForClip_RecordingFlag);
294 RasterToBitmap(canvas, layer_rect, 1.0, NULL);
295 picture->endRecording();
297 return picture;
300 void PicturePileImpl::AnalyzeInRect(gfx::Rect content_rect,
301 float contents_scale,
302 PicturePileImpl::Analysis* analysis) {
303 DCHECK(analysis);
304 TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect");
306 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
307 content_rect, 1.0f / contents_scale);
309 layer_rect.Intersect(gfx::Rect(tiling_.total_size()));
311 SkBitmap empty_bitmap;
312 empty_bitmap.setConfig(SkBitmap::kNo_Config,
313 layer_rect.width(),
314 layer_rect.height());
315 skia::AnalysisDevice device(empty_bitmap);
316 skia::AnalysisCanvas canvas(&device);
318 RasterForAnalysis(&canvas, layer_rect, 1.0f);
320 analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color);
321 analysis->has_text = canvas.HasText();
324 PicturePileImpl::Analysis::Analysis()
325 : is_solid_color(false),
326 has_text(false) {
329 PicturePileImpl::Analysis::~Analysis() {
332 PicturePileImpl::PixelRefIterator::PixelRefIterator(
333 gfx::Rect content_rect,
334 float contents_scale,
335 const PicturePileImpl* picture_pile)
336 : picture_pile_(picture_pile),
337 layer_rect_(gfx::ScaleToEnclosingRect(
338 content_rect, 1.f / contents_scale)),
339 tile_iterator_(&picture_pile_->tiling_, layer_rect_) {
340 // Early out if there isn't a single tile.
341 if (!tile_iterator_)
342 return;
344 AdvanceToTilePictureWithPixelRefs();
347 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
350 PicturePileImpl::PixelRefIterator&
351 PicturePileImpl::PixelRefIterator::operator++() {
352 ++pixel_ref_iterator_;
353 if (pixel_ref_iterator_)
354 return *this;
356 ++tile_iterator_;
357 AdvanceToTilePictureWithPixelRefs();
358 return *this;
361 void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() {
362 for (; tile_iterator_; ++tile_iterator_) {
363 PictureMap::const_iterator it =
364 picture_pile_->picture_map_.find(tile_iterator_.index());
365 if (it == picture_pile_->picture_map_.end())
366 continue;
368 const Picture* picture = it->second.picture.get();
369 if (!picture || (processed_pictures_.count(picture) != 0))
370 continue;
372 processed_pictures_.insert(picture);
373 pixel_ref_iterator_ = Picture::PixelRefIterator(layer_rect_, picture);
374 if (pixel_ref_iterator_)
375 break;
379 void PicturePileImpl::DidBeginTracing() {
380 gfx::Rect layer_rect(tiling_.total_size());
381 std::set<void*> processed_pictures;
382 for (PictureMap::iterator it = picture_map_.begin();
383 it != picture_map_.end();
384 ++it) {
385 Picture* picture = it->second.picture.get();
386 if (picture && (processed_pictures.count(picture) == 0)) {
387 picture->EmitTraceSnapshot();
388 processed_pictures.insert(picture);
393 } // namespace cc