text: // Copyright 2011 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/layers/tiled_layer_impl.h"
  
  #include "base/basictypes.h"
  #include "base/strings/stringprintf.h"
  #include "cc/base/math_util.h"
  #include "cc/debug/debug_colors.h"
  #include "cc/layers/append_quads_data.h"
  #include "cc/layers/quad_sink.h"
  #include "cc/quads/checkerboard_draw_quad.h"
  #include "cc/quads/debug_border_draw_quad.h"
  #include "cc/quads/solid_color_draw_quad.h"
  #include "cc/quads/tile_draw_quad.h"
  #include "cc/resources/layer_tiling_data.h"
  #include "third_party/khronos/GLES2/gl2.h"
  #include "third_party/skia/include/core/SkColor.h"
  #include "ui/gfx/quad_f.h"
  
  namespace cc {
  
  class DrawableTile : public LayerTilingData::Tile {
   public:
    static scoped_ptr<DrawableTile> Create() {
      return make_scoped_ptr(new DrawableTile());
    }
  
    ResourceProvider::ResourceId resource_id() const { return resource_id_; }
    void set_resource_id(ResourceProvider::ResourceId resource_id) {
      resource_id_ = resource_id;
    }
    bool contents_swizzled() { return contents_swizzled_; }
    void set_contents_swizzled(bool contents_swizzled) {
      contents_swizzled_ = contents_swizzled;
    }
  
   private:
    DrawableTile() : resource_id_(0), contents_swizzled_(false) {}
  
    ResourceProvider::ResourceId resource_id_;
    bool contents_swizzled_;
  
    DISALLOW_COPY_AND_ASSIGN(DrawableTile);
  };
  
  TiledLayerImpl::TiledLayerImpl(LayerTreeImpl* tree_impl, int id)
      : LayerImpl(tree_impl, id), skips_draw_(true) {}
  
  TiledLayerImpl::~TiledLayerImpl() {
  }
  
  ResourceProvider::ResourceId TiledLayerImpl::ContentsResourceId() const {
    // This function is only valid for single texture layers, e.g. masks.
    DCHECK(tiler_);
    // It's possible the mask layer is created but has no size or otherwise
    // can't draw.
    if (tiler_->num_tiles_x() == 0 || tiler_->num_tiles_y() == 0)
      return 0;
  
    // Any other number of tiles other than 0 or 1 is incorrect for masks.
    DCHECK_EQ(tiler_->num_tiles_x(), 1);
    DCHECK_EQ(tiler_->num_tiles_y(), 1);
  
    DrawableTile* tile = TileAt(0, 0);
    ResourceProvider::ResourceId resource_id = tile ? tile->resource_id() : 0;
    return resource_id;
  }
  
  bool TiledLayerImpl::HasTileAt(int i, int j) const {
    return !!tiler_->TileAt(i, j);
  }
  
  bool TiledLayerImpl::HasResourceIdForTileAt(int i, int j) const {
    return HasTileAt(i, j) && TileAt(i, j)->resource_id();
  }
  
  DrawableTile* TiledLayerImpl::TileAt(int i, int j) const {
    return static_cast<DrawableTile*>(tiler_->TileAt(i, j));
  }
  
  DrawableTile* TiledLayerImpl::CreateTile(int i, int j) {
    scoped_ptr<DrawableTile> tile(DrawableTile::Create());
    DrawableTile* added_tile = tile.get();
    tiler_->AddTile(tile.PassAs<LayerTilingData::Tile>(), i, j);
  
    return added_tile;
  }
  
  void TiledLayerImpl::GetDebugBorderProperties(SkColor* color,
                                                float* width) const {
    *color = DebugColors::TiledContentLayerBorderColor();
    *width = DebugColors::TiledContentLayerBorderWidth(layer_tree_impl());
  }
  
  scoped_ptr<LayerImpl> TiledLayerImpl::CreateLayerImpl(
      LayerTreeImpl* tree_impl) {
    return TiledLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
  }
  
  void TiledLayerImpl::AsValueInto(base::DictionaryValue* state) const {
    LayerImpl::AsValueInto(state);
    state->Set("invalidation", MathUtil::AsValue(update_rect()).release());
  }
  
  size_t TiledLayerImpl::GPUMemoryUsageInBytes() const {
    size_t amount = 0;
    const size_t kMemoryUsagePerTileInBytes =
        4 * tiler_->tile_size().width() * tiler_->tile_size().height();
    for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
         iter != tiler_->tiles().end();
         ++iter) {
      const DrawableTile* tile = static_cast<DrawableTile*>(iter->second);
      if (!tile || !tile->resource_id())
        continue;
      amount += kMemoryUsagePerTileInBytes;
    }
    return amount;
  }
  
  void TiledLayerImpl::PushPropertiesTo(LayerImpl* layer) {
    LayerImpl::PushPropertiesTo(layer);
  
    TiledLayerImpl* tiled_layer = static_cast<TiledLayerImpl*>(layer);
  
    tiled_layer->set_skips_draw(skips_draw_);
    tiled_layer->SetTilingData(*tiler_);
  
    for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
         iter != tiler_->tiles().end();
         ++iter) {
      int i = iter->first.first;
      int j = iter->first.second;
      DrawableTile* tile = static_cast<DrawableTile*>(iter->second);
  
      tiled_layer->PushTileProperties(i,
                                      j,
                                      tile->resource_id(),
                                      tile->opaque_rect(),
                                      tile->contents_swizzled());
    }
  }
  
  bool TiledLayerImpl::WillDraw(DrawMode draw_mode,
                                ResourceProvider* resource_provider) {
    if (!tiler_ || tiler_->has_empty_bounds() ||
        visible_content_rect().IsEmpty() ||
        draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE)
      return false;
    return LayerImpl::WillDraw(draw_mode, resource_provider);
  }
  
  void TiledLayerImpl::AppendQuads(QuadSink* quad_sink,
                                   AppendQuadsData* append_quads_data) {
    DCHECK(tiler_);
    DCHECK(!tiler_->has_empty_bounds());
    DCHECK(!visible_content_rect().IsEmpty());
  
    gfx::Rect content_rect = visible_content_rect();
    SharedQuadState* shared_quad_state =
        quad_sink->UseSharedQuadState(CreateSharedQuadState());
    AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
  
    int left, top, right, bottom;
    tiler_->ContentRectToTileIndices(content_rect, &left, &top, &right, &bottom);
  
    if (ShowDebugBorders()) {
      for (int j = top; j <= bottom; ++j) {
        for (int i = left; i <= right; ++i) {
          DrawableTile* tile = TileAt(i, j);
          gfx::Rect tile_rect = tiler_->tile_bounds(i, j);
          SkColor border_color;
          float border_width;
  
          if (skips_draw_ || !tile || !tile->resource_id()) {
            border_color = DebugColors::MissingTileBorderColor();
            border_width = DebugColors::MissingTileBorderWidth(layer_tree_impl());
          } else {
            border_color = DebugColors::HighResTileBorderColor();
            border_width = DebugColors::HighResTileBorderWidth(layer_tree_impl());
          }
          scoped_ptr<DebugBorderDrawQuad> debug_border_quad =
              DebugBorderDrawQuad::Create();
          debug_border_quad->SetNew(
              shared_quad_state, tile_rect, border_color, border_width);
          quad_sink->Append(debug_border_quad.PassAs<DrawQuad>(),
                            append_quads_data);
        }
      }
    }
  
    if (skips_draw_)
      return;
  
    for (int j = top; j <= bottom; ++j) {
      for (int i = left; i <= right; ++i) {
        DrawableTile* tile = TileAt(i, j);
        gfx::Rect tile_rect = tiler_->tile_bounds(i, j);
        gfx::Rect display_rect = tile_rect;
        tile_rect.Intersect(content_rect);
  
        // Skip empty tiles.
        if (tile_rect.IsEmpty())
          continue;
  
        if (!tile || !tile->resource_id()) {
          SkColor checker_color;
          if (ShowDebugBorders()) {
            checker_color =
                tile ? DebugColors::InvalidatedTileCheckerboardColor()
                     : DebugColors::EvictedTileCheckerboardColor();
          } else {
            checker_color = DebugColors::DefaultCheckerboardColor();
          }
  
          scoped_ptr<CheckerboardDrawQuad> checkerboard_quad =
              CheckerboardDrawQuad::Create();
          checkerboard_quad->SetNew(
              shared_quad_state, tile_rect, checker_color);
          if (quad_sink->Append(checkerboard_quad.PassAs<DrawQuad>(),
                                append_quads_data))
            append_quads_data->num_missing_tiles++;
  
          continue;
        }
  
        gfx::Rect tile_opaque_rect =
            contents_opaque() ? tile_rect : gfx::IntersectRects(
                                                tile->opaque_rect(), tile_rect);
  
        // Keep track of how the top left has moved, so the texture can be
        // offset the same amount.
        gfx::Vector2d display_offset = tile_rect.origin() - display_rect.origin();
        gfx::Vector2d texture_offset =
            tiler_->texture_offset(i, j) + display_offset;
        gfx::RectF tex_coord_rect = gfx::RectF(tile_rect.size()) + texture_offset;
  
        float tile_width = static_cast<float>(tiler_->tile_size().width());
        float tile_height = static_cast<float>(tiler_->tile_size().height());
        gfx::Size texture_size(tile_width, tile_height);
  
        scoped_ptr<TileDrawQuad> quad = TileDrawQuad::Create();
        quad->SetNew(shared_quad_state,
                     tile_rect,
                     tile_opaque_rect,
                     tile->resource_id(),
                     tex_coord_rect,
                     texture_size,
                     tile->contents_swizzled());
        quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
      }
    }
  }
  
  void TiledLayerImpl::SetTilingData(const LayerTilingData& tiler) {
    if (tiler_) {
      tiler_->reset();
    } else {
      tiler_ = LayerTilingData::Create(tiler.tile_size(),
                                       tiler.has_border_texels()
                                           ? LayerTilingData::HAS_BORDER_TEXELS
                                           : LayerTilingData::NO_BORDER_TEXELS);
    }
    *tiler_ = tiler;
  }
  
  void TiledLayerImpl::PushTileProperties(
      int i,
      int j,
      ResourceProvider::ResourceId resource_id,
      const gfx::Rect& opaque_rect,
      bool contents_swizzled) {
    DrawableTile* tile = TileAt(i, j);
    if (!tile)
      tile = CreateTile(i, j);
    tile->set_resource_id(resource_id);
    tile->set_opaque_rect(opaque_rect);
    tile->set_contents_swizzled(contents_swizzled);
  }
  
  void TiledLayerImpl::PushInvalidTile(int i, int j) {
    DrawableTile* tile = TileAt(i, j);
    if (!tile)
      tile = CreateTile(i, j);
    tile->set_resource_id(0);
    tile->set_opaque_rect(gfx::Rect());
    tile->set_contents_swizzled(false);
  }
  
  Region TiledLayerImpl::VisibleContentOpaqueRegion() const {
    if (skips_draw_)
      return Region();
    if (contents_opaque())
      return visible_content_rect();
    return tiler_->OpaqueRegionInContentRect(visible_content_rect());
  }
  
  void TiledLayerImpl::ReleaseResources() {
    tiler_->reset();
  }
  
  const char* TiledLayerImpl::LayerTypeAsString() const {
    return "cc::TiledLayerImpl";
  }
  
  }  // namespace cc
  