blob: 933bf60775ae5c59e5bcda8ac80be5223b472eab [file] [log] [blame]
// 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),
nearest_neighbor_(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_,
nearest_neighbor_);
}
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,
bool nearest_neighbor) {
// 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 && nearest_neighbor_ == nearest_neighbor)
return;
image_aperture_ = aperture;
border_ = border;
fill_center_ = fill_center;
nearest_neighbor_ = nearest_neighbor;
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,
AppendQuadsData* append_quads_data) {
CheckGeometryLimitations();
SharedQuadState* shared_quad_state =
render_pass->CreateAndAppendSharedQuadState();
PopulateSharedQuadState(shared_quad_state);
AppendDebugBorderQuad(render_pass, bounds(), shared_quad_state,
append_quads_data);
if (!ui_resource_id_)
return;
ResourceId resource =
layer_tree_impl()->ResourceIdForUIResource(ui_resource_id_);
if (!resource)
return;
static const bool flipped = 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 =
draw_properties().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_);
ValidateQuadResources(quad);
}
visible_rect =
draw_properties().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_);
ValidateQuadResources(quad);
}
visible_rect =
draw_properties().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_);
ValidateQuadResources(quad);
}
visible_rect =
draw_properties().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_);
ValidateQuadResources(quad);
}
visible_rect =
draw_properties().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_);
ValidateQuadResources(quad);
}
visible_rect =
draw_properties().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_);
ValidateQuadResources(quad);
}
visible_rect =
draw_properties().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_);
ValidateQuadResources(quad);
}
visible_rect =
draw_properties().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_);
ValidateQuadResources(quad);
}
if (fill_center_) {
visible_rect =
draw_properties().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_);
ValidateQuadResources(quad);
}
}
}
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