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
20namespace cc {
21
22scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
23  return make_scoped_refptr(new PicturePileImpl);
24}
25
26scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther(
27    const PicturePileBase* other) {
28  return make_scoped_refptr(new PicturePileImpl(other));
29}
30
31PicturePileImpl::PicturePileImpl() {
32}
33
34PicturePileImpl::PicturePileImpl(const PicturePileBase* other)
35    : PicturePileBase(other) {
36}
37
38PicturePileImpl::~PicturePileImpl() {
39}
40
41void 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
54void 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
63void 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
133void 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
228void 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
312skia::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
326void 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
332void 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.
356PicturePileImpl::Analysis::Analysis() : is_solid_color(false) {
357}
358
359PicturePileImpl::Analysis::~Analysis() {
360}
361
362PicturePileImpl::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
379PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
380}
381
382PicturePileImpl::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
393void 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
412void 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
426