// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/resources/picture_pile.h"

#include <algorithm>
#include <limits>
#include <vector>

#include "cc/base/region.h"
#include "cc/debug/rendering_stats_instrumentation.h"
#include "cc/resources/picture_pile_impl.h"

namespace {
// Layout pixel buffer around the visible layer rect to record.  Any base
// picture that intersects the visible layer rect expanded by this distance
// will be recorded.
const int kPixelDistanceToRecord = 8000;
}  // namespace

namespace cc {

PicturePile::PicturePile() {
}

PicturePile::~PicturePile() {
}

bool PicturePile::Update(
    ContentLayerClient* painter,
    SkColor background_color,
    bool contents_opaque,
    const Region& invalidation,
    gfx::Rect visible_layer_rect,
    RenderingStatsInstrumentation* stats_instrumentation) {
  background_color_ = background_color;
  contents_opaque_ = contents_opaque;

  gfx::Rect interest_rect = visible_layer_rect;
  interest_rect.Inset(
      -kPixelDistanceToRecord,
      -kPixelDistanceToRecord,
      -kPixelDistanceToRecord,
      -kPixelDistanceToRecord);

  bool invalidated = false;
  for (Region::Iterator i(invalidation); i.has_rect(); i.next()) {
    gfx::Rect invalidation = i.rect();
    // Split this inflated invalidation across tile boundaries and apply it
    // to all tiles that it touches.
    for (TilingData::Iterator iter(&tiling_, invalidation);
         iter; ++iter) {
      const PictureMapKey& key = iter.index();

      PictureMap::iterator picture_it = picture_map_.find(key);
      if (picture_it == picture_map_.end())
        continue;

      invalidated = picture_it->second.Invalidate() || invalidated;
    }
  }

  gfx::Rect record_rect;
  for (TilingData::Iterator it(&tiling_, interest_rect);
       it; ++it) {
    const PictureMapKey& key = it.index();
    const PictureInfo& info = picture_map_[key];
    if (!info.picture.get()) {
      gfx::Rect tile = PaddedRect(key);
      record_rect.Union(tile);
    }
  }

  if (record_rect.IsEmpty()) {
    if (invalidated)
      UpdateRecordedRegion();
    return invalidated;
  }

  int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
  scoped_refptr<Picture> picture = Picture::Create(record_rect);

  {
    base::TimeDelta best_duration = base::TimeDelta::FromInternalValue(
        std::numeric_limits<int64>::max());
    for (int i = 0; i < repeat_count; i++) {
      base::TimeTicks start_time = stats_instrumentation->StartRecording();
      picture->Record(painter, tile_grid_info_);
      base::TimeDelta duration =
          stats_instrumentation->EndRecording(start_time);
      best_duration = std::min(duration, best_duration);
    }
    int recorded_pixel_count =
        picture->LayerRect().width() * picture->LayerRect().height();
    stats_instrumentation->AddRecord(best_duration, recorded_pixel_count);
    if (num_raster_threads_ > 1)
      picture->GatherPixelRefs(tile_grid_info_);
    picture->CloneForDrawing(num_raster_threads_);
  }

  for (TilingData::Iterator it(&tiling_, record_rect);
       it; ++it) {
    const PictureMapKey& key = it.index();
    gfx::Rect tile = PaddedRect(key);
    if (record_rect.Contains(tile)) {
      PictureInfo& info = picture_map_[key];
      info.picture = picture;
    }
  }

  UpdateRecordedRegion();
  return true;
}

}  // namespace cc