Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / cc / resources / picture_pile_impl.cc
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.
4
5 #include <algorithm>
6 #include <limits>
7
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 "third_party/skia/include/core/SkSize.h"
16 #include "ui/gfx/rect_conversions.h"
17 #include "ui/gfx/size_conversions.h"
18 #include "ui/gfx/skia_util.h"
19
20 namespace cc {
21
22 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
23   return make_scoped_refptr(new PicturePileImpl);
24 }
25
26 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther(
27     const PicturePileBase* other) {
28   return make_scoped_refptr(new PicturePileImpl(other));
29 }
30
31 PicturePileImpl::PicturePileImpl() {
32 }
33
34 PicturePileImpl::PicturePileImpl(const PicturePileBase* other)
35     : PicturePileBase(other) {
36 }
37
38 PicturePileImpl::~PicturePileImpl() {
39 }
40
41 void PicturePileImpl::RasterDirect(
42     SkCanvas* canvas,
43     const gfx::Rect& canvas_rect,
44     float contents_scale,
45     RenderingStatsInstrumentation* rendering_stats_instrumentation) {
46   RasterCommon(canvas,
47                NULL,
48                canvas_rect,
49                contents_scale,
50                rendering_stats_instrumentation,
51                false);
52 }
53
54 void PicturePileImpl::RasterForAnalysis(
55     skia::AnalysisCanvas* canvas,
56     const gfx::Rect& canvas_rect,
57     float contents_scale,
58     RenderingStatsInstrumentation* stats_instrumentation) const {
59   RasterCommon(
60       canvas, canvas, canvas_rect, contents_scale, stats_instrumentation, true);
61 }
62
63 void PicturePileImpl::RasterToBitmap(
64     SkCanvas* canvas,
65     const gfx::Rect& canvas_rect,
66     float contents_scale,
67     RenderingStatsInstrumentation* rendering_stats_instrumentation) const {
68   canvas->discard();
69   if (clear_canvas_with_debug_color_) {
70     // Any non-painted areas in the content bounds will be left in this color.
71     canvas->clear(DebugColors::NonPaintedFillColor());
72   }
73
74   // If this picture has opaque contents, it is guaranteeing that it will
75   // draw an opaque rect the size of the layer.  If it is not, then we must
76   // clear this canvas ourselves.
77   if (contents_opaque_ || contents_fill_bounds_completely_) {
78     // Even if completely covered, for rasterizations that touch the edge of the
79     // layer, we also need to raster the background color underneath the last
80     // texel (since the recording won't cover it) and outside the last texel
81     // (due to linear filtering when using this texture).
82     gfx::Rect content_tiling_rect = gfx::ToEnclosingRect(
83         gfx::ScaleRect(gfx::Rect(tiling_.tiling_size()), contents_scale));
84
85     // The final texel of content may only be partially covered by a
86     // rasterization; this rect represents the content rect that is fully
87     // covered by content.
88     gfx::Rect deflated_content_tiling_rect = content_tiling_rect;
89     deflated_content_tiling_rect.Inset(0, 0, 1, 1);
90     if (!deflated_content_tiling_rect.Contains(canvas_rect)) {
91       if (clear_canvas_with_debug_color_) {
92         // Any non-painted areas outside of the content bounds are left in
93         // this color.  If this is seen then it means that cc neglected to
94         // rerasterize a tile that used to intersect with the content rect
95         // after the content bounds grew.
96         canvas->save();
97         canvas->translate(-canvas_rect.x(), -canvas_rect.y());
98         canvas->clipRect(gfx::RectToSkRect(content_tiling_rect),
99                          SkRegion::kDifference_Op);
100         canvas->drawColor(DebugColors::MissingResizeInvalidations(),
101                           SkXfermode::kSrc_Mode);
102         canvas->restore();
103       }
104
105       // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
106       // faster than clearing, so special case this.
107       canvas->save();
108       canvas->translate(-canvas_rect.x(), -canvas_rect.y());
109       gfx::Rect inflated_content_tiling_rect = content_tiling_rect;
110       inflated_content_tiling_rect.Inset(0, 0, -1, -1);
111       canvas->clipRect(gfx::RectToSkRect(inflated_content_tiling_rect),
112                        SkRegion::kReplace_Op);
113       canvas->clipRect(gfx::RectToSkRect(deflated_content_tiling_rect),
114                        SkRegion::kDifference_Op);
115       canvas->drawColor(background_color_, SkXfermode::kSrc_Mode);
116       canvas->restore();
117     }
118   } else {
119     TRACE_EVENT_INSTANT0("cc", "SkCanvas::clear", TRACE_EVENT_SCOPE_THREAD);
120     // Clearing is about ~4x faster than drawing a rect even if the content
121     // isn't covering a majority of the canvas.
122     canvas->clear(SK_ColorTRANSPARENT);
123   }
124
125   RasterCommon(canvas,
126                NULL,
127                canvas_rect,
128                contents_scale,
129                rendering_stats_instrumentation,
130                false);
131 }
132
133 void PicturePileImpl::CoalesceRasters(const gfx::Rect& canvas_rect,
134                                       const gfx::Rect& content_rect,
135                                       float contents_scale,
136                                       PictureRegionMap* results) const {
137   DCHECK(results);
138   // Rasterize the collection of relevant picture piles.
139   gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
140       content_rect, 1.f / contents_scale);
141
142   // Make sure pictures don't overlap by keeping track of previous right/bottom.
143   int min_content_left = -1;
144   int min_content_top = -1;
145   int last_row_index = -1;
146   int last_col_index = -1;
147   gfx::Rect last_content_rect;
148
149   // Coalesce rasters of the same picture into different rects:
150   //  - Compute the clip of each of the pile chunks,
151   //  - Subtract it from the canvas rect to get difference region
152   //  - Later, use the difference region to subtract each of the comprising
153   //    rects from the canvas.
154   // Note that in essence, we're trying to mimic clipRegion with intersect op
155   // that also respects the current canvas transform and clip. In order to use
156   // the canvas transform, we must stick to clipRect operations (clipRegion
157   // ignores the transform). Intersect then can be written as subtracting the
158   // negation of the region we're trying to intersect. Luckily, we know that all
159   // of the rects will have to fit into |content_rect|, so we can start with
160   // that and subtract chunk rects to get the region that we need to subtract
161   // from the canvas. Then, we can use clipRect with difference op to subtract
162   // each rect in the region.
163   bool include_borders = true;
164   for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
165        tile_iter;
166        ++tile_iter) {
167     PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
168     if (map_iter == picture_map_.end())
169       continue;
170     const PictureInfo& info = map_iter->second;
171     const Picture* picture = info.GetPicture();
172     if (!picture)
173       continue;
174
175     // This is intentionally *enclosed* rect, so that the clip is aligned on
176     // integral post-scale content pixels and does not extend past the edges
177     // of the picture chunk's layer rect.  The min_contents_scale enforces that
178     // enough buffer pixels have been added such that the enclosed rect
179     // encompasses all invalidated pixels at any larger scale level.
180     gfx::Rect chunk_rect = PaddedRect(tile_iter.index());
181     gfx::Rect content_clip =
182         gfx::ScaleToEnclosedRect(chunk_rect, contents_scale);
183     DCHECK(!content_clip.IsEmpty()) << "Layer rect: "
184                                     << picture->LayerRect().ToString()
185                                     << "Contents scale: " << contents_scale;
186     content_clip.Intersect(canvas_rect);
187
188     // Make sure iterator goes top->bottom.
189     DCHECK_GE(tile_iter.index_y(), last_row_index);
190     if (tile_iter.index_y() > last_row_index) {
191       // First tile in a new row.
192       min_content_left = content_clip.x();
193       min_content_top = last_content_rect.bottom();
194     } else {
195       // Make sure iterator goes left->right.
196       DCHECK_GT(tile_iter.index_x(), last_col_index);
197       min_content_left = last_content_rect.right();
198       min_content_top = last_content_rect.y();
199     }
200
201     last_col_index = tile_iter.index_x();
202     last_row_index = tile_iter.index_y();
203
204     // Only inset if the content_clip is less than then previous min.
205     int inset_left = std::max(0, min_content_left - content_clip.x());
206     int inset_top = std::max(0, min_content_top - content_clip.y());
207     content_clip.Inset(inset_left, inset_top, 0, 0);
208
209     PictureRegionMap::iterator it = results->find(picture);
210     Region* clip_region;
211     if (it == results->end()) {
212       // The clip for a set of coalesced pictures starts out clipping the entire
213       // canvas.  Each picture added to the set must subtract its own bounds
214       // from the clip region, poking a hole so that the picture is unclipped.
215       clip_region = &(*results)[picture];
216       *clip_region = canvas_rect;
217     } else {
218       clip_region = &it->second;
219     }
220
221     DCHECK(clip_region->Contains(content_clip))
222         << "Content clips should not overlap.";
223     clip_region->Subtract(content_clip);
224     last_content_rect = content_clip;
225   }
226 }
227
228 void PicturePileImpl::RasterCommon(
229     SkCanvas* canvas,
230     SkDrawPictureCallback* callback,
231     const gfx::Rect& canvas_rect,
232     float contents_scale,
233     RenderingStatsInstrumentation* rendering_stats_instrumentation,
234     bool is_analysis) const {
235   DCHECK(contents_scale >= min_contents_scale_);
236
237   canvas->translate(-canvas_rect.x(), -canvas_rect.y());
238   gfx::Rect content_tiling_rect = gfx::ToEnclosingRect(
239       gfx::ScaleRect(gfx::Rect(tiling_.tiling_size()), contents_scale));
240   content_tiling_rect.Intersect(canvas_rect);
241
242   canvas->clipRect(gfx::RectToSkRect(content_tiling_rect),
243                    SkRegion::kIntersect_Op);
244
245   PictureRegionMap picture_region_map;
246   CoalesceRasters(
247       canvas_rect, content_tiling_rect, contents_scale, &picture_region_map);
248
249 #ifndef NDEBUG
250   Region total_clip;
251 #endif  // NDEBUG
252
253   // Iterate the coalesced map and use each picture's region
254   // to clip the canvas.
255   for (PictureRegionMap::iterator it = picture_region_map.begin();
256        it != picture_region_map.end();
257        ++it) {
258     const Picture* picture = it->first;
259     Region negated_clip_region = it->second;
260
261 #ifndef NDEBUG
262     Region positive_clip = content_tiling_rect;
263     positive_clip.Subtract(negated_clip_region);
264     // Make sure we never rasterize the same region twice.
265     DCHECK(!total_clip.Intersects(positive_clip));
266     total_clip.Union(positive_clip);
267 #endif  // NDEBUG
268
269     base::TimeDelta best_duration = base::TimeDelta::Max();
270     int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
271     int rasterized_pixel_count = 0;
272
273     for (int j = 0; j < repeat_count; ++j) {
274       base::TimeTicks start_time;
275       if (rendering_stats_instrumentation)
276         start_time = rendering_stats_instrumentation->StartRecording();
277
278       rasterized_pixel_count = picture->Raster(
279           canvas, callback, negated_clip_region, contents_scale);
280
281       if (rendering_stats_instrumentation) {
282         base::TimeDelta duration =
283             rendering_stats_instrumentation->EndRecording(start_time);
284         best_duration = std::min(best_duration, duration);
285       }
286     }
287
288     if (rendering_stats_instrumentation) {
289       if (is_analysis) {
290         rendering_stats_instrumentation->AddAnalysis(best_duration,
291                                                      rasterized_pixel_count);
292       } else {
293         rendering_stats_instrumentation->AddRaster(best_duration,
294                                                    rasterized_pixel_count);
295       }
296     }
297   }
298
299 #ifndef NDEBUG
300   // Fill the clip with debug color. This allows us to
301   // distinguish between non painted areas and problems with missing
302   // pictures.
303   SkPaint paint;
304   for (Region::Iterator it(total_clip); it.has_rect(); it.next())
305     canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
306   paint.setColor(DebugColors::MissingPictureFillColor());
307   paint.setXfermodeMode(SkXfermode::kSrc_Mode);
308   canvas->drawPaint(paint);
309 #endif  // NDEBUG
310 }
311
312 skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() {
313   TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
314
315   gfx::Rect tiling_rect(tiling_.tiling_size());
316   SkPictureRecorder recorder;
317   SkCanvas* canvas =
318       recorder.beginRecording(tiling_rect.width(), tiling_rect.height());
319   if (!tiling_rect.IsEmpty())
320     RasterToBitmap(canvas, tiling_rect, 1.0, NULL);
321   skia::RefPtr<SkPicture> picture = skia::AdoptRef(recorder.endRecording());
322
323   return picture;
324 }
325
326 void PicturePileImpl::AnalyzeInRect(const gfx::Rect& content_rect,
327                                     float contents_scale,
328                                     PicturePileImpl::Analysis* analysis) const {
329   AnalyzeInRect(content_rect, contents_scale, analysis, NULL);
330 }
331
332 void PicturePileImpl::AnalyzeInRect(
333     const gfx::Rect& content_rect,
334     float contents_scale,
335     PicturePileImpl::Analysis* analysis,
336     RenderingStatsInstrumentation* stats_instrumentation) const {
337   DCHECK(analysis);
338   TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect");
339
340   gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
341       content_rect, 1.0f / contents_scale);
342
343   layer_rect.Intersect(gfx::Rect(tiling_.tiling_size()));
344
345   skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height());
346
347   RasterForAnalysis(&canvas, layer_rect, 1.0f, stats_instrumentation);
348
349   analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color);
350 }
351
352 // Since there are situations when we can skip analysis, the variables have to
353 // be set to their safest values. That is, we have to assume that the tile is
354 // not solid color. As well, we have to assume that the tile has text so we
355 // don't early out incorrectly.
356 PicturePileImpl::Analysis::Analysis() : is_solid_color(false) {
357 }
358
359 PicturePileImpl::Analysis::~Analysis() {
360 }
361
362 PicturePileImpl::PixelRefIterator::PixelRefIterator(
363     const gfx::Rect& content_rect,
364     float contents_scale,
365     const PicturePileImpl* picture_pile)
366     : picture_pile_(picture_pile),
367       layer_rect_(
368           gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale)),
369       tile_iterator_(&picture_pile_->tiling_,
370                      layer_rect_,
371                      false /* include_borders */) {
372   // Early out if there isn't a single tile.
373   if (!tile_iterator_)
374     return;
375
376   AdvanceToTilePictureWithPixelRefs();
377 }
378
379 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
380 }
381
382 PicturePileImpl::PixelRefIterator&
383     PicturePileImpl::PixelRefIterator::operator++() {
384   ++pixel_ref_iterator_;
385   if (pixel_ref_iterator_)
386     return *this;
387
388   ++tile_iterator_;
389   AdvanceToTilePictureWithPixelRefs();
390   return *this;
391 }
392
393 void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() {
394   for (; tile_iterator_; ++tile_iterator_) {
395     PictureMap::const_iterator it =
396         picture_pile_->picture_map_.find(tile_iterator_.index());
397     if (it == picture_pile_->picture_map_.end())
398       continue;
399
400     const Picture* picture = it->second.GetPicture();
401     if (!picture || (processed_pictures_.count(picture) != 0) ||
402         !picture->WillPlayBackBitmaps())
403       continue;
404
405     processed_pictures_.insert(picture);
406     pixel_ref_iterator_ = Picture::PixelRefIterator(layer_rect_, picture);
407     if (pixel_ref_iterator_)
408       break;
409   }
410 }
411
412 void PicturePileImpl::DidBeginTracing() {
413   std::set<const void*> processed_pictures;
414   for (PictureMap::iterator it = picture_map_.begin();
415        it != picture_map_.end();
416        ++it) {
417     const Picture* picture = it->second.GetPicture();
418     if (picture && (processed_pictures.count(picture) == 0)) {
419       picture->EmitTraceSnapshot();
420       processed_pictures.insert(picture);
421     }
422   }
423 }
424
425 }  // namespace cc