text: // 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/layers/nine_patch_layer_impl.h"
  
  #include "base/strings/stringprintf.h"
  #include "base/values.h"
  #include "cc/base/math_util.h"
  #include "cc/quads/texture_draw_quad.h"
  #include "cc/trees/layer_tree_impl.h"
  #include "cc/trees/occlusion.h"
  #include "ui/gfx/geometry/rect_f.h"
  
  namespace cc {
  
  NinePatchLayerImpl::NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id)
      : UIResourceLayerImpl(tree_impl, id),
        fill_center_(false) {}
  
  NinePatchLayerImpl::~NinePatchLayerImpl() {}
  
  scoped_ptr<LayerImpl> NinePatchLayerImpl::CreateLayerImpl(
      LayerTreeImpl* tree_impl) {
    return NinePatchLayerImpl::Create(tree_impl, id());
  }
  
  void NinePatchLayerImpl::PushPropertiesTo(LayerImpl* layer) {
    UIResourceLayerImpl::PushPropertiesTo(layer);
    NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer);
  
    layer_impl->SetLayout(image_aperture_, border_, fill_center_);
  }
  
  static gfx::RectF NormalizedRect(float x,
                                   float y,
                                   float width,
                                   float height,
                                   float total_width,
                                   float total_height) {
    return gfx::RectF(x / total_width,
                      y / total_height,
                      width / total_width,
                      height / total_height);
  }
  
  void NinePatchLayerImpl::SetLayout(const gfx::Rect& aperture,
                                     const gfx::Rect& border,
                                     bool fill_center) {
    // This check imposes an ordering on the call sequence.  An UIResource must
    // exist before SetLayout can be called.
    DCHECK(ui_resource_id_);
  
    if (image_aperture_ == aperture &&
        border_ == border && fill_center_ == fill_center)
      return;
  
    image_aperture_ = aperture;
    border_ = border;
    fill_center_ = fill_center;
  
    NoteLayerPropertyChanged();
  }
  
  void NinePatchLayerImpl::CheckGeometryLimitations() {
    // |border| is in layer space.  It cannot exceed the bounds of the layer.
    DCHECK_GE(bounds().width(), border_.width());
    DCHECK_GE(bounds().height(), border_.height());
  
    // Sanity Check on |border|
    DCHECK_LE(border_.x(), border_.width());
    DCHECK_LE(border_.y(), border_.height());
    DCHECK_GE(border_.x(), 0);
    DCHECK_GE(border_.y(), 0);
  
    // |aperture| is in image space.  It cannot exceed the bounds of the bitmap.
    DCHECK(!image_aperture_.size().IsEmpty());
    DCHECK(gfx::Rect(image_bounds_).Contains(image_aperture_))
        << "image_bounds_ " << gfx::Rect(image_bounds_).ToString()
        << " image_aperture_ " << image_aperture_.ToString();
  }
  
  void NinePatchLayerImpl::AppendQuads(
      RenderPass* render_pass,
      const Occlusion& occlusion_in_content_space,
      AppendQuadsData* append_quads_data) {
    CheckGeometryLimitations();
    SharedQuadState* shared_quad_state =
        render_pass->CreateAndAppendSharedQuadState();
    PopulateSharedQuadState(shared_quad_state);
  
    AppendDebugBorderQuad(
        render_pass, content_bounds(), shared_quad_state, append_quads_data);
  
    if (!ui_resource_id_)
      return;
  
    ResourceProvider::ResourceId resource =
        layer_tree_impl()->ResourceIdForUIResource(ui_resource_id_);
  
    if (!resource)
      return;
  
    static const bool flipped = false;
    static const bool nearest_neighbor = false;
    static const bool premultiplied_alpha = true;
  
    DCHECK(!bounds().IsEmpty());
  
    // NinePatch border widths in layer space.
    int layer_left_width = border_.x();
    int layer_top_height = border_.y();
    int layer_right_width = border_.width() - layer_left_width;
    int layer_bottom_height = border_.height() - layer_top_height;
  
    int layer_middle_width = bounds().width() - border_.width();
    int layer_middle_height = bounds().height() - border_.height();
  
    // Patch positions in layer space
    gfx::Rect layer_top_left(0, 0, layer_left_width, layer_top_height);
    gfx::Rect layer_top_right(bounds().width() - layer_right_width,
                              0,
                              layer_right_width,
                              layer_top_height);
    gfx::Rect layer_bottom_left(0,
                                bounds().height() - layer_bottom_height,
                                layer_left_width,
                                layer_bottom_height);
    gfx::Rect layer_bottom_right(layer_top_right.x(),
                                 layer_bottom_left.y(),
                                 layer_right_width,
                                 layer_bottom_height);
    gfx::Rect layer_top(
        layer_top_left.right(), 0, layer_middle_width, layer_top_height);
    gfx::Rect layer_left(
        0, layer_top_left.bottom(), layer_left_width, layer_middle_height);
    gfx::Rect layer_right(layer_top_right.x(),
                          layer_top_right.bottom(),
                          layer_right_width,
                          layer_left.height());
    gfx::Rect layer_bottom(layer_top.x(),
                           layer_bottom_left.y(),
                           layer_top.width(),
                           layer_bottom_height);
    gfx::Rect layer_center(layer_left_width,
                           layer_top_height,
                           layer_middle_width,
                           layer_middle_height);
  
    // Note the following values are in image (bitmap) space.
    float image_width = image_bounds_.width();
    float image_height = image_bounds_.height();
  
    int image_aperture_left_width = image_aperture_.x();
    int image_aperture_top_height = image_aperture_.y();
    int image_aperture_right_width = image_width - image_aperture_.right();
    int image_aperture_bottom_height = image_height - image_aperture_.bottom();
    // Patch positions in bitmap UV space (from zero to one)
    gfx::RectF uv_top_left = NormalizedRect(0,
                                            0,
                                            image_aperture_left_width,
                                            image_aperture_top_height,
                                            image_width,
                                            image_height);
    gfx::RectF uv_top_right =
        NormalizedRect(image_width - image_aperture_right_width,
                       0,
                       image_aperture_right_width,
                       image_aperture_top_height,
                       image_width,
                       image_height);
    gfx::RectF uv_bottom_left =
        NormalizedRect(0,
                       image_height - image_aperture_bottom_height,
                       image_aperture_left_width,
                       image_aperture_bottom_height,
                       image_width,
                       image_height);
    gfx::RectF uv_bottom_right =
        NormalizedRect(image_width - image_aperture_right_width,
                       image_height - image_aperture_bottom_height,
                       image_aperture_right_width,
                       image_aperture_bottom_height,
                       image_width,
                       image_height);
    gfx::RectF uv_top(
        uv_top_left.right(),
        0,
        (image_width - image_aperture_left_width - image_aperture_right_width) /
            image_width,
        (image_aperture_top_height) / image_height);
    gfx::RectF uv_left(0,
                       uv_top_left.bottom(),
                       image_aperture_left_width / image_width,
                       (image_height - image_aperture_top_height -
                        image_aperture_bottom_height) /
                           image_height);
    gfx::RectF uv_right(uv_top_right.x(),
                        uv_top_right.bottom(),
                        image_aperture_right_width / image_width,
                        uv_left.height());
    gfx::RectF uv_bottom(uv_top.x(),
                         uv_bottom_left.y(),
                         uv_top.width(),
                         image_aperture_bottom_height / image_height);
    gfx::RectF uv_center(uv_top_left.right(),
                         uv_top_left.bottom(),
                         uv_top.width(),
                         uv_left.height());
  
    gfx::Rect opaque_rect;
    gfx::Rect visible_rect;
    const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
    const bool opaque = layer_tree_impl()->IsUIResourceOpaque(ui_resource_id_);
  
    visible_rect =
        occlusion_in_content_space.GetUnoccludedContentRect(layer_top_left);
    opaque_rect = opaque ? visible_rect : gfx::Rect();
    if (!visible_rect.IsEmpty()) {
      TextureDrawQuad* quad =
          render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
      quad->SetNew(shared_quad_state,
                   layer_top_left,
                   opaque_rect,
                   visible_rect,
                   resource,
                   premultiplied_alpha,
                   uv_top_left.origin(),
                   uv_top_left.bottom_right(),
                   SK_ColorTRANSPARENT,
                   vertex_opacity,
                   flipped,
                   nearest_neighbor);
    }
  
    visible_rect =
        occlusion_in_content_space.GetUnoccludedContentRect(layer_top_right);
    opaque_rect = opaque ? visible_rect : gfx::Rect();
    if (!visible_rect.IsEmpty()) {
      TextureDrawQuad* quad =
          render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
      quad->SetNew(shared_quad_state,
                   layer_top_right,
                   opaque_rect,
                   visible_rect,
                   resource,
                   premultiplied_alpha,
                   uv_top_right.origin(),
                   uv_top_right.bottom_right(),
                   SK_ColorTRANSPARENT,
                   vertex_opacity,
                   flipped,
                   nearest_neighbor);
    }
  
    visible_rect =
        occlusion_in_content_space.GetUnoccludedContentRect(layer_bottom_left);
    opaque_rect = opaque ? visible_rect : gfx::Rect();
    if (!visible_rect.IsEmpty()) {
      TextureDrawQuad* quad =
          render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
      quad->SetNew(shared_quad_state,
                   layer_bottom_left,
                   opaque_rect,
                   visible_rect,
                   resource,
                   premultiplied_alpha,
                   uv_bottom_left.origin(),
                   uv_bottom_left.bottom_right(),
                   SK_ColorTRANSPARENT,
                   vertex_opacity,
                   flipped,
                   nearest_neighbor);
    }
  
    visible_rect =
        occlusion_in_content_space.GetUnoccludedContentRect(layer_bottom_right);
    opaque_rect = opaque ? visible_rect : gfx::Rect();
    if (!visible_rect.IsEmpty()) {
      TextureDrawQuad* quad =
          render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
      quad->SetNew(shared_quad_state,
                   layer_bottom_right,
                   opaque_rect,
                   visible_rect,
                   resource,
                   premultiplied_alpha,
                   uv_bottom_right.origin(),
                   uv_bottom_right.bottom_right(),
                   SK_ColorTRANSPARENT,
                   vertex_opacity,
                   flipped,
                   nearest_neighbor);
    }
  
    visible_rect = occlusion_in_content_space.GetUnoccludedContentRect(layer_top);
    opaque_rect = opaque ? visible_rect : gfx::Rect();
    if (!visible_rect.IsEmpty()) {
      TextureDrawQuad* quad =
          render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
      quad->SetNew(shared_quad_state,
                   layer_top,
                   opaque_rect,
                   visible_rect,
                   resource,
                   premultiplied_alpha,
                   uv_top.origin(),
                   uv_top.bottom_right(),
                   SK_ColorTRANSPARENT,
                   vertex_opacity,
                   flipped,
                   nearest_neighbor);
    }
  
    visible_rect =
        occlusion_in_content_space.GetUnoccludedContentRect(layer_left);
    opaque_rect = opaque ? visible_rect : gfx::Rect();
    if (!visible_rect.IsEmpty()) {
      TextureDrawQuad* quad =
          render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
      quad->SetNew(shared_quad_state,
                   layer_left,
                   opaque_rect,
                   visible_rect,
                   resource,
                   premultiplied_alpha,
                   uv_left.origin(),
                   uv_left.bottom_right(),
                   SK_ColorTRANSPARENT,
                   vertex_opacity,
                   flipped,
                   nearest_neighbor);
    }
  
    visible_rect =
        occlusion_in_content_space.GetUnoccludedContentRect(layer_right);
    opaque_rect = opaque ? visible_rect : gfx::Rect();
    if (!visible_rect.IsEmpty()) {
      TextureDrawQuad* quad =
          render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
      quad->SetNew(shared_quad_state,
                   layer_right,
                   opaque_rect,
                   layer_right,
                   resource,
                   premultiplied_alpha,
                   uv_right.origin(),
                   uv_right.bottom_right(),
                   SK_ColorTRANSPARENT,
                   vertex_opacity,
                   flipped,
                   nearest_neighbor);
    }
  
    visible_rect =
        occlusion_in_content_space.GetUnoccludedContentRect(layer_bottom);
    opaque_rect = opaque ? visible_rect : gfx::Rect();
    if (!visible_rect.IsEmpty()) {
      TextureDrawQuad* quad =
          render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
      quad->SetNew(shared_quad_state,
                   layer_bottom,
                   opaque_rect,
                   visible_rect,
                   resource,
                   premultiplied_alpha,
                   uv_bottom.origin(),
                   uv_bottom.bottom_right(),
                   SK_ColorTRANSPARENT,
                   vertex_opacity,
                   flipped,
                   nearest_neighbor);
    }
  
    if (fill_center_) {
      visible_rect =
          occlusion_in_content_space.GetUnoccludedContentRect(layer_center);
      opaque_rect = opaque ? visible_rect : gfx::Rect();
      if (!visible_rect.IsEmpty()) {
        TextureDrawQuad* quad =
            render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
        quad->SetNew(shared_quad_state,
                     layer_center,
                     opaque_rect,
                     visible_rect,
                     resource,
                     premultiplied_alpha,
                     uv_center.origin(),
                     uv_center.bottom_right(),
                     SK_ColorTRANSPARENT,
                     vertex_opacity,
                     flipped,
                     nearest_neighbor);
      }
    }
  }
  
  const char* NinePatchLayerImpl::LayerTypeAsString() const {
    return "cc::NinePatchLayerImpl";
  }
  
  base::DictionaryValue* NinePatchLayerImpl::LayerTreeAsJson() const {
    base::DictionaryValue* result = LayerImpl::LayerTreeAsJson();
  
    base::ListValue* list = new base::ListValue;
    list->AppendInteger(image_aperture_.origin().x());
    list->AppendInteger(image_aperture_.origin().y());
    list->AppendInteger(image_aperture_.size().width());
    list->AppendInteger(image_aperture_.size().height());
    result->Set("ImageAperture", list);
  
    list = new base::ListValue;
    list->AppendInteger(image_bounds_.width());
    list->AppendInteger(image_bounds_.height());
    result->Set("ImageBounds", list);
  
    result->Set("Border", MathUtil::AsValue(border_).release());
  
    result->SetBoolean("FillCenter", fill_center_);
  
    return result;
  }
  
  }  // namespace cc
  